From db9e91152d2ef716d6955f73a034cc8ec9611368 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 23 Jun 2025 09:51:09 +0800 Subject: [PATCH 0001/1187] Feat: Add Tavily operator #3221 (#8400) ### What problem does this PR solve? Feat: Add Tavily operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../node/dropdown/next-step-dropdown.tsx | 2 +- web/src/pages/agent/constant.tsx | 38 +++++++++++- .../agent/form-sheet/use-form-config-map.tsx | 6 ++ .../agent/form/agent-form/agent-tools.tsx | 2 +- web/src/pages/agent/form/agent-form/index.tsx | 1 + .../pages/agent/form/tavily-form/index.tsx | 26 ++++---- .../agent/form/tavily-form/use-values.ts | 58 +++--------------- .../form/tavily-form/use-watch-change.ts | 38 ++++-------- .../pages/agent/form/tool-form/constant.ts | 2 +- .../form/tool-form/tavily-form/index.tsx | 60 +++++++++++++++++++ .../pages/agent/form/tool-form/use-values.ts | 43 +++++++++++++ .../agent/form/tool-form/use-watch-change.ts | 39 ++++++++++++ web/src/pages/agent/hooks.tsx | 2 + web/src/pages/agent/hooks/use-add-node.ts | 2 + 14 files changed, 226 insertions(+), 93 deletions(-) create mode 100644 web/src/pages/agent/form/tool-form/tavily-form/index.tsx create mode 100644 web/src/pages/agent/form/tool-form/use-values.ts create mode 100644 web/src/pages/agent/form/tool-form/use-watch-change.ts diff --git a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx index e741286f293..f8ffc4cb3b3 100644 --- a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx @@ -88,7 +88,7 @@ function AccordionOperators() { Tools - + diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 3f1d192e1f2..cebd4624014 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -387,7 +387,7 @@ const initialQueryBaseValues = { }; export const initialRetrievalValues = { - query: '', + query: AgentGlobals.SysQuery, top_n: 8, top_k: 1024, kb_ids: [], @@ -686,6 +686,41 @@ export const initialAgentValues = { }, }; +export enum TavilySearchDepth { + Basic = 'basic', + Advanced = 'advanced', +} + +export enum TavilyTopic { + News = 'news', + General = 'general', +} + +export const initialTavilyValues = { + api_key: '', + query: AgentGlobals.SysQuery, + search_depth: TavilySearchDepth.Basic, + topic: TavilyTopic.General, + max_results: 5, + days: 7, + include_answer: false, + include_raw_content: true, + include_images: false, + include_image_descriptions: false, + include_domains: [], + exclude_domains: [], + outputs: { + formalized_content: { + value: '', + type: 'string', + }, + json: { + value: {}, + type: 'Object', + }, + }, +}; + export const CategorizeAnchorPointPositions = [ { top: 1, right: 34 }, { top: 8, right: 18 }, @@ -813,6 +848,7 @@ export const NodeMap = { [Operator.WaitingDialogue]: 'ragNode', [Operator.Agent]: 'agentNode', [Operator.Tool]: 'toolNode', + [Operator.Tavily]: 'ragNode', }; export const LanguageOptions = [ diff --git a/web/src/pages/agent/form-sheet/use-form-config-map.tsx b/web/src/pages/agent/form-sheet/use-form-config-map.tsx index d887bf58c35..50b537a07ee 100644 --- a/web/src/pages/agent/form-sheet/use-form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/use-form-config-map.tsx @@ -33,6 +33,7 @@ import RelevantForm from '../form/relevant-form'; import RetrievalForm from '../form/retrieval-form/next'; import RewriteQuestionForm from '../form/rewrite-question-form'; import SwitchForm from '../form/switch-form'; +import TavilyForm from '../form/tavily-form'; import TemplateForm from '../form/template-form'; import ToolForm from '../form/tool-form'; import TuShareForm from '../form/tushare-form'; @@ -375,6 +376,11 @@ export function useFormConfigMap() { defaultValues: {}, schema: z.object({}), }, + [Operator.Tavily]: { + component: TavilyForm, + defaultValues: {}, + schema: z.object({}), + }, }; return FormConfigMap; diff --git a/web/src/pages/agent/form/agent-form/agent-tools.tsx b/web/src/pages/agent/form/agent-form/agent-tools.tsx index 25bbead6d71..5961c8e5ad0 100644 --- a/web/src/pages/agent/form/agent-form/agent-tools.tsx +++ b/web/src/pages/agent/form/agent-form/agent-tools.tsx @@ -36,7 +36,7 @@ export function AgentTools() { {x}
- + { name={`prompts`} render={({ field }) => ( + User Prompt
diff --git a/web/src/pages/agent/form/tavily-form/index.tsx b/web/src/pages/agent/form/tavily-form/index.tsx index 3ff58b37f43..461d0091075 100644 --- a/web/src/pages/agent/form/tavily-form/index.tsx +++ b/web/src/pages/agent/form/tavily-form/index.tsx @@ -15,20 +15,26 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import { + TavilySearchDepth, + TavilyTopic, + initialTavilyValues, +} from '../../constant'; +import { INextOperatorForm } from '../../interface'; import { Output, OutputType } from '../components/output'; import { QueryVariable } from '../components/query-variable'; import { DynamicDomain } from './dynamic-domain'; -import { SearchDepth, Topic, defaultValues, useValues } from './use-values'; +import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; -const TavilyForm = () => { - const values = useValues(); +const TavilyForm = ({ node }: INextOperatorForm) => { + const values = useValues(node); const FormSchema = z.object({ api_key: z.string(), query: z.string(), - search_depth: z.enum([SearchDepth.Advanced, SearchDepth.Basic]), - topic: z.enum([Topic.News, Topic.General]), + search_depth: z.enum([TavilySearchDepth.Advanced, TavilySearchDepth.Basic]), + topic: z.enum([TavilyTopic.News, TavilyTopic.General]), max_results: z.coerce.number(), days: z.coerce.number(), include_answer: z.boolean(), @@ -45,7 +51,7 @@ const TavilyForm = () => { }); const outputList = useMemo(() => { - return Object.entries(defaultValues.outputs).reduce( + return Object.entries(initialTavilyValues.outputs).reduce( (pre, [key, val]) => { pre.push({ title: key, type: val.type }); return pre; @@ -54,7 +60,7 @@ const TavilyForm = () => { ); }, []); - useWatchFormChange(form); + useWatchFormChange(node?.id, form); return (
@@ -92,7 +98,7 @@ const TavilyForm = () => { @@ -104,12 +110,12 @@ const TavilyForm = () => { name="topic" render={({ field }) => ( - Topic + TavilyTopic diff --git a/web/src/pages/agent/form/tavily-form/use-values.ts b/web/src/pages/agent/form/tavily-form/use-values.ts index 486c0662209..011e04005d2 100644 --- a/web/src/pages/agent/form/tavily-form/use-values.ts +++ b/web/src/pages/agent/form/tavily-form/use-values.ts @@ -1,59 +1,15 @@ -import { AgentGlobals } from '@/constants/agent'; +import { RAGFlowNodeType } from '@/interfaces/database/agent'; import { isEmpty } from 'lodash'; import { useMemo } from 'react'; -import useGraphStore from '../../store'; -import { convertToObjectArray, getAgentNodeTools } from '../../utils'; - -export enum SearchDepth { - Basic = 'basic', - Advanced = 'advanced', -} - -export enum Topic { - News = 'news', - General = 'general', -} - -export const defaultValues = { - api_key: '', - query: AgentGlobals.SysQuery, - search_depth: SearchDepth.Basic, - topic: Topic.General, - max_results: 5, - days: 7, - include_answer: false, - include_raw_content: true, - include_images: false, - include_image_descriptions: false, - include_domains: [], - exclude_domains: [], - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: {}, - type: 'Object', - }, - }, -}; - -export function useValues() { - const { clickedToolId, clickedNodeId, findUpstreamNodeById } = useGraphStore( - (state) => state, - ); +import { initialTavilyValues } from '../../constant'; +import { convertToObjectArray } from '../../utils'; +export function useValues(node?: RAGFlowNodeType) { const values = useMemo(() => { - const agentNode = findUpstreamNodeById(clickedNodeId); - const tools = getAgentNodeTools(agentNode); - - const formData = tools.find( - (x) => x.component_name === clickedToolId, - )?.params; + const formData = node?.data?.form; if (isEmpty(formData)) { - return defaultValues; + return initialTavilyValues; } return { @@ -61,7 +17,7 @@ export function useValues() { include_domains: convertToObjectArray(formData.include_domains), exclude_domains: convertToObjectArray(formData.exclude_domains), }; - }, [clickedNodeId, clickedToolId, findUpstreamNodeById]); + }, [node?.data?.form]); return values; } diff --git a/web/src/pages/agent/form/tavily-form/use-watch-change.ts b/web/src/pages/agent/form/tavily-form/use-watch-change.ts index 31927e9ed58..8acaaa2f72b 100644 --- a/web/src/pages/agent/form/tavily-form/use-watch-change.ts +++ b/web/src/pages/agent/form/tavily-form/use-watch-change.ts @@ -1,41 +1,23 @@ import { useEffect } from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; import useGraphStore from '../../store'; -import { convertToStringArray, getAgentNodeTools } from '../../utils'; +import { convertToStringArray } from '../../utils'; -export function useWatchFormChange(form?: UseFormReturn) { +export function useWatchFormChange(id?: string, form?: UseFormReturn) { let values = useWatch({ control: form?.control }); - const { clickedToolId, clickedNodeId, findUpstreamNodeById, updateNodeForm } = - useGraphStore((state) => state); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); useEffect(() => { - const agentNode = findUpstreamNodeById(clickedNodeId); // Manually triggered form updates are synchronized to the canvas - if (agentNode && form?.formState.isDirty) { - const agentNodeId = agentNode?.id; - const tools = getAgentNodeTools(agentNode); - + if (id && form?.formState.isDirty) { values = form?.getValues(); - const nextTools = tools.map((x) => { - if (x.component_name === clickedToolId) { - return { - ...x, - params: { - ...values, - include_domains: convertToStringArray(values.include_domains), - exclude_domains: convertToStringArray(values.exclude_domains), - }, - }; - } - return x; - }); - - const nextValues = { - ...(agentNode?.data?.form ?? {}), - tools: nextTools, + let nextValues: any = { + ...values, + include_domains: convertToStringArray(values.include_domains), + exclude_domains: convertToStringArray(values.exclude_domains), }; - updateNodeForm(agentNodeId, nextValues); + updateNodeForm(id, nextValues); } - }, [form?.formState.isDirty, updateNodeForm, values]); + }, [form?.formState.isDirty, id, updateNodeForm, values]); } diff --git a/web/src/pages/agent/form/tool-form/constant.ts b/web/src/pages/agent/form/tool-form/constant.ts index a22aa8a2dc0..4954f51e16a 100644 --- a/web/src/pages/agent/form/tool-form/constant.ts +++ b/web/src/pages/agent/form/tool-form/constant.ts @@ -13,9 +13,9 @@ import GoogleForm from '../google-form'; import GoogleScholarForm from '../google-scholar-form'; import PubMedForm from '../pubmed-form'; import RetrievalForm from '../retrieval-form/next'; -import TavilyForm from '../tavily-form'; import WikipediaForm from '../wikipedia-form'; import YahooFinanceForm from '../yahoo-finance-form'; +import TavilyForm from './tavily-form'; export const ToolFormConfigMap = { [Operator.Retrieval]: RetrievalForm, diff --git a/web/src/pages/agent/form/tool-form/tavily-form/index.tsx b/web/src/pages/agent/form/tool-form/tavily-form/index.tsx new file mode 100644 index 00000000000..3dde7fde972 --- /dev/null +++ b/web/src/pages/agent/form/tool-form/tavily-form/index.tsx @@ -0,0 +1,60 @@ +import { FormContainer } from '@/components/form-container'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { useValues } from '../use-values'; +import { useWatchFormChange } from '../use-watch-change'; + +const TavilyForm = () => { + const values = useValues(); + + const FormSchema = z.object({ + api_key: z.string(), + }); + + const form = useForm>({ + defaultValues: values, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(form); + + return ( + + { + e.preventDefault(); + }} + > + + ( + + Api Key + + + + + + )} + /> + + + + ); +}; + +export default TavilyForm; diff --git a/web/src/pages/agent/form/tool-form/use-values.ts b/web/src/pages/agent/form/tool-form/use-values.ts new file mode 100644 index 00000000000..f2745d279c9 --- /dev/null +++ b/web/src/pages/agent/form/tool-form/use-values.ts @@ -0,0 +1,43 @@ +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import useGraphStore from '../../store'; +import { getAgentNodeTools } from '../../utils'; + +export enum SearchDepth { + Basic = 'basic', + Advanced = 'advanced', +} + +export enum Topic { + News = 'news', + General = 'general', +} + +export const defaultValues = { + api_key: '', +}; + +export function useValues() { + const { clickedToolId, clickedNodeId, findUpstreamNodeById } = useGraphStore( + (state) => state, + ); + + const values = useMemo(() => { + const agentNode = findUpstreamNodeById(clickedNodeId); + const tools = getAgentNodeTools(agentNode); + + const formData = tools.find( + (x) => x.component_name === clickedToolId, + )?.params; + + if (isEmpty(formData)) { + return defaultValues; + } + + return { + ...formData, + }; + }, [clickedNodeId, clickedToolId, findUpstreamNodeById]); + + return values; +} diff --git a/web/src/pages/agent/form/tool-form/use-watch-change.ts b/web/src/pages/agent/form/tool-form/use-watch-change.ts new file mode 100644 index 00000000000..81a70a235f1 --- /dev/null +++ b/web/src/pages/agent/form/tool-form/use-watch-change.ts @@ -0,0 +1,39 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../../store'; +import { getAgentNodeTools } from '../../utils'; + +export function useWatchFormChange(form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const { clickedToolId, clickedNodeId, findUpstreamNodeById, updateNodeForm } = + useGraphStore((state) => state); + + useEffect(() => { + const agentNode = findUpstreamNodeById(clickedNodeId); + // Manually triggered form updates are synchronized to the canvas + if (agentNode && form?.formState.isDirty) { + const agentNodeId = agentNode?.id; + const tools = getAgentNodeTools(agentNode); + + values = form?.getValues(); + const nextTools = tools.map((x) => { + if (x.component_name === clickedToolId) { + return { + ...x, + params: { + ...values, + }, + }; + } + return x; + }); + + const nextValues = { + ...(agentNode?.data?.form ?? {}), + tools: nextTools, + }; + + updateNodeForm(agentNodeId, nextValues); + } + }, [form?.formState.isDirty, updateNodeForm, values]); +} diff --git a/web/src/pages/agent/hooks.tsx b/web/src/pages/agent/hooks.tsx index 7d194fef0b8..f10a72ecce8 100644 --- a/web/src/pages/agent/hooks.tsx +++ b/web/src/pages/agent/hooks.tsx @@ -64,6 +64,7 @@ import { initialRetrievalValues, initialRewriteQuestionValues, initialSwitchValues, + initialTavilyValues, initialTemplateValues, initialTuShareValues, initialWaitingDialogueValues, @@ -147,6 +148,7 @@ export const useInitializeOperatorParams = () => { [Operator.Code]: initialCodeValues, [Operator.WaitingDialogue]: initialWaitingDialogueValues, [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, + [Operator.Tavily]: initialTavilyValues, }; }, [llmId]); diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index 074e1b20109..0a74eac22b0 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -39,6 +39,7 @@ import { initialRetrievalValues, initialRewriteQuestionValues, initialSwitchValues, + initialTavilyValues, initialTemplateValues, initialTuShareValues, initialWaitingDialogueValues, @@ -104,6 +105,7 @@ export const useInitializeOperatorParams = () => { [Operator.WaitingDialogue]: initialWaitingDialogueValues, [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, [Operator.Tool]: {}, + [Operator.Tavily]: initialTavilyValues, }; }, [llmId]); From 3a50908946c3e28e53b68b7a43545cd395ea1f13 Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Mon, 23 Jun 2025 09:51:28 +0800 Subject: [PATCH 0002/1187] Docs: Added v0.19.1 release notes (#8398) ### What problem does this PR solve? ### Type of change - [x] Documentation Update --- docs/develop/mcp/mcp_client_example.md | 4 ++-- docs/guides/dataset/set_metadata.md | 2 +- docs/release_notes.md | 20 +++++++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/develop/mcp/mcp_client_example.md b/docs/develop/mcp/mcp_client_example.md index ee9c5c2cfde..508316487b8 100644 --- a/docs/develop/mcp/mcp_client_example.md +++ b/docs/develop/mcp/mcp_client_example.md @@ -14,7 +14,7 @@ Python and curl MCP client examples. We provide a *prototype* MCP client example for testing [here](https://github.com/infiniflow/ragflow/blob/main/mcp/client/client.py). -:::danger IMPORTANT +:::info IMPORTANT If your MCP server is running in host mode, include your acquired API key in your client's `headers` when connecting asynchronously to it: ```python @@ -36,7 +36,7 @@ When interacting with the MCP server via HTTP requests, follow this initializati 1. **The client sends an `initialize` request** with protocol version and capabilities. 2. **The server replies with an `initialize` response**, including the supported protocol and capabilities. -3. **The client confirms readiness with an `initialized` notification**. +3. **The client confirms readiness with an `initialized` notification**. _The connection is established between the client and the server, and further operations (such as tool listing) may proceed._ :::tip NOTE diff --git a/docs/guides/dataset/set_metadata.md b/docs/guides/dataset/set_metadata.md index b36761c9486..7107acd5e92 100644 --- a/docs/guides/dataset/set_metadata.md +++ b/docs/guides/dataset/set_metadata.md @@ -9,7 +9,7 @@ Add metadata to an uploaded file --- -On the **Dataset** page of your knowledge base, you can add metadata to any uploaded file. This approach enables you to 'tag' additional information like URL, author, date, and more to an existing file or dataset. In an AI-powered chat, such information will be sent to the LLM with the retrieved chunks for content generation. +On the **Dataset** page of your knowledge base, you can add metadata to any uploaded file. This approach enables you to 'tag' additional information like URL, author, date, and more to an existing file. In an AI-powered chat, such information will be sent to the LLM with the retrieved chunks for content generation. For example, if you have a dataset of HTML files and want the LLM to cite the source URL when responding to your query, add a `"url"` parameter to each file's metadata. diff --git a/docs/release_notes.md b/docs/release_notes.md index 20f66ed9a33..58face4cd10 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -22,6 +22,24 @@ The embedding models included in a full edition are: These two embedding models are optimized specifically for English and Chinese, so performance may be compromised if you use them to embed documents in other languages. ::: +## v0.19.1 + +Released on June 23, 2025. + +### Fixed issues + +- A memory leak issue during high-concurrency requests. +- A context error occurring when using Sandbox in standalone mode. [#8340](https://github.com/infiniflow/ragflow/pull/8340) +- An excessive CPU usage issue caused by Ollama. [#8216](https://github.com/infiniflow/ragflow/pull/8216) +- A bug in the Code Component. [#7949](https://github.com/infiniflow/ragflow/pull/7949) +- Added support for models installed via Ollama or VLLM when creating a knowledge base through the API. [#8069](https://github.com/infiniflow/ragflow/pull/8069) +- Enabled role-based authentication for S3 bucket access. [#8149](https://github.com/infiniflow/ragflow/pull/8149) + +### Added models + +- Qwen 3 Embedding. [#8184](https://github.com/infiniflow/ragflow/pull/8184) +- Voyage Multimodal 3. [#7987](https://github.com/infiniflow/ragflow/pull/7987) + ## v0.19.0 Released on May 26, 2025. @@ -33,7 +51,7 @@ Released on May 26, 2025. - Enhanced image display: Images in Chat and Search now render directly within responses, rather than as external references. Knowledge retrieval testing can retrieve images directly, instead of texts extracted from images. - Claude 4 and ChatGPT o3: Developers can now use the newly released, most advanced Claude model and OpenAI’s latest ChatGPT o3 inference model. -> The following features are contributed by our community contributors: +> The following features have been contributed by our community: - Agent component: Enables tool calling within the Generate Component. Thanks to [notsyncing](https://github.com/notsyncing). - Markdown rendering: Image references in a markdown file can be displayed after chunking. Thanks to [Woody-Hu](https://github.com/Woody-Hu). From 794a4102c281691a995c3e050b75dc9390b0fc69 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Mon, 23 Jun 2025 13:08:11 +0800 Subject: [PATCH 0003/1187] Fix: Document parse via API will alot problen (#8407) ### What problem does this PR solve? #8391 #8404 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --------- Co-authored-by: Kevin Hu --- api/utils/validation_utils.py | 2 +- rag/svr/task_executor.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/api/utils/validation_utils.py b/api/utils/validation_utils.py index d60dc556102..3c53a6332c6 100644 --- a/api/utils/validation_utils.py +++ b/api/utils/validation_utils.py @@ -371,7 +371,7 @@ class ParserConfig(Base): raptor: RaptorConfig | None = None tag_kb_ids: list[str] = Field(default_factory=list) topn_tags: int = Field(default=1, ge=1, le=10) - filename_embd_weight: float | None = Field(default=None, ge=0.0, le=1.0) + filename_embd_weight: float | None = Field(default=0.1, ge=0.0, le=1.0) task_page_size: int | None = Field(default=None, ge=1) pages: list[list[int]] | None = None diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index fbe6f134f61..e5fed0bea56 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -434,7 +434,6 @@ async def embedding(docs, mdl, parser_config=None, callback=None): tk_count += c callback(prog=0.7 + 0.2 * (i + 1) / len(cnts), msg="") cnts = cnts_ - title_w = float(parser_config.get("filename_embd_weight", 0.1)) vects = (title_w * tts + (1 - title_w) * cnts) if len(tts) == len(cnts) else cnts From 83e23f1e8a808fccbe6a3a9b73ed74e0da02df87 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Mon, 23 Jun 2025 14:10:13 +0800 Subject: [PATCH 0004/1187] Fix: rank feature score should be greater than 0. (#8416) ### What problem does this PR solve? #8414 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/prompts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rag/prompts.py b/rag/prompts.py index 389d6a66d84..66c8a4ef49e 100644 --- a/rag/prompts.py +++ b/rag/prompts.py @@ -427,7 +427,8 @@ def content_tagging(chat_mdl, content, all_tags, examples, topn=3): res = {} for k, v in obj.items(): try: - res[str(k)] = int(v) + if int(v) > 0: + res[str(k)] = int(v) except Exception: pass return res From 81a4c0698c4f2ccc13efd1f4542a1a9b3c76b55c Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 23 Jun 2025 14:36:01 +0800 Subject: [PATCH 0005/1187] Feat: Solved the conflict between the Handle click and drag events of the canvas node #3221 (#8413) ### What problem does this PR solve? Feat: Solved the conflict between the Handle click and drag events of the canvas node #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../node/dropdown/next-step-dropdown.tsx | 18 +++++++--- web/src/pages/agent/canvas/node/handle.tsx | 35 +++++++++++-------- .../pages/agent/canvas/node/message-node.tsx | 6 ++-- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx index f8ffc4cb3b3..99f33471a3d 100644 --- a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx @@ -11,16 +11,20 @@ import { DropdownMenuLabel, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; +import { IModalProps } from '@/interfaces/common'; import { Operator } from '@/pages/agent/constant'; import { AgentInstanceContext, HandleContext } from '@/pages/agent/context'; import OperatorIcon from '@/pages/agent/operator-icon'; -import { PropsWithChildren, useContext } from 'react'; +import { PropsWithChildren, createContext, useContext } from 'react'; type OperatorItemProps = { operators: Operator[] }; +const HideModalContext = createContext['showModal']>(() => {}); + function OperatorItemList({ operators }: OperatorItemProps) { const { addCanvasNode } = useContext(AgentInstanceContext); const { nodeId, id, type, position } = useContext(HandleContext); + const hideModal = useContext(HideModalContext); return (
    @@ -34,6 +38,7 @@ function OperatorItemList({ operators }: OperatorItemProps) { id, position, })} + onSelect={() => hideModal?.()} > {x} @@ -95,16 +100,21 @@ function AccordionOperators() { ); } -export function NextStepDropdown({ children }: PropsWithChildren) { +export function NextStepDropdown({ + children, + hideModal, +}: PropsWithChildren & IModalProps) { return ( - + {children} e.stopPropagation()} className="w-[300px] font-semibold" > Next Step - + + + ); diff --git a/web/src/pages/agent/canvas/node/handle.tsx b/web/src/pages/agent/canvas/node/handle.tsx index ad76a87eb63..8cd5f6bcd39 100644 --- a/web/src/pages/agent/canvas/node/handle.tsx +++ b/web/src/pages/agent/canvas/node/handle.tsx @@ -1,3 +1,4 @@ +import { useSetModalState } from '@/hooks/common-hooks'; import { cn } from '@/lib/utils'; import { Handle, HandleProps } from '@xyflow/react'; import { Plus } from 'lucide-react'; @@ -10,6 +11,8 @@ export function CommonHandle({ nodeId, ...props }: HandleProps & { nodeId: string }) { + const { visible, hideModal, showModal } = useSetModalState(); + const value = useMemo( () => ({ nodeId, @@ -22,20 +25,24 @@ export function CommonHandle({ return ( - - { - e.stopPropagation(); - }} - > - - - + { + e.stopPropagation(); + showModal(); + }} + > + + {visible && ( + + + + )} + ); } diff --git a/web/src/pages/agent/canvas/node/message-node.tsx b/web/src/pages/agent/canvas/node/message-node.tsx index 8d4c3199d71..a096acf3c94 100644 --- a/web/src/pages/agent/canvas/node/message-node.tsx +++ b/web/src/pages/agent/canvas/node/message-node.tsx @@ -6,7 +6,7 @@ import { get } from 'lodash'; import { memo } from 'react'; import { NodeHandleId } from '../../constant'; import { CommonHandle } from './handle'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import { LeftHandleStyle } from './handle-icon'; import styles from './index.less'; import NodeHeader from './node-header'; import { NodeWrapper } from './node-wrapper'; @@ -30,7 +30,7 @@ function InnerMessageNode({ nodeId={id} id={NodeHandleId.End} > - + > */} Date: Mon, 23 Jun 2025 14:54:01 +0800 Subject: [PATCH 0006/1187] Fix: doc_aggs issue. (#8418) ### What problem does this PR solve? #8406 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/nlp/search.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/rag/nlp/search.py b/rag/nlp/search.py index 855468c9e0d..ad93c220e7b 100644 --- a/rag/nlp/search.py +++ b/rag/nlp/search.py @@ -391,14 +391,20 @@ def retrieval(self, question, embd_mdl, tenant_ids, kb_ids, page, page_size, sim for i in idx: if sim[i] < similarity_threshold: break - if len(ranks["chunks"]) >= page_size: - if aggs: - continue - break + id = sres.ids[i] chunk = sres.field[id] dnm = chunk.get("docnm_kwd", "") did = chunk.get("doc_id", "") + + if len(ranks["chunks"]) >= page_size: + if aggs: + if dnm not in ranks["doc_aggs"]: + ranks["doc_aggs"][dnm] = {"doc_id": did, "count": 0} + ranks["doc_aggs"][dnm]["count"] += 1 + continue + break + position_int = chunk.get("position_int", []) d = { "chunk_id": id, From f0e078361883cc74bf44ef94016bb368d70f006b Mon Sep 17 00:00:00 2001 From: kira-offgrid Date: Mon, 23 Jun 2025 12:24:25 +0530 Subject: [PATCH 0007/1187] Fix: Database Query Vulnerable to Injection Attacks in rag/utils/opendal_conn.py (#8408) **Context and Purpose:** This PR automatically remediates a security vulnerability: - **Description:** Detected possible formatted SQL query. Use parameterized queries instead. - **Rule ID:** python.lang.security.audit.formatted-sql-query.formatted-sql-query - **Severity:** HIGH - **File:** rag/utils/opendal_conn.py - **Lines Affected:** 98 - 98 This change is necessary to protect the application from potential security risks associated with this vulnerability. **Solution Implemented:** The automated remediation process has applied the necessary changes to the affected code in `rag/utils/opendal_conn.py` to resolve the identified issue. Please review the changes to ensure they are correct and integrate as expected. --- rag/utils/opendal_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rag/utils/opendal_conn.py b/rag/utils/opendal_conn.py index 715c18d190a..7a3b44f0d6b 100644 --- a/rag/utils/opendal_conn.py +++ b/rag/utils/opendal_conn.py @@ -95,7 +95,7 @@ def init_db_config(self): ) cursor = conn.cursor() max_packet = self._kwargs.get('max_allowed_packet', 4194304) # Default to 4MB if not specified - cursor.execute(SET_MAX_ALLOWED_PACKET_SQL.format(max_packet)) + cursor.execute(SET_MAX_ALLOWED_PACKET_SQL, (max_packet,)) conn.commit() cursor.close() conn.close() From 71afebb2c04fc3b48cd9e4c71394160f5c184e98 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 23 Jun 2025 15:27:34 +0800 Subject: [PATCH 0008/1187] Feat: The delete button is displayed only when the cursor is hovered over the connection line #3221 (#8422) ### What problem does this PR solve? Feat: The delete button is displayed only when the cursor is hovered over the connection line #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/canvas/edge/index.tsx | 24 +++++++++++++++++---- web/src/pages/agent/canvas/index.tsx | 4 ++++ web/src/pages/agent/canvas/node/toolbar.tsx | 23 ++++++++++++-------- web/src/pages/agent/hooks.tsx | 2 ++ web/src/pages/agent/store.ts | 19 ++++++++++++++++ web/src/pages/agent/utils.ts | 20 +++++++++++++++++ 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/web/src/pages/agent/canvas/edge/index.tsx b/web/src/pages/agent/canvas/edge/index.tsx index 2b67cdbadd1..aa6e8f94dc0 100644 --- a/web/src/pages/agent/canvas/edge/index.tsx +++ b/web/src/pages/agent/canvas/edge/index.tsx @@ -1,5 +1,6 @@ import { BaseEdge, + Edge, EdgeLabelRenderer, EdgeProps, getBezierPath, @@ -8,7 +9,9 @@ import useGraphStore from '../../store'; import { useTheme } from '@/components/theme-provider'; import { useFetchAgent } from '@/hooks/use-agent-request'; +import { cn } from '@/lib/utils'; import { useMemo } from 'react'; +import { NodeHandleId, Operator } from '../../constant'; import styles from './index.less'; export function ButtonEdge({ @@ -24,7 +27,9 @@ export function ButtonEdge({ style = {}, markerEnd, selected, -}: EdgeProps) { + data, + sourceHandleId, +}: EdgeProps>) { const deleteEdgeById = useGraphStore((state) => state.deleteEdgeById); const [edgePath, labelX, labelY] = getBezierPath({ sourceX, @@ -72,6 +77,14 @@ export function ButtonEdge({ return {}; }, [source, target, graphPath]); + const visible = useMemo(() => { + return ( + data?.isHovered && + sourceHandleId !== NodeHandleId.Tool && // The connection between the agent node and the tool node does not need to display the delete button + !target.startsWith(Operator.Tool) + ); + }, [data?.isHovered, sourceHandleId, target]); + return ( <> +
diff --git a/web/src/pages/agent/hooks.tsx b/web/src/pages/agent/hooks.tsx index f10a72ecce8..b8344f7bf4a 100644 --- a/web/src/pages/agent/hooks.tsx +++ b/web/src/pages/agent/hooks.tsx @@ -90,6 +90,8 @@ const selector = (state: RFState) => ({ onConnect: state.onConnect, setNodes: state.setNodes, onSelectionChange: state.onSelectionChange, + onEdgeMouseEnter: state.onEdgeMouseEnter, + onEdgeMouseLeave: state.onEdgeMouseLeave, }); export const useSelectCanvasData = () => { diff --git a/web/src/pages/agent/store.ts b/web/src/pages/agent/store.ts index c49d8753d98..26bb3750eb7 100644 --- a/web/src/pages/agent/store.ts +++ b/web/src/pages/agent/store.ts @@ -4,6 +4,7 @@ import { Connection, Edge, EdgeChange, + EdgeMouseHandler, OnConnect, OnEdgesChange, OnNodesChange, @@ -27,6 +28,7 @@ import { generateNodeNamesWithIncreasingIndex, getOperatorIndex, isEdgeEqual, + mapEdgeMouseEvent, } from './utils'; export type RFState = { @@ -38,6 +40,9 @@ export type RFState = { clickedToolId: string; // currently selected tool id onNodesChange: OnNodesChange; onEdgesChange: OnEdgesChange; + onEdgeMouseEnter?: EdgeMouseHandler; + /** This event handler is called when mouse of a user leaves an edge */ + onEdgeMouseLeave?: EdgeMouseHandler; onConnect: OnConnect; setNodes: (nodes: RAGFlowNodeType[]) => void; setEdges: (edges: Edge[]) => void; @@ -98,6 +103,20 @@ const useGraphStore = create()( edges: applyEdgeChanges(changes, get().edges), }); }, + onEdgeMouseEnter: (event, edge) => { + const { edges, setEdges } = get(); + const edgeId = edge.id; + + // Updates edge + setEdges(mapEdgeMouseEvent(edges, edgeId, true)); + }, + onEdgeMouseLeave: (event, edge) => { + const { edges, setEdges } = get(); + const edgeId = edge.id; + + // Updates edge + setEdges(mapEdgeMouseEvent(edges, edgeId, false)); + }, onConnect: (connection: Connection) => { const { deletePreviousEdgeOfClassificationNode, diff --git a/web/src/pages/agent/utils.ts b/web/src/pages/agent/utils.ts index 50c6420cbe3..e86e0c39578 100644 --- a/web/src/pages/agent/utils.ts +++ b/web/src/pages/agent/utils.ts @@ -466,3 +466,23 @@ export function getAgentNodeTools(agentNode?: RAGFlowNodeType) { const tools: IAgentForm['tools'] = get(agentNode, 'data.form.tools', []); return tools; } + +export function mapEdgeMouseEvent( + edges: Edge[], + edgeId: string, + isHovered: boolean, +) { + const nextEdges = edges.map((element) => + element.id === edgeId + ? { + ...element, + data: { + ...element.data, + isHovered, + }, + } + : element, + ); + + return nextEdges; +} From 4760e317d5af94c334afb3c20bb5b51818d305ff Mon Sep 17 00:00:00 2001 From: Yesid Cano Castro <46203884+yesidc@users.noreply.github.com> Date: Mon, 23 Jun 2025 09:36:15 +0200 Subject: [PATCH 0009/1187] Feat: Add HTTPS setup instructions and configuration for Nginx (#8401) ### What problem does this PR solve? _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change: Documentation Update/Refactoring #### Summary Adds HTTPS/SSL configuration guide/example to enable secure RAGFlow deployments with proper certificate management. #### Changes - New HTTPS Setup Section: Step-by-step guide for SSL certificate configuration - Let's Encrypt Integration: Complete Certbot setup instructions - Docker Configuration: Volume mapping examples for certificates #### Key Features - Prerequisites checklist - Docker Compose configuration examples - Support for both Let's Encrypt and existing certificates #### Files Modified - `README.md` - `ragflow.https.conf` (new file) --- docker/README.md | 75 +++++++++++++++++++++++++++++++++ docker/nginx/ragflow.https.conf | 41 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 docker/nginx/ragflow.https.conf diff --git a/docker/README.md b/docker/README.md index c63d7385102..9f7fc069a50 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,6 +6,7 @@ - 🐳 [Docker Compose](#-docker-compose) - 🐬 [Docker environment variables](#-docker-environment-variables) - 🐋 [Service configuration](#-service-configuration) +- 📋 [Setup Examples](#-setup-examples) @@ -192,3 +193,77 @@ The [.env](./.env) file contains important environment variables for Docker. > [!TIP] > If you do not set the default LLM here, configure the default LLM on the **Settings** page in the RAGFlow UI. + + +## 📋 Setup Examples + +### 🔒 HTTPS Setup + +#### Prerequisites + +- A registered domain name pointing to your server +- Port 80 and 443 open on your server +- Docker and Docker Compose installed + +#### Getting and configuring certificates (Let's Encrypt) + +If you want your instance to be available under `https`, follow these steps: + +1. **Install Certbot and obtain certificates** + ```bash + # Ubuntu/Debian + sudo apt update && sudo apt install certbot + + # CentOS/RHEL + sudo yum install certbot + + # Obtain certificates (replace with your actual domain) + sudo certbot certonly --standalone -d your-ragflow-domain.com + ``` + +2. **Locate your certificates** + Once generated, your certificates will be located at: + - Certificate: `/etc/letsencrypt/live/your-ragflow-domain.com/fullchain.pem` + - Private key: `/etc/letsencrypt/live/your-ragflow-domain.com/privkey.pem` + +3. **Update docker-compose.yml** + Add the certificate volumes to the `ragflow` service in your `docker-compose.yml`: + ```yaml + services: + ragflow: + # ...existing configuration... + volumes: + # SSL certificates + - /etc/letsencrypt/live/your-ragflow-domain.com/fullchain.pem:/etc/nginx/ssl/fullchain.pem:ro + - /etc/letsencrypt/live/your-ragflow-domain.com/privkey.pem:/etc/nginx/ssl/privkey.pem:ro + # Switch to HTTPS nginx configuration + - ./nginx/ragflow.https.conf:/etc/nginx/conf.d/ragflow.conf + # ...other existing volumes... + + ``` + +4. **Update nginx configuration** + Edit `nginx/ragflow.https.conf` and replace `my_ragflow_domain.com` with your actual domain name. + +5. **Restart the services** + ```bash + docker-compose down + docker-compose up -d + ``` + + +> [!IMPORTANT] +> - Ensure your domain's DNS A record points to your server's IP address +> - Stop any services running on ports 80/443 before obtaining certificates with `--standalone` + +> [!TIP] +> For development or testing, you can use self-signed certificates, but browsers will show security warnings. + +#### Alternative: Using existing certificates + +If you already have SSL certificates from another provider: + +1. Place your certificates in a directory accessible to Docker +2. Update the volume paths in `docker-compose.yml` to point to your certificate files +3. Ensure the certificate file contains the full certificate chain +4. Follow steps 4-5 from the Let's Encrypt guide above \ No newline at end of file diff --git a/docker/nginx/ragflow.https.conf b/docker/nginx/ragflow.https.conf new file mode 100644 index 00000000000..69aa3885f32 --- /dev/null +++ b/docker/nginx/ragflow.https.conf @@ -0,0 +1,41 @@ +server { + listen 80; + server_name your-ragflow-domain.com; + return 301 https://$host$request_uri; +} + + + +server { + listen 443 ssl; + server_name your-ragflow-domain.com; + + ssl_certificate /etc/nginx/ssl/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/privkey.pem; + + root /ragflow/web/dist; + + gzip on; + gzip_min_length 1k; + gzip_comp_level 9; + gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; + gzip_vary on; + gzip_disable "MSIE [1-6]\."; + + location ~ ^/(v1|api) { + proxy_pass http://ragflow:9380; + include proxy.conf; + } + + + location / { + index index.html; + try_files $uri $uri/ /index.html; + } + + # Cache-Control: max-age~@~AExpires + location ~ ^/static/(css|js|media)/ { + expires 10y; + access_log off; + } +} From 244d8a47b9c60909b6f991d09963e2024fba26df Mon Sep 17 00:00:00 2001 From: Liu An Date: Mon, 23 Jun 2025 15:59:25 +0800 Subject: [PATCH 0010/1187] Fix: AzureChat model code (#8426) ### What problem does this PR solve? - Simplify AzureChat constructor by passing base_url directly - Clean up spacing and formatting in chat_model.py - Remove redundant parentheses and improve code consistency - #8423 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/chat_model.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index fbef3478180..b1235ba6201 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -157,9 +157,9 @@ def chat_with_tools(self, system: str, history: list, gen_conf: dict): tk_count = 0 hist = deepcopy(history) # Implement exponential backoff retry strategy - for attempt in range(self.max_retries+1): + for attempt in range(self.max_retries + 1): history = hist - for _ in range(self.max_rounds*2): + for _ in range(self.max_rounds * 2): try: response = self.client.chat.completions.create(model=self.model_name, messages=history, tools=self.tools, **gen_conf) tk_count += self.total_token_count(response) @@ -185,7 +185,6 @@ def chat_with_tools(self, system: str, history: list, gen_conf: dict): except Exception as e: history.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"Tool call error: \n{tool_call}\nException:\n" + str(e)}) - except Exception as e: e = self._exceptions(e, attempt) if e: @@ -198,7 +197,7 @@ def chat(self, system, history, gen_conf): gen_conf = self._clean_conf(gen_conf) # Implement exponential backoff retry strategy - for attempt in range(self.max_retries+1): + for attempt in range(self.max_retries + 1): try: return self._chat(history, gen_conf) except Exception as e: @@ -232,9 +231,9 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): total_tokens = 0 hist = deepcopy(history) # Implement exponential backoff retry strategy - for attempt in range(self.max_retries+1): + for attempt in range(self.max_retries + 1): history = hist - for _ in range(self.max_rounds*2): + for _ in range(self.max_rounds * 2): reasoning_start = False try: response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, tools=tools, **gen_conf) @@ -453,11 +452,11 @@ def __init__(self, key, model_name="deepseek-chat", base_url="https://api.deepse class AzureChat(Base): - def __init__(self, key, model_name, **kwargs): + def __init__(self, key, model_name, base_url, **kwargs): api_key = json.loads(key).get("api_key", "") api_version = json.loads(key).get("api_version", "2024-02-01") - super().__init__(key, model_name, kwargs["base_url"], **kwargs) - self.client = AzureOpenAI(api_key=api_key, azure_endpoint=kwargs["base_url"], api_version=api_version) + super().__init__(key, model_name, base_url, **kwargs) + self.client = AzureOpenAI(api_key=api_key, azure_endpoint=base_url, api_version=api_version) self.model_name = model_name @@ -925,10 +924,10 @@ def __init__(self, key, model_name, base_url=None, **kwargs): class LocalLLM(Base): - def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) from jina import Client + self.client = Client(port=12345, protocol="grpc", asyncio=True) def _prepare_prompt(self, system, history, gen_conf): @@ -985,13 +984,7 @@ def __init__(self, key, model_name, base_url="https://ark.cn-beijing.volces.com/ class MiniMaxChat(Base): - def __init__( - self, - key, - model_name, - base_url="https://api.minimax.chat/v1/text/chatcompletion_v2", - **kwargs - ): + def __init__(self, key, model_name, base_url="https://api.minimax.chat/v1/text/chatcompletion_v2", **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) if not base_url: @@ -1223,6 +1216,7 @@ def _clean_conf(self, gen_conf): def _chat(self, history, gen_conf): from google.generativeai.types import content_types + system = history[0]["content"] if history and history[0]["role"] == "system" else "" hist = [] for item in history: @@ -1880,4 +1874,4 @@ def __init__(self, key=None, model_name="", base_url="", **kwargs): if not base_url: raise ValueError("Local llm url cannot be None") base_url = urljoin(base_url, "v1") - super().__init__(key, model_name, base_url, **kwargs) \ No newline at end of file + super().__init__(key, model_name, base_url, **kwargs) From 0427eebe94fa384aaa0c46423640a952f03efd68 Mon Sep 17 00:00:00 2001 From: Jason <30767508+jason-this@users.noreply.github.com> Date: Mon, 23 Jun 2025 16:00:14 +0800 Subject: [PATCH 0011/1187] Update .env ,Defaults to the v0.19.1-slim edition (#8412) ### What problem does this PR solve? Update .env ,Defaults to the v0.19.1-slim edition ### Type of change - [x] Other (please describe): Update .env ,Defaults to the v0.19.1-slim edition --- docker/.env | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/.env b/docker/.env index 75bda17c004..a5038b29abc 100644 --- a/docker/.env +++ b/docker/.env @@ -91,13 +91,13 @@ REDIS_PASSWORD=infini_rag_flow SVR_HTTP_PORT=9380 # The RAGFlow Docker image to download. -# Defaults to the v0.19.0-slim edition, which is the RAGFlow Docker image without embedding models. -RAGFLOW_IMAGE=infiniflow/ragflow:v0.19.0-slim +# Defaults to the v0.19.1-slim edition, which is the RAGFlow Docker image without embedding models. +RAGFLOW_IMAGE=infiniflow/ragflow:v0.19.1-slim # # To download the RAGFlow Docker image with embedding models, uncomment the following line instead: -# RAGFLOW_IMAGE=infiniflow/ragflow:v0.19.0 +# RAGFLOW_IMAGE=infiniflow/ragflow:v0.19.1 # -# The Docker image of the v0.19.0 edition includes built-in embedding models: +# The Docker image of the v0.19.1 edition includes built-in embedding models: # - BAAI/bge-large-zh-v1.5 # - maidalun1020/bce-embedding-base_v1 # From 03656da4dd8549904a102fb833b98cead88354b8 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Mon, 23 Jun 2025 16:53:59 +0800 Subject: [PATCH 0012/1187] Refa: upgrade MCP SDK to v1.9.4 (#8421) ### What problem does this PR solve? Upgrade MCP SDK to v1.9.4 (latest). ### Type of change - [x] Refactoring --- docker/entrypoint.sh | 4 +- docs/develop/mcp/launch_mcp_server.md | 8 +- mcp/server/server.py | 121 ++++++++++++++------------ pyproject.toml | 3 +- uv.lock | 22 +++-- 5 files changed, 90 insertions(+), 68 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 53868daaeb7..d4065c6fad7 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -148,9 +148,9 @@ function start_mcp_server() { "$PY" "${MCP_SCRIPT_PATH}" \ --host="${MCP_HOST}" \ --port="${MCP_PORT}" \ - --base_url="${MCP_BASE_URL}" \ + --base-url="${MCP_BASE_URL}" \ --mode="${MCP_MODE}" \ - --api_key="${MCP_HOST_API_KEY}" & + --api-key="${MCP_HOST_API_KEY}" & } # ----------------------------------------------------------------------------- diff --git a/docs/develop/mcp/launch_mcp_server.md b/docs/develop/mcp/launch_mcp_server.md index a98939efb4e..8388ce56c70 100644 --- a/docs/develop/mcp/launch_mcp_server.md +++ b/docs/develop/mcp/launch_mcp_server.md @@ -41,11 +41,11 @@ You can start an MCP server either from source code or via Docker. ```bash # Launch the MCP server to work in self-host mode, run either of the following -uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --api_key=ragflow-xxxxx -# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=self-host --api_key=ragflow-xxxxx +uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --api-key=ragflow-xxxxx +# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=self-host --api-key=ragflow-xxxxx # To launch the MCP server to work in host mode, run the following instead: -# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=host +# uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=host ``` Where: @@ -194,4 +194,4 @@ The use of an API key depends on the operating mode of your MCP server. - If launching from source, include the API key in the command. - If launching from Docker, update the API key in **docker/docker-compose.yml**. - **Host mode**: - If your RAGFlow MCP server is working in host mode, include the API key in the `headers` of your client requests to authenticate your client with the RAGFlow server. An example is available [here](https://github.com/infiniflow/ragflow/blob/main/mcp/client/client.py). \ No newline at end of file + If your RAGFlow MCP server is working in host mode, include the API key in the `headers` of your client requests to authenticate your client with the RAGFlow server. An example is available [here](https://github.com/infiniflow/ragflow/blob/main/mcp/client/client.py). diff --git a/mcp/server/server.py b/mcp/server/server.py index 743cd16f94a..899cbca4992 100644 --- a/mcp/server/server.py +++ b/mcp/server/server.py @@ -19,11 +19,11 @@ from contextlib import asynccontextmanager from functools import wraps +import click import requests from starlette.applications import Starlette from starlette.middleware import Middleware -from starlette.middleware.base import BaseHTTPMiddleware -from starlette.responses import JSONResponse +from starlette.responses import JSONResponse, Response from starlette.routing import Mount, Route from strenum import StrEnum @@ -209,50 +209,64 @@ async def call_tool(name: str, arguments: dict, *, connector) -> list[types.Text async def handle_sse(request): async with sse.connect_sse(request.scope, request.receive, request._send) as streams: await app.run(streams[0], streams[1], app.create_initialization_options(experimental_capabilities={"headers": dict(request.headers)})) - - -class AuthMiddleware(BaseHTTPMiddleware): - async def dispatch(self, request, call_next): - # Authentication is deferred, will be handled by RAGFlow core service. - if request.url.path.startswith("/sse") or request.url.path.startswith("/messages"): - token = None - - auth_header = request.headers.get("Authorization") - if auth_header and auth_header.startswith("Bearer "): - token = auth_header.removeprefix("Bearer ").strip() - elif request.headers.get("api_key"): - token = request.headers["api_key"] - - if not token: - return JSONResponse({"error": "Missing or invalid authorization header"}, status_code=401) - return await call_next(request) + return Response() def create_starlette_app(): middleware = None if MODE == LaunchMode.HOST: + from starlette.types import ASGIApp, Receive, Scope, Send + + class AuthMiddleware: + def __init__(self, app: ASGIApp): + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send): + if scope["type"] != "http": + await self.app(scope, receive, send) + return + + path = scope["path"] + if path.startswith("/messages/") or path.startswith("/sse"): + headers = dict(scope["headers"]) + token = None + auth_header = headers.get(b"authorization") + if auth_header and auth_header.startswith(b"Bearer "): + token = auth_header.removeprefix(b"Bearer ").strip() + elif b"api_key" in headers: + token = headers[b"api_key"] + + if not token: + response = JSONResponse({"error": "Missing or invalid authorization header"}, status_code=401) + await response(scope, receive, send) + return + + await self.app(scope, receive, send) + middleware = [Middleware(AuthMiddleware)] return Starlette( debug=True, routes=[ - Route("/sse", endpoint=handle_sse), + Route("/sse", endpoint=handle_sse, methods=["GET"]), Mount("/messages/", app=sse.handle_post_message), ], middleware=middleware, ) -if __name__ == "__main__": - """ - Launch example: - self-host: - uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=self-host --api_key=ragflow-xxxxx - host: - uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=host - """ - - import argparse +@click.command() +@click.option("--base-url", type=str, default="http://127.0.0.1:9380", help="API base URL for RAGFlow backend") +@click.option("--host", type=str, default="127.0.0.1", help="Host to bind the RAGFlow MCP server") +@click.option("--port", type=int, default=9382, help="Port to bind the RAGFlow MCP server") +@click.option( + "--mode", + type=click.Choice(["self-host", "host"]), + default="self-host", + help=("Launch mode:\n self-host: run MCP for a single tenant (requires --api-key)\n host: multi-tenant mode, users must provide Authorization headers"), +) +@click.option("--api-key", type=str, default="", help="API key to use when in self-host mode") +def main(base_url, host, port, mode, api_key): import os import uvicorn @@ -260,31 +274,15 @@ def create_starlette_app(): load_dotenv() - parser = argparse.ArgumentParser(description="RAGFlow MCP Server") - parser.add_argument("--base_url", type=str, default="http://127.0.0.1:9380", help="api_url: http://") - parser.add_argument("--host", type=str, default="127.0.0.1", help="RAGFlow MCP SERVER host") - parser.add_argument("--port", type=str, default="9382", help="RAGFlow MCP SERVER port") - parser.add_argument( - "--mode", - type=str, - default="self-host", - help="Launch mode options:\n" - " * self-host: Launches an MCP server to access a specific tenant space. The 'api_key' argument is required.\n" - " * host: Launches an MCP server that allows users to access their own spaces. Each request must include a Authorization header " - "indicating the user's identification.", - ) - parser.add_argument("--api_key", type=str, default="", help="RAGFlow MCP SERVER HOST API KEY") - args = parser.parse_args() - if args.mode not in ["self-host", "host"]: - parser.error("--mode is only accept 'self-host' or 'host'") - if args.mode == "self-host" and not args.api_key: - parser.error("--api_key is required when --mode is 'self-host'") - - BASE_URL = os.environ.get("RAGFLOW_MCP_BASE_URL", args.base_url) - HOST = os.environ.get("RAGFLOW_MCP_HOST", args.host) - PORT = os.environ.get("RAGFLOW_MCP_PORT", args.port) - MODE = os.environ.get("RAGFLOW_MCP_LAUNCH_MODE", args.mode) - HOST_API_KEY = os.environ.get("RAGFLOW_MCP_HOST_API_KEY", args.api_key) + global BASE_URL, HOST, PORT, MODE, HOST_API_KEY + BASE_URL = os.environ.get("RAGFLOW_MCP_BASE_URL", base_url) + HOST = os.environ.get("RAGFLOW_MCP_HOST", host) + PORT = os.environ.get("RAGFLOW_MCP_PORT", str(port)) + MODE = os.environ.get("RAGFLOW_MCP_LAUNCH_MODE", mode) + HOST_API_KEY = os.environ.get("RAGFLOW_MCP_HOST_API_KEY", api_key) + + if MODE == "self-host" and not HOST_API_KEY: + raise click.UsageError("--api-key is required when --mode is 'self-host'") print( r""" @@ -293,7 +291,7 @@ def create_starlette_app(): | |\/| | | | |_) | \___ \| _| | |_) \ \ / /| _| | |_) | | | | | |___| __/ ___) | |___| _ < \ V / | |___| _ < |_| |_|\____|_| |____/|_____|_| \_\ \_/ |_____|_| \_\ - """, + """, flush=True, ) print(f"MCP launch mode: {MODE}", flush=True) @@ -306,3 +304,14 @@ def create_starlette_app(): host=HOST, port=int(PORT), ) + + +if __name__ == "__main__": + """ + Launch example: + self-host: + uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=self-host --api-key=ragflow-xxxxx + host: + uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=host + """ + main() diff --git a/pyproject.toml b/pyproject.toml index ded458b98b4..7f2fa17771e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,9 +126,10 @@ dependencies = [ "trio>=0.29.0", "langfuse>=2.60.0", "debugpy>=1.8.13", - "mcp>=1.6.0", + "mcp>=1.9.4", "opensearch-py==2.7.1", "pluginlib==0.9.4", + "click>=8.1.8", ] [project.optional-dependencies] diff --git a/uv.lock b/uv.lock index 5cf632156c6..cd5efce0ec7 100644 --- a/uv.lock +++ b/uv.lock @@ -3010,7 +3010,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.6.0" +version = "1.9.4" source = { registry = "https://mirrors.aliyun.com/pypi/simple" } dependencies = [ { name = "anyio" }, @@ -3018,13 +3018,14 @@ dependencies = [ { name = "httpx-sse" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "python-multipart" }, { name = "sse-starlette" }, { name = "starlette" }, - { name = "uvicorn" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/f2/dc2450e566eeccf92d89a00c3e813234ad58e2ba1e31d11467a09ac4f3b9/mcp-1.9.4.tar.gz", hash = "sha256:cfb0bcd1a9535b42edaef89947b9e18a8feb49362e1cc059d6e7fc636f2cb09f" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0" }, + { url = "https://mirrors.aliyun.com/pypi/packages/97/fc/80e655c955137393c443842ffcc4feccab5b12fa7cb8de9ced90f90e6998/mcp-1.9.4-py3-none-any.whl", hash = "sha256:7fcf36b62936adb8e63f89346bccca1268eeca9bf6dfb562ee10b1dfbda9dac0" }, ] [[package]] @@ -4748,6 +4749,15 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" }, ] +[[package]] +name = "python-multipart" +version = "0.0.20" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104" }, +] + [[package]] name = "python-pptx" version = "1.0.2" @@ -4868,6 +4878,7 @@ dependencies = [ { name = "botocore" }, { name = "cachetools" }, { name = "chardet" }, + { name = "click" }, { name = "cn2an" }, { name = "cohere" }, { name = "crawl4ai" }, @@ -5016,6 +5027,7 @@ requires-dist = [ { name = "botocore", specifier = "==1.34.140" }, { name = "cachetools", specifier = "==5.3.3" }, { name = "chardet", specifier = "==5.2.0" }, + { name = "click", specifier = ">=8.1.8" }, { name = "cn2an", specifier = "==0.5.22" }, { name = "cohere", specifier = "==5.6.2" }, { name = "crawl4ai", specifier = "==0.3.8" }, @@ -5054,7 +5066,7 @@ requires-dist = [ { name = "langfuse", specifier = ">=2.60.0" }, { name = "markdown", specifier = "==3.6" }, { name = "markdown-to-json", specifier = "==2.1.1" }, - { name = "mcp", specifier = ">=1.6.0" }, + { name = "mcp", specifier = ">=1.9.4" }, { name = "mini-racer", specifier = ">=0.12.4,<0.13.0" }, { name = "minio", specifier = "==7.2.4" }, { name = "mistralai", specifier = "==0.4.2" }, From e9c6891e2497acb33a4599084311aac3e669e353 Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:45:20 +0800 Subject: [PATCH 0013/1187] Docs: Miscellaneous editorial updates (#8430) ### What problem does this PR solve? ### Type of change - [x] Documentation Update --- docs/guides/upgrade_ragflow.mdx | 4 ++++ docs/release_notes.md | 1 + 2 files changed, 5 insertions(+) diff --git a/docs/guides/upgrade_ragflow.mdx b/docs/guides/upgrade_ragflow.mdx index 7eb8b88f7e6..b9d95a39d4c 100644 --- a/docs/guides/upgrade_ragflow.mdx +++ b/docs/guides/upgrade_ragflow.mdx @@ -87,6 +87,10 @@ To upgrade RAGFlow, you must upgrade **both** your code **and** your Docker imag ## Frequently asked questions +### Do I need to back up my knowledge bases before upgrading RAGFlow? + +No, you do not need to. Upgrading RAGFlow in itself will *not* remove your uploaded data or knowledge base settings. However, be aware that `docker compose -f docker/docker-compose.yml down -v` will remove Docker container volumes, resulting in data loss. + ### Upgrade RAGFlow in an offline environment (without Internet access) 1. From an environment with Internet access, pull the required Docker image. diff --git a/docs/release_notes.md b/docs/release_notes.md index 58face4cd10..412e0ce1820 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -29,6 +29,7 @@ Released on June 23, 2025. ### Fixed issues - A memory leak issue during high-concurrency requests. +- Large file parsing freezes when GraphRAG entity resolution is enabled. [#8223](https://github.com/infiniflow/ragflow/pull/8223) - A context error occurring when using Sandbox in standalone mode. [#8340](https://github.com/infiniflow/ragflow/pull/8340) - An excessive CPU usage issue caused by Ollama. [#8216](https://github.com/infiniflow/ragflow/pull/8216) - A bug in the Code Component. [#7949](https://github.com/infiniflow/ragflow/pull/7949) From fd7ac176057bcea7e7caaa22d4317c01cb75ea45 Mon Sep 17 00:00:00 2001 From: Song Fuchang Date: Mon, 23 Jun 2025 17:45:35 +0800 Subject: [PATCH 0014/1187] Feat: Scratch MCP tool calling support. (#8263) ### What problem does this PR solve? This is a cherry-pick from #7781 as requested. ### Type of change - [x] New Feature (non-breaking change which adds functionality) Co-authored-by: Kevin Hu --- Dockerfile | 1 + Dockerfile.scratch.oc9 | 1 + api/apps/mcp_server_app.py | 107 ++++++++++++++++ api/db/__init__.py | 5 + api/db/db_models.py | 18 +++ api/db/services/mcp_server_service.py | 61 +++++++++ mcp/server/simple_tools_server.py | 23 ++++ mcp_client/__init__.py | 2 + mcp_client/mcp_tool_call.py | 145 ++++++++++++++++++++++ rag/llm/chat_model.py | 19 ++- uv.lock | 2 +- web/src/interfaces/database/mcp-server.ts | 19 +++ web/src/services/mcp-server-service.ts | 41 ++++++ web/src/utils/api.ts | 8 ++ 14 files changed, 445 insertions(+), 7 deletions(-) create mode 100644 api/apps/mcp_server_app.py create mode 100644 api/db/services/mcp_server_service.py create mode 100644 mcp/server/simple_tools_server.py create mode 100644 mcp_client/__init__.py create mode 100644 mcp_client/mcp_tool_call.py create mode 100644 web/src/interfaces/database/mcp-server.ts create mode 100644 web/src/services/mcp-server-service.ts diff --git a/Dockerfile b/Dockerfile index 67fd2645682..0f0727b63b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -200,6 +200,7 @@ COPY graphrag graphrag COPY agentic_reasoning agentic_reasoning COPY pyproject.toml uv.lock ./ COPY mcp mcp +COPY mcp_client mcp_client COPY plugin plugin COPY docker/service_conf.yaml.template ./conf/service_conf.yaml.template diff --git a/Dockerfile.scratch.oc9 b/Dockerfile.scratch.oc9 index 64424735e81..2403eae167e 100644 --- a/Dockerfile.scratch.oc9 +++ b/Dockerfile.scratch.oc9 @@ -33,6 +33,7 @@ ADD ./rag ./rag ADD ./requirements.txt ./requirements.txt ADD ./agent ./agent ADD ./graphrag ./graphrag +ADD ./mcp_client ./mcp_client ADD ./plugin ./plugin RUN dnf install -y openmpi openmpi-devel python3-openmpi diff --git a/api/apps/mcp_server_app.py b/api/apps/mcp_server_app.py new file mode 100644 index 00000000000..188756167f9 --- /dev/null +++ b/api/apps/mcp_server_app.py @@ -0,0 +1,107 @@ +from flask import Response, request +from flask_login import current_user, login_required +from api.db.db_models import MCPServer +from api.db.services.mcp_server_service import MCPServerService +from api.db.services.user_service import TenantService +from api.settings import RetCode +from api.utils import get_uuid +from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request + + +@manager.route("/list", methods=["GET"]) # noqa: F821 +@login_required +def get_list() -> Response: + try: + return get_json_result(data=MCPServerService.get_servers(current_user.id) or []) + except Exception as e: + return server_error_response(e) + + +@manager.route("/get_multiple", methods=["POST"]) # noqa: F821 +@login_required +@validate_request("id_list") +def get_multiple() -> Response: + req = request.json + + try: + return get_json_result(data=MCPServerService.get_servers(current_user.id, id_list=req["id_list"]) or []) + except Exception as e: + return server_error_response(e) + + +@manager.route("/get/", methods=["GET"]) # noqa: F821 +@login_required +def get(ms_id: str) -> Response: + try: + mcp_server = MCPServerService.get_or_none(id=ms_id, tenant_id=current_user.id) + + if mcp_server is None: + return get_json_result(code=RetCode.NOT_FOUND, data=None) + + return get_json_result(data=mcp_server.to_dict()) + except Exception as e: + return server_error_response(e) + + +@manager.route("/create", methods=["POST"]) # noqa: F821 +@login_required +@validate_request("name", "url", "server_type") +def create() -> Response: + req = request.json + + try: + req["id"] = get_uuid() + req["tenant_id"] = current_user.id + + e, _ = TenantService.get_by_id(current_user.id) + + if not e: + return get_data_error_result(message="Tenant not found.") + + if not req.get("headers"): + req["headers"] = {} + + if not MCPServerService.insert(**req): + return get_data_error_result() + + return get_json_result(data={"id": req["id"]}) + except Exception as e: + return server_error_response(e) + + +@manager.route("/update", methods=["POST"]) # noqa: F821 +@login_required +@validate_request("id", "name", "url", "server_type") +def update() -> Response: + req = request.json + + if not req.get("headers"): + req["headers"] = {} + + try: + req["tenant_id"] = current_user.id + + if not MCPServerService.filter_update([MCPServer.id == req["id"], MCPServer.tenant_id == req["tenant_id"]], req): + return get_data_error_result() + + return get_json_result(data={"id": req["id"]}) + except Exception as e: + return server_error_response(e) + + +@manager.route("/rm", methods=["POST"]) # noqa: F821 +@login_required +@validate_request("id") +def rm() -> Response: + req = request.json + ms_id = req["id"] + + try: + req["tenant_id"] = current_user.id + + if not MCPServerService.filter_delete([MCPServer.id == ms_id, MCPServer.tenant_id == req["tenant_id"]]): + return get_data_error_result() + + return get_json_result(data={"id": req["id"]}) + except Exception as e: + return server_error_response(e) diff --git a/api/db/__init__.py b/api/db/__init__.py index a8c85ef4c0b..54cc9853396 100644 --- a/api/db/__init__.py +++ b/api/db/__init__.py @@ -104,4 +104,9 @@ class CanvasType(StrEnum): ChatBot = "chatbot" DocBot = "docbot" + +class MCPServerType(StrEnum): + SSE = "sse" + StreamableHttp = "streamable-http" + KNOWLEDGEBASE_FOLDER_NAME=".knowledgebase" diff --git a/api/db/db_models.py b/api/db/db_models.py index 3ccfbdba392..839203a5f56 100644 --- a/api/db/db_models.py +++ b/api/db/db_models.py @@ -799,6 +799,20 @@ class Meta: db_table = "user_canvas_version" +class MCPServer(DataBaseModel): + id = CharField(max_length=32, primary_key=True) + name = CharField(max_length=255, null=False, help_text="MCP Server name") + tenant_id = CharField(max_length=32, null=False, index=True) + url = CharField(max_length=2048, null=False, help_text="MCP Server URL") + server_type = CharField(max_length=32, null=False, help_text="MCP Server type") + description = TextField(null=True, help_text="MCP Server description") + variables = JSONField(null=True, default=[], help_text="MCP Server variables") + headers = JSONField(null=True, default={}, help_text="MCP Server additional request headers") + + class Meta: + db_table = "mcp_server" + + class Search(DataBaseModel): id = CharField(max_length=32, primary_key=True) avatar = TextField(null=True, help_text="avatar base64 string") @@ -934,3 +948,7 @@ def migrate_db(): migrate(migrator.add_column("llm", "is_tools", BooleanField(null=False, help_text="support tools", default=False))) except Exception: pass + try: + migrate(migrator.add_column("mcp_server", "variables", JSONField(null=True, help_text="MCP Server variables", default=[]))) + except Exception: + pass diff --git a/api/db/services/mcp_server_service.py b/api/db/services/mcp_server_service.py new file mode 100644 index 00000000000..43bc75f6c92 --- /dev/null +++ b/api/db/services/mcp_server_service.py @@ -0,0 +1,61 @@ +# +# 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. +# +from api.db.db_models import DB, MCPServer +from api.db.services.common_service import CommonService + + +class MCPServerService(CommonService): + """Service class for managing MCP server related database operations. + + This class extends CommonService to provide specialized functionality for MCP server management, + including MCP server creation, updates, and deletions. + + Attributes: + model: The MCPServer model class for database operations. + """ + + model = MCPServer + + @classmethod + @DB.connection_context() + def get_servers(cls, tenant_id: str, id_list: list[str] | None = None): + """Retrieve all MCP servers associated with a tenant. + + This method fetches all MCP servers for a given tenant, ordered by creation time. + It only includes fields for list display. + + Args: + tenant_id (str): The unique identifier of the tenant. + id_list (list[str]): Get servers by ID list. Will ignore this condition if None. + + Returns: + list[dict]: List of MCP server dictionaries containing MCP server details. + Returns None if no MCP servers are found. + """ + fields = [ + cls.model.id, cls.model.name, cls.model.server_type, cls.model.url, cls.model.description, + cls.model.variables, cls.model.update_date + ] + + servers = cls.model.select(*fields).order_by(cls.model.create_time.desc()).where(cls.model.tenant_id == tenant_id) + + if id_list is not None: + servers = servers.where(cls.model.id.in_(id_list)) + + servers = list(servers.dicts()) + if not servers: + return None + return servers diff --git a/mcp/server/simple_tools_server.py b/mcp/server/simple_tools_server.py new file mode 100644 index 00000000000..f5f9a5257fa --- /dev/null +++ b/mcp/server/simple_tools_server.py @@ -0,0 +1,23 @@ +from mcp.server import FastMCP + + +app = FastMCP("simple-tools", port=8080) + + +@app.tool() +async def bad_calculator(a: int, b: int) -> str: + """ + A calculator to sum up two numbers (will give wrong answer) + + Args: + a: The first number + b: The second number + + Returns: + Sum of a and b + """ + return str(a + b + 200) + + +if __name__ == "__main__": + app.run(transport="sse") diff --git a/mcp_client/__init__.py b/mcp_client/__init__.py new file mode 100644 index 00000000000..a0712585345 --- /dev/null +++ b/mcp_client/__init__.py @@ -0,0 +1,2 @@ +# ruff: noqa: F401 +from .mcp_tool_call import MCPToolCallSession, mcp_tool_metadata_to_openai_tool, close_multiple_mcp_toolcall_sessions diff --git a/mcp_client/mcp_tool_call.py b/mcp_client/mcp_tool_call.py new file mode 100644 index 00000000000..e0a7cf192db --- /dev/null +++ b/mcp_client/mcp_tool_call.py @@ -0,0 +1,145 @@ +import asyncio +from concurrent.futures import ThreadPoolExecutor +import logging +from string import Template +from typing import Any, Literal +from typing_extensions import override + +from mcp.client.session import ClientSession +from mcp.client.sse import sse_client +from mcp.client.streamable_http import streamablehttp_client +from mcp.types import CallToolResult, ListToolsResult, TextContent, Tool + +from api.db import MCPServerType +from rag.llm.chat_model import ToolCallSession + + +MCPTaskType = Literal["list_tools", "tool_call", "stop"] +MCPTask = tuple[MCPTaskType, dict[str, Any], asyncio.Queue[Any]] + + +class MCPToolCallSession(ToolCallSession): + _EVENT_LOOP = asyncio.new_event_loop() + _THREAD_POOL = ThreadPoolExecutor(max_workers=1) + + _mcp_server: Any + _server_variables: dict[str, Any] + _queue: asyncio.Queue[MCPTask] + _stop = False + + @classmethod + def _init_thread_pool(cls) -> None: + cls._THREAD_POOL.submit(cls._EVENT_LOOP.run_forever) + + def __init__(self, mcp_server: Any, server_variables: dict[str, Any] | None = None) -> None: + self._mcp_server = mcp_server + self._server_variables = server_variables or {} + self._queue = asyncio.Queue() + + asyncio.run_coroutine_threadsafe(self._mcp_server_loop(), MCPToolCallSession._EVENT_LOOP) + + async def _mcp_server_loop(self) -> None: + url = self._mcp_server.url + raw_headers: dict[str, str] = self._mcp_server.headers or {} + headers: dict[str, str] = {} + + for h, v in raw_headers.items(): + nh = Template(h).safe_substitute(self._server_variables) + nv = Template(v).safe_substitute(self._server_variables) + headers[nh] = nv + + _streams_source: Any + + if self._mcp_server.server_type == MCPServerType.SSE: + _streams_source = sse_client(url, headers) + elif self._mcp_server.server_type == MCPServerType.StreamableHttp: + _streams_source = streamablehttp_client(url, headers) + else: + raise ValueError(f"Unsupported MCP server type {self._mcp_server.server_type} id {self._mcp_server.id}") + + async with _streams_source as streams: + async with ClientSession(*streams) as client_session: + await client_session.initialize() + + while not self._stop: + mcp_task, arguments, result_queue = await self._queue.get() + logging.debug(f"Got MCP task {mcp_task} arguments {arguments}") + + r: Any + + try: + if mcp_task == "list_tools": + r = await client_session.list_tools() + elif mcp_task == "tool_call": + r = await client_session.call_tool(**arguments) + elif mcp_task == "stop": + logging.debug(f"Shutting down MCPToolCallSession for server {self._mcp_server.id}") + self._stop = True + continue + else: + r = ValueError(f"MCPToolCallSession for server {self._mcp_server.id} received an unknown task {mcp_task}") + except Exception as e: + r = e + + await result_queue.put(r) + + async def _call_mcp_server(self, task_type: MCPTaskType, **kwargs) -> Any: + results = asyncio.Queue() + await self._queue.put((task_type, kwargs, results)) + result: CallToolResult | Exception = await results.get() + + if isinstance(result, Exception): + raise result + + return result + + async def _call_mcp_tool(self, name: str, arguments: dict[str, Any]) -> str: + result: CallToolResult = await self._call_mcp_server("tool_call", name=name, arguments=arguments) + + if result.isError: + return f"MCP server error: {result.content}" + + # For now we only support text content + if isinstance(result.content[0], TextContent): + return result.content[0].text + else: + return f"Unsupported content type {type(result.content)}" + + async def _get_tools_from_mcp_server(self) -> list[Tool]: + # For now we only fetch the first page of tools + result: ListToolsResult = await self._call_mcp_server("list_tools") + return result.tools + + def get_tools(self) -> list[Tool]: + return asyncio.run_coroutine_threadsafe(self._get_tools_from_mcp_server(), MCPToolCallSession._EVENT_LOOP).result() + + @override + def tool_call(self, name: str, arguments: dict[str, Any]) -> str: + return asyncio.run_coroutine_threadsafe(self._call_mcp_tool(name, arguments), MCPToolCallSession._EVENT_LOOP).result() + + async def close(self) -> None: + await self._call_mcp_server("stop") + + def close_sync(self) -> None: + asyncio.run_coroutine_threadsafe(self.close(), MCPToolCallSession._EVENT_LOOP).result() + + +MCPToolCallSession._init_thread_pool() + + +def close_multiple_mcp_toolcall_sessions(sessions: list[MCPToolCallSession]) -> None: + async def _gather() -> None: + await asyncio.gather(*[s.close() for s in sessions], return_exceptions=True) + + asyncio.run_coroutine_threadsafe(_gather(), MCPToolCallSession._EVENT_LOOP).result() + + +def mcp_tool_metadata_to_openai_tool(mcp_tool: Tool) -> dict[str, Any]: + return { + "type": "function", + "function": { + "name": mcp_tool.name, + "description": mcp_tool.description, + "parameters": mcp_tool.inputSchema, + }, + } diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index b1235ba6201..3c6ea4d0936 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -61,6 +61,9 @@ def tool_call(self, name: str, arguments: dict[str, Any]) -> str: ... class Base(ABC): + tools: list[Any] + toolcall_sessions: dict[str, ToolCallSession] + def __init__(self, key, model_name, base_url, **kwargs): timeout = int(os.environ.get("LM_TIMEOUT_SECONDS", 600)) self.client = OpenAI(api_key=key, base_url=base_url, timeout=timeout) @@ -70,6 +73,8 @@ def __init__(self, key, model_name, base_url, **kwargs): self.base_delay = kwargs.get("retry_interval", float(os.environ.get("LLM_BASE_DELAY", 2.0))) self.max_rounds = kwargs.get("max_rounds", 5) self.is_tools = False + self.tools = [] + self.toolcall_sessions = {} def _get_delay(self): """Calculate retry delay time""" @@ -145,8 +150,10 @@ def bind_tools(self, toolcall_session, tools): if not (toolcall_session and tools): return self.is_tools = True - self.toolcall_session = toolcall_session - self.tools = tools + + for tool in tools: + self.toolcall_sessions[tool["function"]["name"]] = toolcall_session + self.tools.append(tool) def chat_with_tools(self, system: str, history: list, gen_conf: dict): gen_conf = self._clean_conf() @@ -180,7 +187,7 @@ def chat_with_tools(self, system: str, history: list, gen_conf: dict): name = tool_call.function.name try: args = json_repair.loads(tool_call.function.arguments) - tool_response = self.toolcall_session.tool_call(name, args) + tool_response = self.toolcall_sessions[name].tool_call(name, args) history.append({"role": "tool", "tool_call_id": tool_call.id, "content": str(tool_response)}) except Exception as e: history.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"Tool call error: \n{tool_call}\nException:\n" + str(e)}) @@ -286,7 +293,7 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): name = tool_call.function.name try: args = json_repair.loads(tool_call.function.arguments) - tool_response = self.toolcall_session.tool_call(name, args) + tool_response = self.toolcall_sessions[name].tool_call(name, args) history.append( { "role": "assistant", @@ -585,7 +592,7 @@ def chat_with_tools(self, system: str, history: list, gen_conf: dict) -> tuple[s tool_name = assistant_output.tool_calls[0]["function"]["name"] if tool_name: arguments = json.loads(assistant_output.tool_calls[0]["function"]["arguments"]) - tool_info["content"] = self.toolcall_session.tool_call(name=tool_name, arguments=arguments) + tool_info["content"] = self.toolcall_sessions[tool_name].tool_call(name=tool_name, arguments=arguments) history.append(tool_info) response = Generation.call(self.model_name, messages=history, result_format="message", tools=self.tools, **gen_conf) @@ -708,7 +715,7 @@ def _chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict, tool_name = toolcall_message.tool_calls[0]["function"]["name"] history.append(toolcall_message) - tool_info["content"] = self.toolcall_session.tool_call(name=tool_name, arguments=tool_arguments) + tool_info["content"] = self.toolcall_sessions[tool_name].tool_call(name=tool_name, arguments=tool_arguments) history.append(tool_info) tool_info = {"content": "", "role": "tool"} tool_name = "" diff --git a/uv.lock b/uv.lock index cd5efce0ec7..7b9cf6b889c 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.10, <3.13" resolution-markers = [ "python_full_version >= '3.12' and sys_platform == 'darwin'", diff --git a/web/src/interfaces/database/mcp-server.ts b/web/src/interfaces/database/mcp-server.ts new file mode 100644 index 00000000000..34ed7e4b995 --- /dev/null +++ b/web/src/interfaces/database/mcp-server.ts @@ -0,0 +1,19 @@ +export enum McpServerType { + Sse = 'sse', + StreamableHttp = 'streamable-http', +} + +export interface IMcpServerVariable { + key: string; + name: string; +} + +export interface IMcpServerInfo { + id: string; + name: string; + url: string; + server_type: McpServerType; + description?: string; + variables?: IMcpServerVariable[]; + headers: Map; +} diff --git a/web/src/services/mcp-server-service.ts b/web/src/services/mcp-server-service.ts new file mode 100644 index 00000000000..a90fa0961eb --- /dev/null +++ b/web/src/services/mcp-server-service.ts @@ -0,0 +1,41 @@ +import api from '@/utils/api'; +import registerServer from '@/utils/register-server'; +import request from '@/utils/request'; + +const { + getMcpServerList, + getMultipleMcpServers, + createMcpServer, + updateMcpServer, + deleteMcpServer, +} = api; + +const methods = { + get_list: { + url: getMcpServerList, + method: 'get', + }, + get_multiple: { + url: getMultipleMcpServers, + method: 'post', + }, + add: { + url: createMcpServer, + method: 'post' + }, + update: { + url: updateMcpServer, + method: 'post' + }, + rm: { + url: deleteMcpServer, + method: 'post' + }, +} as const; + +const mcpServerService = registerServer(methods, request); + +export const getMcpServer = (serverId: string) => + request.get(api.getMcpServer(serverId)); + +export default mcpServerService; diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index b0c32b123cd..d0369d1e846 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -143,4 +143,12 @@ export default { testDbConnect: `${api_host}/canvas/test_db_connect`, getInputElements: `${api_host}/canvas/input_elements`, debug: `${api_host}/canvas/debug`, + + // mcp server + getMcpServerList: `${api_host}/mcp_server/list`, + getMultipleMcpServers: `${api_host}/mcp_server/get_multiple`, + getMcpServer: (serverId: string) => `${api_host}/mcp_server/get/${serverId}`, + createMcpServer: `${api_host}/mcp_server/create`, + updateMcpServer: `${api_host}/mcp_server/update`, + deleteMcpServer: `${api_host}/mcp_server/rm`, }; From 96b63cc81fa298089401e6fff0f6621fbffc2fd9 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 24 Jun 2025 09:34:33 +0800 Subject: [PATCH 0015/1187] Feat: Use the message_id returned by the interface as the id of the reply message #3221 (#8434) ### What problem does this PR solve? Feat: Use the message_id returned by the interface as the id of the reply message #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/hooks/logic-hooks.ts | 48 +++++++++++++++++++ .../node/dropdown/next-step-dropdown.tsx | 4 +- web/src/pages/agent/chat/hooks.ts | 22 +++++---- web/src/pages/agent/constant.tsx | 7 +-- web/src/pages/agent/form-sheet/next.tsx | 2 +- .../agent/form-sheet/use-form-config-map.tsx | 2 +- .../agent-form/tool-popover/tool-command.tsx | 2 +- .../pages/agent/form/tool-form/constant.ts | 2 +- web/src/pages/agent/hooks.tsx | 2 +- web/src/pages/agent/hooks/use-add-node.ts | 2 +- web/src/pages/agent/utils.ts | 21 ++++++-- 11 files changed, 92 insertions(+), 22 deletions(-) diff --git a/web/src/hooks/logic-hooks.ts b/web/src/hooks/logic-hooks.ts index c0a35357930..73af17e6b20 100644 --- a/web/src/hooks/logic-hooks.ts +++ b/web/src/hooks/logic-hooks.ts @@ -334,6 +334,20 @@ export const useSelectDerivedMessages = () => { [], ); + const addNewestOneQuestion = useCallback((message: Message) => { + setDerivedMessages((pre) => { + return [ + ...pre, + { + ...message, + id: buildMessageUuid(message), // The message id is generated on the front end, + // and the message id returned by the back end is the same as the question id, + // so that the pair of messages can be deleted together when deleting the message + }, + ]; + }); + }, []); + // Add the streaming message to the last item in the message list const addNewestAnswer = useCallback((answer: IAnswer) => { setDerivedMessages((pre) => { @@ -355,6 +369,38 @@ export const useSelectDerivedMessages = () => { }); }, []); + // Add the streaming message to the last item in the message list + const addNewestOneAnswer = useCallback((answer: IAnswer) => { + setDerivedMessages((pre) => { + const idx = pre.findIndex((x) => x.id === answer.id); + + if (idx !== -1) { + return pre.map((x) => { + if (x.id === answer.id) { + return { ...x, content: answer.answer }; + } + return x; + }); + } + + return [ + ...(pre ?? []), + { + role: MessageType.Assistant, + content: answer.answer, + reference: answer.reference, + id: buildMessageUuid({ + id: answer.id, + role: MessageType.Assistant, + }), + prompt: answer.prompt, + audio_binary: answer.audio_binary, + ...omit(answer, 'reference'), + }, + ]; + }); + }, []); + const removeLatestMessage = useCallback(() => { setDerivedMessages((pre) => { const nextMessages = pre?.slice(0, -2) ?? []; @@ -406,6 +452,8 @@ export const useSelectDerivedMessages = () => { addNewestAnswer, removeLatestMessage, removeMessageById, + addNewestOneQuestion, + addNewestOneAnswer, removeMessagesAfterCurrentMessage, }; }; diff --git a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx index 99f33471a3d..3a18233a159 100644 --- a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx @@ -93,7 +93,9 @@ function AccordionOperators() { Tools - + diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/hooks.ts index e813c90c479..d9e220cc0ec 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/hooks.ts @@ -37,6 +37,8 @@ export const useSelectNextMessages = () => { removeLatestMessage, removeMessageById, removeMessagesAfterCurrentMessage, + addNewestOneQuestion, + addNewestOneAnswer, } = useSelectDerivedMessages(); return { @@ -48,6 +50,8 @@ export const useSelectNextMessages = () => { addNewestAnswer, removeLatestMessage, removeMessageById, + addNewestOneQuestion, + addNewestOneAnswer, removeMessagesAfterCurrentMessage, }; }; @@ -57,7 +61,7 @@ function findMessageFromList(eventList: IEventList) { (x) => x.event === MessageEventType.Message, ) as IMessageEvent[]; return { - id: messageEventList[0]?.message_id, + id: eventList[0]?.message_id, content: messageEventList.map((x) => x.data.content).join(''), }; } @@ -83,6 +87,8 @@ export const useSendNextMessage = () => { addNewestAnswer, removeLatestMessage, removeMessageById, + addNewestOneQuestion, + addNewestOneAnswer, } = useSelectNextMessages(); const { id: agentId } = useParams(); const { handleInputChange, value, setValue } = useHandleMessageInputChange(); @@ -132,13 +138,13 @@ export const useSendNextMessage = () => { useEffect(() => { const { content, id } = findMessageFromList(answerList); - if (content) { - addNewestAnswer({ + if (answerList.length > 0) { + addNewestOneAnswer({ answer: content, id: id, }); } - }, [answerList, addNewestAnswer]); + }, [answerList, addNewestOneAnswer]); const handlePressEnter = useCallback(() => { if (trim(value) === '') return; @@ -147,20 +153,20 @@ export const useSendNextMessage = () => { setValue(''); handleSendMessage({ id, content: value.trim(), role: MessageType.User }); } - addNewestQuestion({ + addNewestOneQuestion({ content: value, id, role: MessageType.User, }); - }, [addNewestQuestion, handleSendMessage, done, setValue, value]); + }, [value, done, addNewestOneQuestion, setValue, handleSendMessage]); useEffect(() => { if (prologue) { - addNewestAnswer({ + addNewestOneAnswer({ answer: prologue, }); } - }, [addNewestAnswer, prologue]); + }, [addNewestOneAnswer, prologue]); useEffect(() => { addEventList(answerList); diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index cebd4624014..f590d7a589d 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -85,7 +85,7 @@ export enum Operator { WaitingDialogue = 'WaitingDialogue', Agent = 'Agent', Tool = 'Tool', - Tavily = 'Tavily', + TavilySearch = 'TavilySearch', } export const SwitchLogicOperatorOptions = ['and', 'or']; @@ -250,7 +250,7 @@ export const operatorMap: Record< [Operator.Code]: { backgroundColor: '#4c5458' }, [Operator.WaitingDialogue]: { backgroundColor: '#a5d65c' }, [Operator.Agent]: { backgroundColor: '#a5d65c' }, - [Operator.Tavily]: { backgroundColor: '#a5d65c' }, + [Operator.TavilySearch]: { backgroundColor: '#a5d65c' }, }; export const componentMenuList = [ @@ -805,6 +805,7 @@ export const RestrictedUpstreamMap = { [Operator.Code]: [Operator.Begin], [Operator.WaitingDialogue]: [Operator.Begin], [Operator.Agent]: [Operator.Begin], + [Operator.TavilySearch]: [Operator.Begin], }; export const NodeMap = { @@ -848,7 +849,7 @@ export const NodeMap = { [Operator.WaitingDialogue]: 'ragNode', [Operator.Agent]: 'agentNode', [Operator.Tool]: 'toolNode', - [Operator.Tavily]: 'ragNode', + [Operator.TavilySearch]: 'ragNode', }; export const LanguageOptions = [ diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index fb500458efd..8bfd9d2aff4 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -43,7 +43,7 @@ const FormSheet = ({ const currentFormMap = FormConfigMap[operatorName]; - const OperatorForm = currentFormMap.component ?? EmptyContent; + const OperatorForm = currentFormMap?.component ?? EmptyContent; const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ id: node?.id, diff --git a/web/src/pages/agent/form-sheet/use-form-config-map.tsx b/web/src/pages/agent/form-sheet/use-form-config-map.tsx index 50b537a07ee..267682a1a63 100644 --- a/web/src/pages/agent/form-sheet/use-form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/use-form-config-map.tsx @@ -376,7 +376,7 @@ export function useFormConfigMap() { defaultValues: {}, schema: z.object({}), }, - [Operator.Tavily]: { + [Operator.TavilySearch]: { component: TavilyForm, defaultValues: {}, schema: z.object({}), diff --git a/web/src/pages/agent/form/agent-form/tool-popover/tool-command.tsx b/web/src/pages/agent/form/agent-form/tool-popover/tool-command.tsx index 5d948fe4715..fd94408d870 100644 --- a/web/src/pages/agent/form/agent-form/tool-popover/tool-command.tsx +++ b/web/src/pages/agent/form/agent-form/tool-popover/tool-command.tsx @@ -16,7 +16,7 @@ const Menus = [ { label: 'Search', list: [ - Operator.Tavily, + Operator.TavilySearch, Operator.Google, Operator.Bing, Operator.DuckDuckGo, diff --git a/web/src/pages/agent/form/tool-form/constant.ts b/web/src/pages/agent/form/tool-form/constant.ts index 4954f51e16a..73f6f7b6de2 100644 --- a/web/src/pages/agent/form/tool-form/constant.ts +++ b/web/src/pages/agent/form/tool-form/constant.ts @@ -34,5 +34,5 @@ export const ToolFormConfigMap = { [Operator.YahooFinance]: YahooFinanceForm, [Operator.Crawler]: CrawlerForm, [Operator.Email]: EmailForm, - [Operator.Tavily]: TavilyForm, + [Operator.TavilySearch]: TavilyForm, }; diff --git a/web/src/pages/agent/hooks.tsx b/web/src/pages/agent/hooks.tsx index b8344f7bf4a..507d73e76f6 100644 --- a/web/src/pages/agent/hooks.tsx +++ b/web/src/pages/agent/hooks.tsx @@ -150,7 +150,7 @@ export const useInitializeOperatorParams = () => { [Operator.Code]: initialCodeValues, [Operator.WaitingDialogue]: initialWaitingDialogueValues, [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, - [Operator.Tavily]: initialTavilyValues, + [Operator.TavilySearch]: initialTavilyValues, }; }, [llmId]); diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index 0a74eac22b0..0bc58d02548 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -105,7 +105,7 @@ export const useInitializeOperatorParams = () => { [Operator.WaitingDialogue]: initialWaitingDialogueValues, [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, [Operator.Tool]: {}, - [Operator.Tavily]: initialTavilyValues, + [Operator.TavilySearch]: initialTavilyValues, }; }, [llmId]); diff --git a/web/src/pages/agent/utils.ts b/web/src/pages/agent/utils.ts index e86e0c39578..f670422859f 100644 --- a/web/src/pages/agent/utils.ts +++ b/web/src/pages/agent/utils.ts @@ -94,9 +94,20 @@ const buildComponentDownstreamOrUpstream = ( edges: Edge[], nodeId: string, isBuildDownstream = true, + nodes: Node[], ) => { return edges - .filter((y) => y[isBuildDownstream ? 'source' : 'target'] === nodeId) + .filter((y) => { + const node = nodes.find((x) => x.id === nodeId); + let isNotUpstreamTool = true; + if (isBuildDownstream && node?.data.label === Operator.Agent) { + isNotUpstreamTool = !y.target.startsWith(Operator.Tool); // Exclude the tool operator downstream of the agent operator + } + return ( + y[isBuildDownstream ? 'source' : 'target'] === nodeId && + isNotUpstreamTool + ); + }) .map((y) => y[isBuildDownstream ? 'target' : 'source']); }; @@ -125,6 +136,8 @@ const buildOperatorParams = (operatorName: string) => // initializeOperatorParams(operatorName), // Final processing, for guarantee ); +const ExcludeOperators = [Operator.Note, Operator.Tool]; + // construct a dsl based on the node information of the graph export const buildDslComponentsByGraph = ( nodes: RAGFlowNodeType[], @@ -134,7 +147,7 @@ export const buildDslComponentsByGraph = ( const components: DSLComponents = {}; nodes - ?.filter((x) => x.data.label !== Operator.Note) + ?.filter((x) => !ExcludeOperators.some((y) => y === x.data.label)) .forEach((x) => { const id = x.id; const operatorName = x.data.label; @@ -147,8 +160,8 @@ export const buildDslComponentsByGraph = ( x.data.form as Record, ) ?? {}, }, - downstream: buildComponentDownstreamOrUpstream(edges, id, true), - upstream: buildComponentDownstreamOrUpstream(edges, id, false), + downstream: buildComponentDownstreamOrUpstream(edges, id, true, nodes), + upstream: buildComponentDownstreamOrUpstream(edges, id, false, nodes), parent_id: x?.parentId, }; }); From 49d67cbcb797f39bf87d0256a004b5ecf3f4f2f2 Mon Sep 17 00:00:00 2001 From: Rainman <91299664+Rainman5042@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:35:02 +0800 Subject: [PATCH 0016/1187] fix a bug when using huggingface embedding api (#8432) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? image_version: v0.19.1 This PR fixes a bug in the HuggingFaceEmBedding API method that was causing AssertionError: assert len(vects) == len(docs) during the document embedding process. #### Problem The HuggingFaceEmbed.encode() method had an early return statement inside the for loop, causing it to return after processing only the first text input instead of processing all texts in the input list. **Error Messenge** ```python AssertionError: assert len(vects) == len(docs) # input chunks != embedded vectors from embedding api File "/ragflow/rag/svr/task_executor.py", line 442, in embedding ``` **Buggy code(/ragflow/rag/llm/embedding_model.py)** ```python class HuggingFaceEmbed(Base): def __init__(self, key, model_name, base_url=None): if not model_name: raise ValueError("Model name cannot be None") self.key = key self.model_name = model_name.split("___")[0] self.base_url = base_url or "http://127.0.0.1:8080" def encode(self, texts: list): embeddings = [] for text in texts: response = requests.post(...) if response.status_code == 200: try: embedding = response.json() embeddings.append(embedding[0]) # ❌ Early return return np.array(embeddings), sum([num_tokens_from_string(text) for text in texts]) except Exception as _e: log_exception(_e, response) else: raise Exception(...) ``` **Fixed Code(I just Rollback this function to the v0.19.0 version)** ```python Class HuggingFaceEmbed(Base): def __init__(self, key, model_name, base_url=None): if not model_name: raise ValueError("Model name cannot be None") self.key = key self.model_name = model_name.split("___")[0] self.base_url = base_url or "http://127.0.0.1:8080" def encode(self, texts: list): embeddings = [] for text in texts: response = requests.post(...) if response.status_code == 200: embedding = response.json() embeddings.append(embedding[0]) # ✅ Only append, no return else: raise Exception(...) return np.array(embeddings), sum([num_tokens_from_string(text) for text in texts]) # ✅ Return after processing all ``` ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/embedding_model.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 25cbc5250e0..373485b9af2 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -871,14 +871,11 @@ def encode(self, texts: list): headers={'Content-Type': 'application/json'} ) if response.status_code == 200: - try: - embedding = response.json() - embeddings.append(embedding[0]) - return np.array(embeddings), sum([num_tokens_from_string(text) for text in texts]) - except Exception as _e: - log_exception(_e, response) + embedding = response.json() + embeddings.append(embedding[0]) else: raise Exception(f"Error: {response.status_code} - {response.text}") + return np.array(embeddings), sum([num_tokens_from_string(text) for text in texts]) def encode_queries(self, text): response = requests.post( @@ -887,11 +884,8 @@ def encode_queries(self, text): headers={'Content-Type': 'application/json'} ) if response.status_code == 200: - try: - embedding = response.json() - return np.array(embedding[0]), num_tokens_from_string(text) - except Exception as _e: - log_exception(_e, response) + embedding = response.json() + return np.array(embedding[0]), num_tokens_from_string(text) else: raise Exception(f"Error: {response.status_code} - {response.text}") From 07545fbfd3b852c768dee468f36489f2743d6bd6 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 24 Jun 2025 11:33:01 +0800 Subject: [PATCH 0017/1187] Feat: Delete the agent and tool nodes downstream of the agent node #3221 (#8450) ### What problem does this PR solve? Feat: Delete the agent and tool nodes downstream of the agent node #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../pages/agent/canvas/node/agent-node.tsx | 4 +- web/src/pages/agent/constant.tsx | 2 + .../form/agent-form/tool-popover/index.tsx | 10 ++-- .../tool-popover/use-update-tools.ts | 9 ++-- .../form/agent-form/use-delete-tool-node.ts | 24 ---------- web/src/pages/agent/hooks/use-add-node.ts | 10 ++-- .../pages/agent/hooks/use-before-delete.tsx | 33 +++++++++++-- web/src/pages/agent/store.ts | 48 ++++++++++++++++++- web/src/pages/agent/utils/delete-node.ts | 34 +++++++++++++ .../agent/utils/filter-downstream-nodes.ts | 31 ++++++++++++ 10 files changed, 162 insertions(+), 43 deletions(-) delete mode 100644 web/src/pages/agent/form/agent-form/use-delete-tool-node.ts create mode 100644 web/src/pages/agent/utils/delete-node.ts create mode 100644 web/src/pages/agent/utils/filter-downstream-nodes.ts diff --git a/web/src/pages/agent/canvas/node/agent-node.tsx b/web/src/pages/agent/canvas/node/agent-node.tsx index 2a4057e3184..731f155a73a 100644 --- a/web/src/pages/agent/canvas/node/agent-node.tsx +++ b/web/src/pages/agent/canvas/node/agent-node.tsx @@ -54,13 +54,13 @@ function InnerAgentNode({ type="target" position={Position.Top} isConnectable={false} - id="f" + id={NodeHandleId.AgentTop} > state.deleteAgentToolNodeById, + ); const handleChange = useCallback( (value: string[]) => { @@ -29,11 +31,11 @@ export function ToolPopover({ children }: PropsWithChildren) { nodeId: node?.id, })(); } else { - deleteToolNode(node.id); // TODO: The tool node should be derived from the agent tools data + deleteAgentToolNodeById(node.id); // TODO: The tool node should be derived from the agent tools data } } }, - [addCanvasNode, deleteToolNode, node?.id, updateNodeTools], + [addCanvasNode, deleteAgentToolNodeById, node?.id, updateNodeTools], ); return ( diff --git a/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts b/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts index 3bcf844847b..cca31e68d38 100644 --- a/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts +++ b/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts @@ -3,7 +3,6 @@ import { AgentFormContext } from '@/pages/agent/context'; import useGraphStore from '@/pages/agent/store'; import { get } from 'lodash'; import { useCallback, useContext, useMemo } from 'react'; -import { useDeleteToolNode } from '../use-delete-tool-node'; export function useGetNodeTools() { const node = useContext(AgentFormContext); @@ -48,7 +47,9 @@ export function useDeleteAgentNodeTools() { const { updateNodeForm } = useGraphStore((state) => state); const tools = useGetNodeTools(); const node = useContext(AgentFormContext); - const { deleteToolNode } = useDeleteToolNode(); + const deleteAgentToolNodeById = useGraphStore( + (state) => state.deleteAgentToolNodeById, + ); const deleteNodeTool = useCallback( (value: string) => () => { @@ -56,11 +57,11 @@ export function useDeleteAgentNodeTools() { if (node?.id) { updateNodeForm(node?.id, nextTools, ['tools']); if (nextTools.length === 0) { - deleteToolNode(node?.id); + deleteAgentToolNodeById(node?.id); } } }, - [deleteToolNode, node?.id, tools, updateNodeForm], + [deleteAgentToolNodeById, node?.id, tools, updateNodeForm], ); return { deleteNodeTool }; diff --git a/web/src/pages/agent/form/agent-form/use-delete-tool-node.ts b/web/src/pages/agent/form/agent-form/use-delete-tool-node.ts deleted file mode 100644 index b3227459581..00000000000 --- a/web/src/pages/agent/form/agent-form/use-delete-tool-node.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useCallback } from 'react'; -import { NodeHandleId } from '../../constant'; -import useGraphStore from '../../store'; - -export function useDeleteToolNode() { - const { edges, deleteEdgeById, deleteNodeById } = useGraphStore( - (state) => state, - ); - const deleteToolNode = useCallback( - (agentNodeId: string) => { - const edge = edges.find( - (x) => x.source === agentNodeId && x.sourceHandle === NodeHandleId.Tool, - ); - - if (edge) { - deleteEdgeById(edge.id); - deleteNodeById(edge.target); - } - }, - [deleteEdgeById, deleteNodeById, edges], - ); - - return { deleteToolNode }; -} diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index 0bc58d02548..2ae543dd0f7 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -315,7 +315,11 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { if (agentNode) { // Calculate the coordinates of child nodes to prevent newly added child nodes from covering other child nodes const allChildAgentNodeIds = edges - .filter((x) => x.source === nodeId && x.sourceHandle === 'e') + .filter( + (x) => + x.source === nodeId && + x.sourceHandle === NodeHandleId.AgentBottom, + ) .map((x) => x.target); const xAxises = nodes @@ -334,8 +338,8 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { addEdge({ source: nodeId, target: newNode.id, - sourceHandle: 'e', - targetHandle: 'f', + sourceHandle: NodeHandleId.AgentBottom, + targetHandle: NodeHandleId.AgentTop, }); } } else if (type === Operator.Tool) { diff --git a/web/src/pages/agent/hooks/use-before-delete.tsx b/web/src/pages/agent/hooks/use-before-delete.tsx index 14512ae9609..d08333c8610 100644 --- a/web/src/pages/agent/hooks/use-before-delete.tsx +++ b/web/src/pages/agent/hooks/use-before-delete.tsx @@ -1,14 +1,18 @@ import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { OnBeforeDelete } from '@xyflow/react'; +import { Node, OnBeforeDelete } from '@xyflow/react'; import { Operator } from '../constant'; import useGraphStore from '../store'; +import { deleteAllDownstreamAgentsAndTool } from '../utils/delete-node'; const UndeletableNodes = [Operator.Begin, Operator.IterationStart]; export function useBeforeDelete() { - const getOperatorTypeFromId = useGraphStore( - (state) => state.getOperatorTypeFromId, - ); + const { getOperatorTypeFromId, getNode } = useGraphStore((state) => state); + + const agentPredicate = (node: Node) => { + return getOperatorTypeFromId(node.id) === Operator.Agent; + }; + const handleBeforeDelete: OnBeforeDelete = async ({ nodes, // Nodes to be deleted edges, // Edges to be deleted @@ -47,6 +51,27 @@ export function useBeforeDelete() { return true; }); + // Delete the agent and tool nodes downstream of the agent node + if (nodes.some(agentPredicate)) { + nodes.filter(agentPredicate).forEach((node) => { + const { downstreamAgentAndToolEdges, downstreamAgentAndToolNodeIds } = + deleteAllDownstreamAgentsAndTool(node.id, edges); + + downstreamAgentAndToolNodeIds.forEach((nodeId) => { + const currentNode = getNode(nodeId); + if (toBeDeletedNodes.every((x) => x.id !== nodeId) && currentNode) { + toBeDeletedNodes.push(currentNode); + } + }); + + downstreamAgentAndToolEdges.forEach((edge) => { + if (toBeDeletedEdges.every((x) => x.id !== edge.id)) { + toBeDeletedEdges.push(edge); + } + }); + }, []); + } + return { nodes: toBeDeletedNodes, edges: toBeDeletedEdges, diff --git a/web/src/pages/agent/store.ts b/web/src/pages/agent/store.ts index 26bb3750eb7..31ad0a46b3f 100644 --- a/web/src/pages/agent/store.ts +++ b/web/src/pages/agent/store.ts @@ -21,7 +21,7 @@ import lodashSet from 'lodash/set'; import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; -import { Operator, SwitchElseTo } from './constant'; +import { NodeHandleId, Operator, SwitchElseTo } from './constant'; import { duplicateNodeForm, generateDuplicateNode, @@ -30,6 +30,7 @@ import { isEdgeEqual, mapEdgeMouseEvent, } from './utils'; +import { deleteAllDownstreamAgentsAndTool } from './utils/delete-node'; export type RFState = { nodes: RAGFlowNodeType[]; @@ -70,6 +71,8 @@ export type RFState = { deleteEdge: () => void; deleteEdgeById: (id: string) => void; deleteNodeById: (id: string) => void; + deleteAgentDownstreamNodesById: (id: string) => void; + deleteAgentToolNodeById: (id: string) => void; deleteIterationNodeById: (id: string) => void; deleteEdgeBySourceAndSourceHandle: (connection: Partial) => void; findNodeByName: (operatorName: Operator) => RAGFlowNodeType | undefined; @@ -370,7 +373,16 @@ const useGraphStore = create()( }); }, deleteNodeById: (id: string) => { - const { nodes, edges } = get(); + const { + nodes, + edges, + getOperatorTypeFromId, + deleteAgentDownstreamNodesById, + } = get(); + if (getOperatorTypeFromId(id) === Operator.Agent) { + deleteAgentDownstreamNodesById(id); + return; + } set({ nodes: nodes.filter((node) => node.id !== id), edges: edges @@ -378,6 +390,38 @@ const useGraphStore = create()( .filter((edge) => edge.target !== id), }); }, + deleteAgentDownstreamNodesById: (id) => { + const { edges, nodes } = get(); + + const { downstreamAgentAndToolNodeIds, downstreamAgentAndToolEdges } = + deleteAllDownstreamAgentsAndTool(id, edges); + + set({ + nodes: nodes.filter( + (node) => + !downstreamAgentAndToolNodeIds.some((x) => x === node.id) && + node.id !== id, + ), + edges: edges.filter( + (edge) => + edge.source !== id && + edge.target !== id && + !downstreamAgentAndToolEdges.some((x) => x.id === edge.id), + ), + }); + }, + deleteAgentToolNodeById: (id) => { + const { edges, deleteEdgeById, deleteNodeById } = get(); + + const edge = edges.find( + (x) => x.source === id && x.sourceHandle === NodeHandleId.Tool, + ); + + if (edge) { + deleteEdgeById(edge.id); + deleteNodeById(edge.target); + } + }, deleteIterationNodeById: (id: string) => { const { nodes, edges } = get(); const children = nodes.filter((node) => node.parentId === id); diff --git a/web/src/pages/agent/utils/delete-node.ts b/web/src/pages/agent/utils/delete-node.ts new file mode 100644 index 00000000000..4f1c18ebc6e --- /dev/null +++ b/web/src/pages/agent/utils/delete-node.ts @@ -0,0 +1,34 @@ +import { Edge } from '@xyflow/react'; +import { filterAllDownstreamAgentAndToolNodeIds } from './filter-downstream-nodes'; + +// Delete all downstream agent and tool operators of the current agent operator +export function deleteAllDownstreamAgentsAndTool( + nodeId: string, + edges: Edge[], +) { + const downstreamAgentAndToolNodeIds = filterAllDownstreamAgentAndToolNodeIds( + edges, + [nodeId], + ); + + const downstreamAgentAndToolEdges = downstreamAgentAndToolNodeIds.reduce< + Edge[] + >((pre, cur) => { + const relatedEdges = edges.filter( + (x) => x.source === cur || x.target === cur, + ); + + relatedEdges.forEach((x) => { + if (!pre.some((y) => y.id !== x.id)) { + pre.push(x); + } + }); + + return pre; + }, []); + + return { + downstreamAgentAndToolNodeIds, + downstreamAgentAndToolEdges, + }; +} diff --git a/web/src/pages/agent/utils/filter-downstream-nodes.ts b/web/src/pages/agent/utils/filter-downstream-nodes.ts new file mode 100644 index 00000000000..be9b350a9b1 --- /dev/null +++ b/web/src/pages/agent/utils/filter-downstream-nodes.ts @@ -0,0 +1,31 @@ +import { Edge } from '@xyflow/react'; +import { NodeHandleId } from '../constant'; + +// Get all downstream agent operators of the current agent operator +export function filterAllDownstreamAgentAndToolNodeIds( + edges: Edge[], + nodeIds: string[], +) { + return nodeIds.reduce((pre, nodeId) => { + const currentEdges = edges.filter( + (x) => + x.source === nodeId && + (x.sourceHandle === NodeHandleId.AgentBottom || + x.sourceHandle === NodeHandleId.Tool), + ); + + const downstreamNodeIds: string[] = currentEdges.map((x) => x.target); + + const ids = downstreamNodeIds.concat( + filterAllDownstreamAgentAndToolNodeIds(edges, downstreamNodeIds), + ); + + ids.forEach((x) => { + if (pre.every((y) => y !== x)) { + pre.push(x); + } + }); + + return pre; + }, []); +} From 382d2d0373fea9554982a0718fc8eaf5fe8a5807 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Tue, 24 Jun 2025 13:17:22 +0800 Subject: [PATCH 0018/1187] Refactor:Improve insert file logic (#8445) ### What problem does this PR solve? before refactor 1. create file record 2. Add to blob if have some execption at 2 the system db will have a file record but not have related blob, which will introduce some bug. after refactor 1. add to blob 2. create file record. if 1 success but 2 failed just have a dirty blob in blob system, user will not feel that ### Type of change - [x] Refactoring --- api/apps/file_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/apps/file_app.py b/api/apps/file_app.py index e06c9650cdd..eabe7bd9b1e 100644 --- a/api/apps/file_app.py +++ b/api/apps/file_app.py @@ -99,6 +99,7 @@ def upload(): FileService.query, name=file_obj_names[file_len - 1], parent_id=last_folder.id) + STORAGE_IMPL.put(last_folder.id, location, blob) file = { "id": get_uuid(), "parent_id": last_folder.id, @@ -110,7 +111,6 @@ def upload(): "size": len(blob), } file = FileService.insert(file) - STORAGE_IMPL.put(last_folder.id, location, blob) file_res.append(file.to_json()) return get_json_result(data=file_res) except Exception as e: From 9f9acf0c49855f8702c08fadfe251ad0060269fd Mon Sep 17 00:00:00 2001 From: Liu An Date: Tue, 24 Jun 2025 17:26:16 +0800 Subject: [PATCH 0019/1187] Test: Add document app tests (#8456) ### What problem does this PR solve? - Add new test suite for document app with create/list/parse/upload/remove tests - Update API URLs to use version variable from config in HTTP and web API tests ### Type of change - [x] Add test cases --- test/testcases/test_http_api/common.py | 17 +- .../test_list_documents.py | 2 +- .../test_upload_documents.py | 2 +- test/testcases/test_web_api/common.py | 79 +++++- test/testcases/test_web_api/conftest.py | 32 ++- .../test_document_app/conftest.py | 58 ++++ .../test_document_app/test_create_document.py | 92 +++++++ .../test_document_app/test_list_documents.py | 180 ++++++++++++ .../test_document_app/test_paser_documents.py | 256 ++++++++++++++++++ .../test_document_app/test_rm_documents.py | 104 +++++++ .../test_upload_documents.py | 201 ++++++++++++++ 11 files changed, 998 insertions(+), 25 deletions(-) create mode 100644 test/testcases/test_web_api/test_document_app/conftest.py create mode 100644 test/testcases/test_web_api/test_document_app/test_create_document.py create mode 100644 test/testcases/test_web_api/test_document_app/test_list_documents.py create mode 100644 test/testcases/test_web_api/test_document_app/test_paser_documents.py create mode 100644 test/testcases/test_web_api/test_document_app/test_rm_documents.py create mode 100644 test/testcases/test_web_api/test_document_app/test_upload_documents.py diff --git a/test/testcases/test_http_api/common.py b/test/testcases/test_http_api/common.py index 123fb766736..dba320d981d 100644 --- a/test/testcases/test_http_api/common.py +++ b/test/testcases/test_http_api/common.py @@ -13,22 +13,21 @@ # See the License for the specific language governing permissions and # limitations under the License. # - from pathlib import Path import requests -from configs import HOST_ADDRESS +from configs import HOST_ADDRESS, VERSION from requests_toolbelt import MultipartEncoder from utils.file_utils import create_txt_file HEADERS = {"Content-Type": "application/json"} -DATASETS_API_URL = "/api/v1/datasets" -FILE_API_URL = "/api/v1/datasets/{dataset_id}/documents" -FILE_CHUNK_API_URL = "/api/v1/datasets/{dataset_id}/chunks" -CHUNK_API_URL = "/api/v1/datasets/{dataset_id}/documents/{document_id}/chunks" -CHAT_ASSISTANT_API_URL = "/api/v1/chats" -SESSION_WITH_CHAT_ASSISTANT_API_URL = "/api/v1/chats/{chat_id}/sessions" -SESSION_WITH_AGENT_API_URL = "/api/v1/agents/{agent_id}/sessions" +DATASETS_API_URL = f"/api/{VERSION}/datasets" +FILE_API_URL = f"/api/{VERSION}/datasets/{{dataset_id}}/documents" +FILE_CHUNK_API_URL = f"/api/{VERSION}/datasets/{{dataset_id}}/chunks" +CHUNK_API_URL = f"/api/{VERSION}/datasets/{{dataset_id}}/documents/{{document_id}}/chunks" +CHAT_ASSISTANT_API_URL = f"/api/{VERSION}/chats" +SESSION_WITH_CHAT_ASSISTANT_API_URL = f"/api/{VERSION}/chats/{{chat_id}}/sessions" +SESSION_WITH_AGENT_API_URL = f"/api/{VERSION}/agents/{{agent_id}}/sessions" # DATASET MANAGEMENT diff --git a/test/testcases/test_http_api/test_file_management_within_dataset/test_list_documents.py b/test/testcases/test_http_api/test_file_management_within_dataset/test_list_documents.py index 4fbe59b0bdb..fb4c26711f0 100644 --- a/test/testcases/test_http_api/test_file_management_within_dataset/test_list_documents.py +++ b/test/testcases/test_http_api/test_file_management_within_dataset/test_list_documents.py @@ -346,7 +346,7 @@ def test_concurrent_list(self, HttpApiAuth, add_documents): count = 100 with ThreadPoolExecutor(max_workers=5) as executor: - futures = [executor.submit(list_documents, HttpApiAuth, dataset_id) for i in range(count)] + futures = [executor.submit(list_documents, HttpApiAuth, dataset_id) for _ in range(count)] responses = list(as_completed(futures)) assert len(responses) == count, responses assert all(future.result()["code"] == 0 for future in futures) diff --git a/test/testcases/test_http_api/test_file_management_within_dataset/test_upload_documents.py b/test/testcases/test_http_api/test_file_management_within_dataset/test_upload_documents.py index f8f23864154..27f47472901 100644 --- a/test/testcases/test_http_api/test_file_management_within_dataset/test_upload_documents.py +++ b/test/testcases/test_http_api/test_file_management_within_dataset/test_upload_documents.py @@ -209,7 +209,7 @@ def test_concurrent_upload(self, HttpApiAuth, add_dataset_func, tmp_path): fps.append(fp) with ThreadPoolExecutor(max_workers=5) as executor: - futures = [executor.submit(upload_documents, HttpApiAuth, dataset_id, fps[i : i + 1]) for i in range(count)] + futures = [executor.submit(upload_documents, HttpApiAuth, dataset_id, [fp]) for fp in fps] responses = list(as_completed(futures)) assert len(responses) == count, responses assert all(future.result()["code"] == 0 for future in futures) diff --git a/test/testcases/test_web_api/common.py b/test/testcases/test_web_api/common.py index 69eba070d61..7181018a441 100644 --- a/test/testcases/test_web_api/common.py +++ b/test/testcases/test_web_api/common.py @@ -13,12 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from pathlib import Path + import requests -from configs import HOST_ADDRESS +from configs import HOST_ADDRESS, VERSION +from requests_toolbelt import MultipartEncoder +from utils.file_utils import create_txt_file HEADERS = {"Content-Type": "application/json"} -KB_APP_URL = "/v1/kb" +KB_APP_URL = f"/{VERSION}/kb" +DOCUMENT_APP_URL = f"/{VERSION}/document" # FILE_API_URL = "/api/v1/datasets/{dataset_id}/documents" # FILE_CHUNK_API_URL = "/api/v1/datasets/{dataset_id}/chunks" # CHUNK_API_URL = "/api/v1/datasets/{dataset_id}/documents/{document_id}/chunks" @@ -27,7 +32,7 @@ # SESSION_WITH_AGENT_API_URL = "/api/v1/agents/{agent_id}/sessions" -# DATASET MANAGEMENT +# KB APP def create_kb(auth, payload=None, *, headers=HEADERS, data=None): res = requests.post(url=f"{HOST_ADDRESS}{KB_APP_URL}/create", headers=headers, auth=auth, json=payload, data=data) return res.json() @@ -91,3 +96,71 @@ def batch_create_datasets(auth, num): res = create_kb(auth, {"name": f"kb_{i}"}) ids.append(res["data"]["kb_id"]) return ids + + +# DOCUMENT APP +def upload_documents(auth, payload=None, files_path=None): + url = f"{HOST_ADDRESS}{DOCUMENT_APP_URL}/upload" + + if files_path is None: + files_path = [] + + fields = [] + file_objects = [] + try: + if payload: + for k, v in payload.items(): + fields.append((k, str(v))) + + for fp in files_path: + p = Path(fp) + f = p.open("rb") + fields.append(("file", (p.name, f))) + file_objects.append(f) + m = MultipartEncoder(fields=fields) + + res = requests.post( + url=url, + headers={"Content-Type": m.content_type}, + auth=auth, + data=m, + ) + return res.json() + finally: + for f in file_objects: + f.close() + + +def create_document(auth, payload=None, *, headers=HEADERS, data=None): + res = requests.post(url=f"{HOST_ADDRESS}{DOCUMENT_APP_URL}/create", headers=headers, auth=auth, json=payload, data=data) + return res.json() + + +def list_documents(auth, params=None, payload=None, *, headers=HEADERS, data=None): + if payload is None: + payload = {} + res = requests.post(url=f"{HOST_ADDRESS}{DOCUMENT_APP_URL}/list", headers=headers, auth=auth, params=params, json=payload, data=data) + return res.json() + + +def delete_document(auth, payload=None, *, headers=HEADERS, data=None): + res = requests.post(url=f"{HOST_ADDRESS}{DOCUMENT_APP_URL}/rm", headers=headers, auth=auth, json=payload, data=data) + return res.json() + + +def parse_documents(auth, payload=None, *, headers=HEADERS, data=None): + res = requests.post(url=f"{HOST_ADDRESS}{DOCUMENT_APP_URL}/run", headers=headers, auth=auth, json=payload, data=data) + return res.json() + + +def bulk_upload_documents(auth, kb_id, num, tmp_path): + fps = [] + for i in range(num): + fp = create_txt_file(tmp_path / f"ragflow_test_upload_{i}.txt") + fps.append(fp) + + res = upload_documents(auth, {"kb_id": kb_id}, fps) + document_ids = [] + for document in res["data"]: + document_ids.append(document["id"]) + return document_ids diff --git a/test/testcases/test_web_api/conftest.py b/test/testcases/test_web_api/conftest.py index 44c80d9aff8..82fcf982f3a 100644 --- a/test/testcases/test_web_api/conftest.py +++ b/test/testcases/test_web_api/conftest.py @@ -16,11 +16,15 @@ import pytest from common import ( batch_create_datasets, + list_kbs, + rm_kb, ) -from configs import HOST_ADDRESS, VERSION + +# from configs import HOST_ADDRESS, VERSION from libs.auth import RAGFlowWebApiAuth from pytest import FixtureRequest -from ragflow_sdk import RAGFlow + +# from ragflow_sdk import RAGFlow from utils.file_utils import ( create_docx_file, create_eml_file, @@ -69,32 +73,38 @@ def WebApiAuth(auth): return RAGFlowWebApiAuth(auth) -@pytest.fixture(scope="session") -def client(token: str) -> RAGFlow: - return RAGFlow(api_key=token, base_url=HOST_ADDRESS, version=VERSION) +# @pytest.fixture(scope="session") +# def client(token: str) -> RAGFlow: +# return RAGFlow(api_key=token, base_url=HOST_ADDRESS, version=VERSION) @pytest.fixture(scope="function") -def clear_datasets(request: FixtureRequest, client: RAGFlow): +def clear_datasets(request: FixtureRequest, WebApiAuth: RAGFlowWebApiAuth): def cleanup(): - client.delete_datasets(ids=None) + res = list_kbs(WebApiAuth, params={"page_size": 1000}) + for kb in res["data"]["kbs"]: + rm_kb(WebApiAuth, {"kb_id": kb["id"]}) request.addfinalizer(cleanup) @pytest.fixture(scope="class") -def add_dataset(request: FixtureRequest, client: RAGFlow, WebApiAuth: RAGFlowWebApiAuth) -> str: +def add_dataset(request: FixtureRequest, WebApiAuth: RAGFlowWebApiAuth) -> str: def cleanup(): - client.delete_datasets(ids=None) + res = list_kbs(WebApiAuth, params={"page_size": 1000}) + for kb in res["data"]["kbs"]: + rm_kb(WebApiAuth, {"kb_id": kb["id"]}) request.addfinalizer(cleanup) return batch_create_datasets(WebApiAuth, 1)[0] @pytest.fixture(scope="function") -def add_dataset_func(request: FixtureRequest, client: RAGFlow, WebApiAuth: RAGFlowWebApiAuth) -> str: +def add_dataset_func(request: FixtureRequest, WebApiAuth: RAGFlowWebApiAuth) -> str: def cleanup(): - client.delete_datasets(ids=None) + res = list_kbs(WebApiAuth, params={"page_size": 1000}) + for kb in res["data"]["kbs"]: + rm_kb(WebApiAuth, {"kb_id": kb["id"]}) request.addfinalizer(cleanup) return batch_create_datasets(WebApiAuth, 1)[0] diff --git a/test/testcases/test_web_api/test_document_app/conftest.py b/test/testcases/test_web_api/test_document_app/conftest.py new file mode 100644 index 00000000000..a34bc9be723 --- /dev/null +++ b/test/testcases/test_web_api/test_document_app/conftest.py @@ -0,0 +1,58 @@ +# +# 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 pytest +from common import bulk_upload_documents, delete_document, list_documents + + +@pytest.fixture(scope="function") +def add_document_func(request, WebApiAuth, add_dataset, ragflow_tmp_dir): + def cleanup(): + res = list_documents(WebApiAuth, {"kb_id": dataset_id}) + for doc in res["data"]["docs"]: + delete_document(WebApiAuth, {"doc_id": doc["id"]}) + + request.addfinalizer(cleanup) + + dataset_id = add_dataset + return dataset_id, bulk_upload_documents(WebApiAuth, dataset_id, 1, ragflow_tmp_dir)[0] + + +@pytest.fixture(scope="class") +def add_documents(request, WebApiAuth, add_dataset, ragflow_tmp_dir): + def cleanup(): + res = list_documents(WebApiAuth, {"kb_id": dataset_id}) + for doc in res["data"]["docs"]: + delete_document(WebApiAuth, {"doc_id": doc["id"]}) + + request.addfinalizer(cleanup) + + dataset_id = add_dataset + return dataset_id, bulk_upload_documents(WebApiAuth, dataset_id, 5, ragflow_tmp_dir) + + +@pytest.fixture(scope="function") +def add_documents_func(request, WebApiAuth, add_dataset_func, ragflow_tmp_dir): + def cleanup(): + res = list_documents(WebApiAuth, {"kb_id": dataset_id}) + for doc in res["data"]["docs"]: + delete_document(WebApiAuth, {"doc_id": doc["id"]}) + + request.addfinalizer(cleanup) + + dataset_id = add_dataset_func + return dataset_id, bulk_upload_documents(WebApiAuth, dataset_id, 3, ragflow_tmp_dir) diff --git a/test/testcases/test_web_api/test_document_app/test_create_document.py b/test/testcases/test_web_api/test_document_app/test_create_document.py new file mode 100644 index 00000000000..ba31bd4f2bd --- /dev/null +++ b/test/testcases/test_web_api/test_document_app/test_create_document.py @@ -0,0 +1,92 @@ +# +# 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 string +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import create_document, list_kbs +from configs import DOCUMENT_NAME_LIMIT, INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth +from utils.file_utils import create_txt_file + + +@pytest.mark.p1 +@pytest.mark.usefixtures("clear_datasets") +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = create_document(invalid_auth) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestDocumentCreate: + @pytest.mark.p3 + def test_filename_empty(self, WebApiAuth, add_dataset_func): + kb_id = add_dataset_func + payload = {"name": "", "kb_id": kb_id} + res = create_document(WebApiAuth, payload) + assert res["code"] == 101, res + assert res["message"] == "File name can't be empty.", res + + @pytest.mark.p2 + def test_filename_max_length(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + fp = create_txt_file(tmp_path / f"{'a' * (DOCUMENT_NAME_LIMIT - 4)}.txt") + res = create_document(WebApiAuth, {"name": fp.name, "kb_id": kb_id}) + assert res["code"] == 0, res + assert res["data"]["name"] == fp.name, res + + @pytest.mark.p2 + def test_invalid_kb_id(self, WebApiAuth): + res = create_document(WebApiAuth, {"name": "ragflow_test.txt", "kb_id": "invalid_kb_id"}) + assert res["code"] == 102, res + assert res["message"] == "Can't find this knowledgebase!", res + + @pytest.mark.p3 + def test_filename_special_characters(self, WebApiAuth, add_dataset_func): + kb_id = add_dataset_func + illegal_chars = '<>:"/\\|?*' + translation_table = str.maketrans({char: "_" for char in illegal_chars}) + safe_filename = string.punctuation.translate(translation_table) + filename = f"{safe_filename}.txt" + + res = create_document(WebApiAuth, {"name": filename, "kb_id": kb_id}) + assert res["code"] == 0, res + assert res["data"]["kb_id"] == kb_id, res + assert res["data"]["name"] == filename, f"Expected: {filename}, Got: {res['data']['name']}" + + @pytest.mark.p3 + def test_concurrent_upload(self, WebApiAuth, add_dataset_func): + kb_id = add_dataset_func + + count = 20 + filenames = [f"ragflow_test_{i}.txt" for i in range(count)] + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [executor.submit(create_document, WebApiAuth, {"name": name, "kb_id": kb_id}) for name in filenames] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures), responses + + res = list_kbs(WebApiAuth, {"id": kb_id}) + assert res["data"]["kbs"][0]["doc_num"] == count, res diff --git a/test/testcases/test_web_api/test_document_app/test_list_documents.py b/test/testcases/test_web_api/test_document_app/test_list_documents.py new file mode 100644 index 00000000000..ce973fd3391 --- /dev/null +++ b/test/testcases/test_web_api/test_document_app/test_list_documents.py @@ -0,0 +1,180 @@ +# +# 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. +# +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import list_documents +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth +from utils import is_sorted + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = list_documents(invalid_auth, {"kb_id": "dataset_id"}) + assert res["code"] == expected_code + assert res["message"] == expected_message + + +class TestDocumentsList: + @pytest.mark.p1 + def test_default(self, WebApiAuth, add_documents): + kb_id, _ = add_documents + res = list_documents(WebApiAuth, {"kb_id": kb_id}) + assert res["code"] == 0 + assert len(res["data"]["docs"]) == 5 + assert res["data"]["total"] == 5 + + @pytest.mark.p3 + @pytest.mark.parametrize( + "kb_id, expected_code, expected_message", + [ + ("", 101, 'Lack of "KB ID"'), + ("invalid_dataset_id", 103, "Only owner of knowledgebase authorized for this operation."), + ], + ) + def test_invalid_dataset_id(self, WebApiAuth, kb_id, expected_code, expected_message): + res = list_documents(WebApiAuth, {"kb_id": kb_id}) + assert res["code"] == expected_code + assert res["message"] == expected_message + + @pytest.mark.p1 + @pytest.mark.parametrize( + "params, expected_code, expected_page_size, expected_message", + [ + ({"page": None, "page_size": 2}, 0, 5, ""), + ({"page": 0, "page_size": 2}, 0, 5, ""), + ({"page": 2, "page_size": 2}, 0, 2, ""), + ({"page": 3, "page_size": 2}, 0, 1, ""), + ({"page": "3", "page_size": 2}, 0, 1, ""), + pytest.param({"page": -1, "page_size": 2}, 100, 0, "1064", marks=pytest.mark.skip(reason="issues/5851")), + pytest.param({"page": "a", "page_size": 2}, 100, 0, """ValueError("invalid literal for int() with base 10: 'a'")""", marks=pytest.mark.skip(reason="issues/5851")), + ], + ) + def test_page(self, WebApiAuth, add_documents, params, expected_code, expected_page_size, expected_message): + kb_id, _ = add_documents + res = list_documents(WebApiAuth, {"kb_id": kb_id, **params}) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["docs"]) == expected_page_size, res + assert res["data"]["total"] == 5, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p1 + @pytest.mark.parametrize( + "params, expected_code, expected_page_size, expected_message", + [ + ({"page_size": None}, 0, 5, ""), + ({"page_size": 0}, 0, 5, ""), + ({"page_size": 1}, 0, 5, ""), + ({"page_size": 6}, 0, 5, ""), + ({"page_size": "1"}, 0, 5, ""), + pytest.param({"page_size": -1}, 100, 0, "1064", marks=pytest.mark.skip(reason="issues/5851")), + pytest.param({"page_size": "a"}, 100, 0, """ValueError("invalid literal for int() with base 10: 'a'")""", marks=pytest.mark.skip(reason="issues/5851")), + ], + ) + def test_page_size(self, WebApiAuth, add_documents, params, expected_code, expected_page_size, expected_message): + kb_id, _ = add_documents + res = list_documents(WebApiAuth, {"kb_id": kb_id, **params}) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["docs"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + @pytest.mark.parametrize( + "params, expected_code, assertions, expected_message", + [ + ({"orderby": None}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""), + ({"orderby": "create_time"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""), + ({"orderby": "update_time"}, 0, lambda r: (is_sorted(r["data"]["docs"], "update_time", True)), ""), + pytest.param({"orderby": "name", "desc": "False"}, 0, lambda r: (is_sorted(r["data"]["docs"], "name", False)), "", marks=pytest.mark.skip(reason="issues/5851")), + pytest.param({"orderby": "unknown"}, 102, 0, "orderby should be create_time or update_time", marks=pytest.mark.skip(reason="issues/5851")), + ], + ) + def test_orderby(self, WebApiAuth, add_documents, params, expected_code, assertions, expected_message): + kb_id, _ = add_documents + res = list_documents(WebApiAuth, {"kb_id": kb_id, **params}) + assert res["code"] == expected_code, res + if expected_code == 0: + if callable(assertions): + assert assertions(res) + else: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + @pytest.mark.parametrize( + "params, expected_code, assertions, expected_message", + [ + ({"desc": None}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""), + ({"desc": "true"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""), + ({"desc": "True"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""), + ({"desc": True}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""), + pytest.param({"desc": "false"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", False)), "", marks=pytest.mark.skip(reason="issues/5851")), + ({"desc": "False"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", False)), ""), + ({"desc": False}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", False)), ""), + ({"desc": "False", "orderby": "update_time"}, 0, lambda r: (is_sorted(r["data"]["docs"], "update_time", False)), ""), + pytest.param({"desc": "unknown"}, 102, 0, "desc should be true or false", marks=pytest.mark.skip(reason="issues/5851")), + ], + ) + def test_desc(self, WebApiAuth, add_documents, params, expected_code, assertions, expected_message): + kb_id, _ = add_documents + res = list_documents(WebApiAuth, {"kb_id": kb_id, **params}) + assert res["code"] == expected_code, res + if expected_code == 0: + if callable(assertions): + assert assertions(res) + else: + assert res["message"] == expected_message, res + + @pytest.mark.p2 + @pytest.mark.parametrize( + "params, expected_num", + [ + ({"keywords": None}, 5), + ({"keywords": ""}, 5), + ({"keywords": "0"}, 1), + ({"keywords": "ragflow_test_upload"}, 5), + ({"keywords": "unknown"}, 0), + ], + ) + def test_keywords(self, WebApiAuth, add_documents, params, expected_num): + kb_id, _ = add_documents + res = list_documents(WebApiAuth, {"kb_id": kb_id, **params}) + assert res["code"] == 0, res + assert len(res["data"]["docs"]) == expected_num, res + assert res["data"]["total"] == expected_num, res + + @pytest.mark.p3 + def test_concurrent_list(self, WebApiAuth, add_documents): + kb_id, _ = add_documents + count = 100 + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [executor.submit(list_documents, WebApiAuth, {"kb_id": kb_id}) for i in range(count)] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures), responses diff --git a/test/testcases/test_web_api/test_document_app/test_paser_documents.py b/test/testcases/test_web_api/test_document_app/test_paser_documents.py new file mode 100644 index 00000000000..34e88557181 --- /dev/null +++ b/test/testcases/test_web_api/test_document_app/test_paser_documents.py @@ -0,0 +1,256 @@ +# +# 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. +# +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import bulk_upload_documents, list_documents, parse_documents +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth +from utils import wait_for + + +@wait_for(30, 1, "Document parsing timeout") +def condition(_auth, _kb_id, _document_ids=None): + res = list_documents(_auth, {"kb_id": _kb_id}) + target_docs = res["data"]["docs"] + + if _document_ids is None: + for doc in target_docs: + if doc["run"] != "3": + return False + return True + + target_ids = set(_document_ids) + for doc in target_docs: + if doc["id"] in target_ids: + if doc.get("run") != "3": + return False + return True + + +def validate_document_parse_done(auth, _kb_id, _document_ids): + res = list_documents(auth, {"kb_id": _kb_id}) + for doc in res["data"]["docs"]: + if doc["id"] not in _document_ids: + continue + assert doc["run"] == "3" + assert len(doc["process_begin_at"]) > 0 + assert doc["process_duation"] > 0 + assert doc["progress"] > 0 + assert "Task done" in doc["progress_msg"] + + +def validate_document_parse_cancel(auth, _kb_id, _document_ids): + res = list_documents(auth, {"kb_id": _kb_id}) + for doc in res["data"]["docs"]: + if doc["id"] not in _document_ids: + continue + assert doc["run"] == "2" + assert len(doc["process_begin_at"]) > 0 + assert doc["progress"] == 0.0 + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = parse_documents(invalid_auth) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestDocumentsParse: + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + pytest.param(None, 101, "required argument are missing: doc_ids, run; ", marks=pytest.mark.skip), + pytest.param({"doc_ids": [], "run": "1"}, 0, "", marks=pytest.mark.p1), + pytest.param({"doc_ids": ["invalid_id"], "run": "1"}, 109, "No authorization.", marks=pytest.mark.p3), + pytest.param({"doc_ids": ["\n!?。;!?\"'"], "run": "1"}, 109, "No authorization.", marks=pytest.mark.p3), + pytest.param("not json", 101, "required argument are missing: doc_ids, run; ", marks=pytest.mark.skip), + pytest.param(lambda r: {"doc_ids": r[:1], "run": "1"}, 0, "", marks=pytest.mark.p1), + pytest.param(lambda r: {"doc_ids": r, "run": "1"}, 0, "", marks=pytest.mark.p1), + ], + ) + def test_basic_scenarios(self, WebApiAuth, add_documents_func, payload, expected_code, expected_message): + kb_id, document_ids = add_documents_func + if callable(payload): + payload = payload(document_ids) + res = parse_documents(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + condition(WebApiAuth, kb_id, payload["doc_ids"]) + validate_document_parse_done(WebApiAuth, kb_id, payload["doc_ids"]) + else: + assert res["message"] == expected_message, res + + @pytest.mark.parametrize( + "payload", + [ + pytest.param(lambda r: {"doc_ids": ["invalid_id"] + r, "run": "1"}, marks=pytest.mark.p3), + pytest.param(lambda r: {"doc_ids": r[:1] + ["invalid_id"] + r[1:3], "run": "1"}, marks=pytest.mark.p1), + pytest.param(lambda r: {"doc_ids": r + ["invalid_id"], "run": "1"}, marks=pytest.mark.p3), + ], + ) + def test_parse_partial_invalid_document_id(self, WebApiAuth, add_documents_func, payload): + _, document_ids = add_documents_func + if callable(payload): + payload = payload(document_ids) + res = parse_documents(WebApiAuth, payload) + assert res["code"] == 109, res + assert res["message"] == "No authorization.", res + + @pytest.mark.p3 + def test_repeated_parse(self, WebApiAuth, add_documents_func): + kb_id, document_ids = add_documents_func + res = parse_documents(WebApiAuth, {"doc_ids": document_ids, "run": "1"}) + assert res["code"] == 0, res + + condition(WebApiAuth, kb_id, document_ids) + + res = parse_documents(WebApiAuth, {"doc_ids": document_ids, "run": "1"}) + assert res["code"] == 0, res + + @pytest.mark.p3 + def test_duplicate_parse(self, WebApiAuth, add_documents_func): + kb_id, document_ids = add_documents_func + res = parse_documents(WebApiAuth, {"doc_ids": document_ids + document_ids, "run": "1"}) + assert res["code"] == 0, res + assert res["message"] == "success", res + + condition(WebApiAuth, kb_id, document_ids) + validate_document_parse_done(WebApiAuth, kb_id, document_ids) + + +@pytest.mark.p3 +def test_parse_100_files(WebApiAuth, add_dataset_func, tmp_path): + @wait_for(100, 1, "Document parsing timeout") + def condition(_auth, _kb_id, _document_num): + res = list_documents(_auth, {"kb_id": _kb_id, "page_size": _document_num}) + for doc in res["data"]["docs"]: + if doc["run"] != "3": + return False + return True + + document_num = 100 + kb_id = add_dataset_func + document_ids = bulk_upload_documents(WebApiAuth, kb_id, document_num, tmp_path) + res = parse_documents(WebApiAuth, {"doc_ids": document_ids, "run": "1"}) + assert res["code"] == 0, res + + condition(WebApiAuth, kb_id, document_num) + + validate_document_parse_done(WebApiAuth, kb_id, document_ids) + + +@pytest.mark.p3 +def test_concurrent_parse(WebApiAuth, add_dataset_func, tmp_path): + @wait_for(120, 1, "Document parsing timeout") + def condition(_auth, _kb_id, _document_num): + res = list_documents(_auth, {"kb_id": _kb_id, "page_size": _document_num}) + for doc in res["data"]["docs"]: + if doc["run"] != "3": + return False + return True + + count = 100 + kb_id = add_dataset_func + document_ids = bulk_upload_documents(WebApiAuth, kb_id, count, tmp_path) + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [ + executor.submit( + parse_documents, + WebApiAuth, + {"doc_ids": [document_ids[i]], "run": "1"}, + ) + for i in range(count) + ] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures) + + condition(WebApiAuth, kb_id, count) + + validate_document_parse_done(WebApiAuth, kb_id, document_ids) + + +# @pytest.mark.skip +class TestDocumentsParseStop: + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + pytest.param(None, 101, "required argument are missing: doc_ids, run; ", marks=pytest.mark.skip), + pytest.param({"doc_ids": [], "run": "2"}, 0, "", marks=pytest.mark.p1), + pytest.param({"doc_ids": ["invalid_id"], "run": "2"}, 109, "No authorization.", marks=pytest.mark.p3), + pytest.param({"doc_ids": ["\n!?。;!?\"'"], "run": "2"}, 109, "No authorization.", marks=pytest.mark.p3), + pytest.param("not json", 101, "required argument are missing: doc_ids, run; ", marks=pytest.mark.skip), + pytest.param(lambda r: {"doc_ids": r[:1], "run": "2"}, 0, "", marks=pytest.mark.p1), + pytest.param(lambda r: {"doc_ids": r, "run": "2"}, 0, "", marks=pytest.mark.p1), + ], + ) + def test_basic_scenarios(self, WebApiAuth, add_documents_func, payload, expected_code, expected_message): + @wait_for(10, 1, "Document parsing timeout") + def condition(_auth, _kb_id, _doc_ids): + res = list_documents(_auth, {"kb_id": _kb_id}) + for doc in res["data"]["docs"]: + if doc["id"] in _doc_ids: + if doc["run"] != "3": + return False + return True + + kb_id, document_ids = add_documents_func + parse_documents(WebApiAuth, {"doc_ids": document_ids, "run": "1"}) + + if callable(payload): + payload = payload(document_ids) + + res = parse_documents(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + completed_document_ids = list(set(document_ids) - set(payload["doc_ids"])) + condition(WebApiAuth, kb_id, completed_document_ids) + validate_document_parse_cancel(WebApiAuth, kb_id, payload["doc_ids"]) + validate_document_parse_done(WebApiAuth, kb_id, completed_document_ids) + else: + assert res["message"] == expected_message, res + + @pytest.mark.skip + @pytest.mark.parametrize( + "payload", + [ + lambda r: {"doc_ids": ["invalid_id"] + r, "run": "2"}, + lambda r: {"doc_ids": r[:1] + ["invalid_id"] + r[1:3], "run": "2"}, + lambda r: {"doc_ids": r + ["invalid_id"], "run": "2"}, + ], + ) + def test_stop_parse_partial_invalid_document_id(self, WebApiAuth, add_documents_func, payload): + kb_id, document_ids = add_documents_func + parse_documents(WebApiAuth, {"doc_ids": document_ids, "run": "1"}) + + if callable(payload): + payload = payload(document_ids) + res = parse_documents(WebApiAuth, payload) + assert res["code"] == 109, res + assert res["message"] == "No authorization.", res + + validate_document_parse_cancel(WebApiAuth, kb_id, document_ids) diff --git a/test/testcases/test_web_api/test_document_app/test_rm_documents.py b/test/testcases/test_web_api/test_document_app/test_rm_documents.py new file mode 100644 index 00000000000..2cec5f02d37 --- /dev/null +++ b/test/testcases/test_web_api/test_document_app/test_rm_documents.py @@ -0,0 +1,104 @@ +# +# 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. +# +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import bulk_upload_documents, delete_document, list_documents +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = delete_document(invalid_auth) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestDocumentsDeletion: + @pytest.mark.p1 + @pytest.mark.parametrize( + "payload, expected_code, expected_message, remaining", + [ + (None, 101, "required argument are missing: doc_id; ", 3), + ({"doc_id": ""}, 109, "No authorization.", 3), + ({"doc_id": "invalid_id"}, 109, "No authorization.", 3), + ({"doc_id": "\n!?。;!?\"'"}, 109, "No authorization.", 3), + ("not json", 101, "required argument are missing: doc_id; ", 3), + (lambda r: {"doc_id": r[0]}, 0, "", 2), + ], + ) + def test_basic_scenarios(self, WebApiAuth, add_documents_func, payload, expected_code, expected_message, remaining): + kb_id, document_ids = add_documents_func + if callable(payload): + payload = payload(document_ids) + res = delete_document(WebApiAuth, payload) + assert res["code"] == expected_code, res + if res["code"] != 0: + assert res["message"] == expected_message, res + + res = list_documents(WebApiAuth, {"kb_id": kb_id}) + assert len(res["data"]["docs"]) == remaining, res + assert res["data"]["total"] == remaining, res + + @pytest.mark.p2 + def test_repeated_deletion(self, WebApiAuth, add_documents_func): + _, document_ids = add_documents_func + for doc_id in document_ids: + res = delete_document(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + + for doc_id in document_ids: + res = delete_document(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 109, res + assert res["message"] == "No authorization.", res + + +@pytest.mark.p3 +def test_concurrent_deletion(WebApiAuth, add_dataset, tmp_path): + count = 100 + kb_id = add_dataset + document_ids = bulk_upload_documents(WebApiAuth, kb_id, count, tmp_path) + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [executor.submit(delete_document, WebApiAuth, {"doc_id": document_ids[i]}) for i in range(count)] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures), responses + + +@pytest.mark.p3 +def test_delete_100(WebApiAuth, add_dataset, tmp_path): + documents_num = 100 + kb_id = add_dataset + document_ids = bulk_upload_documents(WebApiAuth, kb_id, documents_num, tmp_path) + res = list_documents(WebApiAuth, {"kb_id": kb_id}) + assert res["data"]["total"] == documents_num, res + + for doc_id in document_ids: + res = delete_document(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + + res = list_documents(WebApiAuth, {"kb_id": kb_id}) + assert res["data"]["total"] == 0, res diff --git a/test/testcases/test_web_api/test_document_app/test_upload_documents.py b/test/testcases/test_web_api/test_document_app/test_upload_documents.py new file mode 100644 index 00000000000..b2b36d57005 --- /dev/null +++ b/test/testcases/test_web_api/test_document_app/test_upload_documents.py @@ -0,0 +1,201 @@ +# +# 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 string +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +import requests +from common import DOCUMENT_APP_URL, list_kbs, upload_documents +from configs import DOCUMENT_NAME_LIMIT, HOST_ADDRESS, INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth +from requests_toolbelt import MultipartEncoder +from utils.file_utils import create_txt_file + + +@pytest.mark.p1 +@pytest.mark.usefixtures("clear_datasets") +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = upload_documents(invalid_auth) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestDocumentsUpload: + @pytest.mark.p1 + def test_valid_single_upload(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + fp = create_txt_file(tmp_path / "ragflow_test.txt") + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, [fp]) + assert res["code"] == 0, res + assert res["data"][0]["kb_id"] == kb_id, res + assert res["data"][0]["name"] == fp.name, res + + @pytest.mark.p1 + @pytest.mark.parametrize( + "generate_test_files", + [ + "docx", + "excel", + "ppt", + "image", + "pdf", + "txt", + "md", + "json", + "eml", + "html", + ], + indirect=True, + ) + def test_file_type_validation(self, WebApiAuth, add_dataset_func, generate_test_files, request): + kb_id = add_dataset_func + fp = generate_test_files[request.node.callspec.params["generate_test_files"]] + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, [fp]) + assert res["code"] == 0, res + assert res["data"][0]["kb_id"] == kb_id, res + assert res["data"][0]["name"] == fp.name, res + + @pytest.mark.p2 + @pytest.mark.parametrize( + "file_type", + ["exe", "unknown"], + ) + def test_unsupported_file_type(self, WebApiAuth, add_dataset_func, tmp_path, file_type): + kb_id = add_dataset_func + fp = tmp_path / f"ragflow_test.{file_type}" + fp.touch() + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, [fp]) + assert res["code"] == 500, res + assert res["message"] == f"ragflow_test.{file_type}: This type of file has not been supported yet!", res + + @pytest.mark.p2 + def test_missing_file(self, WebApiAuth, add_dataset_func): + kb_id = add_dataset_func + res = upload_documents(WebApiAuth, {"kb_id": kb_id}) + assert res["code"] == 101, res + assert res["message"] == "No file part!", res + + @pytest.mark.p3 + def test_empty_file(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + fp = tmp_path / "empty.txt" + fp.touch() + + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, [fp]) + assert res["code"] == 0, res + assert res["data"][0]["size"] == 0, res + + @pytest.mark.p3 + def test_filename_empty(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + + fp = create_txt_file(tmp_path / "ragflow_test.txt") + url = f"{HOST_ADDRESS}{DOCUMENT_APP_URL}/upload" + fields = [("file", ("", fp.open("rb"))), ("kb_id", kb_id)] + m = MultipartEncoder(fields=fields) + res = requests.post( + url=url, + headers={"Content-Type": m.content_type}, + auth=WebApiAuth, + data=m, + ) + assert res.json()["code"] == 101, res + assert res.json()["message"] == "No file selected!", res + + @pytest.mark.p2 + def test_filename_exceeds_max_length(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + fp = create_txt_file(tmp_path / f"{'a' * (DOCUMENT_NAME_LIMIT - 4)}.txt") + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, [fp]) + assert res["code"] == 0, res + assert res["data"][0]["name"] == fp.name, res + + @pytest.mark.p2 + def test_invalid_kb_id(self, WebApiAuth, tmp_path): + fp = create_txt_file(tmp_path / "ragflow_test.txt") + res = upload_documents(WebApiAuth, {"kb_id": "invalid_kb_id"}, [fp]) + assert res["code"] == 100, res + assert res["message"] == """LookupError("Can't find this knowledgebase!")""", res + + @pytest.mark.p2 + def test_duplicate_files(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + fp = create_txt_file(tmp_path / "ragflow_test.txt") + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, [fp, fp]) + assert res["code"] == 0, res + assert len(res["data"]) == 2, res + for i in range(len(res["data"])): + assert res["data"][i]["kb_id"] == kb_id, res + expected_name = fp.name + if i != 0: + expected_name = f"{fp.stem}({i}){fp.suffix}" + assert res["data"][i]["name"] == expected_name, res + + @pytest.mark.p3 + def test_filename_special_characters(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + illegal_chars = '<>:"/\\|?*' + translation_table = str.maketrans({char: "_" for char in illegal_chars}) + safe_filename = string.punctuation.translate(translation_table) + fp = tmp_path / f"{safe_filename}.txt" + fp.write_text("Sample text content") + + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, [fp]) + assert res["code"] == 0, res + assert len(res["data"]) == 1, res + assert res["data"][0]["kb_id"] == kb_id, res + assert res["data"][0]["name"] == fp.name, res + + @pytest.mark.p1 + def test_multiple_files(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + expected_document_count = 20 + fps = [] + for i in range(expected_document_count): + fp = create_txt_file(tmp_path / f"ragflow_test_{i}.txt") + fps.append(fp) + res = upload_documents(WebApiAuth, {"kb_id": kb_id}, fps) + assert res["code"] == 0, res + + res = list_kbs(WebApiAuth) + assert res["data"]["kbs"][0]["doc_num"] == expected_document_count, res + + @pytest.mark.p3 + def test_concurrent_upload(self, WebApiAuth, add_dataset_func, tmp_path): + kb_id = add_dataset_func + + count = 20 + fps = [] + for i in range(count): + fp = create_txt_file(tmp_path / f"ragflow_test_{i}.txt") + fps.append(fp) + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [executor.submit(upload_documents, WebApiAuth, {"kb_id": kb_id}, fps[i : i + 1]) for i in range(count)] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures), responses + + res = list_kbs(WebApiAuth) + assert res["data"]["kbs"][0]["doc_num"] == count, res From bc1b837616223906b0003361a9c6a52ab3a85ec4 Mon Sep 17 00:00:00 2001 From: WuWeiFlow <83688074+WuWeiFlow@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:01:13 +0800 Subject: [PATCH 0020/1187] =?UTF-8?q?FIX:Saving=20an=20RGBA=20image=20dire?= =?UTF-8?q?ctly=20as=20JPEG=20will=20cause=20an=20error.=20If=20the?= =?UTF-8?q?=E2=80=A6=20(#8399)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saving an RGBA image directly as JPEG will cause an error. If the image is in RGBA mode, convert it to RGB mode before saving it in JPG format. ### What problem does this PR solve? During document parsing in the knowledge base, we occasionally encounter the error 'cannot write mode RGBA as JPEG.' This occurs because images in RGBA mode cannot be directly saved as JPEG. They must be converted first before saving. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/svr/task_executor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index e5fed0bea56..b028f7125e3 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -293,6 +293,9 @@ async def upload_to_minio(document, chunk): if isinstance(d["image"], bytes): output_buffer = BytesIO(d["image"]) else: + # If the image is in RGBA mode, convert it to RGB mode before saving it in JPEG format. + if d["image"].mode in ("RGBA", "P"): + d["image"] = d["image"].convert("RGB") d["image"].save(output_buffer, format='JPEG') async with minio_limiter: await trio.to_thread.run_sync(lambda: STORAGE_IMPL.put(task["kb_id"], d["id"], output_buffer.getvalue())) From 1c68c9ebd6cbb68da263b936f2a839501a4d12ff Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 24 Jun 2025 18:01:30 +0800 Subject: [PATCH 0021/1187] Feat: Add IterationNode component #3221 (#8461) ### What problem does this PR solve? Feat: Add IterationNode component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/canvas/index.less | 1 + .../agent/canvas/node/iteration-node.tsx | 69 ++++++-------- web/src/pages/agent/constant.tsx | 3 +- .../agent/form-sheet/use-form-config-map.tsx | 2 +- web/src/pages/agent/form/agent-form/index.tsx | 66 +++++++++---- .../agent/form/components/query-variable.tsx | 6 +- .../pages/agent/form/iteration-form/index.tsx | 61 ++++++++++++ .../agent/form/iteration-form/use-values.ts | 25 +++++ .../pages/agent/form/iteration-from/index.tsx | 94 ------------------- web/src/pages/agent/hooks/use-add-node.ts | 33 ++++--- 10 files changed, 191 insertions(+), 169 deletions(-) create mode 100644 web/src/pages/agent/form/iteration-form/index.tsx create mode 100644 web/src/pages/agent/form/iteration-form/use-values.ts delete mode 100644 web/src/pages/agent/form/iteration-from/index.tsx diff --git a/web/src/pages/agent/canvas/index.less b/web/src/pages/agent/canvas/index.less index d824d88f1bb..21f72e1508f 100644 --- a/web/src/pages/agent/canvas/index.less +++ b/web/src/pages/agent/canvas/index.less @@ -3,6 +3,7 @@ height: 100%; :global(.react-flow__node-group) { .commonNode(); + border-radius: 0 0 10px 10px; padding: 0; border: 0; background-color: transparent; diff --git a/web/src/pages/agent/canvas/node/iteration-node.tsx b/web/src/pages/agent/canvas/node/iteration-node.tsx index 53a84835ded..a6094f97bb4 100644 --- a/web/src/pages/agent/canvas/node/iteration-node.tsx +++ b/web/src/pages/agent/canvas/node/iteration-node.tsx @@ -1,15 +1,17 @@ -import { useTheme } from '@/components/theme-provider'; import { IIterationNode, IIterationStartNode, } from '@/interfaces/database/flow'; import { cn } from '@/lib/utils'; -import { Handle, NodeProps, NodeResizeControl, Position } from '@xyflow/react'; -import { ListRestart } from 'lucide-react'; +import { NodeProps, NodeResizeControl, Position } from '@xyflow/react'; import { memo } from 'react'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import { NodeHandleId, Operator } from '../../constant'; +import OperatorIcon from '../../operator-icon'; +import { CommonHandle } from './handle'; +import { RightHandleStyle } from './handle-icon'; import styles from './index.less'; import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; function ResizeIcon() { return ( @@ -50,47 +52,43 @@ export function InnerIterationNode({ isConnectable = true, selected, }: NodeProps) { - const { theme } = useTheme(); + // const { theme } = useTheme(); return (
- - + + nodeId={id} + > + ) { - const { theme } = useTheme(); - return ( -
- + + id={NodeHandleId.Start} + nodeId={id} + >
- +
-
+ ); } diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index aa5ef47daf7..59dc764ed02 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -644,7 +644,7 @@ export const initialEmailValues = { }; export const initialIterationValues = { - delimiter: ',', + items_ref: '', }; export const initialIterationStartValues = {}; @@ -665,6 +665,7 @@ export const initialWaitingDialogueValues = {}; export const initialAgentValues = { ...initialLlmBaseValues, + description: '', sys_prompt: ``, prompts: [{ role: PromptRole.User, content: `{${AgentGlobals.SysQuery}}` }], message_history_window_size: 12, diff --git a/web/src/pages/agent/form-sheet/use-form-config-map.tsx b/web/src/pages/agent/form-sheet/use-form-config-map.tsx index 267682a1a63..44a112657bf 100644 --- a/web/src/pages/agent/form-sheet/use-form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/use-form-config-map.tsx @@ -23,7 +23,7 @@ import GithubForm from '../form/github-form'; import GoogleForm from '../form/google-form'; import GoogleScholarForm from '../form/google-scholar-form'; import InvokeForm from '../form/invoke-form'; -import IterationForm from '../form/iteration-from'; +import IterationForm from '../form/iteration-form'; import Jin10Form from '../form/jin10-form'; import KeywordExtractForm from '../form/keyword-extract-form'; import MessageForm from '../form/message-form'; diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 5a348d1e5cc..fe7b8af861e 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -10,15 +10,17 @@ import { FormItem, FormLabel, } from '@/components/ui/form'; +import { Textarea } from '@/components/ui/textarea'; import { zodResolver } from '@hookform/resolvers/zod'; import { Position } from '@xyflow/react'; import { useContext, useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { Operator, initialAgentValues } from '../../constant'; +import { NodeHandleId, Operator, initialAgentValues } from '../../constant'; import { AgentInstanceContext } from '../../context'; import { INextOperatorForm } from '../../interface'; +import useGraphStore from '../../store'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; import { AgentTools } from './agent-tools'; @@ -27,6 +29,7 @@ import { useWatchFormChange } from './use-watch-change'; const FormSchema = z.object({ sys_prompt: z.string(), + description: z.string().optional(), prompts: z.string().optional(), // prompts: z // .array( @@ -49,9 +52,17 @@ const FormSchema = z.object({ const AgentForm = ({ node }: INextOperatorForm) => { const { t } = useTranslation(); + const { edges } = useGraphStore((state) => state); const defaultValues = useValues(node); + const isSubAgent = useMemo(() => { + const edge = edges.find( + (x) => x.target === node?.id && x.targetHandle === NodeHandleId.AgentTop, + ); + return !!edge; + }, [edges, node?.id]); + const outputList = useMemo(() => { return [ { title: 'content', type: initialAgentValues.outputs.content.type }, @@ -76,6 +87,20 @@ const AgentForm = ({ node }: INextOperatorForm) => { }} > + {isSubAgent && ( + ( + + Description + + + + + )} + /> + )} { /> - - {/* */} - ( - - User Prompt - -
- -
-
-
- )} - /> -
+ {isSubAgent || ( + + {/* */} + ( + + User Prompt + +
+ +
+
+
+ )} + /> +
+ )} ( {t('flow.query')} diff --git a/web/src/pages/agent/form/iteration-form/index.tsx b/web/src/pages/agent/form/iteration-form/index.tsx new file mode 100644 index 00000000000..bb9b7436f1e --- /dev/null +++ b/web/src/pages/agent/form/iteration-form/index.tsx @@ -0,0 +1,61 @@ +import { FormContainer } from '@/components/form-container'; +import { Form } from '@/components/ui/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { initialRetrievalValues } from '../../constant'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; +import { useValues } from './use-values'; + +const FormSchema = z.object({ + query: z.string().optional(), + similarity_threshold: z.coerce.number(), + keywords_similarity_weight: z.coerce.number(), + top_n: z.coerce.number(), + top_k: z.coerce.number(), + kb_ids: z.array(z.string()), + rerank_id: z.string(), + empty_response: z.string(), +}); + +const IterationForm = ({ node }: INextOperatorForm) => { + const outputList = useMemo(() => { + return [ + { + title: 'formalized_content', + type: initialRetrievalValues.outputs.formalized_content.type, + }, + ]; + }, []); + + const defaultValues = useValues(node); + + const form = useForm({ + defaultValues: defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
+ { + e.preventDefault(); + }} + > + + + + +
+ + ); +}; + +export default IterationForm; diff --git a/web/src/pages/agent/form/iteration-form/use-values.ts b/web/src/pages/agent/form/iteration-form/use-values.ts new file mode 100644 index 00000000000..2f8479efec3 --- /dev/null +++ b/web/src/pages/agent/form/iteration-form/use-values.ts @@ -0,0 +1,25 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { initialIterationValues } from '../../constant'; + +export function useValues(node?: RAGFlowNodeType) { + const defaultValues = useMemo( + () => ({ + ...initialIterationValues, + }), + [], + ); + + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return defaultValues; + } + + return formData; + }, [defaultValues, node?.data?.form]); + + return values; +} diff --git a/web/src/pages/agent/form/iteration-from/index.tsx b/web/src/pages/agent/form/iteration-from/index.tsx deleted file mode 100644 index f0f23918a9f..00000000000 --- a/web/src/pages/agent/form/iteration-from/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { CommaIcon, SemicolonIcon } from '@/assets/icon/Icon'; -import { Form, Select } from 'antd'; -import { - CornerDownLeft, - IndentIncrease, - Minus, - Slash, - Underline, -} from 'lucide-react'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { IOperatorForm } from '../../interface'; -import DynamicInputVariable from '../components/dynamic-input-variable'; - -const optionList = [ - { - value: ',', - icon: CommaIcon, - text: 'comma', - }, - { - value: '\n', - icon: CornerDownLeft, - text: 'lineBreak', - }, - { - value: 'tab', - icon: IndentIncrease, - text: 'tab', - }, - { - value: '_', - icon: Underline, - text: 'underline', - }, - { - value: '/', - icon: Slash, - text: 'diagonal', - }, - { - value: '-', - icon: Minus, - text: 'minus', - }, - { - value: ';', - icon: SemicolonIcon, - text: 'semicolon', - }, -]; - -const IterationForm = ({ onValuesChange, form, node }: IOperatorForm) => { - const { t } = useTranslation(); - - const options = useMemo(() => { - return optionList.map((x) => { - let Icon = x.icon; - - return { - value: x.value, - label: ( -
- - {t(`flow.delimiterOptions.${x.text}`)} -
- ), - }; - }); - }, [t]); - - return ( -
- - - - -
- ); -}; - -export default IterationForm; diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index 2ae543dd0f7..d4008ee0165 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -51,7 +51,6 @@ import useGraphStore from '../store'; import { generateNodeNamesWithIncreasingIndex, getNodeDragHandle, - getRelativePositionToIterationNode, } from '../utils'; export const useInitializeOperatorParams = () => { @@ -234,11 +233,9 @@ function useAddToolNode() { } export function useAddNode(reactFlowInstance?: ReactFlowInstance) { - const addNode = useGraphStore((state) => state.addNode); - const getNode = useGraphStore((state) => state.getNode); - const addEdge = useGraphStore((state) => state.addEdge); - const nodes = useGraphStore((state) => state.nodes); - const edges = useGraphStore((state) => state.edges); + const { edges, nodes, addEdge, addNode, getNode } = useGraphStore( + (state) => state, + ); const getNodeName = useGetNodeName(); const initializeOperatorParams = useInitializeOperatorParams(); const { calculateNewlyBackChildPosition } = useCalculateNewlyChildPosition(); @@ -257,6 +254,8 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { (event?: React.MouseEvent) => { const nodeId = params.nodeId; + const node = getNode(nodeId); + // reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition // and you don't need to subtract the reactFlowBounds.left/top anymore // details: https://@xyflow/react.dev/whats-new/2023-11-10 @@ -289,6 +288,11 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { dragHandle: getNodeDragHandle(type), }; + if (node && node.parentId) { + newNode.parentId = node.parentId; + newNode.extent = 'parent'; + } + if (type === Operator.Iteration) { newNode.width = 500; newNode.height = 250; @@ -307,6 +311,14 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { }; addNode(newNode); addNode(iterationStartNode); + if (nodeId) { + addEdge({ + source: nodeId, + target: newNode.id, + sourceHandle: NodeHandleId.Start, + targetHandle: NodeHandleId.End, + }); + } } else if ( type === Operator.Agent && params.position === Position.Bottom @@ -345,15 +357,6 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { } else if (type === Operator.Tool) { addToolNode(newNode, params.nodeId); } else { - const subNodeOfIteration = getRelativePositionToIterationNode( - nodes, - position, - ); - if (subNodeOfIteration) { - newNode.parentId = subNodeOfIteration.parentId; - newNode.position = subNodeOfIteration.position; - newNode.extent = 'parent'; - } addNode(newNode); addChildEdge(params.position, { source: params.nodeId, From d6a941ebf57a1a0a422d4b2c4a402db1098f3586 Mon Sep 17 00:00:00 2001 From: HaiyangP <46739135+HaiyangPeng@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:18:30 +0800 Subject: [PATCH 0022/1187] Fix the bug of long type value overflow (#8313) ### What problem does this PR solve? This PR will fix the #8271 by extending int type to float type when there is any value out of long type range in a column. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/app/table.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rag/app/table.py b/rag/app/table.py index 450cd628063..90b4c284976 100644 --- a/rag/app/table.py +++ b/rag/app/table.py @@ -92,11 +92,15 @@ def column_data_type(arr): arr = list(arr) counts = {"int": 0, "float": 0, "text": 0, "datetime": 0, "bool": 0} trans = {t: f for f, t in [(int, "int"), (float, "float"), (trans_datatime, "datetime"), (trans_bool, "bool"), (str, "text")]} + float_flag = False for a in arr: if a is None: continue - if re.match(r"[+-]?[0-9]{,19}(\.0+)?$", str(a).replace("%%", "")): + if re.match(r"[+-]?[0-9]+$", str(a).replace("%%", "")): counts["int"] += 1 + if int(str(a)) > 2**63 - 1: + float_flag = True + break elif re.match(r"[+-]?[0-9.]{,19}$", str(a).replace("%%", "")): counts["float"] += 1 elif re.match(r"(true|yes|是|\*|✓|✔|☑|✅|√|false|no|否|⍻|×)$", str(a), flags=re.IGNORECASE): @@ -105,8 +109,11 @@ def column_data_type(arr): counts["datetime"] += 1 else: counts["text"] += 1 - counts = sorted(counts.items(), key=lambda x: x[1] * -1) - ty = counts[0][0] + if float_flag: + ty = "float" + else: + counts = sorted(counts.items(), key=lambda x: x[1] * -1) + ty = counts[0][0] for i in range(len(arr)): if arr[i] is None: continue From 18fd7983f13cf8114df4e3fc989cd09b65cf9ec0 Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:21:54 +0800 Subject: [PATCH 0023/1187] Docs: exporting created knowledge graphs is not supported (#8465) ### What problem does this PR solve? _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) - [ ] New Feature (non-breaking change which adds functionality) - [x] Documentation Update - [ ] Refactoring - [ ] Performance Improvement - [ ] Other (please describe): --- docs/guides/dataset/construct_knowledge_graph.md | 6 +++++- docs/references/http_api_reference.md | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/guides/dataset/construct_knowledge_graph.md b/docs/guides/dataset/construct_knowledge_graph.md index 83f0b9b3fc2..b807f933242 100644 --- a/docs/guides/dataset/construct_knowledge_graph.md +++ b/docs/guides/dataset/construct_knowledge_graph.md @@ -93,4 +93,8 @@ To remove the generated knowledge graph, delete all related files in your knowle ### Where is the created knowledge graph stored? -All chunks of the created knowledge graph are stored in RAGFlow's document engine: either Elasticsearch or [Infinity](https://github.com/infiniflow/infinity). \ No newline at end of file +All chunks of the created knowledge graph are stored in RAGFlow's document engine: either Elasticsearch or [Infinity](https://github.com/infiniflow/infinity). + +### How to export a created knowledge graph? + +Nope. Exporting a created knowledge graph is not supported. If you still consider this feature essential, please [raise an issue](https://github.com/infiniflow/ragflow/issues) explaining your use case and its importance. \ No newline at end of file diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index aef4ce2a2c2..35ca73bb04d 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -3226,7 +3226,9 @@ Failure: Generates five to ten alternative question strings from the user's original query to retrieve more relevant search results. -This operation requires a `Bearer Login Token`, which typically expires with in 24 hours. You can find the it in the Request Headers in your browser easily. +This operation requires a `Bearer Login Token`, which typically expires with in 24 hours. You can find the it in the Request Headers in your browser easily as shown below: + +![Image](https://github.com/user-attachments/assets/17a5a0fe-e411-4e35-8251-85c71444468b) :::tip NOTE The chat model autonomously determines the number of questions to generate based on the instruction, typically between five and ten. From af6850c8d8cb32620b4e5a0e3ffece5ac117302b Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 25 Jun 2025 09:26:04 +0800 Subject: [PATCH 0024/1187] Feat: add MCP dashboard operations (#8460) ### What problem does this PR solve? Add MCP server dashboard operations. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/mcp_server_app.py | 173 +++++++++++++++++++++----- api/apps/search_app.py | 4 +- api/db/__init__.py | 4 +- api/db/db_models.py | 8 +- api/db/services/mcp_server_service.py | 39 ++++-- api/utils/web_utils.py | 12 +- mcp/server/simple_tools_server.py | 23 ---- 7 files changed, 192 insertions(+), 71 deletions(-) delete mode 100644 mcp/server/simple_tools_server.py diff --git a/api/apps/mcp_server_app.py b/api/apps/mcp_server_app.py index 188756167f9..185788c8158 100644 --- a/api/apps/mcp_server_app.py +++ b/api/apps/mcp_server_app.py @@ -1,39 +1,44 @@ from flask import Response, request from flask_login import current_user, login_required + +from api.db import VALID_MCP_SERVER_TYPES from api.db.db_models import MCPServer from api.db.services.mcp_server_service import MCPServerService from api.db.services.user_service import TenantService from api.settings import RetCode from api.utils import get_uuid from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request +from api.utils.web_utils import safe_json_parse -@manager.route("/list", methods=["GET"]) # noqa: F821 +@manager.route("/list", methods=["POST"]) # noqa: F821 @login_required -def get_list() -> Response: +def list_mcp() -> Response: + keywords = request.args.get("keywords", "") + page_number = int(request.args.get("page", 0)) + items_per_page = int(request.args.get("page_size", 0)) + orderby = request.args.get("orderby", "create_time") + if request.args.get("desc", "true").lower() == "false": + desc = False + else: + desc = True + + req = request.get_json() + mcp_ids = req.get("mcp_ids", []) try: - return get_json_result(data=MCPServerService.get_servers(current_user.id) or []) - except Exception as e: - return server_error_response(e) - - -@manager.route("/get_multiple", methods=["POST"]) # noqa: F821 -@login_required -@validate_request("id_list") -def get_multiple() -> Response: - req = request.json + servers = MCPServerService.get_servers(current_user.id, mcp_ids, page_number, items_per_page, orderby, desc, keywords) or [] - try: - return get_json_result(data=MCPServerService.get_servers(current_user.id, id_list=req["id_list"]) or []) + return get_json_result(data={"mcp_servers": servers, "total": len(servers)}) except Exception as e: return server_error_response(e) -@manager.route("/get/", methods=["GET"]) # noqa: F821 +@manager.route("/detail", methods=["GET"]) # noqa: F821 @login_required -def get(ms_id: str) -> Response: +def detail() -> Response: + mcp_id = request.args["mcp_id"] try: - mcp_server = MCPServerService.get_or_none(id=ms_id, tenant_id=current_user.id) + mcp_server = MCPServerService.get_or_none(id=mcp_id, tenant_id=current_user.id) if mcp_server is None: return get_json_result(code=RetCode.NOT_FOUND, data=None) @@ -47,7 +52,18 @@ def get(ms_id: str) -> Response: @login_required @validate_request("name", "url", "server_type") def create() -> Response: - req = request.json + req = request.get_json() + + server_type = req.get("server_type", "") + if server_type not in VALID_MCP_SERVER_TYPES: + return get_data_error_result(message="Unsupported MCP server type.") + + server_name = req.get("name", "") + if not server_name or len(server_name.encode("utf-8")) > 255: + return get_data_error_result(message=f"Invaild MCP name or length is {len(server_name)} which is large than 255.") + + req["headers"] = safe_json_parse(req.get("headers", {})) + req["variables"] = safe_json_parse(req.get("variables", {})) try: req["id"] = get_uuid() @@ -58,9 +74,6 @@ def create() -> Response: if not e: return get_data_error_result(message="Tenant not found.") - if not req.get("headers"): - req["headers"] = {} - if not MCPServerService.insert(**req): return get_data_error_result() @@ -71,37 +84,131 @@ def create() -> Response: @manager.route("/update", methods=["POST"]) # noqa: F821 @login_required -@validate_request("id", "name", "url", "server_type") +@validate_request("id") def update() -> Response: - req = request.json + req = request.get_json() + + server_type = req.get("server_type", "") + if server_type and server_type not in VALID_MCP_SERVER_TYPES: + return get_data_error_result(message="Unsupported MCP server type.") + server_name = req.get("name", "") + if server_name and len(server_name.encode("utf-8")) > 255: + return get_data_error_result(message=f"Invaild MCP name or length is {len(server_name)} which is large than 255.") - if not req.get("headers"): - req["headers"] = {} + req["headers"] = safe_json_parse(req.get("headers", {})) + req["variables"] = safe_json_parse(req.get("variables", {})) try: req["tenant_id"] = current_user.id if not MCPServerService.filter_update([MCPServer.id == req["id"], MCPServer.tenant_id == req["tenant_id"]], req): - return get_data_error_result() + return get_data_error_result(message="Failed to updated MCP server.") - return get_json_result(data={"id": req["id"]}) + e, updated_mcp = MCPServerService.get_by_id(req["id"]) + if not e: + return get_data_error_result(message="Failed to fetch updated MCP server.") + + return get_json_result(data=updated_mcp.to_dict()) except Exception as e: return server_error_response(e) @manager.route("/rm", methods=["POST"]) # noqa: F821 @login_required -@validate_request("id") +@validate_request("mcp_ids") def rm() -> Response: - req = request.json - ms_id = req["id"] + req = request.get_json() + mcp_ids = req.get("mcp_ids", []) try: req["tenant_id"] = current_user.id - if not MCPServerService.filter_delete([MCPServer.id == ms_id, MCPServer.tenant_id == req["tenant_id"]]): - return get_data_error_result() + if not MCPServerService.delete_by_ids(mcp_ids): + return get_data_error_result(message=f"Failed to delete MCP servers {mcp_ids}") - return get_json_result(data={"id": req["id"]}) + return get_json_result(data=True) + except Exception as e: + return server_error_response(e) + + +@manager.route("/import", methods=["POST"]) # noqa: F821 +@login_required +@validate_request("mcpServers") +def import_multiple() -> Response: + req = request.get_json() + servers = req.get("mcpServers", {}) + + if not servers: + return get_data_error_result(message="No MCP servers provided.") + + results = [] + try: + for server_name, config in servers.items(): + if not all(key in config for key in ["type", "url"]): + results.append({"server": server_name, "success": False, "message": "Missing required fields (type or url)"}) + continue + + base_name = server_name + new_name = base_name + counter = 0 + + while True: + e, _ = MCPServerService.get_by_name_and_tenant(name=new_name, tenant_id=current_user.id) + if not e: + break + new_name = f"{base_name}_{counter}" + counter += 1 + + create_data = { + "id": get_uuid(), + "tenant_id": current_user.id, + "name": new_name, + "url": config["url"], + "server_type": config["type"], + "variables": {"authorization_token": config.get("authorization_token", ""), "tool_configuration": config.get("tool_configuration", {})}, + } + + if MCPServerService.insert(**create_data): + result = {"server": server_name, "success": True, "action": "created", "id": create_data["id"], "new_name": new_name} + if new_name != base_name: + result["message"] = f"Renamed from '{base_name}' to avoid duplication" + + results.append(result) + else: + results.append({"server": server_name, "success": False, "message": "Failed to create MCP server."}) + + return get_json_result(data={"results": results}) + except Exception as e: + return server_error_response(e) + + +@manager.route("/export", methods=["POST"]) # noqa: F821 +@login_required +@validate_request("mcp_ids") +def export_multiple() -> Response: + req = request.get_json() + mcp_ids = req.get("mcp_ids", []) + + if not mcp_ids: + return get_data_error_result(message="No MCP server IDs provided.") + + try: + exported_servers = {} + + for mcp_id in mcp_ids: + e, mcp_server = MCPServerService.get_by_id(mcp_id) + + if e and mcp_server.tenant_id == current_user.id: + server_key = mcp_server.name + + exported_servers[server_key] = { + "type": mcp_server.server_type, + "url": mcp_server.url, + "name": mcp_server.name, + "authorization_token": mcp_server.variables.get("authorization_token", ""), + "tool_configuration": mcp_server.variables.get("tool_configuration", {}), + } + + return get_json_result(data={"mcpServers": exported_servers}) except Exception as e: return server_error_response(e) diff --git a/api/apps/search_app.py b/api/apps/search_app.py index 083e6308331..25d11381cca 100644 --- a/api/apps/search_app.py +++ b/api/apps/search_app.py @@ -40,8 +40,8 @@ def create(): return get_data_error_result(message="Search name must be string.") if search_name.strip() == "": return get_data_error_result(message="Search name can't be empty.") - if len(search_name.encode("utf-8")) > DATASET_NAME_LIMIT: - return get_data_error_result(message=f"Search name length is {len(search_name)} which is large than {DATASET_NAME_LIMIT}") + if len(search_name.encode("utf-8")) > 255: + return get_data_error_result(message=f"Search name length is {len(search_name)} which is large than 255.") e, _ = TenantService.get_by_id(current_user.id) if not e: return get_data_error_result(message="Authorizationd identity.") diff --git a/api/db/__init__.py b/api/db/__init__.py index 54cc9853396..a7fb046f803 100644 --- a/api/db/__init__.py +++ b/api/db/__init__.py @@ -107,6 +107,8 @@ class CanvasType(StrEnum): class MCPServerType(StrEnum): SSE = "sse" - StreamableHttp = "streamable-http" + STREAMABLE_HTTP = "streamable-http" + +VALID_MCP_SERVER_TYPES = {MCPServerType.SSE, MCPServerType.STREAMABLE_HTTP} KNOWLEDGEBASE_FOLDER_NAME=".knowledgebase" diff --git a/api/db/db_models.py b/api/db/db_models.py index 839203a5f56..5eba272d98e 100644 --- a/api/db/db_models.py +++ b/api/db/db_models.py @@ -806,13 +806,13 @@ class MCPServer(DataBaseModel): url = CharField(max_length=2048, null=False, help_text="MCP Server URL") server_type = CharField(max_length=32, null=False, help_text="MCP Server type") description = TextField(null=True, help_text="MCP Server description") - variables = JSONField(null=True, default=[], help_text="MCP Server variables") - headers = JSONField(null=True, default={}, help_text="MCP Server additional request headers") + variables = JSONField(null=True, default=dict, help_text="MCP Server variables") + headers = JSONField(null=True, default=dict, help_text="MCP Server additional request headers") class Meta: db_table = "mcp_server" - + class Search(DataBaseModel): id = CharField(max_length=32, primary_key=True) avatar = TextField(null=True, help_text="avatar base64 string") @@ -949,6 +949,6 @@ def migrate_db(): except Exception: pass try: - migrate(migrator.add_column("mcp_server", "variables", JSONField(null=True, help_text="MCP Server variables", default=[]))) + migrate(migrator.add_column("mcp_server", "variables", JSONField(null=True, help_text="MCP Server variables", default=dict))) except Exception: pass diff --git a/api/db/services/mcp_server_service.py b/api/db/services/mcp_server_service.py index 43bc75f6c92..869350094b7 100644 --- a/api/db/services/mcp_server_service.py +++ b/api/db/services/mcp_server_service.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from peewee import fn + from api.db.db_models import DB, MCPServer from api.db.services.common_service import CommonService @@ -31,7 +33,7 @@ class MCPServerService(CommonService): @classmethod @DB.connection_context() - def get_servers(cls, tenant_id: str, id_list: list[str] | None = None): + def get_servers(cls, tenant_id: str, id_list: list[str] | None, page_number, items_per_page, orderby, desc, keywords): """Retrieve all MCP servers associated with a tenant. This method fetches all MCP servers for a given tenant, ordered by creation time. @@ -46,16 +48,39 @@ def get_servers(cls, tenant_id: str, id_list: list[str] | None = None): Returns None if no MCP servers are found. """ fields = [ - cls.model.id, cls.model.name, cls.model.server_type, cls.model.url, cls.model.description, - cls.model.variables, cls.model.update_date + cls.model.id, + cls.model.name, + cls.model.server_type, + cls.model.url, + cls.model.description, + cls.model.variables, + cls.model.create_date, + cls.model.update_date, ] - servers = cls.model.select(*fields).order_by(cls.model.create_time.desc()).where(cls.model.tenant_id == tenant_id) + query = cls.model.select(*fields).order_by(cls.model.create_time.desc()).where(cls.model.tenant_id == tenant_id) - if id_list is not None: - servers = servers.where(cls.model.id.in_(id_list)) + if id_list: + query = query.where(cls.model.id.in_(id_list)) + if keywords: + query = query.where(fn.LOWER(cls.model.name).contains(keywords.lower())) + if desc: + query = query.order_by(cls.model.getter_by(orderby).desc()) + else: + query = query.order_by(cls.model.getter_by(orderby).asc()) + if page_number and items_per_page: + query = query.paginate(page_number, items_per_page) - servers = list(servers.dicts()) + servers = list(query.dicts()) if not servers: return None return servers + + @classmethod + @DB.connection_context() + def get_by_name_and_tenant(cls, name: str, tenant_id: str): + try: + mcp_server = cls.model.query(name=name, tenant_id=tenant_id) + return bool(mcp_server), mcp_server + except Exception: + return False, None diff --git a/api/utils/web_utils.py b/api/utils/web_utils.py index 084b7a6f73f..de3d692dd5c 100644 --- a/api/utils/web_utils.py +++ b/api/utils/web_utils.py @@ -116,4 +116,14 @@ def is_valid_url(url: str) -> bool: return False except socket.gaierror: return False - return True \ No newline at end of file + return True + + +def safe_json_parse(data: str | dict) -> dict: + if isinstance(data, dict): + return data + try: + return json.loads(data) if data else {} + except (json.JSONDecodeError, TypeError): + return {} + diff --git a/mcp/server/simple_tools_server.py b/mcp/server/simple_tools_server.py deleted file mode 100644 index f5f9a5257fa..00000000000 --- a/mcp/server/simple_tools_server.py +++ /dev/null @@ -1,23 +0,0 @@ -from mcp.server import FastMCP - - -app = FastMCP("simple-tools", port=8080) - - -@app.tool() -async def bad_calculator(a: int, b: int) -> str: - """ - A calculator to sum up two numbers (will give wrong answer) - - Args: - a: The first number - b: The second number - - Returns: - Sum of a and b - """ - return str(a + b + 200) - - -if __name__ == "__main__": - app.run(transport="sse") From 8d9d2cc0a9009dc59eb5309bdf06e2fd61f86364 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Wed, 25 Jun 2025 09:58:55 +0800 Subject: [PATCH 0025/1187] Fix: some cases Task return but not set progress (#8469) ### What problem does this PR solve? https://github.com/infiniflow/ragflow/issues/8466 I go through the codes, current logic: When do_handle_task raises an exception, handle_task will set the progress, but for some cases do_handle_task internal will just return but not set the right progress, at this cases the redis stream will been acked but the task is running. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --------- Co-authored-by: Kevin Hu --- api/db/services/task_service.py | 12 ++++++------ rag/svr/task_executor.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/db/services/task_service.py b/api/db/services/task_service.py index 5fd0eefc3ca..1fdfed35025 100644 --- a/api/db/services/task_service.py +++ b/api/db/services/task_service.py @@ -387,12 +387,12 @@ def new_task(): for task in parse_task_array: ck_num += reuse_prev_task_chunks(task, prev_tasks, chunking_config) TaskService.filter_delete([Task.doc_id == doc["id"]]) - chunk_ids = [] - for task in prev_tasks: - if task["chunk_ids"]: - chunk_ids.extend(task["chunk_ids"].split()) - if chunk_ids: - settings.docStoreConn.delete({"id": chunk_ids}, search.index_name(chunking_config["tenant_id"]), + pre_chunk_ids = [] + for pre_task in prev_tasks: + if pre_task["chunk_ids"]: + pre_chunk_ids.extend(pre_task["chunk_ids"].split()) + if pre_chunk_ids: + settings.docStoreConn.delete({"id": pre_chunk_ids}, search.index_name(chunking_config["tenant_id"]), chunking_config["kb_id"]) DocumentService.update_by_id(doc["id"], {"chunk_num": ck_num}) diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index b028f7125e3..0377c72c33e 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -543,6 +543,7 @@ async def do_handle_task(task): # Either using graphrag or Standard chunking methods elif task.get("task_type", "") == "graphrag": if not task_parser_config.get("graphrag", {}).get("use_graphrag", False): + progress_callback(prog=-1.0, msg="Internal configuration error.") return graphrag_conf = task["kb_parser_config"].get("graphrag", {}) start_ts = timer() @@ -558,8 +559,6 @@ async def do_handle_task(task): start_ts = timer() chunks = await build_chunks(task, progress_callback) logging.info("Build document {}: {:.2f}s".format(task_document_name, timer() - start_ts)) - if chunks is None: - return if not chunks: progress_callback(1., msg=f"No chunk built from {task_document_name}") return @@ -614,6 +613,7 @@ async def delete_image(kb_id, chunk_id): async with trio.open_nursery() as nursery: for chunk_id in chunk_ids: nursery.start_soon(delete_image, task_dataset_id, chunk_id) + progress_callback(-1, msg=f"Chunk updates failed since task {task['id']} is unknown.") return logging.info("Indexing doc({}), page({}-{}), chunks({}), elapsed: {:.2f}".format(task_document_name, task_from_page, From f21827bc287f98bc4d7f2fbd183a455d92a49c8f Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 25 Jun 2025 10:01:54 +0800 Subject: [PATCH 0026/1187] Feat: add MCP treamable-http transport (#8449) ### What problem does this PR solve? Add MCP treamable-http transport. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- mcp/client/streamable_http_client.py | 36 ++++++ mcp/server/server.py | 163 +++++++++++++++++++++++---- 2 files changed, 175 insertions(+), 24 deletions(-) create mode 100644 mcp/client/streamable_http_client.py diff --git a/mcp/client/streamable_http_client.py b/mcp/client/streamable_http_client.py new file mode 100644 index 00000000000..ec679e650a8 --- /dev/null +++ b/mcp/client/streamable_http_client.py @@ -0,0 +1,36 @@ +# +# 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. +# +from mcp import ClientSession +from mcp.client.streamable_http import streamablehttp_client + + +async def main(): + try: + async with streamablehttp_client("http://localhost:9382/mcp/") as (read_stream, write_stream, _): + async with ClientSession(read_stream, write_stream) as session: + await session.initialize() + tools = await session.list_tools() + print(f"{tools.tools=}") + response = await session.call_tool(name="ragflow_retrieval", arguments={"dataset_ids": ["bc4177924a7a11f09eff238aa5c10c94"], "document_ids": [], "question": "How to install neovim?"}) + print(f"Tool response: {response.model_dump()}") + except Exception as e: + print(e) + + +if __name__ == "__main__": + from anyio import run + + run(main) diff --git a/mcp/server/server.py b/mcp/server/server.py index 899cbca4992..cb000b3be74 100644 --- a/mcp/server/server.py +++ b/mcp/server/server.py @@ -15,6 +15,7 @@ # import json +import logging from collections.abc import AsyncIterator from contextlib import asynccontextmanager from functools import wraps @@ -29,7 +30,6 @@ import mcp.types as types from mcp.server.lowlevel import Server -from mcp.server.sse import SseServerTransport class LaunchMode(StrEnum): @@ -37,11 +37,19 @@ class LaunchMode(StrEnum): HOST = "host" +class Transport(StrEnum): + SSE = "sse" + STEAMABLE_HTTP = "streamable-http" + + BASE_URL = "http://127.0.0.1:9380" HOST = "127.0.0.1" PORT = "9382" HOST_API_KEY = "" MODE = "" +TRANSPORT_SSE_ENABLED = True +TRANSPORT_STREAMABLE_HTTP_ENABLED = True +JSON_RESPONSE = True class RAGFlowConnector: @@ -115,17 +123,17 @@ def __init__(self, connector: RAGFlowConnector): @asynccontextmanager -async def server_lifespan(server: Server) -> AsyncIterator[dict]: +async def sse_lifespan(server: Server) -> AsyncIterator[dict]: ctx = RAGFlowCtx(RAGFlowConnector(base_url=BASE_URL)) + logging.info("Legacy SSE application started with StreamableHTTP session manager!") try: yield {"ragflow_ctx": ctx} finally: - pass + logging.info("Legacy SSE application shutting down...") -app = Server("ragflow-server", lifespan=server_lifespan) -sse = SseServerTransport("/messages/") +app = Server("ragflow-mcp-server", lifespan=sse_lifespan) def with_api_key(required=True): @@ -206,13 +214,8 @@ async def call_tool(name: str, arguments: dict, *, connector) -> list[types.Text raise ValueError(f"Tool not found: {name}") -async def handle_sse(request): - async with sse.connect_sse(request.scope, request.receive, request._send) as streams: - await app.run(streams[0], streams[1], app.create_initialization_options(experimental_capabilities={"headers": dict(request.headers)})) - return Response() - - def create_starlette_app(): + routes = [] middleware = None if MODE == LaunchMode.HOST: from starlette.types import ASGIApp, Receive, Scope, Send @@ -227,7 +230,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send): return path = scope["path"] - if path.startswith("/messages/") or path.startswith("/sse"): + if path.startswith("/messages/") or path.startswith("/sse") or path.startswith("/mcp"): headers = dict(scope["headers"]) token = None auth_header = headers.get(b"authorization") @@ -245,13 +248,57 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send): middleware = [Middleware(AuthMiddleware)] + # Add SSE routes if enabled + if TRANSPORT_SSE_ENABLED: + from mcp.server.sse import SseServerTransport + + sse = SseServerTransport("/messages/") + + async def handle_sse(request): + async with sse.connect_sse(request.scope, request.receive, request._send) as streams: + await app.run(streams[0], streams[1], app.create_initialization_options(experimental_capabilities={"headers": dict(request.headers)})) + return Response() + + routes.extend( + [ + Route("/sse", endpoint=handle_sse, methods=["GET"]), + Mount("/messages/", app=sse.handle_post_message), + ] + ) + + # Add streamable HTTP route if enabled + streamablehttp_lifespan = None + if TRANSPORT_STREAMABLE_HTTP_ENABLED: + from starlette.types import Receive, Scope, Send + + from mcp.server.streamable_http_manager import StreamableHTTPSessionManager + + session_manager = StreamableHTTPSessionManager( + app=app, + event_store=None, + json_response=JSON_RESPONSE, + stateless=True, + ) + + async def handle_streamable_http(scope: Scope, receive: Receive, send: Send) -> None: + await session_manager.handle_request(scope, receive, send) + + @asynccontextmanager + async def streamablehttp_lifespan(app: Starlette) -> AsyncIterator[None]: + async with session_manager.run(): + logging.info("StreamableHTTP application started with StreamableHTTP session manager!") + try: + yield + finally: + logging.info("StreamableHTTP application shutting down...") + + routes.append(Mount("/mcp", app=handle_streamable_http)) + return Starlette( debug=True, - routes=[ - Route("/sse", endpoint=handle_sse, methods=["GET"]), - Mount("/messages/", app=sse.handle_post_message), - ], + routes=routes, middleware=middleware, + lifespan=streamablehttp_lifespan, ) @@ -266,7 +313,22 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send): help=("Launch mode:\n self-host: run MCP for a single tenant (requires --api-key)\n host: multi-tenant mode, users must provide Authorization headers"), ) @click.option("--api-key", type=str, default="", help="API key to use when in self-host mode") -def main(base_url, host, port, mode, api_key): +@click.option( + "--transport-sse-enabled/--no-transport-sse-enabled", + default=True, + help="Enable or disable legacy SSE transport mode (default: enabled)", +) +@click.option( + "--transport-streamable-http-enabled/--no-transport-streamable-http-enabled", + default=True, + help="Enable or disable streamable-http transport mode (default: enabled)", +) +@click.option( + "--json-response/--no-json-response", + default=True, + help="Enable or disable JSON response mode for streamable-http (default: enabled)", +) +def main(base_url, host, port, mode, api_key, transport_sse_enabled, transport_streamable_http_enabled, json_response): import os import uvicorn @@ -274,16 +336,29 @@ def main(base_url, host, port, mode, api_key): load_dotenv() - global BASE_URL, HOST, PORT, MODE, HOST_API_KEY + def parse_bool_flag(key: str, default: bool) -> bool: + val = os.environ.get(key, str(default)) + return str(val).strip().lower() in ("1", "true", "yes", "on") + + global BASE_URL, HOST, PORT, MODE, HOST_API_KEY, TRANSPORT_SSE_ENABLED, TRANSPORT_STREAMABLE_HTTP_ENABLED, JSON_RESPONSE BASE_URL = os.environ.get("RAGFLOW_MCP_BASE_URL", base_url) HOST = os.environ.get("RAGFLOW_MCP_HOST", host) PORT = os.environ.get("RAGFLOW_MCP_PORT", str(port)) MODE = os.environ.get("RAGFLOW_MCP_LAUNCH_MODE", mode) HOST_API_KEY = os.environ.get("RAGFLOW_MCP_HOST_API_KEY", api_key) + TRANSPORT_SSE_ENABLED = parse_bool_flag("RAGFLOW_MCP_TRANSPORT_SSE_ENABLED", transport_sse_enabled) + TRANSPORT_STREAMABLE_HTTP_ENABLED = parse_bool_flag("RAGFLOW_MCP_TRANSPORT_STREAMABLE_ENABLED", transport_streamable_http_enabled) + JSON_RESPONSE = parse_bool_flag("RAGFLOW_MCP_JSON_RESPONSE", json_response) - if MODE == "self-host" and not HOST_API_KEY: + if MODE == LaunchMode.SELF_HOST and not HOST_API_KEY: raise click.UsageError("--api-key is required when --mode is 'self-host'") + if TRANSPORT_STREAMABLE_HTTP_ENABLED and MODE == LaunchMode.HOST: + raise click.UsageError("The --host mode is not supported with streamable-http transport yet.") + + if not TRANSPORT_STREAMABLE_HTTP_ENABLED and JSON_RESPONSE: + JSON_RESPONSE = False + print( r""" __ __ ____ ____ ____ _____ ______ _______ ____ @@ -299,6 +374,24 @@ def main(base_url, host, port, mode, api_key): print(f"MCP port: {PORT}", flush=True) print(f"MCP base_url: {BASE_URL}", flush=True) + if TRANSPORT_SSE_ENABLED: + print("SSE transport enabled: yes", flush=True) + print("SSE endpoint available at /sse", flush=True) + else: + print("SSE transport enabled: no", flush=True) + + if TRANSPORT_STREAMABLE_HTTP_ENABLED: + print("Streamable HTTP transport enabled: yes", flush=True) + print("Streamable HTTP endpoint available at /mcp", flush=True) + if JSON_RESPONSE: + print("Streamable HTTP mode: JSON response enabled", flush=True) + else: + print("Streamable HTTP mode: SSE over HTTP enabled", flush=True) + else: + print("Streamable HTTP transport enabled: no", flush=True) + if JSON_RESPONSE: + print("Warning: --json-response ignored because streamable transport is disabled.", flush=True) + uvicorn.run( create_starlette_app(), host=HOST, @@ -308,10 +401,32 @@ def main(base_url, host, port, mode, api_key): if __name__ == "__main__": """ - Launch example: - self-host: - uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=self-host --api-key=ragflow-xxxxx - host: - uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base-url=http://127.0.0.1:9380 --mode=host + Launch examples: + + 1. Self-host mode with both SSE and Streamable HTTP (in JSON response mode) enabled (default): + uv run mcp/server/server.py --host=127.0.0.1 --port=9382 \ + --base-url=http://127.0.0.1:9380 \ + --mode=self-host --api-key=ragflow-xxxxx + + 2. Host mode (multi-tenant, self-host only, clients must provide Authorization headers): + uv run mcp/server/server.py --host=127.0.0.1 --port=9382 \ + --base-url=http://127.0.0.1:9380 \ + --mode=host + + 3. Disable legacy SSE (only streamable HTTP will be active): + uv run mcp/server/server.py --no-transport-sse-enabled \ + --mode=self-host --api-key=ragflow-xxxxx + + 4. Disable streamable HTTP (only legacy SSE will be active): + uv run mcp/server/server.py --no-transport-streamable-http-enabled \ + --mode=self-host --api-key=ragflow-xxxxx + + 5. Use streamable HTTP with SSE-style events (disable JSON response): + uv run mcp/server/server.py --transport-streamable-http-enabled --no-json-response \ + --mode=self-host --api-key=ragflow-xxxxx + + 6. Disable both transports (for testing): + uv run mcp/server/server.py --no-transport-sse-enabled --no-transport-streamable-http-enabled \ + --mode=self-host --api-key=ragflow-xxxxx """ main() From 5256980ffbf3b790c7f4583a76c7d18ce23b7580 Mon Sep 17 00:00:00 2001 From: liuzhenghua <1090179900@qq.com> Date: Wed, 25 Jun 2025 10:25:45 +0800 Subject: [PATCH 0027/1187] Fix: Solve the OOM issue when passing large PDF files while using QA chunking method. (#8464) ### What problem does this PR solve? Using the QA chunking method with a large PDF (e.g., 300+ pages) may lead to OOM in the ragflow-worker module. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/app/qa.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rag/app/qa.py b/rag/app/qa.py index 7ce0afabc24..803baa1029d 100644 --- a/rag/app/qa.py +++ b/rag/app/qa.py @@ -310,7 +310,7 @@ def mdQuestionLevel(s): return (len(match.group(0)), s.lstrip('#').lstrip()) if match else (0, s) -def chunk(filename, binary=None, lang="Chinese", callback=None, **kwargs): +def chunk(filename, binary=None, from_page=0, to_page=100000, lang="Chinese", callback=None, **kwargs): """ Excel and csv(txt) format files are supported. If the file is in excel format, there should be 2 column question and answer without header. @@ -410,7 +410,7 @@ def chunk(filename, binary=None, lang="Chinese", callback=None, **kwargs): callback(0.1, "Start to parse.") pdf_parser = Pdf() qai_list, tbls = pdf_parser(filename if not binary else binary, - from_page=0, to_page=10000, callback=callback) + from_page=from_page, to_page=to_page, callback=callback) for q, a, image, poss in qai_list: res.append(beAdocPdf(deepcopy(doc), q, a, eng, image, poss)) return res @@ -468,4 +468,4 @@ def chunk(filename, binary=None, lang="Chinese", callback=None, **kwargs): def dummy(prog=None, msg=""): pass - chunk(sys.argv[1], from_page=0, to_page=10, callback=dummy) \ No newline at end of file + chunk(sys.argv[1], from_page=0, to_page=10, callback=dummy) From ece27c66e9f1e212d5a7c7b1487409e61578052d Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 25 Jun 2025 15:24:22 +0800 Subject: [PATCH 0028/1187] Feat: Insert the node data of the bottom subagent into the tool array of the head agent #3221 (#8471) ### What problem does this PR solve? Feat: Insert the node data of the bottom subagent into the tool array of the head agent #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/form/agent-form/index.tsx | 8 ++- web/src/pages/agent/utils.ts | 52 ++++++++++++++++--- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index fe7b8af861e..dabbffc7201 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -17,10 +17,11 @@ import { useContext, useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { NodeHandleId, Operator, initialAgentValues } from '../../constant'; +import { Operator, initialAgentValues } from '../../constant'; import { AgentInstanceContext } from '../../context'; import { INextOperatorForm } from '../../interface'; import useGraphStore from '../../store'; +import { isBottomSubAgent } from '../../utils'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; import { AgentTools } from './agent-tools'; @@ -57,10 +58,7 @@ const AgentForm = ({ node }: INextOperatorForm) => { const defaultValues = useValues(node); const isSubAgent = useMemo(() => { - const edge = edges.find( - (x) => x.target === node?.id && x.targetHandle === NodeHandleId.AgentTop, - ); - return !!edge; + return isBottomSubAgent(edges, node?.id); }, [edges, node?.id]); const outputList = useMemo(() => { diff --git a/web/src/pages/agent/utils.ts b/web/src/pages/agent/utils.ts index f670422859f..c4c0d0021f4 100644 --- a/web/src/pages/agent/utils.ts +++ b/web/src/pages/agent/utils.ts @@ -15,6 +15,7 @@ import { v4 as uuidv4 } from 'uuid'; import { CategorizeAnchorPointPositions, NoDebugOperatorsList, + NodeHandleId, NodeMap, Operator, } from './constant'; @@ -100,12 +101,20 @@ const buildComponentDownstreamOrUpstream = ( .filter((y) => { const node = nodes.find((x) => x.id === nodeId); let isNotUpstreamTool = true; + let isNotUpstreamAgent = true; if (isBuildDownstream && node?.data.label === Operator.Agent) { - isNotUpstreamTool = !y.target.startsWith(Operator.Tool); // Exclude the tool operator downstream of the agent operator + // Exclude the tool operator downstream of the agent operator + isNotUpstreamTool = !y.target.startsWith(Operator.Tool); + // Exclude the agent operator downstream of the agent operator + isNotUpstreamAgent = !( + y.target.startsWith(Operator.Agent) && + y.targetHandle === NodeHandleId.AgentTop + ); } return ( y[isBuildDownstream ? 'source' : 'target'] === nodeId && - isNotUpstreamTool + isNotUpstreamTool && + isNotUpstreamAgent ); }) .map((y) => y[isBuildDownstream ? 'target' : 'source']); @@ -130,6 +139,25 @@ const removeUselessDataInTheOperator = curry( // return values; // }); +function buildAgentTools(edges: Edge[], nodes: Node[], nodeId: string) { + const node = nodes.find((x) => x.id === nodeId); + const params = { ...(node?.data.form ?? {}) }; + if (node && node.data.label === Operator.Agent) { + const bottomSubAgentEdges = edges.filter( + (x) => x.source === nodeId && x.sourceHandle === NodeHandleId.AgentBottom, + ); + + (params as IAgentForm).tools = (params as IAgentForm).tools.concat( + bottomSubAgentEdges.map((x) => { + const formData = buildAgentTools(edges, nodes, x.target); + + return { component_name: Operator.Agent, params: { ...formData } }; + }), + ); + } + return params; +} + const buildOperatorParams = (operatorName: string) => pipe( removeUselessDataInTheOperator(operatorName), @@ -138,6 +166,13 @@ const buildOperatorParams = (operatorName: string) => const ExcludeOperators = [Operator.Note, Operator.Tool]; +export function isBottomSubAgent(edges: Edge[], nodeId?: string) { + const edge = edges.find( + (x) => x.target === nodeId && x.targetHandle === NodeHandleId.AgentTop, + ); + return !!edge; +} + // construct a dsl based on the node information of the graph export const buildDslComponentsByGraph = ( nodes: RAGFlowNodeType[], @@ -147,18 +182,21 @@ export const buildDslComponentsByGraph = ( const components: DSLComponents = {}; nodes - ?.filter((x) => !ExcludeOperators.some((y) => y === x.data.label)) + ?.filter( + (x) => + !ExcludeOperators.some((y) => y === x.data.label) && + !isBottomSubAgent(edges, x.id), + ) .forEach((x) => { const id = x.id; const operatorName = x.data.label; + + const params = buildAgentTools(edges, nodes, id); components[id] = { obj: { ...(oldDslComponents[id]?.obj ?? {}), component_name: operatorName, - params: - buildOperatorParams(operatorName)( - x.data.form as Record, - ) ?? {}, + params: buildOperatorParams(operatorName)(params) ?? {}, }, downstream: buildComponentDownstreamOrUpstream(edges, id, true, nodes), upstream: buildComponentDownstreamOrUpstream(edges, id, false, nodes), From de8ba7298c8f6f71db36cfc48cd2effc3ed266e6 Mon Sep 17 00:00:00 2001 From: ruansheng Date: Wed, 25 Jun 2025 15:24:37 +0800 Subject: [PATCH 0029/1187] RAGFlow service_conf using .env variable (#8454) ### What problem does this PR solve? Fix: when using external components, it is impossible to specify the port, because the variables in the `docker/.env` variable were not referenced by `docker/service_conf.yaml.template`. https://github.com/infiniflow/ragflow/blob/382d2d0373fea9554982a0718fc8eaf5fe8a5807/docker/.env#L85 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- docker/service_conf.yaml.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/service_conf.yaml.template b/docker/service_conf.yaml.template index 616ccb524b0..fb751d66904 100644 --- a/docker/service_conf.yaml.template +++ b/docker/service_conf.yaml.template @@ -12,7 +12,7 @@ mysql: minio: user: '${MINIO_USER:-rag_flow}' password: '${MINIO_PASSWORD:-infini_rag_flow}' - host: '${MINIO_HOST:-minio}:9000' + host: '${MINIO_HOST:-minio}:${MINIO_PORT:-9000}' es: hosts: 'http://${ES_HOST:-es01}:9200' username: '${ES_USER:-elastic}' @@ -27,7 +27,7 @@ infinity: redis: db: 1 password: '${REDIS_PASSWORD:-infini_rag_flow}' - host: '${REDIS_HOST:-redis}:6379' + host: '${REDIS_HOST:-redis}:${REDIS_PORT:-6379}' # postgres: # name: '${POSTGRES_DBNAME:-rag_flow}' From d63204603275bb8a0a1ee18d880f5e54490fb2c0 Mon Sep 17 00:00:00 2001 From: Tuan Le <30828528+tuankg1028@users.noreply.github.com> Date: Wed, 25 Jun 2025 14:36:54 +0700 Subject: [PATCH 0030/1187] Fixes typo in variable name (#8476) ### What problem does this PR solve? This PR fixes a typo in the variable name `succesfulFilenames`, correcting it to `successfulFilenames`. This ensures consistency and avoids potential errors due to the misspelled variable. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../pages/add-knowledge/components/knowledge-file/hooks.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts b/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts index 005a72e433a..e7ac6f11b8e 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts +++ b/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts @@ -175,7 +175,7 @@ export const useHandleUploadDocument = () => { const ret = await uploadDocument(filesPart); const files = ret?.data || []; - const succesfulFilenames = files.map((file: any) => file.name); + const successfulFilenames = files.map((file: any) => file.name); // set status to done or error on files (based on response) setFileList( @@ -185,7 +185,7 @@ export const useHandleUploadDocument = () => { } let newFile = file; - newFile.status = succesfulFilenames.includes(file.name) + newFile.status = successfulFilenames.includes(file.name) ? 'done' : 'error'; newFile.percent = 100; @@ -197,7 +197,7 @@ export const useHandleUploadDocument = () => { return { code: ret?.code, fileIds: files.map((file: any) => file.id), - totalSuccess: succesfulFilenames.length, + totalSuccess: successfulFilenames.length, }; }; const totalFiles = fileList.length; From b705ff08fe6d6c07235b9b4391ba1d63ef2f9062 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 25 Jun 2025 16:20:59 +0800 Subject: [PATCH 0031/1187] Refa: improve GraphRAG similarity sensitivity to numeric differences (#8479) ### What problem does this PR solve? Improve GraphRAG similarity sensitivity to numeric differences. #8444. ### Type of change - [x] Refactoring --- graphrag/entity_resolution.py | 13 +++++++++++++ rag/nlp/__init__.py | 22 +++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/graphrag/entity_resolution.py b/graphrag/entity_resolution.py index 97b13577595..8d26335ca01 100644 --- a/graphrag/entity_resolution.py +++ b/graphrag/entity_resolution.py @@ -218,7 +218,20 @@ def _process_results( return ans_list + def _has_digit_in_2gram_diff(self, a, b): + def to_2gram_set(s): + return {s[i:i+2] for i in range(len(s) - 1)} + + set_a = to_2gram_set(a) + set_b = to_2gram_set(b) + diff = set_a ^ set_b + + return any(any(c.isdigit() for c in pair) for pair in diff) + def is_similarity(self, a, b): + if self._has_digit_in_2gram_diff(a, b): + return False + if is_english(a) and is_english(b): if editdistance.eval(a, b) <= min(len(a), len(b)) // 2: return True diff --git a/rag/nlp/__init__.py b/rag/nlp/__init__.py index f88c059a5f4..78f73ece7fd 100644 --- a/rag/nlp/__init__.py +++ b/rag/nlp/__init__.py @@ -225,15 +225,23 @@ def bullets_category(sections): def is_english(texts): - eng = 0 if not texts: return False - for t in texts: - if re.match(r"[ `a-zA-Z.,':;/\"?<>!\(\)-]", t.strip()): - eng += 1 - if eng / len(texts) > 0.8: - return True - return False + + pattern = re.compile(r"[`a-zA-Z0-9\s.,':;/\"?<>!\(\)\-]") + + if isinstance(texts, str): + texts = list(texts) + elif isinstance(texts, list): + texts = [t for t in texts if isinstance(t, str) and t.strip()] + else: + return False + + if not texts: + return False + + eng = sum(1 for t in texts if pattern.fullmatch(t.strip())) + return (eng / len(texts)) > 0.8 def is_chinese(text): From c4b58ed19557a7203bae72eadabce554e6918507 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 25 Jun 2025 16:23:20 +0800 Subject: [PATCH 0032/1187] Feat: Filter the query variable drop-down box options by type #3221 (#8485) ### What problem does this PR solve? Feat: Filter the query variable drop-down box options by type #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../originui/select-with-search.tsx | 2 ++ .../pages/agent/canvas/node/agent-node.tsx | 14 +++++------ web/src/pages/agent/constant.tsx | 5 ++++ .../agent/form-sheet/use-form-config-map.tsx | 3 ++- .../agent/form/begin-form/use-edit-query.ts | 6 +++-- .../agent/form/components/query-variable.tsx | 17 ++++++++++--- .../pages/agent/form/iteration-form/index.tsx | 7 ++++-- web/src/pages/agent/hooks/use-add-node.ts | 22 +++++++++++++++- .../pages/agent/hooks/use-get-begin-query.tsx | 25 ++++++++++++++++--- web/src/pages/agent/hooks/use-show-drawer.tsx | 2 +- web/src/pages/agent/store.ts | 11 ++++++++ web/src/pages/flow/constant.tsx | 11 +++++++- .../flow/form/iteration-start-from/index.tsx | 22 ++++++++++++++++ 13 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 web/src/pages/flow/form/iteration-start-from/index.tsx diff --git a/web/src/components/originui/select-with-search.tsx b/web/src/components/originui/select-with-search.tsx index fbf1584e094..469802d3477 100644 --- a/web/src/components/originui/select-with-search.tsx +++ b/web/src/components/originui/select-with-search.tsx @@ -168,3 +168,5 @@ export const SelectWithSearch = forwardRef< ); }); + +SelectWithSearch.displayName = 'SelectWithSearch'; diff --git a/web/src/pages/agent/canvas/node/agent-node.tsx b/web/src/pages/agent/canvas/node/agent-node.tsx index 731f155a73a..8a585760e58 100644 --- a/web/src/pages/agent/canvas/node/agent-node.tsx +++ b/web/src/pages/agent/canvas/node/agent-node.tsx @@ -1,8 +1,9 @@ import { IAgentNode } from '@/interfaces/database/flow'; import { Handle, NodeProps, Position } from '@xyflow/react'; import { memo, useMemo } from 'react'; -import { NodeHandleId, Operator } from '../../constant'; +import { NodeHandleId } from '../../constant'; import useGraphStore from '../../store'; +import { isBottomSubAgent } from '../../utils'; import { CommonHandle } from './handle'; import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; import styles from './index.less'; @@ -16,19 +17,16 @@ function InnerAgentNode({ isConnectable = true, selected, }: NodeProps) { - const getNode = useGraphStore((state) => state.getNode); const edges = useGraphStore((state) => state.edges); - const isNotParentAgent = useMemo(() => { - const edge = edges.find((x) => x.target === id); - const label = getNode(edge?.source)?.data.label; - return label !== Operator.Agent; - }, [edges, getNode, id]); + const isHeadAgent = useMemo(() => { + return !isBottomSubAgent(edges, id); + }, [edges, id]); return ( - {isNotParentAgent && ( + {isHeadAgent && ( <> <>, + component: IterationStartForm, defaultValues: {}, schema: z.object({}), }, diff --git a/web/src/pages/agent/form/begin-form/use-edit-query.ts b/web/src/pages/agent/form/begin-form/use-edit-query.ts index a1bec8b3d10..05b8240f2a5 100644 --- a/web/src/pages/agent/form/begin-form/use-edit-query.ts +++ b/web/src/pages/agent/form/begin-form/use-edit-query.ts @@ -1,9 +1,12 @@ import { useSetModalState } from '@/hooks/common-hooks'; import { useSetSelectedRecord } from '@/hooks/logic-hooks'; import { useCallback, useMemo, useState } from 'react'; +import { UseFormReturn } from 'react-hook-form'; import { BeginQuery, INextOperatorForm } from '../../interface'; -export const useEditQueryRecord = ({ form, node }: INextOperatorForm) => { +export const useEditQueryRecord = ({ + form, +}: INextOperatorForm & { form: UseFormReturn }) => { const { setRecord, currentRecord } = useSetSelectedRecord(); const { visible, hideModal, showModal } = useSetModalState(); const [index, setIndex] = useState(-1); @@ -16,7 +19,6 @@ export const useEditQueryRecord = ({ form, node }: INextOperatorForm) => { const handleEditRecord = useCallback( (record: BeginQuery) => { const inputs: BeginQuery[] = form?.getValues('inputs') || []; - console.log('🚀 ~ useEditQueryRecord ~ inputs:', inputs); const nextQuery: BeginQuery[] = index > -1 ? inputs.toSpliced(index, 1, record) : [...inputs, record]; diff --git a/web/src/pages/agent/form/components/query-variable.tsx b/web/src/pages/agent/form/components/query-variable.tsx index 213d8dbe503..caa85cd7a38 100644 --- a/web/src/pages/agent/form/components/query-variable.tsx +++ b/web/src/pages/agent/form/components/query-variable.tsx @@ -6,18 +6,29 @@ import { FormLabel, FormMessage, } from '@/components/ui/form'; +import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import { VariableType } from '../../constant'; import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; -type QueryVariableProps = { name?: string }; +type QueryVariableProps = { name?: string; type?: VariableType }; -export function QueryVariable({ name = 'query' }: QueryVariableProps) { +export function QueryVariable({ + name = 'query', + type = VariableType.String, +}: QueryVariableProps) { const { t } = useTranslation(); const form = useFormContext(); const nextOptions = useBuildQueryVariableOptions(); + const finalOptions = useMemo(() => { + return nextOptions.map((x) => { + return { ...x, options: x.options.filter((y) => y.type === type) }; + }); + }, [nextOptions, type]); + return ( {t('flow.query')} diff --git a/web/src/pages/agent/form/iteration-form/index.tsx b/web/src/pages/agent/form/iteration-form/index.tsx index bb9b7436f1e..7a9d58dc132 100644 --- a/web/src/pages/agent/form/iteration-form/index.tsx +++ b/web/src/pages/agent/form/iteration-form/index.tsx @@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; -import { initialRetrievalValues } from '../../constant'; +import { initialRetrievalValues, VariableType } from '../../constant'; import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; import { Output } from '../components/output'; @@ -50,7 +50,10 @@ const IterationForm = ({ node }: INextOperatorForm) => { }} > - + diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index d4008ee0165..28aeef54912 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -232,8 +232,15 @@ function useAddToolNode() { return { addToolNode }; } +function isBottomSubAgent(type: string, position: Position) { + return ( + (type === Operator.Agent && position === Position.Bottom) || + type === Operator.Tool + ); +} + export function useAddNode(reactFlowInstance?: ReactFlowInstance) { - const { edges, nodes, addEdge, addNode, getNode } = useGraphStore( + const { edges, nodes, addEdge, addNode, getNode, updateNode } = useGraphStore( (state) => state, ); const getNodeName = useGetNodeName(); @@ -291,6 +298,18 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { if (node && node.parentId) { newNode.parentId = node.parentId; newNode.extent = 'parent'; + const parentNode = getNode(node.parentId); + if (parentNode && !isBottomSubAgent(type, params.position)) { + const MoveRightDistance = 310; + updateNode({ + ...parentNode, + width: (parentNode.width || 0) + MoveRightDistance, + position: { + x: parentNode.position.x + MoveRightDistance / 2, + y: parentNode.position.y, + }, + }); + } } if (type === Operator.Iteration) { @@ -377,6 +396,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { initializeOperatorParams, nodes, reactFlowInstance, + updateNode, ], ); diff --git a/web/src/pages/agent/hooks/use-get-begin-query.tsx b/web/src/pages/agent/hooks/use-get-begin-query.tsx index c53e23f489b..69ba6b8af71 100644 --- a/web/src/pages/agent/hooks/use-get-begin-query.tsx +++ b/web/src/pages/agent/hooks/use-get-begin-query.tsx @@ -5,7 +5,7 @@ import { DefaultOptionType } from 'antd/es/select'; import { isEmpty } from 'lodash'; import get from 'lodash/get'; import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import { BeginId, Operator } from '../constant'; +import { BeginId, BeginQueryType, Operator, VariableType } from '../constant'; import { AgentFormContext } from '../context'; import { buildBeginInputListFromObject } from '../form/begin-form/utils'; import { BeginQuery } from '../interface'; @@ -65,6 +65,7 @@ function buildOutputOptions( return Object.keys(outputs).map((x) => ({ label: x, value: `${nodeId}@${x}`, + type: outputs[x]?.type, })); } @@ -104,6 +105,19 @@ const ExcludedNodes = [ Operator.Note, ]; +const StringList = [ + BeginQueryType.Line, + BeginQueryType.Paragraph, + BeginQueryType.Options, +]; + +function transferToVariableType(type: string) { + if (StringList.some((x) => x === type)) { + return VariableType.String; + } + return type; +} + export function useBuildBeginVariableOptions() { const getBeginNodeDataQuery = useGetBeginNodeDataQuery(); @@ -116,6 +130,7 @@ export function useBuildBeginVariableOptions() { options: query.map((x) => ({ label: x.name, value: `begin@${x.key}`, + type: transferToVariableType(x.type), })), }, ]; @@ -141,9 +156,11 @@ export function useBuildQueryVariableOptions() { const options = useBuildVariableOptions(node?.id); const nextOptions = useMemo(() => { - const globalOptions = Object.keys(data?.dsl?.globals ?? {}).map((x) => ({ - label: x, - value: x, + const globals = data?.dsl?.globals ?? {}; + const globalOptions = Object.entries(globals).map(([key, value]) => ({ + label: key, + value: key, + type: Array.isArray(value) ? VariableType.Array : typeof value, })); return [ { ...options[0], options: [...options[0]?.options, ...globalOptions] }, diff --git a/web/src/pages/agent/hooks/use-show-drawer.tsx b/web/src/pages/agent/hooks/use-show-drawer.tsx index 990ea6f929f..b45863b79dc 100644 --- a/web/src/pages/agent/hooks/use-show-drawer.tsx +++ b/web/src/pages/agent/hooks/use-show-drawer.tsx @@ -57,7 +57,7 @@ export const useShowSingleDebugDrawer = () => { }; }; -const ExcludedNodes = [Operator.IterationStart, Operator.Note]; +const ExcludedNodes = [Operator.Note]; export function useShowDrawer({ drawerVisible, diff --git a/web/src/pages/agent/store.ts b/web/src/pages/agent/store.ts index 31ad0a46b3f..a78768e01b9 100644 --- a/web/src/pages/agent/store.ts +++ b/web/src/pages/agent/store.ts @@ -56,6 +56,7 @@ export type RFState = { onSelectionChange: OnSelectionChangeFunc; addNode: (nodes: RAGFlowNodeType) => void; getNode: (id?: string | null) => RAGFlowNodeType | undefined; + updateNode: (node: RAGFlowNodeType) => void; addEdge: (connection: Connection) => void; getEdge: (id: string) => Edge | undefined; updateFormDataOnConnect: (connection: Connection) => void; @@ -192,6 +193,16 @@ const useGraphStore = create()( addNode: (node: RAGFlowNodeType) => { set({ nodes: get().nodes.concat(node) }); }, + updateNode: (node) => { + const { nodes } = get(); + const nextNodes = nodes.map((x) => { + if (x.id === node.id) { + return node; + } + return x; + }); + set({ nodes: nextNodes }); + }, getNode: (id?: string | null) => { return get().nodes.find((x) => x.id === id); }, diff --git a/web/src/pages/flow/constant.tsx b/web/src/pages/flow/constant.tsx index 77ce945323f..7e5a7604d51 100644 --- a/web/src/pages/flow/constant.tsx +++ b/web/src/pages/flow/constant.tsx @@ -639,7 +639,16 @@ export const initialEmailValues = { export const initialIterationValues = { delimiter: ',', }; -export const initialIterationStartValues = {}; +export const initialIterationStartValues = { + outputs: { + item: { + type: 'unkown', + }, + index: { + type: 'integer', + }, + }, +}; export const initialCodeValues = { lang: 'python', diff --git a/web/src/pages/flow/form/iteration-start-from/index.tsx b/web/src/pages/flow/form/iteration-start-from/index.tsx new file mode 100644 index 00000000000..f4257c2d442 --- /dev/null +++ b/web/src/pages/flow/form/iteration-start-from/index.tsx @@ -0,0 +1,22 @@ +import { Output, OutputType } from '@/pages/agent/form/components/output'; +import { initialIterationStartValues } from '../../constant'; + +const outputs = initialIterationStartValues.outputs; + +const outputList = Object.entries(outputs).reduce( + (pre, [key, value]) => { + pre.push({ title: key, type: value.type }); + + return pre; + }, + [], +); +const IterationStartForm = () => { + return ( +
+ +
+ ); +}; + +export default IterationStartForm; From 340354b79c3487e2d29348fc21ef7a8d5140f375 Mon Sep 17 00:00:00 2001 From: Rainman <91299664+Rainman5042@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:23:35 +0800 Subject: [PATCH 0033/1187] =?UTF-8?q?fix=20the=20error=20'Unknown=20field?= =?UTF-8?q?=20for=20GenerationConfig:=20max=5Ftokens'=20when=20u=E2=80=A6?= =?UTF-8?q?=20(#8473)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? [https://github.com/infiniflow/ragflow/issues/8324](url) docker image version: v0.19.1 The `_clean_conf` function was not implemented in the `_chat` and `chat_streamly` methods of the `GeminiChat` class, causing the error "Unknown field for GenerationConfig: max_tokens" when the default LLM config includes the "max_tokens" parameter. **Buggy Code(ragflow/rag/llm/chat_model.py)** ```python class GeminiChat(Base): def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) from google.generativeai import GenerativeModel, client client.configure(api_key=key) _client = client.get_default_generative_client() self.model_name = "models/" + model_name self.model = GenerativeModel(model_name=self.model_name) self.model._client = _client def _clean_conf(self, gen_conf): for k in list(gen_conf.keys()): if k not in ["temperature", "top_p"]: del gen_conf[k] return gen_conf def _chat(self, history, gen_conf): from google.generativeai.types import content_types system = history[0]["content"] if history and history[0]["role"] == "system" else "" hist = [] for item in history: if item["role"] == "system": continue hist.append(deepcopy(item)) item = hist[-1] if "role" in item and item["role"] == "assistant": item["role"] = "model" if "role" in item and item["role"] == "system": item["role"] = "user" if "content" in item: item["parts"] = item.pop("content") if system: self.model._system_instruction = content_types.to_content(system) response = self.model.generate_content(hist, generation_config=gen_conf) ans = response.text return ans, response.usage_metadata.total_token_count def chat_streamly(self, system, history, gen_conf): from google.generativeai.types import content_types if system: self.model._system_instruction = content_types.to_content(system) #❌_clean_conf was not implemented for k in list(gen_conf.keys()): if k not in ["temperature", "top_p", "max_tokens"]: del gen_conf[k] for item in history: if "role" in item and item["role"] == "assistant": item["role"] = "model" if "content" in item: item["parts"] = item.pop("content") ans = "" try: response = self.model.generate_content(history, generation_config=gen_conf, stream=True) for resp in response: ans = resp.text yield ans yield response._chunks[-1].usage_metadata.total_token_count except Exception as e: yield ans + "\n**ERROR**: " + str(e) yield 0 ``` **Implement the _clean_conf function** ```python class GeminiChat(Base): def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) from google.generativeai import GenerativeModel, client client.configure(api_key=key) _client = client.get_default_generative_client() self.model_name = "models/" + model_name self.model = GenerativeModel(model_name=self.model_name) self.model._client = _client def _clean_conf(self, gen_conf): for k in list(gen_conf.keys()): if k not in ["temperature", "top_p"]: del gen_conf[k] return gen_conf def _chat(self, history, gen_conf): from google.generativeai.types import content_types #✅ implement _clean_conf to remove the wrong parameters gen_conf = self._clean_conf(gen_conf) system = history[0]["content"] if history and history[0]["role"] == "system" else "" hist = [] for item in history: if item["role"] == "system": continue hist.append(deepcopy(item)) item = hist[-1] if "role" in item and item["role"] == "assistant": item["role"] = "model" if "role" in item and item["role"] == "system": item["role"] = "user" if "content" in item: item["parts"] = item.pop("content") if system: self.model._system_instruction = content_types.to_content(system) response = self.model.generate_content(hist, generation_config=gen_conf) ans = response.text return ans, response.usage_metadata.total_token_count def chat_streamly(self, system, history, gen_conf): from google.generativeai.types import content_types #✅ implement _clean_conf to remove the wrong parameters gen_conf = self._clean_conf(gen_conf) if system: self.model._system_instruction = content_types.to_content(system) #✅Removed duplicate parameter filtering logic "for k in list(gen_conf.keys()):" for item in history: if "role" in item and item["role"] == "assistant": item["role"] = "model" if "content" in item: item["parts"] = item.pop("content") ans = "" try: response = self.model.generate_content(history, generation_config=gen_conf, stream=True) for resp in response: ans = resp.text yield ans yield response._chunks[-1].usage_metadata.total_token_count except Exception as e: yield ans + "\n**ERROR**: " + str(e) yield 0 ``` ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --------- Co-authored-by: Kevin Hu --- rag/llm/chat_model.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 3c6ea4d0936..55faa6b3208 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -1217,13 +1217,12 @@ def __init__(self, key, model_name, base_url=None, **kwargs): def _clean_conf(self, gen_conf): for k in list(gen_conf.keys()): - if k not in ["temperature", "top_p"]: + if k not in ["temperature", "top_p", "max_tokens"]: del gen_conf[k] return gen_conf def _chat(self, history, gen_conf): from google.generativeai.types import content_types - system = history[0]["content"] if history and history[0]["role"] == "system" else "" hist = [] for item in history: @@ -1246,12 +1245,9 @@ def _chat(self, history, gen_conf): def chat_streamly(self, system, history, gen_conf): from google.generativeai.types import content_types - + gen_conf = self._clean_conf(gen_conf) if system: self.model._system_instruction = content_types.to_content(system) - for k in list(gen_conf.keys()): - if k not in ["temperature", "top_p", "max_tokens"]: - del gen_conf[k] for item in history: if "role" in item and item["role"] == "assistant": item["role"] = "model" From dac5bcdf17dcafff5181875ded587b372642ca2b Mon Sep 17 00:00:00 2001 From: Liu An Date: Wed, 25 Jun 2025 16:41:32 +0800 Subject: [PATCH 0034/1187] Fix: Enforce default embedding model in create_dataset / update_dataset (#8486) ### What problem does this PR solve? Previous: - Defaulted to hardcoded model 'BAAI/bge-large-zh-v1.5@BAAI' - Did not respect user-configured default embedding_model Now: - Correctly prioritizes user-configured default embedding_model Other: - Make embedding_model optional in CreateDatasetReq with proper None handling - Add default embedding model fallback in dataset update when empty - Enhance validation utils to handle None values and string normalization - Update SDK default embedding model to None to match API changes - Adjust related test cases to reflect new validation rules ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/sdk/dataset.py | 2 ++ api/utils/validation_utils.py | 28 ++++++++++++------- sdk/python/ragflow_sdk/ragflow.py | 2 +- .../test_create_dataset.py | 10 ++++--- .../test_update_dataset.py | 13 ++++++--- .../test_create_dataset.py | 11 ++++---- .../test_update_dataset.py | 16 +++++++---- 7 files changed, 52 insertions(+), 30 deletions(-) diff --git a/api/apps/sdk/dataset.py b/api/apps/sdk/dataset.py index e3675b8cd00..0be206ec0c5 100644 --- a/api/apps/sdk/dataset.py +++ b/api/apps/sdk/dataset.py @@ -347,6 +347,8 @@ def update(tenant_id, dataset_id): return get_error_data_result(message=f"Dataset name '{req['name']}' already exists") if "embd_id" in req: + if not req["embd_id"]: + req["embd_id"] = kb.embd_id if kb.chunk_num != 0 and req["embd_id"] != kb.embd_id: return get_error_data_result(message=f"When chunk_num ({kb.chunk_num}) > 0, embedding_model must remain {kb.embd_id}") ok, err = verify_embedding_availability(req["embd_id"], tenant_id) diff --git a/api/utils/validation_utils.py b/api/utils/validation_utils.py index 3c53a6332c6..d87d8945d7a 100644 --- a/api/utils/validation_utils.py +++ b/api/utils/validation_utils.py @@ -380,7 +380,7 @@ class CreateDatasetReq(Base): name: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1, max_length=DATASET_NAME_LIMIT), Field(...)] avatar: str | None = Field(default=None, max_length=65535) description: str | None = Field(default=None, max_length=65535) - embedding_model: Annotated[str, StringConstraints(strip_whitespace=True, max_length=255), Field(default="", serialization_alias="embd_id")] + embedding_model: str | None = Field(default=None, max_length=255, serialization_alias="embd_id") permission: PermissionEnum = Field(default=PermissionEnum.me, min_length=1, max_length=16) chunk_method: ChunkMethodEnum = Field(default=ChunkMethodEnum.naive, min_length=1, max_length=32, serialization_alias="parser_id") parser_config: ParserConfig | None = Field(default=None) @@ -435,9 +435,16 @@ def validate_avatar_base64(cls, v: str | None) -> str | None: else: raise PydanticCustomError("format_invalid", "Missing MIME prefix. Expected format: data:;base64,") + @field_validator("embedding_model", mode="before") + @classmethod + def normalize_embedding_model(cls, v: Any) -> Any: + if isinstance(v, str): + return v.strip() + return v + @field_validator("embedding_model", mode="after") @classmethod - def validate_embedding_model(cls, v: str) -> str: + def validate_embedding_model(cls, v: str | None) -> str | None: """ Validates embedding model identifier format compliance. @@ -464,16 +471,17 @@ def validate_embedding_model(cls, v: str) -> str: Invalid: "@openai" (empty model_name) Invalid: "text-embedding-3-large@" (empty provider) """ - if "@" not in v: - raise PydanticCustomError("format_invalid", "Embedding model identifier must follow @ format") + if isinstance(v, str): + if "@" not in v: + raise PydanticCustomError("format_invalid", "Embedding model identifier must follow @ format") - components = v.split("@", 1) - if len(components) != 2 or not all(components): - raise PydanticCustomError("format_invalid", "Both model_name and provider must be non-empty strings") + components = v.split("@", 1) + if len(components) != 2 or not all(components): + raise PydanticCustomError("format_invalid", "Both model_name and provider must be non-empty strings") - model_name, provider = components - if not model_name.strip() or not provider.strip(): - raise PydanticCustomError("format_invalid", "Model name and provider cannot be whitespace-only strings") + model_name, provider = components + if not model_name.strip() or not provider.strip(): + raise PydanticCustomError("format_invalid", "Model name and provider cannot be whitespace-only strings") return v @field_validator("permission", mode="before") diff --git a/sdk/python/ragflow_sdk/ragflow.py b/sdk/python/ragflow_sdk/ragflow.py index 5b65d6201d5..95020fda332 100644 --- a/sdk/python/ragflow_sdk/ragflow.py +++ b/sdk/python/ragflow_sdk/ragflow.py @@ -53,7 +53,7 @@ def create_dataset( name: str, avatar: Optional[str] = None, description: Optional[str] = None, - embedding_model: Optional[str] = "BAAI/bge-large-zh-v1.5@BAAI", + embedding_model: Optional[str] = None, permission: str = "me", chunk_method: str = "naive", parser_config: Optional[DataSet.ParserConfig] = None, diff --git a/test/testcases/test_http_api/test_dataset_mangement/test_create_dataset.py b/test/testcases/test_http_api/test_dataset_mangement/test_create_dataset.py index b3b3f9b8abc..22772ad687b 100644 --- a/test/testcases/test_http_api/test_dataset_mangement/test_create_dataset.py +++ b/test/testcases/test_http_api/test_dataset_mangement/test_create_dataset.py @@ -260,19 +260,21 @@ def test_embedding_model_invalid(self, HttpApiAuth, name, embedding_model): @pytest.mark.parametrize( "name, embedding_model", [ + ("empty", ""), + ("space", " "), ("missing_at", "BAAI/bge-large-zh-v1.5BAAI"), ("missing_model_name", "@BAAI"), ("missing_provider", "BAAI/bge-large-zh-v1.5@"), ("whitespace_only_model_name", " @BAAI"), ("whitespace_only_provider", "BAAI/bge-large-zh-v1.5@ "), ], - ids=["missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], + ids=["empty", "space", "missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], ) def test_embedding_model_format(self, HttpApiAuth, name, embedding_model): payload = {"name": name, "embedding_model": embedding_model} res = create_dataset(HttpApiAuth, payload) assert res["code"] == 101, res - if name == "missing_at": + if name in ["empty", "space", "missing_at"]: assert "Embedding model identifier must follow @ format" in res["message"], res else: assert "Both model_name and provider must be non-empty strings" in res["message"], res @@ -288,8 +290,8 @@ def test_embedding_model_unset(self, HttpApiAuth): def test_embedding_model_none(self, HttpApiAuth): payload = {"name": "embedding_model_none", "embedding_model": None} res = create_dataset(HttpApiAuth, payload) - assert res["code"] == 101, res - assert "Input should be a valid string" in res["message"], res + assert res["code"] == 0, res + assert res["data"]["embedding_model"] == "BAAI/bge-large-zh-v1.5@BAAI", res @pytest.mark.p1 @pytest.mark.parametrize( diff --git a/test/testcases/test_http_api/test_dataset_mangement/test_update_dataset.py b/test/testcases/test_http_api/test_dataset_mangement/test_update_dataset.py index 15278800086..36d55795f65 100644 --- a/test/testcases/test_http_api/test_dataset_mangement/test_update_dataset.py +++ b/test/testcases/test_http_api/test_dataset_mangement/test_update_dataset.py @@ -300,20 +300,22 @@ def test_embedding_model_invalid(self, HttpApiAuth, add_dataset_func, name, embe @pytest.mark.parametrize( "name, embedding_model", [ + ("empty", ""), + ("space", " "), ("missing_at", "BAAI/bge-large-zh-v1.5BAAI"), ("missing_model_name", "@BAAI"), ("missing_provider", "BAAI/bge-large-zh-v1.5@"), ("whitespace_only_model_name", " @BAAI"), ("whitespace_only_provider", "BAAI/bge-large-zh-v1.5@ "), ], - ids=["missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], + ids=["empty", "space", "missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], ) def test_embedding_model_format(self, HttpApiAuth, add_dataset_func, name, embedding_model): dataset_id = add_dataset_func payload = {"name": name, "embedding_model": embedding_model} res = update_dataset(HttpApiAuth, dataset_id, payload) assert res["code"] == 101, res - if name == "missing_at": + if name in ["empty", "space", "missing_at"]: assert "Embedding model identifier must follow @ format" in res["message"], res else: assert "Both model_name and provider must be non-empty strings" in res["message"], res @@ -323,8 +325,11 @@ def test_embedding_model_none(self, HttpApiAuth, add_dataset_func): dataset_id = add_dataset_func payload = {"embedding_model": None} res = update_dataset(HttpApiAuth, dataset_id, payload) - assert res["code"] == 101, res - assert "Input should be a valid string" in res["message"], res + assert res["code"] == 0, res + + res = list_datasets(HttpApiAuth) + assert res["code"] == 0, res + assert res["data"][0]["embedding_model"] == "BAAI/bge-large-zh-v1.5@BAAI", res @pytest.mark.p1 @pytest.mark.parametrize( diff --git a/test/testcases/test_sdk_api/test_dataset_mangement/test_create_dataset.py b/test/testcases/test_sdk_api/test_dataset_mangement/test_create_dataset.py index 4ba2696481e..ffeaaf1036e 100644 --- a/test/testcases/test_sdk_api/test_dataset_mangement/test_create_dataset.py +++ b/test/testcases/test_sdk_api/test_dataset_mangement/test_create_dataset.py @@ -217,19 +217,21 @@ def test_embedding_model_invalid(self, client, name, embedding_model): @pytest.mark.parametrize( "name, embedding_model", [ + ("empty", ""), + ("space", " "), ("missing_at", "BAAI/bge-large-zh-v1.5BAAI"), ("missing_model_name", "@BAAI"), ("missing_provider", "BAAI/bge-large-zh-v1.5@"), ("whitespace_only_model_name", " @BAAI"), ("whitespace_only_provider", "BAAI/bge-large-zh-v1.5@ "), ], - ids=["missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], + ids=["empty", "space", "missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], ) def test_embedding_model_format(self, client, name, embedding_model): payload = {"name": name, "embedding_model": embedding_model} with pytest.raises(Exception) as excinfo: client.create_dataset(**payload) - if name == "missing_at": + if name in ["empty", "space", "missing_at"]: assert "Embedding model identifier must follow @ format" in str(excinfo.value), str(excinfo.value) else: assert "Both model_name and provider must be non-empty strings" in str(excinfo.value), str(excinfo.value) @@ -243,9 +245,8 @@ def test_embedding_model_unset(self, client): @pytest.mark.p2 def test_embedding_model_none(self, client): payload = {"name": "embedding_model_none", "embedding_model": None} - with pytest.raises(Exception) as excinfo: - client.create_dataset(**payload) - assert "Input should be a valid string" in str(excinfo.value), str(excinfo.value) + dataset = client.create_dataset(**payload) + assert dataset.embedding_model == "BAAI/bge-large-zh-v1.5@BAAI", str(dataset) @pytest.mark.p1 @pytest.mark.parametrize( diff --git a/test/testcases/test_sdk_api/test_dataset_mangement/test_update_dataset.py b/test/testcases/test_sdk_api/test_dataset_mangement/test_update_dataset.py index f4a0a916324..94c4ddb3718 100644 --- a/test/testcases/test_sdk_api/test_dataset_mangement/test_update_dataset.py +++ b/test/testcases/test_sdk_api/test_dataset_mangement/test_update_dataset.py @@ -207,30 +207,34 @@ def test_embedding_model_invalid(self, add_dataset_func, name, embedding_model): @pytest.mark.parametrize( "name, embedding_model", [ + ("empty", ""), + ("space", " "), ("missing_at", "BAAI/bge-large-zh-v1.5BAAI"), ("missing_model_name", "@BAAI"), ("missing_provider", "BAAI/bge-large-zh-v1.5@"), ("whitespace_only_model_name", " @BAAI"), ("whitespace_only_provider", "BAAI/bge-large-zh-v1.5@ "), ], - ids=["missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], + ids=["empty", "space", "missing_at", "empty_model_name", "empty_provider", "whitespace_only_model_name", "whitespace_only_provider"], ) def test_embedding_model_format(self, add_dataset_func, name, embedding_model): dataset = add_dataset_func with pytest.raises(Exception) as excinfo: dataset.update({"name": name, "embedding_model": embedding_model}) error_msg = str(excinfo.value) - if name == "missing_at": + if name in ["empty", "space", "missing_at"]: assert "Embedding model identifier must follow @ format" in error_msg, error_msg else: assert "Both model_name and provider must be non-empty strings" in error_msg, error_msg @pytest.mark.p2 - def test_embedding_model_none(self, add_dataset_func): + def test_embedding_model_none(self, client, add_dataset_func): dataset = add_dataset_func - with pytest.raises(Exception) as excinfo: - dataset.update({"embedding_model": None}) - assert "Input should be a valid string" in str(excinfo.value), str(excinfo.value) + dataset.update({"embedding_model": None}) + assert dataset.embedding_model == "BAAI/bge-large-zh-v1.5@BAAI", str(dataset) + + retrieved_dataset = client.get_dataset(name=dataset.name) + assert retrieved_dataset.embedding_model == "BAAI/bge-large-zh-v1.5@BAAI", str(retrieved_dataset) @pytest.mark.p1 @pytest.mark.parametrize( From 7353070f49d4377cce4b106115119e596e1a50b6 Mon Sep 17 00:00:00 2001 From: Tuan Le <30828528+tuankg1028@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:53:15 +0700 Subject: [PATCH 0035/1187] Adds retrieval result fields to Chunk (#8478) ### What problem does this PR solve? This PR adds fields to the `Chunk` class to store retrieval results like similarity scores, term similarity, vector similarity, positions, and document type. This allows the chunk object to hold all the information needed when returning search results from the vector database. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- sdk/python/ragflow_sdk/modules/chunk.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdk/python/ragflow_sdk/modules/chunk.py b/sdk/python/ragflow_sdk/modules/chunk.py index f943b88659a..b71314d8ae8 100644 --- a/sdk/python/ragflow_sdk/modules/chunk.py +++ b/sdk/python/ragflow_sdk/modules/chunk.py @@ -35,6 +35,12 @@ def __init__(self, rag, res_dict): self.document_name = "" self.document_id = "" self.available = True + # Additional fields for retrieval results + self.similarity = 0.0 + self.vector_similarity = 0.0 + self.term_similarity = 0.0 + self.positions = [] + self.doc_type = "" for k in list(res_dict.keys()): if k not in self.__dict__: res_dict.pop(k) From c4bfd9fa2c742544488edfa512004f33130eec93 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 25 Jun 2025 18:32:56 +0800 Subject: [PATCH 0036/1187] Feat: Add retrieval tool #3221 (#8491) ### What problem does this PR solve? Feat: Add retrieval tool #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/constant.tsx | 2255 +---------------- web/src/pages/agent/form/agent-form/index.tsx | 17 +- .../agent-form/tool-popover/tool-command.tsx | 1 + .../tool-popover/use-update-tools.ts | 13 +- .../agent/form/baidu-fanyi-form/index.tsx | 4 +- web/src/pages/agent/form/bing-form/index.tsx | 2 +- .../form/components/description-field.tsx | 26 + .../pages/agent/form/crawler-form/index.tsx | 2 +- web/src/pages/agent/form/deepl-form/index.tsx | 2 +- .../pages/agent/form/exesql-form/index.tsx | 2 +- .../pages/agent/form/google-form/index.tsx | 2 +- web/src/pages/agent/form/jin10-form/index.tsx | 4 +- .../pages/agent/form/qweather-form/index.tsx | 4 +- .../pages/agent/form/retrieval-form/next.tsx | 56 +- .../form/rewrite-question-form/index.tsx | 2 +- .../pages/agent/form/tool-form/constant.ts | 2 +- .../form/tool-form/retrieval-form/index.tsx | 59 + .../pages/agent/form/tool-form/use-values.ts | 10 +- .../pages/agent/form/tushare-form/index.tsx | 2 +- .../pages/agent/form/wencai-form/index.tsx | 2 +- .../pages/agent/form/wikipedia-form/index.tsx | 2 +- web/src/pages/agent/hooks/use-add-node.ts | 8 +- web/src/pages/agent/options.ts | 2170 ++++++++++++++++ 23 files changed, 2376 insertions(+), 2271 deletions(-) create mode 100644 web/src/pages/agent/form/components/description-field.tsx create mode 100644 web/src/pages/agent/form/tool-form/retrieval-form/index.tsx create mode 100644 web/src/pages/agent/options.ts diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 24732a1241d..3ea744b5f85 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -20,6 +20,7 @@ import { import { ModelVariableType } from '@/constants/knowledge'; import i18n from '@/locales/config'; import { setInitialChatVariableEnabledFieldValue } from '@/utils/chat'; +import { omit } from 'lodash'; // DuckDuckGo's channel options export enum Channel { @@ -32,7 +33,6 @@ export enum PromptRole { Assistant = 'assistant', } -import upperFirst from 'lodash/upperFirst'; import { CloudUpload, ListOrdered, @@ -446,10 +446,10 @@ export const initialRelevantValues = { export const initialCategorizeValues = { ...initialLlmBaseValues, + query: AgentGlobals.SysQuery, parameter: ModelVariableType.Precise, message_history_window_size: 1, category_description: {}, - ...initialQueryBaseValues, }; export const initialMessageValues = { @@ -853,2213 +853,54 @@ export const NodeMap = { [Operator.TavilySearch]: 'ragNode', }; -export const LanguageOptions = [ - { - value: 'af', - label: 'Afrikaans', - }, - { - value: 'pl', - label: 'Polski', - }, - { - value: 'ar', - label: 'العربية', - }, - { - value: 'ast', - label: 'Asturianu', - }, - { - value: 'az', - label: 'Azərbaycanca', - }, - { - value: 'bg', - label: 'Български', - }, - { - value: 'nan', - label: '閩南語 / Bân-lâm-gú', - }, - { - value: 'bn', - label: 'বাংলা', - }, - { - value: 'be', - label: 'Беларуская', - }, - { - value: 'ca', - label: 'Català', - }, - { - value: 'cs', - label: 'Čeština', - }, - { - value: 'cy', - label: 'Cymraeg', - }, - { - value: 'da', - label: 'Dansk', - }, - { - value: 'de', - label: 'Deutsch', - }, - { - value: 'et', - label: 'Eesti', - }, - { - value: 'el', - label: 'Ελληνικά', - }, - { - value: 'en', - label: 'English', - }, - { - value: 'es', - label: 'Español', - }, - { - value: 'eo', - label: 'Esperanto', - }, - { - value: 'eu', - label: 'Euskara', - }, - { - value: 'fa', - label: 'فارسی', - }, - { - value: 'fr', - label: 'Français', - }, - { - value: 'gl', - label: 'Galego', - }, - { - value: 'ko', - label: '한국어', - }, - { - value: 'hy', - label: 'Հայերեն', - }, - { - value: 'hi', - label: 'हिन्दी', - }, - { - value: 'hr', - label: 'Hrvatski', - }, - { - value: 'id', - label: 'Bahasa Indonesia', - }, - { - value: 'it', - label: 'Italiano', - }, - { - value: 'he', - label: 'עברית', - }, - { - value: 'ka', - label: 'ქართული', - }, - { - value: 'lld', - label: 'Ladin', - }, - { - value: 'la', - label: 'Latina', - }, - { - value: 'lv', - label: 'Latviešu', - }, - { - value: 'lt', - label: 'Lietuvių', - }, - { - value: 'hu', - label: 'Magyar', - }, - { - value: 'mk', - label: 'Македонски', - }, - { - value: 'arz', - label: 'مصرى', - }, - { - value: 'ms', - label: 'Bahasa Melayu', - }, - { - value: 'min', - label: 'Bahaso Minangkabau', - }, - { - value: 'my', - label: 'မြန်မာဘာသာ', - }, - { - value: 'nl', - label: 'Nederlands', - }, - { - value: 'ja', - label: '日本語', - }, - { - value: 'no', - label: 'Norsk (bokmål)', - }, - { - value: 'nn', - label: 'Norsk (nynorsk)', - }, - { - value: 'ce', - label: 'Нохчийн', - }, - { - value: 'uz', - label: 'Oʻzbekcha / Ўзбекча', - }, - { - value: 'pt', - label: 'Português', - }, - { - value: 'kk', - label: 'Қазақша / Qazaqşa / قازاقشا', - }, - { - value: 'ro', - label: 'Română', - }, - { - value: 'ru', - label: 'Русский', - }, - { - value: 'ceb', - label: 'Sinugboanong Binisaya', - }, - { - value: 'sk', - label: 'Slovenčina', - }, - { - value: 'sl', - label: 'Slovenščina', - }, - { - value: 'sr', - label: 'Српски / Srpski', - }, - { - value: 'sh', - label: 'Srpskohrvatski / Српскохрватски', - }, - { - value: 'fi', - label: 'Suomi', - }, - { - value: 'sv', - label: 'Svenska', - }, - { - value: 'ta', - label: 'தமிழ்', - }, - { - value: 'tt', - label: 'Татарча / Tatarça', - }, - { - value: 'th', - label: 'ภาษาไทย', - }, - { - value: 'tg', - label: 'Тоҷикӣ', - }, - { - value: 'azb', - label: 'تۆرکجه', - }, - { - value: 'tr', - label: 'Türkçe', - }, - { - value: 'uk', - label: 'Українська', - }, - { - value: 'ur', - label: 'اردو', - }, - { - value: 'vi', - label: 'Tiếng Việt', - }, - { - value: 'war', - label: 'Winaray', - }, - { - value: 'zh', - label: '中文', - }, - { - value: 'yue', - label: '粵語', - }, +export enum BeginQueryType { + Line = 'line', + Paragraph = 'paragraph', + Options = 'options', + File = 'file', + Integer = 'integer', + Boolean = 'boolean', +} + +export const BeginQueryTypeIconMap = { + [BeginQueryType.Line]: TextCursorInput, + [BeginQueryType.Paragraph]: WrapText, + [BeginQueryType.Options]: OptionIcon, + [BeginQueryType.File]: CloudUpload, + [BeginQueryType.Integer]: ListOrdered, + [BeginQueryType.Boolean]: ToggleLeft, +}; + +export const NoDebugOperatorsList = [ + Operator.Begin, + Operator.Answer, + Operator.Concentrator, + Operator.Template, + Operator.Message, + Operator.RewriteQuestion, + Operator.Switch, + Operator.Iteration, ]; -export const GoogleLanguageOptions = [ - { - language_code: 'af', - language_name: 'Afrikaans', - }, - { - language_code: 'ak', - language_name: 'Akan', - }, - { - language_code: 'sq', - language_name: 'Albanian', - }, - { - language_code: 'ws', - language_name: 'Samoa', - }, - { - language_code: 'am', - language_name: 'Amharic', - }, - { - language_code: 'ar', - language_name: 'Arabic', - }, - { - language_code: 'hy', - language_name: 'Armenian', - }, - { - language_code: 'az', - language_name: 'Azerbaijani', - }, - { - language_code: 'eu', - language_name: 'Basque', - }, - { - language_code: 'be', - language_name: 'Belarusian', - }, - { - language_code: 'bem', - language_name: 'Bemba', - }, - { - language_code: 'bn', - language_name: 'Bengali', - }, - { - language_code: 'bh', - language_name: 'Bihari', - }, - { - language_code: 'xx-bork', - language_name: 'Bork, bork, bork!', - }, - { - language_code: 'bs', - language_name: 'Bosnian', - }, - { - language_code: 'br', - language_name: 'Breton', - }, - { - language_code: 'bg', - language_name: 'Bulgarian', - }, - { - language_code: 'bt', - language_name: 'Bhutanese', - }, - { - language_code: 'km', - language_name: 'Cambodian', - }, - { - language_code: 'ca', - language_name: 'Catalan', - }, - { - language_code: 'chr', - language_name: 'Cherokee', - }, - { - language_code: 'ny', - language_name: 'Chichewa', - }, - { - language_code: 'zh-cn', - language_name: 'Chinese (Simplified)', - }, - { - language_code: 'zh-tw', - language_name: 'Chinese (Traditional)', - }, - { - language_code: 'co', - language_name: 'Corsican', - }, - { - language_code: 'hr', - language_name: 'Croatian', - }, - { - language_code: 'cs', - language_name: 'Czech', - }, - { - language_code: 'da', - language_name: 'Danish', - }, - { - language_code: 'nl', - language_name: 'Dutch', - }, - { - language_code: 'xx-elmer', - language_name: 'Elmer Fudd', - }, - { - language_code: 'en', - language_name: 'English', - }, - { - language_code: 'eo', - language_name: 'Esperanto', - }, - { - language_code: 'et', - language_name: 'Estonian', - }, - { - language_code: 'ee', - language_name: 'Ewe', - }, - { - language_code: 'fo', - language_name: 'Faroese', - }, - { - language_code: 'tl', - language_name: 'Filipino', +export enum NodeHandleId { + Start = 'start', + End = 'end', + Tool = 'tool', + AgentTop = 'agentTop', + AgentBottom = 'agentBottom', +} + +export enum VariableType { + String = 'string', + Array = 'array', +} + +export const DefaultAgentToolValuesMap = { + [Operator.Retrieval]: { + ...omit(initialRetrievalValues, 'query'), + description: '', }, - { - language_code: 'fi', - language_name: 'Finnish', + [Operator.TavilySearch]: { + api_key: '', }, - { - language_code: 'fr', - language_name: 'French', - }, - { - language_code: 'fy', - language_name: 'Frisian', - }, - { - language_code: 'gaa', - language_name: 'Ga', - }, - { - language_code: 'gl', - language_name: 'Galician', - }, - { - language_code: 'ka', - language_name: 'Georgian', - }, - { - language_code: 'de', - language_name: 'German', - }, - { - language_code: 'el', - language_name: 'Greek', - }, - { - language_code: 'kl', - language_name: 'Greenlandic', - }, - { - language_code: 'gn', - language_name: 'Guarani', - }, - { - language_code: 'gu', - language_name: 'Gujarati', - }, - { - language_code: 'xx-hacker', - language_name: 'Hacker', - }, - { - language_code: 'ht', - language_name: 'Haitian Creole', - }, - { - language_code: 'ha', - language_name: 'Hausa', - }, - { - language_code: 'haw', - language_name: 'Hawaiian', - }, - { - language_code: 'iw', - language_name: 'Hebrew', - }, - { - language_code: 'hi', - language_name: 'Hindi', - }, - { - language_code: 'hu', - language_name: 'Hungarian', - }, - { - language_code: 'is', - language_name: 'Icelandic', - }, - { - language_code: 'ig', - language_name: 'Igbo', - }, - { - language_code: 'id', - language_name: 'Indonesian', - }, - { - language_code: 'ia', - language_name: 'Interlingua', - }, - { - language_code: 'ga', - language_name: 'Irish', - }, - { - language_code: 'it', - language_name: 'Italian', - }, - { - language_code: 'ja', - language_name: 'Japanese', - }, - { - language_code: 'jw', - language_name: 'Javanese', - }, - { - language_code: 'kn', - language_name: 'Kannada', - }, - { - language_code: 'kk', - language_name: 'Kazakh', - }, - { - language_code: 'rw', - language_name: 'Kinyarwanda', - }, - { - language_code: 'rn', - language_name: 'Kirundi', - }, - { - language_code: 'xx-klingon', - language_name: 'Klingon', - }, - { - language_code: 'kg', - language_name: 'Kongo', - }, - { - language_code: 'ko', - language_name: 'Korean', - }, - { - language_code: 'kri', - language_name: 'Krio (Sierra Leone)', - }, - { - language_code: 'ku', - language_name: 'Kurdish', - }, - { - language_code: 'ckb', - language_name: 'Kurdish (Soranî)', - }, - { - language_code: 'ky', - language_name: 'Kyrgyz', - }, - { - language_code: 'lo', - language_name: 'Laothian', - }, - { - language_code: 'la', - language_name: 'Latin', - }, - { - language_code: 'lv', - language_name: 'Latvian', - }, - { - language_code: 'ln', - language_name: 'Lingala', - }, - { - language_code: 'lt', - language_name: 'Lithuanian', - }, - { - language_code: 'loz', - language_name: 'Lozi', - }, - { - language_code: 'lg', - language_name: 'Luganda', - }, - { - language_code: 'ach', - language_name: 'Luo', - }, - { - language_code: 'mk', - language_name: 'Macedonian', - }, - { - language_code: 'mg', - language_name: 'Malagasy', - }, - { - language_code: 'ms', - language_name: 'Malay', - }, - { - language_code: 'ml', - language_name: 'Malayalam', - }, - { - language_code: 'mt', - language_name: 'Maltese', - }, - { - language_code: 'mv', - language_name: 'Maldives', - }, - { - language_code: 'mi', - language_name: 'Maori', - }, - { - language_code: 'mr', - language_name: 'Marathi', - }, - { - language_code: 'mfe', - language_name: 'Mauritian Creole', - }, - { - language_code: 'mo', - language_name: 'Moldavian', - }, - { - language_code: 'mn', - language_name: 'Mongolian', - }, - { - language_code: 'sr-me', - language_name: 'Montenegrin', - }, - { - language_code: 'my', - language_name: 'Myanmar', - }, - { - language_code: 'ne', - language_name: 'Nepali', - }, - { - language_code: 'pcm', - language_name: 'Nigerian Pidgin', - }, - { - language_code: 'nso', - language_name: 'Northern Sotho', - }, - { - language_code: 'no', - language_name: 'Norwegian', - }, - { - language_code: 'nn', - language_name: 'Norwegian (Nynorsk)', - }, - { - language_code: 'oc', - language_name: 'Occitan', - }, - { - language_code: 'or', - language_name: 'Oriya', - }, - { - language_code: 'om', - language_name: 'Oromo', - }, - { - language_code: 'ps', - language_name: 'Pashto', - }, - { - language_code: 'fa', - language_name: 'Persian', - }, - { - language_code: 'xx-pirate', - language_name: 'Pirate', - }, - { - language_code: 'pl', - language_name: 'Polish', - }, - { - language_code: 'pt', - language_name: 'Portuguese', - }, - { - language_code: 'pt-br', - language_name: 'Portuguese (Brazil)', - }, - { - language_code: 'pt-pt', - language_name: 'Portuguese (Portugal)', - }, - { - language_code: 'pa', - language_name: 'Punjabi', - }, - { - language_code: 'qu', - language_name: 'Quechua', - }, - { - language_code: 'ro', - language_name: 'Romanian', - }, - { - language_code: 'rm', - language_name: 'Romansh', - }, - { - language_code: 'nyn', - language_name: 'Runyakitara', - }, - { - language_code: 'ru', - language_name: 'Russian', - }, - { - language_code: 'gd', - language_name: 'Scots Gaelic', - }, - { - language_code: 'sr', - language_name: 'Serbian', - }, - { - language_code: 'sh', - language_name: 'Serbo-Croatian', - }, - { - language_code: 'st', - language_name: 'Sesotho', - }, - { - language_code: 'tn', - language_name: 'Setswana', - }, - { - language_code: 'crs', - language_name: 'Seychellois Creole', - }, - { - language_code: 'sn', - language_name: 'Shona', - }, - { - language_code: 'sd', - language_name: 'Sindhi', - }, - { - language_code: 'si', - language_name: 'Sinhalese', - }, - { - language_code: 'sk', - language_name: 'Slovak', - }, - { - language_code: 'sl', - language_name: 'Slovenian', - }, - { - language_code: 'so', - language_name: 'Somali', - }, - { - language_code: 'es', - language_name: 'Spanish', - }, - { - language_code: 'es-419', - language_name: 'Spanish (Latin American)', - }, - { - language_code: 'su', - language_name: 'Sundanese', - }, - { - language_code: 'sw', - language_name: 'Swahili', - }, - { - language_code: 'sv', - language_name: 'Swedish', - }, - { - language_code: 'tg', - language_name: 'Tajik', - }, - { - language_code: 'ta', - language_name: 'Tamil', - }, - { - language_code: 'tt', - language_name: 'Tatar', - }, - { - language_code: 'te', - language_name: 'Telugu', - }, - { - language_code: 'th', - language_name: 'Thai', - }, - { - language_code: 'ti', - language_name: 'Tigrinya', - }, - { - language_code: 'to', - language_name: 'Tonga', - }, - { - language_code: 'lua', - language_name: 'Tshiluba', - }, - { - language_code: 'tum', - language_name: 'Tumbuka', - }, - { - language_code: 'tr', - language_name: 'Turkish', - }, - { - language_code: 'tk', - language_name: 'Turkmen', - }, - { - language_code: 'tw', - language_name: 'Twi', - }, - { - language_code: 'ug', - language_name: 'Uighur', - }, - { - language_code: 'uk', - language_name: 'Ukrainian', - }, - { - language_code: 'ur', - language_name: 'Urdu', - }, - { - language_code: 'uz', - language_name: 'Uzbek', - }, - { - language_code: 'vu', - language_name: 'Vanuatu', - }, - { - language_code: 'vi', - language_name: 'Vietnamese', - }, - { - language_code: 'cy', - language_name: 'Welsh', - }, - { - language_code: 'wo', - language_name: 'Wolof', - }, - { - language_code: 'xh', - language_name: 'Xhosa', - }, - { - language_code: 'yi', - language_name: 'Yiddish', - }, - { - language_code: 'yo', - language_name: 'Yoruba', - }, - { - language_code: 'zu', - language_name: 'Zulu', - }, -].map((x) => ({ label: x.language_name, value: x.language_code })); - -export const GoogleCountryOptions = [ - { - country_code: 'af', - country_name: 'Afghanistan', - }, - { - country_code: 'al', - country_name: 'Albania', - }, - { - country_code: 'dz', - country_name: 'Algeria', - }, - { - country_code: 'as', - country_name: 'American Samoa', - }, - { - country_code: 'ad', - country_name: 'Andorra', - }, - { - country_code: 'ao', - country_name: 'Angola', - }, - { - country_code: 'ai', - country_name: 'Anguilla', - }, - { - country_code: 'aq', - country_name: 'Antarctica', - }, - { - country_code: 'ag', - country_name: 'Antigua and Barbuda', - }, - { - country_code: 'ar', - country_name: 'Argentina', - }, - { - country_code: 'am', - country_name: 'Armenia', - }, - { - country_code: 'aw', - country_name: 'Aruba', - }, - { - country_code: 'au', - country_name: 'Australia', - }, - { - country_code: 'at', - country_name: 'Austria', - }, - { - country_code: 'az', - country_name: 'Azerbaijan', - }, - { - country_code: 'bs', - country_name: 'Bahamas', - }, - { - country_code: 'bh', - country_name: 'Bahrain', - }, - { - country_code: 'bd', - country_name: 'Bangladesh', - }, - { - country_code: 'bb', - country_name: 'Barbados', - }, - { - country_code: 'by', - country_name: 'Belarus', - }, - { - country_code: 'be', - country_name: 'Belgium', - }, - { - country_code: 'bz', - country_name: 'Belize', - }, - { - country_code: 'bj', - country_name: 'Benin', - }, - { - country_code: 'bm', - country_name: 'Bermuda', - }, - { - country_code: 'bt', - country_name: 'Bhutan', - }, - { - country_code: 'bo', - country_name: 'Bolivia', - }, - { - country_code: 'ba', - country_name: 'Bosnia and Herzegovina', - }, - { - country_code: 'bw', - country_name: 'Botswana', - }, - { - country_code: 'bv', - country_name: 'Bouvet Island', - }, - { - country_code: 'br', - country_name: 'Brazil', - }, - { - country_code: 'io', - country_name: 'British Indian Ocean Territory', - }, - { - country_code: 'bn', - country_name: 'Brunei Darussalam', - }, - { - country_code: 'bg', - country_name: 'Bulgaria', - }, - { - country_code: 'bf', - country_name: 'Burkina Faso', - }, - { - country_code: 'bi', - country_name: 'Burundi', - }, - { - country_code: 'kh', - country_name: 'Cambodia', - }, - { - country_code: 'cm', - country_name: 'Cameroon', - }, - { - country_code: 'ca', - country_name: 'Canada', - }, - { - country_code: 'cv', - country_name: 'Cape Verde', - }, - { - country_code: 'ky', - country_name: 'Cayman Islands', - }, - { - country_code: 'cf', - country_name: 'Central African Republic', - }, - { - country_code: 'td', - country_name: 'Chad', - }, - { - country_code: 'cl', - country_name: 'Chile', - }, - { - country_code: 'cn', - country_name: 'China', - }, - { - country_code: 'cx', - country_name: 'Christmas Island', - }, - { - country_code: 'cc', - country_name: 'Cocos (Keeling) Islands', - }, - { - country_code: 'co', - country_name: 'Colombia', - }, - { - country_code: 'km', - country_name: 'Comoros', - }, - { - country_code: 'cg', - country_name: 'Congo', - }, - { - country_code: 'cd', - country_name: 'Congo, the Democratic Republic of the', - }, - { - country_code: 'ck', - country_name: 'Cook Islands', - }, - { - country_code: 'cr', - country_name: 'Costa Rica', - }, - { - country_code: 'ci', - country_name: "Cote D'ivoire", - }, - { - country_code: 'hr', - country_name: 'Croatia', - }, - { - country_code: 'cu', - country_name: 'Cuba', - }, - { - country_code: 'cy', - country_name: 'Cyprus', - }, - { - country_code: 'cz', - country_name: 'Czech Republic', - }, - { - country_code: 'dk', - country_name: 'Denmark', - }, - { - country_code: 'dj', - country_name: 'Djibouti', - }, - { - country_code: 'dm', - country_name: 'Dominica', - }, - { - country_code: 'do', - country_name: 'Dominican Republic', - }, - { - country_code: 'ec', - country_name: 'Ecuador', - }, - { - country_code: 'eg', - country_name: 'Egypt', - }, - { - country_code: 'sv', - country_name: 'El Salvador', - }, - { - country_code: 'gq', - country_name: 'Equatorial Guinea', - }, - { - country_code: 'er', - country_name: 'Eritrea', - }, - { - country_code: 'ee', - country_name: 'Estonia', - }, - { - country_code: 'et', - country_name: 'Ethiopia', - }, - { - country_code: 'fk', - country_name: 'Falkland Islands (Malvinas)', - }, - { - country_code: 'fo', - country_name: 'Faroe Islands', - }, - { - country_code: 'fj', - country_name: 'Fiji', - }, - { - country_code: 'fi', - country_name: 'Finland', - }, - { - country_code: 'fr', - country_name: 'France', - }, - { - country_code: 'gf', - country_name: 'French Guiana', - }, - { - country_code: 'pf', - country_name: 'French Polynesia', - }, - { - country_code: 'tf', - country_name: 'French Southern Territories', - }, - { - country_code: 'ga', - country_name: 'Gabon', - }, - { - country_code: 'gm', - country_name: 'Gambia', - }, - { - country_code: 'ge', - country_name: 'Georgia', - }, - { - country_code: 'de', - country_name: 'Germany', - }, - { - country_code: 'gh', - country_name: 'Ghana', - }, - { - country_code: 'gi', - country_name: 'Gibraltar', - }, - { - country_code: 'gr', - country_name: 'Greece', - }, - { - country_code: 'gl', - country_name: 'Greenland', - }, - { - country_code: 'gd', - country_name: 'Grenada', - }, - { - country_code: 'gp', - country_name: 'Guadeloupe', - }, - { - country_code: 'gu', - country_name: 'Guam', - }, - { - country_code: 'gt', - country_name: 'Guatemala', - }, - { - country_code: 'gn', - country_name: 'Guinea', - }, - { - country_code: 'gw', - country_name: 'Guinea-Bissau', - }, - { - country_code: 'gy', - country_name: 'Guyana', - }, - { - country_code: 'ht', - country_name: 'Haiti', - }, - { - country_code: 'hm', - country_name: 'Heard Island and Mcdonald Islands', - }, - { - country_code: 'va', - country_name: 'Holy See (Vatican City State)', - }, - { - country_code: 'hn', - country_name: 'Honduras', - }, - { - country_code: 'hk', - country_name: 'Hong Kong', - }, - { - country_code: 'hu', - country_name: 'Hungary', - }, - { - country_code: 'is', - country_name: 'Iceland', - }, - { - country_code: 'in', - country_name: 'India', - }, - { - country_code: 'id', - country_name: 'Indonesia', - }, - { - country_code: 'ir', - country_name: 'Iran, Islamic Republic of', - }, - { - country_code: 'iq', - country_name: 'Iraq', - }, - { - country_code: 'ie', - country_name: 'Ireland', - }, - { - country_code: 'il', - country_name: 'Israel', - }, - { - country_code: 'it', - country_name: 'Italy', - }, - { - country_code: 'jm', - country_name: 'Jamaica', - }, - { - country_code: 'jp', - country_name: 'Japan', - }, - { - country_code: 'jo', - country_name: 'Jordan', - }, - { - country_code: 'kz', - country_name: 'Kazakhstan', - }, - { - country_code: 'ke', - country_name: 'Kenya', - }, - { - country_code: 'ki', - country_name: 'Kiribati', - }, - { - country_code: 'kp', - country_name: "Korea, Democratic People's Republic of", - }, - { - country_code: 'kr', - country_name: 'Korea, Republic of', - }, - { - country_code: 'kw', - country_name: 'Kuwait', - }, - { - country_code: 'kg', - country_name: 'Kyrgyzstan', - }, - { - country_code: 'la', - country_name: "Lao People's Democratic Republic", - }, - { - country_code: 'lv', - country_name: 'Latvia', - }, - { - country_code: 'lb', - country_name: 'Lebanon', - }, - { - country_code: 'ls', - country_name: 'Lesotho', - }, - { - country_code: 'lr', - country_name: 'Liberia', - }, - { - country_code: 'ly', - country_name: 'Libyan Arab Jamahiriya', - }, - { - country_code: 'li', - country_name: 'Liechtenstein', - }, - { - country_code: 'lt', - country_name: 'Lithuania', - }, - { - country_code: 'lu', - country_name: 'Luxembourg', - }, - { - country_code: 'mo', - country_name: 'Macao', - }, - { - country_code: 'mk', - country_name: 'Macedonia, the Former Yugosalv Republic of', - }, - { - country_code: 'mg', - country_name: 'Madagascar', - }, - { - country_code: 'mw', - country_name: 'Malawi', - }, - { - country_code: 'my', - country_name: 'Malaysia', - }, - { - country_code: 'mv', - country_name: 'Maldives', - }, - { - country_code: 'ml', - country_name: 'Mali', - }, - { - country_code: 'mt', - country_name: 'Malta', - }, - { - country_code: 'mh', - country_name: 'Marshall Islands', - }, - { - country_code: 'mq', - country_name: 'Martinique', - }, - { - country_code: 'mr', - country_name: 'Mauritania', - }, - { - country_code: 'mu', - country_name: 'Mauritius', - }, - { - country_code: 'yt', - country_name: 'Mayotte', - }, - { - country_code: 'mx', - country_name: 'Mexico', - }, - { - country_code: 'fm', - country_name: 'Micronesia, Federated States of', - }, - { - country_code: 'md', - country_name: 'Moldova, Republic of', - }, - { - country_code: 'mc', - country_name: 'Monaco', - }, - { - country_code: 'mn', - country_name: 'Mongolia', - }, - { - country_code: 'ms', - country_name: 'Montserrat', - }, - { - country_code: 'ma', - country_name: 'Morocco', - }, - { - country_code: 'mz', - country_name: 'Mozambique', - }, - { - country_code: 'mm', - country_name: 'Myanmar', - }, - { - country_code: 'na', - country_name: 'Namibia', - }, - { - country_code: 'nr', - country_name: 'Nauru', - }, - { - country_code: 'np', - country_name: 'Nepal', - }, - { - country_code: 'nl', - country_name: 'Netherlands', - }, - { - country_code: 'an', - country_name: 'Netherlands Antilles', - }, - { - country_code: 'nc', - country_name: 'New Caledonia', - }, - { - country_code: 'nz', - country_name: 'New Zealand', - }, - { - country_code: 'ni', - country_name: 'Nicaragua', - }, - { - country_code: 'ne', - country_name: 'Niger', - }, - { - country_code: 'ng', - country_name: 'Nigeria', - }, - { - country_code: 'nu', - country_name: 'Niue', - }, - { - country_code: 'nf', - country_name: 'Norfolk Island', - }, - { - country_code: 'mp', - country_name: 'Northern Mariana Islands', - }, - { - country_code: 'no', - country_name: 'Norway', - }, - { - country_code: 'om', - country_name: 'Oman', - }, - { - country_code: 'pk', - country_name: 'Pakistan', - }, - { - country_code: 'pw', - country_name: 'Palau', - }, - { - country_code: 'ps', - country_name: 'Palestinian Territory, Occupied', - }, - { - country_code: 'pa', - country_name: 'Panama', - }, - { - country_code: 'pg', - country_name: 'Papua New Guinea', - }, - { - country_code: 'py', - country_name: 'Paraguay', - }, - { - country_code: 'pe', - country_name: 'Peru', - }, - { - country_code: 'ph', - country_name: 'Philippines', - }, - { - country_code: 'pn', - country_name: 'Pitcairn', - }, - { - country_code: 'pl', - country_name: 'Poland', - }, - { - country_code: 'pt', - country_name: 'Portugal', - }, - { - country_code: 'pr', - country_name: 'Puerto Rico', - }, - { - country_code: 'qa', - country_name: 'Qatar', - }, - { - country_code: 're', - country_name: 'Reunion', - }, - { - country_code: 'ro', - country_name: 'Romania', - }, - { - country_code: 'ru', - country_name: 'Russian Federation', - }, - { - country_code: 'rw', - country_name: 'Rwanda', - }, - { - country_code: 'sh', - country_name: 'Saint Helena', - }, - { - country_code: 'kn', - country_name: 'Saint Kitts and Nevis', - }, - { - country_code: 'lc', - country_name: 'Saint Lucia', - }, - { - country_code: 'pm', - country_name: 'Saint Pierre and Miquelon', - }, - { - country_code: 'vc', - country_name: 'Saint Vincent and the Grenadines', - }, - { - country_code: 'ws', - country_name: 'Samoa', - }, - { - country_code: 'sm', - country_name: 'San Marino', - }, - { - country_code: 'st', - country_name: 'Sao Tome and Principe', - }, - { - country_code: 'sa', - country_name: 'Saudi Arabia', - }, - { - country_code: 'sn', - country_name: 'Senegal', - }, - { - country_code: 'rs', - country_name: 'Serbia and Montenegro', - }, - { - country_code: 'sc', - country_name: 'Seychelles', - }, - { - country_code: 'sl', - country_name: 'Sierra Leone', - }, - { - country_code: 'sg', - country_name: 'Singapore', - }, - { - country_code: 'sk', - country_name: 'Slovakia', - }, - { - country_code: 'si', - country_name: 'Slovenia', - }, - { - country_code: 'sb', - country_name: 'Solomon Islands', - }, - { - country_code: 'so', - country_name: 'Somalia', - }, - { - country_code: 'za', - country_name: 'South Africa', - }, - { - country_code: 'gs', - country_name: 'South Georgia and the South Sandwich Islands', - }, - { - country_code: 'es', - country_name: 'Spain', - }, - { - country_code: 'lk', - country_name: 'Sri Lanka', - }, - { - country_code: 'sd', - country_name: 'Sudan', - }, - { - country_code: 'sr', - country_name: 'Suriname', - }, - { - country_code: 'sj', - country_name: 'Svalbard and Jan Mayen', - }, - { - country_code: 'sz', - country_name: 'Swaziland', - }, - { - country_code: 'se', - country_name: 'Sweden', - }, - { - country_code: 'ch', - country_name: 'Switzerland', - }, - { - country_code: 'sy', - country_name: 'Syrian Arab Republic', - }, - { - country_code: 'tw', - country_name: 'Taiwan, Province of China', - }, - { - country_code: 'tj', - country_name: 'Tajikistan', - }, - { - country_code: 'tz', - country_name: 'Tanzania, United Republic of', - }, - { - country_code: 'th', - country_name: 'Thailand', - }, - { - country_code: 'tl', - country_name: 'Timor-Leste', - }, - { - country_code: 'tg', - country_name: 'Togo', - }, - { - country_code: 'tk', - country_name: 'Tokelau', - }, - { - country_code: 'to', - country_name: 'Tonga', - }, - { - country_code: 'tt', - country_name: 'Trinidad and Tobago', - }, - { - country_code: 'tn', - country_name: 'Tunisia', - }, - { - country_code: 'tr', - country_name: 'Turkiye', - }, - { - country_code: 'tm', - country_name: 'Turkmenistan', - }, - { - country_code: 'tc', - country_name: 'Turks and Caicos Islands', - }, - { - country_code: 'tv', - country_name: 'Tuvalu', - }, - { - country_code: 'ug', - country_name: 'Uganda', - }, - { - country_code: 'ua', - country_name: 'Ukraine', - }, - { - country_code: 'ae', - country_name: 'United Arab Emirates', - }, - { - country_code: 'uk', - country_name: 'United Kingdom', - }, - { - country_code: 'gb', - country_name: 'United Kingdom', - }, - { - country_code: 'us', - country_name: 'United States', - }, - { - country_code: 'um', - country_name: 'United States Minor Outlying Islands', - }, - { - country_code: 'uy', - country_name: 'Uruguay', - }, - { - country_code: 'uz', - country_name: 'Uzbekistan', - }, - { - country_code: 'vu', - country_name: 'Vanuatu', - }, - { - country_code: 've', - country_name: 'Venezuela', - }, - { - country_code: 'vn', - country_name: 'Viet Nam', - }, - { - country_code: 'vg', - country_name: 'Virgin Islands, British', - }, - { - country_code: 'vi', - country_name: 'Virgin Islands, U.S.', - }, - { - country_code: 'wf', - country_name: 'Wallis and Futuna', - }, - { - country_code: 'eh', - country_name: 'Western Sahara', - }, - { - country_code: 'ye', - country_name: 'Yemen', - }, - { - country_code: 'zm', - country_name: 'Zambia', - }, - { - country_code: 'zw', - country_name: 'Zimbabwe', - }, -].map((x) => ({ label: x.country_name, value: x.country_code })); - -export const BingCountryOptions = [ - { label: 'Argentina AR', value: 'AR' }, - { label: 'Australia AU', value: 'AU' }, - { label: 'Austria AT', value: 'AT' }, - { label: 'Belgium BE', value: 'BE' }, - { label: 'Brazil BR', value: 'BR' }, - { label: 'Canada CA', value: 'CA' }, - { label: 'Chile CL', value: 'CL' }, - { label: 'Denmark DK', value: 'DK' }, - { label: 'Finland FI', value: 'FI' }, - { label: 'France FR', value: 'FR' }, - { label: 'Germany DE', value: 'DE' }, - { label: 'Hong Kong SAR HK', value: 'HK' }, - { label: 'India IN', value: 'IN' }, - { label: 'Indonesia ID', value: 'ID' }, - { label: 'Italy IT', value: 'IT' }, - { label: 'Japan JP', value: 'JP' }, - { label: 'Korea KR', value: 'KR' }, - { label: 'Malaysia MY', value: 'MY' }, - { label: 'Mexico MX', value: 'MX' }, - { label: 'Netherlands NL', value: 'NL' }, - { label: 'New Zealand NZ', value: 'NZ' }, - { label: 'Norway NO', value: 'NO' }, - { label: "People's Republic of China CN", value: 'CN' }, - { label: 'Poland PL', value: 'PL' }, - { label: 'Portugal PT', value: 'PT' }, - { label: 'Republic of the Philippines PH', value: 'PH' }, - { label: 'Russia RU', value: 'RU' }, - { label: 'Saudi Arabia SA', value: 'SA' }, - { label: 'South Africa ZA', value: 'ZA' }, - { label: 'Spain ES', value: 'ES' }, - { label: 'Sweden SE', value: 'SE' }, - { label: 'Switzerland CH', value: 'CH' }, - { label: 'Taiwan TW', value: 'TW' }, - { label: 'Türkiye TR', value: 'TR' }, - { label: 'United Kingdom GB', value: 'GB' }, - { label: 'United States US', value: 'US' }, -]; - -export const BingLanguageOptions = [ - { label: 'Arabic ar', value: 'ar' }, - { label: 'Basque eu', value: 'eu' }, - { label: 'Bengali bn', value: 'bn' }, - { label: 'Bulgarian bg', value: 'bg' }, - { label: 'Catalan ca', value: 'ca' }, - { label: 'Chinese (Simplified) zh-hans', value: 'ns' }, - { label: 'Chinese (Traditional) zh-hant', value: 'nt' }, - { label: 'Croatian hr', value: 'hr' }, - { label: 'Czech cs', value: 'cs' }, - { label: 'Danish da', value: 'da' }, - { label: 'Dutch nl', value: 'nl' }, - { label: 'English en', value: 'en' }, - { label: 'English-United Kingdom en-gb', value: 'gb' }, - { label: 'Estonian et', value: 'et' }, - { label: 'Finnish fi', value: 'fi' }, - { label: 'French fr', value: 'fr' }, - { label: 'Galician gl', value: 'gl' }, - { label: 'German de', value: 'de' }, - { label: 'Gujarati gu', value: 'gu' }, - { label: 'Hebrew he', value: 'he' }, - { label: 'Hindi hi', value: 'hi' }, - { label: 'Hungarian hu', value: 'hu' }, - { label: 'Icelandic is', value: 'is' }, - { label: 'Italian it', value: 'it' }, - { label: 'Japanese jp', value: 'jp' }, - { label: 'Kannada kn', value: 'kn' }, - { label: 'Korean ko', value: 'ko' }, - { label: 'Latvian lv', value: 'lv' }, - { label: 'Lithuanian lt', value: 'lt' }, - { label: 'Malay ms', value: 'ms' }, - { label: 'Malayalam ml', value: 'ml' }, - { label: 'Marathi mr', value: 'mr' }, - { label: 'Norwegian (Bokmål) nb', value: 'nb' }, - { label: 'Polish pl', value: 'pl' }, - { label: 'Portuguese (Brazil) pt-br', value: 'br' }, - { label: 'Portuguese (Portugal) pt-pt', value: 'pt' }, - { label: 'Punjabi pa', value: 'pa' }, - { label: 'Romanian ro', value: 'ro' }, - { label: 'Russian ru', value: 'ru' }, - { label: 'Serbian (Cyrylic) sr', value: 'sr' }, - { label: 'Slovak sk', value: 'sk' }, - { label: 'Slovenian sl', value: 'sl' }, - { label: 'Spanish es', value: 'es' }, - { label: 'Swedish sv', value: 'sv' }, - { label: 'Tamil ta', value: 'ta' }, - { label: 'Telugu te', value: 'te' }, - { label: 'Thai th', value: 'th' }, - { label: 'Turkish tr', value: 'tr' }, - { label: 'Ukrainian uk', value: 'uk' }, - { label: 'Vietnamese vi', value: 'vi' }, -]; - -export const DeepLSourceLangOptions = [ - { label: 'Arabic [1]', value: 'AR' }, - { label: 'Bulgarian', value: 'BG' }, - { label: 'Czech', value: 'CS' }, - { label: 'Danish', value: 'DA' }, - { label: 'German', value: 'DE' }, - { label: 'Greek', value: 'EL' }, - { label: 'English', value: 'EN' }, - { label: 'Spanish', value: 'ES' }, - { label: 'Estonian', value: 'ET' }, - { label: 'Finnish', value: 'FI' }, - { label: 'French', value: 'FR' }, - { label: 'Hungarian', value: 'HU' }, - { label: 'Indonesian', value: 'ID' }, - { label: 'Italian', value: 'IT' }, - { label: 'Japanese', value: 'JA' }, - { label: 'Korean', value: 'KO' }, - { label: 'Lithuanian', value: 'LT' }, - { label: 'Latvian', value: 'LV' }, - { label: 'Norwegian Bokmål', value: 'NB' }, - { label: 'Dutch', value: 'NL' }, - { label: 'Polish', value: 'PL' }, - { label: 'Portuguese (all Portuguese varieties mixed)', value: 'PT' }, - { label: 'Romanian', value: 'RO' }, - { label: 'Russian', value: 'RU' }, - { label: 'Slovak', value: 'SK' }, - { label: 'Slovenian', value: 'SL' }, - { label: 'Swedish', value: 'SV' }, - { label: 'Turkish', value: 'TR' }, - { label: 'Ukrainian', value: 'UK' }, - { label: 'Chinese', value: 'ZH' }, -]; -export const DeepLTargetLangOptions = [ - { label: 'Arabic [1]', value: 'AR' }, - { label: 'Bulgarian', value: 'BG' }, - { label: 'Czech', value: 'CS' }, - { label: 'Danish', value: 'DA' }, - { label: 'German', value: 'DE' }, - { label: 'Greek', value: 'EL' }, - { label: 'English (British)', value: 'EN-GB' }, - { label: 'English (American)', value: 'EN-US' }, - { label: 'Spanish', value: 'ES' }, - { label: 'Estonian', value: 'ET' }, - { label: 'Finnish', value: 'FI' }, - { label: 'French', value: 'FR' }, - { label: 'Hungarian', value: 'HU' }, - { label: 'Indonesian', value: 'ID' }, - { label: 'Italian', value: 'IT' }, - { label: 'Japanese', value: 'JA' }, - { label: 'Korean', value: 'KO' }, - { label: 'Lithuanian', value: 'LT' }, - { label: 'Latvian', value: 'LV' }, - { label: 'Norwegian Bokmål', value: 'NB' }, - { label: 'Dutch', value: 'NL' }, - { label: 'Polish', value: 'PL' }, - { label: 'Portuguese (Brazilian)', value: 'PT-BR' }, - { - label: - 'Portuguese (all Portuguese varieties excluding Brazilian Portuguese)', - value: 'PT-PT', - }, - { label: 'Romanian', value: 'RO' }, - { label: 'Russian', value: 'RU' }, - { label: 'Slovak', value: 'SK' }, - { label: 'Slovenian', value: 'SL' }, - { label: 'Swedish', value: 'SV' }, - { label: 'Turkish', value: 'TR' }, - { label: 'Ukrainian', value: 'UK' }, - { label: 'Chinese (simplified)', value: 'ZH' }, -]; - -export const BaiduFanyiDomainOptions = [ - 'it', - 'finance', - 'machinery', - 'senimed', - 'novel', - 'academic', - 'aerospace', - 'wiki', - 'news', - 'law', - 'contract', -]; - -export const BaiduFanyiSourceLangOptions = [ - 'auto', - 'zh', - 'en', - 'yue', - 'wyw', - 'jp', - 'kor', - 'fra', - 'spa', - 'th', - 'ara', - 'ru', - 'pt', - 'de', - 'it', - 'el', - 'nl', - 'pl', - 'bul', - 'est', - 'dan', - 'fin', - 'cs', - 'rom', - 'slo', - 'swe', - 'hu', - 'cht', - 'vie', -]; - -export const QWeatherLangOptions = [ - 'zh', - 'zh-hant', - 'en', - 'de', - 'es', - 'fr', - 'it', - 'ja', - 'ko', - 'ru', - 'hi', - 'th', - 'ar', - 'pt', - 'bn', - 'ms', - 'nl', - 'el', - 'la', - 'sv', - 'id', - 'pl', - 'tr', - 'cs', - 'et', - 'vi', - 'fil', - 'fi', - 'he', - 'is', - 'nb', -]; - -export const QWeatherTypeOptions = ['weather', 'indices', 'airquality']; - -export const QWeatherUserTypeOptions = ['free', 'paid']; - -export const QWeatherTimePeriodOptions = [ - 'now', - '3d', - '7d', - '10d', - '15d', - '30d', -]; - -export const ExeSQLOptions = ['mysql', 'postgresql', 'mariadb', 'mssql'].map( - (x) => ({ - label: upperFirst(x), - value: x, - }), -); - -export const WenCaiQueryTypeOptions = [ - 'stock', - 'zhishu', - 'fund', - 'hkstock', - 'usstock', - 'threeboard', - 'conbond', - 'insurance', - 'futures', - 'lccp', - 'foreign_exchange', -]; - -export const Jin10TypeOptions = ['flash', 'calendar', 'symbols', 'news']; -export const Jin10FlashTypeOptions = new Array(5) - .fill(1) - .map((x, idx) => (idx + 1).toString()); -export const Jin10CalendarTypeOptions = ['cj', 'qh', 'hk', 'us']; -export const Jin10CalendarDatashapeOptions = ['data', 'event', 'holiday']; -export const Jin10SymbolsTypeOptions = ['GOODS', 'FOREX', 'FUTURE', 'CRYPTO']; -export const Jin10SymbolsDatatypeOptions = ['symbols', 'quotes']; -export const TuShareSrcOptions = [ - 'sina', - 'wallstreetcn', - '10jqka', - 'eastmoney', - 'yuncaijing', - 'fenghuang', - 'jinrongjie', -]; -export const CrawlerResultOptions = ['markdown', 'html', 'content']; - -export enum BeginQueryType { - Line = 'line', - Paragraph = 'paragraph', - Options = 'options', - File = 'file', - Integer = 'integer', - Boolean = 'boolean', -} - -export const BeginQueryTypeIconMap = { - [BeginQueryType.Line]: TextCursorInput, - [BeginQueryType.Paragraph]: WrapText, - [BeginQueryType.Options]: OptionIcon, - [BeginQueryType.File]: CloudUpload, - [BeginQueryType.Integer]: ListOrdered, - [BeginQueryType.Boolean]: ToggleLeft, }; - -export const NoDebugOperatorsList = [ - Operator.Begin, - Operator.Answer, - Operator.Concentrator, - Operator.Template, - Operator.Message, - Operator.RewriteQuestion, - Operator.Switch, - Operator.Iteration, -]; - -export enum NodeHandleId { - Start = 'start', - End = 'end', - Tool = 'tool', - AgentTop = 'agentTop', - AgentBottom = 'agentBottom', -} - -export enum VariableType { - String = 'string', - Array = 'array', -} diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index dabbffc7201..b89057654e6 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -10,7 +10,6 @@ import { FormItem, FormLabel, } from '@/components/ui/form'; -import { Textarea } from '@/components/ui/textarea'; import { zodResolver } from '@hookform/resolvers/zod'; import { Position } from '@xyflow/react'; import { useContext, useMemo } from 'react'; @@ -22,6 +21,7 @@ import { AgentInstanceContext } from '../../context'; import { INextOperatorForm } from '../../interface'; import useGraphStore from '../../store'; import { isBottomSubAgent } from '../../utils'; +import { DescriptionField } from '../components/description-field'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; import { AgentTools } from './agent-tools'; @@ -85,20 +85,7 @@ const AgentForm = ({ node }: INextOperatorForm) => { }} > - {isSubAgent && ( - ( - - Description - - - - - )} - /> - )} + {isSubAgent && } ((pre, cur) => { const tool = tools.find((x) => x.component_name === cur); - pre.push(tool ? tool : { component_name: cur, params: {} }); + pre.push( + tool + ? tool + : { + component_name: cur, + params: + DefaultAgentToolValuesMap[ + cur as keyof typeof DefaultAgentToolValuesMap + ] || {}, + }, + ); return pre; }, []); diff --git a/web/src/pages/agent/form/baidu-fanyi-form/index.tsx b/web/src/pages/agent/form/baidu-fanyi-form/index.tsx index c4b3990269a..c02b3dd857b 100644 --- a/web/src/pages/agent/form/baidu-fanyi-form/index.tsx +++ b/web/src/pages/agent/form/baidu-fanyi-form/index.tsx @@ -1,11 +1,11 @@ import { useTranslate } from '@/hooks/common-hooks'; import { Form, Input, Select } from 'antd'; import { useMemo } from 'react'; +import { IOperatorForm } from '../../interface'; import { BaiduFanyiDomainOptions, BaiduFanyiSourceLangOptions, -} from '../../constant'; -import { IOperatorForm } from '../../interface'; +} from '../../options'; import DynamicInputVariable from '../components/dynamic-input-variable'; const BaiduFanyiForm = ({ onValuesChange, form, node }: IOperatorForm) => { diff --git a/web/src/pages/agent/form/bing-form/index.tsx b/web/src/pages/agent/form/bing-form/index.tsx index b640f08d0ea..dc6f000bafa 100644 --- a/web/src/pages/agent/form/bing-form/index.tsx +++ b/web/src/pages/agent/form/bing-form/index.tsx @@ -2,8 +2,8 @@ import TopNItem from '@/components/top-n-item'; import { useTranslate } from '@/hooks/common-hooks'; import { Form, Input, Select } from 'antd'; import { useMemo } from 'react'; -import { BingCountryOptions, BingLanguageOptions } from '../../constant'; import { IOperatorForm } from '../../interface'; +import { BingCountryOptions, BingLanguageOptions } from '../../options'; import DynamicInputVariable from '../components/dynamic-input-variable'; const BingForm = ({ onValuesChange, form, node }: IOperatorForm) => { diff --git a/web/src/pages/agent/form/components/description-field.tsx b/web/src/pages/agent/form/components/description-field.tsx new file mode 100644 index 00000000000..81c2ae14009 --- /dev/null +++ b/web/src/pages/agent/form/components/description-field.tsx @@ -0,0 +1,26 @@ +import { + FormControl, + FormField, + FormItem, + FormLabel, +} from '@/components/ui/form'; +import { Textarea } from '@/components/ui/textarea'; +import { useFormContext } from 'react-hook-form'; + +export function DescriptionField() { + const form = useFormContext(); + return ( + ( + + Description + + + + + )} + /> + ); +} diff --git a/web/src/pages/agent/form/crawler-form/index.tsx b/web/src/pages/agent/form/crawler-form/index.tsx index 8ef5f14d6b4..5dd75d0acee 100644 --- a/web/src/pages/agent/form/crawler-form/index.tsx +++ b/web/src/pages/agent/form/crawler-form/index.tsx @@ -1,8 +1,8 @@ import { useTranslate } from '@/hooks/common-hooks'; import { Form, Input, Select } from 'antd'; import { useMemo } from 'react'; -import { CrawlerResultOptions } from '../../constant'; import { IOperatorForm } from '../../interface'; +import { CrawlerResultOptions } from '../../options'; import DynamicInputVariable from '../components/dynamic-input-variable'; const CrawlerForm = ({ onValuesChange, form, node }: IOperatorForm) => { const { t } = useTranslate('flow'); diff --git a/web/src/pages/agent/form/deepl-form/index.tsx b/web/src/pages/agent/form/deepl-form/index.tsx index 1fc8cfc26f3..55240b3c120 100644 --- a/web/src/pages/agent/form/deepl-form/index.tsx +++ b/web/src/pages/agent/form/deepl-form/index.tsx @@ -1,9 +1,9 @@ import TopNItem from '@/components/top-n-item'; import { useTranslate } from '@/hooks/common-hooks'; import { Form, Select } from 'antd'; -import { DeepLSourceLangOptions, DeepLTargetLangOptions } from '../../constant'; import { useBuildSortOptions } from '../../form-hooks'; import { IOperatorForm } from '../../interface'; +import { DeepLSourceLangOptions, DeepLTargetLangOptions } from '../../options'; import DynamicInputVariable from '../components/dynamic-input-variable'; const DeepLForm = ({ onValuesChange, form, node }: IOperatorForm) => { diff --git a/web/src/pages/agent/form/exesql-form/index.tsx b/web/src/pages/agent/form/exesql-form/index.tsx index f88f593d0b5..11d0ce331d5 100644 --- a/web/src/pages/agent/form/exesql-form/index.tsx +++ b/web/src/pages/agent/form/exesql-form/index.tsx @@ -4,8 +4,8 @@ import { useTranslate } from '@/hooks/common-hooks'; import { useTestDbConnect } from '@/hooks/flow-hooks'; import { Button, Flex, Form, Input, InputNumber, Select } from 'antd'; import { useCallback } from 'react'; -import { ExeSQLOptions } from '../../constant'; import { IOperatorForm } from '../../interface'; +import { ExeSQLOptions } from '../../options'; import DynamicInputVariable from '../components/dynamic-input-variable'; const ExeSQLForm = ({ onValuesChange, form, node }: IOperatorForm) => { diff --git a/web/src/pages/agent/form/google-form/index.tsx b/web/src/pages/agent/form/google-form/index.tsx index 75bd3fb5e50..c2abb191523 100644 --- a/web/src/pages/agent/form/google-form/index.tsx +++ b/web/src/pages/agent/form/google-form/index.tsx @@ -1,8 +1,8 @@ import TopNItem from '@/components/top-n-item'; import { useTranslate } from '@/hooks/common-hooks'; import { Form, Input, Select } from 'antd'; -import { GoogleCountryOptions, GoogleLanguageOptions } from '../../constant'; import { IOperatorForm } from '../../interface'; +import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; import DynamicInputVariable from '../components/dynamic-input-variable'; const GoogleForm = ({ onValuesChange, form, node }: IOperatorForm) => { diff --git a/web/src/pages/agent/form/jin10-form/index.tsx b/web/src/pages/agent/form/jin10-form/index.tsx index aa9bb169fbf..2bc6d774a38 100644 --- a/web/src/pages/agent/form/jin10-form/index.tsx +++ b/web/src/pages/agent/form/jin10-form/index.tsx @@ -1,6 +1,7 @@ import { useTranslate } from '@/hooks/common-hooks'; import { Form, Input, Select } from 'antd'; import { useMemo } from 'react'; +import { IOperatorForm } from '../../interface'; import { Jin10CalendarDatashapeOptions, Jin10CalendarTypeOptions, @@ -8,8 +9,7 @@ import { Jin10SymbolsDatatypeOptions, Jin10SymbolsTypeOptions, Jin10TypeOptions, -} from '../../constant'; -import { IOperatorForm } from '../../interface'; +} from '../../options'; import DynamicInputVariable from '../components/dynamic-input-variable'; const Jin10Form = ({ onValuesChange, form, node }: IOperatorForm) => { diff --git a/web/src/pages/agent/form/qweather-form/index.tsx b/web/src/pages/agent/form/qweather-form/index.tsx index bb9be92c8f1..eee088762ad 100644 --- a/web/src/pages/agent/form/qweather-form/index.tsx +++ b/web/src/pages/agent/form/qweather-form/index.tsx @@ -10,13 +10,13 @@ import { Input } from '@/components/ui/input'; import { RAGFlowSelect } from '@/components/ui/select'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import { INextOperatorForm } from '../../interface'; import { QWeatherLangOptions, QWeatherTimePeriodOptions, QWeatherTypeOptions, QWeatherUserTypeOptions, -} from '../../constant'; -import { INextOperatorForm } from '../../interface'; +} from '../../options'; import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; enum FormFieldName { diff --git a/web/src/pages/agent/form/retrieval-form/next.tsx b/web/src/pages/agent/form/retrieval-form/next.tsx index f30faf92df5..68af46000a2 100644 --- a/web/src/pages/agent/form/retrieval-form/next.tsx +++ b/web/src/pages/agent/form/retrieval-form/next.tsx @@ -14,7 +14,7 @@ import { import { Textarea } from '@/components/ui/textarea'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo } from 'react'; -import { useForm } from 'react-hook-form'; +import { useForm, useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { initialRetrievalValues } from '../../constant'; @@ -24,8 +24,7 @@ import { Output } from '../components/output'; import { QueryVariable } from '../components/query-variable'; import { useValues } from './use-values'; -const FormSchema = z.object({ - query: z.string().optional(), +export const RetrievalPartialSchema = { similarity_threshold: z.coerce.number(), keywords_similarity_weight: z.coerce.number(), top_n: z.coerce.number(), @@ -33,11 +32,40 @@ const FormSchema = z.object({ kb_ids: z.array(z.string()), rerank_id: z.string(), empty_response: z.string(), +}; + +export const FormSchema = z.object({ + query: z.string().optional(), + ...RetrievalPartialSchema, }); -const RetrievalForm = ({ node }: INextOperatorForm) => { +export function EmptyResponseField() { const { t } = useTranslation(); + const form = useFormContext(); + return ( + ( + + {t('chat.emptyResponse')} + + + + + + )} + /> + + {/* Create a hidden field to make Form instance record this */} +
} + /> + + {t('flow.input')} + +
+ } + rightContent={ + + } + > + + + + {visible && ( + + )} + + + ); +}; + +export default UserFillUpForm; diff --git a/web/src/pages/agent/form/user-fill-up-form/use-values.ts b/web/src/pages/agent/form/user-fill-up-form/use-values.ts new file mode 100644 index 00000000000..0af1c78c35b --- /dev/null +++ b/web/src/pages/agent/form/user-fill-up-form/use-values.ts @@ -0,0 +1,21 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { initialUserFillUpValues } from '../../constant'; +import { buildBeginInputListFromObject } from '../begin-form/utils'; + +export function useValues(node?: RAGFlowNodeType) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return initialUserFillUpValues; + } + + const inputs = buildBeginInputListFromObject(formData?.inputs); + + return { ...(formData || {}), inputs }; + }, [node?.data?.form]); + + return values; +} diff --git a/web/src/pages/agent/form/user-fill-up-form/use-watch-change.ts b/web/src/pages/agent/form/user-fill-up-form/use-watch-change.ts new file mode 100644 index 00000000000..3dc45126589 --- /dev/null +++ b/web/src/pages/agent/form/user-fill-up-form/use-watch-change.ts @@ -0,0 +1,31 @@ +import { omit } from 'lodash'; +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { BeginQuery } from '../../interface'; +import useGraphStore from '../../store'; + +function transferInputsArrayToObject(inputs: BeginQuery[] = []) { + return inputs.reduce>>((pre, cur) => { + pre[cur.key] = omit(cur, 'key'); + + return pre; + }, {}); +} + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + if (id && form?.formState.isDirty) { + values = form?.getValues(); + + const nextValues = { + ...values, + inputs: transferInputsArrayToObject(values.inputs), + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index 25f75b38386..c243f977370 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -43,6 +43,7 @@ import { initialTavilyValues, initialTemplateValues, initialTuShareValues, + initialUserFillUpValues, initialWaitingDialogueValues, initialWenCaiValues, initialWikipediaValues, @@ -106,6 +107,7 @@ export const useInitializeOperatorParams = () => { [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, [Operator.Tool]: {}, [Operator.TavilySearch]: initialTavilyValues, + [Operator.UserFillUp]: initialUserFillUpValues, }; }, [llmId]); diff --git a/web/src/pages/agent/operator-icon.tsx b/web/src/pages/agent/operator-icon.tsx index 3c24794925a..1547b6ca791 100644 --- a/web/src/pages/agent/operator-icon.tsx +++ b/web/src/pages/agent/operator-icon.tsx @@ -1,6 +1,6 @@ import { IconFont } from '@/components/icon-font'; import { cn } from '@/lib/utils'; -import { CirclePlay } from 'lucide-react'; +import { CirclePlay, MessageSquareMore } from 'lucide-react'; import { Operator } from './constant'; interface IProps { @@ -19,6 +19,7 @@ export const OperatorIconMap = { [Operator.Switch]: 'condition', [Operator.Code]: 'code-set', [Operator.Agent]: 'agent-ai', + [Operator.UserFillUp]: MessageSquareMore, // [Operator.Relevant]: BranchesOutlined, // [Operator.RewriteQuestion]: FormOutlined, // [Operator.KeywordExtract]: KeywordIcon, From 32a7ad3cba1178273cafdc735dee73785d6db912 Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 26 Jun 2025 16:43:06 +0800 Subject: [PATCH 0042/1187] Feat: Customize the output variable name of the loop operator #3221 (#8514) ### What problem does this PR solve? Feat: Customize the output variable name of the loop operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/constant.tsx | 1 + .../pages/agent/form/components/output.tsx | 9 +- .../agent/form/components/query-variable.tsx | 13 ++- .../form/iteration-form/dynamic-output.tsx | 97 +++++++++++++++++++ .../pages/agent/form/iteration-form/index.tsx | 35 +++---- .../agent/form/iteration-form/interface.ts | 2 + .../form/iteration-form/use-build-options.ts | 31 ++++++ .../agent/form/iteration-form/use-values.ts | 21 ++-- .../iteration-form/use-watch-form-change.ts | 29 ++++++ .../pages/agent/hooks/use-get-begin-query.tsx | 2 +- 10 files changed, 202 insertions(+), 38 deletions(-) create mode 100644 web/src/pages/agent/form/iteration-form/dynamic-output.tsx create mode 100644 web/src/pages/agent/form/iteration-form/interface.ts create mode 100644 web/src/pages/agent/form/iteration-form/use-build-options.ts create mode 100644 web/src/pages/agent/form/iteration-form/use-watch-form-change.ts diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index dd8a882d3ad..d5f27d0d36f 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -646,6 +646,7 @@ export const initialEmailValues = { export const initialIterationValues = { items_ref: '', + outputs: {}, }; export const initialIterationStartValues = { outputs: { diff --git a/web/src/pages/agent/form/components/output.tsx b/web/src/pages/agent/form/components/output.tsx index 3f7e4ae27d3..eefc3cef555 100644 --- a/web/src/pages/agent/form/components/output.tsx +++ b/web/src/pages/agent/form/components/output.tsx @@ -1,12 +1,19 @@ export type OutputType = { title: string; - type: string; + type?: string; }; type OutputProps = { list: Array; }; +export function transferOutputs(outputs: Record) { + return Object.entries(outputs).map(([key, value]) => ({ + title: key, + type: value?.type, + })); +} + export function Output({ list }: OutputProps) { return (
diff --git a/web/src/pages/agent/form/components/query-variable.tsx b/web/src/pages/agent/form/components/query-variable.tsx index caa85cd7a38..a0c498a1d56 100644 --- a/web/src/pages/agent/form/components/query-variable.tsx +++ b/web/src/pages/agent/form/components/query-variable.tsx @@ -14,19 +14,18 @@ import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; type QueryVariableProps = { name?: string; type?: VariableType }; -export function QueryVariable({ - name = 'query', - type = VariableType.String, -}: QueryVariableProps) { +export function QueryVariable({ name = 'query', type }: QueryVariableProps) { const { t } = useTranslation(); const form = useFormContext(); const nextOptions = useBuildQueryVariableOptions(); const finalOptions = useMemo(() => { - return nextOptions.map((x) => { - return { ...x, options: x.options.filter((y) => y.type === type) }; - }); + return type + ? nextOptions.map((x) => { + return { ...x, options: x.options.filter((y) => y.type === type) }; + }) + : nextOptions; }, [nextOptions, type]); return ( diff --git a/web/src/pages/agent/form/iteration-form/dynamic-output.tsx b/web/src/pages/agent/form/iteration-form/dynamic-output.tsx new file mode 100644 index 00000000000..6b7e39e3347 --- /dev/null +++ b/web/src/pages/agent/form/iteration-form/dynamic-output.tsx @@ -0,0 +1,97 @@ +'use client'; + +import { FormContainer } from '@/components/form-container'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { BlockButton, Button } from '@/components/ui/button'; +import { + FormControl, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Separator } from '@/components/ui/separator'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { X } from 'lucide-react'; +import { ReactNode } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useBuildSubNodeOutputOptions } from './use-build-options'; + +interface IProps { + node?: RAGFlowNodeType; +} + +export function DynamicOutputForm({ node }: IProps) { + const { t } = useTranslation(); + const form = useFormContext(); + const options = useBuildSubNodeOutputOptions(node?.id); + const name = 'outputs'; + + const { fields, remove, append } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( +
+ {fields.map((field, index) => { + const typeField = `${name}.${index}.name`; + return ( +
+ ( + + + + + + + )} + /> + + ( + + + + + + + )} + /> + +
+ ); + })} + append({ name: '', ref: undefined })}> + Add + +
+ ); +} + +export function VariableTitle({ title }: { title: ReactNode }) { + return
{title}
; +} + +export function DynamicOutput({ node }: IProps) { + return ( + + + + + ); +} diff --git a/web/src/pages/agent/form/iteration-form/index.tsx b/web/src/pages/agent/form/iteration-form/index.tsx index 7a9d58dc132..7ff63125261 100644 --- a/web/src/pages/agent/form/iteration-form/index.tsx +++ b/web/src/pages/agent/form/iteration-form/index.tsx @@ -2,36 +2,23 @@ import { FormContainer } from '@/components/form-container'; import { Form } from '@/components/ui/form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo } from 'react'; -import { useForm } from 'react-hook-form'; +import { useForm, useWatch } from 'react-hook-form'; import { z } from 'zod'; -import { initialRetrievalValues, VariableType } from '../../constant'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { VariableType } from '../../constant'; import { INextOperatorForm } from '../../interface'; import { Output } from '../components/output'; import { QueryVariable } from '../components/query-variable'; +import { DynamicOutput } from './dynamic-output'; +import { OutputArray } from './interface'; import { useValues } from './use-values'; +import { useWatchFormChange } from './use-watch-form-change'; const FormSchema = z.object({ query: z.string().optional(), - similarity_threshold: z.coerce.number(), - keywords_similarity_weight: z.coerce.number(), - top_n: z.coerce.number(), - top_k: z.coerce.number(), - kb_ids: z.array(z.string()), - rerank_id: z.string(), - empty_response: z.string(), + outputs: z.array(z.object({ name: z.string(), value: z.any() })).optional(), }); const IterationForm = ({ node }: INextOperatorForm) => { - const outputList = useMemo(() => { - return [ - { - title: 'formalized_content', - type: initialRetrievalValues.outputs.formalized_content.type, - }, - ]; - }, []); - const defaultValues = useValues(node); const form = useForm({ @@ -39,6 +26,15 @@ const IterationForm = ({ node }: INextOperatorForm) => { resolver: zodResolver(FormSchema), }); + const outputs: OutputArray = useWatch({ + control: form?.control, + name: 'outputs', + }); + + const outputList = useMemo(() => { + return outputs.map((x) => ({ title: x.name, type: x?.type })); + }, [outputs]); + useWatchFormChange(node?.id, form); return ( @@ -55,6 +51,7 @@ const IterationForm = ({ node }: INextOperatorForm) => { type={VariableType.Array} > + diff --git a/web/src/pages/agent/form/iteration-form/interface.ts b/web/src/pages/agent/form/iteration-form/interface.ts new file mode 100644 index 00000000000..1a23c0a2e61 --- /dev/null +++ b/web/src/pages/agent/form/iteration-form/interface.ts @@ -0,0 +1,2 @@ +export type OutputArray = Array<{ name: string; ref: string; type?: string }>; +export type OutputObject = Record; diff --git a/web/src/pages/agent/form/iteration-form/use-build-options.ts b/web/src/pages/agent/form/iteration-form/use-build-options.ts new file mode 100644 index 00000000000..3439000d4e8 --- /dev/null +++ b/web/src/pages/agent/form/iteration-form/use-build-options.ts @@ -0,0 +1,31 @@ +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { Operator } from '../../constant'; +import { buildOutputOptions } from '../../hooks/use-get-begin-query'; +import useGraphStore from '../../store'; + +export function useBuildSubNodeOutputOptions(nodeId?: string) { + const { nodes } = useGraphStore((state) => state); + + const nodeOutputOptions = useMemo(() => { + if (!nodeId) { + return []; + } + + const subNodeWithOutputList = nodes.filter( + (x) => + x.parentId === nodeId && + x.data.label !== Operator.IterationStart && + !isEmpty(x.data?.form?.outputs), + ); + + return subNodeWithOutputList.map((x) => ({ + label: x.data.name, + value: x.id, + title: x.data.name, + options: buildOutputOptions(x.data.form.outputs, x.id), + })); + }, [nodeId, nodes]); + + return nodeOutputOptions; +} diff --git a/web/src/pages/agent/form/iteration-form/use-values.ts b/web/src/pages/agent/form/iteration-form/use-values.ts index 2f8479efec3..7c686255d90 100644 --- a/web/src/pages/agent/form/iteration-form/use-values.ts +++ b/web/src/pages/agent/form/iteration-form/use-values.ts @@ -2,24 +2,25 @@ import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { isEmpty } from 'lodash'; import { useMemo } from 'react'; import { initialIterationValues } from '../../constant'; +import { OutputObject } from './interface'; -export function useValues(node?: RAGFlowNodeType) { - const defaultValues = useMemo( - () => ({ - ...initialIterationValues, - }), - [], - ); +function convertToArray(outputObject: OutputObject) { + return Object.entries(outputObject).map(([key, value]) => ({ + name: key, + ref: value.ref, + })); +} +export function useValues(node?: RAGFlowNodeType) { const values = useMemo(() => { const formData = node?.data?.form; if (isEmpty(formData)) { - return defaultValues; + return { ...initialIterationValues, outputs: [] }; } - return formData; - }, [defaultValues, node?.data?.form]); + return { ...formData, outputs: convertToArray(formData.outputs) }; + }, [node?.data?.form]); return values; } diff --git a/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts b/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts new file mode 100644 index 00000000000..b5d3d626427 --- /dev/null +++ b/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts @@ -0,0 +1,29 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../../store'; +import { OutputArray, OutputObject } from './interface'; + +function transferToObject(list: OutputArray) { + return list.reduce((pre, cur) => { + pre[cur.name] = { ref: cur.ref }; + return pre; + }, {}); +} + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id && form?.formState.isDirty) { + values = form?.getValues(); + let nextValues: any = { + ...values, + outputs: transferToObject(values.outputs), + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/agent/hooks/use-get-begin-query.tsx b/web/src/pages/agent/hooks/use-get-begin-query.tsx index 8ad4164537d..6468e2675ab 100644 --- a/web/src/pages/agent/hooks/use-get-begin-query.tsx +++ b/web/src/pages/agent/hooks/use-get-begin-query.tsx @@ -58,7 +58,7 @@ function filterAllUpstreamNodeIds(edges: Edge[], nodeIds: string[]) { }, []); } -function buildOutputOptions( +export function buildOutputOptions( outputs: Record = {}, nodeId?: string, ) { From d11cfd4e45300b6409c3fefe4528d86b49b9713c Mon Sep 17 00:00:00 2001 From: Liu An Date: Thu, 26 Jun 2025 17:46:00 +0800 Subject: [PATCH 0043/1187] Fix: Add input validation to chunk creation endpoint (#8516) ### What problem does this PR solve? - Include optional `tag_feas` field if present in request - Add input validation for `important_kwd` and `question_kwd` to ensure they are lists - #8462 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/chunk_app.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/apps/chunk_app.py b/api/apps/chunk_app.py index 69b03b9ae69..c5bdee50207 100644 --- a/api/apps/chunk_app.py +++ b/api/apps/chunk_app.py @@ -224,11 +224,17 @@ def create(): "content_with_weight": req["content_with_weight"]} d["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(d["content_ltks"]) d["important_kwd"] = req.get("important_kwd", []) - d["important_tks"] = rag_tokenizer.tokenize(" ".join(req.get("important_kwd", []))) + if not isinstance(d["important_kwd"], list): + return get_data_error_result(message="`important_kwd` is required to be a list") + d["important_tks"] = rag_tokenizer.tokenize(" ".join(d["important_kwd"])) d["question_kwd"] = req.get("question_kwd", []) - d["question_tks"] = rag_tokenizer.tokenize("\n".join(req.get("question_kwd", []))) + if not isinstance(d["question_kwd"], list): + return get_data_error_result(message="`question_kwd` is required to be a list") + d["question_tks"] = rag_tokenizer.tokenize("\n".join(d["question_kwd"])) d["create_time"] = str(datetime.datetime.now()).replace("T", " ")[:19] d["create_timestamp_flt"] = datetime.datetime.now().timestamp() + if "tag_feas" in req: + d["tag_feas"] = req["tag_feas"] try: e, doc = DocumentService.get_by_id(req["doc_id"]) From 05bf01b0586b4a88d560cc1bab15a8df6aace10b Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 26 Jun 2025 17:46:37 +0800 Subject: [PATCH 0044/1187] Feat: Displays the output variable type selected by the loop operator #3221 (#8515) ### What problem does this PR solve? Feat: Displays the output variable type selected by the loop operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../form/iteration-form/dynamic-output.tsx | 36 +++++++++++++++++-- .../agent/form/iteration-form/interface.ts | 2 +- .../agent/form/iteration-form/use-values.ts | 1 + .../iteration-form/use-watch-form-change.ts | 3 +- web/src/pages/agent/index.tsx | 5 +-- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/web/src/pages/agent/form/iteration-form/dynamic-output.tsx b/web/src/pages/agent/form/iteration-form/dynamic-output.tsx index 6b7e39e3347..89a341a37b1 100644 --- a/web/src/pages/agent/form/iteration-form/dynamic-output.tsx +++ b/web/src/pages/agent/form/iteration-form/dynamic-output.tsx @@ -13,7 +13,7 @@ import { Input } from '@/components/ui/input'; import { Separator } from '@/components/ui/separator'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { X } from 'lucide-react'; -import { ReactNode } from 'react'; +import { ReactNode, useCallback, useMemo } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { useBuildSubNodeOutputOptions } from './use-build-options'; @@ -28,6 +28,26 @@ export function DynamicOutputForm({ node }: IProps) { const options = useBuildSubNodeOutputOptions(node?.id); const name = 'outputs'; + const flatOptions = useMemo(() => { + return options.reduce<{ label: string; value: string; type: string }[]>( + (pre, cur) => { + pre.push(...cur.options); + return pre; + }, + [], + ); + }, [options]); + + const findType = useCallback( + (val: string) => { + const type = flatOptions.find((x) => x.value === val)?.type; + if (type) { + return `Array<${type}>`; + } + }, + [flatOptions], + ); + const { fields, remove, append } = useFieldArray({ name: name, control: form.control, @@ -36,12 +56,13 @@ export function DynamicOutputForm({ node }: IProps) { return (
{fields.map((field, index) => { - const typeField = `${name}.${index}.name`; + const nameField = `${name}.${index}.name`; + const typeField = `${name}.${index}.type`; return (
( @@ -64,12 +85,21 @@ export function DynamicOutputForm({ node }: IProps) { { + form.setValue(typeField, findType(val)); + field.onChange(val); + }} > )} /> +
} + /> diff --git a/web/src/pages/agent/form/iteration-form/interface.ts b/web/src/pages/agent/form/iteration-form/interface.ts index 1a23c0a2e61..25f22aab047 100644 --- a/web/src/pages/agent/form/iteration-form/interface.ts +++ b/web/src/pages/agent/form/iteration-form/interface.ts @@ -1,2 +1,2 @@ export type OutputArray = Array<{ name: string; ref: string; type?: string }>; -export type OutputObject = Record; +export type OutputObject = Record; diff --git a/web/src/pages/agent/form/iteration-form/use-values.ts b/web/src/pages/agent/form/iteration-form/use-values.ts index 7c686255d90..29cd0632416 100644 --- a/web/src/pages/agent/form/iteration-form/use-values.ts +++ b/web/src/pages/agent/form/iteration-form/use-values.ts @@ -8,6 +8,7 @@ function convertToArray(outputObject: OutputObject) { return Object.entries(outputObject).map(([key, value]) => ({ name: key, ref: value.ref, + type: value.type, })); } diff --git a/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts b/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts index b5d3d626427..be6f3fa0dea 100644 --- a/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts +++ b/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts @@ -5,7 +5,7 @@ import { OutputArray, OutputObject } from './interface'; function transferToObject(list: OutputArray) { return list.reduce((pre, cur) => { - pre[cur.name] = { ref: cur.ref }; + pre[cur.name] = { ref: cur.ref, type: cur.type }; return pre; }, {}); } @@ -18,6 +18,7 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { // Manually triggered form updates are synchronized to the canvas if (id && form?.formState.isDirty) { values = form?.getValues(); + console.log('🚀 ~ useEffect ~ values:', values); let nextValues: any = { ...values, outputs: transferToObject(values.outputs), diff --git a/web/src/pages/agent/index.tsx b/web/src/pages/agent/index.tsx index 7a195289a7e..bdf51fcc174 100644 --- a/web/src/pages/agent/index.tsx +++ b/web/src/pages/agent/index.tsx @@ -7,14 +7,13 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'; +import { SidebarProvider } from '@/components/ui/sidebar'; import { useSetModalState } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { ReactFlowProvider } from '@xyflow/react'; import { CodeXml, EllipsisVertical, Forward, Import, Key } from 'lucide-react'; import { ComponentPropsWithoutRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { AgentSidebar } from './agent-sidebar'; import AgentCanvas from './canvas'; import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; import { useFetchDataOnMount } from './hooks/use-fetch-data'; @@ -119,9 +118,7 @@ export default function Agent() {
-
-
Date: Thu, 26 Jun 2025 17:46:53 +0800 Subject: [PATCH 0045/1187] Fix: chunk number error after re-parsing (#8513) ### What problem does this PR solve? Fix chunk number error after re-parsing. #8503. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/document_app.py | 7 +++++++ api/db/services/document_service.py | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/api/apps/document_app.py b/api/apps/document_app.py index 68a76394f54..90d62b0b382 100644 --- a/api/apps/document_app.py +++ b/api/apps/document_app.py @@ -365,6 +365,13 @@ def run(): info["progress_msg"] = "" info["chunk_num"] = 0 info["token_num"] = 0 + + e, doc = DocumentService.get_by_id(id) + if not e: + return get_data_error_result(message="Document not found!") + if doc.run == TaskStatus.DONE.value: + DocumentService.clear_chunk_num_when_rerun(doc.id) + DocumentService.update_by_id(id, info) tenant_id = DocumentService.get_tenant_id(id) if not tenant_id: diff --git a/api/db/services/document_service.py b/api/db/services/document_service.py index 8b7bc666000..c69f75acae5 100644 --- a/api/db/services/document_service.py +++ b/api/db/services/document_service.py @@ -279,6 +279,24 @@ def clear_chunk_num(cls, doc_id): Knowledgebase.id == doc.kb_id).execute() return num + + @classmethod + @DB.connection_context() + def clear_chunk_num_when_rerun(cls, doc_id): + doc = cls.model.get_by_id(doc_id) + assert doc, "Can't fine document in database." + + num = ( + Knowledgebase.update( + token_num=Knowledgebase.token_num - doc.token_num, + chunk_num=Knowledgebase.chunk_num - doc.chunk_num, + ) + .where(Knowledgebase.id == doc.kb_id) + .execute() + ) + return num + + @classmethod @DB.connection_context() def get_tenant_id(cls, doc_id): From 2990779d5913108ab1cd96773e48e399304f1294 Mon Sep 17 00:00:00 2001 From: FatMii <39074672+FatMii@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:28:46 +0800 Subject: [PATCH 0046/1187] =?UTF-8?q?fix(prompt-editor):=20resolve=20initi?= =?UTF-8?q?al=20cursor=20position=20and=20auto-newline=20=E2=80=A6=20(#851?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? In web folder's prompt-editor component, when entering content for the first time, the cursor position is abnormal and it will automatically wrap ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) Co-authored-by: leonlai --- web/src/components/prompt-editor/variable-picker-plugin.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/components/prompt-editor/variable-picker-plugin.tsx b/web/src/components/prompt-editor/variable-picker-plugin.tsx index 7aeb798b6b6..6e92d14f989 100644 --- a/web/src/components/prompt-editor/variable-picker-plugin.tsx +++ b/web/src/components/prompt-editor/variable-picker-plugin.tsx @@ -224,6 +224,9 @@ export default function VariablePickerMenuPlugin({ } $getRoot().clear().append(paragraph); + if ($isRangeSelection($getSelection())) { + $getRoot().selectEnd(); + } }, [findLabelByValue], ); From f7b6c4ca99ffdc2256f034aa417855a8d531550e Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 27 Jun 2025 09:27:28 +0800 Subject: [PATCH 0047/1187] Feat: Add StringTransform operator #3221 (#8520) ### What problem does this PR solve? Feat: Add StringTransform operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../node/dropdown/next-step-dropdown.tsx | 6 +- web/src/pages/agent/constant.tsx | 28 +++ .../agent/form-sheet/use-form-config-map.tsx | 6 + .../agent/form/components/query-variable.tsx | 20 ++- .../form/string-transform-form/index.tsx | 161 ++++++++++++++++++ .../form/string-transform-form/use-values.ts | 27 +++ .../use-watch-form-change.ts | 26 +++ web/src/pages/agent/hooks/use-add-node.ts | 2 + web/src/pages/agent/operator-icon.tsx | 5 +- 9 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 web/src/pages/agent/form/string-transform-form/index.tsx create mode 100644 web/src/pages/agent/form/string-transform-form/use-values.ts create mode 100644 web/src/pages/agent/form/string-transform-form/use-watch-form-change.ts diff --git a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx index dfb6b12f14b..4acc9c7461e 100644 --- a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx @@ -23,7 +23,7 @@ const HideModalContext = createContext['showModal']>(() => {}); function OperatorItemList({ operators }: OperatorItemProps) { const { addCanvasNode } = useContext(AgentInstanceContext); - const { nodeId, id, type, position } = useContext(HandleContext); + const { nodeId, id, position } = useContext(HandleContext); const hideModal = useContext(HideModalContext); return ( @@ -89,7 +89,9 @@ function AccordionOperators() { Data Manipulation - + diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index d5f27d0d36f..ea78c976514 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -87,6 +87,7 @@ export enum Operator { Tool = 'Tool', TavilySearch = 'TavilySearch', UserFillUp = 'UserFillUp', + StringTransform = 'StringTransform', } export const SwitchLogicOperatorOptions = ['and', 'or']; @@ -704,6 +705,32 @@ export const initialUserFillUpValues = { inputs: [], }; +export enum StringTransformMethod { + Merge = 'merge', + Split = 'split', +} + +export enum StringTransformDelimiter { + Comma = ',', + Semicolon = ';', + Period = '.', + LineBreak = '\n', + Tab = '\t', + Space = ' ', +} + +export const initialStringTransformValues = { + method: StringTransformMethod.Merge, + split_ref: '', + script: '', + delimiters: [], + outputs: { + result: { + type: 'string', + }, + }, +}; + export enum TavilySearchDepth { Basic = 'basic', Advanced = 'advanced', @@ -869,6 +896,7 @@ export const NodeMap = { [Operator.Tool]: 'toolNode', [Operator.TavilySearch]: 'ragNode', [Operator.UserFillUp]: 'ragNode', + [Operator.StringTransform]: 'ragNode', }; export enum BeginQueryType { diff --git a/web/src/pages/agent/form-sheet/use-form-config-map.tsx b/web/src/pages/agent/form-sheet/use-form-config-map.tsx index 175205f64e0..71ac344e5bf 100644 --- a/web/src/pages/agent/form-sheet/use-form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/use-form-config-map.tsx @@ -33,6 +33,7 @@ import QWeatherForm from '../form/qweather-form'; import RelevantForm from '../form/relevant-form'; import RetrievalForm from '../form/retrieval-form/next'; import RewriteQuestionForm from '../form/rewrite-question-form'; +import { StringTransformForm } from '../form/string-transform-form'; import SwitchForm from '../form/switch-form'; import TavilyForm from '../form/tavily-form'; import TemplateForm from '../form/template-form'; @@ -388,6 +389,11 @@ export function useFormConfigMap() { defaultValues: {}, schema: z.object({}), }, + [Operator.StringTransform]: { + component: StringTransformForm, + defaultValues: {}, + schema: z.object({}), + }, }; return FormConfigMap; diff --git a/web/src/pages/agent/form/components/query-variable.tsx b/web/src/pages/agent/form/components/query-variable.tsx index a0c498a1d56..9b33a87ae10 100644 --- a/web/src/pages/agent/form/components/query-variable.tsx +++ b/web/src/pages/agent/form/components/query-variable.tsx @@ -6,15 +6,23 @@ import { FormLabel, FormMessage, } from '@/components/ui/form'; -import { useMemo } from 'react'; +import { ReactNode, useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { VariableType } from '../../constant'; import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; -type QueryVariableProps = { name?: string; type?: VariableType }; +type QueryVariableProps = { + name?: string; + type?: VariableType; + label?: ReactNode; +}; -export function QueryVariable({ name = 'query', type }: QueryVariableProps) { +export function QueryVariable({ + name = 'query', + type, + label, +}: QueryVariableProps) { const { t } = useTranslation(); const form = useFormContext(); @@ -34,7 +42,11 @@ export function QueryVariable({ name = 'query', type }: QueryVariableProps) { name={name} render={({ field }) => ( - {t('flow.query')} + {label || ( + + {t('flow.query')} + + )} ({ label: key, value: val }), +); + +export const StringTransformForm = ({ node }: INextOperatorForm) => { + const values = useValues(node); + + const FormSchema = z.object({ + method: z.string(), + split_ref: z.string().optional(), + script: z.string().optional(), + delimiters: z.array(z.string()), + outputs: z.object({ result: z.object({ type: z.string() }) }).optional(), + }); + + const form = useForm>({ + defaultValues: values, + resolver: zodResolver(FormSchema), + }); + + const method = useWatch({ control: form.control, name: 'method' }); + + const isSplit = method === StringTransformMethod.Split; + + const outputList = useMemo(() => { + return transferOutputs(values.outputs); + }, [values.outputs]); + + const handleMethodChange = useCallback( + (value: StringTransformMethod) => { + const outputs = { + ...initialStringTransformValues.outputs, + result: { + type: + value === StringTransformMethod.Merge ? 'string' : 'Array', + }, + }; + form.setValue('outputs', outputs); + }, + [form], + ); + + useWatchFormChange(node?.id, form); + + return ( +
+ { + e.preventDefault(); + }} + > + + ( + + method + + { + handleMethodChange(value); + field.onChange(value); + }} + > + + + + )} + /> + {isSplit && ( + split_ref} + name="split_ref" + > + )} + {isSplit || ( + ( + + script + + + + + + )} + /> + )} + ( + + delimiters + + {isSplit ? ( + + ) : ( + + )} + + + + )} + /> +
} + /> +
+
+
+ +
+ + ); +}; diff --git a/web/src/pages/agent/form/string-transform-form/use-values.ts b/web/src/pages/agent/form/string-transform-form/use-values.ts new file mode 100644 index 00000000000..363f1567dbd --- /dev/null +++ b/web/src/pages/agent/form/string-transform-form/use-values.ts @@ -0,0 +1,27 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { + initialStringTransformValues, + StringTransformMethod, +} from '../../constant'; + +export function useValues(node?: RAGFlowNodeType) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return initialStringTransformValues; + } + + return { + ...formData, + delimiters: + formData.method === StringTransformMethod.Merge + ? formData.delimiters[0] + : formData.delimiters, + }; + }, [node?.data?.form]); + + return values; +} diff --git a/web/src/pages/agent/form/string-transform-form/use-watch-form-change.ts b/web/src/pages/agent/form/string-transform-form/use-watch-form-change.ts new file mode 100644 index 00000000000..c5b7841f256 --- /dev/null +++ b/web/src/pages/agent/form/string-transform-form/use-watch-form-change.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { StringTransformMethod } from '../../constant'; +import useGraphStore from '../../store'; + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id && form?.formState.isDirty) { + values = form?.getValues(); + let nextValues: any = values; + + if ( + values.delimiters !== undefined && + values.method === StringTransformMethod.Merge + ) { + nextValues.delimiters = [values.delimiters]; + } + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index c243f977370..023028b1a66 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -39,6 +39,7 @@ import { initialRelevantValues, initialRetrievalValues, initialRewriteQuestionValues, + initialStringTransformValues, initialSwitchValues, initialTavilyValues, initialTemplateValues, @@ -108,6 +109,7 @@ export const useInitializeOperatorParams = () => { [Operator.Tool]: {}, [Operator.TavilySearch]: initialTavilyValues, [Operator.UserFillUp]: initialUserFillUpValues, + [Operator.StringTransform]: initialStringTransformValues, }; }, [llmId]); diff --git a/web/src/pages/agent/operator-icon.tsx b/web/src/pages/agent/operator-icon.tsx index 1547b6ca791..f7d787b72bf 100644 --- a/web/src/pages/agent/operator-icon.tsx +++ b/web/src/pages/agent/operator-icon.tsx @@ -1,6 +1,6 @@ import { IconFont } from '@/components/icon-font'; import { cn } from '@/lib/utils'; -import { CirclePlay, MessageSquareMore } from 'lucide-react'; +import { CirclePlay } from 'lucide-react'; import { Operator } from './constant'; interface IProps { @@ -19,7 +19,8 @@ export const OperatorIconMap = { [Operator.Switch]: 'condition', [Operator.Code]: 'code-set', [Operator.Agent]: 'agent-ai', - [Operator.UserFillUp]: MessageSquareMore, + [Operator.UserFillUp]: 'await', + [Operator.StringTransform]: 'a-textprocessing', // [Operator.Relevant]: BranchesOutlined, // [Operator.RewriteQuestion]: FormOutlined, // [Operator.KeywordExtract]: KeywordIcon, From daf6c82066f083f37784151703eba88196fbd3fb Mon Sep 17 00:00:00 2001 From: zhanglei <357733652@qq.com> Date: Fri, 27 Jun 2025 09:38:33 +0800 Subject: [PATCH 0048/1187] fix: list index out of range (#8518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? stack: ``` 2025-06-26 17:22:24,739 ERROR 1609 list index out of range Traceback (most recent call last): File "/ragflow/.venv/lib/python3.10/site-packages/flask/app.py", line 880, in full_dispatch_request rv = self.dispatch_request() File "/ragflow/.venv/lib/python3.10/site-packages/flask/app.py", line 865, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] File "/ragflow/api/utils/api_utils.py", line 298, in decorated_function return func(*args, **kwargs) File "/ragflow/api/apps/sdk/session.py", line 472, in list_session print(conv["reference"][message_num]) IndexError: list index out of range ``` ![图片](https://github.com/user-attachments/assets/93fe90a8-0434-4842-ba9f-bb5a995b498a) ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) - [ ] New Feature (non-breaking change which adds functionality) - [ ] Documentation Update - [ ] Refactoring - [ ] Performance Improvement - [ ] Other (please describe): --- api/apps/sdk/session.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 1716db9eee5..74e770899f4 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -466,6 +466,8 @@ def list_session(tenant_id, chat_id): message_num = 0 while message_num < len(messages) and message_num < len(conv["reference"]): if message_num != 0 and messages[message_num]["role"] != "user": + if message_num >= len(conv["reference"]): + break chunk_list = [] if "chunks" in conv["reference"][message_num]: chunks = conv["reference"][message_num]["chunks"] From 938d8dd87830e4775a7dbfb8c8824f57c4c4b652 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Fri, 27 Jun 2025 09:41:12 +0800 Subject: [PATCH 0049/1187] Fix: user_default_llm configuration doesn't work for OpenAI API compatible LLM factory (#8502) ### What problem does this PR solve? https://github.com/infiniflow/ragflow/issues/8467 when add llm the llm_name will like "llm1___OpenAI-API" https://github.com/infiniflow/ragflow/blob/f09ca8e79500c0c99ba9703ccbc86965ef05de02/api/apps/llm_app.py#L173 so we should not use llm1 to query ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/db/services/llm_service.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/api/db/services/llm_service.py b/api/db/services/llm_service.py index e124b5b16ac..462eb9d4a22 100644 --- a/api/db/services/llm_service.py +++ b/api/db/services/llm_service.py @@ -45,6 +45,18 @@ def get_api_key(cls, tenant_id, model_name): objs = cls.query(tenant_id=tenant_id, llm_name=mdlnm) else: objs = cls.query(tenant_id=tenant_id, llm_name=mdlnm, llm_factory=fid) + + if (not objs) and fid: + if fid == "LocalAI": + mdlnm += "___LocalAI" + elif fid == "HuggingFace": + mdlnm += "___HuggingFace" + elif fid == "OpenAI-API-Compatible": + mdlnm += "___OpenAI-API" + elif fid == "VLLM": + mdlnm += "___VLLM" + + objs = cls.query(tenant_id=tenant_id, llm_name=mdlnm, llm_factory=fid) if not objs: return return objs[0] From be712714af588d81676190828809682565a09442 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Fri, 27 Jun 2025 10:22:53 +0800 Subject: [PATCH 0050/1187] Refactor:improve the logic to check cancel (#8524) ### What problem does this PR solve? improve the logic to check cancel ### Type of change - [x] Refactoring --------- Co-authored-by: Kevin Hu --- rag/svr/task_executor.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index 0377c72c33e..b24e7c141bc 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -46,7 +46,7 @@ import numpy as np from peewee import DoesNotExist -from api.db import LLMType, ParserType, TaskStatus +from api.db import LLMType, ParserType from api.db.services.document_service import DocumentService from api.db.services.llm_service import LLMBundle from api.db.services.task_service import TaskService @@ -213,8 +213,7 @@ async def collect(): canceled = False task = TaskService.get_task(msg["id"]) if task: - _, doc = DocumentService.get_by_id(task["doc_id"]) - canceled = doc.run == TaskStatus.CANCEL.value or doc.progress < 0 + canceled = TaskService.do_cancel(task["id"]) if not task or canceled: state = "is unknown" if not task else "has been cancelled" FAILED_TASKS += 1 From 7dbe06f7d8d607c2b72a753935c57bfceaaa4a89 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Fri, 27 Jun 2025 10:23:08 +0800 Subject: [PATCH 0051/1187] Refactor: remove useless initialize logic in list_doc (#8523) ### What problem does this PR solve? Remove useless logic in a loop for list_doc ### Type of change - [x] Refactoring - [x] Performance Improvement --- api/apps/sdk/doc.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/api/apps/sdk/doc.py b/api/apps/sdk/doc.py index e0f77c985e9..c744b07ab40 100644 --- a/api/apps/sdk/doc.py +++ b/api/apps/sdk/doc.py @@ -519,20 +519,20 @@ def list_docs(dataset_id, tenant_id): # rename key's name renamed_doc_list = [] + key_mapping = { + "chunk_num": "chunk_count", + "kb_id": "dataset_id", + "token_num": "token_count", + "parser_id": "chunk_method", + } + run_mapping = { + "0": "UNSTART", + "1": "RUNNING", + "2": "CANCEL", + "3": "DONE", + "4": "FAIL", + } for doc in docs: - key_mapping = { - "chunk_num": "chunk_count", - "kb_id": "dataset_id", - "token_num": "token_count", - "parser_id": "chunk_method", - } - run_mapping = { - "0": "UNSTART", - "1": "RUNNING", - "2": "CANCEL", - "3": "DONE", - "4": "FAIL", - } renamed_doc = {} for key, value in doc.items(): if key == "run": From 303c6dd1a8abaf48ffef4cb7570fc6f97a5efd56 Mon Sep 17 00:00:00 2001 From: Tuan Le <30828528+tuankg1028@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:23:21 +0700 Subject: [PATCH 0052/1187] Fix memory leaks in PIL image and BytesIO handling during chunk processing (#8522) ### What problem does this PR solve? This PR addresses critical memory leaks in the task executor's image processing pipeline. The current implementation fails to properly dispose of PIL Image objects and BytesIO buffers during chunk processing, leading to progressive memory accumulation that can cause the task executor to consume excessive memory over time. ### Background context - The `upload_to_minio` function processes images from document chunks and converts them to JPEG format for storage. - PIL Image objects hold significant memory resources that must be explicitly closed to prevent memory leaks. - BytesIO objects also consume memory and should be properly disposed of after use. - In high-throughput scenarios with many image-containing documents, these memory leaks can lead to out-of-memory errors and degraded performance. ### Specific issues fixed - PIL Image objects were not being explicitly closed after processing. - BytesIO buffers lacked proper cleanup in all code paths. - Converted images (RGBA/P to RGB) were not disposing of the original image object. - Memory references to large image data were not being cleared promptly. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] Performance Improvement ### Changes made - Added explicit `d["image"].close()` calls after image processing operations. - Implemented proper cleanup of converted images when changing formats from RGBA/P to RGB. - Enhanced BytesIO cleanup with `try/finally` blocks to ensure disposal in all code paths. - Added explicit `del d["image"]` to clear memory references after processing. This fix ensures stable memory usage during long-running document processing tasks and prevents potential out-of-memory conditions in production environments. --- rag/svr/task_executor.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index b24e7c141bc..ae5f548f9ff 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -289,18 +289,26 @@ async def upload_to_minio(document, chunk): return output_buffer = BytesIO() - if isinstance(d["image"], bytes): - output_buffer = BytesIO(d["image"]) - else: - # If the image is in RGBA mode, convert it to RGB mode before saving it in JPEG format. - if d["image"].mode in ("RGBA", "P"): - d["image"] = d["image"].convert("RGB") - d["image"].save(output_buffer, format='JPEG') - async with minio_limiter: - await trio.to_thread.run_sync(lambda: STORAGE_IMPL.put(task["kb_id"], d["id"], output_buffer.getvalue())) - d["img_id"] = "{}-{}".format(task["kb_id"], d["id"]) - del d["image"] - docs.append(d) + try: + if isinstance(d["image"], bytes): + output_buffer.write(d["image"]) + output_buffer.seek(0) + else: + # If the image is in RGBA mode, convert it to RGB mode before saving it in JPEG format. + if d["image"].mode in ("RGBA", "P"): + converted_image = d["image"].convert("RGB") + d["image"].close() # Close original image + d["image"] = converted_image + d["image"].save(output_buffer, format='JPEG') + d["image"].close() # Close PIL image after saving + + async with minio_limiter: + await trio.to_thread.run_sync(lambda: STORAGE_IMPL.put(task["kb_id"], d["id"], output_buffer.getvalue())) + d["img_id"] = "{}-{}".format(task["kb_id"], d["id"]) + del d["image"] # Remove image reference + docs.append(d) + finally: + output_buffer.close() # Ensure BytesIO is always closed except Exception: logging.exception( "Saving image of chunk {}/{}/{} got exception".format(task["location"], task["name"], d["id"])) From 0478f36e368b055aa88cfc51f958e57e4fa7750c Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Fri, 27 Jun 2025 10:23:34 +0800 Subject: [PATCH 0053/1187] Feat: allow users to choose which MCP tools are enabled (#8519) ### What problem does this PR solve? Allow users to choose which MCP tools are enabled. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/mcp_server_app.py | 48 ++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/api/apps/mcp_server_app.py b/api/apps/mcp_server_app.py index 3d29debdb11..34bd9dfb270 100644 --- a/api/apps/mcp_server_app.py +++ b/api/apps/mcp_server_app.py @@ -85,7 +85,7 @@ def create() -> Response: @manager.route("/update", methods=["POST"]) # noqa: F821 @login_required -@validate_request("id") +@validate_request("mcp_id") def update() -> Response: req = request.get_json() @@ -96,7 +96,7 @@ def update() -> Response: if server_name and len(server_name.encode("utf-8")) > 255: return get_data_error_result(message=f"Invaild MCP name or length is {len(server_name)} which is large than 255.") - mcp_id = req.get("id", "") + mcp_id = req.get("mcp_id", "") e, mcp_server = MCPServerService.get_by_id(mcp_id) if not e or mcp_server.tenant_id != current_user.id: return get_data_error_result(message=f"Cannot find MCP server {mcp_id} for user {current_user.id}") @@ -106,8 +106,10 @@ def update() -> Response: try: req["tenant_id"] = current_user.id + req.pop("mcp_id", None) + req["id"] = mcp_id - if not MCPServerService.filter_update([MCPServer.id == req["id"], MCPServer.tenant_id == req["tenant_id"]], req): + if not MCPServerService.filter_update([MCPServer.id == mcp_id, MCPServer.tenant_id == current_user.id], req): return get_data_error_result(message="Failed to updated MCP server.") e, updated_mcp = MCPServerService.get_by_id(req["id"]) @@ -240,11 +242,23 @@ def list_tools() -> Response: if e and mcp_server.tenant_id == current_user.id: server_key = mcp_server.id + cached_tools = mcp_server.variables.get("tools", {}) + tool_call_session = MCPToolCallSession(mcp_server, mcp_server.variables) tool_call_sessions.append(tool_call_session) - tools = tool_call_session.get_tools(timeout) - results[server_key] = [tool.model_dump() for tool in tools] + try: + tools = tool_call_session.get_tools(timeout) + except Exception: + tools = [] + + results[server_key] = [] + for tool in tools: + tool_dict = tool.model_dump() + cached_tool = cached_tools.get(tool_dict["name"]) + + tool_dict["enabled"] = cached_tool.get("enabled") if cached_tool and "enabled" in cached_tool else True + results[server_key].append(tool_dict) # PERF: blocking call to close sessions — consider moving to background thread or task queue close_multiple_mcp_toolcall_sessions(tool_call_sessions) @@ -284,3 +298,27 @@ def test_tool() -> Response: return get_json_result(data=result) except Exception as e: return server_error_response(e) + + +@manager.route("/cache_tools", methods=["POST"]) # noqa: F821 +@login_required +@validate_request("mcp_id", "tools") +def cache_tool() -> Response: + req = request.get_json() + mcp_id = req.get("mcp_id", "") + if not mcp_id: + return get_data_error_result(message="No MCP server ID provided.") + tools = req.get("tools", []) + + e, mcp_server = MCPServerService.get_by_id(mcp_id) + if not e or mcp_server.tenant_id != current_user.id: + return get_data_error_result(message=f"Cannot find MCP server {mcp_id} for user {current_user.id}") + + variables = mcp_server.variables + tools = {tool["name"]: tool for tool in tools if isinstance(tool, dict) and "name" in tool} + variables["tools"] = tools + + if not MCPServerService.filter_update([MCPServer.id == mcp_id, MCPServer.tenant_id == current_user.id], {"variables": variables}): + return get_data_error_result(message="Failed to updated MCP server.") + + return get_json_result(data=tools) From a10f05f4d7b4e25f6b447c686153f5d8a498d711 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Fri, 27 Jun 2025 12:10:53 +0800 Subject: [PATCH 0054/1187] Fix: chat with tools bug. (#8528) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/chat_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 55faa6b3208..638f021f4bb 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -156,7 +156,7 @@ def bind_tools(self, toolcall_session, tools): self.tools.append(tool) def chat_with_tools(self, system: str, history: list, gen_conf: dict): - gen_conf = self._clean_conf() + gen_conf = self._clean_conf(gen_conf) if system: history.insert(0, {"role": "system", "content": system}) From 5a2099a1c7301720f6e502d22ddc92269c8e7dd9 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 27 Jun 2025 12:11:29 +0800 Subject: [PATCH 0055/1187] Feat: Fixed the issue where the prompt menu content was hidden #3221 (#8530) ### What problem does this PR solve? Feat: Fixed the issue where the prompt menu content was hidden #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/package-lock.json | 13 +++++++++++++ web/package.json | 1 + web/src/pages/agent/constant.tsx | 3 ++- .../prompt-editor/variable-picker-plugin.tsx | 4 +--- .../form/string-transform-form/use-values.ts | 16 +++++++++++----- web/tailwind.config.js | 6 +++++- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index ad3faec8fcd..f06080d395d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -89,6 +89,7 @@ "remark-math": "^6.0.0", "sonner": "^1.7.4", "tailwind-merge": "^2.5.4", + "tailwind-scrollbar": "^3.1.0", "tailwindcss-animate": "^1.0.7", "umi": "^4.0.90", "umi-request": "^1.4.0", @@ -31293,6 +31294,18 @@ "url": "https://github.com/sponsors/dcastil" } }, + "node_modules/tailwind-scrollbar": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/tailwind-scrollbar/-/tailwind-scrollbar-3.1.0.tgz", + "integrity": "sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==", + "license": "MIT", + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "tailwindcss": "3.x" + } + }, "node_modules/tailwindcss": { "version": "3.4.14", "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.14.tgz", diff --git a/web/package.json b/web/package.json index 6184959ef86..f6810c80066 100644 --- a/web/package.json +++ b/web/package.json @@ -100,6 +100,7 @@ "remark-math": "^6.0.0", "sonner": "^1.7.4", "tailwind-merge": "^2.5.4", + "tailwind-scrollbar": "^3.1.0", "tailwindcss-animate": "^1.0.7", "umi": "^4.0.90", "umi-request": "^1.4.0", diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index ea78c976514..3697f91882c 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -723,7 +723,7 @@ export const initialStringTransformValues = { method: StringTransformMethod.Merge, split_ref: '', script: '', - delimiters: [], + delimiters: [StringTransformDelimiter.Comma], outputs: { result: { type: 'string', @@ -851,6 +851,7 @@ export const RestrictedUpstreamMap = { [Operator.WaitingDialogue]: [Operator.Begin], [Operator.Agent]: [Operator.Begin], [Operator.TavilySearch]: [Operator.Begin], + [Operator.StringTransform]: [Operator.Begin], }; export const NodeMap = { diff --git a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx index afa54d47740..8797f2236ba 100644 --- a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx @@ -67,7 +67,6 @@ function VariablePickerMenuItem({ option: VariableOption | VariableInnerOption, ) => void; }) { - console.info('xxxx'); return (
  • { const nextOptions = buildNextOptions(); - console.log('🚀 ~ nextOptions:', nextOptions); return anchorElementRef.current && nextOptions.length ? ReactDOM.createPortal(
    -
      +
        {nextOptions.map((option, i: number) => ( { const formData = node?.data?.form; if (isEmpty(formData)) { - return initialStringTransformValues; + return { + ...initialStringTransformValues, + delimiters: transferDelimiters(formData), + }; } return { ...formData, - delimiters: - formData.method === StringTransformMethod.Merge - ? formData.delimiters[0] - : formData.delimiters, + delimiters: transferDelimiters(formData), }; }, [node?.data?.form]); diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 81237478f98..e2c2a2e4b81 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -219,5 +219,9 @@ module.exports = { }, }, }, - plugins: [require('tailwindcss-animate'), require('@tailwindcss/line-clamp')], + plugins: [ + require('tailwindcss-animate'), + require('@tailwindcss/line-clamp'), + require('tailwind-scrollbar'), + ], }; From 0f7c9556344e5f5c27d39b5beff17177d841016d Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 27 Jun 2025 15:45:53 +0800 Subject: [PATCH 0056/1187] Feat: Display sub-agents in agent form #3221 (#8536) ### What problem does this PR solve? Feat: Display sub-agents in agent form #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../agent/form/agent-form/agent-tools.tsx | 87 +++++++++++++++++-- web/src/pages/agent/form/agent-form/index.tsx | 20 +---- .../agent/utils/filter-downstream-nodes.ts | 46 ++++++++-- 3 files changed, 122 insertions(+), 31 deletions(-) diff --git a/web/src/pages/agent/form/agent-form/agent-tools.tsx b/web/src/pages/agent/form/agent-form/agent-tools.tsx index 5961c8e5ad0..65666de3e28 100644 --- a/web/src/pages/agent/form/agent-form/agent-tools.tsx +++ b/web/src/pages/agent/form/agent-form/agent-tools.tsx @@ -1,7 +1,13 @@ import { BlockButton } from '@/components/ui/button'; import { cn } from '@/lib/utils'; +import { Position } from '@xyflow/react'; import { PencilLine, X } from 'lucide-react'; -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, useCallback, useContext, useMemo } from 'react'; +import { Operator } from '../../constant'; +import { AgentInstanceContext } from '../../context'; +import { INextOperatorForm } from '../../interface'; +import useGraphStore from '../../store'; +import { filterDownstreamAgentNodeIds } from '../../utils/filter-downstream-nodes'; import { ToolPopover } from './tool-popover'; import { useDeleteAgentNodeTools } from './tool-popover/use-update-tools'; import { useGetAgentToolNames } from './use-get-tools'; @@ -24,6 +30,32 @@ export function ToolCard({ ); } +type ActionButtonProps = { + record: T; + deleteRecord(record: T): void; + edit(record: T): void; +}; + +function ActionButton({ edit, deleteRecord, record }: ActionButtonProps) { + const handleDelete = useCallback(() => { + deleteRecord(record); + }, [deleteRecord, record]); + const handleEdit = useCallback(() => { + edit(record); + }, [edit, record]); + + return ( +
        + + +
        + ); +} + export function AgentTools() { const { toolNames } = useGetAgentToolNames(); const { deleteNodeTool } = useDeleteAgentNodeTools(); @@ -35,13 +67,11 @@ export function AgentTools() { {toolNames.map((x) => ( {x} -
        - - -
        + {}} + deleteRecord={deleteNodeTool(x)} + >
        ))}
      @@ -51,3 +81,44 @@ export function AgentTools() {
  • ); } + +export function Agents({ node }: INextOperatorForm) { + const { addCanvasNode } = useContext(AgentInstanceContext); + const { deleteAgentDownstreamNodesById, edges, getNode } = useGraphStore( + (state) => state, + ); + + const subBottomAgentNodeIds = useMemo(() => { + return filterDownstreamAgentNodeIds(edges, node?.id); + }, [edges, node?.id]); + + return ( +
    + Agents +
      + {subBottomAgentNodeIds.map((id) => { + const currentNode = getNode(id); + + return ( + + {currentNode?.data.name} + {}} + deleteRecord={deleteAgentDownstreamNodesById} + > + + ); + })} +
    + + Add Agent + +
    + ); +} diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index b89057654e6..59b6ec89286 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -2,7 +2,6 @@ import { FormContainer } from '@/components/form-container'; import { LargeModelFormField } from '@/components/large-model-form-field'; import { LlmSettingSchema } from '@/components/llm-setting-items/next'; import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; -import { BlockButton } from '@/components/ui/button'; import { Form, FormControl, @@ -11,20 +10,18 @@ import { FormLabel, } from '@/components/ui/form'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Position } from '@xyflow/react'; -import { useContext, useMemo } from 'react'; +import { useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { Operator, initialAgentValues } from '../../constant'; -import { AgentInstanceContext } from '../../context'; +import { initialAgentValues } from '../../constant'; import { INextOperatorForm } from '../../interface'; import useGraphStore from '../../store'; import { isBottomSubAgent } from '../../utils'; import { DescriptionField } from '../components/description-field'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; -import { AgentTools } from './agent-tools'; +import { AgentTools, Agents } from './agent-tools'; import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; @@ -74,8 +71,6 @@ const AgentForm = ({ node }: INextOperatorForm) => { useWatchFormChange(node?.id, form); - const { addCanvasNode } = useContext(AgentInstanceContext); - return (
    { )} - - Add Agent - +
    diff --git a/web/src/pages/agent/utils/filter-downstream-nodes.ts b/web/src/pages/agent/utils/filter-downstream-nodes.ts index be9b350a9b1..f376caff5a3 100644 --- a/web/src/pages/agent/utils/filter-downstream-nodes.ts +++ b/web/src/pages/agent/utils/filter-downstream-nodes.ts @@ -1,23 +1,21 @@ import { Edge } from '@xyflow/react'; import { NodeHandleId } from '../constant'; -// Get all downstream agent operators of the current agent operator -export function filterAllDownstreamAgentAndToolNodeIds( +// Get all downstream node ids +export function filterAllDownstreamNodeIds( edges: Edge[], nodeIds: string[], + predicate: (edge: Edge) => boolean, ) { return nodeIds.reduce((pre, nodeId) => { const currentEdges = edges.filter( - (x) => - x.source === nodeId && - (x.sourceHandle === NodeHandleId.AgentBottom || - x.sourceHandle === NodeHandleId.Tool), + (x) => x.source === nodeId && predicate(x), ); const downstreamNodeIds: string[] = currentEdges.map((x) => x.target); const ids = downstreamNodeIds.concat( - filterAllDownstreamAgentAndToolNodeIds(edges, downstreamNodeIds), + filterAllDownstreamNodeIds(edges, downstreamNodeIds, predicate), ); ids.forEach((x) => { @@ -29,3 +27,37 @@ export function filterAllDownstreamAgentAndToolNodeIds( return pre; }, []); } + +// Get all downstream agent and tool operators of the current agent operator +export function filterAllDownstreamAgentAndToolNodeIds( + edges: Edge[], + nodeIds: string[], +) { + return filterAllDownstreamNodeIds( + edges, + nodeIds, + (edge: Edge) => + edge.sourceHandle === NodeHandleId.AgentBottom || + edge.sourceHandle === NodeHandleId.Tool, + ); +} + +// Get all downstream agent operators of the current agent operator +export function filterAllDownstreamAgentNodeIds( + edges: Edge[], + nodeIds: string[], +) { + return filterAllDownstreamNodeIds( + edges, + nodeIds, + (edge: Edge) => edge.sourceHandle === NodeHandleId.AgentBottom, + ); +} +// The direct child agent node of the current node +export function filterDownstreamAgentNodeIds(edges: Edge[], nodeId?: string) { + return edges + .filter( + (x) => x.source === nodeId && x.sourceHandle === NodeHandleId.AgentBottom, + ) + .map((x) => x.target); +} From 8e1f8a0c4897067c2f7ba7dcd8a1bbd841c326a1 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 27 Jun 2025 18:53:13 +0800 Subject: [PATCH 0057/1187] Feat: Fixed the issue where the begin operator parameters could not be submitted during debugging #3221 (#8539) ### What problem does this PR solve? Feat: Fixed the issue where the begin operator parameters could not be submitted during debugging #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/hooks/use-send-message.ts | 1 + web/src/pages/agent/chat/hooks.ts | 2 - web/src/pages/agent/constant.tsx | 1 + web/src/pages/agent/debug-content/index.tsx | 1 - .../agent/form/components/query-variable.tsx | 6 +- web/src/pages/agent/run-sheet/index.tsx | 58 ++++++++++++------- 6 files changed, 44 insertions(+), 25 deletions(-) diff --git a/web/src/hooks/use-send-message.ts b/web/src/hooks/use-send-message.ts index 0253f29bfc2..7fdf1e21bea 100644 --- a/web/src/hooks/use-send-message.ts +++ b/web/src/hooks/use-send-message.ts @@ -11,6 +11,7 @@ export enum MessageEventType { Message = 'message', MessageEnd = 'message_end', WorkflowFinished = 'workflow_finished', + UserInputs = 'user_inputs', } export interface IAnswerEvent { diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/hooks.ts index d9e220cc0ec..bff0f4720c9 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/hooks.ts @@ -83,8 +83,6 @@ export const useSendNextMessage = () => { loading, derivedMessages, ref, - addNewestQuestion, - addNewestAnswer, removeLatestMessage, removeMessageById, addNewestOneQuestion, diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 3697f91882c..7fe79978b88 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -852,6 +852,7 @@ export const RestrictedUpstreamMap = { [Operator.Agent]: [Operator.Begin], [Operator.TavilySearch]: [Operator.Begin], [Operator.StringTransform]: [Operator.Begin], + [Operator.UserFillUp]: [Operator.Begin], }; export const NodeMap = { diff --git a/web/src/pages/agent/debug-content/index.tsx b/web/src/pages/agent/debug-content/index.tsx index 377c53e04e3..2c71d8ea1e0 100644 --- a/web/src/pages/agent/debug-content/index.tsx +++ b/web/src/pages/agent/debug-content/index.tsx @@ -263,7 +263,6 @@ const DebugContent = ({ const onSubmit = useCallback( (values: z.infer) => { console.log('🚀 ~ values:', values); - return values; const nextValues = Object.entries(values).map(([key, value]) => { const item = parameters[Number(key)]; let nextValue = value; diff --git a/web/src/pages/agent/form/components/query-variable.tsx b/web/src/pages/agent/form/components/query-variable.tsx index 9b33a87ae10..fc13d3f22bb 100644 --- a/web/src/pages/agent/form/components/query-variable.tsx +++ b/web/src/pages/agent/form/components/query-variable.tsx @@ -6,6 +6,7 @@ import { FormLabel, FormMessage, } from '@/components/ui/form'; +import { toLower } from 'lodash'; import { ReactNode, useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -31,7 +32,10 @@ export function QueryVariable({ const finalOptions = useMemo(() => { return type ? nextOptions.map((x) => { - return { ...x, options: x.options.filter((y) => y.type === type) }; + return { + ...x, + options: x.options.filter((y) => toLower(y.type).startsWith(type)), + }; }) : nextOptions; }, [nextOptions, type]); diff --git a/web/src/pages/agent/run-sheet/index.tsx b/web/src/pages/agent/run-sheet/index.tsx index 26adedda1ef..072b45107b9 100644 --- a/web/src/pages/agent/run-sheet/index.tsx +++ b/web/src/pages/agent/run-sheet/index.tsx @@ -1,5 +1,11 @@ +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, +} from '@/components/ui/sheet'; import { IModalProps } from '@/interfaces/common'; -import { Drawer } from 'antd'; +import { cn } from '@/lib/utils'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { BeginId } from '../constant'; @@ -8,14 +14,13 @@ import { useGetBeginNodeDataQuery } from '../hooks/use-get-begin-query'; import { useSaveGraphBeforeOpeningDebugDrawer } from '../hooks/use-save-graph'; import { BeginQuery } from '../interface'; import useGraphStore from '../store'; -import { getDrawerWidth } from '../utils'; const RunSheet = ({ hideModal, showModal: showChatModal, }: IModalProps) => { const { t } = useTranslation(); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + const { updateNodeForm, getNode } = useGraphStore((state) => state); const getBeginNodeDataQuery = useGetBeginNodeDataQuery(); const query: BeginQuery[] = getBeginNodeDataQuery(); @@ -25,12 +30,26 @@ const RunSheet = ({ ); const handleRunAgent = useCallback( - (nextValues: Record) => { - const currentNodes = updateNodeForm(BeginId, nextValues, ['query']); + (nextValues: BeginQuery[]) => { + const beginNode = getNode(BeginId); + const inputs: Record = beginNode?.data.form.inputs; + + const nextInputs = Object.keys(inputs).reduce>( + (pre, key) => { + const item = nextValues.find((x) => x.key === key); + if (item) { + pre[key] = { ...item }; + } + return pre; + }, + {}, + ); + + const currentNodes = updateNodeForm(BeginId, nextInputs, ['inputs']); handleRun(currentNodes); hideModal?.(); }, - [handleRun, hideModal, updateNodeForm], + [getNode, handleRun, hideModal, updateNodeForm], ); const onOk = useCallback( @@ -41,21 +60,18 @@ const RunSheet = ({ ); return ( - - - + + + + {t('flow.testRun')} + + + + ); }; From e441c17c2cfa5b5a35de09b64745aa4caaf41b48 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Fri, 27 Jun 2025 19:28:41 +0800 Subject: [PATCH 0058/1187] Refa: limit embedding concurrency and fix `chat_with_tool` (#8543) ### What problem does this PR solve? #8538 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] Refactoring --- rag/llm/chat_model.py | 362 ++++++++---------------------------------- rag/raptor.py | 16 +- 2 files changed, 75 insertions(+), 303 deletions(-) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 638f021f4bb..9ae96a702d5 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -18,11 +18,9 @@ import logging import os import random -import re import time from abc import ABC from copy import deepcopy -from http import HTTPStatus from typing import Any, Protocol from urllib.parse import urljoin @@ -61,9 +59,6 @@ def tool_call(self, name: str, arguments: dict[str, Any]) -> str: ... class Base(ABC): - tools: list[Any] - toolcall_sessions: dict[str, ToolCallSession] - def __init__(self, key, model_name, base_url, **kwargs): timeout = int(os.environ.get("LM_TIMEOUT_SECONDS", 600)) self.client = OpenAI(api_key=key, base_url=base_url, timeout=timeout) @@ -146,6 +141,37 @@ def _exceptions(self, e, attempt): error_code = ERROR_MAX_RETRIES return f"{ERROR_PREFIX}: {error_code} - {str(e)}" + def _verbose_tool_use(self, name, args, res): + return "" + json.dumps({ + "name": name, + "args": args, + "result": res + }, ensure_ascii=False, indent=2) + "" + + def _append_history(self, hist, tool_call, tool_res): + hist.append( + { + "role": "assistant", + "tool_calls": [ + { + "index": tool_call.index, + "id": tool_call.id, + "function": { + "name": tool_call.function.name, + "arguments": tool_call.function.arguments, + }, + "type": "function", + }, + ], + } + ) + try: + if isinstance(tool_res, dict): + tool_res = json.dumps(tool_res, ensure_ascii=False) + finally: + hist.append({"role": "tool", "tool_call_id": tool_call.id, "content": str(tool_res)}) + return hist + def bind_tools(self, toolcall_session, tools): if not (toolcall_session and tools): return @@ -160,18 +186,19 @@ def chat_with_tools(self, system: str, history: list, gen_conf: dict): if system: history.insert(0, {"role": "system", "content": system}) + gen_conf = self._clean_conf(gen_conf) ans = "" tk_count = 0 hist = deepcopy(history) # Implement exponential backoff retry strategy - for attempt in range(self.max_retries + 1): + for attempt in range(self.max_retries+1): history = hist - for _ in range(self.max_rounds * 2): - try: + try: + for _ in range(self.max_rounds*2): response = self.client.chat.completions.create(model=self.model_name, messages=history, tools=self.tools, **gen_conf) tk_count += self.total_token_count(response) - if any([not response.choices, not response.choices[0].message, not response.choices[0].message.content]): - raise Exception("500 response structure error.") + if any([not response.choices, not response.choices[0].message]): + raise Exception(f"500 response structure error. Response: {response}") if not hasattr(response.choices[0].message, "tool_calls") or not response.choices[0].message.tool_calls: if hasattr(response.choices[0].message, "reasoning_content") and response.choices[0].message.reasoning_content: @@ -188,14 +215,17 @@ def chat_with_tools(self, system: str, history: list, gen_conf: dict): try: args = json_repair.loads(tool_call.function.arguments) tool_response = self.toolcall_sessions[name].tool_call(name, args) - history.append({"role": "tool", "tool_call_id": tool_call.id, "content": str(tool_response)}) + history = self._append_history(history, tool_call, tool_response) + ans += self._verbose_tool_use(name, args, tool_response) except Exception as e: + logging.exception(msg=f"Wrong JSON argument format in LLM tool call response: {tool_call}") history.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"Tool call error: \n{tool_call}\nException:\n" + str(e)}) + ans += self._verbose_tool_use(name, {}, str(e)) - except Exception as e: - e = self._exceptions(e, attempt) - if e: - return e, tk_count + except Exception as e: + e = self._exceptions(e, attempt) + if e: + return e, tk_count assert False, "Shouldn't be here." def chat(self, system, history, gen_conf): @@ -228,9 +258,7 @@ def _wrap_toolcall_message(self, stream): return final_tool_calls def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): - if "max_tokens" in gen_conf: - del gen_conf["max_tokens"] - + gen_conf = self._clean_conf(gen_conf) tools = self.tools if system: history.insert(0, {"role": "system", "content": system}) @@ -240,9 +268,9 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): # Implement exponential backoff retry strategy for attempt in range(self.max_retries + 1): history = hist - for _ in range(self.max_rounds * 2): - reasoning_start = False - try: + try: + for _ in range(self.max_rounds*2): + reasoning_start = False response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, tools=tools, **gen_conf) final_tool_calls = {} answer = "" @@ -252,9 +280,11 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): index = tool_call.index if index not in final_tool_calls: + if not tool_call.function.arguments: + tool_call.function.arguments = "" final_tool_calls[index] = tool_call else: - final_tool_calls[index].function.arguments += tool_call.function.arguments + final_tool_calls[index].function.arguments += tool_call.function.arguments if tool_call.function.arguments else "" continue if any([not resp.choices, not resp.choices[0].delta, not hasattr(resp.choices[0].delta, "content")]): @@ -293,40 +323,26 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): name = tool_call.function.name try: args = json_repair.loads(tool_call.function.arguments) - tool_response = self.toolcall_sessions[name].tool_call(name, args) - history.append( - { - "role": "assistant", - "tool_calls": [ - { - "index": tool_call.index, - "id": tool_call.id, - "function": { - "name": tool_call.function.name, - "arguments": tool_call.function.arguments, - }, - "type": "function", - }, - ], - } - ) - history.append({"role": "tool", "tool_call_id": tool_call.id, "content": str(tool_response)}) + tool_response = self.toolcall_session[name].tool_call(name, args) + history = self._append_history(history, tool_call, tool_response) + yield self._verbose_tool_use(name, args, tool_response) except Exception as e: logging.exception(msg=f"Wrong JSON argument format in LLM tool call response: {tool_call}") history.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"Tool call error: \n{tool_call}\nException:\n" + str(e)}) - except Exception as e: - e = self._exceptions(e, attempt) - if e: - yield total_tokens - return + yield self._verbose_tool_use(name, {}, str(e)) - assert False, "Shouldn't be here." + except Exception as e: + e = self._exceptions(e, attempt) + if e: + yield total_tokens + return + + yield total_tokens def chat_streamly(self, system, history, gen_conf): if system: history.insert(0, {"role": "system", "content": system}) - if "max_tokens" in gen_conf: - del gen_conf["max_tokens"] + gen_conf = self._clean_conf(gen_conf) ans = "" total_tokens = 0 reasoning_start = False @@ -542,252 +558,8 @@ def chat_streamly(self, system, history, gen_conf): class QWenChat(Base): def __init__(self, key, model_name=Generation.Models.qwen_turbo, base_url=None, **kwargs): - super().__init__(key, model_name, base_url=base_url, **kwargs) - - import dashscope - - dashscope.api_key = key - self.model_name = model_name - if self.is_reasoning_model(self.model_name) or self.model_name in ["qwen-vl-plus", "qwen-vl-plus-latest", "qwen-vl-max", "qwen-vl-max-latest"]: - super().__init__(key, model_name, "https://dashscope.aliyuncs.com/compatible-mode/v1", **kwargs) - - def chat_with_tools(self, system: str, history: list, gen_conf: dict) -> tuple[str, int]: - if "max_tokens" in gen_conf: - del gen_conf["max_tokens"] - # if self.is_reasoning_model(self.model_name): - # return super().chat(system, history, gen_conf) - - stream_flag = str(os.environ.get("QWEN_CHAT_BY_STREAM", "true")).lower() == "true" - if not stream_flag: - from http import HTTPStatus - - tools = self.tools - - if system: - history.insert(0, {"role": "system", "content": system}) - - response = Generation.call(self.model_name, messages=history, result_format="message", tools=tools, **gen_conf) - ans = "" - tk_count = 0 - if response.status_code == HTTPStatus.OK: - assistant_output = response.output.choices[0].message - if not ans and "tool_calls" not in assistant_output and "reasoning_content" in assistant_output: - ans += "" + ans + "" - ans += response.output.choices[0].message.content - - if "tool_calls" not in assistant_output: - tk_count += self.total_token_count(response) - if response.output.choices[0].get("finish_reason", "") == "length": - if is_chinese([ans]): - ans += LENGTH_NOTIFICATION_CN - else: - ans += LENGTH_NOTIFICATION_EN - return ans, tk_count - - tk_count += self.total_token_count(response) - history.append(assistant_output) - - while "tool_calls" in assistant_output: - tool_info = {"content": "", "role": "tool", "tool_call_id": assistant_output.tool_calls[0]["id"]} - tool_name = assistant_output.tool_calls[0]["function"]["name"] - if tool_name: - arguments = json.loads(assistant_output.tool_calls[0]["function"]["arguments"]) - tool_info["content"] = self.toolcall_sessions[tool_name].tool_call(name=tool_name, arguments=arguments) - history.append(tool_info) - - response = Generation.call(self.model_name, messages=history, result_format="message", tools=self.tools, **gen_conf) - if response.output.choices[0].get("finish_reason", "") == "length": - tk_count += self.total_token_count(response) - if is_chinese([ans]): - ans += LENGTH_NOTIFICATION_CN - else: - ans += LENGTH_NOTIFICATION_EN - return ans, tk_count - - tk_count += self.total_token_count(response) - assistant_output = response.output.choices[0].message - if assistant_output.content is None: - assistant_output.content = "" - history.append(response) - ans += assistant_output["content"] - return ans, tk_count - else: - return "**ERROR**: " + response.message, tk_count - else: - result_list = [] - for result in self._chat_streamly_with_tools(system, history, gen_conf, incremental_output=True): - result_list.append(result) - error_msg_list = [result for result in result_list if str(result).find("**ERROR**") >= 0] - if len(error_msg_list) > 0: - return "**ERROR**: " + "".join(error_msg_list), 0 - else: - return "".join(result_list[:-1]), result_list[-1] - - def _chat(self, history, gen_conf): - if self.is_reasoning_model(self.model_name) or self.model_name in ["qwen-vl-plus", "qwen-vl-plus-latest", "qwen-vl-max", "qwen-vl-max-latest"]: - return super()._chat(history, gen_conf) - response = Generation.call(self.model_name, messages=history, result_format="message", **gen_conf) - ans = "" - tk_count = 0 - if response.status_code == HTTPStatus.OK: - ans += response.output.choices[0]["message"]["content"] - tk_count += self.total_token_count(response) - if response.output.choices[0].get("finish_reason", "") == "length": - if is_chinese([ans]): - ans += LENGTH_NOTIFICATION_CN - else: - ans += LENGTH_NOTIFICATION_EN - return ans, tk_count - return "**ERROR**: " + response.message, tk_count - - def _wrap_toolcall_message(self, old_message, message): - if not old_message: - return message - tool_call_id = message["tool_calls"][0].get("id") - if tool_call_id: - old_message.tool_calls[0]["id"] = tool_call_id - function = message.tool_calls[0]["function"] - if function: - if function.get("name"): - old_message.tool_calls[0]["function"]["name"] = function["name"] - if function.get("arguments"): - old_message.tool_calls[0]["function"]["arguments"] += function["arguments"] - return old_message - - def _chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict, incremental_output=True): - from http import HTTPStatus - - if system: - history.insert(0, {"role": "system", "content": system}) - if "max_tokens" in gen_conf: - del gen_conf["max_tokens"] - ans = "" - tk_count = 0 - try: - response = Generation.call(self.model_name, messages=history, result_format="message", tools=self.tools, stream=True, incremental_output=incremental_output, **gen_conf) - tool_info = {"content": "", "role": "tool"} - toolcall_message = None - tool_name = "" - tool_arguments = "" - finish_completion = False - reasoning_start = False - while not finish_completion: - for resp in response: - if resp.status_code == HTTPStatus.OK: - assistant_output = resp.output.choices[0].message - ans = resp.output.choices[0].message.content - if not ans and "tool_calls" not in assistant_output and "reasoning_content" in assistant_output: - ans = resp.output.choices[0].message.reasoning_content - if not reasoning_start: - reasoning_start = True - ans = "" + ans - else: - ans = ans + "" - - if "tool_calls" not in assistant_output: - reasoning_start = False - tk_count += self.total_token_count(resp) - if resp.output.choices[0].get("finish_reason", "") == "length": - if is_chinese([ans]): - ans += LENGTH_NOTIFICATION_CN - else: - ans += LENGTH_NOTIFICATION_EN - finish_reason = resp.output.choices[0]["finish_reason"] - if finish_reason == "stop": - finish_completion = True - yield ans - break - yield ans - continue - - tk_count += self.total_token_count(resp) - toolcall_message = self._wrap_toolcall_message(toolcall_message, assistant_output) - if "tool_calls" in assistant_output: - tool_call_finish_reason = resp.output.choices[0]["finish_reason"] - if tool_call_finish_reason == "tool_calls": - try: - tool_arguments = json.loads(toolcall_message.tool_calls[0]["function"]["arguments"]) - except Exception as e: - logging.exception(msg="_chat_streamly_with_tool tool call error") - yield ans + "\n**ERROR**: " + str(e) - finish_completion = True - break - - tool_name = toolcall_message.tool_calls[0]["function"]["name"] - history.append(toolcall_message) - tool_info["content"] = self.toolcall_sessions[tool_name].tool_call(name=tool_name, arguments=tool_arguments) - history.append(tool_info) - tool_info = {"content": "", "role": "tool"} - tool_name = "" - tool_arguments = "" - toolcall_message = None - response = Generation.call(self.model_name, messages=history, result_format="message", tools=self.tools, stream=True, incremental_output=incremental_output, **gen_conf) - else: - yield ( - ans + "\n**ERROR**: " + resp.output.choices[0].message - if not re.search(r" (key|quota)", str(resp.message).lower()) - else "Out of credit. Please set the API key in **settings > Model providers.**" - ) - except Exception as e: - logging.exception(msg="_chat_streamly_with_tool") - yield ans + "\n**ERROR**: " + str(e) - yield tk_count - - def _chat_streamly(self, system, history, gen_conf, incremental_output=True): - from http import HTTPStatus - - if system: - history.insert(0, {"role": "system", "content": system}) - if "max_tokens" in gen_conf: - del gen_conf["max_tokens"] - ans = "" - tk_count = 0 - try: - response = Generation.call(self.model_name, messages=history, result_format="message", stream=True, incremental_output=incremental_output, **gen_conf) - for resp in response: - if resp.status_code == HTTPStatus.OK: - ans = resp.output.choices[0]["message"]["content"] - tk_count = self.total_token_count(resp) - if resp.output.choices[0].get("finish_reason", "") == "length": - if is_chinese(ans): - ans += LENGTH_NOTIFICATION_CN - else: - ans += LENGTH_NOTIFICATION_EN - yield ans - else: - yield ( - ans + "\n**ERROR**: " + resp.message - if not re.search(r" (key|quota)", str(resp.message).lower()) - else "Out of credit. Please set the API key in **settings > Model providers.**" - ) - except Exception as e: - yield ans + "\n**ERROR**: " + str(e) - - yield tk_count - - def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict, incremental_output=True): - if "max_tokens" in gen_conf: - del gen_conf["max_tokens"] - - for txt in self._chat_streamly_with_tools(system, history, gen_conf, incremental_output=incremental_output): - yield txt - - def chat_streamly(self, system, history, gen_conf): - if "max_tokens" in gen_conf: - del gen_conf["max_tokens"] - if self.is_reasoning_model(self.model_name) or self.model_name in ["qwen-vl-plus", "qwen-vl-plus-latest", "qwen-vl-max", "qwen-vl-max-latest"]: - return super().chat_streamly(system, history, gen_conf) - - return self._chat_streamly(system, history, gen_conf) - - @staticmethod - def is_reasoning_model(model_name: str) -> bool: - return any( - [ - model_name.lower().find("deepseek") >= 0, - model_name.lower().find("qwq") >= 0 and model_name.lower() != "qwq-32b-preview", - ] - ) + super().__init__(key, model_name, base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", **kwargs) + return class ZhipuChat(Base): @@ -1877,4 +1649,4 @@ def __init__(self, key=None, model_name="", base_url="", **kwargs): if not base_url: raise ValueError("Local llm url cannot be None") base_url = urljoin(base_url, "v1") - super().__init__(key, model_name, base_url, **kwargs) + super().__init__(key, model_name, base_url, **kwargs) \ No newline at end of file diff --git a/rag/raptor.py b/rag/raptor.py index db2c82d2eec..a8d912f3295 100644 --- a/rag/raptor.py +++ b/rag/raptor.py @@ -105,14 +105,14 @@ async def summarize(ck_idx: list[int]): ], {"temperature": 0.3, "max_tokens": self._max_token}, ) - cnt = re.sub( - "(······\n由于长度的原因,回答被截断了,要继续吗?|For the content length reason, it stopped, continue?)", - "", - cnt, - ) - logging.debug(f"SUM: {cnt}") - embds = await self._embedding_encode(cnt) - chunks.append((cnt, embds)) + cnt = re.sub( + "(······\n由于长度的原因,回答被截断了,要继续吗?|For the content length reason, it stopped, continue?)", + "", + cnt, + ) + logging.debug(f"SUM: {cnt}") + embds = await self._embedding_encode(cnt) + chunks.append((cnt, embds)) labels = [] while end - start > 1: From aafeffa292589fb88c8f17965b41f0be6ad8a800 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Mon, 30 Jun 2025 09:22:31 +0800 Subject: [PATCH 0059/1187] Feat: add gitee as LLM provider. (#8545) ### What problem does this PR solve? ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- conf/llm_factories.json | 414 ++++++++++++++++++++++++++++++++++ rag/llm/__init__.py | 18 +- rag/llm/chat_model.py | 7 + rag/llm/embedding_model.py | 5 + rag/llm/rerank_model.py | 5 + rag/llm/sequence2txt_model.py | 8 + 6 files changed, 452 insertions(+), 5 deletions(-) diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 5e02696e669..52e9e3ff234 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -3287,6 +3287,420 @@ } ] }, + { + "name": "GiteeAI", + "logo": "", + "tags": "LLM,TEXT EMBEDDING,IMAGE2TEXT,SPEECH2TEXT,TEXT RE-RANK", + "status": "1", + "llm": [ + { + "llm_name": "ERNIE-4.5-Turbo", + "tags": "LLM,CHAT", + "max_tokens": 32768, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "ERNIE-X1-Turbo", + "tags": "LLM,CHAT", + "max_tokens": 4096, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "DeepSeek-R1", + "tags": "LLM,CHAT", + "max_tokens": 65792, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "DeepSeek-V3", + "tags": "LLM,CHAT", + "max_tokens": 65792, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen3-235B-A22B", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen3-30B-A3B", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen3-32B", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen3-8B", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen3-4B", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen3-0.6B", + "tags": "LLM,CHAT", + "max_tokens": 32000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "QwQ-32B", + "tags": "LLM,CHAT", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "DeepSeek-R1-Distill-Qwen-32B", + "tags": "LLM,CHAT", + "max_tokens": 65792, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "DeepSeek-R1-Distill-Qwen-14B", + "tags": "LLM,CHAT", + "max_tokens": 65792, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "DeepSeek-R1-Distill-Qwen-7B", + "tags": "LLM,CHAT", + "max_tokens": 65792, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "DeepSeek-R1-Distill-Qwen-1.5B", + "tags": "LLM,CHAT", + "max_tokens": 65792, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen2.5-72B-Instruct", + "tags": "LLM,CHAT", + "max_tokens": 4096, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "Qwen2.5-32B-Instruct", + "tags": "LLM,CHAT", + "max_tokens": 4096, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen2.5-14B-Instruct", + "tags": "LLM,CHAT", + "max_tokens": 4096, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "Qwen2.5-7B-Instruct", + "tags": "LLM,CHAT", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "Qwen2-72B-Instruct", + "tags": "LLM,CHAT", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Qwen2-7B-Instruct", + "tags": "LLM,CHAT", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "GLM-4-32B", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "GLM-4-9B-0414", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "glm-4-9b-chat", + "tags": "LLM,CHAT", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "internlm3-8b-instruct", + "tags": "LLM,CHAT", + "max_tokens": 4096, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "Yi-34B-Chat", + "tags": "LLM,CHAT", + "max_tokens": 32768, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "ERNIE-4.5-Turbo-VL", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 4096, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "Qwen2.5-VL-32B-Instruct", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 32768, + "model_type": "image2text", + "is_tools": true + }, + { + "llm_name": "Qwen2-VL-72B", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 4096, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "Align-DS-V", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 4096, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "InternVL3-78B", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 32768, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "InternVL3-38B", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 32768, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "InternVL2.5-78B", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 32768, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "InternVL2.5-26B", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 16384, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "InternVL2-8B", + "tags": "LLM,IMAGE2TEXT", + "max_tokens": 8192, + "model_type": "image2text", + "is_tools": false + }, + { + "llm_name": "Qwen2-Audio-7B-Instruct", + "tags": "LLM,SPEECH2TEXT,IMAGE2TEXT", + "max_tokens": 8192, + "model_type": "speech2text", + "is_tools": false + }, + { + "llm_name": "whisper-base", + "tags": "SPEECH2TEXT", + "max_tokens": 512, + "model_type": "speech2text", + "is_tools": false + }, + { + "llm_name": "whisper-large", + "tags": "SPEECH2TEXT", + "max_tokens": 512, + "model_type": "speech2text", + "is_tools": false + }, + { + "llm_name": "whisper-large-v3-turbo", + "tags": "SPEECH2TEXT", + "max_tokens": 512, + "model_type": "speech2text", + "is_tools": false + }, + { + "llm_name": "whisper-large-v3", + "tags": "SPEECH2TEXT", + "max_tokens": 512, + "model_type": "speech2text", + "is_tools": false + }, + { + "llm_name": "SenseVoiceSmall", + "tags": "SPEECH2TEXT", + "max_tokens": 512, + "model_type": "speech2text", + "is_tools": false + }, + { + "llm_name": "Qwen3-Reranker-8B", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 32768, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "Qwen3-Reranker-4B", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 32768, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "Qwen3-Reranker-0.6B", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 32768, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "Qwen3-Embedding-8B", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 8192, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "Qwen3-Embedding-4B", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 4096, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "Qwen3-Embedding-0.6B", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 4096, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "jina-clip-v1", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 512, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "jina-clip-v2", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 8192, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "jina-reranker-m0", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 10240, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "bce-embedding-base_v1", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 512, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "bce-reranker-base_v1", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 512, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "bge-m3", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 8192, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "bge-reranker-v2-m3", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 8192, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "bge-large-zh-v1.5", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 1024, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "bge-small-zh-v1.5", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 512, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "nomic-embed-code", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 512, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "all-mpnet-base-v2", + "tags": "TEXT EMBEDDING,TEXT RE-RANK", + "max_tokens": 512, + "model_type": "embedding", + "is_tools": false + } + ] + }, { "name": "Google Cloud", "logo": "", diff --git a/rag/llm/__init__.py b/rag/llm/__init__.py index 323ac9502f8..46ef7871f66 100644 --- a/rag/llm/__init__.py +++ b/rag/llm/__init__.py @@ -45,7 +45,8 @@ HuggingFaceEmbed, VolcEngineEmbed, GPUStackEmbed, - NovitaEmbed + NovitaEmbed, + GiteeEmbed ) from .chat_model import ( GptTurbo, @@ -87,6 +88,7 @@ HuggingFaceChat, GPUStackChat, ModelScopeChat, + GiteeChat ) from .cv_model import ( @@ -129,7 +131,8 @@ QWenRerank, GPUStackRerank, HuggingfaceRerank, - NovitaRerank + NovitaRerank, + GiteeRerank ) from .sequence2txt_model import ( @@ -139,6 +142,7 @@ XinferenceSeq2txt, TencentCloudSeq2txt, GPUStackSeq2txt, + GiteeSeq2txt ) from .tts_model import ( @@ -182,7 +186,8 @@ "HuggingFace": HuggingFaceEmbed, "VolcEngine": VolcEngineEmbed, "GPUStack": GPUStackEmbed, - "NovitaAI": NovitaEmbed + "NovitaAI": NovitaEmbed, + "GiteeAI": GiteeEmbed } CvModel = { @@ -206,7 +211,7 @@ "Tencent Hunyuan": HunyuanCV, "Anthropic": AnthropicCV, "SILICONFLOW": SILICONFLOWCV, - "GPUStack": GPUStackCV, + "GPUStack": GPUStackCV } ChatModel = { @@ -250,6 +255,7 @@ "HuggingFace": HuggingFaceChat, "GPUStack": GPUStackChat, "ModelScope":ModelScopeChat, + "GiteeAI": GiteeChat } RerankModel = { @@ -270,7 +276,8 @@ "Tongyi-Qianwen": QWenRerank, "GPUStack": GPUStackRerank, "HuggingFace": HuggingfaceRerank, - "NovitaAI": NovitaRerank + "NovitaAI": NovitaRerank, + "GiteeAI": GiteeRerank } Seq2txtModel = { @@ -280,6 +287,7 @@ "Xinference": XinferenceSeq2txt, "Tencent Cloud": TencentCloudSeq2txt, "GPUStack": GPUStackSeq2txt, + "GiteeAI": GiteeSeq2txt } TTSModel = { diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 9ae96a702d5..5170d3d28f3 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -1253,6 +1253,13 @@ def __init__(self, key, model_name, base_url="https://api.lingyiwanwu.com/v1", * super().__init__(key, model_name, base_url, **kwargs) +class GiteeChat(Base): + def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/", **kwargs): + if not base_url: + base_url = "https://ai.gitee.com/v1/" + super().__init__(key, model_name, base_url, **kwargs) + + class ReplicateChat(Base): def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 373485b9af2..6fb81591983 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -911,4 +911,9 @@ def __init__(self, key, model_name, base_url): class NovitaEmbed(SILICONFLOWEmbed): def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/embeddings"): + super().__init__(key, model_name, base_url) + + +class GiteeEmbed(SILICONFLOWEmbed): + def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/embeddings"): super().__init__(key, model_name, base_url) \ No newline at end of file diff --git a/rag/llm/rerank_model.py b/rag/llm/rerank_model.py index 5310a5f3baa..03d9fac5a41 100644 --- a/rag/llm/rerank_model.py +++ b/rag/llm/rerank_model.py @@ -629,4 +629,9 @@ def similarity(self, query: str, texts: list): class NovitaRerank(JinaRerank): def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/rerank"): + super().__init__(key, model_name, base_url) + + +class GiteeRerank(JinaRerank): + def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/rerank"): super().__init__(key, model_name, base_url) \ No newline at end of file diff --git a/rag/llm/sequence2txt_model.py b/rag/llm/sequence2txt_model.py index 01e529f86a8..193f0e14d7e 100644 --- a/rag/llm/sequence2txt_model.py +++ b/rag/llm/sequence2txt_model.py @@ -203,3 +203,11 @@ def __init__(self, key, model_name, base_url): self.base_url = base_url self.model_name = model_name self.key = key + + +class GiteeSeq2txt(Base): + def __init__(self, key, model_name="whisper-1", base_url="https://ai.gitee.com/v1/"): + if not base_url: + base_url = "https://ai.gitee.com/v1/" + self.client = OpenAI(api_key=key, base_url=base_url) + self.model_name = model_name \ No newline at end of file From 356d1f34856b8533164fba1375a9231657d79b86 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 30 Jun 2025 10:36:52 +0800 Subject: [PATCH 0060/1187] Feat: Allow users to enter text in the middle of a chat #3221 (#8569) ### What problem does this PR solve? Feat: Allow users to enter text in the middle of a chat #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../components/next-message-item/index.tsx | 31 +++++-- web/src/hooks/logic-hooks.ts | 2 +- web/src/hooks/use-send-message.ts | 9 ++ web/src/interfaces/database/chat.ts | 2 + .../pages/agent/canvas/node/begin-node.tsx | 26 +++--- web/src/pages/agent/chat/box.tsx | 6 +- web/src/pages/agent/chat/hooks.ts | 19 ++++ web/src/pages/agent/chat/input-form.tsx | 86 +++++++++++++++++++ .../agent/form/begin-form/use-edit-query.ts | 9 +- .../agent/form/begin-form/use-watch-change.ts | 4 +- .../iteration-form/use-watch-form-change.ts | 2 +- .../user-fill-up-form/use-watch-change.ts | 5 +- 12 files changed, 166 insertions(+), 35 deletions(-) create mode 100644 web/src/pages/agent/chat/input-form.tsx diff --git a/web/src/components/next-message-item/index.tsx b/web/src/components/next-message-item/index.tsx index 664f46afb0a..bc01010e303 100644 --- a/web/src/components/next-message-item/index.tsx +++ b/web/src/components/next-message-item/index.tsx @@ -3,7 +3,14 @@ import { MessageType } from '@/constants/chat'; import { useSetModalState } from '@/hooks/common-hooks'; import { IReference, IReferenceChunk } from '@/interfaces/database/chat'; import classNames from 'classnames'; -import { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { + PropsWithChildren, + memo, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { useFetchDocumentInfosByIds, @@ -23,7 +30,10 @@ import styles from './index.less'; const { Text } = Typography; -interface IProps extends Partial, IRegenerateMessage { +interface IProps + extends Partial, + IRegenerateMessage, + PropsWithChildren { item: IMessage; reference: IReference; loading?: boolean; @@ -52,6 +62,7 @@ const MessageItem = ({ showLikeButton = true, showLoudspeaker = true, visibleAvatar = true, + children, }: IProps) => { const { theme } = useTheme(); const isAssistant = item.role === MessageType.Assistant; @@ -152,12 +163,16 @@ const MessageItem = ({ : styles.messageUserText } > - + {item.data ? ( + children + ) : ( + + )} {isAssistant && referenceDocumentList.length > 0 && ( { if (idx !== -1) { return pre.map((x) => { if (x.id === answer.id) { - return { ...x, content: answer.answer }; + return { ...x, ...answer, content: answer.answer }; } return x; }); diff --git a/web/src/hooks/use-send-message.ts b/web/src/hooks/use-send-message.ts index 7fdf1e21bea..35dd81a8ed6 100644 --- a/web/src/hooks/use-send-message.ts +++ b/web/src/hooks/use-send-message.ts @@ -1,4 +1,5 @@ import { Authorization } from '@/constants/authorization'; +import { BeginQuery } from '@/pages/agent/interface'; import api from '@/utils/api'; import { getAuthorization } from '@/utils/authorization-util'; import { EventSourceParserStream } from 'eventsource-parser/stream'; @@ -31,6 +32,12 @@ export interface INodeData { created_at: number; } +export interface IInputData { + content: string; + inputs: Record; + tips: string; +} + export interface IMessageData { content: string; } @@ -39,6 +46,8 @@ export type INodeEvent = IAnswerEvent; export type IMessageEvent = IAnswerEvent; +export type IInputEvent = IAnswerEvent; + export type IChatEvent = INodeEvent | IMessageEvent; export type IEventList = Array; diff --git a/web/src/interfaces/database/chat.ts b/web/src/interfaces/database/chat.ts index a22cb2f670c..a5dabd6f301 100644 --- a/web/src/interfaces/database/chat.ts +++ b/web/src/interfaces/database/chat.ts @@ -73,6 +73,7 @@ export interface Message { prompt?: string; id?: string; audio_binary?: string; + data?: any; } export interface IReferenceChunk { @@ -102,6 +103,7 @@ export interface IAnswer { prompt?: string; id?: string; audio_binary?: string; + data?: any; } export interface Docagg { diff --git a/web/src/pages/agent/canvas/node/begin-node.tsx b/web/src/pages/agent/canvas/node/begin-node.tsx index 10df9eeef8b..d2f19914588 100644 --- a/web/src/pages/agent/canvas/node/begin-node.tsx +++ b/web/src/pages/agent/canvas/node/begin-node.tsx @@ -1,6 +1,6 @@ import { IBeginNode } from '@/interfaces/database/flow'; +import { cn } from '@/lib/utils'; import { NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; import get from 'lodash/get'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -20,7 +20,7 @@ import { NodeWrapper } from './node-wrapper'; // TODO: do not allow other nodes to connect to this node function InnerBeginNode({ data, id }: NodeProps) { const { t } = useTranslation(); - const query: BeginQuery[] = get(data, 'form.query', []); + const inputs: Record = get(data, 'form.inputs', {}); return ( @@ -39,24 +39,22 @@ function InnerBeginNode({ data, id }: NodeProps) { {t(`flow.begin`)} - - {query.map((x, idx) => { - const Icon = BeginQueryTypeIconMap[x.type as BeginQueryType]; +
    + {Object.entries(inputs).map(([key, val], idx) => { + const Icon = BeginQueryTypeIconMap[val.type as BeginQueryType]; return ( - - - {x.name} - {x.optional ? 'Yes' : 'No'} - + + {val.name} + {val.optional ? 'Yes' : 'No'} + ); })} - +
    ); } diff --git a/web/src/pages/agent/chat/box.tsx b/web/src/pages/agent/chat/box.tsx index 6daabd17e88..63126ac6e72 100644 --- a/web/src/pages/agent/chat/box.tsx +++ b/web/src/pages/agent/chat/box.tsx @@ -12,6 +12,7 @@ import { useClickDrawer } from '@/components/pdf-drawer/hooks'; import { useFetchAgent } from '@/hooks/use-agent-request'; import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; import { buildMessageUuidWithRole } from '@/utils/chat'; +import { InputForm } from './input-form'; const AgentChatBox = () => { const { @@ -24,6 +25,7 @@ const AgentChatBox = () => { derivedMessages, reference, stopOutputMessage, + send, } = useSendNextMessage(); const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = @@ -59,7 +61,9 @@ const AgentChatBox = () => { index={i} showLikeButton={false} sendLoading={sendLoading} - > + > + + ); })} diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/hooks.ts index bff0f4720c9..4d1972f8100 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/hooks.ts @@ -6,6 +6,7 @@ import { import { useFetchAgent } from '@/hooks/use-agent-request'; import { IEventList, + IInputEvent, IMessageEvent, MessageEventType, useSendMessageBySSE, @@ -66,6 +67,21 @@ function findMessageFromList(eventList: IEventList) { }; } +function findInputFromList(eventList: IEventList) { + const inputEvent = eventList.find( + (x) => x.event === MessageEventType.UserInputs, + ) as IInputEvent; + + if (!inputEvent) { + return {}; + } + + return { + id: inputEvent?.message_id, + data: inputEvent?.data, + }; +} + const useGetBeginNodePrologue = () => { const getNode = useGraphStore((state) => state.getNode); @@ -136,10 +152,12 @@ export const useSendNextMessage = () => { useEffect(() => { const { content, id } = findMessageFromList(answerList); + const inputAnswer = findInputFromList(answerList); if (answerList.length > 0) { addNewestOneAnswer({ answer: content, id: id, + ...inputAnswer, }); } }, [answerList, addNewestOneAnswer]); @@ -181,5 +199,6 @@ export const useSendNextMessage = () => { ref, removeMessageById, stopOutputMessage, + send, }; }; diff --git a/web/src/pages/agent/chat/input-form.tsx b/web/src/pages/agent/chat/input-form.tsx new file mode 100644 index 00000000000..b82dbaf84dd --- /dev/null +++ b/web/src/pages/agent/chat/input-form.tsx @@ -0,0 +1,86 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { toast } from 'sonner'; +import { z } from 'zod'; + +import { Button } from '@/components/ui/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Message } from '@/interfaces/database/chat'; +import { get } from 'lodash'; +import { useParams } from 'umi'; +import { useSendNextMessage } from './hooks'; + +const FormSchema = z.object({ + username: z.string().min(2, { + message: 'Username must be at least 2 characters.', + }), +}); + +type InputFormProps = Pick, 'send'> & { + message: Message; +}; + +export function InputForm({ send, message }: InputFormProps) { + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + username: '', + }, + }); + + const { id: canvasId } = useParams(); + + function onSubmit(data: z.infer) { + const inputs = get(message, 'data.inputs', {}); + + const nextInputs = Object.entries(inputs).reduce((pre, [key, val]) => { + pre[key] = { ...val, value: data.username }; + + return pre; + }, {}); + + send({ + inputs: nextInputs, + id: canvasId, + }); + + toast('You submitted the following values', { + description: ( +
    +          {JSON.stringify(data, null, 2)}
    +        
    + ), + }); + } + + return ( +
    + + ( + + Username + + + + + + )} + /> + + + + ); +} diff --git a/web/src/pages/agent/form/begin-form/use-edit-query.ts b/web/src/pages/agent/form/begin-form/use-edit-query.ts index 05b8240f2a5..80e060411d5 100644 --- a/web/src/pages/agent/form/begin-form/use-edit-query.ts +++ b/web/src/pages/agent/form/begin-form/use-edit-query.ts @@ -23,10 +23,7 @@ export const useEditQueryRecord = ({ const nextQuery: BeginQuery[] = index > -1 ? inputs.toSpliced(index, 1, record) : [...inputs, record]; - form.setValue('inputs', nextQuery, { - shouldDirty: true, - shouldTouch: true, - }); + form.setValue('inputs', nextQuery); hideModal(); }, @@ -45,11 +42,11 @@ export const useEditQueryRecord = ({ const handleDeleteRecord = useCallback( (idx: number) => { const inputs = form?.getValues('inputs') || []; - const nextQuery = inputs.filter( + const nextInputs = inputs.filter( (item: BeginQuery, index: number) => index !== idx, ); - form.setValue('inputs', nextQuery, { shouldDirty: true }); + form.setValue('inputs', nextInputs); }, [form], ); diff --git a/web/src/pages/agent/form/begin-form/use-watch-change.ts b/web/src/pages/agent/form/begin-form/use-watch-change.ts index 3dc45126589..00b40003f6b 100644 --- a/web/src/pages/agent/form/begin-form/use-watch-change.ts +++ b/web/src/pages/agent/form/begin-form/use-watch-change.ts @@ -17,8 +17,8 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { const updateNodeForm = useGraphStore((state) => state.updateNodeForm); useEffect(() => { - if (id && form?.formState.isDirty) { - values = form?.getValues(); + if (id) { + values = form?.getValues() || {}; const nextValues = { ...values, diff --git a/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts b/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts index be6f3fa0dea..4a780667ede 100644 --- a/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts +++ b/web/src/pages/agent/form/iteration-form/use-watch-form-change.ts @@ -3,7 +3,7 @@ import { UseFormReturn, useWatch } from 'react-hook-form'; import useGraphStore from '../../store'; import { OutputArray, OutputObject } from './interface'; -function transferToObject(list: OutputArray) { +export function transferToObject(list: OutputArray) { return list.reduce((pre, cur) => { pre[cur.name] = { ref: cur.ref, type: cur.type }; return pre; diff --git a/web/src/pages/agent/form/user-fill-up-form/use-watch-change.ts b/web/src/pages/agent/form/user-fill-up-form/use-watch-change.ts index 3dc45126589..f6c95d1571e 100644 --- a/web/src/pages/agent/form/user-fill-up-form/use-watch-change.ts +++ b/web/src/pages/agent/form/user-fill-up-form/use-watch-change.ts @@ -17,8 +17,9 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { const updateNodeForm = useGraphStore((state) => state.updateNodeForm); useEffect(() => { - if (id && form?.formState.isDirty) { - values = form?.getValues(); + // TODO: This should only be executed when the form changes + if (id) { + values = form?.getValues() || {}; const nextValues = { ...values, From 10f12fa14958b43abe455c7c7e914f73f5d466b7 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 30 Jun 2025 11:21:51 +0800 Subject: [PATCH 0061/1187] Feat: Support GiteeAI model #1853 (#8573) ### What problem does this PR solve? Feat: Support GiteeAI model #1853 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/assets/svg/llm/gitee-ai.svg | 6 ++++++ web/src/constants/llm.ts | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 web/src/assets/svg/llm/gitee-ai.svg diff --git a/web/src/assets/svg/llm/gitee-ai.svg b/web/src/assets/svg/llm/gitee-ai.svg new file mode 100644 index 00000000000..93a3cea676c --- /dev/null +++ b/web/src/assets/svg/llm/gitee-ai.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/web/src/constants/llm.ts b/web/src/constants/llm.ts index 26f0927311e..edcad4bafce 100644 --- a/web/src/constants/llm.ts +++ b/web/src/constants/llm.ts @@ -49,6 +49,7 @@ export enum LLMFactory { SentenceTransformers = 'sentence-transformers', GPUStack = 'GPUStack', VLLM = 'VLLM', + GiteeAI = 'GiteeAI', } // Please lowercase the file name @@ -103,4 +104,5 @@ export const IconMap = { [LLMFactory.SentenceTransformers]: 'sentence-transformers', [LLMFactory.GPUStack]: 'gpustack', [LLMFactory.VLLM]: 'vllm', + [LLMFactory.GiteeAI]: 'gitee-ai', }; From d46c24045f88c9bb98fd08604b4002be1ed64077 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Mon, 30 Jun 2025 11:22:11 +0800 Subject: [PATCH 0062/1187] Feat: add GiteeAI as a llm provider. (#8572) ### What problem does this PR solve? #1853 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- rag/llm/embedding_model.py | 8 +++++--- rag/llm/rerank_model.py | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 6fb81591983..f53d492b968 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -722,9 +722,7 @@ def __init__(self, key, model_name, base_url="https://api.upstage.ai/v1/solar"): class SILICONFLOWEmbed(Base): - def __init__( - self, key, model_name, base_url="https://api.siliconflow.cn/v1/embeddings" - ): + def __init__(self, key, model_name, base_url="https://api.siliconflow.cn/v1/embeddings"): if not base_url: base_url = "https://api.siliconflow.cn/v1/embeddings" self.headers = { @@ -911,9 +909,13 @@ def __init__(self, key, model_name, base_url): class NovitaEmbed(SILICONFLOWEmbed): def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/embeddings"): + if not base_url: + base_url = "https://api.novita.ai/v3/openai/embeddings" super().__init__(key, model_name, base_url) class GiteeEmbed(SILICONFLOWEmbed): def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/embeddings"): + if not base_url: + base_url = "https://ai.gitee.com/v1/embeddings" super().__init__(key, model_name, base_url) \ No newline at end of file diff --git a/rag/llm/rerank_model.py b/rag/llm/rerank_model.py index 03d9fac5a41..fafab7ee09e 100644 --- a/rag/llm/rerank_model.py +++ b/rag/llm/rerank_model.py @@ -629,9 +629,13 @@ def similarity(self, query: str, texts: list): class NovitaRerank(JinaRerank): def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/rerank"): + if not base_url: + base_url = "https://api.novita.ai/v3/openai/rerank" super().__init__(key, model_name, base_url) class GiteeRerank(JinaRerank): def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/rerank"): + if not base_url: + base_url = "https://ai.gitee.com/v1/rerank" super().__init__(key, model_name, base_url) \ No newline at end of file From 40b1684c1e499e6acba96bbc68e4f810e94c16ae Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 30 Jun 2025 15:39:38 +0800 Subject: [PATCH 0063/1187] Feat: Fixed the issue that the top toolbar disappears when opening the agent operator form #3221 (#8579) ### What problem does this PR solve? Feat: Fixed the issue that the top toolbar disappears when opening the agent operator form #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/app.tsx | 2 +- web/src/pages/agent/canvas/index.less | 2 +- web/src/pages/agent/canvas/index.tsx | 1 + web/src/pages/agent/index.tsx | 19 +++++-------------- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/web/src/app.tsx b/web/src/app.tsx index 1ea4025b6fd..538b331470f 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -78,7 +78,7 @@ function Root({ children }: React.PropsWithChildren) { }} locale={locale} > - + {children} diff --git a/web/src/pages/agent/canvas/index.less b/web/src/pages/agent/canvas/index.less index 21f72e1508f..0183d41b5c4 100644 --- a/web/src/pages/agent/canvas/index.less +++ b/web/src/pages/agent/canvas/index.less @@ -1,6 +1,6 @@ .canvasWrapper { position: relative; - height: 100%; + height: calc(100% - 64px); :global(.react-flow__node-group) { .commonNode(); border-radius: 0 0 10px 10px; diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index 8360f670415..9972f938590 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -174,6 +174,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { isValidConnection={isValidConnection} onEdgeMouseEnter={onEdgeMouseEnter} onEdgeMouseLeave={onEdgeMouseLeave} + className="h-full" defaultEdgeOptions={{ type: 'buttonEdge', markerEnd: 'logo', diff --git a/web/src/pages/agent/index.tsx b/web/src/pages/agent/index.tsx index bdf51fcc174..4b47fb5cb94 100644 --- a/web/src/pages/agent/index.tsx +++ b/web/src/pages/agent/index.tsx @@ -7,7 +7,6 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { SidebarProvider } from '@/components/ui/sidebar'; import { useSetModalState } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { ReactFlowProvider } from '@xyflow/react'; @@ -70,7 +69,7 @@ export default function Agent() { }, [getBeginNodeDataQuery, handleRun, showChatDrawer]); return ( -
    +
    -
    - -
    -
    - -
    -
    -
    -
    +
    {fileUploadVisible && ( Date: Mon, 30 Jun 2025 04:40:23 -0300 Subject: [PATCH 0064/1187] Adding semaphore usage on the '/run' endpoint (#8526) ### What problem does this PR solve? Switching threading.Lock() to asyncio.Lock(), since threading.Lock() is blocking. ### Type of change - [x] Performance Improvement --- sandbox/executor_manager/api/handlers.py | 22 +- sandbox/executor_manager/api/routes.py | 2 +- sandbox/executor_manager/core/container.py | 15 +- sandbox/executor_manager/requirements.txt | 2 +- sandbox/uv.lock | 492 ++++++++++----------- 5 files changed, 268 insertions(+), 265 deletions(-) diff --git a/sandbox/executor_manager/api/handlers.py b/sandbox/executor_manager/api/handlers.py index c6c673df468..a71d103817f 100644 --- a/sandbox/executor_manager/api/handlers.py +++ b/sandbox/executor_manager/api/handlers.py @@ -22,23 +22,23 @@ from services.execution import execute_code from services.limiter import limiter from services.security import analyze_code_security - +from core.container import _CONTAINER_EXECUTION_SEMAPHORES async def healthz_handler(): return {"status": "ok"} - @limiter.limit("5/second") async def run_code_handler(req: CodeExecutionRequest, request: Request): logger.info("🟢 Received /run request") - code = base64.b64decode(req.code_b64).decode("utf-8") - is_safe, issues = analyze_code_security(code, language=req.language) - if not is_safe: - issue_details = "\n".join([f"Line {lineno}: {issue}" for issue, lineno in issues]) - return CodeExecutionResult(status=ResultStatus.PROGRAM_RUNNER_ERROR, stdout="", stderr=issue_details, exit_code=-999, detail="Code is unsafe") + async with _CONTAINER_EXECUTION_SEMAPHORES[req.language]: + code = base64.b64decode(req.code_b64).decode("utf-8") + is_safe, issues = analyze_code_security(code, language=req.language) + if not is_safe: + issue_details = "\n".join([f"Line {lineno}: {issue}" for issue, lineno in issues]) + return CodeExecutionResult(status=ResultStatus.PROGRAM_RUNNER_ERROR, stdout="", stderr=issue_details, exit_code=-999, detail="Code is unsafe") - try: - return await execute_code(req) - except Exception as e: - return CodeExecutionResult(status=ResultStatus.PROGRAM_RUNNER_ERROR, stdout="", stderr=str(e), exit_code=-999, detail="unhandled_exception") + try: + return await execute_code(req) + except Exception as e: + return CodeExecutionResult(status=ResultStatus.PROGRAM_RUNNER_ERROR, stdout="", stderr=str(e), exit_code=-999, detail="unhandled_exception") diff --git a/sandbox/executor_manager/api/routes.py b/sandbox/executor_manager/api/routes.py index 69317b6720c..e43a45cc380 100644 --- a/sandbox/executor_manager/api/routes.py +++ b/sandbox/executor_manager/api/routes.py @@ -20,4 +20,4 @@ router = APIRouter() router.get("/healthz")(healthz_handler) -router.post("/run")(run_code_handler) +router.post("/run")(run_code_handler) \ No newline at end of file diff --git a/sandbox/executor_manager/core/container.py b/sandbox/executor_manager/core/container.py index a026de112fb..30632e5c263 100644 --- a/sandbox/executor_manager/core/container.py +++ b/sandbox/executor_manager/core/container.py @@ -17,7 +17,6 @@ import contextlib import os from queue import Empty, Queue -from threading import Lock from models.enums import SupportLanguage from util import env_setting_enabled, is_valid_memory_limit @@ -26,18 +25,22 @@ from core.logger import logger _CONTAINER_QUEUES: dict[SupportLanguage, Queue] = {} -_CONTAINER_LOCK: Lock = Lock() +_CONTAINER_LOCK: asyncio.Lock = asyncio.Lock() +_CONTAINER_EXECUTION_SEMAPHORES:dict[SupportLanguage,asyncio.Semaphore] = {} async def init_containers(size: int) -> tuple[int, int]: global _CONTAINER_QUEUES _CONTAINER_QUEUES = {SupportLanguage.PYTHON: Queue(), SupportLanguage.NODEJS: Queue()} - with _CONTAINER_LOCK: + async with _CONTAINER_LOCK: while not _CONTAINER_QUEUES[SupportLanguage.PYTHON].empty(): _CONTAINER_QUEUES[SupportLanguage.PYTHON].get_nowait() while not _CONTAINER_QUEUES[SupportLanguage.NODEJS].empty(): _CONTAINER_QUEUES[SupportLanguage.NODEJS].get_nowait() + + for language in SupportLanguage: + _CONTAINER_EXECUTION_SEMAPHORES[language] = asyncio.Semaphore(size) create_tasks = [] for i in range(size): @@ -56,7 +59,7 @@ async def init_containers(size: int) -> tuple[int, int]: async def teardown_containers(): - with _CONTAINER_LOCK: + async with _CONTAINER_LOCK: while not _CONTAINER_QUEUES[SupportLanguage.PYTHON].empty(): name = _CONTAINER_QUEUES[SupportLanguage.PYTHON].get_nowait() await async_run_command("docker", "rm", "-f", name, timeout=5) @@ -151,7 +154,7 @@ async def recreate_container(name: str, language: SupportLanguage) -> bool: async def release_container(name: str, language: SupportLanguage): """Asynchronously release a container""" - with _CONTAINER_LOCK: + async with _CONTAINER_LOCK: if await container_is_running(name): _CONTAINER_QUEUES[language].put(name) logger.info(f"🟢 Released container: {name} (remaining available: {_CONTAINER_QUEUES[language].qsize()})") @@ -168,7 +171,7 @@ async def allocate_container_blocking(language: SupportLanguage, timeout=10) -> while asyncio.get_running_loop().time() - start_time < timeout: try: name = _CONTAINER_QUEUES[language].get_nowait() - with _CONTAINER_LOCK: + async with _CONTAINER_LOCK: if not await container_is_running(name) and not await recreate_container(name, language): continue diff --git a/sandbox/executor_manager/requirements.txt b/sandbox/executor_manager/requirements.txt index 4ee4c706eb5..5295d12a90c 100644 --- a/sandbox/executor_manager/requirements.txt +++ b/sandbox/executor_manager/requirements.txt @@ -1,3 +1,3 @@ fastapi uvicorn -slowapi +slowapi \ No newline at end of file diff --git a/sandbox/uv.lock b/sandbox/uv.lock index 1f27216bf06..ef681064619 100644 --- a/sandbox/uv.lock +++ b/sandbox/uv.lock @@ -1,14 +1,14 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.10" [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] @@ -21,9 +21,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, ] [[package]] @@ -33,79 +33,79 @@ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "nodejs-wheel-binaries" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/18/f5e488eac4960ad9a2e71b95f0d91cf93a982c7f68aa90e4e0554f0bc37e/basedpyright-1.29.1.tar.gz", hash = "sha256:06bbe6c3b50ab4af20f80e154049477a50d8b81d2522eadbc9f472f2f92cd44b", size = 21773469 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/18/f5e488eac4960ad9a2e71b95f0d91cf93a982c7f68aa90e4e0554f0bc37e/basedpyright-1.29.1.tar.gz", hash = "sha256:06bbe6c3b50ab4af20f80e154049477a50d8b81d2522eadbc9f472f2f92cd44b", size = 21773469, upload-time = "2025-04-23T13:29:42.47Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/1b/1bb837bbb7e259928f33d3c105dfef4f5349ef08b3ef45576801256e3234/basedpyright-1.29.1-py3-none-any.whl", hash = "sha256:b7eb65b9d4aaeeea29a349ac494252032a75a364942d0ac466d7f07ddeacc786", size = 11397959 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/1b/1bb837bbb7e259928f33d3c105dfef4f5349ef08b3ef45576801256e3234/basedpyright-1.29.1-py3-none-any.whl", hash = "sha256:b7eb65b9d4aaeeea29a349ac494252032a75a364942d0ac466d7f07ddeacc786", size = 11397959, upload-time = "2025-04-23T13:29:38.106Z" }, ] [[package]] name = "certifi" version = "2025.4.26" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.2" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] [[package]] @@ -115,18 +115,18 @@ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -136,18 +136,18 @@ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, ] [[package]] name = "exceptiongroup" version = "1.2.2" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, ] [[package]] @@ -159,9 +159,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236, upload-time = "2025-03-23T22:55:43.822Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" }, ] [[package]] @@ -199,9 +199,9 @@ dev = [{ name = "basedpyright", specifier = ">=1.29.1" }] name = "h11" version = "0.16.0" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] @@ -212,9 +212,9 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] @@ -227,18 +227,18 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] @@ -250,34 +250,34 @@ dependencies = [ { name = "packaging" }, { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/94/a04e64f487a56f97aff67c53df609cc19d5c3f3e7e5697ec8a1ff8413829/limits-5.1.0.tar.gz", hash = "sha256:b298e4af0b47997da03cbeee9df027ddc2328f8630546125e81083bb56311827", size = 94655 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/94/a04e64f487a56f97aff67c53df609cc19d5c3f3e7e5697ec8a1ff8413829/limits-5.1.0.tar.gz", hash = "sha256:b298e4af0b47997da03cbeee9df027ddc2328f8630546125e81083bb56311827", size = 94655, upload-time = "2025-04-23T18:59:44.457Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/00/876a5ec60addda62ee13ac4b588a5afc0d1a86a431645a91711ceae834cf/limits-5.1.0-py3-none-any.whl", hash = "sha256:f368d4572ac3ef8190cb8b9911ed481175a0b4189894a63cac95cae39ebeb147", size = 60472 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/00/876a5ec60addda62ee13ac4b588a5afc0d1a86a431645a91711ceae834cf/limits-5.1.0-py3-none-any.whl", hash = "sha256:f368d4572ac3ef8190cb8b9911ed481175a0b4189894a63cac95cae39ebeb147", size = 60472, upload-time = "2025-04-23T18:59:42.266Z" }, ] [[package]] name = "nodejs-wheel-binaries" version = "22.15.0" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/5b/6c5f973765b96793d4e4d03684bcbd273b17e471ecc7e9bec4c32b595ebd/nodejs_wheel_binaries-22.15.0.tar.gz", hash = "sha256:ff81aa2a79db279c2266686ebcb829b6634d049a5a49fc7dc6921e4f18af9703", size = 8054 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/5b/6c5f973765b96793d4e4d03684bcbd273b17e471ecc7e9bec4c32b595ebd/nodejs_wheel_binaries-22.15.0.tar.gz", hash = "sha256:ff81aa2a79db279c2266686ebcb829b6634d049a5a49fc7dc6921e4f18af9703", size = 8054, upload-time = "2025-04-23T16:57:40.338Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/a8/a32e5bb99e95c536e7dac781cffab1e7e9f8661b8ee296b93df77e4df7f9/nodejs_wheel_binaries-22.15.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:aa16366d48487fff89446fb237693e777aa2ecd987208db7d4e35acc40c3e1b1", size = 50514526 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/e8/eb024dbb3a7d3b98c8922d1c306be989befad4d2132292954cb902f43b07/nodejs_wheel_binaries-22.15.0-py2.py3-none-macosx_11_0_x86_64.whl", hash = "sha256:a54bb3fee9170003fa8abc69572d819b2b1540344eff78505fcc2129a9175596", size = 51409179 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/0f/baa968456c3577e45c7d0e3715258bd175dcecc67b683a41a5044d5dae40/nodejs_wheel_binaries-22.15.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:867121ccf99d10523f6878a26db86e162c4939690e24cfb5bea56d01ea696c93", size = 57364460 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/a2/977f63cd07ed8fc27bc0d0cd72e801fc3691ffc8cd40a51496ff18a6d0a2/nodejs_wheel_binaries-22.15.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab0fbcda2ddc8aab7db1505d72cb958f99324b3834c4543541a305e02bfe860", size = 57889101 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/7f/57b9c24a4f0d25490527b043146aa0fdff2d8fdc82f90667cdaf6f00cfc9/nodejs_wheel_binaries-22.15.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2bde1d8e00cd955b9ce9ee9ac08309923e2778a790ee791b715e93e487e74bfd", size = 59190817 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/7f/970acbe33b81c22b3c7928f52e32347030aa46d23d779cf781cf9a9cf557/nodejs_wheel_binaries-22.15.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:acdd4ef73b6701aab9fbe02ac5e104f208a5e3c300402fa41ad7bc7f49499fbf", size = 60220316 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/4c/030243c04bb60f0de66c2d7ee3be289c6d28ef09113c06ffa417bdfedf8f/nodejs_wheel_binaries-22.15.0-py2.py3-none-win_amd64.whl", hash = "sha256:51deaf13ee474e39684ce8c066dfe86240edb94e7241950ca789befbbbcbd23d", size = 40718853 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/49/011d472814af4fabeaab7d7ce3d5a1a635a3dadc23ae404d1f546839ecb3/nodejs_wheel_binaries-22.15.0-py2.py3-none-win_arm64.whl", hash = "sha256:01a3fe4d60477f93bf21a44219db33548c75d7fed6dc6e6f4c05cf0adf015609", size = 36436645 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/a8/a32e5bb99e95c536e7dac781cffab1e7e9f8661b8ee296b93df77e4df7f9/nodejs_wheel_binaries-22.15.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:aa16366d48487fff89446fb237693e777aa2ecd987208db7d4e35acc40c3e1b1", size = 50514526, upload-time = "2025-04-23T16:57:07.478Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/e8/eb024dbb3a7d3b98c8922d1c306be989befad4d2132292954cb902f43b07/nodejs_wheel_binaries-22.15.0-py2.py3-none-macosx_11_0_x86_64.whl", hash = "sha256:a54bb3fee9170003fa8abc69572d819b2b1540344eff78505fcc2129a9175596", size = 51409179, upload-time = "2025-04-23T16:57:11.599Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/0f/baa968456c3577e45c7d0e3715258bd175dcecc67b683a41a5044d5dae40/nodejs_wheel_binaries-22.15.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:867121ccf99d10523f6878a26db86e162c4939690e24cfb5bea56d01ea696c93", size = 57364460, upload-time = "2025-04-23T16:57:15.725Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/a2/977f63cd07ed8fc27bc0d0cd72e801fc3691ffc8cd40a51496ff18a6d0a2/nodejs_wheel_binaries-22.15.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab0fbcda2ddc8aab7db1505d72cb958f99324b3834c4543541a305e02bfe860", size = 57889101, upload-time = "2025-04-23T16:57:19.643Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/7f/57b9c24a4f0d25490527b043146aa0fdff2d8fdc82f90667cdaf6f00cfc9/nodejs_wheel_binaries-22.15.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2bde1d8e00cd955b9ce9ee9ac08309923e2778a790ee791b715e93e487e74bfd", size = 59190817, upload-time = "2025-04-23T16:57:23.875Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/7f/970acbe33b81c22b3c7928f52e32347030aa46d23d779cf781cf9a9cf557/nodejs_wheel_binaries-22.15.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:acdd4ef73b6701aab9fbe02ac5e104f208a5e3c300402fa41ad7bc7f49499fbf", size = 60220316, upload-time = "2025-04-23T16:57:28.276Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/4c/030243c04bb60f0de66c2d7ee3be289c6d28ef09113c06ffa417bdfedf8f/nodejs_wheel_binaries-22.15.0-py2.py3-none-win_amd64.whl", hash = "sha256:51deaf13ee474e39684ce8c066dfe86240edb94e7241950ca789befbbbcbd23d", size = 40718853, upload-time = "2025-04-23T16:57:32.651Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/49/011d472814af4fabeaab7d7ce3d5a1a635a3dadc23ae404d1f546839ecb3/nodejs_wheel_binaries-22.15.0-py2.py3-none-win_arm64.whl", hash = "sha256:01a3fe4d60477f93bf21a44219db33548c75d7fed6dc6e6f4c05cf0adf015609", size = 36436645, upload-time = "2025-04-23T16:57:36.326Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] @@ -290,9 +290,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540, upload-time = "2025-04-29T20:38:55.02Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" }, ] [[package]] @@ -302,84 +302,84 @@ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, ] [[package]] @@ -392,9 +392,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, ] [[package]] @@ -404,18 +404,18 @@ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "limits" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/99/adfc7f94ca024736f061257d39118e1542bade7a52e86415a4c4ae92d8ff/slowapi-0.1.9.tar.gz", hash = "sha256:639192d0f1ca01b1c6d95bf6c71d794c3a9ee189855337b4821f7f457dddad77", size = 14028 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/99/adfc7f94ca024736f061257d39118e1542bade7a52e86415a4c4ae92d8ff/slowapi-0.1.9.tar.gz", hash = "sha256:639192d0f1ca01b1c6d95bf6c71d794c3a9ee189855337b4821f7f457dddad77", size = 14028, upload-time = "2024-02-05T12:11:52.13Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/bb/f71c4b7d7e7eb3fc1e8c0458a8979b912f40b58002b9fbf37729b8cb464b/slowapi-0.1.9-py3-none-any.whl", hash = "sha256:cfad116cfb84ad9d763ee155c1e5c5cbf00b0d47399a769b227865f5df576e36", size = 14670 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/bb/f71c4b7d7e7eb3fc1e8c0458a8979b912f40b58002b9fbf37729b8cb464b/slowapi-0.1.9-py3-none-any.whl", hash = "sha256:cfad116cfb84ad9d763ee155c1e5c5cbf00b0d47399a769b227865f5df576e36", size = 14670, upload-time = "2024-02-05T12:11:50.898Z" }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] @@ -425,18 +425,18 @@ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846, upload-time = "2025-04-13T13:56:17.942Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, ] [[package]] name = "typing-extensions" version = "4.13.2" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, ] [[package]] @@ -446,18 +446,18 @@ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, ] [[package]] name = "urllib3" version = "2.4.0" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, ] [[package]] @@ -469,71 +469,71 @@ dependencies = [ { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" }, ] [[package]] name = "wrapt" version = "1.17.2" source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307, upload-time = "2025-01-14T10:33:13.616Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486, upload-time = "2025-01-14T10:33:15.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777, upload-time = "2025-01-14T10:33:17.462Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314, upload-time = "2025-01-14T10:33:21.282Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947, upload-time = "2025-01-14T10:33:24.414Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778, upload-time = "2025-01-14T10:33:26.152Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716, upload-time = "2025-01-14T10:33:27.372Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548, upload-time = "2025-01-14T10:33:28.52Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334, upload-time = "2025-01-14T10:33:29.643Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427, upload-time = "2025-01-14T10:33:30.832Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774, upload-time = "2025-01-14T10:33:32.897Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308, upload-time = "2025-01-14T10:33:33.992Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488, upload-time = "2025-01-14T10:33:35.264Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776, upload-time = "2025-01-14T10:33:38.28Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776, upload-time = "2025-01-14T10:33:40.678Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420, upload-time = "2025-01-14T10:33:41.868Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199, upload-time = "2025-01-14T10:33:43.598Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307, upload-time = "2025-01-14T10:33:48.499Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025, upload-time = "2025-01-14T10:33:51.191Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879, upload-time = "2025-01-14T10:33:52.328Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419, upload-time = "2025-01-14T10:33:53.551Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773, upload-time = "2025-01-14T10:33:56.323Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, ] From d620432e3b872e6db3a6995c37d31d0a3c5b9153 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 30 Jun 2025 19:32:40 +0800 Subject: [PATCH 0065/1187] Feat: In a dialog message, users can enter different types of data #3221 (#8583) ### What problem does this PR solve? Feat: In a dialog message, users can enter different types of data #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/chat/box.tsx | 46 ++++++++- web/src/pages/agent/chat/hooks.ts | 15 +++ web/src/pages/agent/debug-content/index.tsx | 100 ++++++-------------- web/src/pages/agent/run-sheet/index.tsx | 12 +-- web/src/pages/agent/utils.ts | 20 +++- 5 files changed, 110 insertions(+), 83 deletions(-) diff --git a/web/src/pages/agent/chat/box.tsx b/web/src/pages/agent/chat/box.tsx index 63126ac6e72..34d886ef737 100644 --- a/web/src/pages/agent/chat/box.tsx +++ b/web/src/pages/agent/chat/box.tsx @@ -11,8 +11,14 @@ import PdfDrawer from '@/components/pdf-drawer'; import { useClickDrawer } from '@/components/pdf-drawer/hooks'; import { useFetchAgent } from '@/hooks/use-agent-request'; import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; +import { Message } from '@/interfaces/database/chat'; import { buildMessageUuidWithRole } from '@/utils/chat'; -import { InputForm } from './input-form'; +import { get } from 'lodash'; +import { useCallback } from 'react'; +import { useParams } from 'umi'; +import DebugContent from '../debug-content'; +import { BeginQuery } from '../interface'; +import { buildBeginQueryWithObject } from '../utils'; const AgentChatBox = () => { const { @@ -25,7 +31,7 @@ const AgentChatBox = () => { derivedMessages, reference, stopOutputMessage, - send, + sendFormMessage, } = useSendNextMessage(); const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = @@ -33,6 +39,35 @@ const AgentChatBox = () => { useGetFileIcon(); const { data: userInfo } = useFetchUserInfo(); const { data: canvasInfo } = useFetchAgent(); + const { id: canvasId } = useParams(); + + const getInputs = useCallback((message: Message) => { + return get(message, 'data.inputs', {}) as Record; + }, []); + + const buildInputList = useCallback( + (message: Message) => { + return Object.entries(getInputs(message)).map(([key, val]) => { + return { + ...val, + key, + }; + }); + }, + [getInputs], + ); + + const handleOk = useCallback( + (message: Message) => (values: BeginQuery[]) => { + const inputs = getInputs(message); + const nextInputs = buildBeginQueryWithObject(inputs, values); + sendFormMessage({ + inputs: nextInputs, + id: canvasId, + }); + }, + [canvasId, getInputs, sendFormMessage], + ); return ( <> @@ -62,7 +97,12 @@ const AgentChatBox = () => { showLikeButton={false} sendLoading={sendLoading} > - + ); })} diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/hooks.ts index 4d1972f8100..733ad53fe02 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/hooks.ts @@ -22,6 +22,7 @@ import { useParams } from 'umi'; import { v4 as uuid } from 'uuid'; import { BeginId } from '../constant'; import { AgentChatLogContext } from '../context'; +import { BeginQuery } from '../interface'; import useGraphStore from '../store'; import { receiveMessageError } from '../utils'; @@ -176,6 +177,19 @@ export const useSendNextMessage = () => { }); }, [value, done, addNewestOneQuestion, setValue, handleSendMessage]); + const sendFormMessage = useCallback( + (body: { id?: string; inputs: Record }) => { + send(body); + addNewestOneQuestion({ + content: Object.entries(body.inputs) + .map(([key, val]) => `${key}: ${val.value}`) + .join('
    '), + role: MessageType.User, + }); + }, + [addNewestOneQuestion, send], + ); + useEffect(() => { if (prologue) { addNewestOneAnswer({ @@ -200,5 +214,6 @@ export const useSendNextMessage = () => { removeMessageById, stopOutputMessage, send, + sendFormMessage, }; }; diff --git a/web/src/pages/agent/debug-content/index.tsx b/web/src/pages/agent/debug-content/index.tsx index 2c71d8ea1e0..964825f68f7 100644 --- a/web/src/pages/agent/debug-content/index.tsx +++ b/web/src/pages/agent/debug-content/index.tsx @@ -12,11 +12,8 @@ import { Input } from '@/components/ui/input'; import { RAGFlowSelect } from '@/components/ui/select'; import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; -import { useSetModalState } from '@/hooks/common-hooks'; -import { useSetSelectedRecord } from '@/hooks/logic-hooks'; import { zodResolver } from '@hookform/resolvers/zod'; -import { UploadChangeParam, UploadFile } from 'antd/es/upload'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { ReactNode, useCallback, useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; @@ -44,6 +41,7 @@ interface IProps { isNext?: boolean; loading?: boolean; submitButtonDisabled?: boolean; + btnText?: ReactNode; } const values = {}; @@ -54,76 +52,45 @@ const DebugContent = ({ isNext = true, loading = false, submitButtonDisabled = false, + btnText, }: IProps) => { const { t } = useTranslation(); const FormSchema = useMemo(() => { - const obj = parameters.reduce((pre, cur, idx) => { - const type = cur.type; - let fieldSchema; - if (StringFields.some((x) => x === type)) { - fieldSchema = z.string(); - } else if (type === BeginQueryType.Boolean) { - fieldSchema = z.boolean(); - } else if (type === BeginQueryType.Integer) { - fieldSchema = z.coerce.number(); - } else { - fieldSchema = z.instanceof(File); - } + const obj = parameters.reduce>( + (pre, cur, idx) => { + const type = cur.type; + let fieldSchema; + if (StringFields.some((x) => x === type)) { + fieldSchema = z.string(); + } else if (type === BeginQueryType.Boolean) { + fieldSchema = z.boolean(); + } else if (type === BeginQueryType.Integer) { + fieldSchema = z.coerce.number(); + } else { + fieldSchema = z.instanceof(File); + } - if (cur.optional) { - fieldSchema.optional(); - } + if (cur.optional) { + fieldSchema.optional(); + } - pre[idx.toString()] = fieldSchema; + pre[idx.toString()] = fieldSchema; - return pre; - }, {}); + return pre; + }, + {}, + ); return z.object(obj); }, [parameters]); - const form = useForm({ + const form = useForm>({ defaultValues: values, resolver: zodResolver(FormSchema), }); - const { - visible, - hideModal: hidePopover, - switchVisible, - showModal: showPopover, - } = useSetModalState(); - - const { setRecord, currentRecord } = useSetSelectedRecord(); - // const { submittable } = useHandleSubmittable(form); const submittable = true; - const [isUploading, setIsUploading] = useState(false); - - const handleShowPopover = useCallback( - (idx: number) => () => { - setRecord(idx); - showPopover(); - }, - [setRecord, showPopover], - ); - - const normFile = (e: any) => { - if (Array.isArray(e)) { - return e; - } - return e?.fileList; - }; - - const onChange = useCallback( - (optional: boolean) => - ({ fileList }: UploadChangeParam) => { - if (!optional) { - setIsUploading(fileList.some((x) => x.status === 'uploading')); - } - }, - [], - ); const renderWidget = useCallback( (q: BeginQuery, idx: string) => { @@ -132,14 +99,6 @@ const DebugContent = ({ label: q.name ?? q.key, name: idx, }; - if (q.optional === false) { - props.rules = [{ required: true }]; - } - - // const urlList: { url: string; result: string }[] = - // form.getFieldValue(idx) || []; - - const urlList: { url: string; result: string }[] = []; const BeginQueryTypeMap = { [BeginQueryType.Line]: ( @@ -183,7 +142,10 @@ const DebugContent = ({ ({ label: x, value: x })) ?? [] + q.options?.map((x) => ({ + label: x, + value: x as string, + })) ?? [] } {...field} > @@ -295,10 +257,10 @@ const DebugContent = ({ - {t(isNext ? 'common.next' : 'flow.run')} + {btnText || t(isNext ? 'common.next' : 'flow.run')} diff --git a/web/src/pages/agent/run-sheet/index.tsx b/web/src/pages/agent/run-sheet/index.tsx index 072b45107b9..3f661ff078f 100644 --- a/web/src/pages/agent/run-sheet/index.tsx +++ b/web/src/pages/agent/run-sheet/index.tsx @@ -14,6 +14,7 @@ import { useGetBeginNodeDataQuery } from '../hooks/use-get-begin-query'; import { useSaveGraphBeforeOpeningDebugDrawer } from '../hooks/use-save-graph'; import { BeginQuery } from '../interface'; import useGraphStore from '../store'; +import { buildBeginQueryWithObject } from '../utils'; const RunSheet = ({ hideModal, @@ -34,16 +35,7 @@ const RunSheet = ({ const beginNode = getNode(BeginId); const inputs: Record = beginNode?.data.form.inputs; - const nextInputs = Object.keys(inputs).reduce>( - (pre, key) => { - const item = nextValues.find((x) => x.key === key); - if (item) { - pre[key] = { ...item }; - } - return pre; - }, - {}, - ); + const nextInputs = buildBeginQueryWithObject(inputs, nextValues); const currentNodes = updateNodeForm(BeginId, nextInputs, ['inputs']); handleRun(currentNodes); diff --git a/web/src/pages/agent/utils.ts b/web/src/pages/agent/utils.ts index c4c0d0021f4..06fcb52307e 100644 --- a/web/src/pages/agent/utils.ts +++ b/web/src/pages/agent/utils.ts @@ -19,7 +19,7 @@ import { NodeMap, Operator, } from './constant'; -import { IPosition } from './interface'; +import { BeginQuery, IPosition } from './interface'; const buildEdges = ( operatorIds: string[], @@ -537,3 +537,21 @@ export function mapEdgeMouseEvent( return nextEdges; } + +export function buildBeginQueryWithObject( + inputs: Record, + values: BeginQuery[], +) { + const nextInputs = Object.keys(inputs).reduce>( + (pre, key) => { + const item = values.find((x) => x.key === key); + if (item) { + pre[key] = { ...item }; + } + return pre; + }, + {}, + ); + + return nextInputs; +} From 8801de2772c6530c10cd02028838dd47111fe803 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Tue, 1 Jul 2025 09:29:19 +0800 Subject: [PATCH 0066/1187] Refa: change mcp_client module to rag/utils/conn (#8578) ### What problem does this PR solve? Change mcp_client module to rag/utils/conn. ### Type of change - [x] Refactoring --- Dockerfile | 1 - Dockerfile.scratch.oc9 | 1 - api/apps/mcp_server_app.py | 2 +- api/ragflow_server.py | 2 +- mcp_client/__init__.py | 2 -- .../utils/mcp_tool_call_conn.py | 33 ++++++++++++++----- 6 files changed, 27 insertions(+), 14 deletions(-) delete mode 100644 mcp_client/__init__.py rename mcp_client/mcp_tool_call.py => rag/utils/mcp_tool_call_conn.py (88%) diff --git a/Dockerfile b/Dockerfile index 0f0727b63b3..67fd2645682 100644 --- a/Dockerfile +++ b/Dockerfile @@ -200,7 +200,6 @@ COPY graphrag graphrag COPY agentic_reasoning agentic_reasoning COPY pyproject.toml uv.lock ./ COPY mcp mcp -COPY mcp_client mcp_client COPY plugin plugin COPY docker/service_conf.yaml.template ./conf/service_conf.yaml.template diff --git a/Dockerfile.scratch.oc9 b/Dockerfile.scratch.oc9 index 2403eae167e..64424735e81 100644 --- a/Dockerfile.scratch.oc9 +++ b/Dockerfile.scratch.oc9 @@ -33,7 +33,6 @@ ADD ./rag ./rag ADD ./requirements.txt ./requirements.txt ADD ./agent ./agent ADD ./graphrag ./graphrag -ADD ./mcp_client ./mcp_client ADD ./plugin ./plugin RUN dnf install -y openmpi openmpi-devel python3-openmpi diff --git a/api/apps/mcp_server_app.py b/api/apps/mcp_server_app.py index 34bd9dfb270..fb81d51e0d9 100644 --- a/api/apps/mcp_server_app.py +++ b/api/apps/mcp_server_app.py @@ -9,7 +9,7 @@ from api.utils import get_uuid from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request from api.utils.web_utils import get_float, safe_json_parse -from mcp_client.mcp_tool_call import MCPToolCallSession, close_multiple_mcp_toolcall_sessions +from rag.utils.mcp_tool_call_conn import MCPToolCallSession, close_multiple_mcp_toolcall_sessions @manager.route("/list", methods=["POST"]) # noqa: F821 diff --git a/api/ragflow_server.py b/api/ragflow_server.py index 288ae7fd3f0..6d6c72ea16a 100644 --- a/api/ragflow_server.py +++ b/api/ragflow_server.py @@ -19,7 +19,6 @@ # beartype_all(conf=BeartypeConf(violation_type=UserWarning)) # <-- emit warnings from all code from api.utils.log_utils import init_root_logger -from mcp_client.mcp_tool_call import shutdown_all_mcp_sessions from plugin import GlobalPluginManager init_root_logger("ragflow_server") @@ -44,6 +43,7 @@ from api.versions import get_ragflow_version from api.utils import show_configs from rag.settings import print_rag_settings +from rag.utils.mcp_tool_call_conn import shutdown_all_mcp_sessions from rag.utils.redis_conn import RedisDistributedLock stop_event = threading.Event() diff --git a/mcp_client/__init__.py b/mcp_client/__init__.py deleted file mode 100644 index a0712585345..00000000000 --- a/mcp_client/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# ruff: noqa: F401 -from .mcp_tool_call import MCPToolCallSession, mcp_tool_metadata_to_openai_tool, close_multiple_mcp_toolcall_sessions diff --git a/mcp_client/mcp_tool_call.py b/rag/utils/mcp_tool_call_conn.py similarity index 88% rename from mcp_client/mcp_tool_call.py rename to rag/utils/mcp_tool_call_conn.py index 22fe5d20bff..5aca0e75449 100644 --- a/mcp_client/mcp_tool_call.py +++ b/rag/utils/mcp_tool_call_conn.py @@ -1,8 +1,25 @@ +# +# 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 asyncio import logging import threading import weakref from concurrent.futures import ThreadPoolExecutor +from concurrent.futures import TimeoutError as FuturesTimeoutError from string import Template from typing import Any, Literal @@ -101,7 +118,7 @@ async def _call_mcp_server(self, task_type: MCPTaskType, timeout: float = 8, **k try: result: CallToolResult | Exception = await asyncio.wait_for(results.get(), timeout=timeout) except asyncio.TimeoutError: - raise TimeoutError(f"MCP task '{task_type}' timeout after {timeout}s") + raise asyncio.TimeoutError(f"MCP task '{task_type}' timeout after {timeout}s") if isinstance(result, Exception): raise result @@ -128,8 +145,8 @@ def get_tools(self, timeout: float = 10) -> list[Tool]: future = asyncio.run_coroutine_threadsafe(self._get_tools_from_mcp_server(), self._event_loop) try: return future.result(timeout=timeout) - except TimeoutError: - logging.error(f"Timeout when fetching tools from MCP server: {self._mcp_server.id}") + except FuturesTimeoutError: + logging.error(f"Timeout when fetching tools from MCP server: {self._mcp_server.id} (timeout={timeout})") return [] except Exception: logging.exception(f"Error fetching tools from MCP server: {self._mcp_server.id}") @@ -140,9 +157,9 @@ def tool_call(self, name: str, arguments: dict[str, Any], timeout: float = 10) - future = asyncio.run_coroutine_threadsafe(self._call_mcp_tool(name, arguments), self._event_loop) try: return future.result(timeout=timeout) - except TimeoutError as te: - logging.error(f"Timeout calling tool '{name}' on MCP server: {self._mcp_server.id}") - return f"Timeout calling tool '{name}': {te}." + except FuturesTimeoutError: + logging.error(f"Timeout calling tool '{name}' on MCP server: {self._mcp_server.id} (timeout={timeout})") + return f"Timeout calling tool '{name}' (timeout={timeout})." except Exception as e: logging.exception(f"Error calling tool '{name}' on MCP server: {self._mcp_server.id}") return f"Error calling tool '{name}': {e}." @@ -164,8 +181,8 @@ def close_sync(self, timeout: float = 5) -> None: future = asyncio.run_coroutine_threadsafe(self.close(), self._event_loop) try: future.result(timeout=timeout) - except TimeoutError: - logging.error(f"Timeout while closing session for server {self._mcp_server.id}") + except FuturesTimeoutError: + logging.error(f"Timeout while closing session for server {self._mcp_server.id} (timeout={timeout})") except Exception: logging.exception(f"Unexpected error during close_sync for {self._mcp_server.id}") From 4a1680a799ab26e60434e7fa94475542d5b2a1ca Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Tue, 1 Jul 2025 09:47:23 +0800 Subject: [PATCH 0067/1187] doc: change to chunk_token num (#8590) ### What problem does this PR solve? https://github.com/infiniflow/ragflow/issues/8556 ### Type of change - [x] Documentation Update --- docs/references/http_api_reference.md | 4 ++-- docs/references/python_api_reference.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index 35ca73bb04d..2ff65e2eed4 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -881,7 +881,7 @@ curl --request PUT \ { "name": "manual.txt", "chunk_method": "manual", - "parser_config": {"chunk_token_count": 128} + "parser_config": {"chunk_token_num": 128} }' ``` @@ -910,7 +910,7 @@ curl --request PUT \ - `"parser_config"`: (*Body parameter*), `object` The configuration settings for the dataset parser. The attributes in this JSON object vary with the selected `"chunk_method"`: - If `"chunk_method"` is `"naive"`, the `"parser_config"` object contains the following attributes: - - `"chunk_token_count"`: Defaults to `256`. + - `"chunk_token_num"`: Defaults to `256`. - `"layout_recognize"`: Defaults to `true`. - `"html4excel"`: Indicates whether to convert Excel documents into HTML format. Defaults to `false`. - `"delimiter"`: Defaults to `"\n"`. diff --git a/docs/references/python_api_reference.md b/docs/references/python_api_reference.md index af66c0e4983..9c8437ac4ae 100644 --- a/docs/references/python_api_reference.md +++ b/docs/references/python_api_reference.md @@ -461,7 +461,7 @@ dataset = rag_object.list_datasets(id='id') dataset = dataset[0] doc = dataset.list_documents(id="wdfxb5t547d") doc = doc[0] -doc.update([{"parser_config": {"chunk_token_count": 256}}, {"chunk_method": "manual"}]) +doc.update([{"parser_config": {"chunk_token_num": 256}}, {"chunk_method": "manual"}]) ``` --- From 7f19f604a94902ec19832f4ef475b1c59b886865 Mon Sep 17 00:00:00 2001 From: Tuan Le <30828528+tuankg1028@users.noreply.github.com> Date: Tue, 1 Jul 2025 08:48:36 +0700 Subject: [PATCH 0068/1187] Pass Form Instance to GoogleModal Form Component (#8586) ### What problem does this PR solve? This PR enables the `Form` component within the `GoogleModal` to directly access and manipulate the form state by passing the form instance from the parent component. This enhances form control and data manipulation capabilities within the modal, improving the component's functionality and integration with the parent form. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/user-setting/setting-model/google-modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/pages/user-setting/setting-model/google-modal/index.tsx b/web/src/pages/user-setting/setting-model/google-modal/index.tsx index 5bc7af51017..a404118a5aa 100644 --- a/web/src/pages/user-setting/setting-model/google-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/google-modal/index.tsx @@ -47,7 +47,7 @@ const GoogleModal = ({ onCancel={hideModal} okButtonProps={{ loading }} > -
    + label={t('modelType')} name="model_type" From d4da6dce6eea048902fd33683e771af29f0a69e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E6=B5=B7=E8=92=BC=E7=81=86?= Date: Tue, 1 Jul 2025 09:51:53 +0800 Subject: [PATCH 0069/1187] Feat: Add file management HTTP_API (#8395) ### What problem does this PR solve? Add file management HTTP_API for operating files ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/sdk/files.py | 668 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 668 insertions(+) create mode 100644 api/apps/sdk/files.py diff --git a/api/apps/sdk/files.py b/api/apps/sdk/files.py new file mode 100644 index 00000000000..d8fd84aed50 --- /dev/null +++ b/api/apps/sdk/files.py @@ -0,0 +1,668 @@ +import pathlib +import re + +import flask +from flask import request + +from api.db.services.document_service import DocumentService +from api.db.services.file2document_service import File2DocumentService +from api.utils.api_utils import server_error_response, token_required +from api.utils import get_uuid +from api.db import FileType +from api.db.services import duplicate_name +from api.db.services.file_service import FileService +from api.utils.api_utils import get_json_result +from api.utils.file_utils import filename_type +from rag.utils.storage_factory import STORAGE_IMPL + +@manager.route('/file/upload', methods=['POST']) # noqa: F821 +@token_required +def upload(tenant_id): + """ + Upload a file to the system. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: formData + name: file + type: file + required: true + description: The file to upload + - in: formData + name: parent_id + type: string + description: Parent folder ID where the file will be uploaded. Optional. + responses: + 200: + description: Successfully uploaded the file. + schema: + type: object + properties: + data: + type: array + items: + type: object + properties: + id: + type: string + description: File ID + name: + type: string + description: File name + size: + type: integer + description: File size in bytes + type: + type: string + description: File type (e.g., document, folder) + """ + pf_id = request.form.get("parent_id") + + if not pf_id: + root_folder = FileService.get_root_folder(tenant_id) + pf_id = root_folder["id"] + + if 'file' not in request.files: + return get_json_result(data=False, message='No file part!', code=400) + file_objs = request.files.getlist('file') + + for file_obj in file_objs: + if file_obj.filename == '': + return get_json_result(data=False, message='No selected file!', code=400) + + file_res = [] + + try: + e, pf_folder = FileService.get_by_id(pf_id) + if not e: + return get_json_result(data=False, message="Can't find this folder!", code=404) + + for file_obj in file_objs: + # 文件路径处理 + full_path = '/' + file_obj.filename + file_obj_names = full_path.split('/') + file_len = len(file_obj_names) + + # 获取文件夹路径ID + file_id_list = FileService.get_id_list_by_id(pf_id, file_obj_names, 1, [pf_id]) + len_id_list = len(file_id_list) + + # 创建文件夹结构 + if file_len != len_id_list: + e, file = FileService.get_by_id(file_id_list[len_id_list - 1]) + if not e: + return get_json_result(data=False, message="Folder not found!", code=404) + last_folder = FileService.create_folder(file, file_id_list[len_id_list - 1], file_obj_names, len_id_list) + else: + e, file = FileService.get_by_id(file_id_list[len_id_list - 2]) + if not e: + return get_json_result(data=False, message="Folder not found!", code=404) + last_folder = FileService.create_folder(file, file_id_list[len_id_list - 2], file_obj_names, len_id_list) + + filetype = filename_type(file_obj_names[file_len - 1]) + location = file_obj_names[file_len - 1] + while STORAGE_IMPL.obj_exist(last_folder.id, location): + location += "_" + blob = file_obj.read() + filename = duplicate_name(FileService.query, name=file_obj_names[file_len - 1], parent_id=last_folder.id) + + file = { + "id": get_uuid(), + "parent_id": last_folder.id, + "tenant_id": tenant_id, + "created_by": tenant_id, + "type": filetype, + "name": filename, + "location": location, + "size": len(blob), + } + file = FileService.insert(file) + STORAGE_IMPL.put(last_folder.id, location, blob) + file_res.append(file.to_json()) + return get_json_result(data=file_res) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/create', methods=['POST']) # noqa: F821 +@token_required +def create(tenant_id): + """ + Create a new file or folder. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: body + name: body + description: File creation parameters + required: true + schema: + type: object + properties: + name: + type: string + description: Name of the file/folder + parent_id: + type: string + description: Parent folder ID. Optional. + type: + type: string + enum: ["FOLDER", "VIRTUAL"] + description: Type of the file + responses: + 200: + description: File created successfully. + schema: + type: object + properties: + data: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + """ + req = request.json + pf_id = request.json.get("parent_id") + input_file_type = request.json.get("type") + if not pf_id: + root_folder = FileService.get_root_folder(tenant_id) + pf_id = root_folder["id"] + + try: + if not FileService.is_parent_folder_exist(pf_id): + return get_json_result(data=False, message="Parent Folder Doesn't Exist!", code=400) + if FileService.query(name=req["name"], parent_id=pf_id): + return get_json_result(data=False, message="Duplicated folder name in the same folder.", code=409) + + if input_file_type == FileType.FOLDER.value: + file_type = FileType.FOLDER.value + else: + file_type = FileType.VIRTUAL.value + + file = FileService.insert({ + "id": get_uuid(), + "parent_id": pf_id, + "tenant_id": tenant_id, + "created_by": tenant_id, + "name": req["name"], + "location": "", + "size": 0, + "type": file_type + }) + + return get_json_result(data=file.to_json()) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/list', methods=['GET']) # noqa: F821 +@token_required +def list_files(tenant_id): + """ + List files under a specific folder. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: query + name: parent_id + type: string + description: Folder ID to list files from + - in: query + name: keywords + type: string + description: Search keyword filter + - in: query + name: page + type: integer + default: 1 + description: Page number + - in: query + name: page_size + type: integer + default: 15 + description: Number of results per page + - in: query + name: orderby + type: string + default: "create_time" + description: Sort by field + - in: query + name: desc + type: boolean + default: true + description: Descending order + responses: + 200: + description: Successfully retrieved file list. + schema: + type: object + properties: + total: + type: integer + files: + type: array + items: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + size: + type: integer + create_time: + type: string + format: date-time + """ + pf_id = request.args.get("parent_id") + keywords = request.args.get("keywords", "") + page_number = int(request.args.get("page", 1)) + items_per_page = int(request.args.get("page_size", 15)) + orderby = request.args.get("orderby", "create_time") + desc = request.args.get("desc", True) + + if not pf_id: + root_folder = FileService.get_root_folder(tenant_id) + pf_id = root_folder["id"] + FileService.init_knowledgebase_docs(pf_id, tenant_id) + + try: + e, file = FileService.get_by_id(pf_id) + if not e: + return get_json_result(message="Folder not found!", code=404) + + files, total = FileService.get_by_pf_id(tenant_id, pf_id, page_number, items_per_page, orderby, desc, keywords) + + parent_folder = FileService.get_parent_folder(pf_id) + if not parent_folder: + return get_json_result(message="File not found!", code=404) + + return get_json_result(data={"total": total, "files": files, "parent_folder": parent_folder.to_json()}) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/root_folder', methods=['GET']) # noqa: F821 +@token_required +def get_root_folder(tenant_id): + """ + Get user's root folder. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + responses: + 200: + description: Root folder information + schema: + type: object + properties: + data: + type: object + properties: + root_folder: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + """ + try: + root_folder = FileService.get_root_folder(tenant_id) + return get_json_result(data={"root_folder": root_folder}) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/parent_folder', methods=['GET']) # noqa: F821 +@token_required +def get_parent_folder(): + """ + Get parent folder info of a file. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: query + name: file_id + type: string + required: true + description: Target file ID + responses: + 200: + description: Parent folder information + schema: + type: object + properties: + data: + type: object + properties: + parent_folder: + type: object + properties: + id: + type: string + name: + type: string + """ + file_id = request.args.get("file_id") + try: + e, file = FileService.get_by_id(file_id) + if not e: + return get_json_result(message="Folder not found!", code=404) + + parent_folder = FileService.get_parent_folder(file_id) + return get_json_result(data={"parent_folder": parent_folder.to_json()}) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/all_parent_folder', methods=['GET']) # noqa: F821 +@token_required +def get_all_parent_folders(tenant_id): + """ + Get all parent folders of a file. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: query + name: file_id + type: string + required: true + description: Target file ID + responses: + 200: + description: All parent folders of the file + schema: + type: object + properties: + data: + type: object + properties: + parent_folders: + type: array + items: + type: object + properties: + id: + type: string + name: + type: string + """ + file_id = request.args.get("file_id") + try: + e, file = FileService.get_by_id(file_id) + if not e: + return get_json_result(message="Folder not found!", code=404) + + parent_folders = FileService.get_all_parent_folders(file_id) + parent_folders_res = [folder.to_json() for folder in parent_folders] + return get_json_result(data={"parent_folders": parent_folders_res}) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/rm', methods=['POST']) # noqa: F821 +@token_required +def rm(tenant_id): + """ + Delete one or multiple files/folders. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: body + name: body + description: Files to delete + required: true + schema: + type: object + properties: + file_ids: + type: array + items: + type: string + description: List of file IDs to delete + responses: + 200: + description: Successfully deleted files + schema: + type: object + properties: + data: + type: boolean + example: true + """ + req = request.json + file_ids = req["file_ids"] + try: + for file_id in file_ids: + e, file = FileService.get_by_id(file_id) + if not e: + return get_json_result(message="File or Folder not found!", code=404) + if not file.tenant_id: + return get_json_result(message="Tenant not found!", code=404) + + if file.type == FileType.FOLDER.value: + file_id_list = FileService.get_all_innermost_file_ids(file_id, []) + for inner_file_id in file_id_list: + e, file = FileService.get_by_id(inner_file_id) + if not e: + return get_json_result(message="File not found!", code=404) + STORAGE_IMPL.rm(file.parent_id, file.location) + FileService.delete_folder_by_pf_id(tenant_id, file_id) + else: + STORAGE_IMPL.rm(file.parent_id, file.location) + if not FileService.delete(file): + return get_json_result(message="Database error (File removal)!", code=500) + + informs = File2DocumentService.get_by_file_id(file_id) + for inform in informs: + doc_id = inform.document_id + e, doc = DocumentService.get_by_id(doc_id) + if not e: + return get_json_result(message="Document not found!", code=404) + tenant_id = DocumentService.get_tenant_id(doc_id) + if not tenant_id: + return get_json_result(message="Tenant not found!", code=404) + if not DocumentService.remove_document(doc, tenant_id): + return get_json_result(message="Database error (Document removal)!", code=500) + File2DocumentService.delete_by_file_id(file_id) + + return get_json_result(data=True) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/rename', methods=['POST']) # noqa: F821 +@token_required +def rename(tenant_id): + """ + Rename a file. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: body + name: body + description: Rename file + required: true + schema: + type: object + properties: + file_id: + type: string + description: Target file ID + name: + type: string + description: New name for the file + responses: + 200: + description: File renamed successfully + schema: + type: object + properties: + data: + type: boolean + example: true + """ + req = request.json + try: + e, file = FileService.get_by_id(req["file_id"]) + if not e: + return get_json_result(message="File not found!", code=404) + + if file.type != FileType.FOLDER.value and pathlib.Path(req["name"].lower()).suffix != pathlib.Path(file.name.lower()).suffix: + return get_json_result(data=False, message="The extension of file can't be changed", code=400) + + for existing_file in FileService.query(name=req["name"], pf_id=file.parent_id): + if existing_file.name == req["name"]: + return get_json_result(data=False, message="Duplicated file name in the same folder.", code=409) + + if not FileService.update_by_id(req["file_id"], {"name": req["name"]}): + return get_json_result(message="Database error (File rename)!", code=500) + + informs = File2DocumentService.get_by_file_id(req["file_id"]) + if informs: + if not DocumentService.update_by_id(informs[0].document_id, {"name": req["name"]}): + return get_json_result(message="Database error (Document rename)!", code=500) + + return get_json_result(data=True) + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/get/', methods=['GET']) # noqa: F821 +@token_required +def get(tenant_id,file_id): + """ + Download a file. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + produces: + - application/octet-stream + parameters: + - in: path + name: file_id + type: string + required: true + description: File ID to download + responses: + 200: + description: File stream + schema: + type: file + 404: + description: File not found + """ + try: + e, file = FileService.get_by_id(file_id) + if not e: + return get_json_result(message="Document not found!", code=404) + + blob = STORAGE_IMPL.get(file.parent_id, file.location) + if not blob: + b, n = File2DocumentService.get_storage_address(file_id=file_id) + blob = STORAGE_IMPL.get(b, n) + + response = flask.make_response(blob) + ext = re.search(r"\.([^.]+)$", file.name) + if ext: + if file.type == FileType.VISUAL.value: + response.headers.set('Content-Type', 'image/%s' % ext.group(1)) + else: + response.headers.set('Content-Type', 'application/%s' % ext.group(1)) + return response + except Exception as e: + return server_error_response(e) + + +@manager.route('/file/mv', methods=['POST']) # noqa: F821 +@token_required +def move(tenant_id): + """ + Move one or multiple files to another folder. + --- + tags: + - File Management + security: + - ApiKeyAuth: [] + parameters: + - in: body + name: body + description: Move operation + required: true + schema: + type: object + properties: + src_file_ids: + type: array + items: + type: string + description: Source file IDs + dest_file_id: + type: string + description: Destination folder ID + responses: + 200: + description: Files moved successfully + schema: + type: object + properties: + data: + type: boolean + example: true + """ + req = request.json + try: + file_ids = req["src_file_ids"] + parent_id = req["dest_file_id"] + files = FileService.get_by_ids(file_ids) + files_dict = {f.id: f for f in files} + + for file_id in file_ids: + file = files_dict[file_id] + if not file: + return get_json_result(message="File or Folder not found!", code=404) + if not file.tenant_id: + return get_json_result(message="Tenant not found!", code=404) + + fe, _ = FileService.get_by_id(parent_id) + if not fe: + return get_json_result(message="Parent Folder not found!", code=404) + + FileService.move_file(file_ids, parent_id) + return get_json_result(data=True) + except Exception as e: + return server_error_response(e) From 32f8b3ad77b5f80ce3fb4eb06e6b241c5f3cef7d Mon Sep 17 00:00:00 2001 From: symvation <129673064+symvation@users.noreply.github.com> Date: Tue, 1 Jul 2025 10:49:43 +0800 Subject: [PATCH 0070/1187] Fix: the output log is incorrect (#8577) ### What problem does this PR solve? Fix: the output log is incorrect ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) Co-authored-by: liang --- rag/utils/es_conn.py | 6 +++--- rag/utils/opensearch_coon.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rag/utils/es_conn.py b/rag/utils/es_conn.py index 5e1f6ceb05b..c66c7edff93 100644 --- a/rag/utils/es_conn.py +++ b/rag/utils/es_conn.py @@ -254,7 +254,7 @@ def search( if str(e).find("Timeout") > 0: continue raise e - logger.error("ESConnection.search timeout for 3 times!") + logger.error(f"ESConnection.search timeout for {ATTEMPT_TIME} times!") raise Exception("ESConnection.search timeout.") def get(self, chunkId: str, indexName: str, knowledgebaseIds: list[str]) -> dict | None: @@ -274,7 +274,7 @@ def get(self, chunkId: str, indexName: str, knowledgebaseIds: list[str]) -> dict if str(e).find("Timeout") > 0: continue raise e - logger.error("ESConnection.get timeout for 3 times!") + logger.error(f"ESConnection.get timeout for {ATTEMPT_TIME} times!") raise Exception("ESConnection.get timeout.") def insert(self, documents: list[dict], indexName: str, knowledgebaseId: str = None) -> list[str]: @@ -562,5 +562,5 @@ def sql(self, sql: str, fetch_size: int, format: str): except Exception: logger.exception("ESConnection.sql got exception") return None - logger.error("ESConnection.sql timeout for 3 times!") + logger.error(f"ESConnection.sql timeout for {ATTEMPT_TIME} times!") return None diff --git a/rag/utils/opensearch_coon.py b/rag/utils/opensearch_coon.py index 4a8fd0889b2..4fd09b782e0 100644 --- a/rag/utils/opensearch_coon.py +++ b/rag/utils/opensearch_coon.py @@ -262,7 +262,7 @@ def search( if str(e).find("Timeout") > 0: continue raise e - logger.error("OSConnection.search timeout for 3 times!") + logger.error(f"OSConnection.search timeout for {ATTEMPT_TIME} times!") raise Exception("OSConnection.search timeout.") def get(self, chunkId: str, indexName: str, knowledgebaseIds: list[str]) -> dict | None: @@ -282,7 +282,7 @@ def get(self, chunkId: str, indexName: str, knowledgebaseIds: list[str]) -> dict if str(e).find("Timeout") > 0: continue raise e - logger.error("OSConnection.get timeout for 3 times!") + logger.error(f"OSConnection.get timeout for {ATTEMPT_TIME} times!") raise Exception("OSConnection.get timeout.") def insert(self, documents: list[dict], indexName: str, knowledgebaseId: str = None) -> list[str]: @@ -557,5 +557,5 @@ def sql(self, sql: str, fetch_size: int, format: str): except Exception: logger.exception("OSConnection.sql got exception") return None - logger.error("OSConnection.sql timeout for 3 times!") + logger.error(f"OSConnection.sql timeout for {ATTEMPT_TIME} times!") return None From 103027580e98935bdbf91b9149ba966698f3840f Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 1 Jul 2025 10:52:48 +0800 Subject: [PATCH 0071/1187] Feat: Add agent advanced settings form #3221 (#8592) ### What problem does this PR solve? Feat: Add agent advanced settings form #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../message-history-window-size-item.tsx | 17 +--- web/src/components/ui/input.tsx | 15 +++ web/src/components/ui/sheet.tsx | 2 +- web/src/pages/agent/constant.tsx | 14 +++ web/src/pages/agent/form-sheet/next.tsx | 7 +- web/src/pages/agent/form/agent-form/index.tsx | 99 ++++++++++++++++++- .../pages/agent/form/agent-form/use-values.ts | 11 ++- .../agent/form/agent-form/use-watch-change.ts | 8 +- .../agent/form/categorize-form/index.tsx | 4 +- .../form/categorize-form/use-watch-change.ts | 2 +- .../agent/form/components/query-variable.tsx | 2 +- .../pages/agent/hooks/use-get-begin-query.tsx | 5 +- 12 files changed, 157 insertions(+), 29 deletions(-) diff --git a/web/src/components/message-history-window-size-item.tsx b/web/src/components/message-history-window-size-item.tsx index 2f2043f3c0f..e717076e540 100644 --- a/web/src/components/message-history-window-size-item.tsx +++ b/web/src/components/message-history-window-size-item.tsx @@ -1,5 +1,4 @@ import { Form, InputNumber } from 'antd'; -import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { @@ -9,7 +8,7 @@ import { FormLabel, FormMessage, } from './ui/form'; -import { BlurInput, Input } from './ui/input'; +import { NumberInput } from './ui/input'; const MessageHistoryWindowSizeItem = ({ initialValue, @@ -32,20 +31,10 @@ const MessageHistoryWindowSizeItem = ({ export default MessageHistoryWindowSizeItem; -type MessageHistoryWindowSizeFormFieldProps = { - useBlurInput?: boolean; -}; - -export function MessageHistoryWindowSizeFormField({ - useBlurInput = false, -}: MessageHistoryWindowSizeFormFieldProps) { +export function MessageHistoryWindowSizeFormField() { const form = useFormContext(); const { t } = useTranslation(); - const NextInput = useMemo(() => { - return useBlurInput ? BlurInput : Input; - }, [useBlurInput]); - return ( - + diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx index f0ee3c5f331..e8dee5613f2 100644 --- a/web/src/components/ui/input.tsx +++ b/web/src/components/ui/input.tsx @@ -110,3 +110,18 @@ if (process.env.NODE_ENV !== 'production') { export const BlurInput = React.memo(InnerBlurInput); export { ExpandedInput, Input, SearchInput }; + +type NumberInputProps = { onChange?(value: number): void } & InputProps; + +export const NumberInput = ({ onChange, ...props }: NumberInputProps) => { + return ( + { + const value = ev.target.value; + onChange?.(value === '' ? 0 : Number(value)); // convert to number + }} + {...props} + > + ); +}; diff --git a/web/src/components/ui/sheet.tsx b/web/src/components/ui/sheet.tsx index f24cb86ab12..8be732d4428 100644 --- a/web/src/components/ui/sheet.tsx +++ b/web/src/components/ui/sheet.tsx @@ -81,7 +81,7 @@ const SheetContent = React.forwardRef< ), ); -SheetContent.displayName = SheetPrimitive.Content.displayName; +SheetContent.displayName = SheetPrimitive.Content.displayName || 'SheetContent'; const SheetHeader = ({ className, diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 7fe79978b88..5319eb312a9 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -681,6 +681,13 @@ export const initialAgentValues = { sys_prompt: ``, prompts: [{ role: PromptRole.User, content: `{${AgentGlobals.SysQuery}}` }], message_history_window_size: 12, + max_retries: 3, + delay_after_error: 1, + visual_files_var: '', + max_rounds: 5, + exception_method: null, + exception_comment: '', + exception_goto: '', tools: [], outputs: { structured_output: { @@ -941,6 +948,7 @@ export enum NodeHandleId { export enum VariableType { String = 'string', Array = 'array', + File = 'file', } export const DefaultAgentToolValuesMap = { @@ -952,3 +960,9 @@ export const DefaultAgentToolValuesMap = { api_key: '', }, }; + +export enum AgentExceptionMethod { + Comment = 'comment', + Goto = 'goto', + Null = 'null', +} diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index 8bfd9d2aff4..96ea78ff418 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -54,7 +54,10 @@ const FormSheet = ({ return ( - +
    @@ -86,7 +89,7 @@ const FormSheet = ({ {t(`${lowerFirst(operatorName)}Description`)}
    -
    +
    {visible && ( diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 59b6ec89286..b3d3b7edcd3 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -1,3 +1,4 @@ +import { Collapse } from '@/components/collapse'; import { FormContainer } from '@/components/form-container'; import { LargeModelFormField } from '@/components/large-model-form-field'; import { LlmSettingSchema } from '@/components/llm-setting-items/next'; @@ -9,22 +10,32 @@ import { FormItem, FormLabel, } from '@/components/ui/form'; +import { Input, NumberInput } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { buildOptions } from '@/utils/form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { initialAgentValues } from '../../constant'; +import { + AgentExceptionMethod, + VariableType, + initialAgentValues, +} from '../../constant'; import { INextOperatorForm } from '../../interface'; import useGraphStore from '../../store'; import { isBottomSubAgent } from '../../utils'; import { DescriptionField } from '../components/description-field'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; +import { QueryVariable } from '../components/query-variable'; import { AgentTools, Agents } from './agent-tools'; import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; +const exceptionMethodOptions = buildOptions(AgentExceptionMethod); + const FormSchema = z.object({ sys_prompt: z.string(), description: z.string().optional(), @@ -46,6 +57,13 @@ const FormSchema = z.object({ ) .optional(), ...LlmSettingSchema, + max_retries: z.coerce.number(), + delay_after_error: z.coerce.number().optional(), + visual_files_var: z.string().optional(), + max_rounds: z.coerce.number().optional(), + exception_method: z.string().nullable(), + exception_comment: z.string().optional(), + exception_goto: z.string().optional(), }); const AgentForm = ({ node }: INextOperatorForm) => { @@ -64,7 +82,7 @@ const AgentForm = ({ node }: INextOperatorForm) => { ]; }, []); - const form = useForm({ + const form = useForm>({ defaultValues: defaultValues, resolver: zodResolver(FormSchema), }); @@ -122,10 +140,87 @@ const AgentForm = ({ node }: INextOperatorForm) => { /> )} + + Advanced Settings
    }> + + + ( + + Max retries + + + + + )} + /> + ( + + Delay after error + + + + + )} + /> + ( + + Max rounds + + + + + )} + /> + ( + + Exception method + + + + + )} + /> + ( + + Exception comment + + + + + )} + /> + + + diff --git a/web/src/pages/agent/form/agent-form/use-values.ts b/web/src/pages/agent/form/agent-form/use-values.ts index 3e6b057a6a6..21addb892f9 100644 --- a/web/src/pages/agent/form/agent-form/use-values.ts +++ b/web/src/pages/agent/form/agent-form/use-values.ts @@ -2,7 +2,7 @@ import { useFetchModelId } from '@/hooks/logic-hooks'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { get, isEmpty } from 'lodash'; import { useMemo } from 'react'; -import { initialAgentValues } from '../../constant'; +import { AgentExceptionMethod, initialAgentValues } from '../../constant'; export function useValues(node?: RAGFlowNodeType) { const llmId = useFetchModelId(); @@ -23,7 +23,14 @@ export function useValues(node?: RAGFlowNodeType) { return defaultValues; } - return { ...formData, prompts: get(formData, 'prompts.0.content', '') }; + return { + ...formData, + prompts: get(formData, 'prompts.0.content', ''), + exception_method: + formData.exception_method === null + ? AgentExceptionMethod.Null + : formData.exception_method, + }; }, [defaultValues, node?.data?.form]); return values; diff --git a/web/src/pages/agent/form/agent-form/use-watch-change.ts b/web/src/pages/agent/form/agent-form/use-watch-change.ts index 8640a45184a..97c2cb43700 100644 --- a/web/src/pages/agent/form/agent-form/use-watch-change.ts +++ b/web/src/pages/agent/form/agent-form/use-watch-change.ts @@ -1,9 +1,9 @@ import { useEffect } from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; -import { PromptRole } from '../../constant'; +import { AgentExceptionMethod, PromptRole } from '../../constant'; import useGraphStore from '../../store'; -export function useWatchFormChange(id?: string, form?: UseFormReturn) { +export function useWatchFormChange(id?: string, form?: UseFormReturn) { let values = useWatch({ control: form?.control }); const updateNodeForm = useGraphStore((state) => state.updateNodeForm); @@ -14,6 +14,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { let nextValues: any = { ...values, prompts: [{ role: PromptRole.User, content: values.prompts }], + exception_method: + values.exception_method === AgentExceptionMethod.Null + ? null + : values.exception_method, }; updateNodeForm(id, nextValues); diff --git a/web/src/pages/agent/form/categorize-form/index.tsx b/web/src/pages/agent/form/categorize-form/index.tsx index a4faef25453..96d159a74eb 100644 --- a/web/src/pages/agent/form/categorize-form/index.tsx +++ b/web/src/pages/agent/form/categorize-form/index.tsx @@ -59,9 +59,7 @@ const CategorizeForm = ({ node }: INextOperatorForm) => { - + diff --git a/web/src/pages/agent/form/categorize-form/use-watch-change.ts b/web/src/pages/agent/form/categorize-form/use-watch-change.ts index 6f01dc1a9d9..91ae84e80dc 100644 --- a/web/src/pages/agent/form/categorize-form/use-watch-change.ts +++ b/web/src/pages/agent/form/categorize-form/use-watch-change.ts @@ -4,7 +4,7 @@ import { UseFormReturn, useWatch } from 'react-hook-form'; import useGraphStore from '../../store'; import { buildCategorizeObjectFromList } from '../../utils'; -export function useWatchFormChange(id?: string, form?: UseFormReturn) { +export function useWatchFormChange(id?: string, form?: UseFormReturn) { let values = useWatch({ control: form?.control }); const updateNodeForm = useGraphStore((state) => state.updateNodeForm); diff --git a/web/src/pages/agent/form/components/query-variable.tsx b/web/src/pages/agent/form/components/query-variable.tsx index fc13d3f22bb..e235a9504f3 100644 --- a/web/src/pages/agent/form/components/query-variable.tsx +++ b/web/src/pages/agent/form/components/query-variable.tsx @@ -34,7 +34,7 @@ export function QueryVariable({ ? nextOptions.map((x) => { return { ...x, - options: x.options.filter((y) => toLower(y.type).startsWith(type)), + options: x.options.filter((y) => toLower(y.type).includes(type)), }; }) : nextOptions; diff --git a/web/src/pages/agent/hooks/use-get-begin-query.tsx b/web/src/pages/agent/hooks/use-get-begin-query.tsx index 6468e2675ab..8a3f8976366 100644 --- a/web/src/pages/agent/hooks/use-get-begin-query.tsx +++ b/web/src/pages/agent/hooks/use-get-begin-query.tsx @@ -1,3 +1,4 @@ +import { AgentGlobals } from '@/constants/agent'; import { useFetchAgent } from '@/hooks/use-agent-request'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { Edge } from '@xyflow/react'; @@ -161,7 +162,9 @@ export function useBuildQueryVariableOptions() { const globalOptions = Object.entries(globals).map(([key, value]) => ({ label: key, value: key, - type: Array.isArray(value) ? VariableType.Array : typeof value, + type: Array.isArray(value) + ? `${VariableType.Array}${key === AgentGlobals.SysFiles ? '' : ''}` + : typeof value, })); return [ { ...options[0], options: [...options[0]?.options, ...globalOptions] }, From e3edcc306491ee01f12823e58786d0819ec5974f Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Tue, 1 Jul 2025 14:05:18 +0800 Subject: [PATCH 0072/1187] Trivals. (#8597) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/utils/log_utils.py | 6 +++++- graphrag/general/index.py | 2 +- rag/svr/task_executor.py | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/api/utils/log_utils.py b/api/utils/log_utils.py index 3ebedd14821..0a4840e79ac 100644 --- a/api/utils/log_utils.py +++ b/api/utils/log_utils.py @@ -83,5 +83,9 @@ def init_root_logger(logfile_basename: str, log_format: str = "%(asctime)-15s %( def log_exception(e, *args): logging.exception(e) for a in args: - logging.error(str(a)) + if hasattr(a, "text"): + logging.error(a.text) + raise Exception(a.text) + else: + logging.error(str(a)) raise e \ No newline at end of file diff --git a/graphrag/general/index.py b/graphrag/general/index.py index 8c98636791f..6e107bc87a2 100644 --- a/graphrag/general/index.py +++ b/graphrag/general/index.py @@ -57,7 +57,7 @@ async def run_graphrag( subgraph = await generate_subgraph( LightKGExt - if "method" not in row["kb_parser_config"]["graphrag"] or row["kb_parser_config"]["graphrag"]["method"] != "general" + if "method" not in row["kb_parser_config"].get("graphrag", {}) or row["kb_parser_config"]["graphrag"]["method"] != "general" else GeneralKGExt, tenant_id, kb_id, diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index ae5f548f9ff..a5dfafecab4 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -300,11 +300,12 @@ async def upload_to_minio(document, chunk): d["image"].close() # Close original image d["image"] = converted_image d["image"].save(output_buffer, format='JPEG') - d["image"].close() # Close PIL image after saving async with minio_limiter: await trio.to_thread.run_sync(lambda: STORAGE_IMPL.put(task["kb_id"], d["id"], output_buffer.getvalue())) d["img_id"] = "{}-{}".format(task["kb_id"], d["id"]) + if not isinstance(d["image"], bytes): + d["image"].close() del d["image"] # Remove image reference docs.append(d) finally: From 1c77b4ed9b4763641e24b11317b57cc2b711c7b3 Mon Sep 17 00:00:00 2001 From: Tuan Le <30828528+tuankg1028@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:06:07 +0700 Subject: [PATCH 0073/1187] fix: Correctly format message parts in GoogleChat (#8596) ### What problem does this PR solve? This PR addresses an incompatibility issue with the Google Chat API by correcting the message content format in the `GoogleChat` class. Previously, the content was directly assigned to the "parts" field, which did not align with the API's expected format. This change ensures that messages are properly formatted with a "text" key within a dictionary, as required by the API. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/chat_model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 5170d3d28f3..c2120cfca4d 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -1596,7 +1596,9 @@ def _chat(self, history, gen_conf): if "role" in item and item["role"] == "assistant": item["role"] = "model" if "content" in item: - item["parts"] = item.pop("content") + item["parts"] = [{ + "text": item.pop("content"), + }] response = self.client.generate_content(hist, generation_config=gen_conf) ans = response.text From 6b04b07eb4fd2351e8345c37de302094682df664 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 1 Jul 2025 15:52:14 +0800 Subject: [PATCH 0074/1187] Fixed the issue where variables were not displayed in the switch operator #3221 (#8601) ### What problem does this PR solve? Feat: Fixed the issue where variables were not displayed in the switch operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../originui/select-with-search.tsx | 172 +++++++++--------- .../pages/agent/canvas/node/switch-node.tsx | 11 +- web/src/pages/agent/constant.tsx | 2 +- .../pages/agent/form/switch-form/index.tsx | 46 ++--- .../form/switch-form/use-watch-change.ts | 5 +- .../tool-form/{constant.ts => constant.tsx} | 3 +- .../pages/agent/hooks/use-get-begin-query.tsx | 37 +++- 7 files changed, 154 insertions(+), 122 deletions(-) rename web/src/pages/agent/form/tool-form/{constant.ts => constant.tsx} (95%) diff --git a/web/src/components/originui/select-with-search.tsx b/web/src/components/originui/select-with-search.tsx index 469802d3477..7006fc0b50f 100644 --- a/web/src/components/originui/select-with-search.tsx +++ b/web/src/components/originui/select-with-search.tsx @@ -24,6 +24,7 @@ import { PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; import { RAGFlowSelectOptionType } from '../ui/select'; const countries = [ @@ -77,96 +78,105 @@ export type SelectWithSearchFlagProps = { options?: SelectWithSearchFlagOptionType[]; value?: string; onChange?(value: string): void; + triggerClassName?: string; }; export const SelectWithSearch = forwardRef< React.ElementRef, SelectWithSearchFlagProps ->(({ value: val = '', onChange, options = countries }, ref) => { - const id = useId(); - const [open, setOpen] = useState(false); - const [value, setValue] = useState(''); +>( + ( + { value: val = '', onChange, options = countries, triggerClassName }, + ref, + ) => { + const id = useId(); + const [open, setOpen] = useState(false); + const [value, setValue] = useState(''); - const handleSelect = useCallback( - (val: string) => { - setValue(val); - setOpen(false); - onChange?.(val); - }, - [onChange], - ); + const handleSelect = useCallback( + (val: string) => { + setValue(val); + setOpen(false); + onChange?.(val); + }, + [onChange], + ); - useEffect(() => { - setValue(val); - }, [val]); + useEffect(() => { + setValue(val); + }, [val]); - return ( - - - - - - - - - No data found. - {options.map((group) => ( - - - {group.options.map((option) => ( - - - {option.label} - + ) : ( + Select value + )} + - ); -}); + {value === option.value && ( + + )} + + ))} + + + ))} + + + + + ); + }, +); SelectWithSearch.displayName = 'SelectWithSearch'; diff --git a/web/src/pages/agent/canvas/node/switch-node.tsx b/web/src/pages/agent/canvas/node/switch-node.tsx index c386731a0e0..1bd2f45b6f5 100644 --- a/web/src/pages/agent/canvas/node/switch-node.tsx +++ b/web/src/pages/agent/canvas/node/switch-node.tsx @@ -4,7 +4,7 @@ import { ISwitchCondition, ISwitchNode } from '@/interfaces/database/flow'; import { NodeProps, Position } from '@xyflow/react'; import { memo, useCallback } from 'react'; import { NodeHandleId, SwitchOperatorOptions } from '../../constant'; -import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query'; +import { useGetVariableLabelByValue } from '../../hooks/use-get-begin-query'; import { CommonHandle } from './handle'; import { RightHandleStyle } from './handle-icon'; import NodeHeader from './node-header'; @@ -25,12 +25,9 @@ const getConditionKey = (idx: number, length: number) => { const ConditionBlock = ({ condition, nodeId, -}: { - condition: ISwitchCondition; - nodeId: string; -}) => { +}: { condition: ISwitchCondition } & { nodeId: string }) => { const items = condition?.items ?? []; - const getLabel = useGetComponentLabelByValue(nodeId); + const getLabel = useGetVariableLabelByValue(nodeId); const renderOperatorIcon = useCallback((operator?: string) => { const name = SwitchOperatorOptions.find((x) => x.value === operator)?.icon; @@ -83,8 +80,8 @@ function InnerSwitchNode({ id, data, selected }: NodeProps) { {position.condition && ( )}
    diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 5319eb312a9..f069b17cf31 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -81,7 +81,7 @@ export enum Operator { Email = 'Email', Iteration = 'Iteration', IterationStart = 'IterationItem', - Code = 'Code', + Code = 'CodeExec', WaitingDialogue = 'WaitingDialogue', Agent = 'Agent', Tool = 'Tool', diff --git a/web/src/pages/agent/form/switch-form/index.tsx b/web/src/pages/agent/form/switch-form/index.tsx index e1f98f3528f..4882d01e145 100644 --- a/web/src/pages/agent/form/switch-form/index.tsx +++ b/web/src/pages/agent/form/switch-form/index.tsx @@ -1,5 +1,6 @@ import { FormContainer } from '@/components/form-container'; import { IconFont } from '@/components/icon-font'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; import { BlockButton, Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { @@ -12,21 +13,20 @@ import { import { RAGFlowSelect } from '@/components/ui/select'; import { Separator } from '@/components/ui/separator'; import { Textarea } from '@/components/ui/textarea'; -import { ISwitchForm } from '@/interfaces/database/agent'; import { cn } from '@/lib/utils'; import { zodResolver } from '@hookform/resolvers/zod'; +import { toLower } from 'lodash'; import { X } from 'lucide-react'; import { useCallback, useMemo } from 'react'; import { useFieldArray, useForm, useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { - Operator, SwitchLogicOperatorOptions, SwitchOperatorOptions, + VariableType, } from '../../constant'; -import { useBuildFormSelectOptions } from '../../form-hooks'; -import { useBuildComponentIdAndBeginOptions } from '../../hooks/use-get-begin-query'; +import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; import { IOperatorForm } from '../../interface'; import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; @@ -71,18 +71,24 @@ function useBuildSwitchOperatorOptions() { function ConditionCards({ name: parentName, - node, parentIndex, removeParent, parentLength, }: ConditionCardsProps) { const form = useFormContext(); - const { t } = useTranslation(); - const componentIdOptions = useBuildComponentIdAndBeginOptions( - node?.id, - node?.parentId, - ); + const nextOptions = useBuildQueryVariableOptions(); + + const finalOptions = useMemo(() => { + return nextOptions.map((x) => { + return { + ...x, + options: x.options.filter( + (y) => !toLower(y.type).includes(VariableType.Array), + ), + }; + }); + }, [nextOptions]); const switchOperatorOptions = useBuildSwitchOperatorOptions(); @@ -124,12 +130,11 @@ function ConditionCards({ render={({ field }) => ( - + options={finalOptions} + triggerClassName="w-30 text-background-checked bg-transparent border-none text-ellipsis" + > @@ -224,17 +229,6 @@ const SwitchForm = ({ node }: IOperatorForm) => { control: form.control, }); - const buildCategorizeToOptions = useBuildFormSelectOptions( - Operator.Switch, - node?.id, - ); - - const getSelectedConditionTos = () => { - const conditions: ISwitchForm['conditions'] = form?.getValues('conditions'); - - return conditions?.filter((x) => !!x).map((x) => x?.to) ?? []; - }; - const switchLogicOperatorOptions = useMemo(() => { return SwitchLogicOperatorOptions.map((x) => ({ value: x, diff --git a/web/src/pages/agent/form/switch-form/use-watch-change.ts b/web/src/pages/agent/form/switch-form/use-watch-change.ts index c672ad2212c..0c8b8259e51 100644 --- a/web/src/pages/agent/form/switch-form/use-watch-change.ts +++ b/web/src/pages/agent/form/switch-form/use-watch-change.ts @@ -9,8 +9,9 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { useEffect(() => { // Manually triggered form updates are synchronized to the canvas - if (id && form?.formState.isDirty) { - values = form?.getValues(); + console.log('🚀 ~ useWatchFormChange ~ values:', form?.formState.isDirty); + if (id) { + values = form?.getValues() || {}; let nextValues: any = { ...values, conditions: diff --git a/web/src/pages/agent/form/tool-form/constant.ts b/web/src/pages/agent/form/tool-form/constant.tsx similarity index 95% rename from web/src/pages/agent/form/tool-form/constant.ts rename to web/src/pages/agent/form/tool-form/constant.tsx index c10e5056d28..708e2542fee 100644 --- a/web/src/pages/agent/form/tool-form/constant.ts +++ b/web/src/pages/agent/form/tool-form/constant.tsx @@ -2,7 +2,6 @@ import { Operator } from '../../constant'; import AkShareForm from '../akshare-form'; import ArXivForm from '../arxiv-form'; import BingForm from '../bing-form'; -import CodeForm from '../code-form'; import CrawlerForm from '../crawler-form'; import DeepLForm from '../deepl-form'; import DuckDuckGoForm from '../duckduckgo-form'; @@ -19,7 +18,7 @@ import TavilyForm from './tavily-form'; export const ToolFormConfigMap = { [Operator.Retrieval]: RetrievalForm, - [Operator.Code]: CodeForm, + [Operator.Code]: () =>
    , [Operator.DuckDuckGo]: DuckDuckGoForm, [Operator.Wikipedia]: WikipediaForm, [Operator.PubMed]: PubMedForm, diff --git a/web/src/pages/agent/hooks/use-get-begin-query.tsx b/web/src/pages/agent/hooks/use-get-begin-query.tsx index 8a3f8976366..45e05b75d52 100644 --- a/web/src/pages/agent/hooks/use-get-begin-query.tsx +++ b/web/src/pages/agent/hooks/use-get-begin-query.tsx @@ -24,6 +24,18 @@ export const useGetBeginNodeDataQuery = () => { return getBeginNodeDataQuery; }; +export const useGetBeginNodeDataInputs = () => { + const getNode = useGraphStore((state) => state.getNode); + + const inputs = get(getNode(BeginId), 'data.form.inputs', {}); + + const beginNodeDataInputs = useMemo(() => { + return buildBeginInputListFromObject(inputs); + }, [inputs]); + + return beginNodeDataInputs; +}; + export const useGetBeginNodeDataQueryIsSafe = () => { const [isBeginNodeDataQuerySafe, setIsBeginNodeDataQuerySafe] = useState(false); @@ -152,9 +164,9 @@ export const useBuildVariableOptions = (nodeId?: string, parentId?: string) => { return options; }; -export function useBuildQueryVariableOptions() { +export function useBuildQueryVariableOptions(n?: RAGFlowNodeType) { const { data } = useFetchAgent(); - const node = useContext(AgentFormContext); + const node = useContext(AgentFormContext) || n; const options = useBuildVariableOptions(node?.id, node?.parentId); const nextOptions = useMemo(() => { @@ -170,7 +182,7 @@ export function useBuildQueryVariableOptions() { { ...options[0], options: [...options[0]?.options, ...globalOptions] }, ...options.slice(1), ]; - }, [data.dsl.globals, options]); + }, [data.dsl?.globals, options]); return nextOptions; } @@ -241,3 +253,22 @@ export const useGetComponentLabelByValue = (nodeId: string) => { ); return getLabel; }; + +export function useGetVariableLabelByValue(nodeId: string) { + const { getNode } = useGraphStore((state) => state); + const nextOptions = useBuildQueryVariableOptions(getNode(nodeId)); + + const flattenOptions = useMemo(() => { + return nextOptions.reduce((pre, cur) => { + return [...pre, ...cur.options]; + }, []); + }, [nextOptions]); + + const getLabel = useCallback( + (val?: string) => { + return flattenOptions.find((x) => x.value === val)?.label; + }, + [flattenOptions], + ); + return getLabel; +} From 93a8f4a4c829694b869f09a14ca3bc471693064f Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 1 Jul 2025 17:31:56 +0800 Subject: [PATCH 0075/1187] Fix: Fixed the issue that the global variables of the code operator cannot be selected #3221 (#8605) ### What problem does this PR solve? Fix: Fixed the issue that the global variables of the code operator cannot be selected #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../form/code-form/dynamic-input-variable.tsx | 59 ------------------- .../agent/form/code-form/next-variable.tsx | 23 ++++---- .../agent/form/code-form/use-watch-change.ts | 4 +- 3 files changed, 12 insertions(+), 74 deletions(-) delete mode 100644 web/src/pages/agent/form/code-form/dynamic-input-variable.tsx diff --git a/web/src/pages/agent/form/code-form/dynamic-input-variable.tsx b/web/src/pages/agent/form/code-form/dynamic-input-variable.tsx deleted file mode 100644 index c207d280436..00000000000 --- a/web/src/pages/agent/form/code-form/dynamic-input-variable.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { BlockButton } from '@/components/ui/button'; -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { MinusCircleOutlined } from '@ant-design/icons'; -import { Form, Input, Select } from 'antd'; -import { useTranslation } from 'react-i18next'; -import { useBuildVariableOptions } from '../../hooks/use-get-begin-query'; -import { FormCollapse } from '../components/dynamic-input-variable'; - -type DynamicInputVariableProps = { - name?: string; - node?: RAGFlowNodeType; -}; - -export const DynamicInputVariable = ({ - name = 'arguments', - node, -}: DynamicInputVariableProps) => { - const { t } = useTranslation(); - - const valueOptions = useBuildVariableOptions(node?.id, node?.parentId); - - return ( - - - {(fields, { add, remove }) => ( - <> - {fields.map(({ key, name, ...restField }) => ( -
    - - - - - - - remove(name)} /> -
    - ))} - - add()}> - {t('flow.addVariable')} - - - - )} -
    -
    - ); -}; diff --git a/web/src/pages/agent/form/code-form/next-variable.tsx b/web/src/pages/agent/form/code-form/next-variable.tsx index fe668f41b75..c3f0ac5a786 100644 --- a/web/src/pages/agent/form/code-form/next-variable.tsx +++ b/web/src/pages/agent/form/code-form/next-variable.tsx @@ -1,6 +1,7 @@ 'use client'; import { FormContainer } from '@/components/form-container'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; import { BlockButton, Button } from '@/components/ui/button'; import { FormControl, @@ -9,14 +10,13 @@ import { FormMessage, } from '@/components/ui/form'; import { BlurInput } from '@/components/ui/input'; -import { RAGFlowSelect } from '@/components/ui/select'; import { Separator } from '@/components/ui/separator'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { X } from 'lucide-react'; import { ReactNode } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { useBuildVariableOptions } from '../../hooks/use-get-begin-query'; +import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; interface IProps { node?: RAGFlowNodeType; @@ -32,7 +32,7 @@ export const TypeOptions = [ 'Object', ].map((x) => ({ label: x, value: x })); -export function DynamicVariableForm({ node, name = 'arguments' }: IProps) { +export function DynamicVariableForm({ name = 'arguments' }: IProps) { const { t } = useTranslation(); const form = useFormContext(); @@ -41,19 +41,19 @@ export function DynamicVariableForm({ node, name = 'arguments' }: IProps) { control: form.control, }); - const valueOptions = useBuildVariableOptions(node?.id, node?.parentId); + const nextOptions = useBuildQueryVariableOptions(); return (
    {fields.map((field, index) => { const typeField = `${name}.${index}.name`; return ( -
    +
    ( - + ( - + - + > diff --git a/web/src/pages/agent/form/code-form/use-watch-change.ts b/web/src/pages/agent/form/code-form/use-watch-change.ts index 7c97595602e..8f882b0a3f7 100644 --- a/web/src/pages/agent/form/code-form/use-watch-change.ts +++ b/web/src/pages/agent/form/code-form/use-watch-change.ts @@ -9,8 +9,8 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { useEffect(() => { // Manually triggered form updates are synchronized to the canvas - if (id && form?.formState.isDirty) { - values = form?.getValues(); + if (id) { + values = form?.getValues() || {}; let nextValues: any = values; updateNodeForm(id, nextValues); From f586dd0a961082d69f6e37b2f395827069d4027e Mon Sep 17 00:00:00 2001 From: "wenxuan.zhang" Date: Tue, 1 Jul 2025 17:38:11 +0800 Subject: [PATCH 0076/1187] Fix: docx parse error. (#8600) ### What problem does this PR solve? docx parse error. ![image](https://github.com/user-attachments/assets/efbe6d1b-10c8-415e-b693-a86f73e1ffa6) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) ### What problem does this PR solve? Some docx parse with naive cause error. `block.style.name` in Function `__get_nearest_title` will be None in some case. ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) Co-authored-by: wenxuan.zhang --- rag/app/naive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rag/app/naive.py b/rag/app/naive.py index 809da121d30..1fd661b09ed 100644 --- a/rag/app/naive.py +++ b/rag/app/naive.py @@ -121,7 +121,7 @@ def __get_nearest_title(self, table_index, filename): if block_type != 'p': continue - if block.style and re.search(r"Heading\s*(\d+)", block.style.name, re.I): + if block.style and block.style.name and re.search(r"Heading\s*(\d+)", block.style.name, re.I): try: level_match = re.search(r"(\d+)", block.style.name) if level_match: From 0b40eb3e90029baf09b3f39f7ce9f79e3a9a75f3 Mon Sep 17 00:00:00 2001 From: Liu An Date: Wed, 2 Jul 2025 09:49:08 +0800 Subject: [PATCH 0077/1187] Test: Add tests for chunk API endpoints (#8616) ### What problem does this PR solve? - Add comprehensive test suite for chunk operations including: - Test files for create, list, retrieve, update, and delete chunks - Authorization tests - Batch operations tests - Update test configurations and common utilities - Validate `important_kwd` and `question_kwd` fields are lists in chunk_app.py - Reorganize imports and clean up duplicate code ### Type of change - [x] Add test cases --- api/apps/chunk_app.py | 32 +- test/testcases/test_http_api/conftest.py | 1 - test/testcases/test_web_api/common.py | 43 ++- test/testcases/test_web_api/conftest.py | 59 +++- .../test_web_api/test_chunk_app/conftest.py | 49 +++ .../test_chunk_app/test_create_chunk.py | 223 +++++++++++++ .../test_chunk_app/test_list_chunks.py | 145 +++++++++ .../test_chunk_app/test_retrieval_chunks.py | 308 ++++++++++++++++++ .../test_chunk_app/test_rm_chunks.py | 161 +++++++++ .../test_chunk_app/test_update_chunk.py | 232 +++++++++++++ 10 files changed, 1226 insertions(+), 27 deletions(-) create mode 100644 test/testcases/test_web_api/test_chunk_app/conftest.py create mode 100644 test/testcases/test_web_api/test_chunk_app/test_create_chunk.py create mode 100644 test/testcases/test_web_api/test_chunk_app/test_list_chunks.py create mode 100644 test/testcases/test_web_api/test_chunk_app/test_retrieval_chunks.py create mode 100644 test/testcases/test_web_api/test_chunk_app/test_rm_chunks.py create mode 100644 test/testcases/test_web_api/test_chunk_app/test_update_chunk.py diff --git a/api/apps/chunk_app.py b/api/apps/chunk_app.py index c5bdee50207..d6603fcf37b 100644 --- a/api/apps/chunk_app.py +++ b/api/apps/chunk_app.py @@ -15,27 +15,25 @@ # import datetime import json +import re +import xxhash from flask import request -from flask_login import login_required, current_user +from flask_login import current_user, login_required -from rag.app.qa import rmPrefix, beAdoc -from rag.app.tag import label_question -from rag.nlp import search, rag_tokenizer -from rag.prompts import keyword_extraction, cross_languages -from rag.settings import PAGERANK_FLD -from rag.utils import rmSpace +from api import settings from api.db import LLMType, ParserType +from api.db.services.document_service import DocumentService from api.db.services.knowledgebase_service import KnowledgebaseService from api.db.services.llm_service import LLMBundle from api.db.services.user_service import UserTenantService -from api.utils.api_utils import server_error_response, get_data_error_result, validate_request -from api.db.services.document_service import DocumentService -from api import settings -from api.utils.api_utils import get_json_result -import xxhash -import re - +from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request +from rag.app.qa import beAdoc, rmPrefix +from rag.app.tag import label_question +from rag.nlp import rag_tokenizer, search +from rag.prompts import cross_languages, keyword_extraction +from rag.settings import PAGERANK_FLD +from rag.utils import rmSpace @manager.route('/list', methods=['POST']) # noqa: F821 @@ -129,9 +127,13 @@ def set(): d["content_ltks"] = rag_tokenizer.tokenize(req["content_with_weight"]) d["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(d["content_ltks"]) if "important_kwd" in req: + if not isinstance(req["important_kwd"], list): + return get_data_error_result(message="`important_kwd` should be a list") d["important_kwd"] = req["important_kwd"] d["important_tks"] = rag_tokenizer.tokenize(" ".join(req["important_kwd"])) if "question_kwd" in req: + if not isinstance(req["question_kwd"], list): + return get_data_error_result(message="`question_kwd` should be a list") d["question_kwd"] = req["question_kwd"] d["question_tks"] = rag_tokenizer.tokenize("\n".join(req["question_kwd"])) if "tag_kwd" in req: @@ -235,6 +237,8 @@ def create(): d["create_timestamp_flt"] = datetime.datetime.now().timestamp() if "tag_feas" in req: d["tag_feas"] = req["tag_feas"] + if "tag_feas" in req: + d["tag_feas"] = req["tag_feas"] try: e, doc = DocumentService.get_by_id(req["doc_id"]) diff --git a/test/testcases/test_http_api/conftest.py b/test/testcases/test_http_api/conftest.py index 983ef8aee2c..eab05d09bcc 100644 --- a/test/testcases/test_http_api/conftest.py +++ b/test/testcases/test_http_api/conftest.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - from time import sleep import pytest diff --git a/test/testcases/test_web_api/common.py b/test/testcases/test_web_api/common.py index 7181018a441..b9b75c1aa9e 100644 --- a/test/testcases/test_web_api/common.py +++ b/test/testcases/test_web_api/common.py @@ -24,9 +24,7 @@ KB_APP_URL = f"/{VERSION}/kb" DOCUMENT_APP_URL = f"/{VERSION}/document" -# FILE_API_URL = "/api/v1/datasets/{dataset_id}/documents" -# FILE_CHUNK_API_URL = "/api/v1/datasets/{dataset_id}/chunks" -# CHUNK_API_URL = "/api/v1/datasets/{dataset_id}/documents/{document_id}/chunks" +CHUNK_API_URL = f"/{VERSION}/chunk" # CHAT_ASSISTANT_API_URL = "/api/v1/chats" # SESSION_WITH_CHAT_ASSISTANT_API_URL = "/api/v1/chats/{chat_id}/sessions" # SESSION_WITH_AGENT_API_URL = "/api/v1/agents/{agent_id}/sessions" @@ -164,3 +162,42 @@ def bulk_upload_documents(auth, kb_id, num, tmp_path): for document in res["data"]: document_ids.append(document["id"]) return document_ids + + +# CHUNK APP +def add_chunk(auth, payload=None, *, headers=HEADERS, data=None): + res = requests.post(url=f"{HOST_ADDRESS}{CHUNK_API_URL}/create", headers=headers, auth=auth, json=payload, data=data) + return res.json() + + +def list_chunks(auth, payload=None, *, headers=HEADERS): + res = requests.post(url=f"{HOST_ADDRESS}{CHUNK_API_URL}/list", headers=headers, auth=auth, json=payload) + return res.json() + + +def get_chunk(auth, params=None, *, headers=HEADERS): + res = requests.get(url=f"{HOST_ADDRESS}{CHUNK_API_URL}/get", headers=headers, auth=auth, params=params) + return res.json() + + +def update_chunk(auth, payload=None, *, headers=HEADERS): + res = requests.post(url=f"{HOST_ADDRESS}{CHUNK_API_URL}/set", headers=headers, auth=auth, json=payload) + return res.json() + + +def delete_chunks(auth, payload=None, *, headers=HEADERS): + res = requests.post(url=f"{HOST_ADDRESS}{CHUNK_API_URL}/rm", headers=headers, auth=auth, json=payload) + return res.json() + + +def retrieval_chunks(auth, payload=None, *, headers=HEADERS): + res = requests.post(url=f"{HOST_ADDRESS}{CHUNK_API_URL}/retrieval_test", headers=headers, auth=auth, json=payload) + return res.json() + + +def batch_add_chunks(auth, doc_id, num): + chunk_ids = [] + for i in range(num): + res = add_chunk(auth, {"doc_id": doc_id, "content_with_weight": f"chunk test {i}"}) + chunk_ids.append(res["data"]["chunk_id"]) + return chunk_ids diff --git a/test/testcases/test_web_api/conftest.py b/test/testcases/test_web_api/conftest.py index 82fcf982f3a..ebe0e6c29c8 100644 --- a/test/testcases/test_web_api/conftest.py +++ b/test/testcases/test_web_api/conftest.py @@ -13,18 +13,23 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from time import sleep + import pytest from common import ( + batch_add_chunks, batch_create_datasets, + bulk_upload_documents, + delete_chunks, + list_chunks, + list_documents, list_kbs, + parse_documents, rm_kb, ) - -# from configs import HOST_ADDRESS, VERSION from libs.auth import RAGFlowWebApiAuth from pytest import FixtureRequest - -# from ragflow_sdk import RAGFlow +from utils import wait_for from utils.file_utils import ( create_docx_file, create_eml_file, @@ -39,6 +44,15 @@ ) +@wait_for(30, 1, "Document parsing timeout") +def condition(_auth, _kb_id): + res = list_documents(_auth, {"kb_id": _kb_id}) + for doc in res["data"]["docs"]: + if doc["run"] != "3": + return False + return True + + @pytest.fixture def generate_test_files(request: FixtureRequest, tmp_path): file_creators = { @@ -73,11 +87,6 @@ def WebApiAuth(auth): return RAGFlowWebApiAuth(auth) -# @pytest.fixture(scope="session") -# def client(token: str) -> RAGFlow: -# return RAGFlow(api_key=token, base_url=HOST_ADDRESS, version=VERSION) - - @pytest.fixture(scope="function") def clear_datasets(request: FixtureRequest, WebApiAuth: RAGFlowWebApiAuth): def cleanup(): @@ -108,3 +117,35 @@ def cleanup(): request.addfinalizer(cleanup) return batch_create_datasets(WebApiAuth, 1)[0] + + +@pytest.fixture(scope="class") +def add_document(request, WebApiAuth, add_dataset, ragflow_tmp_dir): + # def cleanup(): + # res = list_documents(WebApiAuth, {"kb_id": dataset_id}) + # for doc in res["data"]["docs"]: + # delete_document(WebApiAuth, {"doc_id": doc["id"]}) + + # request.addfinalizer(cleanup) + + dataset_id = add_dataset + return dataset_id, bulk_upload_documents(WebApiAuth, dataset_id, 1, ragflow_tmp_dir)[0] + + +@pytest.fixture(scope="class") +def add_chunks(request, WebApiAuth, add_document): + def cleanup(): + res = list_chunks(WebApiAuth, {"doc_id": document_id}) + if res["code"] == 0: + chunk_ids = [chunk["chunk_id"] for chunk in res["data"]["chunks"]] + delete_chunks(WebApiAuth, {"doc_id": document_id, "chunk_ids": chunk_ids}) + + request.addfinalizer(cleanup) + + kb_id, document_id = add_document + parse_documents(WebApiAuth, {"doc_ids": [document_id], "run": "1"}) + condition(WebApiAuth, kb_id) + chunk_ids = batch_add_chunks(WebApiAuth, document_id, 4) + # issues/6487 + sleep(1) + return kb_id, document_id, chunk_ids diff --git a/test/testcases/test_web_api/test_chunk_app/conftest.py b/test/testcases/test_web_api/test_chunk_app/conftest.py new file mode 100644 index 00000000000..e51a2f09bf1 --- /dev/null +++ b/test/testcases/test_web_api/test_chunk_app/conftest.py @@ -0,0 +1,49 @@ +# +# 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. +# + + +from time import sleep + +import pytest +from common import batch_add_chunks, delete_chunks, list_chunks, list_documents, parse_documents +from utils import wait_for + + +@wait_for(30, 1, "Document parsing timeout") +def condition(_auth, _kb_id): + res = list_documents(_auth, {"kb_id": _kb_id}) + for doc in res["data"]["docs"]: + if doc["run"] != "3": + return False + return True + + +@pytest.fixture(scope="function") +def add_chunks_func(request, WebApiAuth, add_document): + def cleanup(): + res = list_chunks(WebApiAuth, {"doc_id": document_id}) + chunk_ids = [chunk["chunk_id"] for chunk in res["data"]["chunks"]] + delete_chunks(WebApiAuth, {"doc_id": document_id, "chunk_ids": chunk_ids}) + + request.addfinalizer(cleanup) + + kb_id, document_id = add_document + parse_documents(WebApiAuth, {"doc_ids": [document_id], "run": "1"}) + condition(WebApiAuth, kb_id) + chunk_ids = batch_add_chunks(WebApiAuth, document_id, 4) + # issues/6487 + sleep(1) + return kb_id, document_id, chunk_ids diff --git a/test/testcases/test_web_api/test_chunk_app/test_create_chunk.py b/test/testcases/test_web_api/test_chunk_app/test_create_chunk.py new file mode 100644 index 00000000000..c2731b421db --- /dev/null +++ b/test/testcases/test_web_api/test_chunk_app/test_create_chunk.py @@ -0,0 +1,223 @@ +# +# 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. +# +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import add_chunk, delete_document, get_chunk, list_chunks +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth + + +def validate_chunk_details(auth, kb_id, doc_id, payload, res): + chunk_id = res["data"]["chunk_id"] + res = get_chunk(auth, {"chunk_id": chunk_id}) + assert res["code"] == 0, res + chunk = res["data"] + assert chunk["doc_id"] == doc_id + assert chunk["kb_id"] == kb_id + assert chunk["content_with_weight"] == payload["content_with_weight"] + if "important_kwd" in payload: + assert chunk["important_kwd"] == payload["important_kwd"] + if "question_kwd" in payload: + expected = [str(q).strip() for q in payload.get("question_kwd", [])] + assert chunk["question_kwd"] == expected + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = add_chunk(invalid_auth) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestAddChunk: + @pytest.mark.p1 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"content_with_weight": None}, 100, """TypeError("unsupported operand type(s) for +: 'NoneType' and 'str'")"""), + ({"content_with_weight": ""}, 0, ""), + pytest.param( + {"content_with_weight": 1}, + 100, + """TypeError("unsupported operand type(s) for +: 'int' and 'str'")""", + marks=pytest.mark.skip, + ), + ({"content_with_weight": "a"}, 0, ""), + ({"content_with_weight": " "}, 0, ""), + ({"content_with_weight": "\n!?。;!?\"'"}, 0, ""), + ], + ) + def test_content(self, WebApiAuth, add_document, payload, expected_code, expected_message): + kb_id, doc_id = add_document + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] == 0: + chunks_count = res["data"]["doc"]["chunk_num"] + else: + chunks_count = 0 + res = add_chunk(WebApiAuth, {**payload, "doc_id": doc_id}) + assert res["code"] == expected_code, res + if expected_code == 0: + validate_chunk_details(WebApiAuth, kb_id, doc_id, payload, res) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + assert res["data"]["doc"]["chunk_num"] == chunks_count + 1, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p2 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"content_with_weight": "chunk test", "important_kwd": ["a", "b", "c"]}, 0, ""), + ({"content_with_weight": "chunk test", "important_kwd": [""]}, 0, ""), + ( + {"content_with_weight": "chunk test", "important_kwd": [1]}, + 100, + "TypeError('sequence item 0: expected str instance, int found')", + ), + ({"content_with_weight": "chunk test", "important_kwd": ["a", "a"]}, 0, ""), + ({"content_with_weight": "chunk test", "important_kwd": "abc"}, 102, "`important_kwd` is required to be a list"), + ({"content_with_weight": "chunk test", "important_kwd": 123}, 102, "`important_kwd` is required to be a list"), + ], + ) + def test_important_keywords(self, WebApiAuth, add_document, payload, expected_code, expected_message): + kb_id, doc_id = add_document + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] == 0: + chunks_count = res["data"]["doc"]["chunk_num"] + else: + chunks_count = 0 + res = add_chunk(WebApiAuth, {**payload, "doc_id": doc_id}) + assert res["code"] == expected_code, res + if expected_code == 0: + validate_chunk_details(WebApiAuth, kb_id, doc_id, payload, res) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + assert res["data"]["doc"]["chunk_num"] == chunks_count + 1, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p2 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"content_with_weight": "chunk test", "question_kwd": ["a", "b", "c"]}, 0, ""), + ({"content_with_weight": "chunk test", "question_kwd": [""]}, 0, ""), + ({"content_with_weight": "chunk test", "question_kwd": [1]}, 100, "TypeError('sequence item 0: expected str instance, int found')"), + ({"content_with_weight": "chunk test", "question_kwd": ["a", "a"]}, 0, ""), + ({"content_with_weight": "chunk test", "question_kwd": "abc"}, 102, "`question_kwd` is required to be a list"), + ({"content_with_weight": "chunk test", "question_kwd": 123}, 102, "`question_kwd` is required to be a list"), + ], + ) + def test_questions(self, WebApiAuth, add_document, payload, expected_code, expected_message): + kb_id, doc_id = add_document + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] == 0: + chunks_count = res["data"]["doc"]["chunk_num"] + else: + chunks_count = 0 + res = add_chunk(WebApiAuth, {**payload, "doc_id": doc_id}) + assert res["code"] == expected_code, res + if expected_code == 0: + validate_chunk_details(WebApiAuth, kb_id, doc_id, payload, res) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + assert res["data"]["doc"]["chunk_num"] == chunks_count + 1, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + @pytest.mark.parametrize( + "doc_id, expected_code, expected_message", + [ + ("", 102, "Document not found!"), + ("invalid_document_id", 102, "Document not found!"), + ], + ) + def test_invalid_document_id(self, WebApiAuth, add_document, doc_id, expected_code, expected_message): + _, _ = add_document + res = add_chunk(WebApiAuth, {"doc_id": doc_id, "content_with_weight": "chunk test"}) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + @pytest.mark.p3 + def test_repeated_add_chunk(self, WebApiAuth, add_document): + payload = {"content_with_weight": "chunk test"} + kb_id, doc_id = add_document + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] != 0: + assert False, res + chunks_count = res["data"]["doc"]["chunk_num"] + + res = add_chunk(WebApiAuth, {**payload, "doc_id": doc_id}) + assert res["code"] == 0, res + validate_chunk_details(WebApiAuth, kb_id, doc_id, payload, res) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] != 0: + assert False, res + assert res["data"]["doc"]["chunk_num"] == chunks_count + 1, res + + res = add_chunk(WebApiAuth, {**payload, "doc_id": doc_id}) + assert res["code"] == 0, res + validate_chunk_details(WebApiAuth, kb_id, doc_id, payload, res) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] != 0: + assert False, res + assert res["data"]["doc"]["chunk_num"] == chunks_count + 2, res + + @pytest.mark.p2 + def test_add_chunk_to_deleted_document(self, WebApiAuth, add_document): + _, doc_id = add_document + delete_document(WebApiAuth, {"doc_id": doc_id}) + res = add_chunk(WebApiAuth, {"doc_id": doc_id, "content_with_weight": "chunk test"}) + assert res["code"] == 102, res + assert res["message"] == "Document not found!", res + + @pytest.mark.skip(reason="issues/6411") + @pytest.mark.p3 + def test_concurrent_add_chunk(self, WebApiAuth, add_document): + count = 50 + _, doc_id = add_document + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] == 0: + chunks_count = res["data"]["doc"]["chunk_num"] + else: + chunks_count = 0 + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [ + executor.submit( + add_chunk, + WebApiAuth, + {"doc_id": doc_id, "content_with_weight": f"chunk test {i}"}, + ) + for i in range(count) + ] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + assert res["data"]["doc"]["chunk_num"] == chunks_count + count diff --git a/test/testcases/test_web_api/test_chunk_app/test_list_chunks.py b/test/testcases/test_web_api/test_chunk_app/test_list_chunks.py new file mode 100644 index 00000000000..dd567e01d75 --- /dev/null +++ b/test/testcases/test_web_api/test_chunk_app/test_list_chunks.py @@ -0,0 +1,145 @@ +# +# 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 os +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import batch_add_chunks, list_chunks +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = list_chunks(invalid_auth, {"doc_id": "document_id"}) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestChunksList: + @pytest.mark.p1 + @pytest.mark.parametrize( + "params, expected_code, expected_page_size, expected_message", + [ + pytest.param({"page": None, "size": 2}, 100, 0, """TypeError("int() argument must be a string, a bytes-like object or a real number, not 'NoneType'")""", marks=pytest.mark.skip), + pytest.param({"page": 0, "size": 2}, 100, 0, "ValueError('Search does not support negative slicing.')", marks=pytest.mark.skip), + ({"page": 2, "size": 2}, 0, 2, ""), + ({"page": 3, "size": 2}, 0, 1, ""), + ({"page": "3", "size": 2}, 0, 1, ""), + pytest.param({"page": -1, "size": 2}, 100, 0, "ValueError('Search does not support negative slicing.')", marks=pytest.mark.skip), + pytest.param({"page": "a", "size": 2}, 100, 0, """ValueError("invalid literal for int() with base 10: \'a\'")""", marks=pytest.mark.skip), + ], + ) + def test_page(self, WebApiAuth, add_chunks, params, expected_code, expected_page_size, expected_message): + _, doc_id, _ = add_chunks + payload = {"doc_id": doc_id} + if params: + payload.update(params) + res = list_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p1 + @pytest.mark.parametrize( + "params, expected_code, expected_page_size, expected_message", + [ + ({"size": None}, 100, 0, """TypeError("int() argument must be a string, a bytes-like object or a real number, not 'NoneType'")"""), + pytest.param({"size": 0}, 0, 5, "", marks=pytest.mark.skipif(os.getenv("DOC_ENGINE") == "infinity", reason="Infinity does not support page_size=0")), + pytest.param({"size": 0}, 100, 0, "3013", marks=pytest.mark.skipif(os.getenv("DOC_ENGINE") in [None, "opensearch", "elasticsearch"], reason="Infinity does not support page_size=0")), + ({"size": 1}, 0, 1, ""), + ({"size": 6}, 0, 5, ""), + ({"size": "1"}, 0, 1, ""), + pytest.param({"size": -1}, 0, 5, "", marks=pytest.mark.skip), + pytest.param({"size": "a"}, 100, 0, """ValueError("invalid literal for int() with base 10: \'a\'")""", marks=pytest.mark.skip), + ], + ) + def test_page_size(self, WebApiAuth, add_chunks, params, expected_code, expected_page_size, expected_message): + _, doc_id, _ = add_chunks + payload = {"doc_id": doc_id} + if params: + payload.update(params) + res = list_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p2 + @pytest.mark.parametrize( + "params, expected_page_size", + [ + ({"keywords": None}, 5), + ({"keywords": ""}, 5), + ({"keywords": "1"}, 1), + pytest.param({"keywords": "chunk"}, 4, marks=pytest.mark.skipif(os.getenv("DOC_ENGINE") == "infinity", reason="issues/6509")), + ({"keywords": "content"}, 1), + ({"keywords": "unknown"}, 0), + ], + ) + def test_keywords(self, WebApiAuth, add_chunks, params, expected_page_size): + _, doc_id, _ = add_chunks + payload = {"doc_id": doc_id} + if params: + payload.update(params) + res = list_chunks(WebApiAuth, payload) + assert res["code"] == 0, res + assert len(res["data"]["chunks"]) == expected_page_size, res + + @pytest.mark.p3 + def test_invalid_params(self, WebApiAuth, add_chunks): + _, doc_id, _ = add_chunks + payload = {"doc_id": doc_id, "a": "b"} + res = list_chunks(WebApiAuth, payload) + assert res["code"] == 0, res + assert len(res["data"]["chunks"]) == 5, res + + @pytest.mark.p3 + def test_concurrent_list(self, WebApiAuth, add_chunks): + _, doc_id, _ = add_chunks + count = 100 + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [executor.submit(list_chunks, WebApiAuth, {"doc_id": doc_id}) for i in range(count)] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(len(future.result()["data"]["chunks"]) == 5 for future in futures) + + @pytest.mark.p1 + def test_default(self, WebApiAuth, add_document): + _, doc_id = add_document + + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + chunks_count = res["data"]["doc"]["chunk_num"] + batch_add_chunks(WebApiAuth, doc_id, 31) + # issues/6487 + from time import sleep + + sleep(3) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0 + assert len(res["data"]["chunks"]) == 30 + assert res["data"]["doc"]["chunk_num"] == chunks_count + 31 diff --git a/test/testcases/test_web_api/test_chunk_app/test_retrieval_chunks.py b/test/testcases/test_web_api/test_chunk_app/test_retrieval_chunks.py new file mode 100644 index 00000000000..62e8efa448b --- /dev/null +++ b/test/testcases/test_web_api/test_chunk_app/test_retrieval_chunks.py @@ -0,0 +1,308 @@ +# +# 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 os +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import retrieval_chunks +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = retrieval_chunks(invalid_auth, {"kb_id": "dummy_kb_id", "question": "dummy question"}) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestChunksRetrieval: + @pytest.mark.p1 + @pytest.mark.parametrize( + "payload, expected_code, expected_page_size, expected_message", + [ + ({"question": "chunk", "kb_id": None}, 0, 4, ""), + ({"question": "chunk", "doc_ids": None}, 101, 0, "required argument are missing: kb_id; "), + ({"question": "chunk", "kb_id": None, "doc_ids": None}, 0, 4, ""), + ({"question": "chunk"}, 101, 0, "required argument are missing: kb_id; "), + ], + ) + def test_basic_scenarios(self, WebApiAuth, add_chunks, payload, expected_code, expected_page_size, expected_message): + dataset_id, document_id, _ = add_chunks + if "kb_id" in payload: + payload["kb_id"] = [dataset_id] + if "doc_ids" in payload: + payload["doc_ids"] = [document_id] + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p2 + @pytest.mark.parametrize( + "payload, expected_code, expected_page_size, expected_message", + [ + pytest.param( + {"page": None, "size": 2}, + 100, + 0, + """TypeError("int() argument must be a string, a bytes-like object or a real number, not 'NoneType'")""", + marks=pytest.mark.skip, + ), + pytest.param( + {"page": 0, "size": 2}, + 100, + 0, + "ValueError('Search does not support negative slicing.')", + marks=pytest.mark.skip, + ), + pytest.param({"page": 2, "size": 2}, 0, 2, "", marks=pytest.mark.skip(reason="issues/6646")), + ({"page": 3, "size": 2}, 0, 0, ""), + ({"page": "3", "size": 2}, 0, 0, ""), + pytest.param( + {"page": -1, "size": 2}, + 100, + 0, + "ValueError('Search does not support negative slicing.')", + marks=pytest.mark.skip, + ), + pytest.param( + {"page": "a", "size": 2}, + 100, + 0, + """ValueError("invalid literal for int() with base 10: 'a'")""", + marks=pytest.mark.skip, + ), + ], + ) + def test_page(self, WebApiAuth, add_chunks, payload, expected_code, expected_page_size, expected_message): + dataset_id, _, _ = add_chunks + payload.update({"question": "chunk", "kb_id": [dataset_id]}) + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + @pytest.mark.parametrize( + "payload, expected_code, expected_page_size, expected_message", + [ + pytest.param( + {"size": None}, + 100, + 0, + """TypeError("int() argument must be a string, a bytes-like object or a real number, not 'NoneType'")""", + marks=pytest.mark.skip, + ), + # ({"size": 0}, 0, 0, ""), + ({"size": 1}, 0, 1, ""), + ({"size": 5}, 0, 4, ""), + ({"size": "1"}, 0, 1, ""), + # ({"size": -1}, 0, 0, ""), + pytest.param( + {"size": "a"}, + 100, + 0, + """ValueError("invalid literal for int() with base 10: 'a'")""", + marks=pytest.mark.skip, + ), + ], + ) + def test_page_size(self, WebApiAuth, add_chunks, payload, expected_code, expected_page_size, expected_message): + dataset_id, _, _ = add_chunks + payload.update({"question": "chunk", "kb_id": [dataset_id]}) + + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + @pytest.mark.parametrize( + "payload, expected_code, expected_page_size, expected_message", + [ + ({"vector_similarity_weight": 0}, 0, 4, ""), + ({"vector_similarity_weight": 0.5}, 0, 4, ""), + ({"vector_similarity_weight": 10}, 0, 4, ""), + pytest.param( + {"vector_similarity_weight": "a"}, + 100, + 0, + """ValueError("could not convert string to float: 'a'")""", + marks=pytest.mark.skip, + ), + ], + ) + def test_vector_similarity_weight(self, WebApiAuth, add_chunks, payload, expected_code, expected_page_size, expected_message): + dataset_id, _, _ = add_chunks + payload.update({"question": "chunk", "kb_id": [dataset_id]}) + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p2 + @pytest.mark.parametrize( + "payload, expected_code, expected_page_size, expected_message", + [ + ({"top_k": 10}, 0, 4, ""), + pytest.param( + {"top_k": 1}, + 0, + 4, + "", + marks=pytest.mark.skipif(os.getenv("DOC_ENGINE") in ["infinity", "opensearch"], reason="Infinity"), + ), + pytest.param( + {"top_k": 1}, + 0, + 1, + "", + marks=pytest.mark.skipif(os.getenv("DOC_ENGINE") in [None, "opensearch", "elasticsearch"], reason="elasticsearch"), + ), + pytest.param( + {"top_k": -1}, + 100, + 4, + "must be greater than 0", + marks=pytest.mark.skipif(os.getenv("DOC_ENGINE") in ["infinity", "opensearch"], reason="Infinity"), + ), + pytest.param( + {"top_k": -1}, + 100, + 4, + "3014", + marks=pytest.mark.skipif(os.getenv("DOC_ENGINE") in [None, "opensearch", "elasticsearch"], reason="elasticsearch"), + ), + pytest.param( + {"top_k": "a"}, + 100, + 0, + """ValueError("invalid literal for int() with base 10: 'a'")""", + marks=pytest.mark.skip, + ), + ], + ) + def test_top_k(self, WebApiAuth, add_chunks, payload, expected_code, expected_page_size, expected_message): + dataset_id, _, _ = add_chunks + payload.update({"question": "chunk", "kb_id": [dataset_id]}) + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert expected_message in res["message"], res + + @pytest.mark.skip + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"rerank_id": "BAAI/bge-reranker-v2-m3"}, 0, ""), + pytest.param({"rerank_id": "unknown"}, 100, "LookupError('Model(unknown) not authorized')", marks=pytest.mark.skip), + ], + ) + def test_rerank_id(self, WebApiAuth, add_chunks, payload, expected_code, expected_message): + dataset_id, _, _ = add_chunks + payload.update({"question": "chunk", "kb_id": [dataset_id]}) + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) > 0, res + else: + assert expected_message in res["message"], res + + @pytest.mark.skip + @pytest.mark.parametrize( + "payload, expected_code, expected_page_size, expected_message", + [ + ({"keyword": True}, 0, 5, ""), + ({"keyword": "True"}, 0, 5, ""), + ({"keyword": False}, 0, 5, ""), + ({"keyword": "False"}, 0, 5, ""), + ({"keyword": None}, 0, 5, ""), + ], + ) + def test_keyword(self, WebApiAuth, add_chunks, payload, expected_code, expected_page_size, expected_message): + dataset_id, _, _ = add_chunks + payload.update({"question": "chunk test", "kb_id": [dataset_id]}) + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_code == 0: + assert len(res["data"]["chunks"]) == expected_page_size, res + else: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + @pytest.mark.parametrize( + "payload, expected_code, expected_highlight, expected_message", + [ + ({"highlight": True}, 0, True, ""), + ({"highlight": "True"}, 0, True, ""), + pytest.param({"highlight": False}, 0, False, "", marks=pytest.mark.skip(reason="issues/6648")), + pytest.param({"highlight": "False"}, 0, False, "", marks=pytest.mark.skip(reason="issues/6648")), + pytest.param({"highlight": None}, 0, False, "", marks=pytest.mark.skip(reason="issues/6648")), + ], + ) + def test_highlight(self, WebApiAuth, add_chunks, payload, expected_code, expected_highlight, expected_message): + dataset_id, _, _ = add_chunks + payload.update({"question": "chunk", "kb_id": [dataset_id]}) + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if expected_highlight: + for chunk in res["data"]["chunks"]: + assert "highlight" in chunk, res + else: + for chunk in res["data"]["chunks"]: + assert "highlight" not in chunk, res + + if expected_code != 0: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + def test_invalid_params(self, WebApiAuth, add_chunks): + dataset_id, _, _ = add_chunks + payload = {"question": "chunk", "kb_id": [dataset_id], "a": "b"} + res = retrieval_chunks(WebApiAuth, payload) + assert res["code"] == 0, res + assert len(res["data"]["chunks"]) == 4, res + + @pytest.mark.p3 + def test_concurrent_retrieval(self, WebApiAuth, add_chunks): + dataset_id, _, _ = add_chunks + count = 100 + payload = {"question": "chunk", "kb_id": [dataset_id]} + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [executor.submit(retrieval_chunks, WebApiAuth, payload) for i in range(count)] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures) diff --git a/test/testcases/test_web_api/test_chunk_app/test_rm_chunks.py b/test/testcases/test_web_api/test_chunk_app/test_rm_chunks.py new file mode 100644 index 00000000000..b293daf10d7 --- /dev/null +++ b/test/testcases/test_web_api/test_chunk_app/test_rm_chunks.py @@ -0,0 +1,161 @@ +# +# 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. +# +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +from common import batch_add_chunks, delete_chunks, list_chunks +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = delete_chunks(invalid_auth, {"doc_id": "document_id", "chunk_ids": ["1"]}) + assert res["code"] == expected_code + assert res["message"] == expected_message + + +class TestChunksDeletion: + @pytest.mark.p3 + @pytest.mark.parametrize( + "doc_id, expected_code, expected_message", + [ + ("", 102, "Document not found!"), + ("invalid_document_id", 102, "Document not found!"), + ], + ) + def test_invalid_document_id(self, WebApiAuth, add_chunks_func, doc_id, expected_code, expected_message): + _, _, chunk_ids = add_chunks_func + res = delete_chunks(WebApiAuth, {"doc_id": doc_id, "chunk_ids": chunk_ids}) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + @pytest.mark.parametrize( + "payload", + [ + pytest.param(lambda r: {"chunk_ids": ["invalid_id"] + r}, marks=pytest.mark.p3), + pytest.param(lambda r: {"chunk_ids": r[:1] + ["invalid_id"] + r[1:4]}, marks=pytest.mark.p1), + pytest.param(lambda r: {"chunk_ids": r + ["invalid_id"]}, marks=pytest.mark.p3), + ], + ) + def test_delete_partial_invalid_id(self, WebApiAuth, add_chunks_func, payload): + _, doc_id, chunk_ids = add_chunks_func + if callable(payload): + payload = payload(chunk_ids) + payload["doc_id"] = doc_id + res = delete_chunks(WebApiAuth, payload) + assert res["code"] == 0, res + + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + assert len(res["data"]["chunks"]) == 0, res + assert res["data"]["total"] == 0, res + + @pytest.mark.p3 + def test_repeated_deletion(self, WebApiAuth, add_chunks_func): + _, doc_id, chunk_ids = add_chunks_func + payload = {"chunk_ids": chunk_ids, "doc_id": doc_id} + res = delete_chunks(WebApiAuth, payload) + assert res["code"] == 0, res + + res = delete_chunks(WebApiAuth, payload) + assert res["code"] == 102, res + assert res["message"] == "Index updating failure", res + + @pytest.mark.p3 + def test_duplicate_deletion(self, WebApiAuth, add_chunks_func): + _, doc_id, chunk_ids = add_chunks_func + payload = {"chunk_ids": chunk_ids * 2, "doc_id": doc_id} + res = delete_chunks(WebApiAuth, payload) + assert res["code"] == 0, res + + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + assert res["code"] == 0, res + assert len(res["data"]["chunks"]) == 0, res + assert res["data"]["total"] == 0, res + + @pytest.mark.p3 + def test_concurrent_deletion(self, WebApiAuth, add_document): + count = 100 + _, doc_id = add_document + chunk_ids = batch_add_chunks(WebApiAuth, doc_id, count) + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [ + executor.submit( + delete_chunks, + WebApiAuth, + {"doc_id": doc_id, "chunk_ids": chunk_ids[i : i + 1]}, + ) + for i in range(count) + ] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures) + + @pytest.mark.p3 + def test_delete_1k(self, WebApiAuth, add_document): + chunks_num = 1_000 + _, doc_id = add_document + chunk_ids = batch_add_chunks(WebApiAuth, doc_id, chunks_num) + + from time import sleep + + sleep(1) + + res = delete_chunks(WebApiAuth, {"doc_id": doc_id, "chunk_ids": chunk_ids}) + assert res["code"] == 0 + + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] != 0: + assert False, res + assert len(res["data"]["chunks"]) == 0, res + assert res["data"]["total"] == 0, res + + @pytest.mark.parametrize( + "payload, expected_code, expected_message, remaining", + [ + pytest.param(None, 100, """TypeError("argument of type \'NoneType\' is not iterable")""", 5, marks=pytest.mark.skip), + pytest.param({"chunk_ids": ["invalid_id"]}, 102, "Index updating failure", 4, marks=pytest.mark.p3), + pytest.param("not json", 100, """UnboundLocalError("local variable \'duplicate_messages\' referenced before assignment")""", 5, marks=pytest.mark.skip(reason="pull/6376")), + pytest.param(lambda r: {"chunk_ids": r[:1]}, 0, "", 3, marks=pytest.mark.p3), + pytest.param(lambda r: {"chunk_ids": r}, 0, "", 0, marks=pytest.mark.p1), + pytest.param({"chunk_ids": []}, 0, "", 0, marks=pytest.mark.p3), + ], + ) + def test_basic_scenarios(self, WebApiAuth, add_chunks_func, payload, expected_code, expected_message, remaining): + _, doc_id, chunk_ids = add_chunks_func + if callable(payload): + payload = payload(chunk_ids) + payload["doc_id"] = doc_id + res = delete_chunks(WebApiAuth, payload) + assert res["code"] == expected_code, res + if res["code"] != 0: + assert res["message"] == expected_message, res + + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + if res["code"] != 0: + assert False, res + assert len(res["data"]["chunks"]) == remaining, res + assert res["data"]["total"] == remaining, res diff --git a/test/testcases/test_web_api/test_chunk_app/test_update_chunk.py b/test/testcases/test_web_api/test_chunk_app/test_update_chunk.py new file mode 100644 index 00000000000..b1fcd567abe --- /dev/null +++ b/test/testcases/test_web_api/test_chunk_app/test_update_chunk.py @@ -0,0 +1,232 @@ +# +# 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 os +from concurrent.futures import ThreadPoolExecutor, as_completed +from random import randint +from time import sleep + +import pytest +from common import delete_document, list_chunks, update_chunk +from configs import INVALID_API_TOKEN +from libs.auth import RAGFlowWebApiAuth + + +@pytest.mark.p1 +class TestAuthorization: + @pytest.mark.parametrize( + "invalid_auth, expected_code, expected_message", + [ + (None, 401, ""), + (RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, ""), + ], + ) + def test_invalid_auth(self, invalid_auth, expected_code, expected_message): + res = update_chunk(invalid_auth, {"doc_id": "doc_id", "chunk_id": "chunk_id", "content_with_weight": "test"}) + assert res["code"] == expected_code, res + assert res["message"] == expected_message, res + + +class TestUpdateChunk: + @pytest.mark.p1 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"content_with_weight": None}, 100, "TypeError('expected string or bytes-like object')"), + ({"content_with_weight": ""}, 0, ""), + ({"content_with_weight": 1}, 100, "TypeError('expected string or bytes-like object')"), + ({"content_with_weight": "update chunk"}, 0, ""), + ({"content_with_weight": " "}, 0, ""), + ({"content_with_weight": "\n!?。;!?\"'"}, 0, ""), + ], + ) + def test_content(self, WebApiAuth, add_chunks, payload, expected_code, expected_message): + _, doc_id, chunk_ids = add_chunks + chunk_id = chunk_ids[0] + update_payload = {"doc_id": doc_id, "chunk_id": chunk_id} + if payload: + update_payload.update(payload) + res = update_chunk(WebApiAuth, update_payload) + assert res["code"] == expected_code, res + if expected_code != 0: + assert res["message"] == expected_message, res + else: + sleep(1) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + for chunk in res["data"]["chunks"]: + if chunk["chunk_id"] == chunk_id: + assert chunk["content_with_weight"] == payload["content_with_weight"] + + @pytest.mark.p2 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"important_kwd": ["a", "b", "c"]}, 0, ""), + ({"important_kwd": [""]}, 0, ""), + ({"important_kwd": [1]}, 100, "TypeError('sequence item 0: expected str instance, int found')"), + ({"important_kwd": ["a", "a"]}, 0, ""), + ({"important_kwd": "abc"}, 102, "`important_kwd` should be a list"), + ({"important_kwd": 123}, 102, "`important_kwd` should be a list"), + ], + ) + def test_important_keywords(self, WebApiAuth, add_chunks, payload, expected_code, expected_message): + _, doc_id, chunk_ids = add_chunks + chunk_id = chunk_ids[0] + update_payload = {"doc_id": doc_id, "chunk_id": chunk_id, "content_with_weight": "unchanged content"} # Add content_with_weight as it's required + if payload: + update_payload.update(payload) + res = update_chunk(WebApiAuth, update_payload) + assert res["code"] == expected_code, res + if expected_code != 0: + assert res["message"] == expected_message, res + else: + sleep(1) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + for chunk in res["data"]["chunks"]: + if chunk["chunk_id"] == chunk_id: + assert chunk["important_kwd"] == payload["important_kwd"] + + @pytest.mark.p2 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"question_kwd": ["a", "b", "c"]}, 0, ""), + ({"question_kwd": [""]}, 0, ""), + ({"question_kwd": [1]}, 100, "TypeError('sequence item 0: expected str instance, int found')"), + ({"question_kwd": ["a", "a"]}, 0, ""), + ({"question_kwd": "abc"}, 102, "`question_kwd` should be a list"), + ({"question_kwd": 123}, 102, "`question_kwd` should be a list"), + ], + ) + def test_questions(self, WebApiAuth, add_chunks, payload, expected_code, expected_message): + _, doc_id, chunk_ids = add_chunks + chunk_id = chunk_ids[0] + update_payload = {"doc_id": doc_id, "chunk_id": chunk_id, "content_with_weight": "unchanged content"} # Add content_with_weight as it's required + if payload: + update_payload.update(payload) + + res = update_chunk(WebApiAuth, update_payload) + assert res["code"] == expected_code, res + if expected_code != 0: + assert res["message"] == expected_message, res + else: + sleep(1) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + for chunk in res["data"]["chunks"]: + if chunk["chunk_id"] == chunk_id: + assert chunk["question_kwd"] == payload["question_kwd"] + + @pytest.mark.p2 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"available_int": 1}, 0, ""), + ({"available_int": 0}, 0, ""), + ], + ) + def test_available(self, WebApiAuth, add_chunks, payload, expected_code, expected_message): + _, doc_id, chunk_ids = add_chunks + chunk_id = chunk_ids[0] + update_payload = {"doc_id": doc_id, "chunk_id": chunk_id, "content_with_weight": "unchanged content"} + if payload: + update_payload.update(payload) + + res = update_chunk(WebApiAuth, update_payload) + assert res["code"] == expected_code, res + if expected_code != 0: + assert res["message"] == expected_message, res + else: + sleep(1) + res = list_chunks(WebApiAuth, {"doc_id": doc_id}) + for chunk in res["data"]["chunks"]: + if chunk["chunk_id"] == chunk_id: + assert chunk["available_int"] == payload["available_int"] + + @pytest.mark.p3 + @pytest.mark.parametrize( + "doc_id_param, expected_code, expected_message", + [ + ("", 102, "Tenant not found!"), + ("invalid_doc_id", 102, "Tenant not found!"), + ], + ) + def test_invalid_document_id_for_update(self, WebApiAuth, add_chunks, doc_id_param, expected_code, expected_message): + _, _, chunk_ids = add_chunks + chunk_id = chunk_ids[0] + + payload = {"doc_id": doc_id_param, "chunk_id": chunk_id, "content_with_weight": "test content"} + res = update_chunk(WebApiAuth, payload) + assert res["code"] == expected_code + assert expected_message in res["message"] + + @pytest.mark.p3 + def test_repeated_update_chunk(self, WebApiAuth, add_chunks): + _, doc_id, chunk_ids = add_chunks + payload1 = {"doc_id": doc_id, "chunk_id": chunk_ids[0], "content_with_weight": "chunk test 1"} + res = update_chunk(WebApiAuth, payload1) + assert res["code"] == 0 + + payload2 = {"doc_id": doc_id, "chunk_id": chunk_ids[0], "content_with_weight": "chunk test 2"} + res = update_chunk(WebApiAuth, payload2) + assert res["code"] == 0 + + @pytest.mark.p3 + @pytest.mark.parametrize( + "payload, expected_code, expected_message", + [ + ({"unknown_key": "unknown_value"}, 0, ""), + ({}, 0, ""), + pytest.param(None, 100, """TypeError("int() argument must be a string, a bytes-like object or a real number, not 'NoneType'")""", marks=pytest.mark.skip), + ], + ) + def test_invalid_params(self, WebApiAuth, add_chunks, payload, expected_code, expected_message): + _, doc_id, chunk_ids = add_chunks + chunk_id = chunk_ids[0] + update_payload = {"doc_id": doc_id, "chunk_id": chunk_id, "content_with_weight": "unchanged content"} + if payload is not None: + update_payload.update(payload) + + res = update_chunk(WebApiAuth, update_payload) + assert res["code"] == expected_code, res + if expected_code != 0: + assert res["message"] == expected_message, res + + @pytest.mark.p3 + @pytest.mark.skipif(os.getenv("DOC_ENGINE") == "infinity", reason="issues/6554") + def test_concurrent_update_chunk(self, WebApiAuth, add_chunks): + count = 50 + _, doc_id, chunk_ids = add_chunks + + with ThreadPoolExecutor(max_workers=5) as executor: + futures = [ + executor.submit( + update_chunk, + WebApiAuth, + {"doc_id": doc_id, "chunk_id": chunk_ids[randint(0, 3)], "content_with_weight": f"update chunk test {i}"}, + ) + for i in range(count) + ] + responses = list(as_completed(futures)) + assert len(responses) == count, responses + assert all(future.result()["code"] == 0 for future in futures) + + @pytest.mark.p3 + def test_update_chunk_to_deleted_document(self, WebApiAuth, add_chunks): + _, doc_id, chunk_ids = add_chunks + delete_document(WebApiAuth, {"doc_id": doc_id}) + payload = {"doc_id": doc_id, "chunk_id": chunk_ids[0], "content_with_weight": "test content"} + res = update_chunk(WebApiAuth, payload) + assert res["code"] == 102, res + assert res["message"] == "Tenant not found!", res From 212d5ce7ff62cfdcc9f96aa4650d077082052cf2 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 2 Jul 2025 09:49:42 +0800 Subject: [PATCH 0078/1187] Feat: Construct the to field of the classification operator when saving data #3221 (#8610) ### What problem does this PR solve? Feat: Construct the to field of the classification operator when saving data #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/interfaces/database/agent.ts | 1 + web/src/pages/agent/store.ts | 47 ++-------------------------- web/src/pages/agent/utils.ts | 42 ++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index 8fd95d83d7c..aac61a503a2 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -3,6 +3,7 @@ export interface ICategorizeItem { description?: string; examples?: { value: string }[]; index: number; + to: string[]; } export type ICategorizeItemResult = Record< diff --git a/web/src/pages/agent/store.ts b/web/src/pages/agent/store.ts index a78768e01b9..e8cc2b24137 100644 --- a/web/src/pages/agent/store.ts +++ b/web/src/pages/agent/store.ts @@ -66,7 +66,6 @@ export type RFState = { target?: string | null, isConnecting?: boolean, ) => void; - deletePreviousEdgeOfClassificationNode: (connection: Connection) => void; duplicateNode: (id: string, name: string) => void; duplicateIterationNode: (id: string, name: string) => void; deleteEdge: () => void; @@ -122,14 +121,10 @@ const useGraphStore = create()( setEdges(mapEdgeMouseEvent(edges, edgeId, false)); }, onConnect: (connection: Connection) => { - const { - deletePreviousEdgeOfClassificationNode, - updateFormDataOnConnect, - } = get(); + const { updateFormDataOnConnect } = get(); set({ edges: addEdge(connection, get().edges), }); - deletePreviousEdgeOfClassificationNode(connection); updateFormDataOnConnect(connection); }, onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => { @@ -216,7 +211,6 @@ const useGraphStore = create()( set({ edges: addEdge(connection, get().edges), }); - get().deletePreviousEdgeOfClassificationNode(connection); // TODO: This may not be reasonable. You need to choose between listening to changes in the form. get().updateFormDataOnConnect(connection); }, @@ -224,23 +218,11 @@ const useGraphStore = create()( return get().edges.find((x) => x.id === id); }, updateFormDataOnConnect: (connection: Connection) => { - const { getOperatorTypeFromId, updateNodeForm, updateSwitchFormData } = - get(); + const { getOperatorTypeFromId, updateSwitchFormData } = get(); const { source, target, sourceHandle } = connection; const operatorType = getOperatorTypeFromId(source); if (source) { switch (operatorType) { - case Operator.Relevant: - updateNodeForm(source, { [sourceHandle as string]: target }); - break; - case Operator.Categorize: - if (sourceHandle) - updateNodeForm(source, target, [ - 'category_description', - sourceHandle, - 'to', - ]); - break; case Operator.Switch: { updateSwitchFormData(source, sourceHandle, target, true); break; @@ -250,31 +232,6 @@ const useGraphStore = create()( } } }, - deletePreviousEdgeOfClassificationNode: (connection: Connection) => { - // Delete the edge on the classification node or relevant node anchor when the anchor is connected to other nodes - const { edges, getOperatorTypeFromId, deleteEdgeById } = get(); - // the node containing the anchor - const anchoredNodes = [ - Operator.Categorize, - Operator.Relevant, - // Operator.Switch, - ]; - if ( - anchoredNodes.some( - (x) => x === getOperatorTypeFromId(connection.source), - ) - ) { - const previousEdge = edges.find( - (x) => - x.source === connection.source && - x.sourceHandle === connection.sourceHandle && - x.target !== connection.target, - ); - if (previousEdge) { - deleteEdgeById(previousEdge.id); - } - } - }, duplicateNode: (id: string, name: string) => { const { getNode, addNode, generateNodeName, duplicateIterationNode } = get(); diff --git a/web/src/pages/agent/utils.ts b/web/src/pages/agent/utils.ts index 06fcb52307e..f1167b791d7 100644 --- a/web/src/pages/agent/utils.ts +++ b/web/src/pages/agent/utils.ts @@ -1,5 +1,6 @@ import { IAgentForm, + ICategorizeForm, ICategorizeItem, ICategorizeItemResult, } from '@/interfaces/database/agent'; @@ -158,6 +159,33 @@ function buildAgentTools(edges: Edge[], nodes: Node[], nodeId: string) { return params; } +function filterTargetsBySourceHandleId(edges: Edge[], handleId: string) { + return edges.filter((x) => x.sourceHandle === handleId).map((x) => x.target); +} + +function buildCategorizeTos(edges: Edge[], nodes: Node[], nodeId: string) { + const node = nodes.find((x) => x.id === nodeId); + const params = { ...(node?.data.form ?? {}) } as ICategorizeForm; + if (node && node.data.label === Operator.Categorize) { + const subEdges = edges.filter((x) => x.source === nodeId); + + const categoryDescription = params.category_description || {}; + + const nextCategoryDescription = Object.entries(categoryDescription).reduce< + ICategorizeForm['category_description'] + >((pre, [key, val]) => { + pre[key] = { + ...val, + to: filterTargetsBySourceHandleId(subEdges, key), + }; + return pre; + }, {}); + + params.category_description = nextCategoryDescription; + } + return params; +} + const buildOperatorParams = (operatorName: string) => pipe( removeUselessDataInTheOperator(operatorName), @@ -190,8 +218,20 @@ export const buildDslComponentsByGraph = ( .forEach((x) => { const id = x.id; const operatorName = x.data.label; + let params = x?.data.form ?? {}; + + switch (operatorName) { + case Operator.Agent: + params = buildAgentTools(edges, nodes, id); + break; + case Operator.Categorize: + params = buildCategorizeTos(edges, nodes, id); + break; + + default: + break; + } - const params = buildAgentTools(edges, nodes, id); components[id] = { obj: { ...(oldDslComponents[id]?.obj ?? {}), From 9dd3dfaab014668288d1f33a851cce8d7041042b Mon Sep 17 00:00:00 2001 From: Scott Davidson <49713135+sd109@users.noreply.github.com> Date: Wed, 2 Jul 2025 02:58:17 +0100 Subject: [PATCH 0079/1187] Add service_conf and llm_factories options to Helm chart (#8607) ### What problem does this PR solve? ### Type of change - [X] New Feature (non-breaking change which adds functionality) --- helm/templates/ragflow.yaml | 13 +++++++++++++ helm/templates/ragflow_config.yaml | 14 ++++++++++++++ helm/values.yaml | 24 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/helm/templates/ragflow.yaml b/helm/templates/ragflow.yaml index b1f8d1ea155..57a5d224b9f 100644 --- a/helm/templates/ragflow.yaml +++ b/helm/templates/ragflow.yaml @@ -43,6 +43,16 @@ spec: - mountPath: /etc/nginx/nginx.conf subPath: nginx.conf name: nginx-config-volume + {{- with .Values.ragflow.service_conf }} + - mountPath: /ragflow/conf/local.service_conf.yaml + subPath: local.service_conf.yaml + name: service-conf-volume + {{- end }} + {{- with .Values.ragflow.llm_factories }} + - mountPath: /ragflow/conf/llm_factories.json + subPath: llm_factories.json + name: service-conf-volume + {{- end }} envFrom: - secretRef: name: {{ include "ragflow.fullname" . }}-env-config @@ -54,6 +64,9 @@ spec: - name: nginx-config-volume configMap: name: nginx-config + - name: service-conf-volume + configMap: + name: ragflow-service-config --- apiVersion: v1 kind: Service diff --git a/helm/templates/ragflow_config.yaml b/helm/templates/ragflow_config.yaml index 6967ecca577..533bd3df9a8 100644 --- a/helm/templates/ragflow_config.yaml +++ b/helm/templates/ragflow_config.yaml @@ -1,6 +1,20 @@ --- apiVersion: v1 kind: ConfigMap +metadata: + name: ragflow-service-config +data: + {{- with .Values.ragflow.service_conf }} + local.service_conf.yaml: | + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- with .Values.ragflow.llm_factories }} + llm_factories.json: | + {{- . | toPrettyJson | nindent 4 }} + {{- end }} +--- +apiVersion: v1 +kind: ConfigMap metadata: name: nginx-config data: diff --git a/helm/values.yaml b/helm/values.yaml index 396466deb93..4756586f043 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -69,6 +69,30 @@ env: EMBEDDING_BATCH_SIZE: 16 ragflow: + + # Optional service configuration overrides + # to be written to local.service_conf.yaml + # inside the RAGFlow container + # https://ragflow.io/docs/dev/configurations#service-configuration + service_conf: + + # Optional yaml formatted override for the + # llm_factories.json file inside the RAGFlow + # container. + llm_factories: + # factory_llm_infos: + # - name: OpenAI-API-Compatible + # logo: "" + # tags: "LLM,TEXT EMBEDDING,SPEECH2TEXT,MODERATION" + # status: "1" + # llm: + # - llm_name: my-custom-llm + # tags: "LLM,CHAT," + # max_tokens: 100000 + # model_type: chat + # is_tools: false + + # Kubernetes configuration deployment: strategy: resources: From d343cb4deb8f67941197d4f6865854e059c5d650 Mon Sep 17 00:00:00 2001 From: Tuan Le <30828528+tuankg1028@users.noreply.github.com> Date: Wed, 2 Jul 2025 09:02:01 +0700 Subject: [PATCH 0080/1187] Add Google Cloud Vision API Integration (Image2Text) (#8608) ### What problem does this PR solve? This PR introduces Google Cloud Vision API integration to enhance image understanding capabilities in the application. It addresses the need for advanced image description and chat functionalities by implementing a new `GoogleCV` class to handle API interactions and updating relevant configurations. This enables users to leverage Google Cloud Vision for image-to-text tasks, improving the application's ability to process and interpret visual data. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- rag/llm/__init__.py | 4 +- rag/llm/cv_model.py | 191 +++++++++++++++++- .../setting-model/google-modal/index.tsx | 1 + 3 files changed, 194 insertions(+), 2 deletions(-) diff --git a/rag/llm/__init__.py b/rag/llm/__init__.py index 46ef7871f66..b22d722d0bf 100644 --- a/rag/llm/__init__.py +++ b/rag/llm/__init__.py @@ -112,6 +112,7 @@ AnthropicCV, SILICONFLOWCV, GPUStackCV, + GoogleCV, ) from .rerank_model import ( @@ -211,7 +212,8 @@ "Tencent Hunyuan": HunyuanCV, "Anthropic": AnthropicCV, "SILICONFLOW": SILICONFLOWCV, - "GPUStack": GPUStackCV + "GPUStack": GPUStackCV, + "Google Cloud": GoogleCV } ChatModel = { diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index 82640b56f53..8c8f2396366 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -1037,4 +1037,193 @@ def __init__(self, key, model_name, lang="Chinese", base_url=""): base_url = urljoin(base_url, "v1") self.client = OpenAI(api_key=key, base_url=base_url) self.model_name = model_name - self.lang = lang \ No newline at end of file + self.lang = lang + + +class GoogleCV(Base): + def __init__(self, key, model_name, lang="Chinese", base_url=None, **kwargs): + import base64 + + from google.oauth2 import service_account + + key = json.loads(key) + access_token = json.loads(base64.b64decode(key.get("google_service_account_key", ""))) + project_id = key.get("google_project_id", "") + region = key.get("google_region", "") + + scopes = ["https://www.googleapis.com/auth/cloud-platform"] + self.model_name = model_name + self.lang = lang + + if "claude" in self.model_name: + from anthropic import AnthropicVertex + from google.auth.transport.requests import Request + + if access_token: + credits = service_account.Credentials.from_service_account_info(access_token, scopes=scopes) + request = Request() + credits.refresh(request) + token = credits.token + self.client = AnthropicVertex(region=region, project_id=project_id, access_token=token) + else: + self.client = AnthropicVertex(region=region, project_id=project_id) + else: + import vertexai.generative_models as glm + from google.cloud import aiplatform + + if access_token: + credits = service_account.Credentials.from_service_account_info(access_token) + aiplatform.init(credentials=credits, project=project_id, location=region) + else: + aiplatform.init(project=project_id, location=region) + self.client = glm.GenerativeModel(model_name=self.model_name) + + def describe(self, image): + prompt = "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" if self.lang.lower() == "chinese" else \ + "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out." + + if "claude" in self.model_name: + b64 = self.image2base64(image) + vision_prompt = [ + { + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": b64, + }, + }, + { + "type": "text", + "text": prompt + } + ], + } + ] + response = self.client.messages.create( + model=self.model_name, + max_tokens=8192, + messages=vision_prompt + ) + return response.content[0].text.strip(), response.usage.input_tokens + response.usage.output_tokens + else: + import vertexai.generative_models as glm + + b64 = self.image2base64(image) + # Create proper image part for Gemini + image_part = glm.Part.from_data( + data=base64.b64decode(b64), + mime_type="image/jpeg" + ) + input = [prompt, image_part] + res = self.client.generate_content(input) + return res.text, res.usage_metadata.total_token_count + + def describe_with_prompt(self, image, prompt=None): + if "claude" in self.model_name: + b64 = self.image2base64(image) + vision_prompt = [ + { + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": b64, + }, + }, + { + "type": "text", + "text": prompt if prompt else vision_llm_describe_prompt() + } + ], + } + ] + response = self.client.messages.create( + model=self.model_name, + max_tokens=8192, + messages=vision_prompt + ) + return response.content[0].text.strip(), response.usage.input_tokens + response.usage.output_tokens + else: + import vertexai.generative_models as glm + + b64 = self.image2base64(image) + vision_prompt = prompt if prompt else vision_llm_describe_prompt() + # Create proper image part for Gemini + image_part = glm.Part.from_data( + data=base64.b64decode(b64), + mime_type="image/jpeg" + ) + input = [vision_prompt, image_part] + res = self.client.generate_content(input) + return res.text, res.usage_metadata.total_token_count + + def chat(self, system, history, gen_conf, image=""): + if "claude" in self.model_name: + if system: + history[-1]["content"] = system + history[-1]["content"] + "user query: " + history[-1]["content"] + try: + for his in history: + if his["role"] == "user": + his["content"] = [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": image, + }, + }, + { + "type": "text", + "text": his["content"] + } + ] + + response = self.client.messages.create( + model=self.model_name, + max_tokens=8192, + messages=history, + temperature=gen_conf.get("temperature", 0.3), + top_p=gen_conf.get("top_p", 0.7) + ) + return response.content[0].text.strip(), response.usage.input_tokens + response.usage.output_tokens + except Exception as e: + return "**ERROR**: " + str(e), 0 + else: + import vertexai.generative_models as glm + from transformers import GenerationConfig + if system: + history[-1]["content"] = system + history[-1]["content"] + "user query: " + history[-1]["content"] + try: + for his in history: + if his["role"] == "assistant": + his["role"] = "model" + his["parts"] = [his["content"]] + his.pop("content") + if his["role"] == "user": + his["parts"] = [his["content"]] + his.pop("content") + + # Create proper image part for Gemini + img_bytes = base64.b64decode(image) + image_part = glm.Part.from_data( + data=img_bytes, + mime_type="image/jpeg" + ) + history[-1]["parts"].append(image_part) + + response = self.client.generate_content(history, generation_config=GenerationConfig( + temperature=gen_conf.get("temperature", 0.3), + top_p=gen_conf.get("top_p", 0.7))) + + ans = response.text + return ans, response.usage_metadata.total_token_count + except Exception as e: + return "**ERROR**: " + str(e), 0 \ No newline at end of file diff --git a/web/src/pages/user-setting/setting-model/google-modal/index.tsx b/web/src/pages/user-setting/setting-model/google-modal/index.tsx index a404118a5aa..a8795438348 100644 --- a/web/src/pages/user-setting/setting-model/google-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/google-modal/index.tsx @@ -56,6 +56,7 @@ const GoogleModal = ({ > From 695bfe34a2ac8ea3a8bc0452c8851878cca916d4 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 2 Jul 2025 16:45:01 +0800 Subject: [PATCH 0081/1187] fix opendal config 'oss_table' and 'max_allowed_packet' (#8611) ### What problem does this PR solve? Fix the config option name of the opendal table name and setting of 'max_allowed_packet'. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) Signed-off-by: He Wang --- conf/service_conf.yaml | 2 +- rag/utils/opendal_conn.py | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/conf/service_conf.yaml b/conf/service_conf.yaml index 4c0635770c9..7a94a2a1ded 100644 --- a/conf/service_conf.yaml +++ b/conf/service_conf.yaml @@ -62,7 +62,7 @@ redis: # opendal: # scheme: 'mysql' # Storage type, such as s3, oss, azure, etc. # config: -# oss_table: 'your_table_name' +# oss_table: 'opendal_storage' # user_default_llm: # factory: 'Tongyi-Qianwen' # api_key: 'sk-xxxxxxxxxxxxx' diff --git a/rag/utils/opendal_conn.py b/rag/utils/opendal_conn.py index 7a3b44f0d6b..731abf6d6d4 100644 --- a/rag/utils/opendal_conn.py +++ b/rag/utils/opendal_conn.py @@ -1,11 +1,10 @@ import opendal import logging import pymysql -import yaml +from api.utils import get_base_config from rag.utils import singleton -SERVICE_CONF_PATH = "conf/service_conf.yaml" CREATE_TABLE_SQL = """ CREATE TABLE IF NOT EXISTS `{}` ( @@ -20,15 +19,12 @@ """ -def get_opendal_config_from_yaml(yaml_path=SERVICE_CONF_PATH): +def get_opendal_config(): try: - with open(yaml_path, 'r') as f: - config = yaml.safe_load(f) - - opendal_config = config.get('opendal', {}) - kwargs = {} + opendal_config = get_base_config('opendal', {}) if opendal_config.get("scheme") == 'mysql': - mysql_config = config.get('mysql', {}) + mysql_config = get_base_config('mysql', {}) + max_packet = mysql_config.get("max_allowed_packet", 134217728) kwargs = { "scheme": "mysql", "host": mysql_config.get("host", "127.0.0.1"), @@ -36,9 +32,10 @@ def get_opendal_config_from_yaml(yaml_path=SERVICE_CONF_PATH): "user": mysql_config.get("user", "root"), "password": mysql_config.get("password", ""), "database": mysql_config.get("name", "test_open_dal"), - "table": opendal_config.get("config").get("table", "opendal_storage") + "table": opendal_config.get("config").get("oss_table", "opendal_storage"), + "max_allowed_packet": str(max_packet) } - kwargs["connection_string"] = f"mysql://{kwargs['user']}:{kwargs['password']}@{kwargs['host']}:{kwargs['port']}/{kwargs['database']}" + kwargs["connection_string"] = f"mysql://{kwargs['user']}:{kwargs['password']}@{kwargs['host']}:{kwargs['port']}/{kwargs['database']}?max_allowed_packet={max_packet}" else: scheme = opendal_config.get("scheme") config_data = opendal_config.get("config", {}) @@ -53,7 +50,7 @@ def get_opendal_config_from_yaml(yaml_path=SERVICE_CONF_PATH): @singleton class OpenDALStorage: def __init__(self): - self._kwargs = get_opendal_config_from_yaml() + self._kwargs = get_opendal_config() self._scheme = self._kwargs.get('scheme', 'mysql') if self._scheme == 'mysql': self.init_db_config() @@ -95,7 +92,7 @@ def init_db_config(self): ) cursor = conn.cursor() max_packet = self._kwargs.get('max_allowed_packet', 4194304) # Default to 4MB if not specified - cursor.execute(SET_MAX_ALLOWED_PACKET_SQL, (max_packet,)) + cursor.execute(SET_MAX_ALLOWED_PACKET_SQL.format(max_packet)) conn.commit() cursor.close() conn.close() From 040e4ad8a50d074ae99aca6062b8533f16a1903d Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 2 Jul 2025 18:34:21 +0800 Subject: [PATCH 0082/1187] Feat: Convert the arguments parameter of the code operator to a dictionary #3221 (#8623) ### What problem does this PR solve? Feat: Convert the arguments parameter of the code operator to a dictionary #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../components/next-message-item/index.tsx | 18 ++-- web/src/interfaces/database/chat.ts | 2 +- .../agent/canvas/node/iteration-node.tsx | 6 +- web/src/pages/agent/chat/hooks.ts | 17 +++- web/src/pages/agent/chat/input-form.tsx | 86 ------------------- web/src/pages/agent/constant.tsx | 14 ++- .../agent/form/begin-form/use-watch-change.ts | 2 +- .../pages/agent/form/code-form/use-values.ts | 24 +++--- .../agent/form/code-form/use-watch-change.ts | 13 ++- .../prompt-editor/variable-node.tsx | 2 +- .../prompt-editor/variable-picker-plugin.tsx | 4 + 11 files changed, 63 insertions(+), 125 deletions(-) delete mode 100644 web/src/pages/agent/chat/input-form.tsx diff --git a/web/src/components/next-message-item/index.tsx b/web/src/components/next-message-item/index.tsx index bc01010e303..81e9c67e74b 100644 --- a/web/src/components/next-message-item/index.tsx +++ b/web/src/components/next-message-item/index.tsx @@ -130,16 +130,14 @@ const MessageItem = ({ {isAssistant ? ( - index !== 0 && ( - - ) + ) : ( + +
    +
    diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/hooks.ts index 733ad53fe02..7638f7d29e9 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/hooks.ts @@ -22,6 +22,8 @@ import { useParams } from 'umi'; import { v4 as uuid } from 'uuid'; import { BeginId } from '../constant'; import { AgentChatLogContext } from '../context'; +import { transferInputsArrayToObject } from '../form/begin-form/use-watch-change'; +import { useGetBeginNodeDataQuery } from '../hooks/use-get-begin-query'; import { BeginQuery } from '../interface'; import useGraphStore from '../store'; import { receiveMessageError } from '../utils'; @@ -109,6 +111,7 @@ export const useSendNextMessage = () => { const { handleInputChange, value, setValue } = useHandleMessageInputChange(); const { refetch } = useFetchAgent(); const { addEventList } = useContext(AgentChatLogContext); + const getBeginNodeDataQuery = useGetBeginNodeDataQuery(); const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE( api.runCanvas, @@ -191,12 +194,22 @@ export const useSendNextMessage = () => { ); useEffect(() => { - if (prologue) { + const query = getBeginNodeDataQuery(); + if (query.length > 0) { + send({ id: agentId, inputs: transferInputsArrayToObject(query) }); + } else if (prologue) { addNewestOneAnswer({ answer: prologue, }); } - }, [addNewestOneAnswer, prologue]); + }, [ + addNewestOneAnswer, + agentId, + getBeginNodeDataQuery, + prologue, + send, + sendFormMessage, + ]); useEffect(() => { addEventList(answerList); diff --git a/web/src/pages/agent/chat/input-form.tsx b/web/src/pages/agent/chat/input-form.tsx deleted file mode 100644 index b82dbaf84dd..00000000000 --- a/web/src/pages/agent/chat/input-form.tsx +++ /dev/null @@ -1,86 +0,0 @@ -'use client'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useForm } from 'react-hook-form'; -import { toast } from 'sonner'; -import { z } from 'zod'; - -import { Button } from '@/components/ui/button'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { Message } from '@/interfaces/database/chat'; -import { get } from 'lodash'; -import { useParams } from 'umi'; -import { useSendNextMessage } from './hooks'; - -const FormSchema = z.object({ - username: z.string().min(2, { - message: 'Username must be at least 2 characters.', - }), -}); - -type InputFormProps = Pick, 'send'> & { - message: Message; -}; - -export function InputForm({ send, message }: InputFormProps) { - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { - username: '', - }, - }); - - const { id: canvasId } = useParams(); - - function onSubmit(data: z.infer) { - const inputs = get(message, 'data.inputs', {}); - - const nextInputs = Object.entries(inputs).reduce((pre, [key, val]) => { - pre[key] = { ...val, value: data.username }; - - return pre; - }, {}); - - send({ - inputs: nextInputs, - id: canvasId, - }); - - toast('You submitted the following values', { - description: ( -
    -          {JSON.stringify(data, null, 2)}
    -        
    - ), - }); - } - - return ( -
    - - ( - - Username - - - - - - )} - /> - - - - ); -} diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index f069b17cf31..bbb44966fa0 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -661,16 +661,12 @@ export const initialIterationStartValues = { }; export const initialCodeValues = { - lang: 'python', + lang: ProgrammingLanguage.Python, script: CodeTemplateStrMap[ProgrammingLanguage.Python], - arguments: [ - { - name: 'arg1', - }, - { - name: 'arg2', - }, - ], + arguments: { + arg1: '', + arg2: '', + }, }; export const initialWaitingDialogueValues = {}; diff --git a/web/src/pages/agent/form/begin-form/use-watch-change.ts b/web/src/pages/agent/form/begin-form/use-watch-change.ts index 00b40003f6b..f0da58068a1 100644 --- a/web/src/pages/agent/form/begin-form/use-watch-change.ts +++ b/web/src/pages/agent/form/begin-form/use-watch-change.ts @@ -4,7 +4,7 @@ import { UseFormReturn, useWatch } from 'react-hook-form'; import { BeginQuery } from '../../interface'; import useGraphStore from '../../store'; -function transferInputsArrayToObject(inputs: BeginQuery[] = []) { +export function transferInputsArrayToObject(inputs: BeginQuery[] = []) { return inputs.reduce>>((pre, cur) => { pre[cur.key] = omit(cur, 'key'); diff --git a/web/src/pages/agent/form/code-form/use-values.ts b/web/src/pages/agent/form/code-form/use-values.ts index 9dc7d68d4c1..ce8ef1f9b78 100644 --- a/web/src/pages/agent/form/code-form/use-values.ts +++ b/web/src/pages/agent/form/code-form/use-values.ts @@ -1,27 +1,25 @@ -import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { isEmpty } from 'lodash'; import { useMemo } from 'react'; +import { initialCodeValues } from '../../constant'; -export function useValues(node?: RAGFlowNodeType) { - const defaultValues = useMemo( - () => ({ - lang: ProgrammingLanguage.Python, - script: CodeTemplateStrMap[ProgrammingLanguage.Python], - arguments: [], - }), - [], - ); +function convertToArray(args: Record) { + return Object.entries(args).map(([key, value]) => ({ + name: key, + component_id: value, + })); +} +export function useValues(node?: RAGFlowNodeType) { const values = useMemo(() => { const formData = node?.data?.form; if (isEmpty(formData)) { - return defaultValues; + return initialCodeValues; } - return formData; - }, [defaultValues, node?.data?.form]); + return { ...formData, arguments: convertToArray(formData.arguments) }; + }, [node?.data?.form]); return values; } diff --git a/web/src/pages/agent/form/code-form/use-watch-change.ts b/web/src/pages/agent/form/code-form/use-watch-change.ts index 8f882b0a3f7..6ce0187afb8 100644 --- a/web/src/pages/agent/form/code-form/use-watch-change.ts +++ b/web/src/pages/agent/form/code-form/use-watch-change.ts @@ -3,6 +3,14 @@ import { useCallback, useEffect } from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; import useGraphStore from '../../store'; +function convertToObject(list: Array<{ name: string; component_id: string }>) { + return list.reduce>((pre, cur) => { + pre[cur.name] = cur.component_id; + + return pre; + }, {}); +} + export function useWatchFormChange(id?: string, form?: UseFormReturn) { let values = useWatch({ control: form?.control }); const updateNodeForm = useGraphStore((state) => state.updateNodeForm); @@ -11,7 +19,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { // Manually triggered form updates are synchronized to the canvas if (id) { values = form?.getValues() || {}; - let nextValues: any = values; + let nextValues: any = { + ...values, + arguments: convertToObject(values.arguments), + }; updateNodeForm(id, nextValues); } diff --git a/web/src/pages/agent/form/components/prompt-editor/variable-node.tsx b/web/src/pages/agent/form/components/prompt-editor/variable-node.tsx index e2a8cc29f93..d8947727bf0 100644 --- a/web/src/pages/agent/form/components/prompt-editor/variable-node.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/variable-node.tsx @@ -37,7 +37,7 @@ export class VariableNode extends DecoratorNode { let content: ReactNode = ( {this.__label} ); - if (this.__value.startsWith(prefix)) { + if (this.__value?.startsWith(prefix)) { content = (
    {i18n.t(`flow.begin`)} / {content} diff --git a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx index 8797f2236ba..6f0ca8e675b 100644 --- a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx @@ -220,6 +220,10 @@ export default function VariablePickerMenuPlugin({ } $getRoot().clear().append(paragraph); + + if ($isRangeSelection($getSelection())) { + $getRoot().selectEnd(); + } }, [findLabelByValue], ); From 56e6f37ffa7923e83c45812de45079b88733c2c0 Mon Sep 17 00:00:00 2001 From: huansinho Date: Wed, 2 Jul 2025 18:34:38 +0800 Subject: [PATCH 0083/1187] Update Chrome download URL in use_china_mirrors configuration (#8628) ### What problem does this PR solve? Update Chrome download URL in use_china_mirrors configuration ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) Co-authored-by: lqh --- download_deps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/download_deps.py b/download_deps.py index 058cf258b03..0efce81313a 100644 --- a/download_deps.py +++ b/download_deps.py @@ -25,8 +25,8 @@ def get_urls(use_china_mirrors=False) -> Union[str, list[str]]: "https://repo.huaweicloud.com/repository/maven/org/apache/tika/tika-server-standard/3.0.0/tika-server-standard-3.0.0.jar", "https://repo.huaweicloud.com/repository/maven/org/apache/tika/tika-server-standard/3.0.0/tika-server-standard-3.0.0.jar.md5", "https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken", - ["https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.85/linux64/chrome-linux64.zip", "chrome-linux64-121-0-6167-85"], - ["https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.85/linux64/chromedriver-linux64.zip", "chromedriver-linux64-121-0-6167-85"], + ["https://registry.npmmirror.com/-/binary/chrome-for-testing/121.0.6167.85/linux64/chrome-linux64.zip", "chrome-linux64-121-0-6167-85"], + ["https://registry.npmmirror.com/-/binary/chrome-for-testing/121.0.6167.85/linux64/chromedriver-linux64.zip", "chromedriver-linux64-121-0-6167-85"], ] else: return [ From 898da23caad3bc1d877444492a7d4db50525fefe Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 2 Jul 2025 18:35:16 +0800 Subject: [PATCH 0084/1187] make dirs with 'exist_ok=True' (#8629) ### What problem does this PR solve? The following error occurred during local testing, which should be fixed by configuring 'exist_ok=True'. ```log set_progress(7461edc2535c11f0a2aa0242c0a82009), progress: -1, progress_msg: 21:41:41 Page(1~100000001): [ERROR][Errno 17] File exists: '/ragflow/tmp' ``` ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/cv_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index 8c8f2396366..da7efaef18e 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -240,7 +240,7 @@ def prompt(self, binary): # stupid as hell tmp_dir = get_project_base_directory("tmp") if not os.path.exists(tmp_dir): - os.mkdir(tmp_dir) + os.makedirs(tmp_dir, exist_ok=True) path = os.path.join(tmp_dir, "%s.jpg" % get_uuid()) Image.open(io.BytesIO(binary)).save(path) return [ @@ -262,7 +262,7 @@ def vision_llm_prompt(self, binary, prompt=None): # stupid as hell tmp_dir = get_project_base_directory("tmp") if not os.path.exists(tmp_dir): - os.mkdir(tmp_dir) + os.makedirs(tmp_dir, exist_ok=True) path = os.path.join(tmp_dir, "%s.jpg" % get_uuid()) Image.open(io.BytesIO(binary)).save(path) From fffb7c0bba8ff803e0710ad2155b0a8c216fabd7 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Wed, 2 Jul 2025 18:37:34 +0800 Subject: [PATCH 0085/1187] Fix: anthropic llm issue. (#8633) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/settings.py | 2 +- conf/llm_factories.json | 12 --- rag/llm/chat_model.py | 79 ++----------------- ...{opensearch_coon.py => opensearch_conn.py} | 0 4 files changed, 7 insertions(+), 86 deletions(-) rename rag/utils/{opensearch_coon.py => opensearch_conn.py} (100%) diff --git a/api/settings.py b/api/settings.py index 22e9d03f461..f5fd80fb1de 100644 --- a/api/settings.py +++ b/api/settings.py @@ -22,7 +22,7 @@ import rag.utils import rag.utils.es_conn import rag.utils.infinity_conn -import rag.utils.opensearch_coon +import rag.utils.opensearch_conn from api.constants import RAG_FLOW_SERVICE_NAME from api.utils import decrypt_database_config, get_base_config from api.utils.file_utils import get_project_base_directory diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 52e9e3ff234..339c537a6e1 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -3180,18 +3180,6 @@ "max_tokens": 204800, "model_type": "image2text", "is_tools": true - }, - { - "llm_name": "claude-2.1", - "tags": "LLM,CHAT,200k", - "max_tokens": 204800, - "model_type": "chat" - }, - { - "llm_name": "claude-2.0", - "tags": "LLM,CHAT,100k", - "max_tokens": 102400, - "model_type": "chat" } ] }, diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index c2120cfca4d..020815104e1 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -558,7 +558,9 @@ def chat_streamly(self, system, history, gen_conf): class QWenChat(Base): def __init__(self, key, model_name=Generation.Models.qwen_turbo, base_url=None, **kwargs): - super().__init__(key, model_name, base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", **kwargs) + if not base_url: + base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1" + super().__init__(key, model_name, base_url=base_url, **kwargs) return @@ -1442,80 +1444,11 @@ def chat_streamly(self, system, history, gen_conf): class AnthropicChat(Base): - def __init__(self, key, model_name, base_url=None, **kwargs): + def __init__(self, key, model_name, base_url="https://api.anthropic.com/v1/", **kwargs): + if not base_url: + base_url = "https://api.anthropic.com/v1/" super().__init__(key, model_name, base_url=base_url, **kwargs) - import anthropic - - self.client = anthropic.Anthropic(api_key=key) - self.model_name = model_name - - def _clean_conf(self, gen_conf): - if "presence_penalty" in gen_conf: - del gen_conf["presence_penalty"] - if "frequency_penalty" in gen_conf: - del gen_conf["frequency_penalty"] - gen_conf["max_tokens"] = 8192 - if "haiku" in self.model_name or "opus" in self.model_name: - gen_conf["max_tokens"] = 4096 - return gen_conf - - def _chat(self, history, gen_conf): - system = history[0]["content"] if history and history[0]["role"] == "system" else "" - response = self.client.messages.create( - model=self.model_name, - messages=[h for h in history if h["role"] != "system"], - system=system, - stream=False, - **gen_conf, - ).to_dict() - ans = response["content"][0]["text"] - if response["stop_reason"] == "max_tokens": - ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" - return ( - ans, - response["usage"]["input_tokens"] + response["usage"]["output_tokens"], - ) - - def chat_streamly(self, system, history, gen_conf): - if "presence_penalty" in gen_conf: - del gen_conf["presence_penalty"] - if "frequency_penalty" in gen_conf: - del gen_conf["frequency_penalty"] - gen_conf["max_tokens"] = 8192 - if "haiku" in self.model_name or "opus" in self.model_name: - gen_conf["max_tokens"] = 4096 - - ans = "" - total_tokens = 0 - reasoning_start = False - try: - response = self.client.messages.create( - model=self.model_name, - messages=history, - system=system, - stream=True, - **gen_conf, - ) - for res in response: - if res.type == "content_block_delta": - if res.delta.type == "thinking_delta" and res.delta.thinking: - ans = "" - if not reasoning_start: - reasoning_start = True - ans = "" - ans += res.delta.thinking + "" - else: - reasoning_start = False - text = res.delta.text - ans = text - total_tokens += num_tokens_from_string(text) - yield ans - except Exception as e: - yield ans + "\n**ERROR**: " + str(e) - - yield total_tokens - class GoogleChat(Base): def __init__(self, key, model_name, base_url=None, **kwargs): diff --git a/rag/utils/opensearch_coon.py b/rag/utils/opensearch_conn.py similarity index 100% rename from rag/utils/opensearch_coon.py rename to rag/utils/opensearch_conn.py From 62b63acbb57dbce6955e3179b1a2b825afc91ee6 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 2 Jul 2025 18:37:54 +0800 Subject: [PATCH 0086/1187] Refa: more robust mcp tool call (#8631) ### What problem does this PR solve? More robust MCP tool call conn. ### Type of change - [x] Refactoring --- rag/utils/mcp_tool_call_conn.py | 99 ++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/rag/utils/mcp_tool_call_conn.py b/rag/utils/mcp_tool_call_conn.py index 5aca0e75449..5e449477f13 100644 --- a/rag/utils/mcp_tool_call_conn.py +++ b/rag/utils/mcp_tool_call_conn.py @@ -65,32 +65,42 @@ async def _mcp_server_loop(self) -> None: if self._mcp_server.server_type == MCPServerType.SSE: # SSE transport - async with sse_client(url, headers) as stream: - async with ClientSession(*stream) as client_session: - try: - await asyncio.wait_for(client_session.initialize(), timeout=5) - logging.info("client_session initialized successfully") - except asyncio.TimeoutError: - logging.error(f"Timeout initializing client_session for server {self._mcp_server.id}") - return - await self._process_mcp_tasks(client_session) + try: + async with sse_client(url, headers) as stream: + async with ClientSession(*stream) as client_session: + try: + await asyncio.wait_for(client_session.initialize(), timeout=5) + logging.info("client_session initialized successfully") + await self._process_mcp_tasks(client_session) + except asyncio.TimeoutError: + msg = f"Timeout initializing client_session for server {self._mcp_server.id}" + logging.error(msg) + await self._process_mcp_tasks(None, msg) + except Exception: + msg = "Connection failed (possibly due to auth error). Please check authentication settings first" + await self._process_mcp_tasks(None, msg) elif self._mcp_server.server_type == MCPServerType.STREAMABLE_HTTP: # Streamable HTTP transport - async with streamablehttp_client(url, headers) as (read_stream, write_stream, _): - async with ClientSession(read_stream, write_stream) as client_session: - try: - await asyncio.wait_for(client_session.initialize(), timeout=5) - logging.info("client_session initialized successfully") - except asyncio.TimeoutError: - logging.error(f"Timeout initializing client_session for server {self._mcp_server.id}") - return - await asyncio.wait_for(client_session.initialize(), timeout=5) - await self._process_mcp_tasks(client_session) + try: + async with streamablehttp_client(url, headers) as (read_stream, write_stream, _): + async with ClientSession(read_stream, write_stream) as client_session: + try: + await asyncio.wait_for(client_session.initialize(), timeout=5) + logging.info("client_session initialized successfully") + await self._process_mcp_tasks(client_session) + except asyncio.TimeoutError: + msg = f"Timeout initializing client_session for server {self._mcp_server.id}" + logging.error(msg) + await self._process_mcp_tasks(None, msg) + except Exception: + msg = "Connection failed (possibly due to auth error). Please check authentication settings first" + await self._process_mcp_tasks(None, msg) + else: - raise ValueError(f"Unsupported MCP server type {self._mcp_server.server_type} id {self._mcp_server.id}") + await self._process_mcp_tasks(None, f"Unsupported MCP server type: {self._mcp_server.server_type}, id: {self._mcp_server.id}") - async def _process_mcp_tasks(self, client_session: ClientSession) -> None: + async def _process_mcp_tasks(self, client_session: ClientSession | None, error_message: str | None = None) -> None: while not self._close: try: mcp_task, arguments, result_queue = await asyncio.wait_for(self._queue.get(), timeout=1) @@ -100,6 +110,12 @@ async def _process_mcp_tasks(self, client_session: ClientSession) -> None: logging.debug(f"Got MCP task {mcp_task} arguments {arguments}") r: Any = None + + if not client_session or error_message: + r = ValueError(error_message) + await result_queue.put(r) + continue + try: if mcp_task == "list_tools": r = await client_session.list_tools() @@ -112,21 +128,22 @@ async def _process_mcp_tasks(self, client_session: ClientSession) -> None: await result_queue.put(r) - async def _call_mcp_server(self, task_type: MCPTaskType, timeout: float = 8, **kwargs) -> Any: + async def _call_mcp_server(self, task_type: MCPTaskType, timeout: float | int = 8, **kwargs) -> Any: results = asyncio.Queue() await self._queue.put((task_type, kwargs, results)) + try: result: CallToolResult | Exception = await asyncio.wait_for(results.get(), timeout=timeout) + if isinstance(result, Exception): + raise result + return result except asyncio.TimeoutError: raise asyncio.TimeoutError(f"MCP task '{task_type}' timeout after {timeout}s") + except Exception: + raise - if isinstance(result, Exception): - raise result - - return result - - async def _call_mcp_tool(self, name: str, arguments: dict[str, Any]) -> str: - result: CallToolResult = await self._call_mcp_server("tool_call", name=name, arguments=arguments) + async def _call_mcp_tool(self, name: str, arguments: dict[str, Any], timeout: float | int = 10) -> str: + result: CallToolResult = await self._call_mcp_server("tool_call", name=name, arguments=arguments, timeout=timeout) if result.isError: return f"MCP server error: {result.content}" @@ -137,23 +154,27 @@ async def _call_mcp_tool(self, name: str, arguments: dict[str, Any]) -> str: else: return f"Unsupported content type {type(result.content)}" - async def _get_tools_from_mcp_server(self) -> list[Tool]: - result: ListToolsResult = await self._call_mcp_server("list_tools") - return result.tools + async def _get_tools_from_mcp_server(self, timeout: float | int = 8) -> list[Tool]: + try: + result: ListToolsResult = await self._call_mcp_server("list_tools", timeout=timeout) + return result.tools + except Exception: + raise - def get_tools(self, timeout: float = 10) -> list[Tool]: - future = asyncio.run_coroutine_threadsafe(self._get_tools_from_mcp_server(), self._event_loop) + def get_tools(self, timeout: float | int = 10) -> list[Tool]: + future = asyncio.run_coroutine_threadsafe(self._get_tools_from_mcp_server(timeout=timeout), self._event_loop) try: return future.result(timeout=timeout) except FuturesTimeoutError: - logging.error(f"Timeout when fetching tools from MCP server: {self._mcp_server.id} (timeout={timeout})") - return [] + msg = f"Timeout when fetching tools from MCP server: {self._mcp_server.id} (timeout={timeout})" + logging.error(msg) + raise RuntimeError(msg) except Exception: logging.exception(f"Error fetching tools from MCP server: {self._mcp_server.id}") - return [] + raise @override - def tool_call(self, name: str, arguments: dict[str, Any], timeout: float = 10) -> str: + def tool_call(self, name: str, arguments: dict[str, Any], timeout: float | int = 10) -> str: future = asyncio.run_coroutine_threadsafe(self._call_mcp_tool(name, arguments), self._event_loop) try: return future.result(timeout=timeout) @@ -173,7 +194,7 @@ async def close(self) -> None: self._thread_pool.shutdown(wait=True) self.__class__._ALL_INSTANCES.discard(self) - def close_sync(self, timeout: float = 5) -> None: + def close_sync(self, timeout: float | int = 5) -> None: if not self._event_loop.is_running(): logging.warning(f"Event loop already stopped for {self._mcp_server.id}") return From 83c8af1b59350dd6b74cd86e559001bddae7c3c7 Mon Sep 17 00:00:00 2001 From: Can Wang Date: Wed, 2 Jul 2025 18:38:48 +0800 Subject: [PATCH 0087/1187] Fix: page_size can be None error (#8603) ### What problem does this PR solve? Issue #8602 `parser_config.task_page_size` can be defaults to `None` when dataset is created by API. This was not handled by the `task_executor.py` code thus `page_size` could sometimes be `None` which will cause issue in line 351. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/db/services/task_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/db/services/task_service.py b/api/db/services/task_service.py index 1fdfed35025..401489a6946 100644 --- a/api/db/services/task_service.py +++ b/api/db/services/task_service.py @@ -338,9 +338,9 @@ def new_task(): file_bin = STORAGE_IMPL.get(bucket, name) do_layout = doc["parser_config"].get("layout_recognize", "DeepDOC") pages = PdfParser.total_page_number(doc["name"], file_bin) - page_size = doc["parser_config"].get("task_page_size", 12) + page_size = doc["parser_config"].get("task_page_size") or 12 if doc["parser_id"] == "paper": - page_size = doc["parser_config"].get("task_page_size", 22) + page_size = doc["parser_config"].get("task_page_size") or 22 if doc["parser_id"] in ["one", "knowledge_graph"] or do_layout != "DeepDOC": page_size = 10 ** 9 page_ranges = doc["parser_config"].get("pages") or [(1, 10 ** 5)] From 140d4f0d303c8fe1f6020bef4325da29d272cd73 Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Wed, 2 Jul 2025 18:39:11 +0800 Subject: [PATCH 0088/1187] Minor: fixed broken links. (#8636) ### What problem does this PR solve? ### Type of change - [x] Documentation Update --- docs/references/http_api_reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index 2ff65e2eed4..1cca27c0e3a 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -3228,7 +3228,7 @@ Generates five to ten alternative question strings from the user's original quer This operation requires a `Bearer Login Token`, which typically expires with in 24 hours. You can find the it in the Request Headers in your browser easily as shown below: -![Image](https://github.com/user-attachments/assets/17a5a0fe-e411-4e35-8251-85c71444468b) +![Image](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/login_token.jpg) :::tip NOTE The chat model autonomously determines the number of questions to generate based on the instruction, typically between five and ten. From 4243330d5c631496916fca8d60f43f9ae8c517d3 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 2 Jul 2025 18:52:24 +0800 Subject: [PATCH 0089/1187] Feat: add MCP server test endpoint (#8632) ### What problem does this PR solve? Add MCP server test endpoint. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/mcp_server_app.py | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/api/apps/mcp_server_app.py b/api/apps/mcp_server_app.py index fb81d51e0d9..c01c6377719 100644 --- a/api/apps/mcp_server_app.py +++ b/api/apps/mcp_server_app.py @@ -249,8 +249,9 @@ def list_tools() -> Response: try: tools = tool_call_session.get_tools(timeout) - except Exception: + except Exception as e: tools = [] + return get_data_error_result(message=f"MCP list tools error: {e}") results[server_key] = [] for tool in tools: @@ -322,3 +323,45 @@ def cache_tool() -> Response: return get_data_error_result(message="Failed to updated MCP server.") return get_json_result(data=tools) + + +@manager.route("/test_mcp", methods=["POST"]) # noqa: F821 +@validate_request("url", "server_type") +def test_mcp() -> Response: + req = request.get_json() + + url = req.get("url", "") + if not url: + return get_data_error_result(message="Invaild MCP url.") + + server_type = req.get("server_type", "") + if server_type not in VALID_MCP_SERVER_TYPES: + return get_data_error_result(message="Unsupported MCP server type.") + + timeout = get_float(req, "timeout", 10) + headers = safe_json_parse(req.get("headers", {})) + variables = safe_json_parse(req.get("variables", {})) + + mcp_server = MCPServer(id=f"{server_type}: {url}", server_type=server_type, url=url, headers=headers, variables=variables) + + result = [] + try: + tool_call_session = MCPToolCallSession(mcp_server, mcp_server.variables) + + try: + tools = tool_call_session.get_tools(timeout) + except Exception as e: + tools = [] + return get_data_error_result(message=f"Test MCP error: {e}") + finally: + # PERF: blocking call to close sessions — consider moving to background thread or task queue + close_multiple_mcp_toolcall_sessions([tool_call_session]) + + for tool in tools: + tool_dict = tool.model_dump() + tool_dict["enabled"] = True + result.append(tool_dict) + + return get_json_result(data=result) + except Exception as e: + return server_error_response(e) From 747da87a1e05a242925ef8f51d4a2d870606b6dd Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 2 Jul 2025 19:21:40 +0800 Subject: [PATCH 0090/1187] Feat: Combine the output logs of the same operator together #3221 (#8638) ### What problem does this PR solve? Feat: Combine the output logs of the same operator together #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/hooks/use-send-message.ts | 14 +++++ web/src/pages/agent/log-sheet/index.tsx | 82 +++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/web/src/hooks/use-send-message.ts b/web/src/hooks/use-send-message.ts index 35dd81a8ed6..bfa7cca912a 100644 --- a/web/src/hooks/use-send-message.ts +++ b/web/src/hooks/use-send-message.ts @@ -13,6 +13,7 @@ export enum MessageEventType { MessageEnd = 'message_end', WorkflowFinished = 'workflow_finished', UserInputs = 'user_inputs', + NodeLogs = 'node_logs', } export interface IAnswerEvent { @@ -42,12 +43,25 @@ export interface IMessageData { content: string; } +export interface ILogData extends INodeData { + logs: { + name: string; + result: string; + args: { + query: string; + topic: string; + }; + }; +} + export type INodeEvent = IAnswerEvent; export type IMessageEvent = IAnswerEvent; export type IInputEvent = IAnswerEvent; +export type ILogEvent = IAnswerEvent; + export type IChatEvent = INodeEvent | IMessageEvent; export type IEventList = Array; diff --git a/web/src/pages/agent/log-sheet/index.tsx b/web/src/pages/agent/log-sheet/index.tsx index 9eafa230227..0e2903b2e42 100644 --- a/web/src/pages/agent/log-sheet/index.tsx +++ b/web/src/pages/agent/log-sheet/index.tsx @@ -18,9 +18,14 @@ import { SheetHeader, SheetTitle, } from '@/components/ui/sheet'; -import { INodeEvent, MessageEventType } from '@/hooks/use-send-message'; +import { + ILogData, + ILogEvent, + MessageEventType, +} from '@/hooks/use-send-message'; import { IModalProps } from '@/interfaces/common'; import { cn } from '@/lib/utils'; +import { isEmpty } from 'lodash'; import { BellElectric, NotebookText } from 'lucide-react'; import { useCallback, useMemo } from 'react'; import JsonView from 'react18-json-view'; @@ -51,6 +56,25 @@ function JsonViewer({ ); } +function concatData( + firstRecord: Record | Array>, + nextRecord: Record | Array>, +) { + let result: Array> = []; + + if (!isEmpty(firstRecord)) { + result = result.concat(firstRecord); + } + + if (!isEmpty(nextRecord)) { + result = result.concat(nextRecord); + } + + return isEmpty(result) ? {} : result; +} + +type EventWithIndex = { startNodeIdx: number } & ILogEvent; + export function LogSheet({ hideModal, currentEventListWithoutMessage, @@ -64,12 +88,51 @@ export function LogSheet({ [getNode], ); + // Look up to find the nearest start component id and concatenate the finish and log data into one const finishedNodeList = useMemo(() => { return currentEventListWithoutMessage.filter( - (x) => x.event === MessageEventType.NodeFinished, - ) as INodeEvent[]; + (x) => + x.event === MessageEventType.NodeFinished || + x.event === MessageEventType.NodeLogs, + ) as ILogEvent[]; }, [currentEventListWithoutMessage]); - console.log('🚀 ~ finishedNodeList ~ finishedNodeList:', finishedNodeList); + + const nextList = useMemo(() => { + return finishedNodeList.reduce>((pre, cur) => { + const startNodeIdx = ( + currentEventListWithoutMessage as Array + ).findLastIndex( + (x) => + x.data.component_id === cur.data.component_id && + x.event === MessageEventType.NodeStarted, + ); + + const item = pre.find((x) => x.startNodeIdx === startNodeIdx); + + const { logs = {}, inputs = {}, outputs = {} } = cur.data; + if (item) { + const { + inputs: inputList, + outputs: outputList, + logs: logList, + } = item.data; + + item.data = { + ...item.data, + inputs: concatData(inputList, inputs), + outputs: concatData(outputList, outputs), + logs: concatData(logList, logs), + }; + } else { + pre.push({ + ...cur, + startNodeIdx, + }); + } + + return pre; + }, []); + }, [currentEventListWithoutMessage, finishedNodeList]); return ( @@ -82,7 +145,7 @@ export function LogSheet({
    - {finishedNodeList.map((x, idx) => ( + {nextList.map((x, idx) => ( + {isEmpty((x.data as ILogData)?.logs) || ( + + )} +
    From 1dd18f95e95064855f1c0d5359090d3c986f924f Mon Sep 17 00:00:00 2001 From: dcc123456 <1243304602@qq.com> Date: Thu, 3 Jul 2025 13:31:22 +0800 Subject: [PATCH 0091/1187] Optimize the style and logic of the profile (#8639) ### What problem does this PR solve? Optimize the style and logic of the profile [#3221 ](https://github.com/infiniflow/ragflow/issues/3221) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/locales/en.ts | 2 + web/src/locales/zh-traditional.ts | 2 + web/src/locales/zh.ts | 2 + .../pages/profile-setting/profile/index.tsx | 394 ++++++++++-------- 4 files changed, 227 insertions(+), 173 deletions(-) diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 63fa4516369..cbb3655d76b 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -552,6 +552,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s setting: { profile: 'Profile', avatar: 'Avatar', + avatarTip: 'This will be displayed on your profile.', profileDescription: 'Update your photo and personal details here.', maxTokens: 'Max Tokens', maxTokensMessage: 'Max Tokens is required', @@ -584,6 +585,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s currentPassword: 'Current password', currentPasswordMessage: 'Please input your password!', newPassword: 'New password', + changePassword: 'Change Password', newPasswordMessage: 'Please input your password!', newPasswordDescription: 'Your new password must be more than 8 characters.', diff --git a/web/src/locales/zh-traditional.ts b/web/src/locales/zh-traditional.ts index 923dc1c0e25..da38bdef344 100644 --- a/web/src/locales/zh-traditional.ts +++ b/web/src/locales/zh-traditional.ts @@ -535,6 +535,7 @@ export default { setting: { profile: '概述', avatar: '头像', + avatarTip: '這會在你的個人主頁展示', profileDescription: '在此更新您的照片和個人詳細信息。', maxTokens: '最大token數', maxTokensMessage: '最大token數是必填項', @@ -567,6 +568,7 @@ export default { currentPassword: '當前密碼', currentPasswordMessage: '請輸入當前密碼', newPassword: '新密碼', + changePassword: '修改密碼', newPasswordMessage: '請輸入新密碼', newPasswordDescription: '您的新密碼必須超過 8 個字符。', confirmPassword: '確認新密碼', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 2bbb025c154..c9ebfdb42d2 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -556,6 +556,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 setting: { profile: '概要', avatar: '头像', + avatarTip: '这会在你的个人主页展示', profileDescription: '在此更新您的照片和个人详细信息。', maxTokens: '最大token数', maxTokensMessage: '最大token数是必填项', @@ -588,6 +589,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 currentPassword: '当前密码', currentPasswordMessage: '请输入当前密码', newPassword: '新密码', + changePassword: '修改密码', newPasswordMessage: '请输入新密码', newPasswordDescription: '您的新密码必须超过 8 个字符。', confirmPassword: '确认新密码', diff --git a/web/src/pages/profile-setting/profile/index.tsx b/web/src/pages/profile-setting/profile/index.tsx index 7d741663702..ca3a891ab3f 100644 --- a/web/src/pages/profile-setting/profile/index.tsx +++ b/web/src/pages/profile-setting/profile/index.tsx @@ -28,68 +28,71 @@ import { Loader2Icon, Pencil, Upload } from 'lucide-react'; import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +function defineSchema( + t: TFunction<'translation', string>, + showPasswordForm = false, +) { + const baseSchema = z.object({ + userName: z + .string() + .min(1, { message: t('usernameMessage') }) + .trim(), + avatarUrl: z.string().trim(), + timeZone: z + .string() + .trim() + .min(1, { message: t('timezonePlaceholder') }), + email: z + .string({ required_error: 'Please select an email to display.' }) + .trim() + .regex(/^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, { + message: 'Enter a valid email address.', + }), + }); -function defineSchema(t: TFunction<'translation', string>) { - return z - .object({ - userName: z - .string() - .min(1, { - message: t('usernameMessage'), - }) - .trim(), - avatarUrl: z.string().trim(), - timeZone: z - .string() - .trim() - .min(1, { - message: t('timezonePlaceholder'), - }), - email: z - .string({ - required_error: 'Please select an email to display.', - }) - .trim() - .regex( - /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, - { - message: 'Enter a valid email address.', - }, - ), - currPasswd: z - .string() - .trim() - .min(1, { - message: t('currentPasswordMessage'), - }), - newPasswd: z - .string() - .trim() - .min(8, { - message: t('confirmPasswordMessage'), - }), - confirmPasswd: z - .string() - .trim() - .min(8, { - message: t('newPasswordDescription'), - }), - }) - .refine((data) => data.newPasswd === data.confirmPasswd, { - message: t('confirmPasswordNonMatchMessage'), - path: ['confirmPasswd'], - }); -} + if (showPasswordForm) { + return baseSchema + .extend({ + currPasswd: z + .string({ + required_error: t('currentPasswordMessage'), + }) + .trim() + .min(1, { message: t('currentPasswordMessage') }), + newPasswd: z + .string({ + required_error: t('confirmPasswordMessage'), + }) + .trim() + .min(8, { message: t('confirmPasswordMessage') }), + confirmPasswd: z + .string({ + required_error: t('newPasswordDescription'), + }) + .trim() + .min(8, { message: t('newPasswordDescription') }), + }) + .refine((data) => data.newPasswd === data.confirmPasswd, { + message: t('confirmPasswordNonMatchMessage'), + path: ['confirmPasswd'], + }); + } + return baseSchema; +} export default function Profile() { const [avatarFile, setAvatarFile] = useState(null); const [avatarBase64Str, setAvatarBase64Str] = useState(''); // Avatar Image base64 const { data: userInfo } = useFetchUserInfo(); - const { saveSetting, loading: submitLoading } = useSaveSetting(); + const { + saveSetting, + loading: submitLoading, + data: saveUserData, + } = useSaveSetting(); const { t } = useTranslate('setting'); - const FormSchema = defineSchema(t); - + const [showPasswordForm, setShowPasswordForm] = useState(false); + const FormSchema = defineSchema(t, showPasswordForm); const form = useForm>({ resolver: zodResolver(FormSchema), defaultValues: { @@ -97,10 +100,11 @@ export default function Profile() { avatarUrl: '', timeZone: '', email: '', - currPasswd: '', - newPasswd: '', - confirmPasswd: '', + // currPasswd: '', + // newPasswd: '', + // confirmPasswd: '', }, + shouldUnregister: true, }); useEffect(() => { @@ -108,10 +112,20 @@ export default function Profile() { form.setValue('email', userInfo?.email); // email form.setValue('userName', userInfo?.nickname); // nickname form.setValue('timeZone', userInfo?.timezone); // time zone - form.setValue('currPasswd', ''); // current password + // form.setValue('currPasswd', ''); // current password setAvatarBase64Str(userInfo?.avatar ?? ''); }, [userInfo]); + useEffect(() => { + if (saveUserData === 0) { + setShowPasswordForm(false); + form.resetField('currPasswd'); + form.resetField('newPasswd'); + form.resetField('confirmPasswd'); + } + console.log('saveUserData', saveUserData); + }, [saveUserData]); + useEffect(() => { if (avatarFile) { // make use of img compression transformFile2Base64 @@ -122,24 +136,34 @@ export default function Profile() { }, [avatarFile]); function onSubmit(data: z.infer) { - // toast('You submitted the following values', { - // description: ( - //
    -    //       {JSON.stringify(data, null, 2)}
    -    //     
    - // ), - // }); - // console.log('data=', data); - // final submit form - saveSetting({ + const payload: Partial<{ + nickname: string; + password: string; + new_password: string; + avatar: string; + timezone: string; + }> = { nickname: data.userName, - password: rsaPsw(data.currPasswd) as string, - new_password: rsaPsw(data.newPasswd) as string, avatar: avatarBase64Str, timezone: data.timeZone, - }); + }; + + if (showPasswordForm && 'currPasswd' in data && 'newPasswd' in data) { + payload.password = rsaPsw(data.currPasswd!) as string; + payload.new_password = rsaPsw(data.newPasswd!) as string; + } + saveSetting(payload); } + useEffect(() => { + if (showPasswordForm) { + form.register('currPasswd'); + form.register('newPasswd'); + form.register('confirmPasswd'); + } else { + form.unregister(['currPasswd', 'newPasswd', 'confirmPasswd']); + } + }, [showPasswordForm]); return (

    {t('profile')}

    @@ -152,12 +176,13 @@ export default function Profile() { onSubmit={form.handleSubmit(onSubmit)} className="block space-y-6" > + {/* Username Field */} ( -
    +
    * {t('username')} @@ -170,24 +195,26 @@ export default function Profile() { />
    -
    +
    )} /> + + {/* Avatar Field */} ( -
    +
    Avatar - <> +
    {!avatarBase64Str ? (
    @@ -198,18 +225,18 @@ export default function Profile() {
    ) : (
    - + - +
    @@ -234,22 +261,27 @@ export default function Profile() { }} />
    - +
    + {t('avatarTip')} +
    +
    -
    +
    )} /> + + {/* Time Zone Field */} ( -
    +
    * {t('timezone')} @@ -269,37 +301,35 @@ export default function Profile() {
    -
    +
    )} /> + + {/* Email Address Field */} (
    -
    +
    {t('email')} - + <>{field.value}
    -
    +
    -
    +

     

    {t('emailDescription')} @@ -308,92 +338,110 @@ export default function Profile() {

    )} /> -
    + + {/* Password Section */}
    -

    {t('password')}

    +
    +

    {t('password')}

    + +
    {t('passwordDescription')}
    -
    - -
    - ( - -
    - - * - {t('currentPassword')} - - - - -
    -
    -
    - -
    -
    - )} - /> - ( - -
    - - * - {t('newPassword')} - - - - -
    -
    -
    - -
    -
    - )} - /> - ( - -
    - - * - {t('confirmPassword')} - - - { - form.trigger('confirmPasswd'); - }} - onChange={(ev) => { - form.setValue( - 'confirmPasswd', - ev.target.value.trim(), - ); - }} - /> - -
    -
    -
     
    - -
    -
    - )} - /> -
    - + {/* Password Form */} + {showPasswordForm && ( + <> + ( + +
    + + * + {t('currentPassword')} + + + + +
    +
    +
    + +
    +
    + )} + /> + ( + +
    + + * + {t('newPassword')} + + + + +
    +
    +
    + +
    +
    + )} + /> + ( + +
    + + * + {t('confirmPassword')} + + + { + form.trigger('confirmPasswd'); + }} + onChange={(ev) => { + form.setValue( + 'confirmPasswd', + ev.target.value.trim(), + ); + }} + /> + +
    +
    +
    +   +
    + +
    +
    + )} + /> + + )} +
    +
    + ); } diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index bbb44966fa0..022a2742b05 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -667,6 +667,7 @@ export const initialCodeValues = { arg1: '', arg2: '', }, + outputs: { result: { value: '', type: 'string' } }, }; export const initialWaitingDialogueValues = {}; From a4d97dcf121bab8baf38787a407065148ad5a376 Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 3 Jul 2025 17:29:02 +0800 Subject: [PATCH 0093/1187] Feat: Edit the output data of the code operator #3221 (#8649) ### What problem does this PR solve? Feat: Edit the output data of the code operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/interfaces/database/agent.ts | 3 +- web/src/pages/agent/constant.tsx | 2 +- web/src/pages/agent/form/code-form/index.tsx | 35 +++++------ .../agent/form/code-form/next-variable.tsx | 37 ++++++++---- web/src/pages/agent/form/code-form/schema.ts | 14 +++++ .../pages/agent/form/code-form/use-values.ts | 26 +++++++- .../agent/form/code-form/use-watch-change.ts | 60 +++++++++++++++++-- .../pages/agent/utils/build-output-list.ts | 8 +++ 8 files changed, 144 insertions(+), 41 deletions(-) create mode 100644 web/src/pages/agent/form/code-form/schema.ts create mode 100644 web/src/pages/agent/utils/build-output-list.ts diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index aac61a503a2..60f6957abcb 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -135,9 +135,10 @@ export interface IRetrievalForm { } export interface ICodeForm { - inputs?: Array<{ name?: string; component_id?: string }>; + arguments: Record; lang: string; script?: string; + outputs: Record; } export interface IAgentForm { diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 022a2742b05..e1ac5cc07cf 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -667,7 +667,7 @@ export const initialCodeValues = { arg1: '', arg2: '', }, - outputs: { result: { value: '', type: 'string' } }, + outputs: {}, }; export const initialWaitingDialogueValues = {}; diff --git a/web/src/pages/agent/form/code-form/index.tsx b/web/src/pages/agent/form/code-form/index.tsx index 71e0f6f52cd..edeb609d677 100644 --- a/web/src/pages/agent/form/code-form/index.tsx +++ b/web/src/pages/agent/form/code-form/index.tsx @@ -13,16 +13,18 @@ import { import { Input } from '@/components/ui/input'; import { RAGFlowSelect } from '@/components/ui/select'; import { ProgrammingLanguage } from '@/constants/agent'; -import { ICodeForm } from '@/interfaces/database/flow'; +import { ICodeForm } from '@/interfaces/database/agent'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; +import { buildOutputList } from '../../utils/build-output-list'; +import { Output } from '../components/output'; import { DynamicInputVariable, TypeOptions, VariableTitle, } from './next-variable'; +import { FormSchema, FormSchemaType } from './schema'; import { useValues } from './use-values'; import { useHandleLanguageChange, @@ -36,26 +38,14 @@ const options = [ ProgrammingLanguage.Javascript, ].map((x) => ({ value: x, label: x })); +const DynamicFieldName = 'outputs'; + const CodeForm = ({ node }: INextOperatorForm) => { const formData = node?.data.form as ICodeForm; const { t } = useTranslation(); const values = useValues(node); - const FormSchema = z.object({ - lang: z.string(), - script: z.string(), - arguments: z.array( - z.object({ name: z.string(), component_id: z.string() }), - ), - return: z.union([ - z - .array(z.object({ name: z.string(), component_id: z.string() })) - .optional(), - z.object({ name: z.string(), component_id: z.string() }), - ]), - }); - - const form = useForm({ + const form = useForm({ defaultValues: values, resolver: zodResolver(FormSchema), }); @@ -75,6 +65,7 @@ const CodeForm = ({ node }: INextOperatorForm) => { { ) : (
    @@ -132,7 +124,7 @@ const CodeForm = ({ node }: INextOperatorForm) => { ( Name @@ -148,7 +140,7 @@ const CodeForm = ({ node }: INextOperatorForm) => { /> ( Type @@ -167,6 +159,9 @@ const CodeForm = ({ node }: INextOperatorForm) => {
    )} +
    + +
    ); }; diff --git a/web/src/pages/agent/form/code-form/next-variable.tsx b/web/src/pages/agent/form/code-form/next-variable.tsx index c3f0ac5a786..065adbf6499 100644 --- a/web/src/pages/agent/form/code-form/next-variable.tsx +++ b/web/src/pages/agent/form/code-form/next-variable.tsx @@ -10,6 +10,7 @@ import { FormMessage, } from '@/components/ui/form'; import { BlurInput } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; import { Separator } from '@/components/ui/separator'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { X } from 'lucide-react'; @@ -21,18 +22,19 @@ import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; interface IProps { node?: RAGFlowNodeType; name?: string; + isOutputs: boolean; } export const TypeOptions = [ 'String', 'Number', 'Boolean', - 'Array[String]', - 'Array[Number]', + 'Array', + 'Array', 'Object', ].map((x) => ({ label: x, value: x })); -export function DynamicVariableForm({ name = 'arguments' }: IProps) { +export function DynamicVariableForm({ name = 'arguments', isOutputs }: IProps) { const { t } = useTranslation(); const form = useFormContext(); @@ -67,14 +69,22 @@ export function DynamicVariableForm({ name = 'arguments' }: IProps) { ( - + {isOutputs ? ( + + ) : ( + + )} @@ -86,9 +96,7 @@ export function DynamicVariableForm({ name = 'arguments' }: IProps) {
    ); })} - append({ name: '', component_id: undefined })} - > + append({ name: '', type: undefined })}> {t('flow.addVariable')}
    @@ -103,12 +111,17 @@ export function DynamicInputVariable({ node, name, title, + isOutputs = false, }: IProps & { title: ReactNode }) { return (
    - +
    ); diff --git a/web/src/pages/agent/form/code-form/schema.ts b/web/src/pages/agent/form/code-form/schema.ts new file mode 100644 index 00000000000..fe694444e20 --- /dev/null +++ b/web/src/pages/agent/form/code-form/schema.ts @@ -0,0 +1,14 @@ +import { ProgrammingLanguage } from '@/constants/agent'; +import { z } from 'zod'; + +export const FormSchema = z.object({ + lang: z.enum([ProgrammingLanguage.Python, ProgrammingLanguage.Javascript]), + script: z.string(), + arguments: z.array(z.object({ name: z.string(), type: z.string() })), + outputs: z.union([ + z.array(z.object({ name: z.string(), type: z.string() })).optional(), + z.object({ name: z.string(), type: z.string() }), + ]), +}); + +export type FormSchemaType = z.infer; diff --git a/web/src/pages/agent/form/code-form/use-values.ts b/web/src/pages/agent/form/code-form/use-values.ts index ce8ef1f9b78..ea6f2d67cf8 100644 --- a/web/src/pages/agent/form/code-form/use-values.ts +++ b/web/src/pages/agent/form/code-form/use-values.ts @@ -1,3 +1,5 @@ +import { ProgrammingLanguage } from '@/constants/agent'; +import { ICodeForm } from '@/interfaces/database/agent'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { isEmpty } from 'lodash'; import { useMemo } from 'react'; @@ -6,10 +8,26 @@ import { initialCodeValues } from '../../constant'; function convertToArray(args: Record) { return Object.entries(args).map(([key, value]) => ({ name: key, - component_id: value, + type: value, })); } +type OutputsFormType = { name: string; type: string }; + +function convertOutputsToArray({ lang, outputs = {} }: ICodeForm) { + if (lang === ProgrammingLanguage.Python) { + return Object.entries(outputs).map(([key, val]) => ({ + name: key, + type: val.type, + })); + } + return Object.entries(outputs).reduce((pre, [key, val]) => { + pre.name = key; + pre.type = val.type; + return pre; + }, {} as OutputsFormType); +} + export function useValues(node?: RAGFlowNodeType) { const values = useMemo(() => { const formData = node?.data?.form; @@ -18,7 +36,11 @@ export function useValues(node?: RAGFlowNodeType) { return initialCodeValues; } - return { ...formData, arguments: convertToArray(formData.arguments) }; + return { + ...formData, + arguments: convertToArray(formData.arguments), + outputs: convertOutputsToArray(formData), + }; }, [node?.data?.form]); return values; diff --git a/web/src/pages/agent/form/code-form/use-watch-change.ts b/web/src/pages/agent/form/code-form/use-watch-change.ts index 6ce0187afb8..f01241e713f 100644 --- a/web/src/pages/agent/form/code-form/use-watch-change.ts +++ b/web/src/pages/agent/form/code-form/use-watch-change.ts @@ -1,17 +1,55 @@ import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; +import { ICodeForm } from '@/interfaces/database/agent'; +import { isEmpty } from 'lodash'; import { useCallback, useEffect } from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; import useGraphStore from '../../store'; +import { FormSchemaType } from './schema'; -function convertToObject(list: Array<{ name: string; component_id: string }>) { +function convertToObject(list: FormSchemaType['arguments'] = []) { return list.reduce>((pre, cur) => { - pre[cur.name] = cur.component_id; + if (cur.name && cur.type) { + pre[cur.name] = cur.type; + } return pre; }, {}); } -export function useWatchFormChange(id?: string, form?: UseFormReturn) { +type ArrayOutputs = Extract>; + +type ObjectOutputs = Exclude>; + +function convertOutputsToObject({ lang, outputs }: FormSchemaType) { + if (lang === ProgrammingLanguage.Python) { + return (outputs as ArrayOutputs).reduce( + (pre, cur) => { + pre[cur.name] = { + value: '', + type: cur.type, + }; + + return pre; + }, + {}, + ); + } + const outputsObject = outputs as ObjectOutputs; + if (isEmpty(outputsObject)) { + return {}; + } + return { + [outputsObject.name]: { + value: '', + type: outputsObject.type, + }, + }; +} + +export function useWatchFormChange( + id?: string, + form?: UseFormReturn, +) { let values = useWatch({ control: form?.control }); const updateNodeForm = useGraphStore((state) => state.updateNodeForm); @@ -21,7 +59,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { values = form?.getValues() || {}; let nextValues: any = { ...values, - arguments: convertToObject(values.arguments), + arguments: convertToObject( + values?.arguments as FormSchemaType['arguments'], + ), + outputs: convertOutputsToObject(values as FormSchemaType), }; updateNodeForm(id, nextValues); @@ -29,7 +70,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { }, [form?.formState.isDirty, id, updateNodeForm, values]); } -export function useHandleLanguageChange(id?: string, form?: UseFormReturn) { +export function useHandleLanguageChange( + id?: string, + form?: UseFormReturn, +) { const updateNodeForm = useGraphStore((state) => state.updateNodeForm); const handleLanguageChange = useCallback( @@ -37,6 +81,12 @@ export function useHandleLanguageChange(id?: string, form?: UseFormReturn) { if (id) { const script = CodeTemplateStrMap[lang as ProgrammingLanguage]; form?.setValue('script', script); + form?.setValue( + 'outputs', + (lang === ProgrammingLanguage.Python + ? [] + : {}) as FormSchemaType['outputs'], + ); updateNodeForm(id, script, ['script']); } }, diff --git a/web/src/pages/agent/utils/build-output-list.ts b/web/src/pages/agent/utils/build-output-list.ts new file mode 100644 index 00000000000..5a9452965ba --- /dev/null +++ b/web/src/pages/agent/utils/build-output-list.ts @@ -0,0 +1,8 @@ +import { OutputType } from '../form/components/output'; + +export function buildOutputList(outputs: Record>) { + return Object.entries(outputs).reduce((pre, [key, val]) => { + pre.push({ title: key, type: val.type }); + return pre; + }, []); +} From 9771b521cd7339946f00f8cbb8f37668b084c778 Mon Sep 17 00:00:00 2001 From: Shenghang Tsai Date: Thu, 3 Jul 2025 17:29:16 +0800 Subject: [PATCH 0094/1187] Update svg of SiliconFlow with new LOGO (#8647) ### What problem does this PR solve? _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) - [ ] New Feature (non-breaking change which adds functionality) - [ ] Documentation Update - [ ] Refactoring - [ ] Performance Improvement - [ ] Other (please describe): --- web/src/assets/svg/llm/siliconflow.svg | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/web/src/assets/svg/llm/siliconflow.svg b/web/src/assets/svg/llm/siliconflow.svg index 4ce6323dcb1..c6785062867 100644 --- a/web/src/assets/svg/llm/siliconflow.svg +++ b/web/src/assets/svg/llm/siliconflow.svg @@ -1,6 +1,13 @@ - - - - - + + + + + + + + \ No newline at end of file From 3234a15aae8a3a2e1a22f4397240166856897f64 Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 3 Jul 2025 19:04:06 +0800 Subject: [PATCH 0095/1187] Fix: Fixed the issue of retrieval operator text overlapping #3221 (#8652) ### What problem does this PR solve? Fix: Fixed the issue of retrieval operator text overlapping #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../components/auto-keywords-form-field.tsx | 3 + .../max-token-number-from-field.tsx | 2 + web/src/components/page-rank-form-field.tsx | 2 + .../raptor-form-fields.tsx | 4 + .../components/slider-input-form-field.tsx | 97 +++++++++++-------- web/src/constants/form.ts | 5 + web/src/locales/en.ts | 2 +- .../pages/agent/canvas/node/switch-node.tsx | 14 ++- web/src/pages/agent/constant.tsx | 14 ++- .../agent/form/code-form/use-watch-change.ts | 4 +- .../pages/agent/form/switch-form/index.tsx | 28 ++++-- 11 files changed, 113 insertions(+), 62 deletions(-) create mode 100644 web/src/constants/form.ts diff --git a/web/src/components/auto-keywords-form-field.tsx b/web/src/components/auto-keywords-form-field.tsx index d25a7af1f3c..9b8b9da73d0 100644 --- a/web/src/components/auto-keywords-form-field.tsx +++ b/web/src/components/auto-keywords-form-field.tsx @@ -1,3 +1,4 @@ +import { FormLayout } from '@/constants/form'; import { useTranslate } from '@/hooks/common-hooks'; import { SliderInputFormField } from './slider-input-form-field'; @@ -11,6 +12,7 @@ export function AutoKeywordsFormField() { max={30} min={0} tooltip={t('autoKeywordsTip')} + layout={FormLayout.Horizontal} > ); } @@ -25,6 +27,7 @@ export function AutoQuestionsFormField() { max={10} min={0} tooltip={t('autoQuestionsTip')} + layout={FormLayout.Horizontal} > ); } diff --git a/web/src/components/max-token-number-from-field.tsx b/web/src/components/max-token-number-from-field.tsx index bc82cfe5171..a9646f90ff5 100644 --- a/web/src/components/max-token-number-from-field.tsx +++ b/web/src/components/max-token-number-from-field.tsx @@ -1,3 +1,4 @@ +import { FormLayout } from '@/constants/form'; import { useTranslate } from '@/hooks/common-hooks'; import { SliderInputFormField } from './slider-input-form-field'; @@ -14,6 +15,7 @@ export function MaxTokenNumberFormField({ max = 2048 }: IProps) { name={'parser_config.chunk_token_num'} label={t('chunkTokenNumber')} max={max} + layout={FormLayout.Horizontal} > ); } diff --git a/web/src/components/page-rank-form-field.tsx b/web/src/components/page-rank-form-field.tsx index dce708b72d2..ea7d3459056 100644 --- a/web/src/components/page-rank-form-field.tsx +++ b/web/src/components/page-rank-form-field.tsx @@ -1,3 +1,4 @@ +import { FormLayout } from '@/constants/form'; import { useTranslate } from '@/hooks/common-hooks'; import { SliderInputFormField } from './slider-input-form-field'; @@ -12,6 +13,7 @@ export function PageRankFormField() { defaultValue={0} max={100} min={0} + layout={FormLayout.Horizontal} > ); } diff --git a/web/src/components/parse-configuration/raptor-form-fields.tsx b/web/src/components/parse-configuration/raptor-form-fields.tsx index 3a262718a49..b38db7c87f8 100644 --- a/web/src/components/parse-configuration/raptor-form-fields.tsx +++ b/web/src/components/parse-configuration/raptor-form-fields.tsx @@ -1,3 +1,4 @@ +import { FormLayout } from '@/constants/form'; import { DocumentParserType } from '@/constants/knowledge'; import { useTranslate } from '@/hooks/common-hooks'; import random from 'lodash/random'; @@ -130,6 +131,7 @@ const RaptorFormFields = () => { defaultValue={256} max={2048} min={0} + layout={FormLayout.Horizontal} > { step={0.01} max={1} min={0} + layout={FormLayout.Horizontal} > { defaultValue={64} max={1024} min={1} + layout={FormLayout.Horizontal} > ( - -
    - - {label} - -
    - - - - - { - const value = ev.target.value; - field.onChange(value === '' ? 0 : Number(value)); // convert to number - }} - // defaultValue={defaultValue} - > - -
    + + + {label} + +
    + + + + + { + const value = ev.target.value; + field.onChange(value === '' ? 0 : Number(value)); // convert to number + }} + // defaultValue={defaultValue} + > +
    diff --git a/web/src/constants/form.ts b/web/src/constants/form.ts new file mode 100644 index 00000000000..9f2043d032c --- /dev/null +++ b/web/src/constants/form.ts @@ -0,0 +1,5 @@ +export enum FormLayout { + Horizontal = 'horizontal', + Vertical = 'vertical', + Inline = 'inline', +} diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index cbb3655d76b..399898c6ce1 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1275,7 +1275,7 @@ This delimiter is used to split the input text into several text pieces echo of inputVariables: 'Input variables', runningHintText: 'is running...🕞', openingSwitch: 'Opening switch', - openingCopy: 'Opening copy', + openingCopy: 'Opening greeting', openingSwitchTip: 'Your users will see this welcome message at the beginning.', modeTip: 'The mode defines how the workflow is initiated.', diff --git a/web/src/pages/agent/canvas/node/switch-node.tsx b/web/src/pages/agent/canvas/node/switch-node.tsx index 1bd2f45b6f5..0045fdf992b 100644 --- a/web/src/pages/agent/canvas/node/switch-node.tsx +++ b/web/src/pages/agent/canvas/node/switch-node.tsx @@ -1,9 +1,9 @@ -import { IconFont } from '@/components/icon-font'; import { Card, CardContent } from '@/components/ui/card'; import { ISwitchCondition, ISwitchNode } from '@/interfaces/database/flow'; import { NodeProps, Position } from '@xyflow/react'; import { memo, useCallback } from 'react'; import { NodeHandleId, SwitchOperatorOptions } from '../../constant'; +import { LogicalOperatorIcon } from '../../form/switch-form'; import { useGetVariableLabelByValue } from '../../hooks/use-get-begin-query'; import { CommonHandle } from './handle'; import { RightHandleStyle } from './handle-icon'; @@ -30,8 +30,16 @@ const ConditionBlock = ({ const getLabel = useGetVariableLabelByValue(nodeId); const renderOperatorIcon = useCallback((operator?: string) => { - const name = SwitchOperatorOptions.find((x) => x.value === operator)?.icon; - return ; + const item = SwitchOperatorOptions.find((x) => x.value === operator); + if (item) { + return ( + + ); + } + return <>; }, []); return ( diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index e1ac5cc07cf..788a697e524 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -34,6 +34,8 @@ export enum PromptRole { } import { + Circle, + CircleSlash2, CloudUpload, ListOrdered, OptionIcon, @@ -378,8 +380,16 @@ export const SwitchOperatorOptions = [ { value: 'not contains', label: 'notContains', icon: 'not-contains' }, { value: 'start with', label: 'startWith', icon: 'list-start' }, { value: 'end with', label: 'endWith', icon: 'list-end' }, - { value: 'empty', label: 'empty', icon: 'circle' }, - { value: 'not empty', label: 'notEmpty', icon: 'circle-slash-2' }, + { + value: 'empty', + label: 'empty', + icon: , + }, + { + value: 'not empty', + label: 'notEmpty', + icon: , + }, ]; export const SwitchElseTo = 'end_cpn_ids'; diff --git a/web/src/pages/agent/form/code-form/use-watch-change.ts b/web/src/pages/agent/form/code-form/use-watch-change.ts index f01241e713f..80e0c8b15d7 100644 --- a/web/src/pages/agent/form/code-form/use-watch-change.ts +++ b/web/src/pages/agent/form/code-form/use-watch-change.ts @@ -8,9 +8,7 @@ import { FormSchemaType } from './schema'; function convertToObject(list: FormSchemaType['arguments'] = []) { return list.reduce>((pre, cur) => { - if (cur.name && cur.type) { - pre[cur.name] = cur.type; - } + pre[cur.name] = cur.type; return pre; }, {}); diff --git a/web/src/pages/agent/form/switch-form/index.tsx b/web/src/pages/agent/form/switch-form/index.tsx index 4882d01e145..64b791974da 100644 --- a/web/src/pages/agent/form/switch-form/index.tsx +++ b/web/src/pages/agent/form/switch-form/index.tsx @@ -41,18 +41,21 @@ type ConditionCardsProps = { parentLength: number; } & IOperatorForm; -const OperatorIcon = function OperatorIcon({ +export const LogicalOperatorIcon = function OperatorIcon({ icon, value, }: Omit<(typeof SwitchOperatorOptions)[0], 'label'>) { - return ( - ', - })} - > - ); + if (typeof icon === 'string') { + return ( + ', + })} + > + ); + } + return icon; }; function useBuildSwitchOperatorOptions() { @@ -61,7 +64,12 @@ function useBuildSwitchOperatorOptions() { const switchOperatorOptions = useMemo(() => { return SwitchOperatorOptions.map((x) => ({ value: x.value, - icon: , + icon: ( + + ), label: t(`flow.switchOperatorOptions.${x.label}`), })); }, [t]); From f8a6987f1e5a112981456377c1160c9a87aa9856 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Thu, 3 Jul 2025 19:05:31 +0800 Subject: [PATCH 0096/1187] Refa: automatic LLMs registration (#8651) ### What problem does this PR solve? Support automatic LLMs registration. ### Type of change - [x] Refactoring --- rag/llm/__init__.py | 318 ++++--------------------------- rag/llm/chat_model.py | 104 ++++++++-- rag/llm/cv_model.py | 329 ++++++++++++++++---------------- rag/llm/embedding_model.py | 345 +++++++++++++++------------------- rag/llm/rerank_model.py | 166 ++++++++-------- rag/llm/sequence2txt_model.py | 87 +++++---- rag/llm/tts_model.py | 142 +++++--------- 7 files changed, 617 insertions(+), 874 deletions(-) diff --git a/rag/llm/__init__.py b/rag/llm/__init__.py index b22d722d0bf..e9542bbe8f5 100644 --- a/rag/llm/__init__.py +++ b/rag/llm/__init__.py @@ -15,289 +15,53 @@ # # AFTER UPDATING THIS FILE, PLEASE ENSURE THAT docs/references/supported_models.mdx IS ALSO UPDATED for consistency! # -from .embedding_model import ( - OllamaEmbed, - LocalAIEmbed, - OpenAIEmbed, - AzureEmbed, - XinferenceEmbed, - QWenEmbed, - ZhipuEmbed, - FastEmbed, - YoudaoEmbed, - BaiChuanEmbed, - JinaEmbed, - DefaultEmbedding, - MistralEmbed, - BedrockEmbed, - GeminiEmbed, - NvidiaEmbed, - LmStudioEmbed, - OpenAI_APIEmbed, - CoHereEmbed, - TogetherAIEmbed, - PerfXCloudEmbed, - UpstageEmbed, - SILICONFLOWEmbed, - ReplicateEmbed, - BaiduYiyanEmbed, - VoyageEmbed, - HuggingFaceEmbed, - VolcEngineEmbed, - GPUStackEmbed, - NovitaEmbed, - GiteeEmbed -) -from .chat_model import ( - GptTurbo, - AzureChat, - ZhipuChat, - QWenChat, - OllamaChat, - LocalAIChat, - XinferenceChat, - MoonshotChat, - DeepSeekChat, - VolcEngineChat, - BaiChuanChat, - MiniMaxChat, - MistralChat, - GeminiChat, - BedrockChat, - GroqChat, - OpenRouterChat, - StepFunChat, - NvidiaChat, - LmStudioChat, - OpenAI_APIChat, - CoHereChat, - LeptonAIChat, - TogetherAIChat, - PerfXCloudChat, - UpstageChat, - NovitaAIChat, - SILICONFLOWChat, - PPIOChat, - YiChat, - ReplicateChat, - HunyuanChat, - SparkChat, - BaiduYiyanChat, - AnthropicChat, - GoogleChat, - HuggingFaceChat, - GPUStackChat, - ModelScopeChat, - GiteeChat -) -from .cv_model import ( - GptV4, - AzureGptV4, - OllamaCV, - XinferenceCV, - QWenCV, - Zhipu4V, - LocalCV, - GeminiCV, - OpenRouterCV, - LocalAICV, - NvidiaCV, - LmStudioCV, - StepFunCV, - OpenAI_APICV, - TogetherAICV, - YiCV, - HunyuanCV, - AnthropicCV, - SILICONFLOWCV, - GPUStackCV, - GoogleCV, -) +import importlib +import inspect -from .rerank_model import ( - LocalAIRerank, - DefaultRerank, - JinaRerank, - YoudaoRerank, - XInferenceRerank, - NvidiaRerank, - LmStudioRerank, - OpenAI_APIRerank, - CoHereRerank, - TogetherAIRerank, - SILICONFLOWRerank, - BaiduYiyanRerank, - VoyageRerank, - QWenRerank, - GPUStackRerank, - HuggingfaceRerank, - NovitaRerank, - GiteeRerank -) +ChatModel = globals().get("ChatModel", {}) +CvModel = globals().get("CvModel", {}) +EmbeddingModel = globals().get("EmbeddingModel", {}) +RerankModel = globals().get("RerankModel", {}) +Seq2txtModel = globals().get("Seq2txtModel", {}) +TTSModel = globals().get("TTSModel", {}) -from .sequence2txt_model import ( - GPTSeq2txt, - QWenSeq2txt, - AzureSeq2txt, - XinferenceSeq2txt, - TencentCloudSeq2txt, - GPUStackSeq2txt, - GiteeSeq2txt -) - -from .tts_model import ( - FishAudioTTS, - QwenTTS, - OpenAITTS, - SparkTTS, - XinferenceTTS, - GPUStackTTS, - SILICONFLOWTTS, -) - -EmbeddingModel = { - "Ollama": OllamaEmbed, - "LocalAI": LocalAIEmbed, - "OpenAI": OpenAIEmbed, - "Azure-OpenAI": AzureEmbed, - "Xinference": XinferenceEmbed, - "Tongyi-Qianwen": QWenEmbed, - "ZHIPU-AI": ZhipuEmbed, - "FastEmbed": FastEmbed, - "Youdao": YoudaoEmbed, - "BaiChuan": BaiChuanEmbed, - "Jina": JinaEmbed, - "BAAI": DefaultEmbedding, - "Mistral": MistralEmbed, - "Bedrock": BedrockEmbed, - "Gemini": GeminiEmbed, - "NVIDIA": NvidiaEmbed, - "LM-Studio": LmStudioEmbed, - "OpenAI-API-Compatible": OpenAI_APIEmbed, - "VLLM": OpenAI_APIEmbed, - "Cohere": CoHereEmbed, - "TogetherAI": TogetherAIEmbed, - "PerfXCloud": PerfXCloudEmbed, - "Upstage": UpstageEmbed, - "SILICONFLOW": SILICONFLOWEmbed, - "Replicate": ReplicateEmbed, - "BaiduYiyan": BaiduYiyanEmbed, - "Voyage AI": VoyageEmbed, - "HuggingFace": HuggingFaceEmbed, - "VolcEngine": VolcEngineEmbed, - "GPUStack": GPUStackEmbed, - "NovitaAI": NovitaEmbed, - "GiteeAI": GiteeEmbed +MODULE_MAPPING = { + "chat_model": ChatModel, + "cv_model": CvModel, + "embedding_model": EmbeddingModel, + "rerank_model": RerankModel, + "sequence2txt_model": Seq2txtModel, + "tts_model": TTSModel, } -CvModel = { - "OpenAI": GptV4, - "Azure-OpenAI": AzureGptV4, - "Ollama": OllamaCV, - "Xinference": XinferenceCV, - "Tongyi-Qianwen": QWenCV, - "ZHIPU-AI": Zhipu4V, - "Moonshot": LocalCV, - "Gemini": GeminiCV, - "OpenRouter": OpenRouterCV, - "LocalAI": LocalAICV, - "NVIDIA": NvidiaCV, - "LM-Studio": LmStudioCV, - "StepFun": StepFunCV, - "OpenAI-API-Compatible": OpenAI_APICV, - "VLLM": OpenAI_APICV, - "TogetherAI": TogetherAICV, - "01.AI": YiCV, - "Tencent Hunyuan": HunyuanCV, - "Anthropic": AnthropicCV, - "SILICONFLOW": SILICONFLOWCV, - "GPUStack": GPUStackCV, - "Google Cloud": GoogleCV -} +package_name = __name__ -ChatModel = { - "OpenAI": GptTurbo, - "Azure-OpenAI": AzureChat, - "ZHIPU-AI": ZhipuChat, - "Tongyi-Qianwen": QWenChat, - "Ollama": OllamaChat, - "LocalAI": LocalAIChat, - "Xinference": XinferenceChat, - "Moonshot": MoonshotChat, - "DeepSeek": DeepSeekChat, - "VolcEngine": VolcEngineChat, - "BaiChuan": BaiChuanChat, - "MiniMax": MiniMaxChat, - "Mistral": MistralChat, - "Gemini": GeminiChat, - "Bedrock": BedrockChat, - "Groq": GroqChat, - "OpenRouter": OpenRouterChat, - "StepFun": StepFunChat, - "NVIDIA": NvidiaChat, - "LM-Studio": LmStudioChat, - "OpenAI-API-Compatible": OpenAI_APIChat, - "VLLM": OpenAI_APIChat, - "Cohere": CoHereChat, - "LeptonAI": LeptonAIChat, - "TogetherAI": TogetherAIChat, - "PerfXCloud": PerfXCloudChat, - "Upstage": UpstageChat, - "NovitaAI": NovitaAIChat, - "SILICONFLOW": SILICONFLOWChat, - "PPIO": PPIOChat, - "01.AI": YiChat, - "Replicate": ReplicateChat, - "Tencent Hunyuan": HunyuanChat, - "XunFei Spark": SparkChat, - "BaiduYiyan": BaiduYiyanChat, - "Anthropic": AnthropicChat, - "Google Cloud": GoogleChat, - "HuggingFace": HuggingFaceChat, - "GPUStack": GPUStackChat, - "ModelScope":ModelScopeChat, - "GiteeAI": GiteeChat -} +for module_name, mapping_dict in MODULE_MAPPING.items(): + full_module_name = f"{package_name}.{module_name}" + module = importlib.import_module(full_module_name) -RerankModel = { - "LocalAI": LocalAIRerank, - "BAAI": DefaultRerank, - "Jina": JinaRerank, - "Youdao": YoudaoRerank, - "Xinference": XInferenceRerank, - "NVIDIA": NvidiaRerank, - "LM-Studio": LmStudioRerank, - "OpenAI-API-Compatible": OpenAI_APIRerank, - "VLLM": CoHereRerank, - "Cohere": CoHereRerank, - "TogetherAI": TogetherAIRerank, - "SILICONFLOW": SILICONFLOWRerank, - "BaiduYiyan": BaiduYiyanRerank, - "Voyage AI": VoyageRerank, - "Tongyi-Qianwen": QWenRerank, - "GPUStack": GPUStackRerank, - "HuggingFace": HuggingfaceRerank, - "NovitaAI": NovitaRerank, - "GiteeAI": GiteeRerank -} + base_class = None + for name, obj in inspect.getmembers(module): + if inspect.isclass(obj) and name == "Base": + base_class = obj + break + if base_class is None: + continue -Seq2txtModel = { - "OpenAI": GPTSeq2txt, - "Tongyi-Qianwen": QWenSeq2txt, - "Azure-OpenAI": AzureSeq2txt, - "Xinference": XinferenceSeq2txt, - "Tencent Cloud": TencentCloudSeq2txt, - "GPUStack": GPUStackSeq2txt, - "GiteeAI": GiteeSeq2txt -} + for _, obj in inspect.getmembers(module): + if inspect.isclass(obj) and issubclass(obj, base_class) and obj is not base_class and hasattr(obj, "_FACTORY_NAME"): + if isinstance(obj._FACTORY_NAME, list): + for factory_name in obj._FACTORY_NAME: + mapping_dict[factory_name] = obj + else: + mapping_dict[obj._FACTORY_NAME] = obj -TTSModel = { - "Fish Audio": FishAudioTTS, - "Tongyi-Qianwen": QwenTTS, - "OpenAI": OpenAITTS, - "XunFei Spark": SparkTTS, - "Xinference": XinferenceTTS, - "GPUStack": GPUStackTTS, - "SILICONFLOW": SILICONFLOWTTS, -} +__all__ = [ + "ChatModel", + "CvModel", + "EmbeddingModel", + "RerankModel", + "Seq2txtModel", + "TTSModel", +] diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 020815104e1..f254ed18681 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -142,11 +142,7 @@ def _exceptions(self, e, attempt): return f"{ERROR_PREFIX}: {error_code} - {str(e)}" def _verbose_tool_use(self, name, args, res): - return "" + json.dumps({ - "name": name, - "args": args, - "result": res - }, ensure_ascii=False, indent=2) + "" + return "" + json.dumps({"name": name, "args": args, "result": res}, ensure_ascii=False, indent=2) + "" def _append_history(self, hist, tool_call, tool_res): hist.append( @@ -191,10 +187,10 @@ def chat_with_tools(self, system: str, history: list, gen_conf: dict): tk_count = 0 hist = deepcopy(history) # Implement exponential backoff retry strategy - for attempt in range(self.max_retries+1): + for attempt in range(self.max_retries + 1): history = hist try: - for _ in range(self.max_rounds*2): + for _ in range(self.max_rounds * 2): response = self.client.chat.completions.create(model=self.model_name, messages=history, tools=self.tools, **gen_conf) tk_count += self.total_token_count(response) if any([not response.choices, not response.choices[0].message]): @@ -269,7 +265,7 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): for attempt in range(self.max_retries + 1): history = hist try: - for _ in range(self.max_rounds*2): + for _ in range(self.max_rounds * 2): reasoning_start = False response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, tools=tools, **gen_conf) final_tool_calls = {} @@ -430,6 +426,8 @@ def count_tokens(text): class GptTurbo(Base): + _FACTORY_NAME = "OpenAI" + def __init__(self, key, model_name="gpt-3.5-turbo", base_url="https://api.openai.com/v1", **kwargs): if not base_url: base_url = "https://api.openai.com/v1" @@ -437,6 +435,8 @@ def __init__(self, key, model_name="gpt-3.5-turbo", base_url="https://api.openai class MoonshotChat(Base): + _FACTORY_NAME = "Moonshot" + def __init__(self, key, model_name="moonshot-v1-8k", base_url="https://api.moonshot.cn/v1", **kwargs): if not base_url: base_url = "https://api.moonshot.cn/v1" @@ -444,6 +444,8 @@ def __init__(self, key, model_name="moonshot-v1-8k", base_url="https://api.moons class XinferenceChat(Base): + _FACTORY_NAME = "Xinference" + def __init__(self, key=None, model_name="", base_url="", **kwargs): if not base_url: raise ValueError("Local llm url cannot be None") @@ -452,6 +454,8 @@ def __init__(self, key=None, model_name="", base_url="", **kwargs): class HuggingFaceChat(Base): + _FACTORY_NAME = "HuggingFace" + def __init__(self, key=None, model_name="", base_url="", **kwargs): if not base_url: raise ValueError("Local llm url cannot be None") @@ -460,6 +464,8 @@ def __init__(self, key=None, model_name="", base_url="", **kwargs): class ModelScopeChat(Base): + _FACTORY_NAME = "ModelScope" + def __init__(self, key=None, model_name="", base_url="", **kwargs): if not base_url: raise ValueError("Local llm url cannot be None") @@ -468,6 +474,8 @@ def __init__(self, key=None, model_name="", base_url="", **kwargs): class DeepSeekChat(Base): + _FACTORY_NAME = "DeepSeek" + def __init__(self, key, model_name="deepseek-chat", base_url="https://api.deepseek.com/v1", **kwargs): if not base_url: base_url = "https://api.deepseek.com/v1" @@ -475,6 +483,8 @@ def __init__(self, key, model_name="deepseek-chat", base_url="https://api.deepse class AzureChat(Base): + _FACTORY_NAME = "Azure-OpenAI" + def __init__(self, key, model_name, base_url, **kwargs): api_key = json.loads(key).get("api_key", "") api_version = json.loads(key).get("api_version", "2024-02-01") @@ -484,6 +494,8 @@ def __init__(self, key, model_name, base_url, **kwargs): class BaiChuanChat(Base): + _FACTORY_NAME = "BaiChuan" + def __init__(self, key, model_name="Baichuan3-Turbo", base_url="https://api.baichuan-ai.com/v1", **kwargs): if not base_url: base_url = "https://api.baichuan-ai.com/v1" @@ -557,6 +569,8 @@ def chat_streamly(self, system, history, gen_conf): class QWenChat(Base): + _FACTORY_NAME = "Tongyi-Qianwen" + def __init__(self, key, model_name=Generation.Models.qwen_turbo, base_url=None, **kwargs): if not base_url: base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1" @@ -565,6 +579,8 @@ def __init__(self, key, model_name=Generation.Models.qwen_turbo, base_url=None, class ZhipuChat(Base): + _FACTORY_NAME = "ZHIPU-AI" + def __init__(self, key, model_name="glm-3-turbo", base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -630,6 +646,8 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict): class OllamaChat(Base): + _FACTORY_NAME = "Ollama" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -694,6 +712,8 @@ def chat_streamly(self, system, history, gen_conf): class LocalAIChat(Base): + _FACTORY_NAME = "LocalAI" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -752,6 +772,8 @@ def chat_streamly(self, system, history, gen_conf): class VolcEngineChat(Base): + _FACTORY_NAME = "VolcEngine" + def __init__(self, key, model_name, base_url="https://ark.cn-beijing.volces.com/api/v3", **kwargs): """ Since do not want to modify the original database fields, and the VolcEngine authentication method is quite special, @@ -765,6 +787,8 @@ def __init__(self, key, model_name, base_url="https://ark.cn-beijing.volces.com/ class MiniMaxChat(Base): + _FACTORY_NAME = "MiniMax" + def __init__(self, key, model_name, base_url="https://api.minimax.chat/v1/text/chatcompletion_v2", **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -843,6 +867,8 @@ def chat_streamly(self, system, history, gen_conf): class MistralChat(Base): + _FACTORY_NAME = "Mistral" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -896,6 +922,8 @@ def chat_streamly(self, system, history, gen_conf): class BedrockChat(Base): + _FACTORY_NAME = "Bedrock" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -978,6 +1006,8 @@ def chat_streamly(self, system, history, gen_conf): class GeminiChat(Base): + _FACTORY_NAME = "Gemini" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -997,6 +1027,7 @@ def _clean_conf(self, gen_conf): def _chat(self, history, gen_conf): from google.generativeai.types import content_types + system = history[0]["content"] if history and history[0]["role"] == "system" else "" hist = [] for item in history: @@ -1019,6 +1050,7 @@ def _chat(self, history, gen_conf): def chat_streamly(self, system, history, gen_conf): from google.generativeai.types import content_types + gen_conf = self._clean_conf(gen_conf) if system: self.model._system_instruction = content_types.to_content(system) @@ -1042,6 +1074,8 @@ def chat_streamly(self, system, history, gen_conf): class GroqChat(Base): + _FACTORY_NAME = "Groq" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -1086,6 +1120,8 @@ def chat_streamly(self, system, history, gen_conf): ## openrouter class OpenRouterChat(Base): + _FACTORY_NAME = "OpenRouter" + def __init__(self, key, model_name, base_url="https://openrouter.ai/api/v1", **kwargs): if not base_url: base_url = "https://openrouter.ai/api/v1" @@ -1093,6 +1129,8 @@ def __init__(self, key, model_name, base_url="https://openrouter.ai/api/v1", **k class StepFunChat(Base): + _FACTORY_NAME = "StepFun" + def __init__(self, key, model_name, base_url="https://api.stepfun.com/v1", **kwargs): if not base_url: base_url = "https://api.stepfun.com/v1" @@ -1100,6 +1138,8 @@ def __init__(self, key, model_name, base_url="https://api.stepfun.com/v1", **kwa class NvidiaChat(Base): + _FACTORY_NAME = "NVIDIA" + def __init__(self, key, model_name, base_url="https://integrate.api.nvidia.com/v1", **kwargs): if not base_url: base_url = "https://integrate.api.nvidia.com/v1" @@ -1107,6 +1147,8 @@ def __init__(self, key, model_name, base_url="https://integrate.api.nvidia.com/v class LmStudioChat(Base): + _FACTORY_NAME = "LM-Studio" + def __init__(self, key, model_name, base_url, **kwargs): if not base_url: raise ValueError("Local llm url cannot be None") @@ -1117,6 +1159,8 @@ def __init__(self, key, model_name, base_url, **kwargs): class OpenAI_APIChat(Base): + _FACTORY_NAME = ["VLLM", "OpenAI-API-Compatible"] + def __init__(self, key, model_name, base_url): if not base_url: raise ValueError("url cannot be None") @@ -1125,6 +1169,8 @@ def __init__(self, key, model_name, base_url): class PPIOChat(Base): + _FACTORY_NAME = "PPIO" + def __init__(self, key, model_name, base_url="https://api.ppinfra.com/v3/openai", **kwargs): if not base_url: base_url = "https://api.ppinfra.com/v3/openai" @@ -1132,6 +1178,8 @@ def __init__(self, key, model_name, base_url="https://api.ppinfra.com/v3/openai" class CoHereChat(Base): + _FACTORY_NAME = "Cohere" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -1207,6 +1255,8 @@ def chat_streamly(self, system, history, gen_conf): class LeptonAIChat(Base): + _FACTORY_NAME = "LeptonAI" + def __init__(self, key, model_name, base_url=None, **kwargs): if not base_url: base_url = urljoin("https://" + model_name + ".lepton.run", "api/v1") @@ -1214,6 +1264,8 @@ def __init__(self, key, model_name, base_url=None, **kwargs): class TogetherAIChat(Base): + _FACTORY_NAME = "TogetherAI" + def __init__(self, key, model_name, base_url="https://api.together.xyz/v1", **kwargs): if not base_url: base_url = "https://api.together.xyz/v1" @@ -1221,6 +1273,8 @@ def __init__(self, key, model_name, base_url="https://api.together.xyz/v1", **kw class PerfXCloudChat(Base): + _FACTORY_NAME = "PerfXCloud" + def __init__(self, key, model_name, base_url="https://cloud.perfxlab.cn/v1", **kwargs): if not base_url: base_url = "https://cloud.perfxlab.cn/v1" @@ -1228,6 +1282,8 @@ def __init__(self, key, model_name, base_url="https://cloud.perfxlab.cn/v1", **k class UpstageChat(Base): + _FACTORY_NAME = "Upstage" + def __init__(self, key, model_name, base_url="https://api.upstage.ai/v1/solar", **kwargs): if not base_url: base_url = "https://api.upstage.ai/v1/solar" @@ -1235,6 +1291,8 @@ def __init__(self, key, model_name, base_url="https://api.upstage.ai/v1/solar", class NovitaAIChat(Base): + _FACTORY_NAME = "NovitaAI" + def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai", **kwargs): if not base_url: base_url = "https://api.novita.ai/v3/openai" @@ -1242,6 +1300,8 @@ def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai", class SILICONFLOWChat(Base): + _FACTORY_NAME = "SILICONFLOW" + def __init__(self, key, model_name, base_url="https://api.siliconflow.cn/v1", **kwargs): if not base_url: base_url = "https://api.siliconflow.cn/v1" @@ -1249,6 +1309,8 @@ def __init__(self, key, model_name, base_url="https://api.siliconflow.cn/v1", ** class YiChat(Base): + _FACTORY_NAME = "01.AI" + def __init__(self, key, model_name, base_url="https://api.lingyiwanwu.com/v1", **kwargs): if not base_url: base_url = "https://api.lingyiwanwu.com/v1" @@ -1256,6 +1318,8 @@ def __init__(self, key, model_name, base_url="https://api.lingyiwanwu.com/v1", * class GiteeChat(Base): + _FACTORY_NAME = "GiteeAI" + def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/", **kwargs): if not base_url: base_url = "https://ai.gitee.com/v1/" @@ -1263,6 +1327,8 @@ def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/", **kwarg class ReplicateChat(Base): + _FACTORY_NAME = "Replicate" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -1302,6 +1368,8 @@ def chat_streamly(self, system, history, gen_conf): class HunyuanChat(Base): + _FACTORY_NAME = "Tencent Hunyuan" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -1378,6 +1446,8 @@ def chat_streamly(self, system, history, gen_conf): class SparkChat(Base): + _FACTORY_NAME = "XunFei Spark" + def __init__(self, key, model_name, base_url="https://spark-api-open.xf-yun.com/v1", **kwargs): if not base_url: base_url = "https://spark-api-open.xf-yun.com/v1" @@ -1398,6 +1468,8 @@ def __init__(self, key, model_name, base_url="https://spark-api-open.xf-yun.com/ class BaiduYiyanChat(Base): + _FACTORY_NAME = "BaiduYiyan" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -1444,6 +1516,8 @@ def chat_streamly(self, system, history, gen_conf): class AnthropicChat(Base): + _FACTORY_NAME = "Anthropic" + def __init__(self, key, model_name, base_url="https://api.anthropic.com/v1/", **kwargs): if not base_url: base_url = "https://api.anthropic.com/v1/" @@ -1451,6 +1525,8 @@ def __init__(self, key, model_name, base_url="https://api.anthropic.com/v1/", ** class GoogleChat(Base): + _FACTORY_NAME = "Google Cloud" + def __init__(self, key, model_name, base_url=None, **kwargs): super().__init__(key, model_name, base_url=base_url, **kwargs) @@ -1529,9 +1605,11 @@ def _chat(self, history, gen_conf): if "role" in item and item["role"] == "assistant": item["role"] = "model" if "content" in item: - item["parts"] = [{ - "text": item.pop("content"), - }] + item["parts"] = [ + { + "text": item.pop("content"), + } + ] response = self.client.generate_content(hist, generation_config=gen_conf) ans = response.text @@ -1587,8 +1665,10 @@ def chat_streamly(self, system, history, gen_conf): class GPUStackChat(Base): + _FACTORY_NAME = "GPUStack" + def __init__(self, key=None, model_name="", base_url="", **kwargs): if not base_url: raise ValueError("Local llm url cannot be None") base_url = urljoin(base_url, "v1") - super().__init__(key, model_name, base_url, **kwargs) \ No newline at end of file + super().__init__(key, model_name, base_url, **kwargs) diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index da7efaef18e..afa39d69a99 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -57,7 +57,7 @@ def chat(self, system, history, gen_conf, image=""): model=self.model_name, messages=history, temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7) + top_p=gen_conf.get("top_p", 0.7), ) return response.choices[0].message.content.strip(), response.usage.total_tokens except Exception as e: @@ -79,7 +79,7 @@ def chat_streamly(self, system, history, gen_conf, image=""): messages=history, temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7), - stream=True + stream=True, ) for resp in response: if not resp.choices[0].delta.content: @@ -87,8 +87,7 @@ def chat_streamly(self, system, history, gen_conf, image=""): delta = resp.choices[0].delta.content ans += delta if resp.choices[0].finish_reason == "length": - ans += "...\nFor the content length reason, it stopped, continue?" if is_english( - [ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" + ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" tk_count = resp.usage.total_tokens if resp.choices[0].finish_reason == "stop": tk_count = resp.usage.total_tokens @@ -117,13 +116,12 @@ def prompt(self, b64): "content": [ { "type": "image_url", - "image_url": { - "url": f"data:image/jpeg;base64,{b64}" - }, + "image_url": {"url": f"data:image/jpeg;base64,{b64}"}, }, { - "text": "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" if self.lang.lower() == "chinese" else - "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out.", + "text": "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" + if self.lang.lower() == "chinese" + else "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out.", }, ], } @@ -136,9 +134,7 @@ def vision_llm_prompt(self, b64, prompt=None): "content": [ { "type": "image_url", - "image_url": { - "url": f"data:image/jpeg;base64,{b64}" - }, + "image_url": {"url": f"data:image/jpeg;base64,{b64}"}, }, { "type": "text", @@ -156,14 +152,13 @@ def chat_prompt(self, text, b64): "url": f"data:image/jpeg;base64,{b64}", }, }, - { - "type": "text", - "text": text - }, + {"type": "text", "text": text}, ] class GptV4(Base): + _FACTORY_NAME = "OpenAI" + def __init__(self, key, model_name="gpt-4-vision-preview", lang="Chinese", base_url="https://api.openai.com/v1"): if not base_url: base_url = "https://api.openai.com/v1" @@ -181,7 +176,7 @@ def describe(self, image): res = self.client.chat.completions.create( model=self.model_name, - messages=prompt + messages=prompt, ) return res.choices[0].message.content.strip(), res.usage.total_tokens @@ -197,9 +192,11 @@ def describe_with_prompt(self, image, prompt=None): class AzureGptV4(Base): + _FACTORY_NAME = "Azure-OpenAI" + def __init__(self, key, model_name, lang="Chinese", **kwargs): - api_key = json.loads(key).get('api_key', '') - api_version = json.loads(key).get('api_version', '2024-02-01') + api_key = json.loads(key).get("api_key", "") + api_version = json.loads(key).get("api_version", "2024-02-01") self.client = AzureOpenAI(api_key=api_key, azure_endpoint=kwargs["base_url"], api_version=api_version) self.model_name = model_name self.lang = lang @@ -212,10 +209,7 @@ def describe(self, image): if "text" in c: c["type"] = "text" - res = self.client.chat.completions.create( - model=self.model_name, - messages=prompt - ) + res = self.client.chat.completions.create(model=self.model_name, messages=prompt) return res.choices[0].message.content.strip(), res.usage.total_tokens def describe_with_prompt(self, image, prompt=None): @@ -230,8 +224,11 @@ def describe_with_prompt(self, image, prompt=None): class QWenCV(Base): + _FACTORY_NAME = "Tongyi-Qianwen" + def __init__(self, key, model_name="qwen-vl-chat-v1", lang="Chinese", **kwargs): import dashscope + dashscope.api_key = key self.model_name = model_name self.lang = lang @@ -247,12 +244,11 @@ def prompt(self, binary): { "role": "user", "content": [ + {"image": f"file://{path}"}, { - "image": f"file://{path}" - }, - { - "text": "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" if self.lang.lower() == "chinese" else - "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out.", + "text": "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" + if self.lang.lower() == "chinese" + else "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out.", }, ], } @@ -270,11 +266,9 @@ def vision_llm_prompt(self, binary, prompt=None): { "role": "user", "content": [ + {"image": f"file://{path}"}, { - "image": f"file://{path}" - }, - { - "text": prompt if prompt else vision_llm_describe_prompt(), + "text": prompt if prompt else vision_llm_describe_prompt(), }, ], } @@ -290,9 +284,10 @@ def describe(self, image): from http import HTTPStatus from dashscope import MultiModalConversation + response = MultiModalConversation.call(model=self.model_name, messages=self.prompt(image)) if response.status_code == HTTPStatus.OK: - return response.output.choices[0]['message']['content'][0]["text"], response.usage.output_tokens + return response.output.choices[0]["message"]["content"][0]["text"], response.usage.output_tokens return response.message, 0 def describe_with_prompt(self, image, prompt=None): @@ -303,33 +298,36 @@ def describe_with_prompt(self, image, prompt=None): vision_prompt = self.vision_llm_prompt(image, prompt) if prompt else self.vision_llm_prompt(image) response = MultiModalConversation.call(model=self.model_name, messages=vision_prompt) if response.status_code == HTTPStatus.OK: - return response.output.choices[0]['message']['content'][0]["text"], response.usage.output_tokens + return response.output.choices[0]["message"]["content"][0]["text"], response.usage.output_tokens return response.message, 0 def chat(self, system, history, gen_conf, image=""): from http import HTTPStatus from dashscope import MultiModalConversation + if system: history[-1]["content"] = system + history[-1]["content"] + "user query: " + history[-1]["content"] for his in history: if his["role"] == "user": his["content"] = self.chat_prompt(his["content"], image) - response = MultiModalConversation.call(model=self.model_name, messages=history, - temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7)) + response = MultiModalConversation.call( + model=self.model_name, + messages=history, + temperature=gen_conf.get("temperature", 0.3), + top_p=gen_conf.get("top_p", 0.7), + ) ans = "" tk_count = 0 if response.status_code == HTTPStatus.OK: - ans = response.output.choices[0]['message']['content'] + ans = response.output.choices[0]["message"]["content"] if isinstance(ans, list): ans = ans[0]["text"] if ans else "" tk_count += response.usage.total_tokens if response.output.choices[0].get("finish_reason", "") == "length": - ans += "...\nFor the content length reason, it stopped, continue?" if is_english( - [ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" + ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" return ans, tk_count return "**ERROR**: " + response.message, tk_count @@ -338,6 +336,7 @@ def chat_streamly(self, system, history, gen_conf, image=""): from http import HTTPStatus from dashscope import MultiModalConversation + if system: history[-1]["content"] = system + history[-1]["content"] + "user query: " + history[-1]["content"] @@ -348,24 +347,25 @@ def chat_streamly(self, system, history, gen_conf, image=""): ans = "" tk_count = 0 try: - response = MultiModalConversation.call(model=self.model_name, messages=history, - temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7), - stream=True) + response = MultiModalConversation.call( + model=self.model_name, + messages=history, + temperature=gen_conf.get("temperature", 0.3), + top_p=gen_conf.get("top_p", 0.7), + stream=True, + ) for resp in response: if resp.status_code == HTTPStatus.OK: - cnt = resp.output.choices[0]['message']['content'] + cnt = resp.output.choices[0]["message"]["content"] if isinstance(cnt, list): cnt = cnt[0]["text"] if ans else "" ans += cnt tk_count = resp.usage.total_tokens if resp.output.choices[0].get("finish_reason", "") == "length": - ans += "...\nFor the content length reason, it stopped, continue?" if is_english( - [ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" + ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" yield ans else: - yield ans + "\n**ERROR**: " + resp.message if str(resp.message).find( - "Access") < 0 else "Out of credit. Please set the API key in **settings > Model providers.**" + yield ans + "\n**ERROR**: " + resp.message if str(resp.message).find("Access") < 0 else "Out of credit. Please set the API key in **settings > Model providers.**" except Exception as e: yield ans + "\n**ERROR**: " + str(e) @@ -373,6 +373,8 @@ def chat_streamly(self, system, history, gen_conf, image=""): class Zhipu4V(Base): + _FACTORY_NAME = "ZHIPU-AI" + def __init__(self, key, model_name="glm-4v", lang="Chinese", **kwargs): self.client = ZhipuAI(api_key=key) self.model_name = model_name @@ -394,10 +396,7 @@ def describe_with_prompt(self, image, prompt=None): b64 = self.image2base64(image) vision_prompt = self.vision_llm_prompt(b64, prompt) if prompt else self.vision_llm_prompt(b64) - res = self.client.chat.completions.create( - model=self.model_name, - messages=vision_prompt - ) + res = self.client.chat.completions.create(model=self.model_name, messages=vision_prompt) return res.choices[0].message.content.strip(), res.usage.total_tokens def chat(self, system, history, gen_conf, image=""): @@ -412,7 +411,7 @@ def chat(self, system, history, gen_conf, image=""): model=self.model_name, messages=history, temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7) + top_p=gen_conf.get("top_p", 0.7), ) return response.choices[0].message.content.strip(), response.usage.total_tokens except Exception as e: @@ -434,7 +433,7 @@ def chat_streamly(self, system, history, gen_conf, image=""): messages=history, temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7), - stream=True + stream=True, ) for resp in response: if not resp.choices[0].delta.content: @@ -442,8 +441,7 @@ def chat_streamly(self, system, history, gen_conf, image=""): delta = resp.choices[0].delta.content ans += delta if resp.choices[0].finish_reason == "length": - ans += "...\nFor the content length reason, it stopped, continue?" if is_english( - [ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" + ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" tk_count = resp.usage.total_tokens if resp.choices[0].finish_reason == "stop": tk_count = resp.usage.total_tokens @@ -455,6 +453,8 @@ def chat_streamly(self, system, history, gen_conf, image=""): class OllamaCV(Base): + _FACTORY_NAME = "Ollama" + def __init__(self, key, model_name, lang="Chinese", **kwargs): self.client = Client(host=kwargs["base_url"]) self.model_name = model_name @@ -466,7 +466,7 @@ def describe(self, image): response = self.client.generate( model=self.model_name, prompt=prompt[0]["content"][1]["text"], - images=[image] + images=[image], ) ans = response["response"].strip() return ans, 128 @@ -507,7 +507,7 @@ def chat(self, system, history, gen_conf, image=""): model=self.model_name, messages=history, options=options, - keep_alive=-1 + keep_alive=-1, ) ans = response["message"]["content"].strip() @@ -538,7 +538,7 @@ def chat_streamly(self, system, history, gen_conf, image=""): messages=history, stream=True, options=options, - keep_alive=-1 + keep_alive=-1, ) for resp in response: if resp["done"]: @@ -551,6 +551,8 @@ def chat_streamly(self, system, history, gen_conf, image=""): class LocalAICV(GptV4): + _FACTORY_NAME = "LocalAI" + def __init__(self, key, model_name, base_url, lang="Chinese"): if not base_url: raise ValueError("Local cv model url cannot be None") @@ -561,6 +563,8 @@ def __init__(self, key, model_name, base_url, lang="Chinese"): class XinferenceCV(Base): + _FACTORY_NAME = "Xinference" + def __init__(self, key, model_name="", lang="Chinese", base_url=""): base_url = urljoin(base_url, "v1") self.client = OpenAI(api_key=key, base_url=base_url) @@ -570,10 +574,7 @@ def __init__(self, key, model_name="", lang="Chinese", base_url=""): def describe(self, image): b64 = self.image2base64(image) - res = self.client.chat.completions.create( - model=self.model_name, - messages=self.prompt(b64) - ) + res = self.client.chat.completions.create(model=self.model_name, messages=self.prompt(b64)) return res.choices[0].message.content.strip(), res.usage.total_tokens def describe_with_prompt(self, image, prompt=None): @@ -588,8 +589,11 @@ def describe_with_prompt(self, image, prompt=None): class GeminiCV(Base): + _FACTORY_NAME = "Gemini" + def __init__(self, key, model_name="gemini-1.0-pro-vision-latest", lang="Chinese", **kwargs): from google.generativeai import GenerativeModel, client + client.configure(api_key=key) _client = client.get_default_generative_client() self.model_name = model_name @@ -599,18 +603,21 @@ def __init__(self, key, model_name="gemini-1.0-pro-vision-latest", lang="Chinese def describe(self, image): from PIL.Image import open - prompt = "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" if self.lang.lower() == "chinese" else \ - "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out." + + prompt = ( + "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" + if self.lang.lower() == "chinese" + else "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out." + ) b64 = self.image2base64(image) img = open(BytesIO(base64.b64decode(b64))) input = [prompt, img] - res = self.model.generate_content( - input - ) + res = self.model.generate_content(input) return res.text, res.usage_metadata.total_token_count def describe_with_prompt(self, image, prompt=None): from PIL.Image import open + b64 = self.image2base64(image) vision_prompt = self.vision_llm_prompt(b64, prompt) if prompt else self.vision_llm_prompt(b64) img = open(BytesIO(base64.b64decode(b64))) @@ -622,6 +629,7 @@ def describe_with_prompt(self, image, prompt=None): def chat(self, system, history, gen_conf, image=""): from transformers import GenerationConfig + if system: history[-1]["content"] = system + history[-1]["content"] + "user query: " + history[-1]["content"] try: @@ -635,9 +643,7 @@ def chat(self, system, history, gen_conf, image=""): his.pop("content") history[-1]["parts"].append("data:image/jpeg;base64," + image) - response = self.model.generate_content(history, generation_config=GenerationConfig( - temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7))) + response = self.model.generate_content(history, generation_config=GenerationConfig(temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7))) ans = response.text return ans, response.usage_metadata.total_token_count @@ -646,6 +652,7 @@ def chat(self, system, history, gen_conf, image=""): def chat_streamly(self, system, history, gen_conf, image=""): from transformers import GenerationConfig + if system: history[-1]["content"] = system + history[-1]["content"] + "user query: " + history[-1]["content"] @@ -661,9 +668,11 @@ def chat_streamly(self, system, history, gen_conf, image=""): his.pop("content") history[-1]["parts"].append("data:image/jpeg;base64," + image) - response = self.model.generate_content(history, generation_config=GenerationConfig( - temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7)), stream=True) + response = self.model.generate_content( + history, + generation_config=GenerationConfig(temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7)), + stream=True, + ) for resp in response: if not resp.text: @@ -677,6 +686,8 @@ def chat_streamly(self, system, history, gen_conf, image=""): class OpenRouterCV(GptV4): + _FACTORY_NAME = "OpenRouter" + def __init__( self, key, @@ -692,6 +703,8 @@ def __init__( class LocalCV(Base): + _FACTORY_NAME = "Moonshot" + def __init__(self, key, model_name="glm-4v", lang="Chinese", **kwargs): pass @@ -700,6 +713,8 @@ def describe(self, image): class NvidiaCV(Base): + _FACTORY_NAME = "NVIDIA" + def __init__( self, key, @@ -726,9 +741,7 @@ def describe(self, image): "content-type": "application/json", "Authorization": f"Bearer {self.key}", }, - json={ - "messages": self.prompt(b64) - }, + json={"messages": self.prompt(b64)}, ) response = response.json() return ( @@ -774,10 +787,7 @@ def vision_llm_prompt(self, b64, prompt=None): return [ { "role": "user", - "content": ( - prompt if prompt else vision_llm_describe_prompt() - ) - + f' ', + "content": (prompt if prompt else vision_llm_describe_prompt()) + f' ', } ] @@ -791,6 +801,8 @@ def chat_prompt(self, text, b64): class StepFunCV(GptV4): + _FACTORY_NAME = "StepFun" + def __init__(self, key, model_name="step-1v-8k", lang="Chinese", base_url="https://api.stepfun.com/v1"): if not base_url: base_url = "https://api.stepfun.com/v1" @@ -800,6 +812,8 @@ def __init__(self, key, model_name="step-1v-8k", lang="Chinese", base_url="https class LmStudioCV(GptV4): + _FACTORY_NAME = "LM-Studio" + def __init__(self, key, model_name, lang="Chinese", base_url=""): if not base_url: raise ValueError("Local llm url cannot be None") @@ -810,6 +824,8 @@ def __init__(self, key, model_name, lang="Chinese", base_url=""): class OpenAI_APICV(GptV4): + _FACTORY_NAME = ["VLLM", "OpenAI-API-Compatible"] + def __init__(self, key, model_name, lang="Chinese", base_url=""): if not base_url: raise ValueError("url cannot be None") @@ -820,6 +836,8 @@ def __init__(self, key, model_name, lang="Chinese", base_url=""): class TogetherAICV(GptV4): + _FACTORY_NAME = "TogetherAI" + def __init__(self, key, model_name, lang="Chinese", base_url="https://api.together.xyz/v1"): if not base_url: base_url = "https://api.together.xyz/v1" @@ -827,20 +845,38 @@ def __init__(self, key, model_name, lang="Chinese", base_url="https://api.togeth class YiCV(GptV4): - def __init__(self, key, model_name, lang="Chinese", base_url="https://api.lingyiwanwu.com/v1",): + _FACTORY_NAME = "01.AI" + + def __init__( + self, + key, + model_name, + lang="Chinese", + base_url="https://api.lingyiwanwu.com/v1", + ): if not base_url: base_url = "https://api.lingyiwanwu.com/v1" super().__init__(key, model_name, lang, base_url) class SILICONFLOWCV(GptV4): - def __init__(self, key, model_name, lang="Chinese", base_url="https://api.siliconflow.cn/v1",): + _FACTORY_NAME = "SILICONFLOW" + + def __init__( + self, + key, + model_name, + lang="Chinese", + base_url="https://api.siliconflow.cn/v1", + ): if not base_url: base_url = "https://api.siliconflow.cn/v1" super().__init__(key, model_name, lang, base_url) class HunyuanCV(Base): + _FACTORY_NAME = "Tencent Hunyuan" + def __init__(self, key, model_name, lang="Chinese", base_url=None): from tencentcloud.common import credential from tencentcloud.hunyuan.v20230901 import hunyuan_client @@ -895,14 +931,13 @@ def prompt(self, b64): "Contents": [ { "Type": "image_url", - "ImageUrl": { - "Url": f"data:image/jpeg;base64,{b64}" - }, + "ImageUrl": {"Url": f"data:image/jpeg;base64,{b64}"}, }, { "Type": "text", - "Text": "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" if self.lang.lower() == "chinese" else - "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out.", + "Text": "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" + if self.lang.lower() == "chinese" + else "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out.", }, ], } @@ -910,6 +945,8 @@ def prompt(self, b64): class AnthropicCV(Base): + _FACTORY_NAME = "Anthropic" + def __init__(self, key, model_name, base_url=None): import anthropic @@ -933,38 +970,29 @@ def prompt(self, b64, prompt): "data": b64, }, }, - { - "type": "text", - "text": prompt - } + {"type": "text", "text": prompt}, ], } ] def describe(self, image): b64 = self.image2base64(image) - prompt = self.prompt(b64, - "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" if self.lang.lower() == "chinese" else - "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out." - ) - - response = self.client.messages.create( - model=self.model_name, - max_tokens=self.max_tokens, - messages=prompt + prompt = self.prompt( + b64, + "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" + if self.lang.lower() == "chinese" + else "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out.", ) - return response["content"][0]["text"].strip(), response["usage"]["input_tokens"]+response["usage"]["output_tokens"] + + response = self.client.messages.create(model=self.model_name, max_tokens=self.max_tokens, messages=prompt) + return response["content"][0]["text"].strip(), response["usage"]["input_tokens"] + response["usage"]["output_tokens"] def describe_with_prompt(self, image, prompt=None): b64 = self.image2base64(image) prompt = self.prompt(b64, prompt if prompt else vision_llm_describe_prompt()) - response = self.client.messages.create( - model=self.model_name, - max_tokens=self.max_tokens, - messages=prompt - ) - return response["content"][0]["text"].strip(), response["usage"]["input_tokens"]+response["usage"]["output_tokens"] + response = self.client.messages.create(model=self.model_name, max_tokens=self.max_tokens, messages=prompt) + return response["content"][0]["text"].strip(), response["usage"]["input_tokens"] + response["usage"]["output_tokens"] def chat(self, system, history, gen_conf): if "presence_penalty" in gen_conf: @@ -984,11 +1012,7 @@ def chat(self, system, history, gen_conf): ).to_dict() ans = response["content"][0]["text"] if response["stop_reason"] == "max_tokens": - ans += ( - "...\nFor the content length reason, it stopped, continue?" - if is_english([ans]) - else "······\n由于长度的原因,回答被截断了,要继续吗?" - ) + ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?" return ( ans, response["usage"]["input_tokens"] + response["usage"]["output_tokens"], @@ -1014,7 +1038,7 @@ def chat_streamly(self, system, history, gen_conf): **gen_conf, ) for res in response: - if res.type == 'content_block_delta': + if res.type == "content_block_delta": if res.delta.type == "thinking_delta" and res.delta.thinking: if ans.find("") < 0: ans += "" @@ -1030,7 +1054,10 @@ def chat_streamly(self, system, history, gen_conf): yield total_tokens + class GPUStackCV(GptV4): + _FACTORY_NAME = "GPUStack" + def __init__(self, key, model_name, lang="Chinese", base_url=""): if not base_url: raise ValueError("Local llm url cannot be None") @@ -1041,11 +1068,13 @@ def __init__(self, key, model_name, lang="Chinese", base_url=""): class GoogleCV(Base): + _FACTORY_NAME = "Google Cloud" + def __init__(self, key, model_name, lang="Chinese", base_url=None, **kwargs): import base64 from google.oauth2 import service_account - + key = json.loads(key) access_token = json.loads(base64.b64decode(key.get("google_service_account_key", ""))) project_id = key.get("google_project_id", "") @@ -1079,9 +1108,12 @@ def __init__(self, key, model_name, lang="Chinese", base_url=None, **kwargs): self.client = glm.GenerativeModel(model_name=self.model_name) def describe(self, image): - prompt = "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" if self.lang.lower() == "chinese" else \ - "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out." - + prompt = ( + "请用中文详细描述一下图中的内容,比如时间,地点,人物,事情,人物心情等,如果有数据请提取出数据。" + if self.lang.lower() == "chinese" + else "Please describe the content of this picture, like where, when, who, what happen. If it has number data, please extract them out." + ) + if "claude" in self.model_name: b64 = self.image2base64(image) vision_prompt = [ @@ -1096,28 +1128,22 @@ def describe(self, image): "data": b64, }, }, - { - "type": "text", - "text": prompt - } + {"type": "text", "text": prompt}, ], } ] response = self.client.messages.create( model=self.model_name, max_tokens=8192, - messages=vision_prompt + messages=vision_prompt, ) return response.content[0].text.strip(), response.usage.input_tokens + response.usage.output_tokens else: import vertexai.generative_models as glm - + b64 = self.image2base64(image) # Create proper image part for Gemini - image_part = glm.Part.from_data( - data=base64.b64decode(b64), - mime_type="image/jpeg" - ) + image_part = glm.Part.from_data(data=base64.b64decode(b64), mime_type="image/jpeg") input = [prompt, image_part] res = self.client.generate_content(input) return res.text, res.usage_metadata.total_token_count @@ -1137,29 +1163,19 @@ def describe_with_prompt(self, image, prompt=None): "data": b64, }, }, - { - "type": "text", - "text": prompt if prompt else vision_llm_describe_prompt() - } + {"type": "text", "text": prompt if prompt else vision_llm_describe_prompt()}, ], } ] - response = self.client.messages.create( - model=self.model_name, - max_tokens=8192, - messages=vision_prompt - ) + response = self.client.messages.create(model=self.model_name, max_tokens=8192, messages=vision_prompt) return response.content[0].text.strip(), response.usage.input_tokens + response.usage.output_tokens else: import vertexai.generative_models as glm - + b64 = self.image2base64(image) vision_prompt = prompt if prompt else vision_llm_describe_prompt() # Create proper image part for Gemini - image_part = glm.Part.from_data( - data=base64.b64decode(b64), - mime_type="image/jpeg" - ) + image_part = glm.Part.from_data(data=base64.b64decode(b64), mime_type="image/jpeg") input = [vision_prompt, image_part] res = self.client.generate_content(input) return res.text, res.usage_metadata.total_token_count @@ -1180,25 +1196,17 @@ def chat(self, system, history, gen_conf, image=""): "data": image, }, }, - { - "type": "text", - "text": his["content"] - } + {"type": "text", "text": his["content"]}, ] - response = self.client.messages.create( - model=self.model_name, - max_tokens=8192, - messages=history, - temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7) - ) + response = self.client.messages.create(model=self.model_name, max_tokens=8192, messages=history, temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7)) return response.content[0].text.strip(), response.usage.input_tokens + response.usage.output_tokens except Exception as e: return "**ERROR**: " + str(e), 0 else: import vertexai.generative_models as glm from transformers import GenerationConfig + if system: history[-1]["content"] = system + history[-1]["content"] + "user query: " + history[-1]["content"] try: @@ -1210,20 +1218,15 @@ def chat(self, system, history, gen_conf, image=""): if his["role"] == "user": his["parts"] = [his["content"]] his.pop("content") - + # Create proper image part for Gemini img_bytes = base64.b64decode(image) - image_part = glm.Part.from_data( - data=img_bytes, - mime_type="image/jpeg" - ) + image_part = glm.Part.from_data(data=img_bytes, mime_type="image/jpeg") history[-1]["parts"].append(image_part) - response = self.client.generate_content(history, generation_config=GenerationConfig( - temperature=gen_conf.get("temperature", 0.3), - top_p=gen_conf.get("top_p", 0.7))) + response = self.client.generate_content(history, generation_config=GenerationConfig(temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7))) ans = response.text return ans, response.usage_metadata.total_token_count except Exception as e: - return "**ERROR**: " + str(e), 0 \ No newline at end of file + return "**ERROR**: " + str(e), 0 diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index f53d492b968..fbfb7468b52 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -13,28 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json import logging +import os import re import threading +from abc import ABC from urllib.parse import urljoin +import dashscope +import google.generativeai as genai +import numpy as np import requests from huggingface_hub import snapshot_download -from zhipuai import ZhipuAI -import os -from abc import ABC from ollama import Client -import dashscope from openai import OpenAI -import numpy as np -import asyncio +from zhipuai import ZhipuAI from api import settings from api.utils.file_utils import get_home_cache_dir from api.utils.log_utils import log_exception from rag.utils import num_tokens_from_string, truncate -import google.generativeai as genai -import json class Base(ABC): @@ -60,7 +59,8 @@ def total_token_count(self, resp): class DefaultEmbedding(Base): - os.environ['CUDA_VISIBLE_DEVICES'] = '0' + _FACTORY_NAME = "BAAI" + os.environ["CUDA_VISIBLE_DEVICES"] = "0" _model = None _model_name = "" _model_lock = threading.Lock() @@ -79,21 +79,22 @@ def __init__(self, key, model_name, **kwargs): """ if not settings.LIGHTEN: with DefaultEmbedding._model_lock: - from FlagEmbedding import FlagModel import torch + from FlagEmbedding import FlagModel + if not DefaultEmbedding._model or model_name != DefaultEmbedding._model_name: try: - DefaultEmbedding._model = FlagModel(os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), - query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:", - use_fp16=torch.cuda.is_available()) + DefaultEmbedding._model = FlagModel( + os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), + query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:", + use_fp16=torch.cuda.is_available(), + ) DefaultEmbedding._model_name = model_name except Exception: - model_dir = snapshot_download(repo_id="BAAI/bge-large-zh-v1.5", - local_dir=os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), - local_dir_use_symlinks=False) - DefaultEmbedding._model = FlagModel(model_dir, - query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:", - use_fp16=torch.cuda.is_available()) + model_dir = snapshot_download( + repo_id="BAAI/bge-large-zh-v1.5", local_dir=os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), local_dir_use_symlinks=False + ) + DefaultEmbedding._model = FlagModel(model_dir, query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:", use_fp16=torch.cuda.is_available()) self._model = DefaultEmbedding._model self._model_name = DefaultEmbedding._model_name @@ -105,7 +106,7 @@ def encode(self, texts: list): token_count += num_tokens_from_string(t) ress = [] for i in range(0, len(texts), batch_size): - ress.extend(self._model.encode(texts[i:i + batch_size]).tolist()) + ress.extend(self._model.encode(texts[i : i + batch_size]).tolist()) return np.array(ress), token_count def encode_queries(self, text: str): @@ -114,8 +115,9 @@ def encode_queries(self, text: str): class OpenAIEmbed(Base): - def __init__(self, key, model_name="text-embedding-ada-002", - base_url="https://api.openai.com/v1"): + _FACTORY_NAME = "OpenAI" + + def __init__(self, key, model_name="text-embedding-ada-002", base_url="https://api.openai.com/v1"): if not base_url: base_url = "https://api.openai.com/v1" self.client = OpenAI(api_key=key, base_url=base_url) @@ -128,8 +130,7 @@ def encode(self, texts: list): ress = [] total_tokens = 0 for i in range(0, len(texts), batch_size): - res = self.client.embeddings.create(input=texts[i:i + batch_size], - model=self.model_name) + res = self.client.embeddings.create(input=texts[i : i + batch_size], model=self.model_name) try: ress.extend([d.embedding for d in res.data]) total_tokens += self.total_token_count(res) @@ -138,12 +139,13 @@ def encode(self, texts: list): return np.array(ress), total_tokens def encode_queries(self, text): - res = self.client.embeddings.create(input=[truncate(text, 8191)], - model=self.model_name) + res = self.client.embeddings.create(input=[truncate(text, 8191)], model=self.model_name) return np.array(res.data[0].embedding), self.total_token_count(res) class LocalAIEmbed(Base): + _FACTORY_NAME = "LocalAI" + def __init__(self, key, model_name, base_url): if not base_url: raise ValueError("Local embedding model url cannot be None") @@ -155,7 +157,7 @@ def encode(self, texts: list): batch_size = 16 ress = [] for i in range(0, len(texts), batch_size): - res = self.client.embeddings.create(input=texts[i:i + batch_size], model=self.model_name) + res = self.client.embeddings.create(input=texts[i : i + batch_size], model=self.model_name) try: ress.extend([d.embedding for d in res.data]) except Exception as _e: @@ -169,41 +171,42 @@ def encode_queries(self, text): class AzureEmbed(OpenAIEmbed): + _FACTORY_NAME = "Azure-OpenAI" + def __init__(self, key, model_name, **kwargs): from openai.lib.azure import AzureOpenAI - api_key = json.loads(key).get('api_key', '') - api_version = json.loads(key).get('api_version', '2024-02-01') + + api_key = json.loads(key).get("api_key", "") + api_version = json.loads(key).get("api_version", "2024-02-01") self.client = AzureOpenAI(api_key=api_key, azure_endpoint=kwargs["base_url"], api_version=api_version) self.model_name = model_name class BaiChuanEmbed(OpenAIEmbed): - def __init__(self, key, - model_name='Baichuan-Text-Embedding', - base_url='https://api.baichuan-ai.com/v1'): + _FACTORY_NAME = "BaiChuan" + + def __init__(self, key, model_name="Baichuan-Text-Embedding", base_url="https://api.baichuan-ai.com/v1"): if not base_url: base_url = "https://api.baichuan-ai.com/v1" super().__init__(key, model_name, base_url) class QWenEmbed(Base): + _FACTORY_NAME = "Tongyi-Qianwen" + def __init__(self, key, model_name="text_embedding_v2", **kwargs): self.key = key self.model_name = model_name def encode(self, texts: list): import dashscope + batch_size = 4 res = [] token_count = 0 texts = [truncate(t, 2048) for t in texts] for i in range(0, len(texts), batch_size): - resp = dashscope.TextEmbedding.call( - model=self.model_name, - input=texts[i:i + batch_size], - api_key=self.key, - text_type="document" - ) + resp = dashscope.TextEmbedding.call(model=self.model_name, input=texts[i : i + batch_size], api_key=self.key, text_type="document") try: embds = [[] for _ in range(len(resp["output"]["embeddings"]))] for e in resp["output"]["embeddings"]: @@ -216,20 +219,16 @@ def encode(self, texts: list): return np.array(res), token_count def encode_queries(self, text): - resp = dashscope.TextEmbedding.call( - model=self.model_name, - input=text[:2048], - api_key=self.key, - text_type="query" - ) + resp = dashscope.TextEmbedding.call(model=self.model_name, input=text[:2048], api_key=self.key, text_type="query") try: - return np.array(resp["output"]["embeddings"][0] - ["embedding"]), self.total_token_count(resp) + return np.array(resp["output"]["embeddings"][0]["embedding"]), self.total_token_count(resp) except Exception as _e: log_exception(_e, resp) class ZhipuEmbed(Base): + _FACTORY_NAME = "ZHIPU-AI" + def __init__(self, key, model_name="embedding-2", **kwargs): self.client = ZhipuAI(api_key=key) self.model_name = model_name @@ -246,8 +245,7 @@ def encode(self, texts: list): texts = [truncate(t, MAX_LEN) for t in texts] for txt in texts: - res = self.client.embeddings.create(input=txt, - model=self.model_name) + res = self.client.embeddings.create(input=txt, model=self.model_name) try: arr.append(res.data[0].embedding) tks_num += self.total_token_count(res) @@ -256,8 +254,7 @@ def encode(self, texts: list): return np.array(arr), tks_num def encode_queries(self, text): - res = self.client.embeddings.create(input=text, - model=self.model_name) + res = self.client.embeddings.create(input=text, model=self.model_name) try: return np.array(res.data[0].embedding), self.total_token_count(res) except Exception as _e: @@ -265,18 +262,17 @@ def encode_queries(self, text): class OllamaEmbed(Base): + _FACTORY_NAME = "Ollama" + def __init__(self, key, model_name, **kwargs): - self.client = Client(host=kwargs["base_url"]) if not key or key == "x" else \ - Client(host=kwargs["base_url"], headers={"Authorization": f"Bear {key}"}) + self.client = Client(host=kwargs["base_url"]) if not key or key == "x" else Client(host=kwargs["base_url"], headers={"Authorization": f"Bear {key}"}) self.model_name = model_name def encode(self, texts: list): arr = [] tks_num = 0 for txt in texts: - res = self.client.embeddings(prompt=txt, - model=self.model_name, - options={"use_mmap": True}) + res = self.client.embeddings(prompt=txt, model=self.model_name, options={"use_mmap": True}) try: arr.append(res["embedding"]) except Exception as _e: @@ -285,9 +281,7 @@ def encode(self, texts: list): return np.array(arr), tks_num def encode_queries(self, text): - res = self.client.embeddings(prompt=text, - model=self.model_name, - options={"use_mmap": True}) + res = self.client.embeddings(prompt=text, model=self.model_name, options={"use_mmap": True}) try: return np.array(res["embedding"]), 128 except Exception as _e: @@ -295,27 +289,28 @@ def encode_queries(self, text): class FastEmbed(DefaultEmbedding): - + _FACTORY_NAME = "FastEmbed" + def __init__( - self, - key: str | None = None, - model_name: str = "BAAI/bge-small-en-v1.5", - cache_dir: str | None = None, - threads: int | None = None, - **kwargs, + self, + key: str | None = None, + model_name: str = "BAAI/bge-small-en-v1.5", + cache_dir: str | None = None, + threads: int | None = None, + **kwargs, ): if not settings.LIGHTEN: with FastEmbed._model_lock: from fastembed import TextEmbedding + if not DefaultEmbedding._model or model_name != DefaultEmbedding._model_name: try: DefaultEmbedding._model = TextEmbedding(model_name, cache_dir, threads, **kwargs) DefaultEmbedding._model_name = model_name except Exception: - cache_dir = snapshot_download(repo_id="BAAI/bge-small-en-v1.5", - local_dir=os.path.join(get_home_cache_dir(), - re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), - local_dir_use_symlinks=False) + cache_dir = snapshot_download( + repo_id="BAAI/bge-small-en-v1.5", local_dir=os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), local_dir_use_symlinks=False + ) DefaultEmbedding._model = TextEmbedding(model_name, cache_dir, threads, **kwargs) self._model = DefaultEmbedding._model self._model_name = model_name @@ -340,6 +335,8 @@ def encode_queries(self, text: str): class XinferenceEmbed(Base): + _FACTORY_NAME = "Xinference" + def __init__(self, key, model_name="", base_url=""): base_url = urljoin(base_url, "v1") self.client = OpenAI(api_key=key, base_url=base_url) @@ -350,7 +347,7 @@ def encode(self, texts: list): ress = [] total_tokens = 0 for i in range(0, len(texts), batch_size): - res = self.client.embeddings.create(input=texts[i:i + batch_size], model=self.model_name) + res = self.client.embeddings.create(input=texts[i : i + batch_size], model=self.model_name) try: ress.extend([d.embedding for d in res.data]) total_tokens += self.total_token_count(res) @@ -359,8 +356,7 @@ def encode(self, texts: list): return np.array(ress), total_tokens def encode_queries(self, text): - res = self.client.embeddings.create(input=[text], - model=self.model_name) + res = self.client.embeddings.create(input=[text], model=self.model_name) try: return np.array(res.data[0].embedding), self.total_token_count(res) except Exception as _e: @@ -368,20 +364,18 @@ def encode_queries(self, text): class YoudaoEmbed(Base): + _FACTORY_NAME = "Youdao" _client = None def __init__(self, key=None, model_name="maidalun1020/bce-embedding-base_v1", **kwargs): if not settings.LIGHTEN and not YoudaoEmbed._client: from BCEmbedding import EmbeddingModel as qanthing + try: logging.info("LOADING BCE...") - YoudaoEmbed._client = qanthing(model_name_or_path=os.path.join( - get_home_cache_dir(), - "bce-embedding-base_v1")) + YoudaoEmbed._client = qanthing(model_name_or_path=os.path.join(get_home_cache_dir(), "bce-embedding-base_v1")) except Exception: - YoudaoEmbed._client = qanthing( - model_name_or_path=model_name.replace( - "maidalun1020", "InfiniFlow")) + YoudaoEmbed._client = qanthing(model_name_or_path=model_name.replace("maidalun1020", "InfiniFlow")) def encode(self, texts: list): batch_size = 10 @@ -390,7 +384,7 @@ def encode(self, texts: list): for t in texts: token_count += num_tokens_from_string(t) for i in range(0, len(texts), batch_size): - embds = YoudaoEmbed._client.encode(texts[i:i + batch_size]) + embds = YoudaoEmbed._client.encode(texts[i : i + batch_size]) res.extend(embds) return np.array(res), token_count @@ -400,14 +394,11 @@ def encode_queries(self, text): class JinaEmbed(Base): - def __init__(self, key, model_name="jina-embeddings-v3", - base_url="https://api.jina.ai/v1/embeddings"): + _FACTORY_NAME = "Jina" + def __init__(self, key, model_name="jina-embeddings-v3", base_url="https://api.jina.ai/v1/embeddings"): self.base_url = "https://api.jina.ai/v1/embeddings" - self.headers = { - "Content-Type": "application/json", - "Authorization": f"Bearer {key}" - } + self.headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"} self.model_name = model_name def encode(self, texts: list): @@ -416,11 +407,7 @@ def encode(self, texts: list): ress = [] token_count = 0 for i in range(0, len(texts), batch_size): - data = { - "model": self.model_name, - "input": texts[i:i + batch_size], - 'encoding_type': 'float' - } + data = {"model": self.model_name, "input": texts[i : i + batch_size], "encoding_type": "float"} response = requests.post(self.base_url, headers=self.headers, json=data) try: res = response.json() @@ -435,50 +422,12 @@ def encode_queries(self, text): return np.array(embds[0]), cnt -class InfinityEmbed(Base): - _model = None - - def __init__( - self, - model_names: list[str] = ("BAAI/bge-small-en-v1.5",), - engine_kwargs: dict = {}, - key = None, - ): - - from infinity_emb import EngineArgs - from infinity_emb.engine import AsyncEngineArray - - self._default_model = model_names[0] - self.engine_array = AsyncEngineArray.from_args([EngineArgs(model_name_or_path = model_name, **engine_kwargs) for model_name in model_names]) - - async def _embed(self, sentences: list[str], model_name: str = ""): - if not model_name: - model_name = self._default_model - engine = self.engine_array[model_name] - was_already_running = engine.is_running - if not was_already_running: - await engine.astart() - embeddings, usage = await engine.embed(sentences=sentences) - if not was_already_running: - await engine.astop() - return embeddings, usage - - def encode(self, texts: list[str], model_name: str = "") -> tuple[np.ndarray, int]: - # Using the internal tokenizer to encode the texts and get the total - # number of tokens - embeddings, usage = asyncio.run(self._embed(texts, model_name)) - return np.array(embeddings), usage - - def encode_queries(self, text: str) -> tuple[np.ndarray, int]: - # Using the internal tokenizer to encode the texts and get the total - # number of tokens - return self.encode([text]) - - class MistralEmbed(Base): - def __init__(self, key, model_name="mistral-embed", - base_url=None): + _FACTORY_NAME = "Mistral" + + def __init__(self, key, model_name="mistral-embed", base_url=None): from mistralai.client import MistralClient + self.client = MistralClient(api_key=key) self.model_name = model_name @@ -488,8 +437,7 @@ def encode(self, texts: list): ress = [] token_count = 0 for i in range(0, len(texts), batch_size): - res = self.client.embeddings(input=texts[i:i + batch_size], - model=self.model_name) + res = self.client.embeddings(input=texts[i : i + batch_size], model=self.model_name) try: ress.extend([d.embedding for d in res.data]) token_count += self.total_token_count(res) @@ -498,8 +446,7 @@ def encode(self, texts: list): return np.array(ress), token_count def encode_queries(self, text): - res = self.client.embeddings(input=[truncate(text, 8196)], - model=self.model_name) + res = self.client.embeddings(input=[truncate(text, 8196)], model=self.model_name) try: return np.array(res.data[0].embedding), self.total_token_count(res) except Exception as _e: @@ -507,30 +454,31 @@ def encode_queries(self, text): class BedrockEmbed(Base): - def __init__(self, key, model_name, - **kwargs): + _FACTORY_NAME = "Bedrock" + + def __init__(self, key, model_name, **kwargs): import boto3 - self.bedrock_ak = json.loads(key).get('bedrock_ak', '') - self.bedrock_sk = json.loads(key).get('bedrock_sk', '') - self.bedrock_region = json.loads(key).get('bedrock_region', '') + + self.bedrock_ak = json.loads(key).get("bedrock_ak", "") + self.bedrock_sk = json.loads(key).get("bedrock_sk", "") + self.bedrock_region = json.loads(key).get("bedrock_region", "") self.model_name = model_name - - if self.bedrock_ak == '' or self.bedrock_sk == '' or self.bedrock_region == '': + + if self.bedrock_ak == "" or self.bedrock_sk == "" or self.bedrock_region == "": # Try to create a client using the default credentials (AWS_PROFILE, AWS_DEFAULT_REGION, etc.) - self.client = boto3.client('bedrock-runtime') + self.client = boto3.client("bedrock-runtime") else: - self.client = boto3.client(service_name='bedrock-runtime', region_name=self.bedrock_region, - aws_access_key_id=self.bedrock_ak, aws_secret_access_key=self.bedrock_sk) + self.client = boto3.client(service_name="bedrock-runtime", region_name=self.bedrock_region, aws_access_key_id=self.bedrock_ak, aws_secret_access_key=self.bedrock_sk) def encode(self, texts: list): texts = [truncate(t, 8196) for t in texts] embeddings = [] token_count = 0 for text in texts: - if self.model_name.split('.')[0] == 'amazon': + if self.model_name.split(".")[0] == "amazon": body = {"inputText": text} - elif self.model_name.split('.')[0] == 'cohere': - body = {"texts": [text], "input_type": 'search_document'} + elif self.model_name.split(".")[0] == "cohere": + body = {"texts": [text], "input_type": "search_document"} response = self.client.invoke_model(modelId=self.model_name, body=json.dumps(body)) try: @@ -545,10 +493,10 @@ def encode(self, texts: list): def encode_queries(self, text): embeddings = [] token_count = num_tokens_from_string(text) - if self.model_name.split('.')[0] == 'amazon': + if self.model_name.split(".")[0] == "amazon": body = {"inputText": truncate(text, 8196)} - elif self.model_name.split('.')[0] == 'cohere': - body = {"texts": [truncate(text, 8196)], "input_type": 'search_query'} + elif self.model_name.split(".")[0] == "cohere": + body = {"texts": [truncate(text, 8196)], "input_type": "search_query"} response = self.client.invoke_model(modelId=self.model_name, body=json.dumps(body)) try: @@ -561,11 +509,12 @@ def encode_queries(self, text): class GeminiEmbed(Base): - def __init__(self, key, model_name='models/text-embedding-004', - **kwargs): + _FACTORY_NAME = "Gemini" + + def __init__(self, key, model_name="models/text-embedding-004", **kwargs): self.key = key - self.model_name = 'models/' + model_name - + self.model_name = "models/" + model_name + def encode(self, texts: list): texts = [truncate(t, 2048) for t in texts] token_count = sum(num_tokens_from_string(text) for text in texts) @@ -573,35 +522,27 @@ def encode(self, texts: list): batch_size = 16 ress = [] for i in range(0, len(texts), batch_size): - result = genai.embed_content( - model=self.model_name, - content=texts[i: i + batch_size], - task_type="retrieval_document", - title="Embedding of single string") + result = genai.embed_content(model=self.model_name, content=texts[i : i + batch_size], task_type="retrieval_document", title="Embedding of single string") try: - ress.extend(result['embedding']) + ress.extend(result["embedding"]) except Exception as _e: log_exception(_e, result) - return np.array(ress),token_count - + return np.array(ress), token_count + def encode_queries(self, text): genai.configure(api_key=self.key) - result = genai.embed_content( - model=self.model_name, - content=truncate(text,2048), - task_type="retrieval_document", - title="Embedding of single string") + result = genai.embed_content(model=self.model_name, content=truncate(text, 2048), task_type="retrieval_document", title="Embedding of single string") token_count = num_tokens_from_string(text) try: - return np.array(result['embedding']), token_count + return np.array(result["embedding"]), token_count except Exception as _e: log_exception(_e, result) class NvidiaEmbed(Base): - def __init__( - self, key, model_name, base_url="https://integrate.api.nvidia.com/v1/embeddings" - ): + _FACTORY_NAME = "NVIDIA" + + def __init__(self, key, model_name, base_url="https://integrate.api.nvidia.com/v1/embeddings"): if not base_url: base_url = "https://integrate.api.nvidia.com/v1/embeddings" self.api_key = key @@ -645,6 +586,8 @@ def encode_queries(self, text): class LmStudioEmbed(LocalAIEmbed): + _FACTORY_NAME = "LM-Studio" + def __init__(self, key, model_name, base_url): if not base_url: raise ValueError("Local llm url cannot be None") @@ -654,6 +597,8 @@ def __init__(self, key, model_name, base_url): class OpenAI_APIEmbed(OpenAIEmbed): + _FACTORY_NAME = ["VLLM", "OpenAI-API-Compatible"] + def __init__(self, key, model_name, base_url): if not base_url: raise ValueError("url cannot be None") @@ -663,6 +608,8 @@ def __init__(self, key, model_name, base_url): class CoHereEmbed(Base): + _FACTORY_NAME = "Cohere" + def __init__(self, key, model_name, base_url=None): from cohere import Client @@ -701,6 +648,8 @@ def encode_queries(self, text): class TogetherAIEmbed(OpenAIEmbed): + _FACTORY_NAME = "TogetherAI" + def __init__(self, key, model_name, base_url="https://api.together.xyz/v1"): if not base_url: base_url = "https://api.together.xyz/v1" @@ -708,6 +657,8 @@ def __init__(self, key, model_name, base_url="https://api.together.xyz/v1"): class PerfXCloudEmbed(OpenAIEmbed): + _FACTORY_NAME = "PerfXCloud" + def __init__(self, key, model_name, base_url="https://cloud.perfxlab.cn/v1"): if not base_url: base_url = "https://cloud.perfxlab.cn/v1" @@ -715,6 +666,8 @@ def __init__(self, key, model_name, base_url="https://cloud.perfxlab.cn/v1"): class UpstageEmbed(OpenAIEmbed): + _FACTORY_NAME = "Upstage" + def __init__(self, key, model_name, base_url="https://api.upstage.ai/v1/solar"): if not base_url: base_url = "https://api.upstage.ai/v1/solar" @@ -722,6 +675,8 @@ def __init__(self, key, model_name, base_url="https://api.upstage.ai/v1/solar"): class SILICONFLOWEmbed(Base): + _FACTORY_NAME = "SILICONFLOW" + def __init__(self, key, model_name, base_url="https://api.siliconflow.cn/v1/embeddings"): if not base_url: base_url = "https://api.siliconflow.cn/v1/embeddings" @@ -769,6 +724,8 @@ def encode_queries(self, text): class ReplicateEmbed(Base): + _FACTORY_NAME = "Replicate" + def __init__(self, key, model_name, base_url=None): from replicate.client import Client @@ -790,6 +747,8 @@ def encode_queries(self, text): class BaiduYiyanEmbed(Base): + _FACTORY_NAME = "BaiduYiyan" + def __init__(self, key, model_name, base_url=None): import qianfan @@ -821,6 +780,8 @@ def encode_queries(self, text): class VoyageEmbed(Base): + _FACTORY_NAME = "Voyage AI" + def __init__(self, key, model_name, base_url=None): import voyageai @@ -832,9 +793,7 @@ def encode(self, texts: list): ress = [] token_count = 0 for i in range(0, len(texts), batch_size): - res = self.client.embed( - texts=texts[i : i + batch_size], model=self.model_name, input_type="document" - ) + res = self.client.embed(texts=texts[i : i + batch_size], model=self.model_name, input_type="document") try: ress.extend(res.embeddings) token_count += res.total_tokens @@ -843,9 +802,7 @@ def encode(self, texts: list): return np.array(ress), token_count def encode_queries(self, text): - res = self.client.embed( - texts=text, model=self.model_name, input_type="query" - ) + res = self.client.embed(texts=text, model=self.model_name, input_type="query") try: return np.array(res.embeddings)[0], res.total_tokens except Exception as _e: @@ -853,6 +810,8 @@ def encode_queries(self, text): class HuggingFaceEmbed(Base): + _FACTORY_NAME = "HuggingFace" + def __init__(self, key, model_name, base_url=None): if not model_name: raise ValueError("Model name cannot be None") @@ -863,11 +822,7 @@ def __init__(self, key, model_name, base_url=None): def encode(self, texts: list): embeddings = [] for text in texts: - response = requests.post( - f"{self.base_url}/embed", - json={"inputs": text}, - headers={'Content-Type': 'application/json'} - ) + response = requests.post(f"{self.base_url}/embed", json={"inputs": text}, headers={"Content-Type": "application/json"}) if response.status_code == 200: embedding = response.json() embeddings.append(embedding[0]) @@ -876,11 +831,7 @@ def encode(self, texts: list): return np.array(embeddings), sum([num_tokens_from_string(text) for text in texts]) def encode_queries(self, text): - response = requests.post( - f"{self.base_url}/embed", - json={"inputs": text}, - headers={'Content-Type': 'application/json'} - ) + response = requests.post(f"{self.base_url}/embed", json={"inputs": text}, headers={"Content-Type": "application/json"}) if response.status_code == 200: embedding = response.json() return np.array(embedding[0]), num_tokens_from_string(text) @@ -889,15 +840,19 @@ def encode_queries(self, text): class VolcEngineEmbed(OpenAIEmbed): + _FACTORY_NAME = "VolcEngine" + def __init__(self, key, model_name, base_url="https://ark.cn-beijing.volces.com/api/v3"): if not base_url: base_url = "https://ark.cn-beijing.volces.com/api/v3" - ark_api_key = json.loads(key).get('ark_api_key', '') - model_name = json.loads(key).get('ep_id', '') + json.loads(key).get('endpoint_id', '') - super().__init__(ark_api_key,model_name,base_url) + ark_api_key = json.loads(key).get("ark_api_key", "") + model_name = json.loads(key).get("ep_id", "") + json.loads(key).get("endpoint_id", "") + super().__init__(ark_api_key, model_name, base_url) class GPUStackEmbed(OpenAIEmbed): + _FACTORY_NAME = "GPUStack" + def __init__(self, key, model_name, base_url): if not base_url: raise ValueError("url cannot be None") @@ -908,6 +863,8 @@ def __init__(self, key, model_name, base_url): class NovitaEmbed(SILICONFLOWEmbed): + _FACTORY_NAME = "NovitaAI" + def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/embeddings"): if not base_url: base_url = "https://api.novita.ai/v3/openai/embeddings" @@ -915,7 +872,9 @@ def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/em class GiteeEmbed(SILICONFLOWEmbed): + _FACTORY_NAME = "GiteeAI" + def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/embeddings"): if not base_url: base_url = "https://ai.gitee.com/v1/embeddings" - super().__init__(key, model_name, base_url) \ No newline at end of file + super().__init__(key, model_name, base_url) diff --git a/rag/llm/rerank_model.py b/rag/llm/rerank_model.py index fafab7ee09e..88fda1478f2 100644 --- a/rag/llm/rerank_model.py +++ b/rag/llm/rerank_model.py @@ -13,24 +13,24 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json +import os import re import threading +from abc import ABC from collections.abc import Iterable from urllib.parse import urljoin -import requests import httpx -from huggingface_hub import snapshot_download -import os -from abc import ABC import numpy as np +import requests +from huggingface_hub import snapshot_download from yarl import URL from api import settings from api.utils.file_utils import get_home_cache_dir from api.utils.log_utils import log_exception from rag.utils import num_tokens_from_string, truncate -import json def sigmoid(x): @@ -57,6 +57,7 @@ def total_token_count(self, resp): class DefaultRerank(Base): + _FACTORY_NAME = "BAAI" _model = None _model_lock = threading.Lock() @@ -75,17 +76,13 @@ def __init__(self, key, model_name, **kwargs): if not settings.LIGHTEN and not DefaultRerank._model: import torch from FlagEmbedding import FlagReranker + with DefaultRerank._model_lock: if not DefaultRerank._model: try: - DefaultRerank._model = FlagReranker( - os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), - use_fp16=torch.cuda.is_available()) + DefaultRerank._model = FlagReranker(os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), use_fp16=torch.cuda.is_available()) except Exception: - model_dir = snapshot_download(repo_id=model_name, - local_dir=os.path.join(get_home_cache_dir(), - re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), - local_dir_use_symlinks=False) + model_dir = snapshot_download(repo_id=model_name, local_dir=os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name)), local_dir_use_symlinks=False) DefaultRerank._model = FlagReranker(model_dir, use_fp16=torch.cuda.is_available()) self._model = DefaultRerank._model self._dynamic_batch_size = 8 @@ -94,6 +91,7 @@ def __init__(self, key, model_name, **kwargs): def torch_empty_cache(self): try: import torch + torch.cuda.empty_cache() except Exception as e: print(f"Error emptying cache: {e}") @@ -112,7 +110,7 @@ def _process_batch(self, pairs, max_batch_size=None): while retry_count < max_retries: try: # call subclass implemented batch processing calculation - batch_scores = self._compute_batch_scores(pairs[i:i + current_batch]) + batch_scores = self._compute_batch_scores(pairs[i : i + current_batch]) res.extend(batch_scores) i += current_batch self._dynamic_batch_size = min(self._dynamic_batch_size * 2, 8) @@ -152,23 +150,16 @@ def similarity(self, query: str, texts: list): class JinaRerank(Base): - def __init__(self, key, model_name="jina-reranker-v2-base-multilingual", - base_url="https://api.jina.ai/v1/rerank"): + _FACTORY_NAME = "Jina" + + def __init__(self, key, model_name="jina-reranker-v2-base-multilingual", base_url="https://api.jina.ai/v1/rerank"): self.base_url = "https://api.jina.ai/v1/rerank" - self.headers = { - "Content-Type": "application/json", - "Authorization": f"Bearer {key}" - } + self.headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"} self.model_name = model_name def similarity(self, query: str, texts: list): texts = [truncate(t, 8196) for t in texts] - data = { - "model": self.model_name, - "query": query, - "documents": texts, - "top_n": len(texts) - } + data = {"model": self.model_name, "query": query, "documents": texts, "top_n": len(texts)} res = requests.post(self.base_url, headers=self.headers, json=data).json() rank = np.zeros(len(texts), dtype=float) try: @@ -180,22 +171,20 @@ def similarity(self, query: str, texts: list): class YoudaoRerank(DefaultRerank): + _FACTORY_NAME = "Youdao" _model = None _model_lock = threading.Lock() def __init__(self, key=None, model_name="maidalun1020/bce-reranker-base_v1", **kwargs): if not settings.LIGHTEN and not YoudaoRerank._model: from BCEmbedding import RerankerModel + with YoudaoRerank._model_lock: if not YoudaoRerank._model: try: - YoudaoRerank._model = RerankerModel(model_name_or_path=os.path.join( - get_home_cache_dir(), - re.sub(r"^[a-zA-Z0-9]+/", "", model_name))) + YoudaoRerank._model = RerankerModel(model_name_or_path=os.path.join(get_home_cache_dir(), re.sub(r"^[a-zA-Z0-9]+/", "", model_name))) except Exception: - YoudaoRerank._model = RerankerModel( - model_name_or_path=model_name.replace( - "maidalun1020", "InfiniFlow")) + YoudaoRerank._model = RerankerModel(model_name_or_path=model_name.replace("maidalun1020", "InfiniFlow")) self._model = YoudaoRerank._model self._dynamic_batch_size = 8 @@ -212,6 +201,8 @@ def similarity(self, query: str, texts: list): class XInferenceRerank(Base): + _FACTORY_NAME = "Xinference" + def __init__(self, key="x", model_name="", base_url=""): if base_url.find("/v1") == -1: base_url = urljoin(base_url, "/v1/rerank") @@ -219,10 +210,7 @@ def __init__(self, key="x", model_name="", base_url=""): base_url = urljoin(base_url, "/v1/rerank") self.model_name = model_name self.base_url = base_url - self.headers = { - "Content-Type": "application/json", - "accept": "application/json" - } + self.headers = {"Content-Type": "application/json", "accept": "application/json"} if key and key != "x": self.headers["Authorization"] = f"Bearer {key}" @@ -233,13 +221,7 @@ def similarity(self, query: str, texts: list): token_count = 0 for _, t in pairs: token_count += num_tokens_from_string(t) - data = { - "model": self.model_name, - "query": query, - "return_documents": "true", - "return_len": "true", - "documents": texts - } + data = {"model": self.model_name, "query": query, "return_documents": "true", "return_len": "true", "documents": texts} res = requests.post(self.base_url, headers=self.headers, json=data).json() rank = np.zeros(len(texts), dtype=float) try: @@ -251,15 +233,14 @@ def similarity(self, query: str, texts: list): class LocalAIRerank(Base): + _FACTORY_NAME = "LocalAI" + def __init__(self, key, model_name, base_url): if base_url.find("/rerank") == -1: self.base_url = urljoin(base_url, "/rerank") else: self.base_url = base_url - self.headers = { - "Content-Type": "application/json", - "Authorization": f"Bearer {key}" - } + self.headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"} self.model_name = model_name.split("___")[0] def similarity(self, query: str, texts: list): @@ -296,16 +277,15 @@ def similarity(self, query: str, texts: list): class NvidiaRerank(Base): - def __init__( - self, key, model_name, base_url="https://ai.api.nvidia.com/v1/retrieval/nvidia/" - ): + _FACTORY_NAME = "NVIDIA" + + def __init__(self, key, model_name, base_url="https://ai.api.nvidia.com/v1/retrieval/nvidia/"): if not base_url: base_url = "https://ai.api.nvidia.com/v1/retrieval/nvidia/" self.model_name = model_name if self.model_name == "nvidia/nv-rerankqa-mistral-4b-v3": - self.base_url = urljoin(base_url, "nv-rerankqa-mistral-4b-v3/reranking" - ) + self.base_url = urljoin(base_url, "nv-rerankqa-mistral-4b-v3/reranking") if self.model_name == "nvidia/rerank-qa-mistral-4b": self.base_url = urljoin(base_url, "reranking") @@ -318,9 +298,7 @@ def __init__( } def similarity(self, query: str, texts: list): - token_count = num_tokens_from_string(query) + sum( - [num_tokens_from_string(t) for t in texts] - ) + token_count = num_tokens_from_string(query) + sum([num_tokens_from_string(t) for t in texts]) data = { "model": self.model_name, "query": {"text": query}, @@ -339,6 +317,8 @@ def similarity(self, query: str, texts: list): class LmStudioRerank(Base): + _FACTORY_NAME = "LM-Studio" + def __init__(self, key, model_name, base_url): pass @@ -347,15 +327,14 @@ def similarity(self, query: str, texts: list): class OpenAI_APIRerank(Base): + _FACTORY_NAME = "OpenAI-API-Compatible" + def __init__(self, key, model_name, base_url): if base_url.find("/rerank") == -1: self.base_url = urljoin(base_url, "/rerank") else: self.base_url = base_url - self.headers = { - "Content-Type": "application/json", - "Authorization": f"Bearer {key}" - } + self.headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"} self.model_name = model_name.split("___")[0] def similarity(self, query: str, texts: list): @@ -392,6 +371,8 @@ def similarity(self, query: str, texts: list): class CoHereRerank(Base): + _FACTORY_NAME = ["Cohere", "VLLM"] + def __init__(self, key, model_name, base_url=None): from cohere import Client @@ -399,9 +380,7 @@ def __init__(self, key, model_name, base_url=None): self.model_name = model_name.split("___")[0] def similarity(self, query: str, texts: list): - token_count = num_tokens_from_string(query) + sum( - [num_tokens_from_string(t) for t in texts] - ) + token_count = num_tokens_from_string(query) + sum([num_tokens_from_string(t) for t in texts]) res = self.client.rerank( model=self.model_name, query=query, @@ -419,6 +398,8 @@ def similarity(self, query: str, texts: list): class TogetherAIRerank(Base): + _FACTORY_NAME = "TogetherAI" + def __init__(self, key, model_name, base_url): pass @@ -427,9 +408,9 @@ def similarity(self, query: str, texts: list): class SILICONFLOWRerank(Base): - def __init__( - self, key, model_name, base_url="https://api.siliconflow.cn/v1/rerank" - ): + _FACTORY_NAME = "SILICONFLOW" + + def __init__(self, key, model_name, base_url="https://api.siliconflow.cn/v1/rerank"): if not base_url: base_url = "https://api.siliconflow.cn/v1/rerank" self.model_name = model_name @@ -450,9 +431,7 @@ def similarity(self, query: str, texts: list): "max_chunks_per_doc": 1024, "overlap_tokens": 80, } - response = requests.post( - self.base_url, json=payload, headers=self.headers - ).json() + response = requests.post(self.base_url, json=payload, headers=self.headers).json() rank = np.zeros(len(texts), dtype=float) try: for d in response["results"]: @@ -466,6 +445,8 @@ def similarity(self, query: str, texts: list): class BaiduYiyanRerank(Base): + _FACTORY_NAME = "BaiduYiyan" + def __init__(self, key, model_name, base_url=None): from qianfan.resources import Reranker @@ -492,6 +473,8 @@ def similarity(self, query: str, texts: list): class VoyageRerank(Base): + _FACTORY_NAME = "Voyage AI" + def __init__(self, key, model_name, base_url=None): import voyageai @@ -502,9 +485,7 @@ def similarity(self, query: str, texts: list): rank = np.zeros(len(texts), dtype=float) if not texts: return rank, 0 - res = self.client.rerank( - query=query, documents=texts, model=self.model_name, top_k=len(texts) - ) + res = self.client.rerank(query=query, documents=texts, model=self.model_name, top_k=len(texts)) try: for r in res.results: rank[r.index] = r.relevance_score @@ -514,22 +495,20 @@ def similarity(self, query: str, texts: list): class QWenRerank(Base): - def __init__(self, key, model_name='gte-rerank', base_url=None, **kwargs): + _FACTORY_NAME = "Tongyi-Qianwen" + + def __init__(self, key, model_name="gte-rerank", base_url=None, **kwargs): import dashscope + self.api_key = key self.model_name = dashscope.TextReRank.Models.gte_rerank if model_name is None else model_name def similarity(self, query: str, texts: list): - import dashscope from http import HTTPStatus - resp = dashscope.TextReRank.call( - api_key=self.api_key, - model=self.model_name, - query=query, - documents=texts, - top_n=len(texts), - return_documents=False - ) + + import dashscope + + resp = dashscope.TextReRank.call(api_key=self.api_key, model=self.model_name, query=query, documents=texts, top_n=len(texts), return_documents=False) rank = np.zeros(len(texts), dtype=float) if resp.status_code == HTTPStatus.OK: try: @@ -543,6 +522,8 @@ def similarity(self, query: str, texts: list): class HuggingfaceRerank(DefaultRerank): + _FACTORY_NAME = "HuggingFace" + @staticmethod def post(query: str, texts: list, url="127.0.0.1"): exc = None @@ -550,9 +531,9 @@ def post(query: str, texts: list, url="127.0.0.1"): batch_size = 8 for i in range(0, len(texts), batch_size): try: - res = requests.post(f"http://{url}/rerank", headers={"Content-Type": "application/json"}, - json={"query": query, "texts": texts[i: i + batch_size], - "raw_scores": False, "truncate": True}) + res = requests.post( + f"http://{url}/rerank", headers={"Content-Type": "application/json"}, json={"query": query, "texts": texts[i : i + batch_size], "raw_scores": False, "truncate": True} + ) for o in res.json(): scores[o["index"] + i] = o["score"] @@ -577,9 +558,9 @@ def similarity(self, query: str, texts: list) -> tuple[np.ndarray, int]: class GPUStackRerank(Base): - def __init__( - self, key, model_name, base_url - ): + _FACTORY_NAME = "GPUStack" + + def __init__(self, key, model_name, base_url): if not base_url: raise ValueError("url cannot be None") @@ -600,9 +581,7 @@ def similarity(self, query: str, texts: list): } try: - response = requests.post( - self.base_url, json=payload, headers=self.headers - ) + response = requests.post(self.base_url, json=payload, headers=self.headers) response.raise_for_status() response_json = response.json() @@ -623,11 +602,12 @@ def similarity(self, query: str, texts: list): ) except httpx.HTTPStatusError as e: - raise ValueError( - f"Error calling GPUStackRerank model {self.model_name}: {e.response.status_code} - {e.response.text}") + raise ValueError(f"Error calling GPUStackRerank model {self.model_name}: {e.response.status_code} - {e.response.text}") class NovitaRerank(JinaRerank): + _FACTORY_NAME = "NovitaAI" + def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/rerank"): if not base_url: base_url = "https://api.novita.ai/v3/openai/rerank" @@ -635,7 +615,9 @@ def __init__(self, key, model_name, base_url="https://api.novita.ai/v3/openai/re class GiteeRerank(JinaRerank): + _FACTORY_NAME = "GiteeAI" + def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/rerank"): if not base_url: base_url = "https://ai.gitee.com/v1/rerank" - super().__init__(key, model_name, base_url) \ No newline at end of file + super().__init__(key, model_name, base_url) diff --git a/rag/llm/sequence2txt_model.py b/rag/llm/sequence2txt_model.py index 193f0e14d7e..7d6c24b768f 100644 --- a/rag/llm/sequence2txt_model.py +++ b/rag/llm/sequence2txt_model.py @@ -13,16 +13,18 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import os -import requests -from openai.lib.azure import AzureOpenAI +import base64 import io +import json +import os +import re from abc import ABC + +import requests from openai import OpenAI -import json +from openai.lib.azure import AzureOpenAI + from rag.utils import num_tokens_from_string -import base64 -import re class Base(ABC): @@ -30,11 +32,7 @@ def __init__(self, key, model_name): pass def transcription(self, audio, **kwargs): - transcription = self.client.audio.transcriptions.create( - model=self.model_name, - file=audio, - response_format="text" - ) + transcription = self.client.audio.transcriptions.create(model=self.model_name, file=audio, response_format="text") return transcription.text.strip(), num_tokens_from_string(transcription.text.strip()) def audio2base64(self, audio): @@ -46,6 +44,8 @@ def audio2base64(self, audio): class GPTSeq2txt(Base): + _FACTORY_NAME = "OpenAI" + def __init__(self, key, model_name="whisper-1", base_url="https://api.openai.com/v1"): if not base_url: base_url = "https://api.openai.com/v1" @@ -54,31 +54,34 @@ def __init__(self, key, model_name="whisper-1", base_url="https://api.openai.com class QWenSeq2txt(Base): + _FACTORY_NAME = "Tongyi-Qianwen" + def __init__(self, key, model_name="paraformer-realtime-8k-v1", **kwargs): import dashscope + dashscope.api_key = key self.model_name = model_name def transcription(self, audio, format): from http import HTTPStatus + from dashscope.audio.asr import Recognition - recognition = Recognition(model=self.model_name, - format=format, - sample_rate=16000, - callback=None) + recognition = Recognition(model=self.model_name, format=format, sample_rate=16000, callback=None) result = recognition.call(audio) ans = "" if result.status_code == HTTPStatus.OK: for sentence in result.get_sentence(): - ans += sentence.text.decode('utf-8') + '\n' + ans += sentence.text.decode("utf-8") + "\n" return ans, num_tokens_from_string(ans) return "**ERROR**: " + result.message, 0 class AzureSeq2txt(Base): + _FACTORY_NAME = "Azure-OpenAI" + def __init__(self, key, model_name, lang="Chinese", **kwargs): self.client = AzureOpenAI(api_key=key, azure_endpoint=kwargs["base_url"], api_version="2024-02-01") self.model_name = model_name @@ -86,43 +89,33 @@ def __init__(self, key, model_name, lang="Chinese", **kwargs): class XinferenceSeq2txt(Base): + _FACTORY_NAME = "Xinference" + def __init__(self, key, model_name="whisper-small", **kwargs): - self.base_url = kwargs.get('base_url', None) + self.base_url = kwargs.get("base_url", None) self.model_name = model_name self.key = key def transcription(self, audio, language="zh", prompt=None, response_format="json", temperature=0.7): if isinstance(audio, str): - audio_file = open(audio, 'rb') + audio_file = open(audio, "rb") audio_data = audio_file.read() audio_file_name = audio.split("/")[-1] else: audio_data = audio audio_file_name = "audio.wav" - payload = { - "model": self.model_name, - "language": language, - "prompt": prompt, - "response_format": response_format, - "temperature": temperature - } + payload = {"model": self.model_name, "language": language, "prompt": prompt, "response_format": response_format, "temperature": temperature} - files = { - "file": (audio_file_name, audio_data, 'audio/wav') - } + files = {"file": (audio_file_name, audio_data, "audio/wav")} try: - response = requests.post( - f"{self.base_url}/v1/audio/transcriptions", - files=files, - data=payload - ) + response = requests.post(f"{self.base_url}/v1/audio/transcriptions", files=files, data=payload) response.raise_for_status() result = response.json() - if 'text' in result: - transcription_text = result['text'].strip() + if "text" in result: + transcription_text = result["text"].strip() return transcription_text, num_tokens_from_string(transcription_text) else: return "**ERROR**: Failed to retrieve transcription.", 0 @@ -132,11 +125,11 @@ def transcription(self, audio, language="zh", prompt=None, response_format="json class TencentCloudSeq2txt(Base): - def __init__( - self, key, model_name="16k_zh", base_url="https://asr.tencentcloudapi.com" - ): - from tencentcloud.common import credential + _FACTORY_NAME = "Tencent Cloud" + + def __init__(self, key, model_name="16k_zh", base_url="https://asr.tencentcloudapi.com"): from tencentcloud.asr.v20190614 import asr_client + from tencentcloud.common import credential key = json.loads(key) sid = key.get("tencent_cloud_sid", "") @@ -146,11 +139,12 @@ def __init__( self.model_name = model_name def transcription(self, audio, max_retries=60, retry_interval=5): + import time + + from tencentcloud.asr.v20190614 import models from tencentcloud.common.exception.tencent_cloud_sdk_exception import ( TencentCloudSDKException, ) - from tencentcloud.asr.v20190614 import models - import time b64 = self.audio2base64(audio) try: @@ -174,9 +168,7 @@ def transcription(self, audio, max_retries=60, retry_interval=5): while retries < max_retries: resp = self.client.DescribeTaskStatus(req) if resp.Data.StatusStr == "success": - text = re.sub( - r"\[\d+:\d+\.\d+,\d+:\d+\.\d+\]\s*", "", resp.Data.Result - ).strip() + text = re.sub(r"\[\d+:\d+\.\d+,\d+:\d+\.\d+\]\s*", "", resp.Data.Result).strip() return text, num_tokens_from_string(text) elif resp.Data.StatusStr == "failed": return ( @@ -195,6 +187,8 @@ def transcription(self, audio, max_retries=60, retry_interval=5): class GPUStackSeq2txt(Base): + _FACTORY_NAME = "GPUStack" + def __init__(self, key, model_name, base_url): if not base_url: raise ValueError("url cannot be None") @@ -206,8 +200,11 @@ def __init__(self, key, model_name, base_url): class GiteeSeq2txt(Base): + _FACTORY_NAME = "GiteeAI" + def __init__(self, key, model_name="whisper-1", base_url="https://ai.gitee.com/v1/"): if not base_url: base_url = "https://ai.gitee.com/v1/" self.client = OpenAI(api_key=key, base_url=base_url) - self.model_name = model_name \ No newline at end of file + self.model_name = model_name + diff --git a/rag/llm/tts_model.py b/rag/llm/tts_model.py index 7111b2f6093..b2333b426e0 100644 --- a/rag/llm/tts_model.py +++ b/rag/llm/tts_model.py @@ -70,10 +70,12 @@ def tts(self, audio): pass def normalize_text(self, text): - return re.sub(r'(\*\*|##\d+\$\$|#)', '', text) + return re.sub(r"(\*\*|##\d+\$\$|#)", "", text) class FishAudioTTS(Base): + _FACTORY_NAME = "Fish Audio" + def __init__(self, key, model_name, base_url="https://api.fish.audio/v1/tts"): if not base_url: base_url = "https://api.fish.audio/v1/tts" @@ -94,13 +96,11 @@ def tts(self, text): with httpx.Client() as client: try: with client.stream( - method="POST", - url=self.base_url, - content=ormsgpack.packb( - request, option=ormsgpack.OPT_SERIALIZE_PYDANTIC - ), - headers=self.headers, - timeout=None, + method="POST", + url=self.base_url, + content=ormsgpack.packb(request, option=ormsgpack.OPT_SERIALIZE_PYDANTIC), + headers=self.headers, + timeout=None, ) as response: if response.status_code == HTTPStatus.OK: for chunk in response.iter_bytes(): @@ -115,6 +115,8 @@ def tts(self, text): class QwenTTS(Base): + _FACTORY_NAME = "Tongyi-Qianwen" + def __init__(self, key, model_name, base_url=""): import dashscope @@ -122,10 +124,11 @@ def __init__(self, key, model_name, base_url=""): dashscope.api_key = key def tts(self, text): - from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse - from dashscope.audio.tts import ResultCallback, SpeechSynthesizer, SpeechSynthesisResult from collections import deque + from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse + from dashscope.audio.tts import ResultCallback, SpeechSynthesisResult, SpeechSynthesizer + class Callback(ResultCallback): def __init__(self) -> None: self.dque = deque() @@ -159,10 +162,7 @@ def on_event(self, result: SpeechSynthesisResult): text = self.normalize_text(text) callback = Callback() - SpeechSynthesizer.call(model=self.model_name, - text=text, - callback=callback, - format="mp3") + SpeechSynthesizer.call(model=self.model_name, text=text, callback=callback, format="mp3") try: for data in callback._run(): yield data @@ -173,24 +173,19 @@ def on_event(self, result: SpeechSynthesisResult): class OpenAITTS(Base): + _FACTORY_NAME = "OpenAI" + def __init__(self, key, model_name="tts-1", base_url="https://api.openai.com/v1"): if not base_url: base_url = "https://api.openai.com/v1" self.api_key = key self.model_name = model_name self.base_url = base_url - self.headers = { - "Authorization": f"Bearer {self.api_key}", - "Content-Type": "application/json" - } + self.headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} def tts(self, text, voice="alloy"): text = self.normalize_text(text) - payload = { - "model": self.model_name, - "voice": voice, - "input": text - } + payload = {"model": self.model_name, "voice": voice, "input": text} response = requests.post(f"{self.base_url}/audio/speech", headers=self.headers, json=payload, stream=True) @@ -201,7 +196,8 @@ def tts(self, text, voice="alloy"): yield chunk -class SparkTTS: +class SparkTTS(Base): + _FACTORY_NAME = "XunFei Spark" STATUS_FIRST_FRAME = 0 STATUS_CONTINUE_FRAME = 1 STATUS_LAST_FRAME = 2 @@ -219,29 +215,23 @@ def __init__(self, key, model_name, base_url=""): # 生成url def create_url(self): - url = 'wss://tts-api.xfyun.cn/v2/tts' + url = "wss://tts-api.xfyun.cn/v2/tts" now = datetime.now() date = format_date_time(mktime(now.timetuple())) signature_origin = "host: " + "ws-api.xfyun.cn" + "\n" signature_origin += "date: " + date + "\n" signature_origin += "GET " + "/v2/tts " + "HTTP/1.1" - signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'), - digestmod=hashlib.sha256).digest() - signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8') - authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % ( - self.APIKey, "hmac-sha256", "host date request-line", signature_sha) - authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8') - v = { - "authorization": authorization, - "date": date, - "host": "ws-api.xfyun.cn" - } - url = url + '?' + urlencode(v) + signature_sha = hmac.new(self.APISecret.encode("utf-8"), signature_origin.encode("utf-8"), digestmod=hashlib.sha256).digest() + signature_sha = base64.b64encode(signature_sha).decode(encoding="utf-8") + authorization_origin = 'api_key="%s", algorithm="%s", headers="%s", signature="%s"' % (self.APIKey, "hmac-sha256", "host date request-line", signature_sha) + authorization = base64.b64encode(authorization_origin.encode("utf-8")).decode(encoding="utf-8") + v = {"authorization": authorization, "date": date, "host": "ws-api.xfyun.cn"} + url = url + "?" + urlencode(v) return url def tts(self, text): BusinessArgs = {"aue": "lame", "sfl": 1, "auf": "audio/L16;rate=16000", "vcn": self.model_name, "tte": "utf8"} - Data = {"status": 2, "text": base64.b64encode(text.encode('utf-8')).decode('utf-8')} + Data = {"status": 2, "text": base64.b64encode(text.encode("utf-8")).decode("utf-8")} CommonArgs = {"app_id": self.APPID} audio_queue = self.audio_queue model_name = self.model_name @@ -273,9 +263,7 @@ def on_close(self, ws, close_status_code, close_msg): def on_open(self, ws): def run(*args): - d = {"common": CommonArgs, - "business": BusinessArgs, - "data": Data} + d = {"common": CommonArgs, "business": BusinessArgs, "data": Data} ws.send(json.dumps(d)) thread.start_new_thread(run, ()) @@ -283,44 +271,32 @@ def run(*args): wsUrl = self.create_url() websocket.enableTrace(False) a = Callback() - ws = websocket.WebSocketApp(wsUrl, on_open=a.on_open, on_error=a.on_error, on_close=a.on_close, - on_message=a.on_message) + ws = websocket.WebSocketApp(wsUrl, on_open=a.on_open, on_error=a.on_error, on_close=a.on_close, on_message=a.on_message) status_code = 0 ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) while True: audio_chunk = self.audio_queue.get() if audio_chunk is None: if status_code == 0: - raise Exception( - f"Fail to access model({model_name}) using the provided credentials. **ERROR**: Invalid APPID, API Secret, or API Key.") + raise Exception(f"Fail to access model({model_name}) using the provided credentials. **ERROR**: Invalid APPID, API Secret, or API Key.") else: break status_code = 1 yield audio_chunk -class XinferenceTTS: +class XinferenceTTS(Base): + _FACTORY_NAME = "Xinference" + def __init__(self, key, model_name, **kwargs): self.base_url = kwargs.get("base_url", None) self.model_name = model_name - self.headers = { - "accept": "application/json", - "Content-Type": "application/json" - } + self.headers = {"accept": "application/json", "Content-Type": "application/json"} def tts(self, text, voice="中文女", stream=True): - payload = { - "model": self.model_name, - "input": text, - "voice": voice - } + payload = {"model": self.model_name, "input": text, "voice": voice} - response = requests.post( - f"{self.base_url}/v1/audio/speech", - headers=self.headers, - json=payload, - stream=stream - ) + response = requests.post(f"{self.base_url}/v1/audio/speech", headers=self.headers, json=payload, stream=stream) if response.status_code != 200: raise Exception(f"**Error**: {response.status_code}, {response.text}") @@ -332,22 +308,16 @@ def tts(self, text, voice="中文女", stream=True): class OllamaTTS(Base): def __init__(self, key, model_name="ollama-tts", base_url="https://api.ollama.ai/v1"): - if not base_url: + if not base_url: base_url = "https://api.ollama.ai/v1" self.model_name = model_name self.base_url = base_url - self.headers = { - "Content-Type": "application/json" - } + self.headers = {"Content-Type": "application/json"} if key and key != "x": self.headers["Authorization"] = f"Bear {key}" def tts(self, text, voice="standard-voice"): - payload = { - "model": self.model_name, - "voice": voice, - "input": text - } + payload = {"model": self.model_name, "voice": voice, "input": text} response = requests.post(f"{self.base_url}/audio/tts", headers=self.headers, json=payload, stream=True) @@ -359,30 +329,19 @@ def tts(self, text, voice="standard-voice"): yield chunk -class GPUStackTTS: +class GPUStackTTS(Base): + _FACTORY_NAME = "GPUStack" + def __init__(self, key, model_name, **kwargs): self.base_url = kwargs.get("base_url", None) self.api_key = key self.model_name = model_name - self.headers = { - "accept": "application/json", - "Content-Type": "application/json", - "Authorization": f"Bearer {self.api_key}" - } + self.headers = {"accept": "application/json", "Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"} def tts(self, text, voice="Chinese Female", stream=True): - payload = { - "model": self.model_name, - "input": text, - "voice": voice - } + payload = {"model": self.model_name, "input": text, "voice": voice} - response = requests.post( - f"{self.base_url}/v1/audio/speech", - headers=self.headers, - json=payload, - stream=stream - ) + response = requests.post(f"{self.base_url}/v1/audio/speech", headers=self.headers, json=payload, stream=stream) if response.status_code != 200: raise Exception(f"**Error**: {response.status_code}, {response.text}") @@ -393,16 +352,15 @@ def tts(self, text, voice="Chinese Female", stream=True): class SILICONFLOWTTS(Base): + _FACTORY_NAME = "SILICONFLOW" + def __init__(self, key, model_name="FunAudioLLM/CosyVoice2-0.5B", base_url="https://api.siliconflow.cn/v1"): if not base_url: base_url = "https://api.siliconflow.cn/v1" self.api_key = key self.model_name = model_name self.base_url = base_url - self.headers = { - "Authorization": f"Bearer {self.api_key}", - "Content-Type": "application/json" - } + self.headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} def tts(self, text, voice="anna"): text = self.normalize_text(text) @@ -414,7 +372,7 @@ def tts(self, text, voice="anna"): "sample_rate": 123, "stream": True, "speed": 1, - "gain": 0 + "gain": 0, } response = requests.post(f"{self.base_url}/audio/speech", headers=self.headers, json=payload) From 194e088d01c6f713745d3fcebbc6b1c3df85b22e Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 4 Jul 2025 12:21:00 +0800 Subject: [PATCH 0097/1187] Fix: Fixed the issue where the debug form Switch component had no default value #3221 (#8662) ### What problem does this PR solve? Fix: Fixed the issue where the debug form Switch component had no default value #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/pages/agent/debug-content/index.tsx | 35 ++++++++++++--------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/web/src/pages/agent/debug-content/index.tsx b/web/src/pages/agent/debug-content/index.tsx index 964825f68f7..9228bed85d3 100644 --- a/web/src/pages/agent/debug-content/index.tsx +++ b/web/src/pages/agent/debug-content/index.tsx @@ -44,8 +44,6 @@ interface IProps { btnText?: ReactNode; } -const values = {}; - const DebugContent = ({ parameters, ok, @@ -56,15 +54,20 @@ const DebugContent = ({ }: IProps) => { const { t } = useTranslation(); - const FormSchema = useMemo(() => { - const obj = parameters.reduce>( + const formSchemaValues = useMemo(() => { + const obj = parameters.reduce<{ + schema: Record; + values: Record; + }>( (pre, cur, idx) => { const type = cur.type; let fieldSchema; + let value; if (StringFields.some((x) => x === type)) { - fieldSchema = z.string(); + fieldSchema = z.string().trim().min(1); } else if (type === BeginQueryType.Boolean) { fieldSchema = z.boolean(); + value = false; } else if (type === BeginQueryType.Integer) { fieldSchema = z.coerce.number(); } else { @@ -72,22 +75,25 @@ const DebugContent = ({ } if (cur.optional) { - fieldSchema.optional(); + fieldSchema = fieldSchema.optional(); } - pre[idx.toString()] = fieldSchema; + const index = idx.toString(); + + pre.schema[index] = fieldSchema; + pre.values[index] = value; return pre; }, - {}, + { schema: {}, values: {} }, ); - return z.object(obj); + return { schema: z.object(obj.schema), values: obj.values }; }, [parameters]); - const form = useForm>({ - defaultValues: values, - resolver: zodResolver(FormSchema), + const form = useForm>({ + defaultValues: formSchemaValues.values, + resolver: zodResolver(formSchemaValues.schema), }); const submittable = true; @@ -223,8 +229,7 @@ const DebugContent = ({ ); const onSubmit = useCallback( - (values: z.infer) => { - console.log('🚀 ~ values:', values); + (values: z.infer) => { const nextValues = Object.entries(values).map(([key, value]) => { const item = parameters[Number(key)]; let nextValue = value; @@ -243,7 +248,7 @@ const DebugContent = ({ ok(nextValues); }, - [ok, parameters], + [formSchemaValues, ok, parameters], ); return ( From d5f6335f99c4da8d9bc01c1364f056ece961a015 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Fri, 4 Jul 2025 12:41:28 +0800 Subject: [PATCH 0098/1187] Fix: The data set created by API call failed to parse after uploading the file. (#8657) ### What problem does this PR solve? https://github.com/infiniflow/ragflow/issues/8656 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/svr/task_executor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index a5dfafecab4..adcaa4c238a 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -445,7 +445,10 @@ async def embedding(docs, mdl, parser_config=None, callback=None): tk_count += c callback(prog=0.7 + 0.2 * (i + 1) / len(cnts), msg="") cnts = cnts_ - title_w = float(parser_config.get("filename_embd_weight", 0.1)) + filename_embd_weight = parser_config.get("filename_embd_weight", 0.1) # due to the db support none value + if not filename_embd_weight: + filename_embd_weight = 0.1 + title_w = float(filename_embd_weight) vects = (title_w * tts + (1 - title_w) * cnts) if len(tts) == len(cnts) else cnts From 9fbb36ca404c034545e0631559f9f3e0a2c488b1 Mon Sep 17 00:00:00 2001 From: Gifford Nowland Date: Thu, 3 Jul 2025 23:05:56 -0700 Subject: [PATCH 0099/1187] feat: use official sources for chromedriver-linux in download_deps.py (#8665) ### What problem does this PR solve? Resolves ambiguity and potential MITM attacks by using official channel for chromedriver-linux in download_deps.py ### Type of change - [x] Performance Improvement --- download_deps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/download_deps.py b/download_deps.py index 0efce81313a..58fba434122 100644 --- a/download_deps.py +++ b/download_deps.py @@ -35,8 +35,8 @@ def get_urls(use_china_mirrors=False) -> Union[str, list[str]]: "https://repo1.maven.org/maven2/org/apache/tika/tika-server-standard/3.0.0/tika-server-standard-3.0.0.jar", "https://repo1.maven.org/maven2/org/apache/tika/tika-server-standard/3.0.0/tika-server-standard-3.0.0.jar.md5", "https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken", - "https://bit.ly/chrome-linux64-121-0-6167-85", - "https://bit.ly/chromedriver-linux64-121-0-6167-85", + "https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.85/linux64/chrome-linux64.zip", + "https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.85/linux64/chromedriver-linux64.zip", ] repos = [ From b382b63f9a3edebd910900e9ad2919a8f0b2a3ee Mon Sep 17 00:00:00 2001 From: Hwting <837479851@qq.com> Date: Fri, 4 Jul 2025 14:06:20 +0800 Subject: [PATCH 0100/1187] Fix(docker-compose)Update docker-compose-base.yml (#8650) ### What problem does this PR solve? 1.Optimize Redis Health Check ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- docker/docker-compose-base.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index 4b88509d00b..cdd5f91213b 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -194,6 +194,12 @@ services: networks: - ragflow restart: on-failure + healthcheck: + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] + interval: 5s + timeout: 3s + retries: 3 + start_period: 10s From 1cf24be04b1cf31dab1025a2238c16f1e8dca1a9 Mon Sep 17 00:00:00 2001 From: dcc123456 <1243304602@qq.com> Date: Fri, 4 Jul 2025 15:11:30 +0800 Subject: [PATCH 0101/1187] =?UTF-8?q?fix:Optimized=20the=20style=20of=20th?= =?UTF-8?q?e=20dataset=20configuration=20page=20and=20added=20the=20l?= =?UTF-8?q?=E2=80=A6=20(#8655)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? Optimized the style of the dataset configuration page and added the logic of cancelling submission [#3221](https://github.com/infiniflow/ragflow/issues/3221) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/locales/en.ts | 1 + web/src/locales/zh-traditional.ts | 1 + web/src/locales/zh.ts | 1 + web/src/pages/dataset/dataset/index.tsx | 15 ++- .../dataset/dataset/use-select-filters.ts | 3 +- .../dataset/setting/chunk-method-form.tsx | 15 ++- .../pages/dataset/setting/general-form.tsx | 115 +++++++----------- web/src/pages/dataset/setting/index.tsx | 2 +- 8 files changed, 71 insertions(+), 82 deletions(-) diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 399898c6ce1..9397b73d6a4 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -207,6 +207,7 @@ export default { 'Update your knowledge base configuration here, particularly the chunking method.', name: 'Knowledge base name', photo: 'Knowledge base photo', + photoTip: 'You can upload a file with 4 MB', description: 'Description', language: 'Document language', languageMessage: 'Please input your language!', diff --git a/web/src/locales/zh-traditional.ts b/web/src/locales/zh-traditional.ts index da38bdef344..1d2662a262b 100644 --- a/web/src/locales/zh-traditional.ts +++ b/web/src/locales/zh-traditional.ts @@ -205,6 +205,7 @@ export default { titleDescription: '在這裡更新您的知識庫詳細信息,尤其是切片方法。', name: '知識庫名稱', photo: '知識庫圖片', + photoTip: '你可以上傳4MB的文件', description: '描述', language: '文件語言', languageMessage: '請輸入語言', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index c9ebfdb42d2..eca66c4e403 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -205,6 +205,7 @@ export default { titleDescription: '在这里更新您的知识库详细信息,尤其是切片方法。', name: '知识库名称', photo: '知识库图片', + photoTip: '你可以上传4MB的文件', description: '描述', language: '文档语言', languageMessage: '请输入语言', diff --git a/web/src/pages/dataset/dataset/index.tsx b/web/src/pages/dataset/dataset/index.tsx index 41d5050fdde..8ceeea173a9 100644 --- a/web/src/pages/dataset/dataset/index.tsx +++ b/web/src/pages/dataset/dataset/index.tsx @@ -12,7 +12,9 @@ import { } from '@/components/ui/dropdown-menu'; import { useRowSelection } from '@/hooks/logic-hooks/use-row-selection'; import { useFetchDocumentList } from '@/hooks/use-document-request'; +import { IDocumentInfo } from '@/interfaces/database/document'; import { Upload } from 'lucide-react'; +import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { DatasetTable } from './dataset-table'; import { useBulkOperateDataset } from './use-bulk-operate-dataset'; @@ -40,7 +42,16 @@ export default function Dataset() { handleFilterSubmit, loading, } = useFetchDocumentList(); - const { filters } = useSelectDatasetFilters(); + const { filters, documents: filteredDocuments } = useSelectDatasetFilters(); + const [datasetInfo, setDatasetInfo] = useState(documents); + + useMemo(() => { + setDatasetInfo(documents); + }, [documents]); + + useMemo(() => { + setDatasetInfo(filteredDocuments); + }, [filteredDocuments]); const { createLoading, @@ -100,7 +111,7 @@ export default function Dataset() { )} { return [ { field: 'type', label: 'File Type', list: fileTypes }, @@ -27,5 +26,5 @@ export function useSelectDatasetFilters() { ]; }, [fileStatus, fileTypes]); - return { fileTypes, fileStatus, filters }; + return { fileTypes, fileStatus, filters, documents }; } diff --git a/web/src/pages/dataset/setting/chunk-method-form.tsx b/web/src/pages/dataset/setting/chunk-method-form.tsx index 1f9ccb229da..1e5e59c2dae 100644 --- a/web/src/pages/dataset/setting/chunk-method-form.tsx +++ b/web/src/pages/dataset/setting/chunk-method-form.tsx @@ -70,15 +70,22 @@ export function ChunkMethodForm() {
    -
    +
    +
    diff --git a/web/src/pages/dataset/setting/general-form.tsx b/web/src/pages/dataset/setting/general-form.tsx index e546402bd87..69dd82079c7 100644 --- a/web/src/pages/dataset/setting/general-form.tsx +++ b/web/src/pages/dataset/setting/general-form.tsx @@ -9,7 +9,6 @@ import { FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { useUpdateKnowledge } from '@/hooks/knowledge-hooks'; import { transformFile2Base64 } from '@/utils/file-util'; import { Loader2Icon, Pencil, Upload } from 'lucide-react'; @@ -79,43 +78,17 @@ export function GeneralForm() { )} /> - { - // null initialize empty string - if (typeof field.value === 'object' && !field.value) { - form.setValue('description', ' '); - } - return ( - -
    - - {t('flow.description')} - - - - -
    -
    -
    - -
    -
    - ); - }} - /> ( + render={() => (
    {t('setting.avatar')} - <> +
    {!avatarBase64Str ? (
    @@ -126,7 +99,7 @@ export function GeneralForm() {
    ) : (
    - +
    - +
    + {t('knowledgeConfiguration.photoTip')} +
    +
    @@ -172,54 +148,48 @@ export function GeneralForm() { /> ( - - - {t('knowledgeConfiguration.permissions')} - - - - - - - - - {t('knowledgeConfiguration.me')} - - - - - - - - {t('knowledgeConfiguration.team')} - - - - - - - )} + name="description" + render={({ field }) => { + // null initialize empty string + if (typeof field.value === 'object' && !field.value) { + form.setValue('description', ' '); + } + return ( + +
    + + {t('flow.description')} + + + + +
    +
    +
    + +
    +
    + ); + }} /> -
    +
    +
    diff --git a/web/src/pages/dataset/setting/index.tsx b/web/src/pages/dataset/setting/index.tsx index 515ca214a46..7c460bd75b0 100644 --- a/web/src/pages/dataset/setting/index.tsx +++ b/web/src/pages/dataset/setting/index.tsx @@ -102,7 +102,7 @@ export default function DatasetSettings() { setCurrentTab(val); }} > - + Date: Fri, 4 Jul 2025 15:59:41 +0800 Subject: [PATCH 0102/1187] Refa: refactor prompts into markdown-style structure using Jinja2 (#8667) ### What problem does this PR solve? Refactor prompts into markdown-style structure using Jinja2. ### Type of change - [x] Refactoring --- rag/prompt_template.py | 21 ++ rag/prompts.py | 334 ++++-------------- rag/prompts/citation_prompt.md | 46 +++ rag/prompts/content_tagging_prompt.md | 32 ++ rag/prompts/cross_languages_sys_prompt.md | 35 ++ rag/prompts/cross_languages_user_prompt.md | 7 + rag/prompts/full_question_prompt.md | 62 ++++ rag/prompts/keyword_prompt.md | 16 + rag/prompts/question_prompt.md | 19 + rag/prompts/vision_llm_describe_prompt.md | 23 ++ .../vision_llm_figure_describe_prompt.md | 24 ++ 11 files changed, 358 insertions(+), 261 deletions(-) create mode 100644 rag/prompt_template.py create mode 100644 rag/prompts/citation_prompt.md create mode 100644 rag/prompts/content_tagging_prompt.md create mode 100644 rag/prompts/cross_languages_sys_prompt.md create mode 100644 rag/prompts/cross_languages_user_prompt.md create mode 100644 rag/prompts/full_question_prompt.md create mode 100644 rag/prompts/keyword_prompt.md create mode 100644 rag/prompts/question_prompt.md create mode 100644 rag/prompts/vision_llm_describe_prompt.md create mode 100644 rag/prompts/vision_llm_figure_describe_prompt.md diff --git a/rag/prompt_template.py b/rag/prompt_template.py new file mode 100644 index 00000000000..c30a6f9a52d --- /dev/null +++ b/rag/prompt_template.py @@ -0,0 +1,21 @@ +import os + +BASE_DIR = os.path.dirname(__file__) + +PROMPT_DIR = os.path.join(BASE_DIR, "prompts") + +_loaded_prompts = {} + + +def load_prompt(name: str) -> str: + if name in _loaded_prompts: + return _loaded_prompts[name] + + path = os.path.join(PROMPT_DIR, f"{name}.md") + if not os.path.isfile(path): + raise FileNotFoundError(f"Prompt file '{name}.md' not found in prompts/ directory.") + + with open(path, "r", encoding="utf-8") as f: + content = f.read().strip() + _loaded_prompts[name] = content + return content diff --git a/rag/prompts.py b/rag/prompts.py index 66c8a4ef49e..ff808391692 100644 --- a/rag/prompts.py +++ b/rag/prompts.py @@ -19,10 +19,11 @@ import re from collections import defaultdict +import jinja2 import json_repair from api import settings -from api.db import LLMType +from rag.prompt_template import load_prompt from rag.settings import TAG_FLD from rag.utils import encoder, num_tokens_from_string @@ -119,7 +120,7 @@ def kb_prompt(kbinfos, max_tokens): doc2chunks = defaultdict(lambda: {"chunks": [], "meta": []}) for i, ck in enumerate(kbinfos["chunks"][:chunks_num]): cnt = f"---\nID: {i}\n" + (f"URL: {ck['url']}\n" if "url" in ck else "") - cnt += re.sub(r"( style=\"[^\"]+\"||)", " ", ck["content_with_weight"], flags=re.DOTALL|re.IGNORECASE) + cnt += re.sub(r"( style=\"[^\"]+\"||)", " ", ck["content_with_weight"], flags=re.DOTALL | re.IGNORECASE) doc2chunks[ck["docnm_kwd"]]["chunks"].append(cnt) doc2chunks[ck["docnm_kwd"]]["meta"] = docs.get(ck["doc_id"], {}) @@ -135,70 +136,31 @@ def kb_prompt(kbinfos, max_tokens): return knowledges -def citation_prompt(): - print("USE PROMPT", flush=True) - return """ +CITATION_PROMPT_TEMPLATE = load_prompt("citation_prompt") +CONTENT_TAGGING_PROMPT_TEMPLATE = load_prompt("content_tagging_prompt") +CROSS_LANGUAGES_SYS_PROMPT_TEMPLATE = load_prompt("cross_languages_sys_prompt") +CROSS_LANGUAGES_USER_PROMPT_TEMPLATE = load_prompt("cross_languages_user_prompt") +FULL_QUESTION_PROMPT_TEMPLATE = load_prompt("full_question_prompt") +KEYWORD_PROMPT_TEMPLATE = load_prompt("keyword_prompt") +QUESTION_PROMPT_TEMPLATE = load_prompt("question_prompt") +VISION_LLM_DESCRIBE_PROMPT = load_prompt("vision_llm_describe_prompt") +VISION_LLM_FIGURE_DESCRIBE_PROMPT = load_prompt("vision_llm_figure_describe_prompt") -# Citation requirements: +PROMPT_JINJA_ENV = jinja2.Environment(autoescape=False, trim_blocks=True, lstrip_blocks=True) -- Use a uniform citation format such as [ID:i] [ID:j], where "i" and "j" are document IDs enclosed in square brackets. Separate multiple IDs with spaces (e.g., [ID:0] [ID:1]). -- Citation markers must be placed at the end of a sentence, separated by a space from the final punctuation (e.g., period, question mark). A maximum of 4 citations are allowed per sentence. -- DO NOT insert CITATION in the answer if the content is not from retrieved chunks. -- DO NOT use standalone Document IDs (e.g., '#ID#'). -- Citations ALWAYS in the "[ID:i]" format. -- STRICTLY prohibit the use of strikethrough symbols (e.g., ~~) or any other non-standard formatting syntax. -- Any failure to adhere to the above rules, including but not limited to incorrect formatting, use of prohibited styles, or unsupported citations, will be considered an error, and no citation will be added for that sentence. ---- Example START --- -: Here is the knowledge base: - -Document: Elon Musk Breaks Silence on Crypto, Warns Against Dogecoin ... -URL: https://blockworks.co/news/elon-musk-crypto-dogecoin -ID: 0 -The Tesla co-founder advised against going all-in on dogecoin, but Elon Musk said it’s still his favorite crypto... - -Document: Elon Musk's Dogecoin tweet sparks social media frenzy -ID: 1 -Musk said he is 'willing to serve' D.O.G.E. – shorthand for Dogecoin. - -Document: Causal effect of Elon Musk tweets on Dogecoin price -ID: 2 -If you think of Dogecoin — the cryptocurrency based on a meme — you can’t help but also think of Elon Musk... - -Document: Elon Musk's Tweet Ignites Dogecoin's Future In Public Services -ID: 3 -The market is heating up after Elon Musk's announcement about Dogecoin. Is this a new era for crypto?... - - The above is the knowledge base. - -: What's the Elon's view on dogecoin? - -: Musk has consistently expressed his fondness for Dogecoin, often citing its humor and the inclusion of dogs in its branding. He has referred to it as his favorite cryptocurrency [ID:0] [ID:1]. -Recently, Musk has hinted at potential future roles for Dogecoin. His tweets have sparked speculation about Dogecoin's potential integration into public services [ID:3]. -Overall, while Musk enjoys Dogecoin and often promotes it, he also warns against over-investing in it, reflecting both his personal amusement and caution regarding its speculative nature. - ---- Example END --- - -""" +def citation_prompt() -> str: + template = PROMPT_JINJA_ENV.from_string(CITATION_PROMPT_TEMPLATE) + return template.render() def keyword_extraction(chat_mdl, content, topn=3): - prompt = f""" -Role: You are a text analyzer. -Task: Extract the most important keywords/phrases of a given piece of text content. -Requirements: - - Summarize the text content, and give the top {topn} important keywords/phrases. - - The keywords MUST be in the same language as the given piece of text content. - - The keywords are delimited by ENGLISH COMMA. - - Output keywords ONLY. - -### Text Content -{content} - -""" - msg = [{"role": "system", "content": prompt}, {"role": "user", "content": "Output: "}] + template = PROMPT_JINJA_ENV.from_string(KEYWORD_PROMPT_TEMPLATE) + rendered_prompt = template.render(content=content, topn=topn) + + msg = [{"role": "system", "content": rendered_prompt}, {"role": "user", "content": "Output: "}] _, msg = message_fit_in(msg, chat_mdl.max_length) - kwd = chat_mdl.chat(prompt, msg[1:], {"temperature": 0.2}) + kwd = chat_mdl.chat(rendered_prompt, msg[1:], {"temperature": 0.2}) if isinstance(kwd, tuple): kwd = kwd[0] kwd = re.sub(r"^.*", "", kwd, flags=re.DOTALL) @@ -208,24 +170,12 @@ def keyword_extraction(chat_mdl, content, topn=3): def question_proposal(chat_mdl, content, topn=3): - prompt = f""" -Role: You are a text analyzer. -Task: Propose {topn} questions about a given piece of text content. -Requirements: - - Understand and summarize the text content, and propose the top {topn} important questions. - - The questions SHOULD NOT have overlapping meanings. - - The questions SHOULD cover the main content of the text as much as possible. - - The questions MUST be in the same language as the given piece of text content. - - One question per line. - - Output questions ONLY. - -### Text Content -{content} - -""" - msg = [{"role": "system", "content": prompt}, {"role": "user", "content": "Output: "}] + template = PROMPT_JINJA_ENV.from_string(QUESTION_PROMPT_TEMPLATE) + rendered_prompt = template.render(content=content, topn=topn) + + msg = [{"role": "system", "content": rendered_prompt}, {"role": "user", "content": "Output: "}] _, msg = message_fit_in(msg, chat_mdl.max_length) - kwd = chat_mdl.chat(prompt, msg[1:], {"temperature": 0.2}) + kwd = chat_mdl.chat(rendered_prompt, msg[1:], {"temperature": 0.2}) if isinstance(kwd, tuple): kwd = kwd[0] kwd = re.sub(r"^.*", "", kwd, flags=re.DOTALL) @@ -235,6 +185,7 @@ def question_proposal(chat_mdl, content, topn=3): def full_question(tenant_id, llm_id, messages, language=None): + from api.db import LLMType from api.db.services.llm_service import LLMBundle if llm_id2llm_type(llm_id) == "image2text": @@ -246,73 +197,27 @@ def full_question(tenant_id, llm_id, messages, language=None): if m["role"] not in ["user", "assistant"]: continue conv.append("{}: {}".format(m["role"].upper(), m["content"])) - conv = "\n".join(conv) + conversation = "\n".join(conv) today = datetime.date.today().isoformat() yesterday = (datetime.date.today() - datetime.timedelta(days=1)).isoformat() tomorrow = (datetime.date.today() + datetime.timedelta(days=1)).isoformat() - prompt = f""" -Role: A helpful assistant - -Task and steps: - 1. Generate a full user question that would follow the conversation. - 2. If the user's question involves relative date, you need to convert it into absolute date based on the current date, which is {today}. For example: 'yesterday' would be converted to {yesterday}. - -Requirements & Restrictions: - - If the user's latest question is already complete, don't do anything, just return the original question. - - DON'T generate anything except a refined question.""" - if language: - prompt += f""" - - Text generated MUST be in {language}.""" - else: - prompt += """ - - Text generated MUST be in the same language as the original user's question. -""" - prompt += f""" - -###################### --Examples- -###################### - -# Example 1 -## Conversation -USER: What is the name of Donald Trump's father? -ASSISTANT: Fred Trump. -USER: And his mother? -############### -Output: What's the name of Donald Trump's mother? - ------------- -# Example 2 -## Conversation -USER: What is the name of Donald Trump's father? -ASSISTANT: Fred Trump. -USER: And his mother? -ASSISTANT: Mary Trump. -User: What's her full name? -############### -Output: What's the full name of Donald Trump's mother Mary Trump? - ------------- -# Example 3 -## Conversation -USER: What's the weather today in London? -ASSISTANT: Cloudy. -USER: What's about tomorrow in Rochester? -############### -Output: What's the weather in Rochester on {tomorrow}? - -###################### -# Real Data -## Conversation -{conv} -############### - """ - ans = chat_mdl.chat(prompt, [{"role": "user", "content": "Output: "}], {"temperature": 0.2}) + + template = PROMPT_JINJA_ENV.from_string(FULL_QUESTION_PROMPT_TEMPLATE) + rendered_prompt = template.render( + today=today, + yesterday=yesterday, + tomorrow=tomorrow, + conversation=conversation, + language=language, + ) + + ans = chat_mdl.chat(rendered_prompt, [{"role": "user", "content": "Output: "}], {"temperature": 0.2}) ans = re.sub(r"^.*", "", ans, flags=re.DOTALL) return ans if ans.find("**ERROR**") < 0 else messages[-1]["content"] def cross_languages(tenant_id, llm_id, query, languages=[]): + from api.db import LLMType from api.db.services.llm_service import LLMBundle if llm_id and llm_id2llm_type(llm_id) == "image2text": @@ -320,47 +225,10 @@ def cross_languages(tenant_id, llm_id, query, languages=[]): else: chat_mdl = LLMBundle(tenant_id, LLMType.CHAT, llm_id) - sys_prompt = """ -Act as a streamlined multilingual translator. Strictly output translations separated by ### without any explanations or formatting. Follow these rules: - -1. Accept batch translation requests in format: -[source text] -=== -[target languages separated by commas] - -2. Always maintain: -- Original formatting (tables/lists/spacing) -- Technical terminology accuracy -- Cultural context appropriateness - -3. Output format: -[language1 translation] -### -[language1 translation] - -**Examples:** -Input: -Hello World! Let's discuss AI safety. -=== -Chinese, French, Japanese - -Output: -你好世界!让我们讨论人工智能安全问题。 -### -Bonjour le monde ! Parlons de la sécurité de l'IA. -### -こんにちは世界!AIの安全性について話し合いましょう。 -""" - user_prompt = f""" -Input: -{query} -=== -{", ".join(languages)} - -Output: -""" - - ans = chat_mdl.chat(sys_prompt, [{"role": "user", "content": user_prompt}], {"temperature": 0.2}) + rendered_sys_prompt = PROMPT_JINJA_ENV.from_string(CROSS_LANGUAGES_SYS_PROMPT_TEMPLATE).render() + rendered_user_prompt = PROMPT_JINJA_ENV.from_string(CROSS_LANGUAGES_USER_PROMPT_TEMPLATE).render(query=query, languages=languages) + + ans = chat_mdl.chat(rendered_sys_prompt, [{"role": "user", "content": rendered_user_prompt}], {"temperature": 0.2}) ans = re.sub(r"^.*", "", ans, flags=re.DOTALL) if ans.find("**ERROR**") >= 0: return query @@ -368,46 +236,21 @@ def cross_languages(tenant_id, llm_id, query, languages=[]): def content_tagging(chat_mdl, content, all_tags, examples, topn=3): - prompt = f""" -Role: You are a text analyzer. - -Task: Add tags (labels) to a given piece of text content based on the examples and the entire tag set. - -Steps: - - Review the tag/label set. - - Review examples which all consist of both text content and assigned tags with relevance score in JSON format. - - Summarize the text content, and tag it with the top {topn} most relevant tags from the set of tags/labels and the corresponding relevance score. + template = PROMPT_JINJA_ENV.from_string(CONTENT_TAGGING_PROMPT_TEMPLATE) -Requirements: - - The tags MUST be from the tag set. - - The output MUST be in JSON format only, the key is tag and the value is its relevance score. - - The relevance score must range from 1 to 10. - - Output keywords ONLY. + for ex in examples: + ex["tags_json"] = json.dumps(ex[TAG_FLD], indent=2, ensure_ascii=False) -# TAG SET -{", ".join(all_tags)} + rendered_prompt = template.render( + topn=topn, + all_tags=all_tags, + examples=examples, + content=content, + ) -""" - for i, ex in enumerate(examples): - prompt += """ -# Examples {} -### Text Content -{} - -Output: -{} - - """.format(i, ex["content"], json.dumps(ex[TAG_FLD], indent=2, ensure_ascii=False)) - - prompt += f""" -# Real Data -### Text Content -{content} - -""" - msg = [{"role": "system", "content": prompt}, {"role": "user", "content": "Output: "}] + msg = [{"role": "system", "content": rendered_prompt}, {"role": "user", "content": "Output: "}] _, msg = message_fit_in(msg, chat_mdl.max_length) - kwd = chat_mdl.chat(prompt, msg[1:], {"temperature": 0.5}) + kwd = chat_mdl.chat(rendered_prompt, msg[1:], {"temperature": 0.5}) if isinstance(kwd, tuple): kwd = kwd[0] kwd = re.sub(r"^.*", "", kwd, flags=re.DOTALL) @@ -418,7 +261,7 @@ def content_tagging(chat_mdl, content, all_tags, examples, topn=3): obj = json_repair.loads(kwd) except json_repair.JSONDecodeError: try: - result = kwd.replace(prompt[:-1], "").replace("user", "").replace("model", "").strip() + result = kwd.replace(rendered_prompt[:-1], "").replace("user", "").replace("model", "").strip() result = "{" + result.split("{")[1].split("}")[0] + "}" obj = json_repair.loads(result) except Exception as e: @@ -435,54 +278,23 @@ def content_tagging(chat_mdl, content, all_tags, examples, topn=3): def vision_llm_describe_prompt(page=None) -> str: - prompt_en = """ -INSTRUCTION: -Transcribe the content from the provided PDF page image into clean Markdown format. -- Only output the content transcribed from the image. -- Do NOT output this instruction or any other explanation. -- If the content is missing or you do not understand the input, return an empty string. - -RULES: -1. Do NOT generate examples, demonstrations, or templates. -2. Do NOT output any extra text such as 'Example', 'Example Output', or similar. -3. Do NOT generate any tables, headings, or content that is not explicitly present in the image. -4. Transcribe content word-for-word. Do NOT modify, translate, or omit any content. -5. Do NOT explain Markdown or mention that you are using Markdown. -6. Do NOT wrap the output in ```markdown or ``` blocks. -7. Only apply Markdown structure to headings, paragraphs, lists, and tables, strictly based on the layout of the image. Do NOT create tables unless an actual table exists in the image. -8. Preserve the original language, information, and order exactly as shown in the image. -""" - - if page is not None: - prompt_en += f"\nAt the end of the transcription, add the page divider: `--- Page {page} ---`." - - prompt_en += """ -FAILURE HANDLING: -- If you do not detect valid content in the image, return an empty string. -""" - return prompt_en + template = PROMPT_JINJA_ENV.from_string(VISION_LLM_DESCRIBE_PROMPT) + + return template.render(page=page) def vision_llm_figure_describe_prompt() -> str: - prompt = """ -You are an expert visual data analyst. Analyze the image and provide a comprehensive description of its content. Focus on identifying the type of visual data representation (e.g., bar chart, pie chart, line graph, table, flowchart), its structure, and any text captions or labels included in the image. - -Tasks: -1. Describe the overall structure of the visual representation. Specify if it is a chart, graph, table, or diagram. -2. Identify and extract any axes, legends, titles, or labels present in the image. Provide the exact text where available. -3. Extract the data points from the visual elements (e.g., bar heights, line graph coordinates, pie chart segments, table rows and columns). -4. Analyze and explain any trends, comparisons, or patterns shown in the data. -5. Capture any annotations, captions, or footnotes, and explain their relevance to the image. -6. Only include details that are explicitly present in the image. If an element (e.g., axis, legend, or caption) does not exist or is not visible, do not mention it. - -Output format (include only sections relevant to the image content): -- Visual Type: [Type] -- Title: [Title text, if available] -- Axes / Legends / Labels: [Details, if available] -- Data Points: [Extracted data] -- Trends / Insights: [Analysis and interpretation] -- Captions / Annotations: [Text and relevance, if available] - -Ensure high accuracy, clarity, and completeness in your analysis, and include only the information present in the image. Avoid unnecessary statements about missing elements. -""" - return prompt + template = PROMPT_JINJA_ENV.from_string(VISION_LLM_FIGURE_DESCRIBE_PROMPT) + return template.render() + + +if __name__ == "__main__": + print(CITATION_PROMPT_TEMPLATE) + print(CONTENT_TAGGING_PROMPT_TEMPLATE) + print(CROSS_LANGUAGES_SYS_PROMPT_TEMPLATE) + print(CROSS_LANGUAGES_USER_PROMPT_TEMPLATE) + print(FULL_QUESTION_PROMPT_TEMPLATE) + print(KEYWORD_PROMPT_TEMPLATE) + print(QUESTION_PROMPT_TEMPLATE) + print(VISION_LLM_DESCRIBE_PROMPT) + print(VISION_LLM_FIGURE_DESCRIBE_PROMPT) diff --git a/rag/prompts/citation_prompt.md b/rag/prompts/citation_prompt.md new file mode 100644 index 00000000000..c36036c2730 --- /dev/null +++ b/rag/prompts/citation_prompt.md @@ -0,0 +1,46 @@ +## Citation Requirements + +- Use a uniform citation format such as [ID:i] [ID:j], where "i" and "j" are document IDs enclosed in square brackets. Separate multiple IDs with spaces (e.g., [ID:0] [ID:1]). +- Citation markers must be placed at the end of a sentence, separated by a space from the final punctuation (e.g., period, question mark). +- A maximum of 4 citations are allowed per sentence. +- DO NOT insert citations if the content is not from retrieved chunks. +- DO NOT use standalone Document IDs (e.g., #ID#). +- Citations MUST always follow the [ID:i] format. +- STRICTLY prohibit the use of strikethrough symbols (e.g., ~~) or any other non-standard formatting syntax. +- Any violation of the above rules — including incorrect formatting, prohibited styles, or unsupported citations — will result in no citation being added for that sentence. + +--- + +## Example START + +: Here is the knowledge base: + +Document: Elon Musk Breaks Silence on Crypto, Warns Against Dogecoin ... +URL: https://blockworks.co/news/elon-musk-crypto-dogecoin +ID: 0 +The Tesla co-founder advised against going all-in on dogecoin, but Elon Musk said it’s still his favorite crypto... + +Document: Elon Musk's Dogecoin tweet sparks social media frenzy +ID: 1 +Musk said he is 'willing to serve' D.O.G.E. – shorthand for Dogecoin. + +Document: Causal effect of Elon Musk tweets on Dogecoin price +ID: 2 +If you think of Dogecoin — the cryptocurrency based on a meme — you can’t help but also think of Elon Musk... + +Document: Elon Musk's Tweet Ignites Dogecoin's Future In Public Services +ID: 3 +The market is heating up after Elon Musk's announcement about Dogecoin. Is this a new era for crypto?... + +The above is the knowledge base. + +: What's Elon's view on dogecoin? + +: +Musk has consistently expressed his fondness for Dogecoin, often citing its humor and the inclusion of dogs in its branding. He has referred to it as his favorite cryptocurrency [ID:0] [ID:1]. + +Recently, Musk has hinted at potential future roles for Dogecoin. His tweets have sparked speculation about Dogecoin's potential integration into public services [ID:3]. + +Overall, while Musk enjoys Dogecoin and often promotes it, he also warns against over-investing in it, reflecting both his personal amusement and caution regarding its speculative nature. + +## Example END diff --git a/rag/prompts/content_tagging_prompt.md b/rag/prompts/content_tagging_prompt.md new file mode 100644 index 00000000000..75d6f1586e0 --- /dev/null +++ b/rag/prompts/content_tagging_prompt.md @@ -0,0 +1,32 @@ +## Role +You are a text analyzer. + +## Task +Add tags (labels) to a given piece of text content based on the examples and the entire tag set. + +## Steps +- Review the tag/label set. +- Review examples which all consist of both text content and assigned tags with relevance score in JSON format. +- Summarize the text content, and tag it with the top {{ topn }} most relevant tags from the set of tags/labels and the corresponding relevance score. + +## Requirements +- The tags MUST be from the tag set. +- The output MUST be in JSON format only, the key is tag and the value is its relevance score. +- The relevance score must range from 1 to 10. +- Output keywords ONLY. + +# TAG SET +{{ all_tags | join(', ') }} + +{% for ex in examples %} +# Examples {{ loop.index0 }} +### Text Content +{{ ex.content }} + +Output: +{{ ex.tags_json }} + +{% endfor %} +# Real Data +### Text Content +{{ content }} diff --git a/rag/prompts/cross_languages_sys_prompt.md b/rag/prompts/cross_languages_sys_prompt.md new file mode 100644 index 00000000000..9761944daad --- /dev/null +++ b/rag/prompts/cross_languages_sys_prompt.md @@ -0,0 +1,35 @@ +## Role +A streamlined multilingual translator. + +## Behavior Rules +1. Accept batch translation requests in the following format: + **Input:** `[text]` + **Target Languages:** comma-separated list + +2. Maintain: + - Original formatting (tables, lists, spacing) + - Technical terminology accuracy + - Cultural context appropriateness + +3. Output translations in the following format: + +[Translation in language1] +### +[Translation in language2] + +--- + +## Example + +**Input:** +Hello World! Let's discuss AI safety. +=== +Chinese, French, Japanese + +**Output:** +你好世界!让我们讨论人工智能安全问题。 +### +Bonjour le monde ! Parlons de la sécurité de l'IA. +### +こんにちは世界!AIの安全性について話し合いましょう。 + diff --git a/rag/prompts/cross_languages_user_prompt.md b/rag/prompts/cross_languages_user_prompt.md new file mode 100644 index 00000000000..f729ef5664a --- /dev/null +++ b/rag/prompts/cross_languages_user_prompt.md @@ -0,0 +1,7 @@ +**Input:** +{{ query }} +=== +{{ languages | join(', ') }} + +**Output:** + diff --git a/rag/prompts/full_question_prompt.md b/rag/prompts/full_question_prompt.md new file mode 100644 index 00000000000..d7276a3ea28 --- /dev/null +++ b/rag/prompts/full_question_prompt.md @@ -0,0 +1,62 @@ +## Role +A helpful assistant. + +## Task & Steps +1. Generate a full user question that would follow the conversation. +2. If the user's question involves relative dates, convert them into absolute dates based on today ({{ today }}). + - "yesterday" = {{ yesterday }}, "tomorrow" = {{ tomorrow }} + +## Requirements & Restrictions +- If the user's latest question is already complete, don't do anything — just return the original question. +- DON'T generate anything except a refined question. +{% if language %} +- Text generated MUST be in {{ language }}. +{% else %} +- Text generated MUST be in the same language as the original user's question. +{% endif %} + +--- + +## Examples + +### Example 1 +**Conversation:** + +USER: What is the name of Donald Trump's father? +ASSISTANT: Fred Trump. +USER: And his mother? + +**Output:** What's the name of Donald Trump's mother? + +--- + +### Example 2 +**Conversation:** + +USER: What is the name of Donald Trump's father? +ASSISTANT: Fred Trump. +USER: And his mother? +ASSISTANT: Mary Trump. +USER: What's her full name? + +**Output:** What's the full name of Donald Trump's mother Mary Trump? + +--- + +### Example 3 +**Conversation:** + +USER: What's the weather today in London? +ASSISTANT: Cloudy. +USER: What's about tomorrow in Rochester? + +**Output:** What's the weather in Rochester on {{ tomorrow }}? + +--- + +## Real Data + +**Conversation:** + +{{ conversation }} + diff --git a/rag/prompts/keyword_prompt.md b/rag/prompts/keyword_prompt.md new file mode 100644 index 00000000000..67729f259a8 --- /dev/null +++ b/rag/prompts/keyword_prompt.md @@ -0,0 +1,16 @@ +## Role +You are a text analyzer. + +## Task +Extract the most important keywords/phrases of a given piece of text content. + +## Requirements +- Summarize the text content, and give the top {{ topn }} important keywords/phrases. +- The keywords MUST be in the same language as the given piece of text content. +- The keywords are delimited by ENGLISH COMMA. +- Output keywords ONLY. + +--- + +## Text Content +{{ content }} diff --git a/rag/prompts/question_prompt.md b/rag/prompts/question_prompt.md new file mode 100644 index 00000000000..ec9889fbfee --- /dev/null +++ b/rag/prompts/question_prompt.md @@ -0,0 +1,19 @@ +## Role +You are a text analyzer. + +## Task +Propose {{ topn }} questions about a given piece of text content. + +## Requirements +- Understand and summarize the text content, and propose the top {{ topn }} important questions. +- The questions SHOULD NOT have overlapping meanings. +- The questions SHOULD cover the main content of the text as much as possible. +- The questions MUST be in the same language as the given piece of text content. +- One question per line. +- Output questions ONLY. + +--- + +## Text Content +{{ content }} + diff --git a/rag/prompts/vision_llm_describe_prompt.md b/rag/prompts/vision_llm_describe_prompt.md new file mode 100644 index 00000000000..8800703dcfa --- /dev/null +++ b/rag/prompts/vision_llm_describe_prompt.md @@ -0,0 +1,23 @@ +## INSTRUCTION +Transcribe the content from the provided PDF page image into clean Markdown format. + +- Only output the content transcribed from the image. +- Do NOT output this instruction or any other explanation. +- If the content is missing or you do not understand the input, return an empty string. + +## RULES +1. Do NOT generate examples, demonstrations, or templates. +2. Do NOT output any extra text such as 'Example', 'Example Output', or similar. +3. Do NOT generate any tables, headings, or content that is not explicitly present in the image. +4. Transcribe content word-for-word. Do NOT modify, translate, or omit any content. +5. Do NOT explain Markdown or mention that you are using Markdown. +6. Do NOT wrap the output in ```markdown or ``` blocks. +7. Only apply Markdown structure to headings, paragraphs, lists, and tables, strictly based on the layout of the image. Do NOT create tables unless an actual table exists in the image. +8. Preserve the original language, information, and order exactly as shown in the image. + +{% if page %} +At the end of the transcription, add the page divider: `--- Page {{ page }} ---`. +{% endif %} + +> If you do not detect valid content in the image, return an empty string. + diff --git a/rag/prompts/vision_llm_figure_describe_prompt.md b/rag/prompts/vision_llm_figure_describe_prompt.md new file mode 100644 index 00000000000..7e528564145 --- /dev/null +++ b/rag/prompts/vision_llm_figure_describe_prompt.md @@ -0,0 +1,24 @@ +## ROLE +You are an expert visual data analyst. + +## GOAL +Analyze the image and provide a comprehensive description of its content. Focus on identifying the type of visual data representation (e.g., bar chart, pie chart, line graph, table, flowchart), its structure, and any text captions or labels included in the image. + +## TASKS +1. Describe the overall structure of the visual representation. Specify if it is a chart, graph, table, or diagram. +2. Identify and extract any axes, legends, titles, or labels present in the image. Provide the exact text where available. +3. Extract the data points from the visual elements (e.g., bar heights, line graph coordinates, pie chart segments, table rows and columns). +4. Analyze and explain any trends, comparisons, or patterns shown in the data. +5. Capture any annotations, captions, or footnotes, and explain their relevance to the image. +6. Only include details that are explicitly present in the image. If an element (e.g., axis, legend, or caption) does not exist or is not visible, do not mention it. + +## OUTPUT FORMAT (Include only sections relevant to the image content) +- Visual Type: [Type] +- Title: [Title text, if available] +- Axes / Legends / Labels: [Details, if available] +- Data Points: [Extracted data] +- Trends / Insights: [Analysis and interpretation] +- Captions / Annotations: [Text and relevance, if available] + +> Ensure high accuracy, clarity, and completeness in your analysis, and include only the information present in the image. Avoid unnecessary statements about missing elements. + From 7f707ef5ed9d79a98ee4de177daa4d31174abee1 Mon Sep 17 00:00:00 2001 From: dcc123456 <1243304602@qq.com> Date: Fri, 4 Jul 2025 19:00:30 +0800 Subject: [PATCH 0103/1187] Fix: optimize the chunk result page (#8676) ### What problem does this PR solve? fix: Create a new message component to replace the antd message component, create a new Spin component to replace the antd Spin component, optimize the original paging component style, and optimize the chunk result page[ #3221](https://github.com/infiniflow/ragflow/issues/3221) ### Type of change - [X] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/ui/message.ts | 29 +++++++ web/src/components/ui/ragflow-pagination.tsx | 77 +++++++++++++++---- web/src/components/ui/spin.tsx | 47 +++++++++++ .../components/knowledge-chunk/index.tsx | 67 ++++++++-------- 4 files changed, 168 insertions(+), 52 deletions(-) create mode 100644 web/src/components/ui/message.ts create mode 100644 web/src/components/ui/spin.tsx diff --git a/web/src/components/ui/message.ts b/web/src/components/ui/message.ts new file mode 100644 index 00000000000..24c834ffbd2 --- /dev/null +++ b/web/src/components/ui/message.ts @@ -0,0 +1,29 @@ +import { toast } from 'sonner'; + +const message = { + success: (msg: string) => { + toast.success(msg, { + position: 'top-center', + closeButton: false, + }); + }, + error: (msg: string) => { + toast.error(msg, { + position: 'top-center', + closeButton: false, + }); + }, + warning: (msg: string) => { + toast.warning(msg, { + position: 'top-center', + closeButton: false, + }); + }, + info: (msg: string) => { + toast.info(msg, { + position: 'top-center', + closeButton: false, + }); + }, +}; +export default message; diff --git a/web/src/components/ui/ragflow-pagination.tsx b/web/src/components/ui/ragflow-pagination.tsx index 264df10f1a3..dc0f14e6a23 100644 --- a/web/src/components/ui/ragflow-pagination.tsx +++ b/web/src/components/ui/ragflow-pagination.tsx @@ -97,30 +97,73 @@ export function RAGFlowPagination({ setCurrentPageSize(pageSize.toString()); }, [pageSize]); + // Generates an array of page numbers to display + const displayedPages = useMemo(() => { + const totalPages = pages.length; + const maxDisplayedPages = 5; + + if (totalPages <= maxDisplayedPages) { + return pages; + } + + const left = Math.max(2, currentPage - 2); + const right = Math.min(totalPages - 1, currentPage + 2); + + const newPages = []; + + newPages.push(1); + + if (left > 2) { + newPages.push(-1); // Indicates an ellipsis + } + + for (let i = left; i <= right; i++) { + newPages.push(i); + } + + if (right < totalPages - 1) { + newPages.push(-1); + } + + if (totalPages > 1) { + newPages.push(totalPages); + } + + return newPages; + }, [pages, currentPage]); + return ( -
    +
    Total {total} - {pages.map((x) => ( - - - {x} - - - ))} - - - + + {displayedPages.map((page, index) => + page === -1 ? ( + + + + ) : ( + + + {page} + + + ), + )} + diff --git a/web/src/components/ui/spin.tsx b/web/src/components/ui/spin.tsx new file mode 100644 index 00000000000..945ae857e47 --- /dev/null +++ b/web/src/components/ui/spin.tsx @@ -0,0 +1,47 @@ +import { cn } from '@/lib/utils'; +import React from 'react'; + +interface SpinProps { + spinning?: boolean; + size?: 'small' | 'default' | 'large'; + className?: string; + children?: React.ReactNode; +} + +const sizeClasses = { + small: 'w-4 h-4', + default: 'w-6 h-6', + large: 'w-8 h-8', +}; + +export const Spin: React.FC = ({ + spinning = true, + size = 'default', + className, + children, +}) => { + return ( +
    + {spinning && ( +
    +
    +
    + )} + {children} +
    + ); +}; diff --git a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx index 0dfd42089d1..f1b05a3a8f1 100644 --- a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx +++ b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx @@ -1,6 +1,4 @@ import { useFetchNextChunkList, useSwitchChunk } from '@/hooks/chunk-hooks'; -import type { PaginationProps } from 'antd'; -import { Flex, Pagination, Space, Spin, message } from 'antd'; import classNames from 'classnames'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -19,6 +17,12 @@ import ChunkResultBar from './components/chunk-result-bar'; import CheckboxSets from './components/chunk-result-bar/checkbox-sets'; import DocumentHeader from './components/document-preview/document-header'; +import message from '@/components/ui/message'; +import { + RAGFlowPagination, + RAGFlowPaginationType, +} from '@/components/ui/ragflow-pagination'; +import { Spin } from '@/components/ui/spin'; import styles from './index.less'; const Chunk = () => { @@ -49,7 +53,7 @@ const Chunk = () => { documentId, } = useUpdateChunk(); - const onPaginationChange: PaginationProps['onShowSizeChange'] = ( + const onPaginationChange: RAGFlowPaginationType['onChange'] = ( page, size, ) => { @@ -123,21 +127,8 @@ const Chunk = () => { return ( <>
    - {/* */} - {/* */} - -
    +
    +
    @@ -150,9 +141,11 @@ const Chunk = () => {
    )}
    -
    @@ -180,12 +173,14 @@ const Chunk = () => { />
    - {data.map((item) => ( { textMode={textMode} > ))} - +
    - + onChange={(page, pageSize) => { + onPaginationChange(page, pageSize); + }} + >
    - - +
    +
    {chunkUpdatingVisible && ( Date: Fri, 4 Jul 2025 19:12:13 +0800 Subject: [PATCH 0104/1187] Fix: Wrong Citation Display #8594 #8474 (#8671) ### What problem does this PR solve? Fix: Wrong Citation Display #8594 #8474 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/pages/chat/markdown-content/index.tsx | 4 ++-- web/src/pages/chat/utils.ts | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/web/src/pages/chat/markdown-content/index.tsx b/web/src/pages/chat/markdown-content/index.tsx index 61b27347dfd..2d6393d424b 100644 --- a/web/src/pages/chat/markdown-content/index.tsx +++ b/web/src/pages/chat/markdown-content/index.tsx @@ -31,7 +31,7 @@ import classNames from 'classnames'; import { pipe } from 'lodash/fp'; import styles from './index.less'; -const getChunkIndex = (match: string) => Number(match.slice(2, -2)); +const getChunkIndex = (match: string) => Number(match); // TODO: The display of the table is inconsistent with the display previously placed in the MessageItem. const MarkdownContent = ({ reference, @@ -121,7 +121,7 @@ const MarkdownContent = ({ document, }; }, - [fileThumbnails, reference?.chunks, reference?.doc_aggs], + [fileThumbnails, reference], ); const getPopoverContent = useCallback( diff --git a/web/src/pages/chat/utils.ts b/web/src/pages/chat/utils.ts index a36f08f3e79..aa5cadd098c 100644 --- a/web/src/pages/chat/utils.ts +++ b/web/src/pages/chat/utils.ts @@ -29,9 +29,9 @@ export const buildMessageItemReference = ( conversation: { message: IMessage[]; reference: IReference[] }, message: IMessage, ) => { - const assistantMessages = conversation.message?.filter( - (x) => x.role === MessageType.Assistant, - ); + const assistantMessages = conversation.message + ?.filter((x) => x.role === MessageType.Assistant) + .slice(1); const referenceIndex = assistantMessages.findIndex( (x) => x.id === message.id, ); @@ -47,11 +47,7 @@ export const currentReg = /\[ID:(\d+)\]/g; // To be compatible with the old index matching mode export const replaceTextByOldReg = (text: string) => { - return ( - text - // ?.replace(currentReg, transformReg) - .replace(oldReg, (substring: string) => { - return `[ID:${substring.slice(2, -2)}]`; - }) - ); + return text.replace(oldReg, (substring: string) => { + return `[ID:${substring.slice(2, -2)}]`; + }); }; From 1ac61c0f0f9db1634df440587c8ee27a0ec24f90 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Fri, 4 Jul 2025 19:40:39 +0800 Subject: [PATCH 0105/1187] Fix: secure canvas (#8670) ### What problem does this PR solve? Secure canvas access. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/canvas_app.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py index d80eb093c94..b500d19f1c3 100644 --- a/api/apps/canvas_app.py +++ b/api/apps/canvas_app.py @@ -81,17 +81,16 @@ def save(): UserCanvasVersionService.delete_all_versions(req["id"]) return get_json_result(data=req) - - @manager.route('/get/', methods=['GET']) # noqa: F821 @login_required def get(canvas_id): e, c = UserCanvasService.get_by_tenant_id(canvas_id) - if not e: + if not e or c["user_id"] != current_user.id: return get_data_error_result(message="canvas not found.") return get_json_result(data=c) + @manager.route('/getsse/', methods=['GET']) # type: ignore # noqa: F821 def getsse(canvas_id): token = request.headers.get('Authorization').split() @@ -101,8 +100,9 @@ def getsse(canvas_id): objs = APIToken.query(beta=token) if not objs: return get_data_error_result(message='Authentication error: API key is invalid!"') + tenant_id = objs[0].tenant_id e, c = UserCanvasService.get_by_id(canvas_id) - if not e: + if not e or c.user_id != tenant_id: return get_data_error_result(message="canvas not found.") return get_json_result(data=c.to_dict()) From 8a3b5d1d76242a1b2513f010ad7d95355ff3b4a2 Mon Sep 17 00:00:00 2001 From: "kwrobel.eth" Date: Fri, 4 Jul 2025 13:46:31 +0200 Subject: [PATCH 0106/1187] Fix a small typo in count of used fragments (#8673) ### What problem does this PR solve? Fix a small typo in count of used fragments. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --------- Co-authored-by: Kevin Hu --- rag/prompts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rag/prompts.py b/rag/prompts.py index ff808391692..ac04846da1e 100644 --- a/rag/prompts.py +++ b/rag/prompts.py @@ -104,6 +104,7 @@ def kb_prompt(kbinfos, max_tokens): from api.db.services.document_service import DocumentService knowledges = [ck["content_with_weight"] for ck in kbinfos["chunks"]] + kwlg_len = len(knowledges) used_token_count = 0 chunks_num = 0 for i, c in enumerate(knowledges): @@ -111,7 +112,7 @@ def kb_prompt(kbinfos, max_tokens): chunks_num += 1 if max_tokens * 0.97 < used_token_count: knowledges = knowledges[:i] - logging.warning(f"Not all the retrieval into prompt: {i + 1}/{len(knowledges)}") + logging.warning(f"Not all the retrieval into prompt: {len(knowledges)}/{kwlg_len}") break docs = DocumentService.get_by_ids([ck["doc_id"] for ck in kbinfos["chunks"][:chunks_num]]) From ebf827a956c411513f0d1dae161f982ceca9eab7 Mon Sep 17 00:00:00 2001 From: Hwting <837479851@qq.com> Date: Fri, 4 Jul 2025 20:03:03 +0800 Subject: [PATCH 0107/1187] fix(docker-compose):The old base image lost the curl command, and the image has been updated to fix this issue. Add Health Check (#8672) ### What problem does this PR solve? 1.The old base image lost the curl command, and an updated image was used to fix this issue (the service has been tested in the new version) 2.Add Health Check ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- docker/docker-compose-base.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index cdd5f91213b..cf5c9b6b4cf 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -164,7 +164,7 @@ services: restart: on-failure minio: - image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z + image: quay.io/minio/minio container_name: ragflow-minio command: server --console-address ":9001" /data ports: @@ -180,6 +180,11 @@ services: networks: - ragflow restart: on-failure + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 redis: # swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/valkey/valkey:8 From 1e6bda735a2b924674b040e56ea651f8b0d8a96f Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Mon, 7 Jul 2025 09:22:25 +0800 Subject: [PATCH 0108/1187] Fix: add ES re-connect once request timeout. (#8678) ### What problem does this PR solve? #8669 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/utils/es_conn.py | 81 ++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/rag/utils/es_conn.py b/rag/utils/es_conn.py index c66c7edff93..47d4f1a4f08 100644 --- a/rag/utils/es_conn.py +++ b/rag/utils/es_conn.py @@ -44,19 +44,12 @@ def __init__(self): logger.info(f"Use Elasticsearch {settings.ES['hosts']} as the doc engine.") for _ in range(ATTEMPT_TIME): try: - self.es = Elasticsearch( - settings.ES["hosts"].split(","), - basic_auth=(settings.ES["username"], settings.ES[ - "password"]) if "username" in settings.ES and "password" in settings.ES else None, - verify_certs=False, - timeout=600 - ) - if self.es: - self.info = self.es.info() + if self._connect(): break except Exception as e: logger.warning(f"{str(e)}. Waiting Elasticsearch {settings.ES['hosts']} to be healthy.") time.sleep(5) + if not self.es.ping(): msg = f"Elasticsearch {settings.ES['hosts']} is unhealthy in 120s." logger.error(msg) @@ -75,6 +68,19 @@ def __init__(self): self.mapping = json.load(open(fp_mapping, "r")) logger.info(f"Elasticsearch {settings.ES['hosts']} is healthy.") + def _connect(self): + self.es = Elasticsearch( + settings.ES["hosts"].split(","), + basic_auth=(settings.ES["username"], settings.ES[ + "password"]) if "username" in settings.ES and "password" in settings.ES else None, + verify_certs=False, + timeout=600 + ) + if self.es: + self.info = self.es.info() + return True + return False + """ Database operations """ @@ -118,10 +124,13 @@ def indexExist(self, indexName: str, knowledgebaseId: str = None) -> bool: for i in range(ATTEMPT_TIME): try: return s.exists() + except ConnectionTimeout: + logger.exception("ES request timeout") + time.sleep(3) + self._connect() + continue except Exception as e: - logger.exception("ESConnection.indexExist got exception") - if str(e).find("Timeout") > 0 or str(e).find("Conflict") > 0: - continue + logger.exception(e) break return False @@ -249,11 +258,14 @@ def search( raise Exception("Es Timeout.") logger.debug(f"ESConnection.search {str(indexNames)} res: " + str(res)) return res + except ConnectionTimeout: + logger.exception("ES request timeout") + self._connect() + continue except Exception as e: - logger.exception(f"ESConnection.search {str(indexNames)} query: " + str(q)) - if str(e).find("Timeout") > 0: - continue + logger.exception(f"ESConnection.search {str(indexNames)} query: " + str(q) + str(e)) raise e + logger.error(f"ESConnection.search timeout for {ATTEMPT_TIME} times!") raise Exception("ESConnection.search timeout.") @@ -271,8 +283,6 @@ def get(self, chunkId: str, indexName: str, knowledgebaseIds: list[str]) -> dict return None except Exception as e: logger.exception(f"ESConnection.get({chunkId}) got exception") - if str(e).find("Timeout") > 0: - continue raise e logger.error(f"ESConnection.get timeout for {ATTEMPT_TIME} times!") raise Exception("ESConnection.get timeout.") @@ -304,14 +314,15 @@ def insert(self, documents: list[dict], indexName: str, knowledgebaseId: str = N if action in item and "error" in item[action]: res.append(str(item[action]["_id"]) + ":" + str(item[action]["error"])) return res + except ConnectionTimeout: + logger.exception("ES request timeout") + time.sleep(3) + self._connect() + continue except Exception as e: res.append(str(e)) logger.warning("ESConnection.insert got exception: " + str(e)) - res = [] - if re.search(r"(Timeout|time out)", str(e), re.IGNORECASE): - res.append(str(e)) - time.sleep(3) - continue + return res def update(self, condition: dict, newValue: dict, indexName: str, knowledgebaseId: str) -> bool: @@ -334,9 +345,7 @@ def update(self, condition: dict, newValue: dict, indexName: str, knowledgebaseI return True except Exception as e: logger.exception( - f"ESConnection.update(index={indexName}, id={chunkId}, doc={json.dumps(condition, ensure_ascii=False)}) got exception") - if re.search(r"(timeout|connection)", str(e).lower()): - continue + f"ESConnection.update(index={indexName}, id={chunkId}, doc={json.dumps(condition, ensure_ascii=False)}) got exception: "+str(e)) break return False @@ -398,10 +407,13 @@ def update(self, condition: dict, newValue: dict, indexName: str, knowledgebaseI try: _ = ubq.execute() return True + except ConnectionTimeout: + logger.exception("ES request timeout") + time.sleep(3) + self._connect() + continue except Exception as e: logger.error("ESConnection.update got exception: " + str(e) + "\n".join(scripts)) - if re.search(r"(timeout|connection|conflict)", str(e).lower()): - continue break return False @@ -438,17 +450,18 @@ def delete(self, condition: dict, indexName: str, knowledgebaseId: str) -> int: logger.debug("ESConnection.delete query: " + json.dumps(qry.to_dict())) for _ in range(ATTEMPT_TIME): try: - #print(Search().query(qry).to_dict(), flush=True) res = self.es.delete_by_query( index=indexName, body=Search().query(qry).to_dict(), refresh=True) return res["deleted"] + except ConnectionTimeout: + logger.exception("ES request timeout") + time.sleep(3) + self._connect() + continue except Exception as e: logger.warning("ESConnection.delete got exception: " + str(e)) - if re.search(r"(timeout|connection)", str(e).lower()): - time.sleep(3) - continue if re.search(r"(not_found)", str(e), re.IGNORECASE): return 0 return 0 @@ -557,10 +570,12 @@ def sql(self, sql: str, fetch_size: int, format: str): request_timeout="2s") return res except ConnectionTimeout: - logger.exception("ESConnection.sql timeout") + logger.exception("ES request timeout") + time.sleep(3) + self._connect() continue except Exception: logger.exception("ESConnection.sql got exception") - return None + break logger.error(f"ESConnection.sql timeout for {ATTEMPT_TIME} times!") return None From ae3683c3469c356e73b16131ba5afd868bdacdde Mon Sep 17 00:00:00 2001 From: Fee He <602527818@qq.com> Date: Mon, 7 Jul 2025 09:48:51 +0800 Subject: [PATCH 0109/1187] fix task_service.py (#8687) Fix the case where pages variable might be None ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/db/services/task_service.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/db/services/task_service.py b/api/db/services/task_service.py index 401489a6946..95317453da9 100644 --- a/api/db/services/task_service.py +++ b/api/db/services/task_service.py @@ -338,6 +338,8 @@ def new_task(): file_bin = STORAGE_IMPL.get(bucket, name) do_layout = doc["parser_config"].get("layout_recognize", "DeepDOC") pages = PdfParser.total_page_number(doc["name"], file_bin) + if pages is None: + pages = 0 page_size = doc["parser_config"].get("task_page_size") or 12 if doc["parser_id"] == "paper": page_size = doc["parser_config"].get("task_page_size") or 22 From 9580e9965031bc19e18d3f6725376897f5dc1891 Mon Sep 17 00:00:00 2001 From: 6607changchun <84566142+6607changchun@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:15:52 +0800 Subject: [PATCH 0110/1187] fix: retry embedding with Qwen family models when limits temporarily reached. (#8690) fix: retry embedding with Qwen family models when limits temporarily reached. APIs of Qwen family models are limited by calling rates. When reached, the "output" attribute of the "resp" will be None, and in turn cause TypeError when trying to retrieve "embeddings". Since these limits are almost temporary, I have added a simple retry mechanism to avoid it. Besides, if retry_max reached, the error can be early raised, instead of hidden behind "TypeError". ### What problem does this PR solve? Sometimes Qwen blocks calling due to rate limits, but it will cause the whole parsing procedure stops when creating knowledge base. In this situation, resp["output"] will be None, and resp["output"]["embeddings"] will cause TypeError. Since the limits are temporary, I apply a simple retry mechanism to solve it. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --------- Co-authored-by: Kevin Hu --- rag/llm/embedding_model.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index fbfb7468b52..3590318f9a5 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -200,13 +200,22 @@ def __init__(self, key, model_name="text_embedding_v2", **kwargs): def encode(self, texts: list): import dashscope + import time batch_size = 4 res = [] token_count = 0 texts = [truncate(t, 2048) for t in texts] for i in range(0, len(texts), batch_size): + retry_max = 5 resp = dashscope.TextEmbedding.call(model=self.model_name, input=texts[i : i + batch_size], api_key=self.key, text_type="document") + while resp["output"] is None and retry_max > 0: + time.sleep(10) + resp = dashscope.TextEmbedding.call(model=self.model_name, input=texts[i : i + batch_size], api_key=self.key, text_type="document") + retry_max -= 1 + if retry_max == 0 and resp["output"] is None: + log_exception(ValueError("Retry_max reached, calling embedding model failed")) + raise try: embds = [[] for _ in range(len(resp["output"]["embeddings"]))] for e in resp["output"]["embeddings"]: From 4a9708889eedc7abafd23be026f0942bd139f7c1 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 7 Jul 2025 12:18:18 +0800 Subject: [PATCH 0111/1187] Feat: Support uploading files when running agent #3221 (#8697) ### What problem does this PR solve? Feat: Support uploading files when running agent #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/file-upload.tsx | 1434 +++++++++++++++++ web/src/hooks/use-agent-request.ts | 32 + web/src/pages/agent/chat/box.tsx | 4 +- web/src/pages/agent/debug-content/index.tsx | 27 +- .../pages/agent/debug-content/uploader.tsx | 116 ++ web/src/pages/agent/utils.ts | 23 +- web/src/pages/agent/utils/chat.ts | 21 + web/src/services/flow-service.ts | 5 + web/src/utils/api.ts | 1 + 9 files changed, 1635 insertions(+), 28 deletions(-) create mode 100644 web/src/components/file-upload.tsx create mode 100644 web/src/pages/agent/debug-content/uploader.tsx create mode 100644 web/src/pages/agent/utils/chat.ts diff --git a/web/src/components/file-upload.tsx b/web/src/components/file-upload.tsx new file mode 100644 index 00000000000..d880e43423e --- /dev/null +++ b/web/src/components/file-upload.tsx @@ -0,0 +1,1434 @@ +'use client'; + +import { cn } from '@/lib/utils'; +import { Slot } from '@radix-ui/react-slot'; +import { + FileArchiveIcon, + FileAudioIcon, + FileCodeIcon, + FileCogIcon, + FileIcon, + FileTextIcon, + FileVideoIcon, +} from 'lucide-react'; +import * as React from 'react'; + +const ROOT_NAME = 'FileUpload'; +const DROPZONE_NAME = 'FileUploadDropzone'; +const TRIGGER_NAME = 'FileUploadTrigger'; +const LIST_NAME = 'FileUploadList'; +const ITEM_NAME = 'FileUploadItem'; +const ITEM_PREVIEW_NAME = 'FileUploadItemPreview'; +const ITEM_METADATA_NAME = 'FileUploadItemMetadata'; +const ITEM_PROGRESS_NAME = 'FileUploadItemProgress'; +const ITEM_DELETE_NAME = 'FileUploadItemDelete'; +const CLEAR_NAME = 'FileUploadClear'; + +function useLazyRef(fn: () => T) { + const ref = React.useRef(null); + + if (ref.current === null) { + ref.current = fn(); + } + + return ref as React.RefObject; +} + +type Direction = 'ltr' | 'rtl'; + +const DirectionContext = React.createContext(undefined); + +function useDirection(dirProp?: Direction): Direction { + const contextDir = React.useContext(DirectionContext); + return dirProp ?? contextDir ?? 'ltr'; +} + +interface FileState { + file: File; + progress: number; + error?: string; + status: 'idle' | 'uploading' | 'error' | 'success'; +} + +interface StoreState { + files: Map; + dragOver: boolean; + invalid: boolean; +} + +type StoreAction = + | { type: 'ADD_FILES'; files: File[] } + | { type: 'SET_FILES'; files: File[] } + | { type: 'SET_PROGRESS'; file: File; progress: number } + | { type: 'SET_SUCCESS'; file: File } + | { type: 'SET_ERROR'; file: File; error: string } + | { type: 'REMOVE_FILE'; file: File } + | { type: 'SET_DRAG_OVER'; dragOver: boolean } + | { type: 'SET_INVALID'; invalid: boolean } + | { type: 'CLEAR' }; + +function createStore( + listeners: Set<() => void>, + files: Map, + urlCache: WeakMap, + invalid: boolean, + onValueChange?: (files: File[]) => void, +) { + let state: StoreState = { + files, + dragOver: false, + invalid: invalid, + }; + + function reducer(state: StoreState, action: StoreAction): StoreState { + switch (action.type) { + case 'ADD_FILES': { + for (const file of action.files) { + files.set(file, { + file, + progress: 0, + status: 'idle', + }); + } + + if (onValueChange) { + const fileList = Array.from(files.values()).map( + (fileState) => fileState.file, + ); + onValueChange(fileList); + } + return { ...state, files }; + } + + case 'SET_FILES': { + const newFileSet = new Set(action.files); + for (const existingFile of files.keys()) { + if (!newFileSet.has(existingFile)) { + files.delete(existingFile); + } + } + + for (const file of action.files) { + const existingState = files.get(file); + if (!existingState) { + files.set(file, { + file, + progress: 0, + status: 'idle', + }); + } + } + return { ...state, files }; + } + + case 'SET_PROGRESS': { + const fileState = files.get(action.file); + if (fileState) { + files.set(action.file, { + ...fileState, + progress: action.progress, + status: 'uploading', + }); + } + return { ...state, files }; + } + + case 'SET_SUCCESS': { + const fileState = files.get(action.file); + if (fileState) { + files.set(action.file, { + ...fileState, + progress: 100, + status: 'success', + }); + } + return { ...state, files }; + } + + case 'SET_ERROR': { + const fileState = files.get(action.file); + if (fileState) { + files.set(action.file, { + ...fileState, + error: action.error, + status: 'error', + }); + } + return { ...state, files }; + } + + case 'REMOVE_FILE': { + if (urlCache) { + const cachedUrl = urlCache.get(action.file); + if (cachedUrl) { + URL.revokeObjectURL(cachedUrl); + urlCache.delete(action.file); + } + } + + files.delete(action.file); + + if (onValueChange) { + const fileList = Array.from(files.values()).map( + (fileState) => fileState.file, + ); + onValueChange(fileList); + } + return { ...state, files }; + } + + case 'SET_DRAG_OVER': { + return { ...state, dragOver: action.dragOver }; + } + + case 'SET_INVALID': { + return { ...state, invalid: action.invalid }; + } + + case 'CLEAR': { + if (urlCache) { + for (const file of files.keys()) { + const cachedUrl = urlCache.get(file); + if (cachedUrl) { + URL.revokeObjectURL(cachedUrl); + urlCache.delete(file); + } + } + } + + files.clear(); + if (onValueChange) { + onValueChange([]); + } + return { ...state, files, invalid: false }; + } + + default: + return state; + } + } + + function getState() { + return state; + } + + function dispatch(action: StoreAction) { + state = reducer(state, action); + for (const listener of listeners) { + listener(); + } + } + + function subscribe(listener: () => void) { + listeners.add(listener); + return () => listeners.delete(listener); + } + + return { getState, dispatch, subscribe }; +} + +const StoreContext = React.createContext | null>( + null, +); + +function useStoreContext(consumerName: string) { + const context = React.useContext(StoreContext); + if (!context) { + throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``); + } + return context; +} + +function useStore(selector: (state: StoreState) => T): T { + const store = useStoreContext(ROOT_NAME); + + const lastValueRef = useLazyRef<{ value: T; state: StoreState } | null>( + () => null, + ); + + const getSnapshot = React.useCallback(() => { + const state = store.getState(); + const prevValue = lastValueRef.current; + + if (prevValue && prevValue.state === state) { + return prevValue.value; + } + + const nextValue = selector(state); + lastValueRef.current = { value: nextValue, state }; + return nextValue; + }, [store, selector, lastValueRef]); + + return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot); +} + +interface FileUploadContextValue { + inputId: string; + dropzoneId: string; + listId: string; + labelId: string; + disabled: boolean; + dir: Direction; + inputRef: React.RefObject; + urlCache: WeakMap; +} + +const FileUploadContext = React.createContext( + null, +); + +function useFileUploadContext(consumerName: string) { + const context = React.useContext(FileUploadContext); + if (!context) { + throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``); + } + return context; +} + +interface FileUploadRootProps + extends Omit< + React.ComponentPropsWithoutRef<'div'>, + 'defaultValue' | 'onChange' + > { + value?: File[]; + defaultValue?: File[]; + onValueChange?: (files: File[]) => void; + onAccept?: (files: File[]) => void; + onFileAccept?: (file: File) => void; + onFileReject?: (file: File, message: string) => void; + onFileValidate?: (file: File) => string | null | undefined; + onUpload?: ( + files: File[], + options: { + onProgress: (file: File, progress: number) => void; + onSuccess: (file: File) => void; + onError: (file: File, error: Error) => void; + }, + ) => Promise | void; + accept?: string; + maxFiles?: number; + maxSize?: number; + dir?: Direction; + label?: string; + name?: string; + asChild?: boolean; + disabled?: boolean; + invalid?: boolean; + multiple?: boolean; + required?: boolean; +} + +function FileUploadRoot(props: FileUploadRootProps) { + const { + value, + defaultValue, + onValueChange, + onAccept, + onFileAccept, + onFileReject, + onFileValidate, + onUpload, + accept, + maxFiles, + maxSize, + dir: dirProp, + label, + name, + asChild, + disabled = false, + invalid = false, + multiple = false, + required = false, + children, + className, + ...rootProps + } = props; + + const inputId = React.useId(); + const dropzoneId = React.useId(); + const listId = React.useId(); + const labelId = React.useId(); + + const dir = useDirection(dirProp); + const listeners = useLazyRef(() => new Set<() => void>()).current; + const files = useLazyRef>(() => new Map()).current; + const urlCache = useLazyRef(() => new WeakMap()).current; + const inputRef = React.useRef(null); + const isControlled = value !== undefined; + + const store = React.useMemo( + () => createStore(listeners, files, urlCache, invalid, onValueChange), + [listeners, files, invalid, onValueChange, urlCache], + ); + + const acceptTypes = React.useMemo( + () => accept?.split(',').map((t) => t.trim()) ?? null, + [accept], + ); + + const onProgress = useLazyRef(() => { + let frame = 0; + return (file: File, progress: number) => { + if (frame) return; + frame = requestAnimationFrame(() => { + frame = 0; + store.dispatch({ + type: 'SET_PROGRESS', + file, + progress: Math.min(Math.max(0, progress), 100), + }); + }); + }; + }).current; + + React.useEffect(() => { + if (isControlled) { + store.dispatch({ type: 'SET_FILES', files: value }); + } else if ( + defaultValue && + defaultValue.length > 0 && + !store.getState().files.size + ) { + store.dispatch({ type: 'SET_FILES', files: defaultValue }); + } + }, [value, defaultValue, isControlled, store]); + + React.useEffect(() => { + return () => { + for (const file of files.keys()) { + const cachedUrl = urlCache.get(file); + if (cachedUrl) { + URL.revokeObjectURL(cachedUrl); + } + } + }; + }, [files, urlCache]); + + const onFilesChange = React.useCallback( + (originalFiles: File[]) => { + if (disabled) return; + + let filesToProcess = [...originalFiles]; + let invalid = false; + + if (maxFiles) { + const currentCount = store.getState().files.size; + const remainingSlotCount = Math.max(0, maxFiles - currentCount); + + if (remainingSlotCount < filesToProcess.length) { + const rejectedFiles = filesToProcess.slice(remainingSlotCount); + invalid = true; + + filesToProcess = filesToProcess.slice(0, remainingSlotCount); + + for (const file of rejectedFiles) { + let rejectionMessage = `Maximum ${maxFiles} files allowed`; + + if (onFileValidate) { + const validationMessage = onFileValidate(file); + if (validationMessage) { + rejectionMessage = validationMessage; + } + } + + onFileReject?.(file, rejectionMessage); + } + } + } + + const acceptedFiles: File[] = []; + const rejectedFiles: { file: File; message: string }[] = []; + + for (const file of filesToProcess) { + let rejected = false; + let rejectionMessage = ''; + + if (onFileValidate) { + const validationMessage = onFileValidate(file); + if (validationMessage) { + rejectionMessage = validationMessage; + onFileReject?.(file, rejectionMessage); + rejected = true; + invalid = true; + continue; + } + } + + if (acceptTypes) { + const fileType = file.type; + const fileExtension = `.${file.name.split('.').pop()}`; + + if ( + !acceptTypes.some( + (type) => + type === fileType || + type === fileExtension || + (type.includes('/*') && + fileType.startsWith(type.replace('/*', '/'))), + ) + ) { + rejectionMessage = 'File type not accepted'; + onFileReject?.(file, rejectionMessage); + rejected = true; + invalid = true; + } + } + + if (maxSize && file.size > maxSize) { + rejectionMessage = 'File too large'; + onFileReject?.(file, rejectionMessage); + rejected = true; + invalid = true; + } + + if (!rejected) { + acceptedFiles.push(file); + } else { + rejectedFiles.push({ file, message: rejectionMessage }); + } + } + + if (invalid) { + store.dispatch({ type: 'SET_INVALID', invalid }); + setTimeout(() => { + store.dispatch({ type: 'SET_INVALID', invalid: false }); + }, 2000); + } + + if (acceptedFiles.length > 0) { + store.dispatch({ type: 'ADD_FILES', files: acceptedFiles }); + + if (isControlled && onValueChange) { + const currentFiles = Array.from(store.getState().files.values()).map( + (f) => f.file, + ); + onValueChange([...currentFiles]); + } + + if (onAccept) { + onAccept(acceptedFiles); + } + + for (const file of acceptedFiles) { + onFileAccept?.(file); + } + + if (onUpload) { + requestAnimationFrame(() => { + onFilesUpload(acceptedFiles); + }); + } + } + }, + [ + store, + isControlled, + onValueChange, + onAccept, + onFileAccept, + onUpload, + maxFiles, + onFileValidate, + onFileReject, + acceptTypes, + maxSize, + disabled, + ], + ); + + const onFilesUpload = React.useCallback( + async (files: File[]) => { + try { + for (const file of files) { + store.dispatch({ type: 'SET_PROGRESS', file, progress: 0 }); + } + + if (onUpload) { + await onUpload(files, { + onProgress, + onSuccess: (file) => { + store.dispatch({ type: 'SET_SUCCESS', file }); + }, + onError: (file, error) => { + store.dispatch({ + type: 'SET_ERROR', + file, + error: error.message ?? 'Upload failed', + }); + }, + }); + } else { + for (const file of files) { + store.dispatch({ type: 'SET_SUCCESS', file }); + } + } + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Upload failed'; + for (const file of files) { + store.dispatch({ + type: 'SET_ERROR', + file, + error: errorMessage, + }); + } + } + }, + [store, onUpload, onProgress], + ); + + const onInputChange = React.useCallback( + (event: React.ChangeEvent) => { + const files = Array.from(event.target.files ?? []); + onFilesChange(files); + event.target.value = ''; + }, + [onFilesChange], + ); + + const contextValue = React.useMemo( + () => ({ + dropzoneId, + inputId, + listId, + labelId, + dir, + disabled, + inputRef, + urlCache, + }), + [dropzoneId, inputId, listId, labelId, dir, disabled, urlCache], + ); + + const RootPrimitive = asChild ? Slot : 'div'; + + return ( + + + + {children} + + + {label ?? 'File upload'} + + + + + ); +} + +interface FileUploadDropzoneProps + extends React.ComponentPropsWithoutRef<'div'> { + asChild?: boolean; +} + +function FileUploadDropzone(props: FileUploadDropzoneProps) { + const { + asChild, + className, + onClick: onClickProp, + onDragOver: onDragOverProp, + onDragEnter: onDragEnterProp, + onDragLeave: onDragLeaveProp, + onDrop: onDropProp, + onPaste: onPasteProp, + onKeyDown: onKeyDownProp, + ...dropzoneProps + } = props; + + const context = useFileUploadContext(DROPZONE_NAME); + const store = useStoreContext(DROPZONE_NAME); + const dragOver = useStore((state) => state.dragOver); + const invalid = useStore((state) => state.invalid); + + const onClick = React.useCallback( + (event: React.MouseEvent) => { + onClickProp?.(event); + + if (event.defaultPrevented) return; + + const target = event.target; + + const isFromTrigger = + target instanceof HTMLElement && + target.closest('[data-slot="file-upload-trigger"]'); + + if (!isFromTrigger) { + context.inputRef.current?.click(); + } + }, + [context.inputRef, onClickProp], + ); + + const onDragOver = React.useCallback( + (event: React.DragEvent) => { + onDragOverProp?.(event); + + if (event.defaultPrevented) return; + + event.preventDefault(); + store.dispatch({ type: 'SET_DRAG_OVER', dragOver: true }); + }, + [store, onDragOverProp], + ); + + const onDragEnter = React.useCallback( + (event: React.DragEvent) => { + onDragEnterProp?.(event); + + if (event.defaultPrevented) return; + + event.preventDefault(); + store.dispatch({ type: 'SET_DRAG_OVER', dragOver: true }); + }, + [store, onDragEnterProp], + ); + + const onDragLeave = React.useCallback( + (event: React.DragEvent) => { + onDragLeaveProp?.(event); + + if (event.defaultPrevented) return; + + const relatedTarget = event.relatedTarget; + if ( + relatedTarget && + relatedTarget instanceof Node && + event.currentTarget.contains(relatedTarget) + ) { + return; + } + + event.preventDefault(); + store.dispatch({ type: 'SET_DRAG_OVER', dragOver: false }); + }, + [store, onDragLeaveProp], + ); + + const onDrop = React.useCallback( + (event: React.DragEvent) => { + onDropProp?.(event); + + if (event.defaultPrevented) return; + + event.preventDefault(); + store.dispatch({ type: 'SET_DRAG_OVER', dragOver: false }); + + const files = Array.from(event.dataTransfer.files); + const inputElement = context.inputRef.current; + if (!inputElement) return; + + const dataTransfer = new DataTransfer(); + for (const file of files) { + dataTransfer.items.add(file); + } + + inputElement.files = dataTransfer.files; + inputElement.dispatchEvent(new Event('change', { bubbles: true })); + }, + [store, context.inputRef, onDropProp], + ); + + const onPaste = React.useCallback( + (event: React.ClipboardEvent) => { + onPasteProp?.(event); + + if (event.defaultPrevented) return; + + event.preventDefault(); + store.dispatch({ type: 'SET_DRAG_OVER', dragOver: false }); + + const items = event.clipboardData?.items; + if (!items) return; + + const files: File[] = []; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (item?.kind === 'file') { + const file = item.getAsFile(); + if (file) { + files.push(file); + } + } + } + + if (files.length === 0) return; + + const inputElement = context.inputRef.current; + if (!inputElement) return; + + const dataTransfer = new DataTransfer(); + for (const file of files) { + dataTransfer.items.add(file); + } + + inputElement.files = dataTransfer.files; + inputElement.dispatchEvent(new Event('change', { bubbles: true })); + }, + [store, context.inputRef, onPasteProp], + ); + + const onKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + onKeyDownProp?.(event); + + if ( + !event.defaultPrevented && + (event.key === 'Enter' || event.key === ' ') + ) { + event.preventDefault(); + context.inputRef.current?.click(); + } + }, + [context.inputRef, onKeyDownProp], + ); + + const DropzonePrimitive = asChild ? Slot : 'div'; + + return ( + + ); +} + +interface FileUploadTriggerProps + extends React.ComponentPropsWithoutRef<'button'> { + asChild?: boolean; +} + +function FileUploadTrigger(props: FileUploadTriggerProps) { + const { asChild, onClick: onClickProp, ...triggerProps } = props; + const context = useFileUploadContext(TRIGGER_NAME); + + const onClick = React.useCallback( + (event: React.MouseEvent) => { + onClickProp?.(event); + + if (event.defaultPrevented) return; + + context.inputRef.current?.click(); + }, + [context.inputRef, onClickProp], + ); + + const TriggerPrimitive = asChild ? Slot : 'button'; + + return ( + + ); +} + +interface FileUploadListProps extends React.ComponentPropsWithoutRef<'div'> { + orientation?: 'horizontal' | 'vertical'; + asChild?: boolean; + forceMount?: boolean; +} + +function FileUploadList(props: FileUploadListProps) { + const { + className, + orientation = 'vertical', + asChild, + forceMount, + ...listProps + } = props; + + const context = useFileUploadContext(LIST_NAME); + const fileCount = useStore((state) => state.files.size); + const shouldRender = forceMount || fileCount > 0; + + if (!shouldRender) return null; + + const ListPrimitive = asChild ? Slot : 'div'; + + return ( + + ); +} + +interface FileUploadItemContextValue { + id: string; + fileState: FileState | undefined; + nameId: string; + sizeId: string; + statusId: string; + messageId: string; +} + +const FileUploadItemContext = + React.createContext(null); + +function useFileUploadItemContext(consumerName: string) { + const context = React.useContext(FileUploadItemContext); + if (!context) { + throw new Error(`\`${consumerName}\` must be used within \`${ITEM_NAME}\``); + } + return context; +} + +interface FileUploadItemProps extends React.ComponentPropsWithoutRef<'div'> { + value: File; + asChild?: boolean; +} + +function FileUploadItem(props: FileUploadItemProps) { + const { value, asChild, className, ...itemProps } = props; + + const id = React.useId(); + const statusId = `${id}-status`; + const nameId = `${id}-name`; + const sizeId = `${id}-size`; + const messageId = `${id}-message`; + + const context = useFileUploadContext(ITEM_NAME); + const fileState = useStore((state) => state.files.get(value)); + const fileCount = useStore((state) => state.files.size); + const fileIndex = useStore((state) => { + const files = Array.from(state.files.keys()); + return files.indexOf(value) + 1; + }); + + const itemContext = React.useMemo( + () => ({ + id, + fileState, + nameId, + sizeId, + statusId, + messageId, + }), + [id, fileState, statusId, nameId, sizeId, messageId], + ); + + if (!fileState) return null; + + const statusText = fileState.error + ? `Error: ${fileState.error}` + : fileState.status === 'uploading' + ? `Uploading: ${fileState.progress}% complete` + : fileState.status === 'success' + ? 'Upload complete' + : 'Ready to upload'; + + const ItemPrimitive = asChild ? Slot : 'div'; + + return ( + + + {props.children} + + {statusText} + + + + ); +} + +function formatBytes(bytes: number) { + if (bytes === 0) return '0 B'; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return `${(bytes / 1024 ** i).toFixed(i ? 1 : 0)} ${sizes[i]}`; +} + +function getFileIcon(file: File) { + const type = file.type; + const extension = file.name.split('.').pop()?.toLowerCase() ?? ''; + + if (type.startsWith('video/')) { + return ; + } + + if (type.startsWith('audio/')) { + return ; + } + + if ( + type.startsWith('text/') || + ['txt', 'md', 'rtf', 'pdf'].includes(extension) + ) { + return ; + } + + if ( + [ + 'html', + 'css', + 'js', + 'jsx', + 'ts', + 'tsx', + 'json', + 'xml', + 'php', + 'py', + 'rb', + 'java', + 'c', + 'cpp', + 'cs', + ].includes(extension) + ) { + return ; + } + + if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2'].includes(extension)) { + return ; + } + + if ( + ['exe', 'msi', 'app', 'apk', 'deb', 'rpm'].includes(extension) || + type.startsWith('application/') + ) { + return ; + } + + return ; +} + +interface FileUploadItemPreviewProps + extends React.ComponentPropsWithoutRef<'div'> { + render?: (file: File) => React.ReactNode; + asChild?: boolean; +} + +function FileUploadItemPreview(props: FileUploadItemPreviewProps) { + const { render, asChild, children, className, ...previewProps } = props; + + const itemContext = useFileUploadItemContext(ITEM_PREVIEW_NAME); + const context = useFileUploadContext(ITEM_PREVIEW_NAME); + + const onPreviewRender = React.useCallback( + (file: File) => { + if (render) return render(file); + + if (itemContext.fileState?.file.type.startsWith('image/')) { + let url = context.urlCache.get(file); + if (!url) { + url = URL.createObjectURL(file); + context.urlCache.set(file, url); + } + + return ( + {file.name} + ); + } + + return getFileIcon(file); + }, + [render, itemContext.fileState?.file.type, context.urlCache], + ); + + if (!itemContext.fileState) return null; + + const ItemPreviewPrimitive = asChild ? Slot : 'div'; + + return ( + svg]:size-10', + className, + )} + > + {onPreviewRender(itemContext.fileState.file)} + {children} + + ); +} + +interface FileUploadItemMetadataProps + extends React.ComponentPropsWithoutRef<'div'> { + asChild?: boolean; + size?: 'default' | 'sm'; +} + +function FileUploadItemMetadata(props: FileUploadItemMetadataProps) { + const { + asChild, + size = 'default', + children, + className, + ...metadataProps + } = props; + + const context = useFileUploadContext(ITEM_METADATA_NAME); + const itemContext = useFileUploadItemContext(ITEM_METADATA_NAME); + + if (!itemContext.fileState) return null; + + const ItemMetadataPrimitive = asChild ? Slot : 'div'; + + return ( + + {children ?? ( + <> + + {itemContext.fileState.file.name} + + + {formatBytes(itemContext.fileState.file.size)} + + {itemContext.fileState.error && ( + + {itemContext.fileState.error} + + )} + + )} + + ); +} +interface FileUploadItemProgressProps + extends React.ComponentPropsWithoutRef<'div'> { + variant?: 'linear' | 'circular' | 'fill'; + size?: number; + asChild?: boolean; + forceMount?: boolean; +} + +function FileUploadItemProgress(props: FileUploadItemProgressProps) { + const { + variant = 'linear', + size = 40, + asChild, + forceMount, + className, + ...progressProps + } = props; + + const itemContext = useFileUploadItemContext(ITEM_PROGRESS_NAME); + + if (!itemContext.fileState) return null; + + const shouldRender = forceMount || itemContext.fileState.progress !== 100; + + if (!shouldRender) return null; + + const ItemProgressPrimitive = asChild ? Slot : 'div'; + + switch (variant) { + case 'circular': { + const circumference = 2 * Math.PI * ((size - 4) / 2); + const strokeDashoffset = + circumference - (itemContext.fileState.progress / 100) * circumference; + + return ( + + + + + + + ); + } + + case 'fill': { + const progressPercentage = itemContext.fileState.progress; + const topInset = 100 - progressPercentage; + + return ( + + ); + } + + default: + return ( + +
    + + ); + } +} + +interface FileUploadItemDeleteProps + extends React.ComponentPropsWithoutRef<'button'> { + asChild?: boolean; +} + +function FileUploadItemDelete(props: FileUploadItemDeleteProps) { + const { asChild, onClick: onClickProp, ...deleteProps } = props; + + const store = useStoreContext(ITEM_DELETE_NAME); + const itemContext = useFileUploadItemContext(ITEM_DELETE_NAME); + + const onClick = React.useCallback( + (event: React.MouseEvent) => { + onClickProp?.(event); + + if (!itemContext.fileState || event.defaultPrevented) return; + + store.dispatch({ + type: 'REMOVE_FILE', + file: itemContext.fileState.file, + }); + }, + [store, itemContext.fileState, onClickProp], + ); + + if (!itemContext.fileState) return null; + + const ItemDeletePrimitive = asChild ? Slot : 'button'; + + return ( + + ); +} + +interface FileUploadClearProps + extends React.ComponentPropsWithoutRef<'button'> { + forceMount?: boolean; + asChild?: boolean; +} + +function FileUploadClear(props: FileUploadClearProps) { + const { + asChild, + forceMount, + disabled, + onClick: onClickProp, + ...clearProps + } = props; + + const context = useFileUploadContext(CLEAR_NAME); + const store = useStoreContext(CLEAR_NAME); + const fileCount = useStore((state) => state.files.size); + + const isDisabled = disabled || context.disabled; + + const onClick = React.useCallback( + (event: React.MouseEvent) => { + onClickProp?.(event); + + if (event.defaultPrevented) return; + + store.dispatch({ type: 'CLEAR' }); + }, + [store, onClickProp], + ); + + const shouldRender = forceMount || fileCount > 0; + + if (!shouldRender) return null; + + const ClearPrimitive = asChild ? Slot : 'button'; + + return ( + + ); +} + +export { + FileUploadClear as Clear, + FileUploadDropzone as Dropzone, + FileUploadRoot as FileUpload, + FileUploadClear, + FileUploadDropzone, + FileUploadItem, + FileUploadItemDelete, + FileUploadItemMetadata, + FileUploadItemPreview, + FileUploadItemProgress, + FileUploadList, + FileUploadTrigger, + FileUploadItem as Item, + FileUploadItemDelete as ItemDelete, + FileUploadItemMetadata as ItemMetadata, + FileUploadItemPreview as ItemPreview, + FileUploadItemProgress as ItemProgress, + FileUploadList as List, + // + FileUploadRoot as Root, + FileUploadTrigger as Trigger, + // + useStore as useFileUpload, + // + type FileUploadRootProps as FileUploadProps, +}; diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index 016718af86f..cf099653701 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -26,6 +26,7 @@ export const enum AgentApiAction { ResetAgent = 'resetAgent', SetAgent = 'setAgent', FetchAgentTemplates = 'fetchAgentTemplates', + UploadCanvasFile = 'uploadCanvasFile', } export const EmptyDsl = { @@ -268,3 +269,34 @@ export const useSetAgent = () => { return { data, loading, setAgent: mutateAsync }; }; + +export const useUploadCanvasFile = () => { + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [AgentApiAction.UploadCanvasFile], + mutationFn: async (body: any) => { + let nextBody = body; + try { + if (Array.isArray(body)) { + nextBody = new FormData(); + body.forEach((file: File) => { + nextBody.append('file', file as any); + }); + } + + const { data } = await flowService.uploadCanvasFile(nextBody); + if (data?.code === 0) { + message.success(i18n.t('message.uploaded')); + } + return data; + } catch (error) { + message.error('error'); + } + }, + }); + + return { data, loading, uploadCanvasFile: mutateAsync }; +}; diff --git a/web/src/pages/agent/chat/box.tsx b/web/src/pages/agent/chat/box.tsx index 34d886ef737..bae4c50bbed 100644 --- a/web/src/pages/agent/chat/box.tsx +++ b/web/src/pages/agent/chat/box.tsx @@ -1,6 +1,5 @@ import { MessageType } from '@/constants/chat'; import { useGetFileIcon } from '@/pages/chat/hooks'; -import { buildMessageItemReference } from '@/pages/chat/utils'; import { Spin } from 'antd'; import { useSendNextMessage } from './hooks'; @@ -19,6 +18,7 @@ import { useParams } from 'umi'; import DebugContent from '../debug-content'; import { BeginQuery } from '../interface'; import { buildBeginQueryWithObject } from '../utils'; +import { buildAgentMessageItemReference } from '../utils/chat'; const AgentChatBox = () => { const { @@ -88,7 +88,7 @@ const AgentChatBox = () => { avatar={userInfo.avatar} avatarDialog={canvasInfo.avatar} item={message} - reference={buildMessageItemReference( + reference={buildAgentMessageItemReference( { message: derivedMessages, reference }, message, )} diff --git a/web/src/pages/agent/debug-content/index.tsx b/web/src/pages/agent/debug-content/index.tsx index 9228bed85d3..ae0ab4120f5 100644 --- a/web/src/pages/agent/debug-content/index.tsx +++ b/web/src/pages/agent/debug-content/index.tsx @@ -1,4 +1,3 @@ -import { FileUploader } from '@/components/file-uploader'; import { ButtonLoading } from '@/components/ui/button'; import { Form, @@ -19,6 +18,7 @@ import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { BeginQueryType } from '../constant'; import { BeginQuery } from '../interface'; +import { FileUploadDirectUpload } from './uploader'; export const BeginQueryComponentMap = { [BeginQueryType.Line]: 'string', @@ -71,7 +71,7 @@ const DebugContent = ({ } else if (type === BeginQueryType.Integer) { fieldSchema = z.coerce.number(); } else { - fieldSchema = z.instanceof(File); + fieldSchema = z.record(z.any()); } if (cur.optional) { @@ -165,18 +165,16 @@ const DebugContent = ({ (
    {t('assistantAvatar')} - + onChange={field.onChange} + > @@ -232,18 +230,7 @@ const DebugContent = ({ (values: z.infer) => { const nextValues = Object.entries(values).map(([key, value]) => { const item = parameters[Number(key)]; - let nextValue = value; - if (Array.isArray(value)) { - nextValue = ``; - - value.forEach((x) => { - nextValue += - x?.originFileObj instanceof File - ? `${x.name}\n${x.response?.data}\n----\n` - : `${x.url}\n${x.result}\n----\n`; - }); - } - return { ...item, value: nextValue }; + return { ...item, value }; }); ok(nextValues); diff --git a/web/src/pages/agent/debug-content/uploader.tsx b/web/src/pages/agent/debug-content/uploader.tsx new file mode 100644 index 00000000000..569f7ad3a93 --- /dev/null +++ b/web/src/pages/agent/debug-content/uploader.tsx @@ -0,0 +1,116 @@ +'use client'; + +import { + FileUpload, + FileUploadDropzone, + FileUploadItem, + FileUploadItemDelete, + FileUploadItemMetadata, + FileUploadItemPreview, + FileUploadItemProgress, + FileUploadList, + FileUploadTrigger, + type FileUploadProps, +} from '@/components/file-upload'; +import { Button } from '@/components/ui/button'; +import { useUploadCanvasFile } from '@/hooks/use-agent-request'; +import { Upload, X } from 'lucide-react'; +import * as React from 'react'; +import { toast } from 'sonner'; + +type FileUploadDirectUploadProps = { + value: Record; + onChange(value: Record): void; +}; + +export function FileUploadDirectUpload({ + onChange, +}: FileUploadDirectUploadProps) { + const [files, setFiles] = React.useState([]); + + const { uploadCanvasFile } = useUploadCanvasFile(); + + const onUpload: NonNullable = React.useCallback( + async (files, { onSuccess, onError }) => { + try { + const uploadPromises = files.map(async (file) => { + const handleError = (error?: any) => { + onError( + file, + error instanceof Error ? error : new Error('Upload failed'), + ); + }; + try { + const ret = await uploadCanvasFile([file]); + if (ret.code === 0) { + onSuccess(file); + onChange(ret.data); + } else { + handleError(); + } + } catch (error) { + handleError(error); + } + }); + + // Wait for all uploads to complete + await Promise.all(uploadPromises); + } catch (error) { + // This handles any error that might occur outside the individual upload processes + console.error('Unexpected error during upload:', error); + } + }, + [onChange, uploadCanvasFile], + ); + + const onFileReject = React.useCallback((file: File, message: string) => { + toast(message, { + description: `"${file.name.length > 20 ? `${file.name.slice(0, 20)}...` : file.name}" has been rejected`, + }); + }, []); + + return ( + + +
    +
    + +
    +

    Drag & drop files here

    +

    + Or click to browse (max 2 files) +

    +
    + + + +
    + + {files.map((file, index) => ( + +
    + + + + + +
    + +
    + ))} +
    +
    + ); +} diff --git a/web/src/pages/agent/utils.ts b/web/src/pages/agent/utils.ts index f1167b791d7..e2e064487fd 100644 --- a/web/src/pages/agent/utils.ts +++ b/web/src/pages/agent/utils.ts @@ -150,13 +150,22 @@ function buildAgentTools(edges: Edge[], nodes: Node[], nodeId: string) { (params as IAgentForm).tools = (params as IAgentForm).tools.concat( bottomSubAgentEdges.map((x) => { - const formData = buildAgentTools(edges, nodes, x.target); - - return { component_name: Operator.Agent, params: { ...formData } }; + const { + params: formData, + id, + name, + } = buildAgentTools(edges, nodes, x.target); + + return { + component_name: Operator.Agent, + id, + name, + params: { ...formData }, + }; }), ); } - return params; + return { params, name: node?.data.name, id: node?.id }; } function filterTargetsBySourceHandleId(edges: Edge[], handleId: string) { @@ -221,9 +230,11 @@ export const buildDslComponentsByGraph = ( let params = x?.data.form ?? {}; switch (operatorName) { - case Operator.Agent: - params = buildAgentTools(edges, nodes, id); + case Operator.Agent: { + const { params: formData } = buildAgentTools(edges, nodes, id); + params = formData; break; + } case Operator.Categorize: params = buildCategorizeTos(edges, nodes, id); break; diff --git a/web/src/pages/agent/utils/chat.ts b/web/src/pages/agent/utils/chat.ts new file mode 100644 index 00000000000..15c8ff85028 --- /dev/null +++ b/web/src/pages/agent/utils/chat.ts @@ -0,0 +1,21 @@ +import { MessageType } from '@/constants/chat'; +import { IReference } from '@/interfaces/database/chat'; +import { IMessage } from '@/pages/chat/interface'; +import { isEmpty } from 'lodash'; + +export const buildAgentMessageItemReference = ( + conversation: { message: IMessage[]; reference: IReference[] }, + message: IMessage, +) => { + const assistantMessages = conversation.message?.filter( + (x) => x.role === MessageType.Assistant, + ); + const referenceIndex = assistantMessages.findIndex( + (x) => x.id === message.id, + ); + const reference = !isEmpty(message?.reference) + ? message?.reference + : (conversation?.reference ?? [])[referenceIndex]; + + return reference ?? { doc_aggs: [], chunks: [], total: 0 }; +}; diff --git a/web/src/services/flow-service.ts b/web/src/services/flow-service.ts index 7da121ea475..846d8ec7419 100644 --- a/web/src/services/flow-service.ts +++ b/web/src/services/flow-service.ts @@ -18,6 +18,7 @@ const { debug, listCanvasTeam, settingCanvas, + uploadCanvasFile, } = api; const methods = { @@ -81,6 +82,10 @@ const methods = { url: settingCanvas, method: 'post', }, + uploadCanvasFile: { + url: uploadCanvasFile, + method: 'post', + }, } as const; const flowService = registerServer(methods, request); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index d0369d1e846..817cf46aede 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -143,6 +143,7 @@ export default { testDbConnect: `${api_host}/canvas/test_db_connect`, getInputElements: `${api_host}/canvas/input_elements`, debug: `${api_host}/canvas/debug`, + uploadCanvasFile: `${api_host}/canvas/upload`, // mcp server getMcpServerList: `${api_host}/mcp_server/list`, From 07eee8329c2dcef9e691472ee8409400934fc723 Mon Sep 17 00:00:00 2001 From: Liu An Date: Mon, 7 Jul 2025 13:06:32 +0800 Subject: [PATCH 0112/1187] Refa: Update Minio image to specific release version in docker-compose-base.yml (#8693) ### What problem does this PR solve? - Ensure consistent Minio deployment by pinning the image to a specific release version (RELEASE.2025-06-13T11-33-47Z) for stability and reproducibility. - #8672 ### Type of change - [x] Refactoring --- docker/docker-compose-base.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index cf5c9b6b4cf..a69bcbdab50 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -164,7 +164,7 @@ services: restart: on-failure minio: - image: quay.io/minio/minio + image: quay.io/minio/minio:RELEASE.2025-06-13T11-33-47Z container_name: ragflow-minio command: server --console-address ":9001" /data ports: From 789ae87727c52aa0d20c79303f549cae827a864d Mon Sep 17 00:00:00 2001 From: cutiechi Date: Mon, 7 Jul 2025 13:07:34 +0800 Subject: [PATCH 0113/1187] Fix: Prevent Duplicate Retrieval Requests on Knowledge Testing (#8683) ### What problem does this PR solve? Previously, when testing knowledge retrieval and clicking the test button, the component would trigger two API requests instead of one. This led to redundant network calls and inconsistent results being displayed. Before: ![image](https://github.com/user-attachments/assets/530d9a97-04f7-4db4-8489-0a7b67c78194) After: ![image](https://github.com/user-attachments/assets/d17caf18-a6b1-46bc-b077-d81de0a73818) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../components/knowledge-testing/index.tsx | 41 ++++++++++++++----- .../testing-result/index.tsx | 24 ++++------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx index d7140c92618..531941d30a6 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx @@ -3,33 +3,50 @@ import { useTestChunkRetrieval, } from '@/hooks/knowledge-hooks'; import { Flex, Form } from 'antd'; +import { useMemo, useState } from 'react'; import TestingControl from './testing-control'; import TestingResult from './testing-result'; -import { useState } from 'react'; import styles from './index.less'; const KnowledgeTesting = () => { const [form] = Form.useForm(); - const { testChunk } = useTestChunkRetrieval(); - const { testChunkAll } = useTestChunkAllRetrieval(); + const { + data: retrievalData, + testChunk, + loading: retrievalLoading, + } = useTestChunkRetrieval(); + const { + data: allRetrievalData, + testChunkAll, + loading: allRetrievalLoading, + } = useTestChunkAllRetrieval(); const [selectedDocumentIds, setSelectedDocumentIds] = useState([]); const handleTesting = async (documentIds: string[] = []) => { const values = await form.validateFields(); - testChunk({ + const params = { ...values, - doc_ids: Array.isArray(documentIds) ? documentIds : [], vector_similarity_weight: 1 - values.vector_similarity_weight, - }); + }; - testChunkAll({ - ...values, - doc_ids: [], - vector_similarity_weight: 1 - values.vector_similarity_weight, - }); + if (Array.isArray(documentIds) && documentIds.length > 0) { + testChunk({ + ...params, + doc_ids: documentIds, + }); + } else { + testChunkAll({ + ...params, + doc_ids: [], + }); + } }; + const testingResult = useMemo(() => { + return selectedDocumentIds.length > 0 ? retrievalData : allRetrievalData; + }, [allRetrievalData, retrievalData, selectedDocumentIds.length]); + return ( { selectedDocumentIds={selectedDocumentIds} > Promise; selectedDocumentIds: string[]; setSelectedDocumentIds: (ids: string[]) => void; + data?: ITestingResult; + loading?: boolean; } const TestingResult = ({ handleTesting, selectedDocumentIds, setSelectedDocumentIds, + data, + loading, }: IProps) => { - const { documents, chunks, total } = useSelectTestingResult(); - const { documents: documentsAll, total: totalAll } = useAllTestingResult(); + const { documents, chunks, total } = data || {}; const { t } = useTranslate('knowledgeDetails'); const { pagination, setPagination } = useGetPaginationWithRouter(); - const isSuccess = useSelectIsTestingSuccess(); - const isAllSuccess = useAllTestingSuccess(); const onChange: PaginationProps['onChange'] = (pageNumber, pageSize) => { pagination.onChange?.(pageNumber, pageSize); @@ -97,8 +92,7 @@ const TestingResult = ({ > - {selectedDocumentIds?.length ?? 0}/ - {documentsAll?.length ?? 0} + {selectedDocumentIds?.length ?? 0}/{documents?.length ?? 0} {t('filesSelected')} @@ -121,7 +115,7 @@ const TestingResult = ({ flex={1} className={styles.selectFilesCollapse} > - {isSuccess && chunks.length > 0 ? ( + {loading === false && chunks && chunks.length > 0 ? ( chunks?.map((x) => ( }>
    @@ -136,7 +130,7 @@ const TestingResult = ({
    {x.content_with_weight}
    )) - ) : isSuccess && chunks.length === 0 ? ( + ) : loading === false && chunks && chunks.length === 0 ? ( ) : null} From 4d7bfd2ba302761d1e598cfd669f6f4af6ff3e43 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Mon, 7 Jul 2025 14:11:47 +0800 Subject: [PATCH 0114/1187] Fix: typo process_duration (#8696) ### What problem does this PR solve? Fix typo process_duration. ### Type of change - [x] Documentation Update - [x] Refactoring --- api/apps/document_app.py | 2 +- api/apps/sdk/doc.py | 12 ++++++------ api/db/db_models.py | 12 ++++++++++-- api/db/services/document_service.py | 10 +++++----- docs/references/http_api_reference.md | 4 ++-- docs/references/python_api_reference.md | 2 +- sdk/python/ragflow_sdk/modules/document.py | 2 +- .../test_parse_documents.py | 2 +- .../test_stop_parse_documents.py | 2 +- .../test_update_document.py | 2 +- .../test_parse_documents.py | 2 +- .../test_stop_parse_documents.py | 2 +- .../test_update_document.py | 2 +- .../test_parse_documents.py | 2 +- .../test_stop_parse_documents.py | 2 +- .../test_update_document.py | 2 +- .../test_document_app/test_paser_documents.py | 2 +- web/src/interfaces/database/document.ts | 2 +- web/src/interfaces/database/knowledge.ts | 2 +- .../knowledge-file/parsing-status-cell/index.tsx | 4 ++-- web/src/pages/dataset/dataset/parsing-card.tsx | 4 ++-- 21 files changed, 42 insertions(+), 34 deletions(-) diff --git a/api/apps/document_app.py b/api/apps/document_app.py index 90d62b0b382..5273c2bcfe9 100644 --- a/api/apps/document_app.py +++ b/api/apps/document_app.py @@ -490,7 +490,7 @@ def change_parser(): if "parser_config" in req: DocumentService.update_parser_config(doc.id, req["parser_config"]) if doc.token_num > 0: - e = DocumentService.increment_chunk_num(doc.id, doc.kb_id, doc.token_num * -1, doc.chunk_num * -1, doc.process_duation * -1) + e = DocumentService.increment_chunk_num(doc.id, doc.kb_id, doc.token_num * -1, doc.chunk_num * -1, doc.process_duration * -1) if not e: return get_data_error_result(message="Document not found!") tenant_id = DocumentService.get_tenant_id(req["doc_id"]) diff --git a/api/apps/sdk/doc.py b/api/apps/sdk/doc.py index c744b07ab40..c3bce0bc531 100644 --- a/api/apps/sdk/doc.py +++ b/api/apps/sdk/doc.py @@ -300,7 +300,7 @@ def update_doc(tenant_id, dataset_id, document_id): doc.kb_id, doc.token_num * -1, doc.chunk_num * -1, - doc.process_duation * -1, + doc.process_duration * -1, ) if not e: return get_error_data_result(message="Document not found!") @@ -526,11 +526,11 @@ def list_docs(dataset_id, tenant_id): "parser_id": "chunk_method", } run_mapping = { - "0": "UNSTART", - "1": "RUNNING", - "2": "CANCEL", - "3": "DONE", - "4": "FAIL", + "0": "UNSTART", + "1": "RUNNING", + "2": "CANCEL", + "3": "DONE", + "4": "FAIL", } for doc in docs: renamed_doc = {} diff --git a/api/db/db_models.py b/api/db/db_models.py index 5eba272d98e..b174d562aa9 100644 --- a/api/db/db_models.py +++ b/api/db/db_models.py @@ -632,7 +632,7 @@ class Document(DataBaseModel): progress = FloatField(default=0, index=True) progress_msg = TextField(null=True, help_text="process message", default="") process_begin_at = DateTimeField(null=True, index=True) - process_duation = FloatField(default=0) + process_duration = FloatField(default=0) meta_fields = JSONField(null=True, default={}) run = CharField(max_length=1, null=True, help_text="start to run processing or cancel.(1: run it; 2: cancel)", default="0", index=True) @@ -675,7 +675,7 @@ class Task(DataBaseModel): priority = IntegerField(default=0) begin_at = DateTimeField(null=True, index=True) - process_duation = FloatField(default=0) + process_duration = FloatField(default=0) progress = FloatField(default=0, index=True) progress_msg = TextField(null=True, help_text="process message", default="") @@ -952,3 +952,11 @@ def migrate_db(): migrate(migrator.add_column("mcp_server", "variables", JSONField(null=True, help_text="MCP Server variables", default=dict))) except Exception: pass + try: + migrate(migrator.rename_column("task", "process_duation", "process_duration")) + except Exception: + pass + try: + migrate(migrator.rename_column("document", "process_duation", "process_duration")) + except Exception: + pass diff --git a/api/db/services/document_service.py b/api/db/services/document_service.py index c69f75acae5..f6d7e0def81 100644 --- a/api/db/services/document_service.py +++ b/api/db/services/document_service.py @@ -228,10 +228,10 @@ def get_unfinished_docs(cls): @classmethod @DB.connection_context() - def increment_chunk_num(cls, doc_id, kb_id, token_num, chunk_num, duation): + def increment_chunk_num(cls, doc_id, kb_id, token_num, chunk_num, duration): num = cls.model.update(token_num=cls.model.token_num + token_num, chunk_num=cls.model.chunk_num + chunk_num, - process_duation=cls.model.process_duation + duation).where( + process_duration=cls.model.process_duration + duration).where( cls.model.id == doc_id).execute() if num == 0: raise LookupError( @@ -246,10 +246,10 @@ def increment_chunk_num(cls, doc_id, kb_id, token_num, chunk_num, duation): @classmethod @DB.connection_context() - def decrement_chunk_num(cls, doc_id, kb_id, token_num, chunk_num, duation): + def decrement_chunk_num(cls, doc_id, kb_id, token_num, chunk_num, duration): num = cls.model.update(token_num=cls.model.token_num - token_num, chunk_num=cls.model.chunk_num - chunk_num, - process_duation=cls.model.process_duation + duation).where( + process_duration=cls.model.process_duration + duration).where( cls.model.id == doc_id).execute() if num == 0: raise LookupError( @@ -525,7 +525,7 @@ def update_progress(cls): msg = "\n".join(sorted(msg)) info = { - "process_duation": datetime.timestamp( + "process_duration": datetime.timestamp( datetime.now()) - d["process_begin_at"].timestamp(), "run": status} diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index 1cca27c0e3a..d3f87b00a19 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -1058,7 +1058,7 @@ Success: }, "chunk_method": "naive", "process_begin_at": null, - "process_duation": 0.0, + "process_duration": 0.0, "progress": 0.0, "progress_msg": "", "run": "0", @@ -1417,7 +1417,7 @@ Success: } }, "process_begin_at": "Thu, 24 Oct 2024 09:56:44 GMT", - "process_duation": 0.54213, + "process_duration": 0.54213, "progress": 0.0, "progress_msg": "Task dispatched...", "run": "2", diff --git a/docs/references/python_api_reference.md b/docs/references/python_api_reference.md index 9c8437ac4ae..bb50e1e9971 100644 --- a/docs/references/python_api_reference.md +++ b/docs/references/python_api_reference.md @@ -552,7 +552,7 @@ A `Document` object contains the following attributes: - `progress`: `float` The current processing progress as a percentage. Defaults to `0.0`. - `progress_msg`: `str` A message indicating the current progress status. Defaults to `""`. - `process_begin_at`: `datetime` The start time of document processing. Defaults to `None`. -- `process_duation`: `float` Duration of the processing in seconds. Defaults to `0.0`. +- `process_duration`: `float` Duration of the processing in seconds. Defaults to `0.0`. - `run`: `str` The document's processing status: - `"UNSTART"` (default) - `"RUNNING"` diff --git a/sdk/python/ragflow_sdk/modules/document.py b/sdk/python/ragflow_sdk/modules/document.py index cec8de161f2..6cb0e3b5e31 100644 --- a/sdk/python/ragflow_sdk/modules/document.py +++ b/sdk/python/ragflow_sdk/modules/document.py @@ -41,7 +41,7 @@ def __init__(self, rag, res_dict): self.progress = 0.0 self.progress_msg = "" self.process_begin_at = None - self.process_duation = 0.0 + self.process_duration = 0.0 self.run = "0" self.status = "1" for k in list(res_dict.keys()): diff --git a/sdk/python/test/test_http_api/test_file_management_within_dataset/test_parse_documents.py b/sdk/python/test/test_http_api/test_file_management_within_dataset/test_parse_documents.py index 9c1c7302ce3..0689f717277 100644 --- a/sdk/python/test/test_http_api/test_file_management_within_dataset/test_parse_documents.py +++ b/sdk/python/test/test_http_api/test_file_management_within_dataset/test_parse_documents.py @@ -46,7 +46,7 @@ def validate_document_details(auth, dataset_id, document_ids): doc = res["data"]["docs"][0] assert doc["run"] == "DONE" assert len(doc["process_begin_at"]) > 0 - assert doc["process_duation"] > 0 + assert doc["process_duration"] > 0 assert doc["progress"] > 0 assert "Task done" in doc["progress_msg"] diff --git a/sdk/python/test/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py b/sdk/python/test/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py index 7f05302b56a..6f0927930f9 100644 --- a/sdk/python/test/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py +++ b/sdk/python/test/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py @@ -28,7 +28,7 @@ def validate_document_parse_done(auth, dataset_id, document_ids): doc = res["data"]["docs"][0] assert doc["run"] == "DONE" assert len(doc["process_begin_at"]) > 0 - assert doc["process_duation"] > 0 + assert doc["process_duration"] > 0 assert doc["progress"] > 0 assert "Task done" in doc["progress_msg"] diff --git a/sdk/python/test/test_http_api/test_file_management_within_dataset/test_update_document.py b/sdk/python/test/test_http_api/test_file_management_within_dataset/test_update_document.py index 5bf6f0410a5..29dbc55bef2 100644 --- a/sdk/python/test/test_http_api/test_file_management_within_dataset/test_update_document.py +++ b/sdk/python/test/test_http_api/test_file_management_within_dataset/test_update_document.py @@ -228,7 +228,7 @@ def test_chunk_method(self, get_http_api_auth, add_documents, chunk_method, expe marks=pytest.mark.skip(reason="issues/6104"), ), pytest.param( - {"process_duation": 1.0}, + {"process_duration": 1.0}, 102, "The input parameters are invalid.", marks=pytest.mark.skip(reason="issues/6104"), diff --git a/test/testcases/test_http_api/test_file_management_within_dataset/test_parse_documents.py b/test/testcases/test_http_api/test_file_management_within_dataset/test_parse_documents.py index e8ffa914ed3..04aa8cac214 100644 --- a/test/testcases/test_http_api/test_file_management_within_dataset/test_parse_documents.py +++ b/test/testcases/test_http_api/test_file_management_within_dataset/test_parse_documents.py @@ -47,7 +47,7 @@ def validate_document_details(auth, dataset_id, document_ids): doc = res["data"]["docs"][0] assert doc["run"] == "DONE" assert len(doc["process_begin_at"]) > 0 - assert doc["process_duation"] > 0 + assert doc["process_duration"] > 0 assert doc["progress"] > 0 assert "Task done" in doc["progress_msg"] diff --git a/test/testcases/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py b/test/testcases/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py index 4c324487893..67d89c81537 100644 --- a/test/testcases/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py +++ b/test/testcases/test_http_api/test_file_management_within_dataset/test_stop_parse_documents.py @@ -29,7 +29,7 @@ def validate_document_parse_done(auth, dataset_id, document_ids): doc = res["data"]["docs"][0] assert doc["run"] == "DONE" assert len(doc["process_begin_at"]) > 0 - assert doc["process_duation"] > 0 + assert doc["process_duration"] > 0 assert doc["progress"] > 0 assert "Task done" in doc["progress_msg"] diff --git a/test/testcases/test_http_api/test_file_management_within_dataset/test_update_document.py b/test/testcases/test_http_api/test_file_management_within_dataset/test_update_document.py index ca7bbe5c748..e2ab5d031d0 100644 --- a/test/testcases/test_http_api/test_file_management_within_dataset/test_update_document.py +++ b/test/testcases/test_http_api/test_file_management_within_dataset/test_update_document.py @@ -229,7 +229,7 @@ def test_chunk_method(self, HttpApiAuth, add_documents, chunk_method, expected_c marks=pytest.mark.skip(reason="issues/6104"), ), pytest.param( - {"process_duation": 1.0}, + {"process_duration": 1.0}, 102, "The input parameters are invalid.", marks=pytest.mark.skip(reason="issues/6104"), diff --git a/test/testcases/test_sdk_api/test_file_management_within_dataset/test_parse_documents.py b/test/testcases/test_sdk_api/test_file_management_within_dataset/test_parse_documents.py index 9b76bf6d1d4..2afc9928f0d 100644 --- a/test/testcases/test_sdk_api/test_file_management_within_dataset/test_parse_documents.py +++ b/test/testcases/test_sdk_api/test_file_management_within_dataset/test_parse_documents.py @@ -45,7 +45,7 @@ def validate_document_details(dataset, document_ids): if document.id in document_ids: assert document.run == "DONE" assert len(document.process_begin_at) > 0 - assert document.process_duation > 0 + assert document.process_duration > 0 assert document.progress > 0 assert "Task done" in document.progress_msg diff --git a/test/testcases/test_sdk_api/test_file_management_within_dataset/test_stop_parse_documents.py b/test/testcases/test_sdk_api/test_file_management_within_dataset/test_stop_parse_documents.py index 9b561881ef3..8454ed9471e 100644 --- a/test/testcases/test_sdk_api/test_file_management_within_dataset/test_stop_parse_documents.py +++ b/test/testcases/test_sdk_api/test_file_management_within_dataset/test_stop_parse_documents.py @@ -23,7 +23,7 @@ def validate_document_parse_done(dataset, document_ids): if document.id in document_ids: assert document.run == "DONE" assert len(document.process_begin_at) > 0 - assert document.process_duation > 0 + assert document.process_duration > 0 assert document.progress > 0 assert "Task done" in document.progress_msg diff --git a/test/testcases/test_sdk_api/test_file_management_within_dataset/test_update_document.py b/test/testcases/test_sdk_api/test_file_management_within_dataset/test_update_document.py index f1d3dadb8d2..1f738bea20a 100644 --- a/test/testcases/test_sdk_api/test_file_management_within_dataset/test_update_document.py +++ b/test/testcases/test_sdk_api/test_file_management_within_dataset/test_update_document.py @@ -141,7 +141,7 @@ def test_chunk_method(self, add_documents, chunk_method, expected_message): marks=pytest.mark.skip(reason="issues/6104"), ), pytest.param( - {"process_duation": 1.0}, + {"process_duration": 1.0}, "The input parameters are invalid", marks=pytest.mark.skip(reason="issues/6104"), ), diff --git a/test/testcases/test_web_api/test_document_app/test_paser_documents.py b/test/testcases/test_web_api/test_document_app/test_paser_documents.py index 34e88557181..55946b7f5a0 100644 --- a/test/testcases/test_web_api/test_document_app/test_paser_documents.py +++ b/test/testcases/test_web_api/test_document_app/test_paser_documents.py @@ -48,7 +48,7 @@ def validate_document_parse_done(auth, _kb_id, _document_ids): continue assert doc["run"] == "3" assert len(doc["process_begin_at"]) > 0 - assert doc["process_duation"] > 0 + assert doc["process_duration"] > 0 assert doc["progress"] > 0 assert "Task done" in doc["progress_msg"] diff --git a/web/src/interfaces/database/document.ts b/web/src/interfaces/database/document.ts index 59bc5d0213e..5ccbc44a289 100644 --- a/web/src/interfaces/database/document.ts +++ b/web/src/interfaces/database/document.ts @@ -12,7 +12,7 @@ export interface IDocumentInfo { parser_config: IParserConfig; parser_id: string; process_begin_at?: string; - process_duation: number; + process_duration: number; progress: number; progress_msg: string; run: RunningStatus; diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index 4b357ee88ad..011f8a9eb71 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -69,7 +69,7 @@ export interface IKnowledgeFile { name: string; parser_id: string; process_begin_at?: any; - process_duation: number; + process_duration: number; progress: number; // parsing process progress_msg: string; // parsing log run: RunningStatus; // parsing status diff --git a/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx b/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx index bd77431ea8b..bb052a6698b 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx @@ -61,9 +61,9 @@ const PopoverContent = ({ record }: IProps) => { children: record.process_begin_at, }, { - key: 'process_duation', + key: 'process_duration', label: t('processDuration'), - children: `${record.process_duation.toFixed(2)} s`, + children: `${record.process_duration.toFixed(2)} s`, }, { key: 'progress_msg', diff --git a/web/src/pages/dataset/dataset/parsing-card.tsx b/web/src/pages/dataset/dataset/parsing-card.tsx index 31c750f0b70..b5511dae8ab 100644 --- a/web/src/pages/dataset/dataset/parsing-card.tsx +++ b/web/src/pages/dataset/dataset/parsing-card.tsx @@ -53,9 +53,9 @@ export const PopoverContent = ({ record }: IProps) => { children: record.process_begin_at, }, { - key: 'knowledgeDetails.process_duation', + key: 'knowledgeDetails.process_duration', label: t('processDuration'), - children: `${record.process_duation.toFixed(2)} s`, + children: `${record.process_duration.toFixed(2)} s`, }, { key: 'progress_msg', From 2259bb258672e67cf2f20691454d076e5ecc143f Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Mon, 7 Jul 2025 14:13:13 +0800 Subject: [PATCH 0115/1187] fix:Use use-chunk-request.ts to replace chunk-hooks.ts; implement chunk selectAll, enable, disable and other functions (#8695) ### What problem does this PR solve? Use use-chunk-request.ts to replace chunk-hooks.ts; implement chunk selectAll, enable, disable and other functions [#3221](https://github.com/infiniflow/ragflow/issues/3221) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/hooks/use-chunk-request.ts | 28 +++++++++- .../chunk-result-bar/checkbox-sets.tsx | 39 ++++++++++++-- .../components/knowledge-chunk/index.less | 3 +- .../components/knowledge-chunk/index.tsx | 53 +++++++++++++------ 4 files changed, 100 insertions(+), 23 deletions(-) diff --git a/web/src/hooks/use-chunk-request.ts b/web/src/hooks/use-chunk-request.ts index 1cb80242386..320f57979bf 100644 --- a/web/src/hooks/use-chunk-request.ts +++ b/web/src/hooks/use-chunk-request.ts @@ -1,9 +1,11 @@ +import message from '@/components/ui/message'; import { ResponseGetType } from '@/interfaces/database/base'; import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge'; import kbService from '@/services/knowledge-service'; -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { useDebounce } from 'ahooks'; import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { IChunkListResult } from './chunk-hooks'; import { useGetPaginationWithRouter, @@ -89,3 +91,27 @@ export const useFetchNextChunkList = (): ResponseGetType<{ handleSetAvailable, }; }; + +export const useSwitchChunk = () => { + const { t } = useTranslation(); + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: ['switchChunk'], + mutationFn: async (params: { + chunk_ids?: string[]; + available_int?: number; + doc_id: string; + }) => { + const { data } = await kbService.switch_chunk(params); + if (data.code === 0) { + message.success(t('message.modified')); + } + return data?.code; + }, + }); + + return { data, loading, switchChunk: mutateAsync }; +}; diff --git a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/chunk-result-bar/checkbox-sets.tsx b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/chunk-result-bar/checkbox-sets.tsx index 8c03f177612..131d99619aa 100644 --- a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/chunk-result-bar/checkbox-sets.tsx +++ b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/chunk-result-bar/checkbox-sets.tsx @@ -3,7 +3,14 @@ import { Label } from '@/components/ui/label'; import { Ban, CircleCheck, Trash2 } from 'lucide-react'; import { useCallback } from 'react'; -export default ({ selectAllChunk, checked }) => { +type ICheckboxSetProps = { + selectAllChunk: (e: any) => void; + removeChunk: (e?: any) => void; + switchChunk: (available: number) => void; + checked: boolean; +}; +export default (props: ICheckboxSetProps) => { + const { selectAllChunk, removeChunk, switchChunk, checked } = props; const handleSelectAllCheck = useCallback( (e: any) => { console.log('eee=', e); @@ -12,25 +19,47 @@ export default ({ selectAllChunk, checked }) => { [selectAllChunk], ); + const handleDeleteClick = useCallback(() => { + removeChunk(); + }, [removeChunk]); + + const handleEnabledClick = useCallback(() => { + switchChunk(1); + }, [switchChunk]); + + const handleDisabledClick = useCallback(() => { + switchChunk(0); + }, [switchChunk]); + return ( -
    +
    -
    +
    Enable
    -
    +
    Disable
    -
    +
    Delete
    diff --git a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.less b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.less index 10e3b358bd8..6d1370305ad 100644 --- a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.less +++ b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.less @@ -41,7 +41,7 @@ .chunkContainer { display: flex; // height: calc(100vh - 332px); - height: calc(100vh - 270px); + height: calc(100vh - 300px); } .chunkOtherContainer { @@ -50,6 +50,7 @@ .pageFooter { padding-top: 10px; + padding-right: 10px; height: 32px; } } diff --git a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx index f1b05a3a8f1..692d430a184 100644 --- a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx +++ b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx @@ -1,6 +1,9 @@ -import { useFetchNextChunkList, useSwitchChunk } from '@/hooks/chunk-hooks'; +import { + useFetchNextChunkList, + useSwitchChunk, +} from '@/hooks/use-chunk-request'; import classNames from 'classnames'; -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import ChunkCard from './components/chunk-card'; import CreatingModal from './components/chunk-creating-modal'; @@ -43,6 +46,7 @@ const Chunk = () => { const { t } = useTranslation(); const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); const { switchChunk } = useSwitchChunk(); + const [chunkList, setChunkList] = useState(data); const { chunkUpdatingLoading, onChunkUpdatingOk, @@ -53,6 +57,9 @@ const Chunk = () => { documentId, } = useUpdateChunk(); + useEffect(() => { + setChunkList(data); + }, [data]); const onPaginationChange: RAGFlowPaginationType['onChange'] = ( page, size, @@ -115,10 +122,22 @@ const Chunk = () => { available_int: available, doc_id: documentId, }); - if (!chunkIds && resCode === 0) { + if (ids?.length && resCode === 0) { + chunkList.forEach((x: any) => { + if (ids.indexOf(x['chunk_id']) > -1) { + x['available_int'] = available; + } + }); + setChunkList(chunkList); } }, - [switchChunk, documentId, selectedChunkIds, showSelectedChunkWarning], + [ + switchChunk, + documentId, + selectedChunkIds, + showSelectedChunkWarning, + chunkList, + ], ); const { highlights, setWidthAndHeight } = @@ -156,7 +175,7 @@ const Chunk = () => {
    -
    +
    {
    @@ -182,7 +203,7 @@ const Chunk = () => { 'flex flex-col gap-4', )} > - {data.map((item) => ( + {chunkList.map((item) => ( { ))}
    +
    + { + onPaginationChange(page, pageSize); + }} + > +
    -
    - { - onPaginationChange(page, pageSize); - }} - > -
    From e60ec0a31bab0d81155b316938b74b18b179bbd8 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Mon, 7 Jul 2025 14:13:37 +0800 Subject: [PATCH 0116/1187] Fix:disallowed special token while embedding (#8692) ### What problem does this PR solve? https://github.com/infiniflow/ragflow/issues/8567 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/embedding_model.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 3590318f9a5..1b0b927b041 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -273,6 +273,8 @@ def encode_queries(self, text): class OllamaEmbed(Base): _FACTORY_NAME = "Ollama" + _special_tokens = ["<|endoftext|>"] + def __init__(self, key, model_name, **kwargs): self.client = Client(host=kwargs["base_url"]) if not key or key == "x" else Client(host=kwargs["base_url"], headers={"Authorization": f"Bear {key}"}) self.model_name = model_name @@ -281,6 +283,9 @@ def encode(self, texts: list): arr = [] tks_num = 0 for txt in texts: + # remove special tokens if they exist + for token in OllamaEmbed._special_tokens: + txt = txt.replace(token, "") res = self.client.embeddings(prompt=txt, model=self.model_name, options={"use_mmap": True}) try: arr.append(res["embedding"]) @@ -290,6 +295,9 @@ def encode(self, texts: list): return np.array(arr), tks_num def encode_queries(self, text): + # remove special tokens if they exist + for token in OllamaEmbed._special_tokens: + text = text.replace(token, "") res = self.client.embeddings(prompt=text, model=self.model_name, options={"use_mmap": True}) try: return np.array(res["embedding"]), 128 From 441fb92aa710fff614bb2d70fc3f93b830e5c70d Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Mon, 7 Jul 2025 14:50:23 +0800 Subject: [PATCH 0117/1187] Fix: suppress docker-compose warning (#8698) ### What problem does this PR solve? Suppress docker-compose warning like: ```bash The "HF_ENDPOINT" variable is not set. Defaulting to a blank string. The "MACOS" variable is not set. Defaulting to a blank string. The "SANDBOX_EXECUTOR_MANAGER_IMAGE variable is not set. Defaulting to a blank string. The "SANDBOX_EXECUTOR_MANAGER_PORT variable is not set. Defaulting to a blank string. ``` ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] Refactoring --- docker/docker-compose-base.yml | 4 ++-- docker/docker-compose.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index a69bcbdab50..7e339350603 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -107,10 +107,10 @@ services: container_name: ragflow-sandbox-executor-manager profiles: - sandbox - image: ${SANDBOX_EXECUTOR_MANAGER_IMAGE} + image: ${SANDBOX_EXECUTOR_MANAGER_IMAGE-infiniflow/sandbox-executor-manager:latest} privileged: true ports: - - ${SANDBOX_EXECUTOR_MANAGER_PORT}:9385 + - ${SANDBOX_EXECUTOR_MANAGER_PORT-9385}:9385 env_file: .env volumes: - /var/run/docker.sock:/var/run/docker.sock diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index bbf2111ed16..2496ac9b565 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -38,8 +38,8 @@ services: env_file: .env environment: - TZ=${TIMEZONE} - - HF_ENDPOINT=${HF_ENDPOINT} - - MACOS=${MACOS} + - HF_ENDPOINT=${HF_ENDPOINT-} + - MACOS=${MACOS-} networks: - ragflow restart: on-failure From 5b52b7561a18c3e143aa8dc46b1f961b0819629e Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 7 Jul 2025 17:28:46 +0800 Subject: [PATCH 0118/1187] Fix: Fix text errors #3221 (#8708) ### What problem does this PR solve? Fix: Fix text errors #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/pages/agent/form/begin-form/begin-dynamic-options.tsx | 2 +- web/src/pages/agent/form/retrieval-form/next.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/pages/agent/form/begin-form/begin-dynamic-options.tsx b/web/src/pages/agent/form/begin-form/begin-dynamic-options.tsx index d71da8d2bbf..12b2bfb4b4d 100644 --- a/web/src/pages/agent/form/begin-form/begin-dynamic-options.tsx +++ b/web/src/pages/agent/form/begin-form/begin-dynamic-options.tsx @@ -50,7 +50,7 @@ export function BeginDynamicOptions() { ); })} append({ value: '' })} type="button"> - {t('flow.addVariable')} + {t('flow.addField')}
    ); diff --git a/web/src/pages/agent/form/retrieval-form/next.tsx b/web/src/pages/agent/form/retrieval-form/next.tsx index 68af46000a2..fdef95d7fe5 100644 --- a/web/src/pages/agent/form/retrieval-form/next.tsx +++ b/web/src/pages/agent/form/retrieval-form/next.tsx @@ -49,7 +49,9 @@ export function EmptyResponseField() { name="empty_response" render={({ field }) => ( - {t('chat.emptyResponse')} + + {t('chat.emptyResponse')} + + + + )} + /> + + )} { const llmId = useFetchModelId(); @@ -114,8 +120,17 @@ export const useInitializeOperatorParams = () => { }, [llmId]); const initializeOperatorParams = useCallback( - (operatorName: Operator) => { - return initialFormValuesMap[operatorName]; + (operatorName: Operator, position: Position) => { + const initialValues = initialFormValuesMap[operatorName]; + if (isBottomSubAgent(operatorName, position)) { + return { + ...initialValues, + description: 'This is an agent for a specific task.', + user_prompt: 'This is the order you need to send to the agent.', + }; + } + + return initialValues; }, [initialFormValuesMap], ); @@ -235,13 +250,6 @@ function useAddToolNode() { return { addToolNode }; } -function isBottomSubAgent(type: string, position: Position) { - return ( - (type === Operator.Agent && position === Position.Bottom) || - type === Operator.Tool - ); -} - function useResizeIterationNode() { const { getNode, nodes, updateNode } = useGraphStore((state) => state); @@ -324,7 +332,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { getNodeName(type), nodes, ), - form: initializeOperatorParams(type as Operator), + form: initializeOperatorParams(type as Operator, params.position), }, sourcePosition: Position.Right, targetPosition: Position.Left, diff --git a/web/src/pages/profile-setting/mcp/mcp-card.tsx b/web/src/pages/profile-setting/mcp/mcp-card.tsx index 1c154d86530..bf6023c9773 100644 --- a/web/src/pages/profile-setting/mcp/mcp-card.tsx +++ b/web/src/pages/profile-setting/mcp/mcp-card.tsx @@ -35,7 +35,7 @@ export function McpCard({

    {data.name}

    - + = useCallback((e) => { e.stopPropagation(); }, []); - const handleDelete: MouseEventHandler = - useCallback(() => {}, []); + const handleDelete: MouseEventHandler = useCallback(() => { + deleteMcpServer([mcpId]); + }, [deleteMcpServer, mcpId]); return ( {children} - {t('common.rename')} + {t('common.edit')} diff --git a/web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx b/web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx index 79820079d88..e5c1c147380 100644 --- a/web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx +++ b/web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx @@ -1,3 +1,4 @@ +import { useDeleteMcpServer } from '@/hooks/use-mcp-request'; import { Trash2, Upload } from 'lucide-react'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -5,10 +6,13 @@ import { useTranslation } from 'react-i18next'; export function useBulkOperateMCP() { const { t } = useTranslation(); const [selectedList, setSelectedList] = useState>([]); + const { deleteMcpServer } = useDeleteMcpServer(); const handleEnableClick = useCallback(() => {}, []); - const handleDelete = useCallback(() => {}, []); + const handleDelete = useCallback(() => { + deleteMcpServer(selectedList); + }, [deleteMcpServer, selectedList]); const handleSelectChange = useCallback((id: string, checked: boolean) => { setSelectedList((list) => { From 8281ceb40682dff28f011e8ece4a18849cb3dc32 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Thu, 10 Jul 2025 14:28:57 +0800 Subject: [PATCH 0142/1187] Refa: refine retry gap. (#8773) ### What problem does this PR solve? ### Type of change - [x] Refactoring - [x] Performance Improvement --- rag/llm/chat_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index f254ed18681..9217b4122cd 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -73,7 +73,7 @@ def __init__(self, key, model_name, base_url, **kwargs): def _get_delay(self): """Calculate retry delay time""" - return self.base_delay + random.uniform(0, 0.5) + return self.base_delay + random.uniform(10, 150) def _classify_error(self, error): """Classify error based on error message content""" From 512772c45a4502a5ae3da9e26ae0c8ab7d56e42c Mon Sep 17 00:00:00 2001 From: haol Date: Thu, 10 Jul 2025 14:32:28 +0800 Subject: [PATCH 0143/1187] Fix: Resolve typo in /list route function (#8769) ### What problem does this PR solve? Fixes a function name typo for the `/list` route in `api/apps/conversation_app.py`. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/conversation_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/apps/conversation_app.py b/api/apps/conversation_app.py index 5ba39716f8b..16d67bc57a3 100644 --- a/api/apps/conversation_app.py +++ b/api/apps/conversation_app.py @@ -161,7 +161,7 @@ def rm(): @manager.route("/list", methods=["GET"]) # noqa: F821 @login_required -def list_convsersation(): +def list_conversation(): dialog_id = request.args["dialog_id"] try: if not DialogService.query(tenant_id=current_user.id, id=dialog_id): From 98829f5dbe317747b356958baeb85464c5b767ab Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 10 Jul 2025 18:36:34 +0800 Subject: [PATCH 0144/1187] Feat: Modify the agent tool name #3221 (#8780) ### What problem does this PR solve? Feat: Modify the agent tool name #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/interfaces/database/agent.ts | 1 + web/src/pages/agent/form-sheet/next.tsx | 2 +- .../tool-popover/use-update-tools.ts | 1 + web/src/pages/agent/hooks.tsx | 171 +----------------- .../pages/agent/hooks/use-change-node-name.ts | 120 ++++++++++++ 5 files changed, 125 insertions(+), 170 deletions(-) create mode 100644 web/src/pages/agent/hooks/use-change-node-name.ts diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index ca954c050f2..088564bc13c 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -155,6 +155,7 @@ export interface IAgentForm { exception_comment: any; exception_goto: any; tools: Array<{ + name: string; component_name: string; params: Record; }>; diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index 39794bfca97..d707e219b21 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -14,7 +14,7 @@ import { Play, X } from 'lucide-react'; import { BeginId, Operator } from '../constant'; import { AgentFormContext } from '../context'; import { RunTooltip } from '../flow-tooltip'; -import { useHandleNodeNameChange } from '../hooks'; +import { useHandleNodeNameChange } from '../hooks/use-change-node-name'; import OperatorIcon from '../operator-icon'; import { needsSingleStepDebugging } from '../utils'; import SingleDebugDrawer from './single-debug-drawer'; diff --git a/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts b/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts index 8db1df8abc7..52d893cc224 100644 --- a/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts +++ b/web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts @@ -29,6 +29,7 @@ export function useUpdateAgentNodeTools() { ? tool : { component_name: cur, + name: cur, params: DefaultAgentToolValuesMap[ cur as keyof typeof DefaultAgentToolValuesMap diff --git a/web/src/pages/agent/hooks.tsx b/web/src/pages/agent/hooks.tsx index 507d73e76f6..c1b44b605fc 100644 --- a/web/src/pages/agent/hooks.tsx +++ b/web/src/pages/agent/hooks.tsx @@ -5,34 +5,19 @@ import { Position, ReactFlowInstance, } from '@xyflow/react'; -import React, { - ChangeEvent, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; // import { shallow } from 'zustand/shallow'; import { settledModelVariableMap } from '@/constants/knowledge'; import { useFetchModelId } from '@/hooks/logic-hooks'; -import { ISwitchForm } from '@/interfaces/database/agent'; -import { - ICategorizeForm, - IRelevantForm, - RAGFlowNodeType, -} from '@/interfaces/database/flow'; -import { message } from 'antd'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { humanId } from 'human-id'; import { get, lowerFirst, omit } from 'lodash'; -import trim from 'lodash/trim'; import { UseFormReturn } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { v4 as uuid } from 'uuid'; import { NodeMap, Operator, RestrictedUpstreamMap, - SwitchElseTo, initialAgentValues, initialAkShareValues, initialArXivValues, @@ -76,7 +61,6 @@ import useGraphStore, { RFState } from './store'; import { buildCategorizeObjectFromList, generateNodeNamesWithIncreasingIndex, - generateSwitchHandleText, getNodeDragHandle, getRelativePositionToIterationNode, replaceIdWithText, @@ -389,43 +373,6 @@ export const useValidateConnection = () => { return isValidConnection; }; -export const useHandleNodeNameChange = ({ - id, - data, -}: { - id?: string; - data: any; -}) => { - const [name, setName] = useState(''); - const { updateNodeName, nodes } = useGraphStore((state) => state); - const previousName = data?.name; - - const handleNameBlur = useCallback(() => { - const existsSameName = nodes.some((x) => x.data.name === name); - if (trim(name) === '' || existsSameName) { - if (existsSameName && previousName !== name) { - message.error('The name cannot be repeated'); - } - setName(previousName); - return; - } - - if (id) { - updateNodeName(id, name); - } - }, [name, id, updateNodeName, previousName, nodes]); - - const handleNameChange = useCallback((e: ChangeEvent) => { - setName(e.target.value); - }, []); - - useEffect(() => { - setName(previousName); - }, [previousName]); - - return { name, handleNameBlur, handleNameChange }; -}; - export const useReplaceIdWithName = () => { const getNode = useGraphStore((state) => state.getNode); @@ -448,120 +395,6 @@ export const useReplaceIdWithText = (output: unknown) => { }; }; -/** - * monitor changes in the data.form field of the categorize and relevant operators - * and then synchronize them to the edge - */ -export const useWatchNodeFormDataChange = () => { - const { getNode, nodes, setEdgesByNodeId } = useGraphStore((state) => state); - - const buildCategorizeEdgesByFormData = useCallback( - (nodeId: string, form: ICategorizeForm) => { - // add - // delete - // edit - const categoryDescription = form.category_description; - const downstreamEdges = Object.keys(categoryDescription).reduce( - (pre, sourceHandle) => { - const target = categoryDescription[sourceHandle]?.to; - if (target) { - pre.push({ - id: uuid(), - source: nodeId, - target, - sourceHandle, - }); - } - - return pre; - }, - [], - ); - - setEdgesByNodeId(nodeId, downstreamEdges); - }, - [setEdgesByNodeId], - ); - - const buildRelevantEdgesByFormData = useCallback( - (nodeId: string, form: IRelevantForm) => { - const downstreamEdges = ['yes', 'no'].reduce((pre, cur) => { - const target = form[cur as keyof IRelevantForm] as string; - if (target) { - pre.push({ id: uuid(), source: nodeId, target, sourceHandle: cur }); - } - - return pre; - }, []); - - setEdgesByNodeId(nodeId, downstreamEdges); - }, - [setEdgesByNodeId], - ); - - const buildSwitchEdgesByFormData = useCallback( - (nodeId: string, form: ISwitchForm) => { - // add - // delete - // edit - const conditions = form.conditions; - const downstreamEdges = conditions.reduce((pre, _, idx) => { - const target = conditions[idx]?.to; - if (target) { - pre.push({ - id: uuid(), - source: nodeId, - target, - sourceHandle: generateSwitchHandleText(idx), - }); - } - - return pre; - }, []); - - // Splice the else condition of the conditional judgment to the edge list - const elseTo = form[SwitchElseTo]; - if (elseTo) { - downstreamEdges.push({ - id: uuid(), - source: nodeId, - target: elseTo, - sourceHandle: SwitchElseTo, - }); - } - - setEdgesByNodeId(nodeId, downstreamEdges); - }, - [setEdgesByNodeId], - ); - - useEffect(() => { - nodes.forEach((node) => { - const currentNode = getNode(node.id); - const form = currentNode?.data.form ?? {}; - const operatorType = currentNode?.data.label; - switch (operatorType) { - case Operator.Relevant: - buildRelevantEdgesByFormData(node.id, form as IRelevantForm); - break; - case Operator.Categorize: - buildCategorizeEdgesByFormData(node.id, form as ICategorizeForm); - break; - // case Operator.Switch: - // buildSwitchEdgesByFormData(node.id, form as ISwitchForm); - // break; - default: - break; - } - }); - }, [ - nodes, - buildCategorizeEdgesByFormData, - getNode, - buildRelevantEdgesByFormData, - ]); -}; - export const useDuplicateNode = () => { const duplicateNodeById = useGraphStore((store) => store.duplicateNode); const getNodeName = useGetNodeName(); diff --git a/web/src/pages/agent/hooks/use-change-node-name.ts b/web/src/pages/agent/hooks/use-change-node-name.ts new file mode 100644 index 00000000000..61a5653d732 --- /dev/null +++ b/web/src/pages/agent/hooks/use-change-node-name.ts @@ -0,0 +1,120 @@ +import message from '@/components/ui/message'; +import { trim } from 'lodash'; +import { + ChangeEvent, + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import { Operator } from '../constant'; +import useGraphStore from '../store'; +import { getAgentNodeTools } from '../utils'; + +export function useHandleTooNodeNameChange({ + id, + name, + setName, +}: { + id?: string; + name?: string; + setName: Dispatch>; +}) { + const { clickedToolId, findUpstreamNodeById, updateNodeForm } = useGraphStore( + (state) => state, + ); + const agentNode = findUpstreamNodeById(id); + const tools = getAgentNodeTools(agentNode); + + const previousName = useMemo(() => { + const tool = tools.find((x) => x.component_name === clickedToolId); + return tool?.name || tool?.component_name; + }, [clickedToolId, tools]); + + const handleToolNameBlur = useCallback(() => { + const trimmedName = trim(name); + const existsSameName = tools.some((x) => x.name === trimmedName); + if (trimmedName === '' || existsSameName) { + if (existsSameName && previousName !== name) { + message.error('The name cannot be repeated'); + } + setName(previousName || ''); + return; + } + + if (agentNode?.id) { + const nextTools = tools.map((x) => { + if (x.component_name === clickedToolId) { + return { + ...x, + name, + }; + } + return x; + }); + updateNodeForm(agentNode?.id, nextTools, ['tools']); + } + }, [ + agentNode?.id, + clickedToolId, + name, + previousName, + setName, + tools, + updateNodeForm, + ]); + + return { handleToolNameBlur, previousToolName: previousName }; +} + +export const useHandleNodeNameChange = ({ + id, + data, +}: { + id?: string; + data: any; +}) => { + const [name, setName] = useState(''); + const { updateNodeName, nodes, getOperatorTypeFromId } = useGraphStore( + (state) => state, + ); + const previousName = data?.name; + const isToolNode = getOperatorTypeFromId(id) === Operator.Tool; + + const { handleToolNameBlur, previousToolName } = useHandleTooNodeNameChange({ + id, + name, + setName, + }); + + const handleNameBlur = useCallback(() => { + const existsSameName = nodes.some((x) => x.data.name === name); + if (trim(name) === '' || existsSameName) { + if (existsSameName && previousName !== name) { + message.error('The name cannot be repeated'); + } + setName(previousName); + return; + } + + if (id) { + updateNodeName(id, name); + } + }, [name, id, updateNodeName, previousName, nodes]); + + const handleNameChange = useCallback((e: ChangeEvent) => { + setName(e.target.value); + }, []); + + useEffect(() => { + setName(isToolNode ? previousToolName : previousName); + }, [isToolNode, previousName, previousToolName]); + + return { + name, + handleNameBlur: isToolNode ? handleToolNameBlur : handleNameBlur, + handleNameChange, + }; +}; From fc0c81acc6f03770c06f58ff33dae36e9ef92d08 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 11 Jul 2025 10:34:57 +0800 Subject: [PATCH 0145/1187] Feat: Edit MCP server #3221 (#8784) ### What problem does this PR solve? Feat: Edit MCP server #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/collapse.tsx | 20 +++++- web/src/hooks/use-mcp-request.ts | 18 +++--- .../profile-setting/mcp/edit-mcp-dialog.tsx | 63 ++++++++++++++++--- .../profile-setting/mcp/edit-mcp-form.tsx | 22 ++----- web/src/pages/profile-setting/mcp/index.tsx | 5 +- .../pages/profile-setting/mcp/mcp-card.tsx | 7 ++- .../profile-setting/mcp/mcp-dropdown.tsx | 14 ++--- .../pages/profile-setting/mcp/use-edit-mcp.ts | 16 ++--- web/src/services/mcp-server-service.ts | 2 +- 9 files changed, 109 insertions(+), 58 deletions(-) diff --git a/web/src/components/collapse.tsx b/web/src/components/collapse.tsx index 35e85e257c0..81d7f8a7656 100644 --- a/web/src/components/collapse.tsx +++ b/web/src/components/collapse.tsx @@ -3,17 +3,31 @@ import { CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; +import { CollapsibleProps } from '@radix-ui/react-collapsible'; import { ListCollapse } from 'lucide-react'; import { PropsWithChildren, ReactNode } from 'react'; -type CollapseProps = { +type CollapseProps = Omit & { title?: ReactNode; rightContent?: ReactNode; } & PropsWithChildren; -export function Collapse({ title, children, rightContent }: CollapseProps) { +export function Collapse({ + title, + children, + rightContent, + open, + defaultOpen = true, + onOpenChange, + disabled, +}: CollapseProps) { return ( - +
    diff --git a/web/src/hooks/use-mcp-request.ts b/web/src/hooks/use-mcp-request.ts index 4bf9057289c..bf4dd3a901f 100644 --- a/web/src/hooks/use-mcp-request.ts +++ b/web/src/hooks/use-mcp-request.ts @@ -1,10 +1,13 @@ import message from '@/components/ui/message'; -import { IMcpServerListResponse, IMCPTool } from '@/interfaces/database/mcp'; +import { + IMcpServer, + IMcpServerListResponse, + IMCPTool, +} from '@/interfaces/database/mcp'; import { ITestMcpRequestBody } from '@/interfaces/request/mcp'; import i18n from '@/locales/config'; import mcpServerService from '@/services/mcp-server-service'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { useState } from 'react'; export const enum McpApiAction { ListMcpServer = 'listMcpServer', @@ -34,20 +37,19 @@ export const useListMcpServer = () => { return { data, loading }; }; -export const useGetMcpServer = () => { - const [id, setId] = useState(''); - const { data, isFetching: loading } = useQuery({ +export const useGetMcpServer = (id: string) => { + const { data, isFetching: loading } = useQuery({ queryKey: [McpApiAction.GetMcpServer, id], - initialData: {}, + initialData: {} as IMcpServer, gcTime: 0, enabled: !!id, queryFn: async () => { - const { data } = await mcpServerService.get(); + const { data } = await mcpServerService.get({ mcp_id: id }); return data?.data ?? {}; }, }); - return { data, loading, setId, id }; + return { data, loading, id }; }; export const useCreateMcpServer = () => { diff --git a/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx b/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx index 69687a81f5c..6f81e310ad3 100644 --- a/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx +++ b/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx @@ -7,15 +7,22 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; -import { useTestMcpServer } from '@/hooks/use-mcp-request'; +import { useGetMcpServer, useTestMcpServer } from '@/hooks/use-mcp-request'; import { IModalProps } from '@/interfaces/common'; import { IMCPTool, IMCPToolObject } from '@/interfaces/database/mcp'; -import { omit } from 'lodash'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { isEmpty, omit, pick } from 'lodash'; import { RefreshCw } from 'lucide-react'; -import { MouseEventHandler, useCallback, useState } from 'react'; +import { MouseEventHandler, useCallback, useMemo, useState } from 'react'; +import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { EditMcpForm, FormId, useBuildFormSchema } from './edit-mcp-form'; +import { + EditMcpForm, + FormId, + ServerType, + useBuildFormSchema, +} from './edit-mcp-form'; import { McpToolCard } from './tool-card'; function transferToolToObject(tools: IMCPTool[] = []) { @@ -25,11 +32,37 @@ function transferToolToObject(tools: IMCPTool[] = []) { }, {}); } -export function EditMcpDialog({ hideModal, loading, onOk }: IModalProps) { +function transferToolToArray(tools: IMCPToolObject) { + return Object.entries(tools).reduce((pre, [name, tool]) => { + pre.push({ ...tool, name }); + return pre; + }, []); +} + +export function EditMcpDialog({ + hideModal, + loading, + onOk, + id, +}: IModalProps & { id: string }) { const { t } = useTranslation(); - const { testMcpServer, data: tools } = useTestMcpServer(); + const { + testMcpServer, + data: tools, + loading: testLoading, + } = useTestMcpServer(); const [isTriggeredBySaving, setIsTriggeredBySaving] = useState(false); const FormSchema = useBuildFormSchema(); + const [collapseOpen, setCollapseOpen] = useState(true); + const { data } = useGetMcpServer(id); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + values: isEmpty(data) + ? { name: '', server_type: ServerType.SSE, url: '' } + : pick(data, ['name', 'server_type', 'url']), + }); + console.log('🚀 ~ form:', form.formState.dirtyFields); const handleTest: MouseEventHandler = useCallback((e) => { e.stopPropagation(); @@ -54,15 +87,25 @@ export function EditMcpDialog({ hideModal, loading, onOk }: IModalProps) { } }; + const nextTools = useMemo(() => { + return tools || transferToolToArray(data.variables?.tools || {}); + }, [data.variables?.tools, tools]); + + const dirtyFields = form.formState.dirtyFields; + const fieldChanged = 'server_type' in dirtyFields || 'url' in dirtyFields; + const disabled = !!!tools?.length || testLoading || fieldChanged; + return ( Edit profile - + {tools?.length || 0} tools available
    } + open={collapseOpen} + onOpenChange={setCollapseOpen} rightContent={ } > -
    - {tools?.map((x) => ( +
    + {nextTools?.map((x) => ( ))}
    @@ -86,7 +129,7 @@ export function EditMcpDialog({ hideModal, loading, onOk }: IModalProps) { form={FormId} loading={loading} onClick={handleSave} - disabled={!!!tools?.length} + disabled={disabled} > {t('common.save')} diff --git a/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx b/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx index e71cdf631c2..0e063743c7a 100644 --- a/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx +++ b/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx @@ -1,7 +1,6 @@ 'use client'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { useForm } from 'react-hook-form'; +import { UseFormReturn } from 'react-hook-form'; import { z } from 'zod'; import { @@ -16,12 +15,11 @@ import { Input } from '@/components/ui/input'; import { RAGFlowSelect } from '@/components/ui/select'; import { IModalProps } from '@/interfaces/common'; import { buildOptions } from '@/utils/form'; -import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; export const FormId = 'EditMcpForm'; -enum ServerType { +export enum ServerType { SSE = 'sse', StreamableHttp = 'streamable-http', } @@ -57,28 +55,16 @@ export function useBuildFormSchema() { } export function EditMcpForm({ - initialName, + form, onOk, -}: IModalProps & { initialName?: string }) { +}: IModalProps & { form: UseFormReturn }) { const { t } = useTranslation(); - const FormSchema = useBuildFormSchema(); - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { name: '', server_type: ServerType.SSE, url: '' }, - }); - function onSubmit(data: z.infer) { onOk?.(data); } - useEffect(() => { - if (initialName) { - form.setValue('name', initialName); - } - }, [form, initialName]); - return (
    ))}
    @@ -49,6 +51,7 @@ export default function McpServer() { )}
    diff --git a/web/src/pages/profile-setting/mcp/mcp-card.tsx b/web/src/pages/profile-setting/mcp/mcp-card.tsx index bf6023c9773..b83c20c5e2b 100644 --- a/web/src/pages/profile-setting/mcp/mcp-card.tsx +++ b/web/src/pages/profile-setting/mcp/mcp-card.tsx @@ -7,15 +7,18 @@ import { isPlainObject } from 'lodash'; import { useMemo } from 'react'; import { McpDropdown } from './mcp-dropdown'; import { UseBulkOperateMCPReturnType } from './use-bulk-operate-mcp'; +import { UseEditMcpReturnType } from './use-edit-mcp'; export type DatasetCardProps = { data: IMcpServer; -} & Pick; +} & Pick & + Pick; export function McpCard({ data, selectedList, handleSelectChange, + showEditModal, }: DatasetCardProps) { const toolLength = useMemo(() => { const tools = data.variables?.tools; @@ -35,7 +38,7 @@ export function McpCard({

    {data.name}

    - + ) { const { t } = useTranslation(); const { deleteMcpServer } = useDeleteMcpServer(); - const handleShowAgentRenameModal: MouseEventHandler = - useCallback((e) => { - e.stopPropagation(); - }, []); - const handleDelete: MouseEventHandler = useCallback(() => { deleteMcpServer([mcpId]); }, [deleteMcpServer, mcpId]); @@ -31,7 +31,7 @@ export function McpDropdown({ {children} - + {t('common.edit')} diff --git a/web/src/pages/profile-setting/mcp/use-edit-mcp.ts b/web/src/pages/profile-setting/mcp/use-edit-mcp.ts index 90392955501..94895d935c7 100644 --- a/web/src/pages/profile-setting/mcp/use-edit-mcp.ts +++ b/web/src/pages/profile-setting/mcp/use-edit-mcp.ts @@ -1,10 +1,9 @@ import { useSetModalState } from '@/hooks/common-hooks'; import { useCreateMcpServer, - useGetMcpServer, useUpdateMcpServer, } from '@/hooks/use-mcp-request'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; export const useEditMcp = () => { const { @@ -13,14 +12,13 @@ export const useEditMcp = () => { showModal: showEditModal, } = useSetModalState(); const { createMcpServer, loading } = useCreateMcpServer(); - const { data, setId, id } = useGetMcpServer(); + const [id, setId] = useState(''); + const { updateMcpServer } = useUpdateMcpServer(); const handleShowModal = useCallback( - (id?: string) => () => { - if (id) { - setId(id); - } + (id: string) => () => { + setId(id); showEditModal(); }, [setId, showEditModal], @@ -47,7 +45,9 @@ export const useEditMcp = () => { showEditModal: handleShowModal, loading, createMcpServer, - detail: data, handleOk, + id, }; }; + +export type UseEditMcpReturnType = ReturnType; diff --git a/web/src/services/mcp-server-service.ts b/web/src/services/mcp-server-service.ts index df423281346..7cb22a090ae 100644 --- a/web/src/services/mcp-server-service.ts +++ b/web/src/services/mcp-server-service.ts @@ -23,7 +23,7 @@ const methods = { }, get: { url: getMcpServer, - method: 'post', + method: 'get', }, create: { url: createMcpServer, From 1895667573cf93d5d707e41f2bd8e26fe2dc8d35 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Fri, 11 Jul 2025 10:35:23 +0800 Subject: [PATCH 0146/1187] Feat: add xAI provider (#8781) ### What problem does this PR solve? Add xAI provider (experimental feature, requires user feedback). ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- conf/llm_factories.json | 55 ++++++++++++++++++++++++++++ docs/references/supported_models.mdx | 1 + rag/llm/chat_model.py | 10 +++++ rag/llm/cv_model.py | 10 +++++ 4 files changed, 76 insertions(+) diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 339c537a6e1..461520f5928 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -141,6 +141,61 @@ } ] }, + { + "name": "xAI", + "logo": "", + "tags": "LLM", + "status": "1", + "llm": [ + { + "llm_name": "grok-4", + "tags": "LLM,CHAT,256k", + "max_tokens": 256000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "grok-3", + "tags": "LLM,CHAT,130k", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true + + }, + { + "llm_name": "grok-3-fast", + "tags": "LLM,CHAT,130k", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true + + }, + { + "llm_name": "grok-3-mini", + "tags": "LLM,CHAT,130k", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true + + }, + { + "llm_name": "grok-3-mini-mini-fast", + "tags": "LLM,CHAT,130k", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true + + }, + { + "llm_name": "grok-2-vision", + "tags": "LLM,CHAT,IMAGE2TEXT,32k", + "max_tokens": 32768, + "model_type": "image2text", + "is_tools": true + + } + ] + }, { "name": "Tongyi-Qianwen", "logo": "", diff --git a/docs/references/supported_models.mdx b/docs/references/supported_models.mdx index db8cfb0603e..897dcff2114 100644 --- a/docs/references/supported_models.mdx +++ b/docs/references/supported_models.mdx @@ -58,6 +58,7 @@ A complete list of models supported by RAGFlow, which will continue to expand. | Voyage AI | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | | Xinference | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | XunFei Spark | :heavy_check_mark: | | | | | :heavy_check_mark: | +| xAI | :heavy_check_mark: | | | :heavy_check_mark: | | | | Youdao | | :heavy_check_mark: | :heavy_check_mark: | | | | | ZHIPU-AI | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | | | | 01.AI | :heavy_check_mark: | | | | | | diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 9217b4122cd..3ffe03af2ac 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -568,6 +568,16 @@ def chat_streamly(self, system, history, gen_conf): yield total_tokens +class xAIChat(Base): + _FACTORY_NAME = "xAI" + + def __init__(self, key, model_name="grok-3", base_url=None, **kwargs): + if not base_url: + base_url = "https://api.x.ai/v1" + super().__init__(key, model_name, base_url=base_url, **kwargs) + return + + class QWenChat(Base): _FACTORY_NAME = "Tongyi-Qianwen" diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index afa39d69a99..fbccd5dffc0 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -223,6 +223,16 @@ def describe_with_prompt(self, image, prompt=None): return res.choices[0].message.content.strip(), res.usage.total_tokens +class xAICV(Base): + _FACTORY_NAME = "xAI" + + def __init__(self, key, model_name="grok-3", base_url=None, **kwargs): + if not base_url: + base_url = "https://api.x.ai/v1" + super().__init__(key, model_name, base_url=base_url, **kwargs) + return + + class QWenCV(Base): _FACTORY_NAME = "Tongyi-Qianwen" From e8aee8d7204d29dca5ef2068cf4e36711524fcdd Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Fri, 11 Jul 2025 10:38:59 +0800 Subject: [PATCH 0147/1187] Feat: change document status in bulk (#8777) ### What problem does this PR solve? Change document status in bulk. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/document_app.py | 52 ++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/api/apps/document_app.py b/api/apps/document_app.py index 10090977011..bc1e5851b21 100644 --- a/api/apps/document_app.py +++ b/api/apps/document_app.py @@ -250,7 +250,6 @@ def get_filter(): else: return get_json_result(data=False, message="Only owner of knowledgebase authorized for this operation.", code=settings.RetCode.OPERATING_ERROR) - keywords = req.get("keywords", "") suffix = req.get("suffix", []) @@ -307,31 +306,42 @@ def thumbnails(): @manager.route("/change_status", methods=["POST"]) # noqa: F821 @login_required -@validate_request("doc_id", "status") +@validate_request("doc_ids", "status") def change_status(): - req = request.json - if str(req["status"]) not in ["0", "1"]: - return get_json_result(data=False, message='"Status" must be either 0 or 1!', code=settings.RetCode.ARGUMENT_ERROR) + req = request.get_json() + doc_ids = req.get("doc_ids", []) + status = str(req.get("status", "")) - if not DocumentService.accessible(req["doc_id"], current_user.id): - return get_json_result(data=False, message="No authorization.", code=settings.RetCode.AUTHENTICATION_ERROR) + if status not in ["0", "1"]: + return get_json_result(data=False, message='"Status" must be either 0 or 1!', code=settings.RetCode.ARGUMENT_ERROR) - try: - e, doc = DocumentService.get_by_id(req["doc_id"]) - if not e: - return get_data_error_result(message="Document not found!") - e, kb = KnowledgebaseService.get_by_id(doc.kb_id) - if not e: - return get_data_error_result(message="Can't find this knowledgebase!") + result = {} + for doc_id in doc_ids: + if not DocumentService.accessible(doc_id, current_user.id): + result[doc_id] = {"error": "No authorization."} + continue - if not DocumentService.update_by_id(req["doc_id"], {"status": str(req["status"])}): - return get_data_error_result(message="Database error (Document update)!") + try: + e, doc = DocumentService.get_by_id(doc_id) + if not e: + result[doc_id] = {"error": "No authorization."} + continue + e, kb = KnowledgebaseService.get_by_id(doc.kb_id) + if not e: + result[doc_id] = {"error": "Can't find this knowledgebase!"} + continue + if not DocumentService.update_by_id(doc_id, {"status": str(status)}): + result[doc_id] = {"error": "Database error (Document update)!"} + continue + + status_int = int(status) + if not settings.docStoreConn.update({"doc_id": doc_id}, {"available_int": status_int}, search.index_name(kb.tenant_id), doc.kb_id): + result[doc_id] = {"error": "Database error (docStore update)!"} + result[doc_id] = {"status": status} + except Exception as e: + result[doc_id] = {"error": f"Internal server error: {str(e)}"} - status = int(req["status"]) - settings.docStoreConn.update({"doc_id": req["doc_id"]}, {"available_int": status}, search.index_name(kb.tenant_id), doc.kb_id) - return get_json_result(data=True) - except Exception as e: - return server_error_response(e) + return get_json_result(data=result) @manager.route("/rm", methods=["POST"]) # noqa: F821 From 07208e519bf60814b5f3a278dc381a83d78af766 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Fri, 11 Jul 2025 11:34:04 +0800 Subject: [PATCH 0148/1187] Fix: Wrong_Input_type_for_Gemin (#8783) ### What problem does this PR solve? https://github.com/infiniflow/ragflow/issues/8763#issuecomment-3055317110 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/cv_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index fbccd5dffc0..5303dfca05f 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -629,7 +629,7 @@ def describe_with_prompt(self, image, prompt=None): from PIL.Image import open b64 = self.image2base64(image) - vision_prompt = self.vision_llm_prompt(b64, prompt) if prompt else self.vision_llm_prompt(b64) + vision_prompt = prompt if prompt else vision_llm_describe_prompt() img = open(BytesIO(base64.b64decode(b64))) input = [vision_prompt, img] res = self.model.generate_content( From 52dce4329d500bc162cede811733163406821e58 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Fri, 11 Jul 2025 11:34:36 +0800 Subject: [PATCH 0149/1187] fix: fix dataset-page's bugs (#8786) ### What problem does this PR solve? fix dataset-page's bugs,Input component supports icon, added Radio component, and removed antd from chunk-result-bar page [#3221 ](https://github.com/infiniflow/ragflow/issues/3221) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../components/entity-types-form-field.tsx | 45 +++--- web/src/components/entity-types-item.tsx | 2 +- .../use-handle-filter-submit.ts | 13 +- .../max-token-number-from-field.tsx | 3 +- web/src/components/originui/input.tsx | 55 ++++++-- .../raptor-form-fields.tsx | 56 ++++---- web/src/components/ui/input.tsx | 7 +- web/src/components/ui/popover.tsx | 22 ++- web/src/components/ui/radio.tsx | 133 ++++++++++++++++++ web/src/components/ui/segmented.tsx | 15 +- web/src/hooks/chunk-hooks.ts | 4 +- web/src/hooks/document-hooks.ts | 5 +- web/src/hooks/knowledge-hooks.ts | 5 +- web/src/hooks/logic-hooks.ts | 34 +++-- web/src/hooks/logic-hooks/navigate-hooks.ts | 2 + web/src/hooks/use-document-request.ts | 5 +- .../components/chunk-result-bar/index.tsx | 111 +++++++++------ .../components/knowledge-chunk/index.tsx | 11 +- .../dataset/use-bulk-operate-dataset.tsx | 4 +- .../setting/chunk-method-learn-more.tsx | 2 +- .../dataset/setting/configuration/naive.tsx | 2 +- .../pages/dataset/setting/general-form.tsx | 3 +- web/src/pages/dataset/setting/index.tsx | 13 -- 23 files changed, 399 insertions(+), 153 deletions(-) create mode 100644 web/src/components/ui/radio.tsx diff --git a/web/src/components/entity-types-form-field.tsx b/web/src/components/entity-types-form-field.tsx index 3a5de8ce586..ddf08cb0bed 100644 --- a/web/src/components/entity-types-form-field.tsx +++ b/web/src/components/entity-types-form-field.tsx @@ -12,7 +12,13 @@ import { type EntityTypesFormFieldProps = { name?: string; }; - +const initialEntityTypes = [ + 'organization', + 'person', + 'geo', + 'event', + 'category', +]; export function EntityTypesFormField({ name = 'parser_config.entity_types', }: EntityTypesFormFieldProps) { @@ -23,24 +29,27 @@ export function EntityTypesFormField({ ( - -
    - - * {t('entityTypes')} - -
    - - - + defaultValue={initialEntityTypes} + render={({ field }) => { + return ( + +
    + + * {t('entityTypes')} + +
    + + + +
    +
    +
    +
    +
    -
    -
    -
    - -
    - - )} + + ); + }} /> ); } diff --git a/web/src/components/entity-types-item.tsx b/web/src/components/entity-types-item.tsx index d52fb0e1380..ba26e5f4ce7 100644 --- a/web/src/components/entity-types-item.tsx +++ b/web/src/components/entity-types-item.tsx @@ -25,7 +25,7 @@ const EntityTypesItem = ({ rules={[{ required: true }]} initialValue={initialEntityTypes} > - + ); }; diff --git a/web/src/components/list-filter-bar/use-handle-filter-submit.ts b/web/src/components/list-filter-bar/use-handle-filter-submit.ts index 55cc4c6fe87..189a8d6b843 100644 --- a/web/src/components/list-filter-bar/use-handle-filter-submit.ts +++ b/web/src/components/list-filter-bar/use-handle-filter-submit.ts @@ -1,12 +1,17 @@ +import { useGetPaginationWithRouter } from '@/hooks/logic-hooks'; import { useCallback, useState } from 'react'; import { FilterChange, FilterValue } from './interface'; export function useHandleFilterSubmit() { const [filterValue, setFilterValue] = useState({}); - - const handleFilterSubmit: FilterChange = useCallback((value) => { - setFilterValue(value); - }, []); + const { setPagination } = useGetPaginationWithRouter(); + const handleFilterSubmit: FilterChange = useCallback( + (value) => { + setFilterValue(value); + setPagination({ page: 1 }); + }, + [setPagination], + ); return { filterValue, setFilterValue, handleFilterSubmit }; } diff --git a/web/src/components/max-token-number-from-field.tsx b/web/src/components/max-token-number-from-field.tsx index a9646f90ff5..f1762c1a830 100644 --- a/web/src/components/max-token-number-from-field.tsx +++ b/web/src/components/max-token-number-from-field.tsx @@ -7,7 +7,7 @@ interface IProps { max?: number; } -export function MaxTokenNumberFormField({ max = 2048 }: IProps) { +export function MaxTokenNumberFormField({ max = 2048, initialValue }: IProps) { const { t } = useTranslate('knowledgeConfiguration'); return ( @@ -15,6 +15,7 @@ export function MaxTokenNumberFormField({ max = 2048 }: IProps) { name={'parser_config.chunk_token_num'} label={t('chunkTokenNumber')} max={max} + defaultValue={initialValue ?? 0} layout={FormLayout.Horizontal} > ); diff --git a/web/src/components/originui/input.tsx b/web/src/components/originui/input.tsx index d8c125f9115..18cf78f9779 100644 --- a/web/src/components/originui/input.tsx +++ b/web/src/components/originui/input.tsx @@ -1,24 +1,49 @@ import * as React from 'react'; import { cn } from '@/lib/utils'; +type InputProps = React.ComponentProps<'input'> & { + icon?: React.ReactNode; + iconPosition?: 'left' | 'right'; +}; -function Input({ className, type, ...props }: React.ComponentProps<'input'>) { +function Input({ + className, + type, + icon, + iconPosition = 'left', + ...props +}: InputProps) { return ( - + {icon && ( +
    + {icon} +
    )} - {...props} - /> + +
    ); } diff --git a/web/src/components/parse-configuration/raptor-form-fields.tsx b/web/src/components/parse-configuration/raptor-form-fields.tsx index b38db7c87f8..8ee184e46a9 100644 --- a/web/src/components/parse-configuration/raptor-form-fields.tsx +++ b/web/src/components/parse-configuration/raptor-form-fields.tsx @@ -64,10 +64,10 @@ const RaptorFormFields = () => { control={form.control} name={UseRaptorField} render={({ field }) => { - if (typeof field.value === 'undefined') { - // default value set - form.setValue('parser_config.raptor.use_raptor', false); - } + // if (typeof field.value === 'undefined') { + // // default value set + // form.setValue('parser_config.raptor.use_raptor', false); + // } return ( { ( - -
    - - {t('prompt')} - -
    - - - - - )} - /> - - )} + {isSubAgent && } (methods); + +export default agentService; diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 81142022ae0..93272175da9 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -148,6 +148,9 @@ export default { trace: `${api_host}/canvas/trace`, // agent inputForm: `${api_host}/canvas/input_form`, + fetchVersionList: (id: string) => `${api_host}/canvas/getlistversion/${id}`, + fetchVersion: (id: string) => `${api_host}/canvas/getversion/${id}`, + fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`, // mcp server listMcpServer: `${api_host}/mcp_server/list`, diff --git a/web/src/utils/next-request.ts b/web/src/utils/next-request.ts new file mode 100644 index 00000000000..3b4285646cd --- /dev/null +++ b/web/src/utils/next-request.ts @@ -0,0 +1,146 @@ +import message from '@/components/ui/message'; +import { Authorization } from '@/constants/authorization'; +import i18n from '@/locales/config'; +import authorizationUtil, { + getAuthorization, + redirectToLogin, +} from '@/utils/authorization-util'; +import { notification } from 'antd'; +import axios from 'axios'; +import { convertTheKeysOfTheObjectToSnake } from './common-util'; + +const FAILED_TO_FETCH = 'Failed to fetch'; + +export const RetcodeMessage = { + 200: i18n.t('message.200'), + 201: i18n.t('message.201'), + 202: i18n.t('message.202'), + 204: i18n.t('message.204'), + 400: i18n.t('message.400'), + 401: i18n.t('message.401'), + 403: i18n.t('message.403'), + 404: i18n.t('message.404'), + 406: i18n.t('message.406'), + 410: i18n.t('message.410'), + 413: i18n.t('message.413'), + 422: i18n.t('message.422'), + 500: i18n.t('message.500'), + 502: i18n.t('message.502'), + 503: i18n.t('message.503'), + 504: i18n.t('message.504'), +}; +export type ResultCode = + | 200 + | 201 + | 202 + | 204 + | 400 + | 401 + | 403 + | 404 + | 406 + | 410 + | 413 + | 422 + | 500 + | 502 + | 503 + | 504; + +const errorHandler = (error: { + response: Response; + message: string; +}): Response => { + const { response } = error; + if (error.message === FAILED_TO_FETCH) { + notification.error({ + description: i18n.t('message.networkAnomalyDescription'), + message: i18n.t('message.networkAnomaly'), + }); + } else { + if (response && response.status) { + const errorText = + RetcodeMessage[response.status as ResultCode] || response.statusText; + const { status, url } = response; + notification.error({ + message: `${i18n.t('message.requestError')} ${status}: ${url}`, + description: errorText, + }); + } + } + return response ?? { data: { code: 1999 } }; +}; + +const request = axios.create({ + // errorHandler, + timeout: 300000, + // getResponse: true, +}); + +request.interceptors.request.use( + (config) => { + const data = convertTheKeysOfTheObjectToSnake(config.data); + const params = convertTheKeysOfTheObjectToSnake(config.params); + + const newConfig = { ...config, data, params }; + + if (!newConfig.skipToken) { + newConfig.headers.set(Authorization, getAuthorization()); + } + + return newConfig; + }, + function (error) { + return Promise.reject(error); + }, +); + +request.interceptors.response.use( + async (response) => { + if (response?.status === 413 || response?.status === 504) { + message.error(RetcodeMessage[response?.status as ResultCode]); + } + + if (response.config.responseType === 'blob') { + return response; + } + const data = response?.data; + if (data?.code === 100) { + message.error(data?.message); + } else if (data?.code === 401) { + notification.error({ + message: data?.message, + description: data?.message, + duration: 3, + }); + authorizationUtil.removeAll(); + redirectToLogin(); + } else if (data?.code !== 0) { + notification.error({ + message: `${i18n.t('message.hint')} : ${data?.code}`, + description: data?.message, + duration: 3, + }); + } + return response; + }, + function (error) { + console.log('🚀 ~ error:', error); + errorHandler(error); + return Promise.reject(error); + }, +); + +export default request; + +export const get = (url: string) => { + return request.get(url); +}; + +export const post = (url: string, body: any) => { + return request.post(url, { data: body }); +}; + +export const drop = () => {}; + +export const put = () => {}; diff --git a/web/src/utils/register-server.ts b/web/src/utils/register-server.ts index 3aa987538f3..c70ebaf329b 100644 --- a/web/src/utils/register-server.ts +++ b/web/src/utils/register-server.ts @@ -1,5 +1,8 @@ +import { AxiosRequestConfig, AxiosResponse } from 'axios'; +import { isObject } from 'lodash'; import omit from 'lodash/omit'; import { RequestMethod } from 'umi-request'; +import request from './next-request'; type Service = Record< T, @@ -39,3 +42,39 @@ const registerServer = ( }; export default registerServer; + +export function registerNextServer( + requestRecord: Record< + T, + { url: string | ((...args: Array) => string); method: string } + >, +) { + type Server = Record< + T, + ( + config?: + | AxiosRequestConfig + | Record + | string + | number + | boolean + | undefined, + useAxiosNativeConfig?: boolean, + ) => Promise> + >; + const server: Server = {} as Server; + + for (const name in requestRecord) { + if (Object.prototype.hasOwnProperty.call(requestRecord, name)) { + const { url, method } = requestRecord[name]; + server[name] = (config, useAxiosNativeConfig = false) => { + const nextConfig = useAxiosNativeConfig ? config : { data: config }; + const finalConfig = isObject(nextConfig) ? nextConfig : {}; + const nextUrl = typeof url === 'function' ? url(config) : url; + return request({ url: nextUrl, method, ...finalConfig }); + }; + } + } + + return server; +} From 8345e92671737ecb9a90310e5a3b015f2fa25e17 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 23 Jul 2025 18:10:05 +0800 Subject: [PATCH 0234/1187] Feat: OpenAI-compatible-API supports references (#8997) ### What problem does this PR solve? OpenAI-compatible-API supports references. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/sdk/session.py | 161 +++++++++++++++--------- docs/references/python_api_reference.md | 18 ++- 2 files changed, 114 insertions(+), 65 deletions(-) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 74e770899f4..dd272255274 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -19,21 +19,22 @@ import tiktoken from flask import Response, jsonify, request -from api.db.services.conversation_service import ConversationService, iframe_completion -from api.db.services.conversation_service import completion as rag_completion -from api.db.services.canvas_service import completion as agent_completion, completionOpenAI + from agent.canvas import Canvas from api.db import LLMType, StatusEnum from api.db.db_models import APIToken from api.db.services.api_service import API4ConversationService -from api.db.services.canvas_service import UserCanvasService +from api.db.services.canvas_service import UserCanvasService, completionOpenAI +from api.db.services.canvas_service import completion as agent_completion +from api.db.services.conversation_service import ConversationService, iframe_completion +from api.db.services.conversation_service import completion as rag_completion from api.db.services.dialog_service import DialogService, ask, chat from api.db.services.file_service import FileService from api.db.services.knowledgebase_service import KnowledgebaseService -from api.utils import get_uuid -from api.utils.api_utils import get_result, token_required, get_data_openai, get_error_data_result, validate_request, check_duplicate_ids from api.db.services.llm_service import LLMBundle - +from api.utils import get_uuid +from api.utils.api_utils import check_duplicate_ids, get_data_openai, get_error_data_result, get_result, token_required, validate_request +from rag.prompts import chunks_format @manager.route("/chats//sessions", methods=["POST"]) # noqa: F821 @@ -181,10 +182,16 @@ def chat_completion(tenant_id, chat_id): def chat_completion_openai_like(tenant_id, chat_id): """ OpenAI-like chat completion API that simulates the behavior of OpenAI's completions endpoint. - + This function allows users to interact with a model and receive responses based on a series of historical messages. If `stream` is set to True (by default), the response will be streamed in chunks, mimicking the OpenAI-style API. Set `stream` to False explicitly, the response will be returned in a single complete answer. + + Reference: + + - If `stream` is True, the final answer and reference information will appear in the **last chunk** of the stream. + - If `stream` is False, the reference will be included in `choices[0].message.reference`. + Example usage: curl -X POST https://ragflow_address.com/api/v1/chats_openai//chat/completions \ @@ -202,7 +209,10 @@ def chat_completion_openai_like(tenant_id, chat_id): model = "model" client = OpenAI(api_key="ragflow-api-key", base_url=f"http://ragflow_address/api/v1/chats_openai/") - + + stream = True + reference = True + completion = client.chat.completions.create( model=model, messages=[ @@ -211,17 +221,24 @@ def chat_completion_openai_like(tenant_id, chat_id): {"role": "assistant", "content": "I am an AI assistant named..."}, {"role": "user", "content": "Can you tell me how to install neovim"}, ], - stream=True + stream=stream, + extra_body={"reference": reference} ) - - stream = True + if stream: - for chunk in completion: - print(chunk) + for chunk in completion: + print(chunk) + if reference and chunk.choices[0].finish_reason == "stop": + print(f"Reference:\n{chunk.choices[0].delta.reference}") + print(f"Final content:\n{chunk.choices[0].delta.final_content}") else: print(completion.choices[0].message.content) + if reference: + print(completion.choices[0].message.reference) """ - req = request.json + req = request.get_json() + + need_reference = bool(req.get("reference", False)) messages = req.get("messages", []) # To prevent empty [] input @@ -261,9 +278,23 @@ def streamed_response_generator(chat_id, dia, msg): token_used = 0 answer_cache = "" reasoning_cache = "" + last_ans = {} response = { "id": f"chatcmpl-{chat_id}", - "choices": [{"delta": {"content": "", "role": "assistant", "function_call": None, "tool_calls": None, "reasoning_content": ""}, "finish_reason": None, "index": 0, "logprobs": None}], + "choices": [ + { + "delta": { + "content": "", + "role": "assistant", + "function_call": None, + "tool_calls": None, + "reasoning_content": "", + }, + "finish_reason": None, + "index": 0, + "logprobs": None, + } + ], "created": int(time.time()), "model": "model", "object": "chat.completion.chunk", @@ -272,7 +303,8 @@ def streamed_response_generator(chat_id, dia, msg): } try: - for ans in chat(dia, msg, True, toolcall_session=toolcall_session, tools=tools): + for ans in chat(dia, msg, True, toolcall_session=toolcall_session, tools=tools, quote=need_reference): + last_ans = ans answer = ans["answer"] reasoning_match = re.search(r"(.*?)", answer, flags=re.DOTALL) @@ -324,6 +356,9 @@ def streamed_response_generator(chat_id, dia, msg): response["choices"][0]["delta"]["reasoning_content"] = None response["choices"][0]["finish_reason"] = "stop" response["usage"] = {"prompt_tokens": len(prompt), "completion_tokens": token_used, "total_tokens": len(prompt) + token_used} + if need_reference: + response["choices"][0]["delta"]["reference"] = chunks_format(last_ans.get("reference", [])) + response["choices"][0]["delta"]["final_content"] = last_ans.get("answer", "") yield f"data:{json.dumps(response, ensure_ascii=False)}\n\n" yield "data:[DONE]\n\n" @@ -335,7 +370,7 @@ def streamed_response_generator(chat_id, dia, msg): return resp else: answer = None - for ans in chat(dia, msg, False, toolcall_session=toolcall_session, tools=tools): + for ans in chat(dia, msg, False, toolcall_session=toolcall_session, tools=tools, quote=need_reference): # focus answer content only answer = ans break @@ -356,14 +391,28 @@ def streamed_response_generator(chat_id, dia, msg): "rejected_prediction_tokens": 0, # 0 for simplicity }, }, - "choices": [{"message": {"role": "assistant", "content": content}, "logprobs": None, "finish_reason": "stop", "index": 0}], + "choices": [ + { + "message": { + "role": "assistant", + "content": content, + }, + "logprobs": None, + "finish_reason": "stop", + "index": 0, + } + ], } + if need_reference: + response["choices"][0]["message"]["reference"] = chunks_format(answer.get("reference", [])) + return jsonify(response) -@manager.route('/agents_openai//chat/completions', methods=['POST']) # noqa: F821 + +@manager.route("/agents_openai//chat/completions", methods=["POST"]) # noqa: F821 @validate_request("model", "messages") # noqa: F821 @token_required -def agents_completion_openai_compatibility (tenant_id, agent_id): +def agents_completion_openai_compatibility(tenant_id, agent_id): req = request.json tiktokenenc = tiktoken.get_encoding("cl100k_base") messages = req.get("messages", []) @@ -371,29 +420,31 @@ def agents_completion_openai_compatibility (tenant_id, agent_id): return get_error_data_result("You must provide at least one message.") if not UserCanvasService.query(user_id=tenant_id, id=agent_id): return get_error_data_result(f"You don't own the agent {agent_id}") - + filtered_messages = [m for m in messages if m["role"] in ["user", "assistant"]] prompt_tokens = sum(len(tiktokenenc.encode(m["content"])) for m in filtered_messages) if not filtered_messages: - return jsonify(get_data_openai( - id=agent_id, - content="No valid messages found (user or assistant).", - finish_reason="stop", - model=req.get("model", ""), - completion_tokens=len(tiktokenenc.encode("No valid messages found (user or assistant).")), - prompt_tokens=prompt_tokens, - )) - + return jsonify( + get_data_openai( + id=agent_id, + content="No valid messages found (user or assistant).", + finish_reason="stop", + model=req.get("model", ""), + completion_tokens=len(tiktokenenc.encode("No valid messages found (user or assistant).")), + prompt_tokens=prompt_tokens, + ) + ) + # Get the last user message as the question question = next((m["content"] for m in reversed(messages) if m["role"] == "user"), "") - + if req.get("stream", True): - return Response(completionOpenAI(tenant_id, agent_id, question, session_id=req.get("id", req.get("metadata", {}).get("id","")), stream=True), mimetype="text/event-stream") + return Response(completionOpenAI(tenant_id, agent_id, question, session_id=req.get("id", req.get("metadata", {}).get("id", "")), stream=True), mimetype="text/event-stream") else: # For non-streaming, just return the response directly - response = next(completionOpenAI(tenant_id, agent_id, question, session_id=req.get("id", req.get("metadata", {}).get("id","")), stream=False)) + response = next(completionOpenAI(tenant_id, agent_id, question, session_id=req.get("id", req.get("metadata", {}).get("id", "")), stream=False)) return jsonify(response) - + @manager.route("/agents//completions", methods=["POST"]) # noqa: F821 @token_required @@ -547,7 +598,7 @@ def list_agent_session(tenant_id, agent_id): def delete(tenant_id, chat_id): if not DialogService.query(id=chat_id, tenant_id=tenant_id, status=StatusEnum.VALID.value): return get_error_data_result(message="You don't own the chat") - + errors = [] success_count = 0 req = request.json @@ -563,10 +614,10 @@ def delete(tenant_id, chat_id): conv_list.append(conv.id) else: conv_list = ids - + unique_conv_ids, duplicate_messages = check_duplicate_ids(conv_list, "session") conv_list = unique_conv_ids - + for id in conv_list: conv = ConversationService.query(id=id, dialog_id=chat_id) if not conv: @@ -574,25 +625,19 @@ def delete(tenant_id, chat_id): continue ConversationService.delete_by_id(id) success_count += 1 - + if errors: if success_count > 0: - return get_result( - data={"success_count": success_count, "errors": errors}, - message=f"Partially deleted {success_count} sessions with {len(errors)} errors" - ) + return get_result(data={"success_count": success_count, "errors": errors}, message=f"Partially deleted {success_count} sessions with {len(errors)} errors") else: return get_error_data_result(message="; ".join(errors)) - + if duplicate_messages: if success_count > 0: - return get_result( - message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", - data={"success_count": success_count, "errors": duplicate_messages} - ) + return get_result(message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", data={"success_count": success_count, "errors": duplicate_messages}) else: return get_error_data_result(message=";".join(duplicate_messages)) - + return get_result() @@ -632,25 +677,19 @@ def delete_agent_session(tenant_id, agent_id): continue API4ConversationService.delete_by_id(session_id) success_count += 1 - + if errors: if success_count > 0: - return get_result( - data={"success_count": success_count, "errors": errors}, - message=f"Partially deleted {success_count} sessions with {len(errors)} errors" - ) + return get_result(data={"success_count": success_count, "errors": errors}, message=f"Partially deleted {success_count} sessions with {len(errors)} errors") else: return get_error_data_result(message="; ".join(errors)) - + if duplicate_messages: if success_count > 0: - return get_result( - message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", - data={"success_count": success_count, "errors": duplicate_messages} - ) + return get_result(message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", data={"success_count": success_count, "errors": duplicate_messages}) else: return get_error_data_result(message=";".join(duplicate_messages)) - + return get_result() @@ -719,7 +758,7 @@ def related_questions(tenant_id): Reason: - When searching, users often only use one or two keywords, making it difficult to fully express their information needs. - - Generating related search terms can help users dig deeper into relevant information and improve search efficiency. + - Generating related search terms can help users dig deeper into relevant information and improve search efficiency. - At the same time, related terms can also help search engines better understand user needs and return more accurate search results. """ diff --git a/docs/references/python_api_reference.md b/docs/references/python_api_reference.md index e84dc958fe4..12ff48678f0 100644 --- a/docs/references/python_api_reference.md +++ b/docs/references/python_api_reference.md @@ -69,21 +69,31 @@ from openai import OpenAI model = "model" client = OpenAI(api_key="ragflow-api-key", base_url=f"http://ragflow_address/api/v1/chats_openai/") +stream = True +reference = True + completion = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Who are you?"}, + {"role": "assistant", "content": "I am an AI assistant named..."}, + {"role": "user", "content": "Can you tell me how to install neovim"}, ], - stream=True + stream=stream, + extra_body={"reference": reference} ) -stream = True if stream: - for chunk in completion: - print(chunk) +for chunk in completion: + print(chunk) + if reference and chunk.choices[0].finish_reason == "stop": + print(f"Reference:\n{chunk.choices[0].delta.reference}") + print(f"Final content:\n{chunk.choices[0].delta.final_content}") else: print(completion.choices[0].message.content) + if reference: + print(completion.choices[0].message.reference) ``` ## DATASET MANAGEMENT From 5f0ec005bab95f318a9672ea1c0eee71b6e69ef2 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 23 Jul 2025 18:10:18 +0800 Subject: [PATCH 0235/1187] Feat: Share agent dialog box externally #3221 (#9005) ### What problem does this PR solve? Feat: Share agent dialog box externally #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/public/iconfont.js | 2 +- .../next-message-item/group-button.tsx | 10 +- .../components/next-message-item/index.tsx | 3 + web/src/constants/setting.ts | 1 + web/src/hooks/logic-hooks/navigate-hooks.ts | 9 +- web/src/hooks/use-agent-request.ts | 30 +++ web/src/layouts/components/header/index.tsx | 2 +- web/src/locales/en.ts | 1 + web/src/pages/agent/chat/box.tsx | 66 +++--- .../{hooks.ts => use-send-agent-message.ts} | 191 +++++++----------- web/src/pages/agent/embed-dialog/index.tsx | 2 +- web/src/pages/agent/index.tsx | 22 +- web/src/pages/agents/agent-templates.tsx | 27 ++- .../hooks/use-send-shared-message.ts | 127 +----------- web/src/pages/next-chats/share/index.less | 13 -- web/src/pages/next-chats/share/index.tsx | 116 ++++++++++- web/src/pages/next-chats/share/large.tsx | 123 ----------- web/src/pages/next-chats/utils.ts | 5 +- web/src/pages/user-setting/constants.tsx | 4 + web/src/routes.ts | 14 ++ web/src/services/agent-service.ts | 5 + web/src/utils/api.ts | 1 + 22 files changed, 347 insertions(+), 427 deletions(-) rename web/src/pages/agent/chat/{hooks.ts => use-send-agent-message.ts} (80%) delete mode 100644 web/src/pages/next-chats/share/index.less delete mode 100644 web/src/pages/next-chats/share/large.tsx diff --git a/web/public/iconfont.js b/web/public/iconfont.js index b405874429f..94672574f0e 100644 --- a/web/public/iconfont.js +++ b/web/public/iconfont.js @@ -1,5 +1,5 @@ (window._iconfont_svg_string_4909832 = - ''), + ''), ((h) => { var a = (l = (l = document.getElementsByTagName('script'))[ l.length - 1 diff --git a/web/src/components/next-message-item/group-button.tsx b/web/src/components/next-message-item/group-button.tsx index 3e1b21e888f..06c528fda63 100644 --- a/web/src/components/next-message-item/group-button.tsx +++ b/web/src/components/next-message-item/group-button.tsx @@ -27,6 +27,7 @@ interface IProps { showLikeButton: boolean; audioBinary?: string; showLoudspeaker?: boolean; + showLog?: boolean; } export const AssistantGroupButton = ({ @@ -36,6 +37,7 @@ export const AssistantGroupButton = ({ audioBinary, showLikeButton, showLoudspeaker = true, + showLog = true, }: IProps) => { const { visible, hideModal, showModal, onFeedbackOk, loading } = useSendFeedback(messageId); @@ -91,9 +93,11 @@ export const AssistantGroupButton = ({ )} - - - + {showLog && ( + + + + )} {visible && ( ) : ( { navigate(Routes.Chat); }, [navigate]); - const navigateToAgentList = useCallback(() => { + const navigateToAgents = useCallback(() => { navigate(Routes.Agents); }, [navigate]); + const navigateToAgentList = useCallback(() => { + navigate(Routes.AgentList); + }, [navigate]); + const navigateToAgent = useCallback( (id: string) => () => { navigate(`${Routes.Agent}/${id}`); @@ -114,11 +118,12 @@ export const useNavigatePage = () => { navigateToChunkParsedResult, getQueryString, navigateToChunk, - navigateToAgentList, + navigateToAgents, navigateToAgent, navigateToAgentTemplates, navigateToSearchList, navigateToSearch, navigateToFiles, + navigateToAgentList, }; }; diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index 2eef5cbee7d..bb7e53218c9 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -35,6 +35,7 @@ export const enum AgentApiAction { FetchInputForm = 'fetchInputForm', FetchVersionList = 'fetchVersionList', FetchVersion = 'fetchVersion', + FetchAgentAvatar = 'fetchAgentAvatar', } export const EmptyDsl = { @@ -444,3 +445,32 @@ export const useFetchVersion = ( return { data, loading }; }; + +export const useFetchAgentAvatar = (): { + data: IFlow; + loading: boolean; + refetch: () => void; +} => { + const { sharedId } = useGetSharedChatSearchParams(); + + const { + data, + isFetching: loading, + refetch, + } = useQuery({ + queryKey: [AgentApiAction.FetchAgentAvatar], + initialData: {} as IFlow, + refetchOnReconnect: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + gcTime: 0, + queryFn: async () => { + if (!sharedId) return {}; + const { data } = await agentService.fetchAgentAvatar(sharedId); + + return data?.data ?? {}; + }, + }); + + return { data, loading, refetch }; +}; diff --git a/web/src/layouts/components/header/index.tsx b/web/src/layouts/components/header/index.tsx index 87a6d3bdc5b..ffecfb7709c 100644 --- a/web/src/layouts/components/header/index.tsx +++ b/web/src/layouts/components/header/index.tsx @@ -29,7 +29,7 @@ const RagHeader = () => { { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon }, { path: '/chat', name: t('chat'), icon: MessageOutlined }, { path: '/search', name: t('search'), icon: SearchOutlined }, - { path: '/flow', name: t('flow'), icon: GraphIcon }, + { path: '/agent-list', name: t('flow'), icon: GraphIcon }, { path: '/file', name: t('fileManager'), icon: FileIcon }, ], [t], diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 881a5ebe624..290277f93fd 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -732,6 +732,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s view: 'View', modelsToBeAddedTooltip: 'If your model provider is not listed but claims to be "OpenAI-compatible", select the OpenAI-API-compatible card to add the relevant model(s). ', + mcp: 'MCP', }, message: { registered: 'Registered!', diff --git a/web/src/pages/agent/chat/box.tsx b/web/src/pages/agent/chat/box.tsx index 631427a47b4..31a6f26f4dc 100644 --- a/web/src/pages/agent/chat/box.tsx +++ b/web/src/pages/agent/chat/box.tsx @@ -1,8 +1,7 @@ import { MessageType } from '@/constants/chat'; import { useGetFileIcon } from '@/pages/chat/hooks'; -import { Spin } from 'antd'; -import { useSendNextMessage } from './hooks'; +import { useSendAgentMessage } from './use-send-agent-message'; import MessageInput from '@/components/message-input'; import MessageItem from '@/components/next-message-item'; @@ -25,13 +24,12 @@ const AgentChatBox = () => { handleInputChange, handlePressEnter, value, - loading, ref, derivedMessages, stopOutputMessage, sendFormMessage, findReferenceByMessageId, - } = useSendNextMessage(); + } = useSendAgentMessage(); const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = useClickDrawer(); @@ -73,36 +71,36 @@ const AgentChatBox = () => {
    - - {derivedMessages?.map((message, i) => { - return ( - - - - ); - })} - + {/* */} + {derivedMessages?.map((message, i) => { + return ( + + + + ); + })} + {/* */}
    diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/use-send-agent-message.ts similarity index 80% rename from web/src/pages/agent/chat/hooks.ts rename to web/src/pages/agent/chat/use-send-agent-message.ts index 280b81ed723..30169f1a23b 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/use-send-agent-message.ts @@ -4,7 +4,6 @@ import { useHandleMessageInputChange, useSelectDerivedMessages, } from '@/hooks/logic-hooks'; -import { useFetchAgent } from '@/hooks/use-agent-request'; import { IEventList, IInputEvent, @@ -30,37 +29,7 @@ import { BeginQuery } from '../interface'; import useGraphStore from '../store'; import { receiveMessageError } from '../utils'; -export const useSelectNextMessages = () => { - const { data: flowDetail, loading } = useFetchAgent(); - const reference = flowDetail.dsl.retrieval; - const { - derivedMessages, - ref, - addNewestQuestion, - addNewestAnswer, - removeLatestMessage, - removeMessageById, - removeMessagesAfterCurrentMessage, - addNewestOneQuestion, - addNewestOneAnswer, - } = useSelectDerivedMessages(); - - return { - reference, - loading, - derivedMessages, - ref, - addNewestQuestion, - addNewestAnswer, - removeLatestMessage, - removeMessageById, - addNewestOneQuestion, - addNewestOneAnswer, - removeMessagesAfterCurrentMessage, - }; -}; - -function findMessageFromList(eventList: IEventList) { +export function findMessageFromList(eventList: IEventList) { const messageEventList = eventList.filter( (x) => x.event === MessageEventType.Message, ) as IMessageEvent[]; @@ -101,7 +70,7 @@ function findMessageFromList(eventList: IEventList) { }; } -function findInputFromList(eventList: IEventList) { +export function findInputFromList(eventList: IEventList) { const inputEvent = eventList.find( (x) => x.event === MessageEventType.UserInputs, ) as IInputEvent; @@ -120,7 +89,7 @@ export function getLatestError(eventList: IEventList) { return get(eventList.at(-1), 'data.outputs._ERROR'); } -const useGetBeginNodePrologue = () => { +export const useGetBeginNodePrologue = () => { const getNode = useGraphStore((state) => state.getNode); return useMemo(() => { @@ -131,31 +100,61 @@ const useGetBeginNodePrologue = () => { }, [getNode]); }; -export const useSendNextMessage = () => { +export function useFindMessageReference(answerList: IEventList) { + const [messageEndEventList, setMessageEndEventList] = useState< + IMessageEndEvent[] + >([]); + + const findReferenceByMessageId = useCallback( + (messageId: string) => { + const event = messageEndEventList.find( + (item) => item.message_id === messageId, + ); + if (event) { + return (event?.data as IMessageEndData)?.reference; + } + }, + [messageEndEventList], + ); + + useEffect(() => { + const messageEndEvent = answerList.find( + (x) => x.event === MessageEventType.MessageEnd, + ); + if (messageEndEvent) { + setMessageEndEventList((list) => { + const nextList = [...list]; + if ( + nextList.every((x) => x.message_id !== messageEndEvent.message_id) + ) { + nextList.push(messageEndEvent as IMessageEndEvent); + } + return nextList; + }); + } + }, [answerList]); + + return { findReferenceByMessageId }; +} + +export const useSendAgentMessage = (url?: string) => { + const { id: agentId } = useParams(); + const { handleInputChange, value, setValue } = useHandleMessageInputChange(); + const inputs = useSelectBeginNodeDataInputs(); + const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE( + url || api.runCanvas, + ); + const { findReferenceByMessageId } = useFindMessageReference(answerList); + const prologue = useGetBeginNodePrologue(); const { - reference, - loading, derivedMessages, ref, removeLatestMessage, removeMessageById, addNewestOneQuestion, addNewestOneAnswer, - } = useSelectNextMessages(); - const { id: agentId } = useParams(); - const { handleInputChange, value, setValue } = useHandleMessageInputChange(); - const { refetch } = useFetchAgent(); + } = useSelectDerivedMessages(); const { addEventList } = useContext(AgentChatLogContext); - const inputs = useSelectBeginNodeDataInputs(); - const [messageEndEventList, setMessageEndEventList] = useState< - IMessageEndEvent[] - >([]); - - const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE( - api.runCanvas, - ); - - const prologue = useGetBeginNodePrologue(); const sendMessage = useCallback( async ({ message }: { message: Message; messages?: Message[] }) => { @@ -182,35 +181,40 @@ export const useSendNextMessage = () => { setValue(message.content); removeLatestMessage(); } else { - refetch(); // pull the message list after sending the message successfully + // refetch(); // pull the message list after sending the message successfully } }, - [agentId, send, inputs, setValue, removeLatestMessage, refetch], + [agentId, send, inputs, setValue, removeLatestMessage], ); - const handleSendMessage = useCallback( - async (message: Message) => { - sendMessage({ message }); + const sendFormMessage = useCallback( + (body: { id?: string; inputs: Record }) => { + send(body); + addNewestOneQuestion({ + content: Object.entries(body.inputs) + .map(([key, val]) => `${key}: ${val.value}`) + .join('
    '), + role: MessageType.User, + }); }, - [sendMessage], + [addNewestOneQuestion, send], ); - useEffect(() => { - const messageEndEvent = answerList.find( - (x) => x.event === MessageEventType.MessageEnd, - ); - if (messageEndEvent) { - setMessageEndEventList((list) => { - const nextList = [...list]; - if ( - nextList.every((x) => x.message_id !== messageEndEvent.message_id) - ) { - nextList.push(messageEndEvent as IMessageEndEvent); - } - return nextList; + const handlePressEnter = useCallback(() => { + if (trim(value) === '') return; + const id = uuid(); + if (done) { + setValue(''); + sendMessage({ + message: { id, content: value.trim(), role: MessageType.User }, }); } - }, [addEventList.length, answerList]); + addNewestOneQuestion({ + content: value, + id, + role: MessageType.User, + }); + }, [value, done, addNewestOneQuestion, setValue, sendMessage]); useEffect(() => { const { content, id } = findMessageFromList(answerList); @@ -224,45 +228,6 @@ export const useSendNextMessage = () => { } }, [answerList, addNewestOneAnswer]); - const handlePressEnter = useCallback(() => { - if (trim(value) === '') return; - const id = uuid(); - if (done) { - setValue(''); - handleSendMessage({ id, content: value.trim(), role: MessageType.User }); - } - addNewestOneQuestion({ - content: value, - id, - role: MessageType.User, - }); - }, [value, done, addNewestOneQuestion, setValue, handleSendMessage]); - - const sendFormMessage = useCallback( - (body: { id?: string; inputs: Record }) => { - send(body); - addNewestOneQuestion({ - content: Object.entries(body.inputs) - .map(([key, val]) => `${key}: ${val.value}`) - .join('
    '), - role: MessageType.User, - }); - }, - [addNewestOneQuestion, send], - ); - - const findReferenceByMessageId = useCallback( - (messageId: string) => { - const event = messageEndEventList.find( - (item) => item.message_id === messageId, - ); - if (event) { - return (event?.data as IMessageEndData)?.reference; - } - }, - [messageEndEventList], - ); - useEffect(() => { if (prologue) { addNewestOneAnswer({ @@ -272,7 +237,9 @@ export const useSendNextMessage = () => { }, [addNewestOneAnswer, agentId, prologue, send, sendFormMessage]); useEffect(() => { - addEventList(answerList); + if (typeof addEventList === 'function') { + addEventList(answerList); + } }, [addEventList, answerList]); return { @@ -280,8 +247,6 @@ export const useSendNextMessage = () => { handleInputChange, value, sendLoading: !done, - reference, - loading, derivedMessages, ref, removeMessageById, diff --git a/web/src/pages/agent/embed-dialog/index.tsx b/web/src/pages/agent/embed-dialog/index.tsx index 63abba9d931..8b5e64bf5ad 100644 --- a/web/src/pages/agent/embed-dialog/index.tsx +++ b/web/src/pages/agent/embed-dialog/index.tsx @@ -68,7 +68,7 @@ function EmbedDialog({ const generateIframeSrc = useCallback(() => { const { visibleAvatar, locale } = values; - let src = `${location.origin}/chat/share?shared_id=${token}&from=${from}&auth=${beta}`; + let src = `${location.origin}/next-chat/share?shared_id=${token}&from=${from}&auth=${beta}`; if (visibleAvatar) { src += '&visible_avatar=1'; } diff --git a/web/src/pages/agent/index.tsx b/web/src/pages/agent/index.tsx index 2eb850683e4..cced9b4693a 100644 --- a/web/src/pages/agent/index.tsx +++ b/web/src/pages/agent/index.tsx @@ -18,6 +18,7 @@ import { import { SharedFrom } from '@/constants/chat'; import { useSetModalState } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; +import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; import { ReactFlowProvider } from '@xyflow/react'; import { ChevronDown, @@ -37,7 +38,10 @@ import AgentCanvas from './canvas'; import EmbedDialog from './embed-dialog'; import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; import { useFetchDataOnMount } from './hooks/use-fetch-data'; -import { useGetBeginNodeDataInputs } from './hooks/use-get-begin-query'; +import { + useGetBeginNodeDataInputs, + useGetBeginNodeDataQueryIsSafe, +} from './hooks/use-get-begin-query'; import { useOpenDocument } from './hooks/use-open-document'; import { useSaveGraph, @@ -67,6 +71,8 @@ export default function Agent() { showModal: showChatDrawer, } = useSetModalState(); const { t } = useTranslation(); + const { data: userInfo } = useFetchUserInfo(); + const openDocument = useOpenDocument(); const { handleExportJson, @@ -76,7 +82,7 @@ export default function Agent() { hideFileUploadModal, } = useHandleExportOrImportJsonFile(); const { saveGraph, loading } = useSaveGraph(); - const { flowDetail } = useFetchDataOnMount(); + const { flowDetail: agentDetail } = useFetchDataOnMount(); const inputs = useGetBeginNodeDataInputs(); const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); const handleRunAgent = useCallback(() => { @@ -95,6 +101,8 @@ export default function Agent() { const { showEmbedModal, hideEmbedModal, embedVisible, beta } = useShowEmbedModal(); + const isBeginNodeDataQuerySafe = useGetBeginNodeDataQueryIsSafe(); + return (
    @@ -107,7 +115,7 @@ export default function Agent() { - {flowDetail.title} + {agentDetail.title} @@ -154,7 +162,13 @@ export default function Agent() { {t('flow.export')} - + {t('common.embedIntoSite')} diff --git a/web/src/pages/agents/agent-templates.tsx b/web/src/pages/agents/agent-templates.tsx index 3d4b43f83b3..47b780a77c5 100644 --- a/web/src/pages/agents/agent-templates.tsx +++ b/web/src/pages/agents/agent-templates.tsx @@ -1,4 +1,12 @@ import { PageHeader } from '@/components/page-header'; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from '@/components/ui/breadcrumb'; import { useSetModalState } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useFetchAgentTemplates, useSetAgent } from '@/hooks/use-agent-request'; @@ -57,10 +65,21 @@ export default function AgentTemplates() { return (
    - + + + + + + Agent + + + + + {t('flow.createGraph')} + + + +
    {list?.map((x) => { return ( diff --git a/web/src/pages/next-chats/hooks/use-send-shared-message.ts b/web/src/pages/next-chats/hooks/use-send-shared-message.ts index c378eec304f..e80b9d9fb40 100644 --- a/web/src/pages/next-chats/hooks/use-send-shared-message.ts +++ b/web/src/pages/next-chats/hooks/use-send-shared-message.ts @@ -1,20 +1,7 @@ -import { MessageType, SharedFrom } from '@/constants/chat'; -import { useCreateNextSharedConversation } from '@/hooks/chat-hooks'; -import { - useHandleMessageInputChange, - useSelectDerivedMessages, - useSendMessageWithSse, -} from '@/hooks/logic-hooks'; -import { Message } from '@/interfaces/database/chat'; -import { message } from 'antd'; -import { get } from 'lodash'; +import { SharedFrom } from '@/constants/chat'; +import { useSendAgentMessage } from '@/pages/agent/chat/use-send-agent-message'; import trim from 'lodash/trim'; -import { useCallback, useEffect, useState } from 'react'; import { useSearchParams } from 'umi'; -import { v4 as uuid } from 'uuid'; - -const isCompletionError = (res: any) => - res && (res?.response.status !== 200 || res?.data?.code !== 0); export const useSendButtonDisabled = (value: string) => { return trim(value) === ''; @@ -40,110 +27,14 @@ export const useGetSharedChatSearchParams = () => { }; }; -export const useSendSharedMessage = () => { - const { - from, - sharedId: conversationId, - data: data, - } = useGetSharedChatSearchParams(); - const { createSharedConversation: setConversation } = - useCreateNextSharedConversation(); - const { handleInputChange, value, setValue } = useHandleMessageInputChange(); - const { send, answer, done, stopOutputMessage } = useSendMessageWithSse( - `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`, - ); - const { - derivedMessages, - ref, - removeLatestMessage, - addNewestAnswer, - addNewestQuestion, - } = useSelectDerivedMessages(); - const [hasError, setHasError] = useState(false); - - const sendMessage = useCallback( - async (message: Message, id?: string) => { - const res = await send({ - conversation_id: id ?? conversationId, - quote: true, - question: message.content, - session_id: get(derivedMessages, '0.session_id'), - }); +export function useSendNextSharedMessage() { + const { from, sharedId: conversationId } = useGetSharedChatSearchParams(); + const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`; - if (isCompletionError(res)) { - // cancel loading - setValue(message.content); - removeLatestMessage(); - } - }, - [send, conversationId, derivedMessages, setValue, removeLatestMessage], - ); - - const handleSendMessage = useCallback( - async (message: Message) => { - if (conversationId !== '') { - sendMessage(message); - } else { - const data = await setConversation('user id'); - if (data.code === 0) { - const id = data.data.id; - sendMessage(message, id); - } - } - }, - [conversationId, setConversation, sendMessage], - ); - - const fetchSessionId = useCallback(async () => { - const payload = { question: '' }; - const ret = await send({ ...payload, ...data }); - if (isCompletionError(ret)) { - message.error(ret?.data.message); - setHasError(true); - } - }, [data, send]); - - useEffect(() => { - fetchSessionId(); - }, [fetchSessionId, send]); - - useEffect(() => { - if (answer.answer) { - addNewestAnswer(answer); - } - }, [answer, addNewestAnswer]); - - const handlePressEnter = useCallback( - (documentIds: string[]) => { - if (trim(value) === '') return; - const id = uuid(); - if (done) { - setValue(''); - addNewestQuestion({ - content: value, - doc_ids: documentIds, - id, - role: MessageType.User, - }); - handleSendMessage({ - content: value.trim(), - id, - role: MessageType.User, - }); - } - }, - [addNewestQuestion, done, handleSendMessage, setValue, value], - ); + const ret = useSendAgentMessage(url); return { - handlePressEnter, - handleInputChange, - value, - sendLoading: !done, - ref, - loading: false, - derivedMessages, - hasError, - stopOutputMessage, + ...ret, + hasError: false, }; -}; +} diff --git a/web/src/pages/next-chats/share/index.less b/web/src/pages/next-chats/share/index.less deleted file mode 100644 index 01e090061e9..00000000000 --- a/web/src/pages/next-chats/share/index.less +++ /dev/null @@ -1,13 +0,0 @@ -.chatWrapper { - height: 100vh; -} - -.chatContainer { - padding: 10px; - box-sizing: border-box; - height: 100%; - .messageContainer { - overflow-y: auto; - padding-right: 6px; - } -} diff --git a/web/src/pages/next-chats/share/index.tsx b/web/src/pages/next-chats/share/index.tsx index acaadcbf944..30c3ea24d85 100644 --- a/web/src/pages/next-chats/share/index.tsx +++ b/web/src/pages/next-chats/share/index.tsx @@ -1,13 +1,115 @@ -import ChatContainer from './large'; +import MessageInput from '@/components/message-input'; +import MessageItem from '@/components/next-message-item'; +import PdfDrawer from '@/components/pdf-drawer'; +import { useClickDrawer } from '@/components/pdf-drawer/hooks'; +import { MessageType, SharedFrom } from '@/constants/chat'; +import { useFetchNextConversationSSE } from '@/hooks/chat-hooks'; +import { useFetchAgentAvatar } from '@/hooks/use-agent-request'; +import { cn } from '@/lib/utils'; +import i18n from '@/locales/config'; +import { useSendButtonDisabled } from '@/pages/chat/hooks'; +import { buildMessageUuidWithRole } from '@/utils/chat'; +import React, { forwardRef, useMemo } from 'react'; +import { + useGetSharedChatSearchParams, + useSendNextSharedMessage, +} from '../hooks/use-send-shared-message'; -import styles from './index.less'; +const ChatContainer = () => { + const { + sharedId: conversationId, + from, + locale, + visibleAvatar, + } = useGetSharedChatSearchParams(); + const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = + useClickDrawer(); + + const { + handlePressEnter, + handleInputChange, + value, + sendLoading, + ref, + derivedMessages, + hasError, + stopOutputMessage, + findReferenceByMessageId, + } = useSendNextSharedMessage(); + const sendDisabled = useSendButtonDisabled(value); + + const useFetchAvatar = useMemo(() => { + return from === SharedFrom.Agent + ? useFetchAgentAvatar + : useFetchNextConversationSSE; + }, [from]); + + React.useEffect(() => { + if (locale && i18n.language !== locale) { + i18n.changeLanguage(locale); + } + }, [locale, visibleAvatar]); + const { data: avatarData } = useFetchAvatar(); + + if (!conversationId) { + return
    empty
    ; + } -const SharedChat = () => { return ( -
    - -
    +
    +
    +
    +
    + {derivedMessages?.map((message, i) => { + return ( + + ); + })} +
    +
    +
    + + +
    + {visible && ( + + )} +
    ); }; -export default SharedChat; +export default forwardRef(ChatContainer); diff --git a/web/src/pages/next-chats/share/large.tsx b/web/src/pages/next-chats/share/large.tsx deleted file mode 100644 index 6aad99bbe15..00000000000 --- a/web/src/pages/next-chats/share/large.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import MessageInput from '@/components/message-input'; -import MessageItem from '@/components/message-item'; -import PdfDrawer from '@/components/pdf-drawer'; -import { useClickDrawer } from '@/components/pdf-drawer/hooks'; -import { MessageType, SharedFrom } from '@/constants/chat'; -import { useFetchNextConversationSSE } from '@/hooks/chat-hooks'; -import { useFetchFlowSSE } from '@/hooks/flow-hooks'; -import i18n from '@/locales/config'; -import { useSendButtonDisabled } from '@/pages/chat/hooks'; -import { buildMessageUuidWithRole } from '@/utils/chat'; -import { Flex, Spin } from 'antd'; -import React, { forwardRef, useMemo } from 'react'; -import { - useGetSharedChatSearchParams, - useSendSharedMessage, -} from '../hooks/use-send-shared-message'; -import { buildMessageItemReference } from '../utils'; -import styles from './index.less'; - -const ChatContainer = () => { - const { - sharedId: conversationId, - from, - locale, - visibleAvatar, - } = useGetSharedChatSearchParams(); - const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = - useClickDrawer(); - - const { - handlePressEnter, - handleInputChange, - value, - sendLoading, - loading, - ref, - derivedMessages, - hasError, - stopOutputMessage, - } = useSendSharedMessage(); - const sendDisabled = useSendButtonDisabled(value); - - const useFetchAvatar = useMemo(() => { - return from === SharedFrom.Agent - ? useFetchFlowSSE - : useFetchNextConversationSSE; - }, [from]); - React.useEffect(() => { - if (locale && i18n.language !== locale) { - i18n.changeLanguage(locale); - } - }, [locale, visibleAvatar]); - const { data: avatarData } = useFetchAvatar(); - - if (!conversationId) { - return
    empty
    ; - } - - return ( - <> - - -
    - - {derivedMessages?.map((message, i) => { - return ( - - ); - })} - -
    -
    - - - - - {visible && ( - - )} - - ); -}; - -export default forwardRef(ChatContainer); diff --git a/web/src/pages/next-chats/utils.ts b/web/src/pages/next-chats/utils.ts index e3e4f5ff205..a31f40097e9 100644 --- a/web/src/pages/next-chats/utils.ts +++ b/web/src/pages/next-chats/utils.ts @@ -1,8 +1,7 @@ -import { MessageType } from '@/constants/chat'; +import { EmptyConversationId, MessageType } from '@/constants/chat'; import { IConversation, IReference } from '@/interfaces/database/chat'; import { isEmpty } from 'lodash'; -import { EmptyConversationId } from './constants'; -import { IMessage } from './interface'; +import { IMessage } from '../chat/interface'; export const isConversationIdExist = (conversationId: string) => { return conversationId !== EmptyConversationId && conversationId !== ''; diff --git a/web/src/pages/user-setting/constants.tsx b/web/src/pages/user-setting/constants.tsx index 6c774554680..ecf555d62cd 100644 --- a/web/src/pages/user-setting/constants.tsx +++ b/web/src/pages/user-setting/constants.tsx @@ -6,6 +6,7 @@ import { ProfileIcon, TeamIcon, } from '@/assets/icon/Icon'; +import { IconFont } from '@/components/icon-font'; import { LLMFactory } from '@/constants/llm'; import { UserSettingRouteKey } from '@/constants/setting'; import { MonitorOutlined } from '@ant-design/icons'; @@ -18,6 +19,9 @@ export const UserSettingIconMap = { [UserSettingRouteKey.Team]: , [UserSettingRouteKey.Logout]: , [UserSettingRouteKey.Api]: , + [UserSettingRouteKey.MCP]: ( + + ), }; export * from '@/constants/setting'; diff --git a/web/src/routes.ts b/web/src/routes.ts index 2bfdd4e5a08..612ac2323ef 100644 --- a/web/src/routes.ts +++ b/web/src/routes.ts @@ -8,6 +8,7 @@ export enum Routes { Agent = '/agent', AgentTemplates = '/agent-templates', Agents = '/agents', + AgentList = '/agent-list', Searches = '/next-searches', Search = '/next-search', Chats = '/next-chats', @@ -53,6 +54,11 @@ const routes = [ component: '@/pages/chat/share', layout: false, }, + { + path: '/next-chat/share', + component: '@/pages/next-chats/share', + layout: false, + }, { path: '/', component: '@/layouts', @@ -135,6 +141,10 @@ const routes = [ path: '/user-setting/api', component: '@/pages/user-setting/setting-api', }, + { + path: `/user-setting${Routes.Mcp}`, + component: `@/pages${Routes.ProfileMcp}`, + }, ], }, { @@ -145,6 +155,10 @@ const routes = [ path: '/flow', component: '@/pages/flow/list', }, + { + path: Routes.AgentList, + component: `@/pages/${Routes.Agents}`, + }, { path: '/flow/:id', component: '@/pages/flow', diff --git a/web/src/services/agent-service.ts b/web/src/services/agent-service.ts index eaf1897ae0b..74fe0b48642 100644 --- a/web/src/services/agent-service.ts +++ b/web/src/services/agent-service.ts @@ -20,6 +20,7 @@ const { fetchVersionList, fetchVersion, fetchCanvas, + fetchAgentAvatar, } = api; const methods = { @@ -95,6 +96,10 @@ const methods = { url: inputForm, method: 'get', }, + fetchAgentAvatar: { + url: fetchAgentAvatar, + method: 'get', + }, } as const; const agentService = registerNextServer(methods); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 93272175da9..6e268b6c92f 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -151,6 +151,7 @@ export default { fetchVersionList: (id: string) => `${api_host}/canvas/getlistversion/${id}`, fetchVersion: (id: string) => `${api_host}/canvas/getversion/${id}`, fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`, + fetchAgentAvatar: (id: string) => `${api_host}/canvas/getsse/${id}`, // mcp server listMcpServer: `${api_host}/mcp_server/list`, From 7ebc1f0943b307d522fffe9dd8739e8d028f2ea1 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 23 Jul 2025 18:10:35 +0800 Subject: [PATCH 0236/1187] Feat: add model provider DeepInfra (#9003) ### What problem does this PR solve? Add model provider DeepInfra. This model list comes from our community. NOTE: most endpoints haven't been tested, but they should work as OpenAI does. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- conf/llm_factories.json | 293 +++++++++++++++++++++++++++ docs/references/supported_models.mdx | 1 + rag/llm/chat_model.py | 9 + rag/llm/embedding_model.py | 12 +- rag/llm/sequence2txt_model.py | 10 + rag/llm/tts_model.py | 9 + web/src/assets/svg/llm/deepinfra.svg | 1 + web/src/constants/llm.ts | 2 + 8 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 web/src/assets/svg/llm/deepinfra.svg diff --git a/conf/llm_factories.json b/conf/llm_factories.json index aa24de6ac7f..95f7720d006 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -3830,6 +3830,299 @@ "tags": "LLM,TEXT EMBEDDING,TTS,SPEECH2TEXT,TEXT RE-RANK", "status": "1", "llm": [] + }, + { + "name": "DeepInfra", + "logo": "", + "tags": "LLM,TEXT EMBEDDING,TTS,SPEECH2TEXT,MODERATION", + "status": "1", + "llm": [ + { + "llm_name": "moonshotai/Kimi-K2-Instruct", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "mistralai/Voxtral-Small-24B-2507", + "tags": "SPEECH2TEXT", + "model_type": "speech2text" + }, + { + "llm_name": "mistralai/Voxtral-Mini-3B-2507", + "tags": "SPEECH2TEXT", + "model_type": "speech2text" + }, + { + "llm_name": "deepseek-ai/DeepSeek-R1-0528-Turbo", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "Qwen/Qwen3-235B-A22B", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "Qwen/Qwen3-30B-A3B", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "Qwen/Qwen3-32B", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "Qwen/Qwen3-14B", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "deepseek-ai/DeepSeek-V3-0324-Turbo", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-Turbo", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "meta-llama/Llama-4-Scout-17B-16E-Instruct", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "deepseek-ai/DeepSeek-R1-0528", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "deepseek-ai/DeepSeek-V3-0324", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "mistralai/Devstral-Small-2507", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "mistralai/Mistral-Small-3.2-24B-Instruct-2506", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "meta-llama/Llama-Guard-4-12B", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "Qwen/QwQ-32B", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "anthropic/claude-4-opus", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "anthropic/claude-4-sonnet", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "google/gemini-2.5-flash", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "google/gemini-2.5-pro", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "google/gemma-3-27b-it", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "google/gemma-3-12b-it", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "google/gemma-3-4b-it", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "hexgrad/Kokoro-82M", + "tags": "TTS", + "model_type": "tts" + }, + { + "llm_name": "canopylabs/orpheus-3b-0.1-ft", + "tags": "TTS", + "model_type": "tts" + }, + { + "llm_name": "sesame/csm-1b", + "tags": "TTS", + "model_type": "tts" + }, + { + "llm_name": "microsoft/Phi-4-multimodal-instruct", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "deepseek-ai/DeepSeek-V3", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "meta-llama/Llama-3.3-70B-Instruct-Turbo", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "meta-llama/Llama-3.3-70B-Instruct", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "microsoft/phi-4", + "tags": "LLM,CHAT", + "model_type": "chat" + }, + { + "llm_name": "openai/whisper-large-v3-turbo", + "tags": "SPEECH2TEXT", + "model_type": "speech2text" + }, + { + "llm_name": "BAAI/bge-base-en-v1.5", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "BAAI/bge-en-icl", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "BAAI/bge-large-en-v1.5", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "BAAI/bge-m3", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "BAAI/bge-m3-multi", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "Qwen/Qwen3-Embedding-0.6B", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "Qwen/Qwen3-Embedding-4B", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "Qwen/Qwen3-Embedding-8B", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "intfloat/e5-base-v2", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "intfloat/e5-large-v2", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "intfloat/multilingual-e5-large", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "intfloat/multilingual-e5-large-instruct", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "sentence-transformers/all-MiniLM-L12-v2", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "sentence-transformers/all-MiniLM-L6-v2", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "sentence-transformers/all-mpnet-base-v2", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "sentence-transformers/clip-ViT-B-32", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "sentence-transformers/clip-ViT-B-32-multilingual-v1", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "sentence-transformers/multi-qa-mpnet-base-dot-v1", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "sentence-transformers/paraphrase-MiniLM-L6-v2", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "shibing624/text2vec-base-chinese", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "thenlper/gte-base", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + }, + { + "llm_name": "thenlper/gte-large", + "tags": "TEXT EMBEDDING", + "model_type": "embedding" + } + ] } ] } diff --git a/docs/references/supported_models.mdx b/docs/references/supported_models.mdx index 897dcff2114..472e5fbe4d6 100644 --- a/docs/references/supported_models.mdx +++ b/docs/references/supported_models.mdx @@ -62,6 +62,7 @@ A complete list of models supported by RAGFlow, which will continue to expand. | Youdao | | :heavy_check_mark: | :heavy_check_mark: | | | | | ZHIPU-AI | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | | | | 01.AI | :heavy_check_mark: | | | | | | +| DeepInfra | :heavy_check_mark: | :heavy_check_mark: | | | :heavy_check_mark: | :heavy_check_mark: | ```mdx-code-block diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 068871ef0db..ad8af6f3f2a 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -1682,3 +1682,12 @@ def __init__(self, key=None, model_name="", base_url="", **kwargs): raise ValueError("Local llm url cannot be None") base_url = urljoin(base_url, "v1") super().__init__(key, model_name, base_url, **kwargs) + + +class DeepInfraChat(Base): + _FACTORY_NAME = "DeepInfra" + + def __init__(self, key, model_name, base_url="https://api.deepinfra.com/v1/openai", **kwargs): + if not base_url: + base_url = "https://api.deepinfra.com/v1/openai" + super().__init__(key, model_name, base_url, **kwargs) diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 9774e7dd98d..5c99a413971 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -202,9 +202,10 @@ def __init__(self, key, model_name="text_embedding_v2", **kwargs): self.model_name = model_name def encode(self, texts: list): - import dashscope import time + import dashscope + batch_size = 4 res = [] token_count = 0 @@ -900,3 +901,12 @@ def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/embeddings if not base_url: base_url = "https://ai.gitee.com/v1/embeddings" super().__init__(key, model_name, base_url) + + +class DeepInfraEmbed(OpenAIEmbed): + _FACTORY_NAME = "DeepInfra" + + def __init__(self, key, model_name, base_url="https://api.deepinfra.com/v1/openai"): + if not base_url: + base_url = "https://api.deepinfra.com/v1/openai" + super().__init__(key, model_name, base_url) diff --git a/rag/llm/sequence2txt_model.py b/rag/llm/sequence2txt_model.py index 7d6c24b768f..1300144ad8e 100644 --- a/rag/llm/sequence2txt_model.py +++ b/rag/llm/sequence2txt_model.py @@ -208,3 +208,13 @@ def __init__(self, key, model_name="whisper-1", base_url="https://ai.gitee.com/v self.client = OpenAI(api_key=key, base_url=base_url) self.model_name = model_name + +class DeepInfraSeq2txt(Base): + _FACTORY_NAME = "DeepInfra" + + def __init__(self, key, model_name, base_url="https://api.deepinfra.com/v1/openai", **kwargs): + if not base_url: + base_url = "https://api.deepinfra.com/v1/openai" + + self.client = OpenAI(api_key=key, base_url=base_url) + self.model_name = model_name diff --git a/rag/llm/tts_model.py b/rag/llm/tts_model.py index b2333b426e0..f606405468d 100644 --- a/rag/llm/tts_model.py +++ b/rag/llm/tts_model.py @@ -382,3 +382,12 @@ def tts(self, text, voice="anna"): for chunk in response.iter_content(): if chunk: yield chunk + + +class DeepInfraTTS(OpenAITTS): + _FACTORY_NAME = "DeepInfra" + + def __init__(self, key, model_name, base_url="https://api.deepinfra.com/v1/openai", **kwargs): + if not base_url: + base_url = "https://api.deepinfra.com/v1/openai" + super().__init__(key, model_name, base_url, **kwargs) diff --git a/web/src/assets/svg/llm/deepinfra.svg b/web/src/assets/svg/llm/deepinfra.svg new file mode 100644 index 00000000000..565c8095b58 --- /dev/null +++ b/web/src/assets/svg/llm/deepinfra.svg @@ -0,0 +1 @@ +DeepInfra \ No newline at end of file diff --git a/web/src/constants/llm.ts b/web/src/constants/llm.ts index edcad4bafce..60e2aeb77ca 100644 --- a/web/src/constants/llm.ts +++ b/web/src/constants/llm.ts @@ -50,6 +50,7 @@ export enum LLMFactory { GPUStack = 'GPUStack', VLLM = 'VLLM', GiteeAI = 'GiteeAI', + DeepInfra = 'DeepInfra', } // Please lowercase the file name @@ -105,4 +106,5 @@ export const IconMap = { [LLMFactory.GPUStack]: 'gpustack', [LLMFactory.VLLM]: 'vllm', [LLMFactory.GiteeAI]: 'gitee-ai', + [LLMFactory.DeepInfra]: 'deepinfra', }; From a2f73af1a4d2260d95b52e2ae0b4bdae212866b9 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 23 Jul 2025 18:10:51 +0800 Subject: [PATCH 0237/1187] Fix: typo Bearer token (#8998) ### What problem does this PR solve? Typo Bearer token. #8960 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/embedding_model.py | 2 +- rag/llm/tts_model.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 5c99a413971..415e9b71228 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -283,7 +283,7 @@ class OllamaEmbed(Base): _special_tokens = ["<|endoftext|>"] def __init__(self, key, model_name, **kwargs): - self.client = Client(host=kwargs["base_url"]) if not key or key == "x" else Client(host=kwargs["base_url"], headers={"Authorization": f"Bear {key}"}) + self.client = Client(host=kwargs["base_url"]) if not key or key == "x" else Client(host=kwargs["base_url"], headers={"Authorization": f"Bearer {key}"}) self.model_name = model_name def encode(self, texts: list): diff --git a/rag/llm/tts_model.py b/rag/llm/tts_model.py index f606405468d..32e7064b3b1 100644 --- a/rag/llm/tts_model.py +++ b/rag/llm/tts_model.py @@ -314,7 +314,7 @@ def __init__(self, key, model_name="ollama-tts", base_url="https://api.ollama.ai self.base_url = base_url self.headers = {"Content-Type": "application/json"} if key and key != "x": - self.headers["Authorization"] = f"Bear {key}" + self.headers["Authorization"] = f"Bearer {key}" def tts(self, text, voice="standard-voice"): payload = {"model": self.model_name, "voice": voice, "input": text} From ad177951e947dd1d060805ca7ffe665de5261760 Mon Sep 17 00:00:00 2001 From: Zhichang Yu Date: Wed, 23 Jul 2025 19:27:57 +0800 Subject: [PATCH 0238/1187] Bump to infinity v0.6.0-dev4 (#9013) ### What problem does this PR solve? Bump to infinity v0.6.0-dev4. WARNNING: infinity v0.6.0-dev4 has very different meta data format with older versions. You have to destroy infinity data volume are restart infinity container if there's existing data. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- docker/docker-compose-base.yml | 2 +- docker/infinity_conf.toml | 13 ++----------- helm/values.yaml | 2 +- pyproject.toml | 2 +- uv.lock | 6 +++--- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index 7e339350603..73c1d54157f 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -77,7 +77,7 @@ services: container_name: ragflow-infinity profiles: - infinity - image: infiniflow/infinity:v0.6.0-dev3 + image: infiniflow/infinity:v0.6.0-dev4 volumes: - infinity_data:/var/infinity - ./infinity_conf.toml:/infinity_conf.toml diff --git a/docker/infinity_conf.toml b/docker/infinity_conf.toml index 38e8f460c0b..cc8a0dcde68 100644 --- a/docker/infinity_conf.toml +++ b/docker/infinity_conf.toml @@ -47,20 +47,11 @@ mem_index_capacity = 65536 buffer_manager_size = "8GB" lru_num = 7 temp_dir = "/var/infinity/tmp" -result_cache = "off" -memindex_memory_quota = "4GB" +result_cache = "on" +memindex_memory_quota = "1GB" [wal] wal_dir = "/var/infinity/wal" -full_checkpoint_interval = "30s" -delta_checkpoint_interval = "5s" -# delta_checkpoint_threshold = 1000000000 -wal_compact_threshold = "1GB" - -# flush_at_once: write and flush log each commit -# only_write: write log, OS control when to flush the log, default -# flush_per_second: logs are written after each commit and flushed to disk per second. -wal_flush = "only_write" [resource] resource_dir = "/var/infinity/resource" diff --git a/helm/values.yaml b/helm/values.yaml index b0d311c6ebb..2894a855900 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -113,7 +113,7 @@ ragflow: infinity: image: repository: infiniflow/infinity - tag: v0.6.0-dev3 + tag: v0.6.0-dev4 storage: className: capacity: 5Gi diff --git a/pyproject.toml b/pyproject.toml index 34121cedfb7..65297affc16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ dependencies = [ "html-text==0.6.2", "httpx==0.27.0", "huggingface-hub>=0.25.0,<0.26.0", - "infinity-sdk==0.6.0-dev3", + "infinity-sdk==0.6.0-dev4", "infinity-emb>=0.0.66,<0.0.67", "itsdangerous==2.1.2", "json-repair==0.35.0", diff --git a/uv.lock b/uv.lock index 7b9cf6b889c..3e550fba61a 100644 --- a/uv.lock +++ b/uv.lock @@ -2450,7 +2450,7 @@ wheels = [ [[package]] name = "infinity-sdk" -version = "0.6.0.dev3" +version = "0.6.0.dev4" source = { registry = "https://mirrors.aliyun.com/pypi/simple" } dependencies = [ { name = "numpy" }, @@ -2467,7 +2467,7 @@ dependencies = [ { name = "thrift" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fc/2c/5d93a6200e8022549d07c2609f89e81b96fe78383409881ca210f16736e1/infinity_sdk-0.6.0.dev3-py3-none-any.whl", hash = "sha256:e9f528446f21debbd1d15d11f42a5da4f38d62ad4537faacfdda6b2d295bf8fd" }, + { url = "https://mirrors.aliyun.com/pypi/packages/d4/cc/645ed8de15952940c7308a788036376583a5fc29fdcf3e4bc75b5ad0c881/infinity_sdk-0.6.0.dev4-py3-none-any.whl", hash = "sha256:f8f4bd8a44e3fae7b4228b5c9e9a16559b4905f50d2d7d0a3d18f39974613e7a" }, ] [[package]] @@ -5060,7 +5060,7 @@ requires-dist = [ { name = "httpx", specifier = "==0.27.0" }, { name = "huggingface-hub", specifier = ">=0.25.0,<0.26.0" }, { name = "infinity-emb", specifier = ">=0.0.66,<0.0.67" }, - { name = "infinity-sdk", specifier = "==0.6.0.dev3" }, + { name = "infinity-sdk", specifier = "==0.6.0.dev4" }, { name = "itsdangerous", specifier = "==2.1.2" }, { name = "json-repair", specifier = "==0.35.0" }, { name = "langfuse", specifier = ">=2.60.0" }, From 03e39ca9be72f9850464bfbc3ccb6a0c27b4f89c Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Thu, 24 Jul 2025 09:30:05 +0800 Subject: [PATCH 0239/1187] =?UTF-8?q?Fix=EF=BC=9AOptimize=20Agent=20templa?= =?UTF-8?q?te=20page,=20=20fix=20bugs=20in=20knowledge=20base=20(#9009)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? Replace Avatar with RAGFlowAvatar component for knowledge base and agent, optimize Agent template page, and modify bugs in knowledge base #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../components/entity-types-form-field.tsx | 8 --- web/src/components/knowledge-base-item.tsx | 10 +-- .../raptor-form-fields.tsx | 24 ++++--- web/src/hooks/llm-hooks.tsx | 16 +++-- web/src/locales/en.ts | 1 + web/src/locales/zh.ts | 1 + .../agent/canvas/node/retrieval-node.tsx | 13 ++-- web/src/pages/agents/agent-templates.tsx | 15 ++++- web/src/pages/agents/template-card.tsx | 64 +++++++++++-------- web/src/pages/agents/template-sidebar.tsx | 57 +++++++++++++++++ .../dataset/dataset/parsing-status-cell.tsx | 23 +++++-- web/src/pages/dataset/setting/hooks.ts | 13 +++- web/src/pages/dataset/setting/index.tsx | 4 -- web/src/pages/datasets/dataset-card.tsx | 11 ++-- .../pages/user-setting/setting-model/hooks.ts | 15 +++-- .../user-setting/setting-model/index.tsx | 33 +++++++--- .../setting-model/ollama-modal/index.tsx | 17 ++--- 17 files changed, 222 insertions(+), 103 deletions(-) create mode 100644 web/src/pages/agents/template-sidebar.tsx diff --git a/web/src/components/entity-types-form-field.tsx b/web/src/components/entity-types-form-field.tsx index ddf08cb0bed..6008b20b0d4 100644 --- a/web/src/components/entity-types-form-field.tsx +++ b/web/src/components/entity-types-form-field.tsx @@ -12,13 +12,6 @@ import { type EntityTypesFormFieldProps = { name?: string; }; -const initialEntityTypes = [ - 'organization', - 'person', - 'geo', - 'event', - 'category', -]; export function EntityTypesFormField({ name = 'parser_config.entity_types', }: EntityTypesFormFieldProps) { @@ -29,7 +22,6 @@ export function EntityTypesFormField({ { return ( diff --git a/web/src/components/knowledge-base-item.tsx b/web/src/components/knowledge-base-item.tsx index 7ae98e4d540..e01ff6e8071 100644 --- a/web/src/components/knowledge-base-item.tsx +++ b/web/src/components/knowledge-base-item.tsx @@ -3,9 +3,8 @@ import { useTranslate } from '@/hooks/common-hooks'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { UserOutlined } from '@ant-design/icons'; import { Avatar as AntAvatar, Form, Select, Space } from 'antd'; -import { Book } from 'lucide-react'; import { useFormContext } from 'react-hook-form'; -import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar'; +import { RAGFlowAvatar } from './ragflow-avatar'; import { FormControl, FormField, FormItem, FormLabel } from './ui/form'; import { MultiSelect } from './ui/multi-select'; @@ -81,12 +80,7 @@ export function KnowledgeBaseFormField() { label: x.name, value: x.id, icon: () => ( - - - - - - + ), })); diff --git a/web/src/components/parse-configuration/raptor-form-fields.tsx b/web/src/components/parse-configuration/raptor-form-fields.tsx index 8ee184e46a9..17d631ad34b 100644 --- a/web/src/components/parse-configuration/raptor-form-fields.tsx +++ b/web/src/components/parse-configuration/raptor-form-fields.tsx @@ -3,7 +3,7 @@ import { DocumentParserType } from '@/constants/knowledge'; import { useTranslate } from '@/hooks/common-hooks'; import random from 'lodash/random'; import { Plus } from 'lucide-react'; -import { useCallback } from 'react'; +import { useCallback, useEffect } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { SliderInputFormField } from '../slider-input-form-field'; import { Button } from '../ui/button'; @@ -46,6 +46,10 @@ export const showTagItems = (parserId: DocumentParserType) => { const UseRaptorField = 'parser_config.raptor.use_raptor'; const RandomSeedField = 'parser_config.raptor.random_seed'; +const MaxTokenField = 'parser_config.raptor.max_token'; +const ThresholdField = 'parser_config.raptor.threshold'; +const MaxCluster = 'parser_config.raptor.max_cluster'; +const Prompt = 'parser_config.raptor.prompt'; // The three types "table", "resume" and "one" do not display this configuration. @@ -53,6 +57,15 @@ const RaptorFormFields = () => { const form = useFormContext(); const { t } = useTranslate('knowledgeConfiguration'); const useRaptor = useWatch({ name: UseRaptorField }); + useEffect(() => { + if (useRaptor) { + form.setValue(MaxTokenField, 256); + form.setValue(ThresholdField, 0.1); + form.setValue(MaxCluster, 64); + form.setValue(RandomSeedField, 0); + form.setValue(Prompt, t('promptText')); + } + }, [form, useRaptor, t]); const handleGenerate = useCallback(() => { form.setValue(RandomSeedField, random(10000)); @@ -114,11 +127,7 @@ const RaptorFormFields = () => {
    - + diff --git a/web/src/pages/datasets/dataset-card.tsx b/web/src/pages/datasets/dataset-card.tsx index 0ddc99df654..0e074d5a8c7 100644 --- a/web/src/pages/datasets/dataset-card.tsx +++ b/web/src/pages/datasets/dataset-card.tsx @@ -72,10 +72,7 @@ export function SeeAllCard() { const { navigateToDatasetList } = useNavigatePage(); return ( - + See All diff --git a/web/src/pages/home/application-card.tsx b/web/src/pages/home/application-card.tsx index 63e97b971d5..fc118d5d78d 100644 --- a/web/src/pages/home/application-card.tsx +++ b/web/src/pages/home/application-card.tsx @@ -1,5 +1,5 @@ import { MoreButton } from '@/components/more-button'; -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { RAGFlowAvatar } from '@/components/ragflow-avatar'; import { Card, CardContent } from '@/components/ui/card'; import { formatDate } from '@/utils/date'; import { ChevronRight } from 'lucide-react'; @@ -17,10 +17,11 @@ export function ApplicationCard({ app }: ApplicationCardProps) {
    - - - CN - +

    {app.title} diff --git a/web/src/pages/home/header.tsx b/web/src/pages/home/header.tsx deleted file mode 100644 index f36516d08d8..00000000000 --- a/web/src/pages/home/header.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; -import { Button } from '@/components/ui/button'; -import { Container } from '@/components/ui/container'; -import { Segmented, SegmentedValue } from '@/components/ui/segmented'; -import { useTranslate } from '@/hooks/common-hooks'; -import { useNavigateWithFromState } from '@/hooks/route-hook'; -import { - ChevronDown, - Cpu, - Github, - Library, - MessageSquareText, - Search, - Star, - Zap, -} from 'lucide-react'; -import { useCallback, useMemo, useState } from 'react'; -import { useLocation } from 'umi'; - -export function HomeHeader() { - const { t } = useTranslate('header'); - const { pathname } = useLocation(); - const navigate = useNavigateWithFromState(); - const [currentPath, setCurrentPath] = useState('/home'); - - const tagsData = useMemo( - () => [ - { path: '/home', name: t('knowledgeBase'), icon: Library }, - { path: '/chat', name: t('chat'), icon: MessageSquareText }, - { path: '/search', name: t('search'), icon: Search }, - { path: '/flow', name: t('flow'), icon: Cpu }, - // { path: '/file', name: t('fileManager'), icon: FileIcon }, - ], - [t], - ); - - const options = useMemo(() => { - return tagsData.map((tag) => { - const HeaderIcon = tag.icon; - - return { - label: ( -
    - - {tag.name} -
    - ), - value: tag.path, - }; - }); - }, [tagsData]); - - // const currentPath = useMemo(() => { - // return tagsData.find((x) => pathname.startsWith(x.path))?.name || 'home'; - // }, [pathname, tagsData]); - - const handleChange = (path: SegmentedValue) => { - // navigate(path as string); - setCurrentPath(path as string); - }; - - const handleLogoClick = useCallback(() => { - navigate('/'); - }, [navigate]); - - return ( -
    -
    - logo - -
    -
    - -
    -
    - - V 0.13.0 - - - - - - CN - - yifanwu92@gmail.com - - -
    -
    - ); -} diff --git a/web/src/pages/next-search/index.tsx b/web/src/pages/next-search/index.tsx index 489067ee8f0..391f159350d 100644 --- a/web/src/pages/next-search/index.tsx +++ b/web/src/pages/next-search/index.tsx @@ -13,9 +13,7 @@ export default function SearchPage() { - +

    diff --git a/web/src/pages/next-searches/search-card.tsx b/web/src/pages/next-searches/search-card.tsx index 3f8188a636d..5b1e5a385aa 100644 --- a/web/src/pages/next-searches/search-card.tsx +++ b/web/src/pages/next-searches/search-card.tsx @@ -15,7 +15,7 @@ export function SearchCard({ data }: IProps) { const { navigateToSearch } = useNavigatePage(); return ( - +

    Model library

    - +
    {modelLibraryList.map((x, idx) => ( diff --git a/web/src/pages/profile-setting/model/model-card.tsx b/web/src/pages/profile-setting/model/model-card.tsx index d84fd891794..dad059298d7 100644 --- a/web/src/pages/profile-setting/model/model-card.tsx +++ b/web/src/pages/profile-setting/model/model-card.tsx @@ -104,7 +104,7 @@ export function AddModelCard() { -
    @@ -126,7 +126,7 @@ export function ModelLibraryCard() {

    LLM,TEXT EMBEDDING, SPEECH2TEXT, MODERATION

    -
    diff --git a/web/src/pages/profile-setting/plan/index.tsx b/web/src/pages/profile-setting/plan/index.tsx index ebfb0cad7d3..2da8dad378b 100644 --- a/web/src/pages/profile-setting/plan/index.tsx +++ b/web/src/pages/profile-setting/plan/index.tsx @@ -79,20 +79,20 @@ export default function Plan() { return (

    Plan & balance

    - +
    Balance $ 100.00
    The value equals to 1,000 tokens or 10.00 GBs of storage -
    - +
    Upgrade to access
    @@ -107,7 +107,7 @@ export default function Plan() { options={options} value={val} onChange={handleChange} - className="bg-colors-background-inverse-standard inline-flex" + className="inline-flex" >
    {pricingData.map((plan, index) => ( diff --git a/web/src/pages/profile-setting/plan/pricing-card.tsx b/web/src/pages/profile-setting/plan/pricing-card.tsx index 13354f631bb..2f0024cdf70 100644 --- a/web/src/pages/profile-setting/plan/pricing-card.tsx +++ b/web/src/pages/profile-setting/plan/pricing-card.tsx @@ -38,10 +38,7 @@ export function PricingCard({
    - + {isPro && } {isEnterprise && } {title} @@ -59,7 +56,6 @@ export function PricingCard({ )}
    diff --git a/web/src/pages/profile-setting/prompt/index.tsx b/web/src/pages/profile-setting/prompt/index.tsx index 7ae30ab998e..0c569f01c81 100644 --- a/web/src/pages/profile-setting/prompt/index.tsx +++ b/web/src/pages/profile-setting/prompt/index.tsx @@ -16,7 +16,7 @@ const PromptManagement = () => {

    Prompt templates

    - diff --git a/web/src/pages/profile-setting/team/index.tsx b/web/src/pages/profile-setting/team/index.tsx index d919b2f30f0..c04d03b602e 100644 --- a/web/src/pages/profile-setting/team/index.tsx +++ b/web/src/pages/profile-setting/team/index.tsx @@ -32,7 +32,7 @@ const TeamManagement = () => {

    Team management

    - @@ -46,7 +46,7 @@ const TeamManagement = () => {
    - +

    Project

    @@ -63,7 +63,7 @@ const TeamManagement = () => {
    - + {teamMembers.map((member, idx) => ( diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 06e51dc4c25..aab2b9455bc 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -178,10 +178,6 @@ module.exports = { DEFAULT: 'var(--colors-background-inverse-strong)', foreground: 'var(--background-inverse-standard-foreground)', }, - 'colors-background-inverse-weak': { - DEFAULT: 'var(--colors-background-inverse-weak)', - foreground: 'var(--background-inverse-standard-foreground)', - }, 'colors-background-neutral-standard': { DEFAULT: 'var(--colors-background-neutral-standard)', foreground: 'var(--background-inverse-standard-foreground)', diff --git a/web/tailwind.css b/web/tailwind.css index 2b92885b64a..dbcf7ebc14a 100644 --- a/web/tailwind.css +++ b/web/tailwind.css @@ -189,7 +189,6 @@ ); --colors-background-inverse-standard: rgba(230, 227, 246, 0.15); --colors-background-inverse-strong: rgba(255, 255, 255, 0.8); - --colors-background-inverse-weak: rgba(184, 181, 203, 0.15); --colors-background-neutral-standard: rgba(11, 10, 18, 1); --colors-background-neutral-strong: rgba(29, 26, 44, 1); --colors-background-neutral-weak: rgba(17, 16, 23, 1); From 07354f4a1a688fb2fa57703d13020f5ccf3235b1 Mon Sep 17 00:00:00 2001 From: YyBoom <13637682833@163.com> Date: Thu, 7 Aug 2025 18:06:49 +0800 Subject: [PATCH 0377/1187] Add files viaContribute a new workflow template: SQL Assistant upload (#9311) ### What problem does this PR solve? Contribute a new workflow template: SQL Assistant ### Type of change - [x] Other (please describe): new workflow template --- agent/templates/SQL Assistant.json | 721 +++++++++++++++++++++++++++++ 1 file changed, 721 insertions(+) create mode 100644 agent/templates/SQL Assistant.json diff --git a/agent/templates/SQL Assistant.json b/agent/templates/SQL Assistant.json new file mode 100644 index 00000000000..e7f68b615f3 --- /dev/null +++ b/agent/templates/SQL Assistant.json @@ -0,0 +1,721 @@ +{ + "id": 17, + "title": "SQL Assistant", + "description": "SQL Assistant is an AI-powered tool that lets business users turn plain-English questions into fully formed SQL queries. Simply type your question (e.g., “Show me last quarter’s top 10 products by revenue”) and SQL Assistant generates the exact SQL, runs it against your database, and returns the results in seconds. ", + "canvas_type": "Marketing", + "dsl": { + "components": { + "Agent:WickedGoatsDivide": { + "downstream": [ + "ExeSQL:TiredShirtsPull" + ], + "obj": { + "component_name": "Agent", + "params": { + "delay_after_error": 1, + "description": "", + "exception_default_value": "", + "exception_goto": [], + "exception_method": "", + "frequencyPenaltyEnabled": false, + "frequency_penalty": 0.7, + "llm_id": "qwen-max@Tongyi-Qianwen", + "maxTokensEnabled": false, + "max_retries": 3, + "max_rounds": 5, + "max_tokens": 256, + "mcp": [], + "message_history_window_size": 12, + "outputs": { + "content": { + "type": "string", + "value": "" + } + }, + "presencePenaltyEnabled": false, + "presence_penalty": 0.4, + "prompts": [ + { + "content": "User's query: {sys.query}\n\nSchema: {Retrieval:HappyTiesFilm@formalized_content}\n\nSamples about question to SQL: {Retrieval:SmartNewsHammer@formalized_content}\n\nDescription about meanings of tables and files: {Retrieval:SweetDancersAppear@formalized_content}", + "role": "user" + } + ], + "sys_prompt": "### ROLE\nYou are a Text-to-SQL assistant. \nGiven a relational database schema and a natural-language request, you must produce a **single, syntactically-correct MySQL query** that answers the request. \nReturn **nothing except the SQL statement itself**\u2014no code fences, no commentary, no explanations, no comments, no trailing semicolon if not required.\n\n\n### EXAMPLES \n-- Example 1 \nUser: List every product name and its unit price. \nSQL:\nSELECT name, unit_price FROM Products;\n\n-- Example 2 \nUser: Show the names and emails of customers who placed orders in January 2025. \nSQL:\nSELECT DISTINCT c.name, c.email\nFROM Customers c\nJOIN Orders o ON o.customer_id = c.id\nWHERE o.order_date BETWEEN '2025-01-01' AND '2025-01-31';\n\n-- Example 3 \nUser: How many orders have a status of \"Completed\" for each month in 2024? \nSQL:\nSELECT DATE_FORMAT(order_date, '%Y-%m') AS month,\n COUNT(*) AS completed_orders\nFROM Orders\nWHERE status = 'Completed'\n AND YEAR(order_date) = 2024\nGROUP BY month\nORDER BY month;\n\n-- Example 4 \nUser: Which products generated at least \\$10 000 in total revenue? \nSQL:\nSELECT p.id, p.name, SUM(oi.quantity * oi.unit_price) AS revenue\nFROM Products p\nJOIN OrderItems oi ON oi.product_id = p.id\nGROUP BY p.id, p.name\nHAVING revenue >= 10000\nORDER BY revenue DESC;\n\n\n### OUTPUT GUIDELINES\n1. Think through the schema and the request. \n2. Write **only** the final MySQL query. \n3. Do **not** wrap the query in back-ticks or markdown fences. \n4. Do **not** add explanations, comments, or additional text\u2014just the SQL.", + "temperature": 0.1, + "temperatureEnabled": false, + "tools": [], + "topPEnabled": false, + "top_p": 0.3, + "user_prompt": "", + "visual_files_var": "" + } + }, + "upstream": [ + "Retrieval:HappyTiesFilm", + "Retrieval:SmartNewsHammer", + "Retrieval:SweetDancersAppear" + ] + }, + "ExeSQL:TiredShirtsPull": { + "downstream": [ + "Message:ShaggyMasksAttend" + ], + "obj": { + "component_name": "ExeSQL", + "params": { + "database": "", + "db_type": "mysql", + "host": "", + "max_records": 1024, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + }, + "json": { + "type": "Array", + "value": [] + } + }, + "password": "", + "port": 3306, + "sql": "Agent:WickedGoatsDivide@content", + "username": "" + } + }, + "upstream": [ + "Agent:WickedGoatsDivide" + ] + }, + "Message:ShaggyMasksAttend": { + "downstream": [], + "obj": { + "component_name": "Message", + "params": { + "content": [ + "{ExeSQL:TiredShirtsPull@formalized_content}" + ] + } + }, + "upstream": [ + "ExeSQL:TiredShirtsPull" + ] + }, + "Retrieval:HappyTiesFilm": { + "downstream": [ + "Agent:WickedGoatsDivide" + ], + "obj": { + "component_name": "Retrieval", + "params": { + "cross_languages": [], + "empty_response": "", + "kb_ids": [ + "ed31364c727211f0bdb2bafe6e7908e6" + ], + "keywords_similarity_weight": 0.7, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + } + }, + "query": "sys.query", + "rerank_id": "", + "similarity_threshold": 0.2, + "top_k": 1024, + "top_n": 8, + "use_kg": false + } + }, + "upstream": [ + "begin" + ] + }, + "Retrieval:SmartNewsHammer": { + "downstream": [ + "Agent:WickedGoatsDivide" + ], + "obj": { + "component_name": "Retrieval", + "params": { + "cross_languages": [], + "empty_response": "", + "kb_ids": [ + "0f968106727311f08357bafe6e7908e6" + ], + "keywords_similarity_weight": 0.7, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + } + }, + "query": "sys.query", + "rerank_id": "", + "similarity_threshold": 0.2, + "top_k": 1024, + "top_n": 8, + "use_kg": false + } + }, + "upstream": [ + "begin" + ] + }, + "Retrieval:SweetDancersAppear": { + "downstream": [ + "Agent:WickedGoatsDivide" + ], + "obj": { + "component_name": "Retrieval", + "params": { + "cross_languages": [], + "empty_response": "", + "kb_ids": [ + "4ad1f9d0727311f0827dbafe6e7908e6" + ], + "keywords_similarity_weight": 0.7, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + } + }, + "query": "sys.query", + "rerank_id": "", + "similarity_threshold": 0.2, + "top_k": 1024, + "top_n": 8, + "use_kg": false + } + }, + "upstream": [ + "begin" + ] + }, + "begin": { + "downstream": [ + "Retrieval:HappyTiesFilm", + "Retrieval:SmartNewsHammer", + "Retrieval:SweetDancersAppear" + ], + "obj": { + "component_name": "Begin", + "params": { + "enablePrologue": true, + "inputs": {}, + "mode": "conversational", + "prologue": "Hi! I'm your SQL assistant, what can I do for you?" + } + }, + "upstream": [] + } + }, + "globals": { + "sys.conversation_turns": 0, + "sys.files": [], + "sys.query": "", + "sys.user_id": "" + }, + "graph": { + "edges": [ + { + "data": { + "isHovered": false + }, + "id": "xy-edge__beginstart-Retrieval:HappyTiesFilmend", + "source": "begin", + "sourceHandle": "start", + "target": "Retrieval:HappyTiesFilm", + "targetHandle": "end" + }, + { + "id": "xy-edge__beginstart-Retrieval:SmartNewsHammerend", + "source": "begin", + "sourceHandle": "start", + "target": "Retrieval:SmartNewsHammer", + "targetHandle": "end" + }, + { + "id": "xy-edge__beginstart-Retrieval:SweetDancersAppearend", + "source": "begin", + "sourceHandle": "start", + "target": "Retrieval:SweetDancersAppear", + "targetHandle": "end" + }, + { + "data": { + "isHovered": false + }, + "id": "xy-edge__Retrieval:HappyTiesFilmstart-Agent:WickedGoatsDivideend", + "source": "Retrieval:HappyTiesFilm", + "sourceHandle": "start", + "target": "Agent:WickedGoatsDivide", + "targetHandle": "end" + }, + { + "data": { + "isHovered": false + }, + "id": "xy-edge__Retrieval:SmartNewsHammerstart-Agent:WickedGoatsDivideend", + "markerEnd": "logo", + "source": "Retrieval:SmartNewsHammer", + "sourceHandle": "start", + "style": { + "stroke": "rgba(91, 93, 106, 1)", + "strokeWidth": 1 + }, + "target": "Agent:WickedGoatsDivide", + "targetHandle": "end", + "type": "buttonEdge", + "zIndex": 1001 + }, + { + "data": { + "isHovered": false + }, + "id": "xy-edge__Retrieval:SweetDancersAppearstart-Agent:WickedGoatsDivideend", + "markerEnd": "logo", + "source": "Retrieval:SweetDancersAppear", + "sourceHandle": "start", + "style": { + "stroke": "rgba(91, 93, 106, 1)", + "strokeWidth": 1 + }, + "target": "Agent:WickedGoatsDivide", + "targetHandle": "end", + "type": "buttonEdge", + "zIndex": 1001 + }, + { + "data": { + "isHovered": false + }, + "id": "xy-edge__Agent:WickedGoatsDividestart-ExeSQL:TiredShirtsPullend", + "source": "Agent:WickedGoatsDivide", + "sourceHandle": "start", + "target": "ExeSQL:TiredShirtsPull", + "targetHandle": "end" + }, + { + "data": { + "isHovered": false + }, + "id": "xy-edge__ExeSQL:TiredShirtsPullstart-Message:ShaggyMasksAttendend", + "source": "ExeSQL:TiredShirtsPull", + "sourceHandle": "start", + "target": "Message:ShaggyMasksAttend", + "targetHandle": "end" + } + ], + "nodes": [ + { + "data": { + "form": { + "enablePrologue": true, + "inputs": {}, + "mode": "conversational", + "prologue": "Hi! I'm your SQL assistant, what can I do for you?" + }, + "label": "Begin", + "name": "begin" + }, + "id": "begin", + "measured": { + "height": 48, + "width": 200 + }, + "position": { + "x": 50, + "y": 200 + }, + "selected": false, + "sourcePosition": "left", + "targetPosition": "right", + "type": "beginNode" + }, + { + "data": { + "form": { + "cross_languages": [], + "empty_response": "", + "kb_ids": [ + "ed31364c727211f0bdb2bafe6e7908e6" + ], + "keywords_similarity_weight": 0.7, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + } + }, + "query": "sys.query", + "rerank_id": "", + "similarity_threshold": 0.2, + "top_k": 1024, + "top_n": 8, + "use_kg": false + }, + "label": "Retrieval", + "name": "Schema" + }, + "dragging": false, + "id": "Retrieval:HappyTiesFilm", + "measured": { + "height": 96, + "width": 200 + }, + "position": { + "x": 414, + "y": 20.5 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "retrievalNode" + }, + { + "data": { + "form": { + "cross_languages": [], + "empty_response": "", + "kb_ids": [ + "0f968106727311f08357bafe6e7908e6" + ], + "keywords_similarity_weight": 0.7, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + } + }, + "query": "sys.query", + "rerank_id": "", + "similarity_threshold": 0.2, + "top_k": 1024, + "top_n": 8, + "use_kg": false + }, + "label": "Retrieval", + "name": "Question to SQL" + }, + "dragging": false, + "id": "Retrieval:SmartNewsHammer", + "measured": { + "height": 96, + "width": 200 + }, + "position": { + "x": 406.5, + "y": 175.5 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "retrievalNode" + }, + { + "data": { + "form": { + "cross_languages": [], + "empty_response": "", + "kb_ids": [ + "4ad1f9d0727311f0827dbafe6e7908e6" + ], + "keywords_similarity_weight": 0.7, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + } + }, + "query": "sys.query", + "rerank_id": "", + "similarity_threshold": 0.2, + "top_k": 1024, + "top_n": 8, + "use_kg": false + }, + "label": "Retrieval", + "name": "Database Description" + }, + "dragging": false, + "id": "Retrieval:SweetDancersAppear", + "measured": { + "height": 96, + "width": 200 + }, + "position": { + "x": 403.5, + "y": 328 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "retrievalNode" + }, + { + "data": { + "form": { + "delay_after_error": 1, + "description": "", + "exception_default_value": "", + "exception_goto": [], + "exception_method": "", + "frequencyPenaltyEnabled": false, + "frequency_penalty": 0.7, + "llm_id": "qwen-max@Tongyi-Qianwen", + "maxTokensEnabled": false, + "max_retries": 3, + "max_rounds": 5, + "max_tokens": 256, + "mcp": [], + "message_history_window_size": 12, + "outputs": { + "content": { + "type": "string", + "value": "" + } + }, + "presencePenaltyEnabled": false, + "presence_penalty": 0.4, + "prompts": [ + { + "content": "User's query: {sys.query}\n\nSchema: {Retrieval:HappyTiesFilm@formalized_content}\n\nSamples about question to SQL: {Retrieval:SmartNewsHammer@formalized_content}\n\nDescription about meanings of tables and files: {Retrieval:SweetDancersAppear@formalized_content}", + "role": "user" + } + ], + "sys_prompt": "### ROLE\nYou are a Text-to-SQL assistant. \nGiven a relational database schema and a natural-language request, you must produce a **single, syntactically-correct MySQL query** that answers the request. \nReturn **nothing except the SQL statement itself**\u2014no code fences, no commentary, no explanations, no comments, no trailing semicolon if not required.\n\n\n### EXAMPLES \n-- Example 1 \nUser: List every product name and its unit price. \nSQL:\nSELECT name, unit_price FROM Products;\n\n-- Example 2 \nUser: Show the names and emails of customers who placed orders in January 2025. \nSQL:\nSELECT DISTINCT c.name, c.email\nFROM Customers c\nJOIN Orders o ON o.customer_id = c.id\nWHERE o.order_date BETWEEN '2025-01-01' AND '2025-01-31';\n\n-- Example 3 \nUser: How many orders have a status of \"Completed\" for each month in 2024? \nSQL:\nSELECT DATE_FORMAT(order_date, '%Y-%m') AS month,\n COUNT(*) AS completed_orders\nFROM Orders\nWHERE status = 'Completed'\n AND YEAR(order_date) = 2024\nGROUP BY month\nORDER BY month;\n\n-- Example 4 \nUser: Which products generated at least \\$10 000 in total revenue? \nSQL:\nSELECT p.id, p.name, SUM(oi.quantity * oi.unit_price) AS revenue\nFROM Products p\nJOIN OrderItems oi ON oi.product_id = p.id\nGROUP BY p.id, p.name\nHAVING revenue >= 10000\nORDER BY revenue DESC;\n\n\n### OUTPUT GUIDELINES\n1. Think through the schema and the request. \n2. Write **only** the final MySQL query. \n3. Do **not** wrap the query in back-ticks or markdown fences. \n4. Do **not** add explanations, comments, or additional text\u2014just the SQL.", + "temperature": 0.1, + "temperatureEnabled": false, + "tools": [], + "topPEnabled": false, + "top_p": 0.3, + "user_prompt": "", + "visual_files_var": "" + }, + "label": "Agent", + "name": "SQL Generator " + }, + "dragging": false, + "id": "Agent:WickedGoatsDivide", + "measured": { + "height": 84, + "width": 200 + }, + "position": { + "x": 981, + "y": 174 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "agentNode" + }, + { + "data": { + "form": { + "database": "", + "db_type": "mysql", + "host": "", + "max_records": 1024, + "outputs": { + "formalized_content": { + "type": "string", + "value": "" + }, + "json": { + "type": "Array", + "value": [] + } + }, + "password": "", + "port": 3306, + "sql": "Agent:WickedGoatsDivide@content", + "username": "" + }, + "label": "ExeSQL", + "name": "ExeSQL_0" + }, + "dragging": false, + "id": "ExeSQL:TiredShirtsPull", + "measured": { + "height": 56, + "width": 200 + }, + "position": { + "x": 1211.5, + "y": 212.5 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "ragNode" + }, + { + "data": { + "form": { + "content": [ + "{ExeSQL:TiredShirtsPull@formalized_content}" + ] + }, + "label": "Message", + "name": "Message_0" + }, + "dragging": false, + "id": "Message:ShaggyMasksAttend", + "measured": { + "height": 56, + "width": 200 + }, + "position": { + "x": 1447.3125, + "y": 181.5 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "messageNode" + }, + { + "data": { + "form": { + "text": "Searches for relevant database creation statements.\n\nIt should label with a knowledgebase to which the schema is dumped in. You could use \" General \" as parsing method, \" 2 \" as chunk size and \" ; \" as delimiter." + }, + "label": "Note", + "name": "Note Schema" + }, + "dragHandle": ".note-drag-handle", + "dragging": false, + "height": 188, + "id": "Note:ThickClubsFloat", + "measured": { + "height": 188, + "width": 392 + }, + "position": { + "x": 689, + "y": -180.31251144409183 + }, + "resizing": false, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "noteNode", + "width": 392 + }, + { + "data": { + "form": { + "text": "Searches for samples about question to SQL. \n\nYou could use \" Q&A \" as parsing method.\n\nPlease check this dataset:\nhttps://huggingface.co/datasets/InfiniFlow/text2sql" + }, + "label": "Note", + "name": "Note: Question to SQL" + }, + "dragHandle": ".note-drag-handle", + "dragging": false, + "height": 154, + "id": "Note:ElevenLionsJoke", + "measured": { + "height": 154, + "width": 345 + }, + "position": { + "x": 693.5, + "y": 138 + }, + "resizing": false, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "noteNode", + "width": 345 + }, + { + "data": { + "form": { + "text": "Searches for description about meanings of tables and fields.\n\nYou could use \" General \" as parsing method, \" 2 \" as chunk size and \" ### \" as delimiter." + }, + "label": "Note", + "name": "Note: Database Description" + }, + "dragHandle": ".note-drag-handle", + "dragging": false, + "height": 158, + "id": "Note:ManyRosesTrade", + "measured": { + "height": 158, + "width": 408 + }, + "position": { + "x": 691.5, + "y": 435.69736389555317 + }, + "resizing": false, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "noteNode", + "width": 408 + }, + { + "data": { + "form": { + "text": "The Agent learns which tables may be available based on the responses from three knowledge bases and converts the user's input into SQL statements." + }, + "label": "Note", + "name": "Note: SQL Generator" + }, + "dragHandle": ".note-drag-handle", + "dragging": false, + "height": 132, + "id": "Note:RudeHousesInvite", + "measured": { + "height": 132, + "width": 383 + }, + "position": { + "x": 1106.9254833678003, + "y": 290.5891036507015 + }, + "resizing": false, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "noteNode", + "width": 383 + }, + { + "data": { + "form": { + "text": "Connect to your database to execute SQL statements." + }, + "label": "Note", + "name": "Note: SQL Executor" + }, + "dragHandle": ".note-drag-handle", + "dragging": false, + "id": "Note:HungryBatsLay", + "measured": { + "height": 136, + "width": 255 + }, + "position": { + "x": 1185, + "y": -30 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "noteNode" + } + ] + }, + "history": [], + "messages": [], + "path": [], + "retrieval": [] + }, + "avatar": "data:image/png;base64,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" +} \ No newline at end of file From 1bd64dafcbfa1c7a9a3a4c91f05884f204deb9f8 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Fri, 8 Aug 2025 10:00:16 +0800 Subject: [PATCH 0378/1187] Fix: update broken agent completion due to v0.20.0 changes (#9309) ### What problem does this PR solve? Update broken agent completion due to v0.20.0 changes. #9199 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/sdk/session.py | 83 ++++++++++++------------------- api/db/services/canvas_service.py | 2 +- 2 files changed, 34 insertions(+), 51 deletions(-) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 0476e35728e..a32d058896f 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -51,7 +51,7 @@ def create(tenant_id, chat_id): "name": req.get("name", "New session"), "message": [{"role": "assistant", "content": dia[0].prompt_config.get("prologue")}], "user_id": req.get("user_id", ""), - "reference":[{}], + "reference": [{}], } if not conv.get("name"): return get_error_data_result(message="`name` can not be empty.") @@ -475,41 +475,38 @@ def agents_completion_openai_compatibility(tenant_id, agent_id): @token_required def agent_completions(tenant_id, agent_id): req = request.json - cvs = UserCanvasService.query(user_id=tenant_id, id=agent_id) - if not cvs: - return get_error_data_result(f"You don't own the agent {agent_id}") - if req.get("session_id"): - dsl = cvs[0].dsl - if not isinstance(dsl, str): - dsl = json.dumps(dsl) - conv = API4ConversationService.query(id=req["session_id"], dialog_id=agent_id) - if not conv: - return get_error_data_result(f"You don't own the session {req['session_id']}") - # If an update to UserCanvas is detected, update the API4Conversation.dsl - sync_dsl = req.get("sync_dsl", False) - if sync_dsl is True and cvs[0].update_time > conv[0].update_time: - current_dsl = conv[0].dsl - new_dsl = json.loads(dsl) - state_fields = ["history", "messages", "path", "reference"] - states = {field: current_dsl.get(field, []) for field in state_fields} - current_dsl.update(new_dsl) - current_dsl.update(states) - API4ConversationService.update_by_id(req["session_id"], {"dsl": current_dsl}) - else: - req["question"] = "" + ans = {} if req.get("stream", True): - resp = Response(agent_completion(tenant_id, agent_id, **req), mimetype="text/event-stream") + + def generate(): + for answer in agent_completion(tenant_id=tenant_id, agent_id=agent_id, **req): + if isinstance(answer, str): + try: + ans = json.loads(answer[5:]) # remove "data:" + except Exception: + continue + + if ans.get("event") != "message": + continue + + yield answer + + yield "data:[DONE]\n\n" + + resp = Response(generate(), mimetype="text/event-stream") resp.headers.add_header("Cache-control", "no-cache") resp.headers.add_header("Connection", "keep-alive") resp.headers.add_header("X-Accel-Buffering", "no") resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") return resp - try: - for answer in agent_completion(tenant_id, agent_id, **req): - return get_result(data=answer) - except Exception as e: - return get_error_data_result(str(e)) + + for answer in agent_completion(tenant_id=tenant_id, agent_id=agent_id, **req): + try: + ans = json.loads(answer[5:]) # remove "data:" + except Exception as e: + return get_result(data=f"**ERROR**: {str(e)}") + return get_result(data=ans) @manager.route("/chats//sessions", methods=["GET"]) # noqa: F821 @@ -836,44 +833,30 @@ def chatbot_completions(dialog_id): @manager.route("/agentbots//completions", methods=["POST"]) # noqa: F821 -def agent_bot_completions(agent_id): +@token_required +def agent_bot_completions(tenant_id, agent_id): req = request.json - token = request.headers.get("Authorization").split() - if len(token) != 2: - return get_error_data_result(message='Authorization is not valid!"') - token = token[1] - objs = APIToken.query(beta=token) - if not objs: - return get_error_data_result(message='Authentication error: API key is invalid!"') - if req.get("stream", True): - resp = Response(agent_completion(objs[0].tenant_id, agent_id, **req), mimetype="text/event-stream") + resp = Response(agent_completion(tenant_id, agent_id, **req), mimetype="text/event-stream") resp.headers.add_header("Cache-control", "no-cache") resp.headers.add_header("Connection", "keep-alive") resp.headers.add_header("X-Accel-Buffering", "no") resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") return resp - for answer in agent_completion(objs[0].tenant_id, agent_id, **req): + for answer in agent_completion(tenant_id, agent_id, **req): return get_result(data=answer) @manager.route("/agentbots//inputs", methods=["GET"]) # noqa: F821 -def begin_inputs(agent_id): - token = request.headers.get("Authorization").split() - if len(token) != 2: - return get_error_data_result(message='Authorization is not valid!"') - token = token[1] - objs = APIToken.query(beta=token) - if not objs: - return get_error_data_result(message='Authentication error: API key is invalid!"') - +@token_required +def begin_inputs(tenant_id, agent_id): e, cvs = UserCanvasService.get_by_id(agent_id) if not e: return get_error_data_result(f"Can't find agent by ID: {agent_id}") - canvas = Canvas(json.dumps(cvs.dsl), objs[0].tenant_id) + canvas = Canvas(json.dumps(cvs.dsl), tenant_id) return get_result( data={ "title": cvs.title, diff --git a/api/db/services/canvas_service.py b/api/db/services/canvas_service.py index f791edf7ace..b15c12007ed 100644 --- a/api/db/services/canvas_service.py +++ b/api/db/services/canvas_service.py @@ -123,7 +123,7 @@ def get_by_tenant_ids(cls, joined_tenant_ids, user_id, def completion(tenant_id, agent_id, session_id=None, **kwargs): - query = kwargs.get("query", "") + query = kwargs.get("query", "") or kwargs.get("question", "") files = kwargs.get("files", []) inputs = kwargs.get("inputs", {}) user_id = kwargs.get("user_id", "") From 58a64000ea2850fdab8611baee19bb55e5d02fbb Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 8 Aug 2025 11:00:55 +0800 Subject: [PATCH 0379/1187] Feat: Render agent setting dialog #3221 (#9312) ### What problem does this PR solve? Feat: Render agent setting dialog #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/ragflow-form.tsx | 44 +++++ web/src/components/shared-badge.tsx | 16 ++ web/src/components/ui/radio-group.tsx | 39 ++--- web/src/hooks/use-agent-request.ts | 28 ++++ web/src/interfaces/database/flow.ts | 2 +- web/src/pages/agent/index.tsx | 21 ++- web/src/pages/agent/setting-dialog/index.tsx | 53 ++++++ .../agent/setting-dialog/setting-form.tsx | 158 ++++++++++++++++++ web/src/pages/agents/agent-card.tsx | 2 + web/tailwind.config.js | 1 + 10 files changed, 339 insertions(+), 25 deletions(-) create mode 100644 web/src/components/ragflow-form.tsx create mode 100644 web/src/components/shared-badge.tsx create mode 100644 web/src/pages/agent/setting-dialog/index.tsx create mode 100644 web/src/pages/agent/setting-dialog/setting-form.tsx diff --git a/web/src/components/ragflow-form.tsx b/web/src/components/ragflow-form.tsx new file mode 100644 index 00000000000..4882227147d --- /dev/null +++ b/web/src/components/ragflow-form.tsx @@ -0,0 +1,44 @@ +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { ReactNode, cloneElement, isValidElement } from 'react'; +import { ControllerRenderProps, useFormContext } from 'react-hook-form'; + +type RAGFlowFormItemProps = { + name: string; + label: ReactNode; + tooltip?: ReactNode; + children: ReactNode | ((field: ControllerRenderProps) => ReactNode); +}; + +export function RAGFlowFormItem({ + name, + label, + tooltip, + children, +}: RAGFlowFormItemProps) { + const form = useFormContext(); + return ( + ( + + {label} + + {typeof children === 'function' + ? children(field) + : isValidElement(children) + ? cloneElement(children, { ...field }) + : children} + + + + )} + /> + ); +} diff --git a/web/src/components/shared-badge.tsx b/web/src/components/shared-badge.tsx new file mode 100644 index 00000000000..a083792575d --- /dev/null +++ b/web/src/components/shared-badge.tsx @@ -0,0 +1,16 @@ +import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; +import { PropsWithChildren } from 'react'; + +export function SharedBadge({ children }: PropsWithChildren) { + const { data: userInfo } = useFetchUserInfo(); + + if (typeof children === 'string' && userInfo.nickname === children) { + return null; + } + + return ( + + {children} + + ); +} diff --git a/web/src/components/ui/radio-group.tsx b/web/src/components/ui/radio-group.tsx index f7e403a0fe8..f1e246ba787 100644 --- a/web/src/components/ui/radio-group.tsx +++ b/web/src/components/ui/radio-group.tsx @@ -1,44 +1,45 @@ 'use client'; import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'; -import { Circle } from 'lucide-react'; +import { CircleIcon } from 'lucide-react'; import * as React from 'react'; import { cn } from '@/lib/utils'; -const RadioGroup = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { +function RadioGroup({ + className, + ...props +}: React.ComponentProps) { return ( ); -}); -RadioGroup.displayName = RadioGroupPrimitive.Root.displayName; +} -const RadioGroupItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { +function RadioGroupItem({ + className, + ...props +}: React.ComponentProps) { return ( - - + + ); -}); -RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName; +} export { RadioGroup, RadioGroupItem }; diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index 40d92ab13ba..fb4015ba71f 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -48,6 +48,7 @@ export const enum AgentApiAction { FetchVersion = 'fetchVersion', FetchAgentAvatar = 'fetchAgentAvatar', FetchExternalAgentInputs = 'fetchExternalAgentInputs', + SetAgentSetting = 'setAgentSetting', } export const EmptyDsl = { @@ -613,3 +614,30 @@ export const useFetchExternalAgentInputs = () => { return { data, loading, refetch }; }; + +export const useSetAgentSetting = () => { + const { id } = useParams(); + const queryClient = useQueryClient(); + + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [AgentApiAction.SetAgentSetting], + mutationFn: async (params: any) => { + const ret = await agentService.settingCanvas({ id, ...params }); + if (ret?.data?.code === 0) { + message.success('success'); + queryClient.invalidateQueries({ + queryKey: [AgentApiAction.FetchAgentDetail], + }); + } else { + message.error(ret?.data?.data); + } + return ret?.data?.code; + }, + }); + + return { data, loading, setAgentSetting: mutateAsync }; +}; diff --git a/web/src/interfaces/database/flow.ts b/web/src/interfaces/database/flow.ts index 2d4aa4cbda2..95968114e31 100644 --- a/web/src/interfaces/database/flow.ts +++ b/web/src/interfaces/database/flow.ts @@ -32,7 +32,7 @@ export declare interface IFlow { canvas_type: null; create_date: string; create_time: number; - description: null; + description: string; dsl: DSL; id: string; title: string; diff --git a/web/src/pages/agent/index.tsx b/web/src/pages/agent/index.tsx index fc776914eac..e8017430cd7 100644 --- a/web/src/pages/agent/index.tsx +++ b/web/src/pages/agent/index.tsx @@ -27,6 +27,7 @@ import { LaptopMinimalCheck, Logs, ScreenShare, + Settings, Upload, } from 'lucide-react'; import { ComponentPropsWithoutRef, useCallback } from 'react'; @@ -43,6 +44,7 @@ import { useWatchAgentChange, } from './hooks/use-save-graph'; import { useShowEmbedModal } from './hooks/use-show-dialog'; +import { SettingDialog } from './setting-dialog'; import { UploadAgentDialog } from './upload-agent-dialog'; import { useAgentHistoryManager } from './use-agent-history-manager'; import { VersionDialog } from './version-dialog'; @@ -92,6 +94,12 @@ export default function Agent() { showModal: showVersionDialog, } = useSetModalState(); + const { + visible: settingDialogVisible, + hideModal: hideSettingDialog, + showModal: showSettingDialog, + } = useSetModalState(); + const { showEmbedModal, hideEmbedModal, embedVisible, beta } = useShowEmbedModal(); const { navigateToAgentLogs } = useNavigatePage(); @@ -149,11 +157,6 @@ export default function Agent() { - {/* - - API - */} - {/* */} {t('flow.import')} @@ -163,6 +166,11 @@ export default function Agent() { {t('flow.export')} + + + + {t('flow.setting')} + {location.hostname !== 'demo.ragflow.io' && ( <> @@ -201,6 +209,9 @@ export default function Agent() { {versionDialogVisible && ( )} + {settingDialogVisible && ( + + )} ); } diff --git a/web/src/pages/agent/setting-dialog/index.tsx b/web/src/pages/agent/setting-dialog/index.tsx new file mode 100644 index 00000000000..6d0e1e97680 --- /dev/null +++ b/web/src/pages/agent/setting-dialog/index.tsx @@ -0,0 +1,53 @@ +import { ButtonLoading } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { useSetAgentSetting } from '@/hooks/use-agent-request'; +import { IModalProps } from '@/interfaces/common'; +import { transformFile2Base64 } from '@/utils/file-util'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + AgentSettingId, + SettingForm, + SettingFormSchemaType, +} from './setting-form'; + +export function SettingDialog({ hideModal }: IModalProps) { + const { t } = useTranslation(); + const { setAgentSetting } = useSetAgentSetting(); + + const submit = useCallback( + async (values: SettingFormSchemaType) => { + const avatar = values.avatar; + const code = await setAgentSetting({ + ...values, + avatar: avatar.length > 0 ? await transformFile2Base64(avatar[0]) : '', + }); + if (code === 0) { + hideModal?.(); + } + }, + [hideModal, setAgentSetting], + ); + + return ( + + + + Are you absolutely sure? + + + + + {t('common.save')} + + + + + ); +} diff --git a/web/src/pages/agent/setting-dialog/setting-form.tsx b/web/src/pages/agent/setting-dialog/setting-form.tsx new file mode 100644 index 00000000000..d4fe0c07be2 --- /dev/null +++ b/web/src/pages/agent/setting-dialog/setting-form.tsx @@ -0,0 +1,158 @@ +import { z } from 'zod'; + +import { + FileUpload, + FileUploadDropzone, + FileUploadItem, + FileUploadItemDelete, + FileUploadItemMetadata, + FileUploadItemPreview, + FileUploadList, + FileUploadTrigger, +} from '@/components/file-upload'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Button } from '@/components/ui/button'; +import { Form, FormControl, FormItem, FormLabel } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +import { Textarea } from '@/components/ui/textarea'; +import { useTranslate } from '@/hooks/common-hooks'; +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { transformBase64ToFile } from '@/utils/file-util'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { CloudUpload, X } from 'lucide-react'; +import { useEffect } from 'react'; +import { useForm } from 'react-hook-form'; + +const formSchema = z.object({ + title: z.string().min(1, {}), + avatar: z.array(z.custom()), + description: z.string(), + permission: z.string(), +}); + +export type SettingFormSchemaType = z.infer; + +export const AgentSettingId = 'agentSettingId'; + +type SettingFormProps = { + submit: (values: SettingFormSchemaType) => void; +}; + +export function SettingForm({ submit }: SettingFormProps) { + const { t } = useTranslate('flow.settings'); + const { data } = useFetchAgent(); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + title: '', + permission: 'me', + }, + }); + + useEffect(() => { + form.reset({ + title: data?.title, + description: data?.description, + avatar: data.avatar ? [transformBase64ToFile(data.avatar)] : [], + permission: data?.permission, + }); + }, [data, form]); + + return ( + + + + + + + {(field) => ( + { + form.setError('avatar', { + message, + }); + }} + multiple + > + + + Drag and drop or + + + + to upload + + + {field.value?.map((file: File, index: number) => ( + + + + + + + + ))} + + + )} + + + + + + + )} + /> + ( + + {t('emptyResponse')} + + + + + + )} + /> + ( + + {t('setAnOpener')} + + + + + + )} + /> + + + + + ); } diff --git a/web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx b/web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx index 92c79c122fb..c8d54d03af5 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-model-settings.tsx @@ -9,35 +9,31 @@ import { import { Textarea } from '@/components/ui/textarea'; import { useTranslate } from '@/hooks/common-hooks'; import { useFormContext } from 'react-hook-form'; -import { Subhead } from './subhead'; export function ChatModelSettings() { const { t } = useTranslate('chat'); const form = useFormContext(); return ( -
    - Model Setting -
    - ( - - {t('system')} - - diff --git a/web/src/pages/agent/form/components/output.tsx b/web/src/pages/agent/form/components/output.tsx index 8d8aabde92a..c481c7a3884 100644 --- a/web/src/pages/agent/form/components/output.tsx +++ b/web/src/pages/agent/form/components/output.tsx @@ -1,3 +1,5 @@ +import { t } from 'i18next'; + export type OutputType = { title: string; type?: string; @@ -17,7 +19,7 @@ export function transferOutputs(outputs: Record) { export function Output({ list }: OutputProps) { return (
    -
    Output
    +
    {t('flow.output')}
      {list.map((x, idx) => (
    • ( {label || ( - + {t('flow.query')} )} diff --git a/web/src/pages/agent/form/exesql-form/index.tsx b/web/src/pages/agent/form/exesql-form/index.tsx index a0f05a41cf0..5a6cb5ba693 100644 --- a/web/src/pages/agent/form/exesql-form/index.tsx +++ b/web/src/pages/agent/form/exesql-form/index.tsx @@ -132,7 +132,7 @@ export function ExeSQLFormWidgets({ loading }: { loading: boolean }) {
      - Test + {t('test')}
      diff --git a/web/src/pages/agent/form/google-form/index.tsx b/web/src/pages/agent/form/google-form/index.tsx index 01cdc3b1017..cec5ae4c732 100644 --- a/web/src/pages/agent/form/google-form/index.tsx +++ b/web/src/pages/agent/form/google-form/index.tsx @@ -99,13 +99,13 @@ const GoogleForm = ({ node }: INextOperatorForm) => { - + ( - {t('start')} + {t('flowStart')} @@ -118,7 +118,7 @@ const GoogleForm = ({ node }: INextOperatorForm) => { name={`num`} render={({ field }) => ( - {t('num')} + {t('flowNum')} diff --git a/web/src/pages/agent/form/invoke-form/variable-dialog.tsx b/web/src/pages/agent/form/invoke-form/variable-dialog.tsx index 8239971c826..03c4d83b037 100644 --- a/web/src/pages/agent/form/invoke-form/variable-dialog.tsx +++ b/web/src/pages/agent/form/invoke-form/variable-dialog.tsx @@ -134,7 +134,7 @@ export function VariableDialog({ > diff --git a/web/src/pages/agent/form/invoke-form/variable-table.tsx b/web/src/pages/agent/form/invoke-form/variable-table.tsx index 9a57bd4c074..33a74767002 100644 --- a/web/src/pages/agent/form/invoke-form/variable-table.tsx +++ b/web/src/pages/agent/form/invoke-form/variable-table.tsx @@ -61,7 +61,7 @@ export function VariableTable({ const columns: ColumnDef[] = [ { accessorKey: 'key', - header: 'key', + header: t('flow.key'), meta: { cellClassName: 'max-w-30' }, cell: ({ row }) => { const key: string = row.getValue('key'); diff --git a/web/src/pages/agent/form/iteration-form/dynamic-output.tsx b/web/src/pages/agent/form/iteration-form/dynamic-output.tsx index 18a71f10710..c31be8fd062 100644 --- a/web/src/pages/agent/form/iteration-form/dynamic-output.tsx +++ b/web/src/pages/agent/form/iteration-form/dynamic-output.tsx @@ -12,6 +12,7 @@ import { import { Input } from '@/components/ui/input'; import { Separator } from '@/components/ui/separator'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { t } from 'i18next'; import { X } from 'lucide-react'; import { ReactNode, useCallback, useMemo } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; @@ -107,7 +108,7 @@ export function DynamicOutputForm({ node }: IProps) { ); })} append({ name: '', ref: undefined })}> - Add + {t('common.add')}
    ); @@ -120,7 +121,7 @@ export function VariableTitle({ title }: { title: ReactNode }) { export function DynamicOutput({ node }: IProps) { return ( - + ); diff --git a/web/src/pages/agent/form/retrieval-form/next.tsx b/web/src/pages/agent/form/retrieval-form/next.tsx index 03ae60a2505..425d08e4b3f 100644 --- a/web/src/pages/agent/form/retrieval-form/next.tsx +++ b/web/src/pages/agent/form/retrieval-form/next.tsx @@ -104,7 +104,7 @@ function RetrievalForm({ node }: INextOperatorForm) { - Advanced Settings}> + {t('flow.advancedSettings')}}> ({ label: key, value: val }), + ([key, val]) => ({ label: t('flow.' + toLower(key)), value: val }), ); function StringTransformForm({ node }: INextOperatorForm) { @@ -84,11 +85,13 @@ function StringTransformForm({ node }: INextOperatorForm) { name="method" render={({ field }) => ( - method + {t('flow.method')} ({ label: t('flow.' + val), value: val }), + )} onChange={(value) => { handleMethodChange(value); field.onChange(value); @@ -111,7 +114,7 @@ function StringTransformForm({ node }: INextOperatorForm) { name="script" render={({ field }) => ( - script + {t('flow.script')} @@ -125,7 +128,7 @@ function StringTransformForm({ node }: INextOperatorForm) { name="delimiters" render={({ field }) => ( - delimiters + {t('flow.delimiters')} {isSplit ? ( append({ operator: switchOperatorOptions[0].value })} > - Add + {t('common.add')}
    @@ -268,7 +269,7 @@ function SwitchForm({ node }: IOperatorForm) { className="-translate-y-1" onClick={() => remove(index)} > - Remove + {t('common.remove')} )} @@ -317,7 +318,7 @@ function SwitchForm({ node }: IOperatorForm) { }) } > - Add + {t('common.add')} diff --git a/web/src/pages/agent/form/tavily-extract-form/index.tsx b/web/src/pages/agent/form/tavily-extract-form/index.tsx index 87c8ae75e2a..45a9e0fe718 100644 --- a/web/src/pages/agent/form/tavily-extract-form/index.tsx +++ b/web/src/pages/agent/form/tavily-extract-form/index.tsx @@ -10,6 +10,7 @@ import { import { RAGFlowSelect } from '@/components/ui/select'; import { buildOptions } from '@/utils/form'; import { zodResolver } from '@hookform/resolvers/zod'; +import { t } from 'i18next'; import { memo } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; @@ -79,12 +80,12 @@ function TavilyExtractForm({ node }: INextOperatorForm) { name="extract_depth" render={({ field }) => ( - Extract Depth + {t('flow.extractDepth')} @@ -96,7 +97,7 @@ function TavilyExtractForm({ node }: INextOperatorForm) { name="format" render={({ field }) => ( - Format + {t('flow.format')} { ))} - append({ value: '' })}>Add + append({ value: '' })}> + {t('common.add')} + ); }; diff --git a/web/src/pages/agent/form/tavily-form/index.tsx b/web/src/pages/agent/form/tavily-form/index.tsx index e1acae98b43..afa8d859b42 100644 --- a/web/src/pages/agent/form/tavily-form/index.tsx +++ b/web/src/pages/agent/form/tavily-form/index.tsx @@ -12,6 +12,7 @@ import { RAGFlowSelect } from '@/components/ui/select'; import { Switch } from '@/components/ui/switch'; import { buildOptions } from '@/utils/form'; import { zodResolver } from '@hookform/resolvers/zod'; +import { t } from 'i18next'; import { memo } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; @@ -74,12 +75,12 @@ function TavilyForm({ node }: INextOperatorForm) { name="search_depth" render={({ field }) => ( - Search Depth + {t('flow.searchDepth')} @@ -91,12 +92,12 @@ function TavilyForm({ node }: INextOperatorForm) { name="topic" render={({ field }) => ( - TavilyTopic + {t('flow.tavilyTopic')} @@ -108,7 +109,7 @@ function TavilyForm({ node }: INextOperatorForm) { name="max_results" render={({ field }) => ( - Max Results + {t('flow.maxResults')} @@ -121,7 +122,7 @@ function TavilyForm({ node }: INextOperatorForm) { name="days" render={({ field }) => ( - Days + {t('flow.days')} @@ -134,7 +135,7 @@ function TavilyForm({ node }: INextOperatorForm) { name="include_answer" render={({ field }) => ( - Include Answer + {t('flow.includeAnswer')} ( - Include Raw Content + {t('flow.includeRawContent')} ( - Include Images + {t('flow.includeImages')} ( - Include Image Descriptions + {t('flow.includeImageDescriptions')} diff --git a/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx b/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx index 9ae19549287..bf187a6bf71 100644 --- a/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx +++ b/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx @@ -8,6 +8,7 @@ import { TopNFormField } from '@/components/top-n-item'; import { Form } from '@/components/ui/form'; import { UseKnowledgeGraphFormField } from '@/components/use-knowledge-graph-item'; import { zodResolver } from '@hookform/resolvers/zod'; +import { t } from 'i18next'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { DescriptionField } from '../../components/description-field'; @@ -41,7 +42,7 @@ const RetrievalForm = () => { - Advanced Settings}> + {t('flow.advancedSettings')}}> ( - Guiding Question + {t('flow.guidingQuestion')} ( - Message + + {t('flow.msg')} + + + + + )} + /> + ), + [BeginQueryType.Options]: ( + ( + + {props.label} + + ({ + label: x, + value: x as string, + })) ?? [] + } + {...field} + > + + + + )} + /> + ), + [BeginQueryType.File]: ( + + ( +
    + + {t('assistantAvatar')} + + + + + +
    + )} + /> +
    + ), + [BeginQueryType.Integer]: ( + ( + + {props.label} + + + + + + )} + /> + ), + [BeginQueryType.Boolean]: ( + ( + + {props.label} + + + + + + )} + /> + ), + }; + + return ( + BeginQueryTypeMap[q.type as BeginQueryType] ?? + BeginQueryTypeMap[BeginQueryType.Paragraph] + ); + }, + [form, t], + ); + + const onSubmit = useCallback( + (values: z.infer) => { + const nextValues = Object.entries(values).map(([key, value]) => { + const item = parameters[Number(key)]; + return { ...item, value }; + }); + + ok(nextValues); + }, + [formSchemaValues, ok, parameters], + ); + return ( + <> +
    + {message?.data?.tips &&
    {message.data.tips}
    } +
    + + {parameters.map((x, idx) => { + return
    {renderWidget(x, idx.toString())}
    ; + })} +
    + + {btnText || t(isNext ? 'common.next' : 'flow.run')} + +
    + + +
    + + ); +}; + +export default DebugContent; diff --git a/web/src/pages/data-flow/debug-content/popover-form.tsx b/web/src/pages/data-flow/debug-content/popover-form.tsx new file mode 100644 index 00000000000..9465d903b46 --- /dev/null +++ b/web/src/pages/data-flow/debug-content/popover-form.tsx @@ -0,0 +1,103 @@ +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Popover, PopoverContent } from '@/components/ui/popover'; +import { useParseDocument } from '@/hooks/document-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { PropsWithChildren } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +const reg = + /^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/; + +const FormSchema = z.object({ + url: z.string(), + result: z.any(), +}); + +const values = { + url: '', + result: null, +}; + +export const PopoverForm = ({ + children, + visible, + switchVisible, +}: PropsWithChildren>) => { + const form = useForm({ + defaultValues: values, + resolver: zodResolver(FormSchema), + }); + const { parseDocument, loading } = useParseDocument(); + const { t } = useTranslation(); + + // useResetFormOnCloseModal({ + // form, + // visible, + // }); + + async function onSubmit(values: z.infer) { + const val = values.url; + + if (reg.test(val)) { + const ret = await parseDocument(val); + if (ret?.data?.code === 0) { + form.setValue('result', ret?.data?.data); + } + } + } + + const content = ( +
    + + ( + + + e.preventDefault()} + placeholder={t('flow.pasteFileLink')} + // suffix={ + // + // } + /> + + + + )} + /> + <>} + /> + + + ); + + return ( + + {children} + {content} + + ); +}; diff --git a/web/src/pages/data-flow/debug-content/uploader.tsx b/web/src/pages/data-flow/debug-content/uploader.tsx new file mode 100644 index 00000000000..e11a6f41d31 --- /dev/null +++ b/web/src/pages/data-flow/debug-content/uploader.tsx @@ -0,0 +1,116 @@ +'use client'; + +import { + FileUpload, + FileUploadDropzone, + FileUploadItem, + FileUploadItemDelete, + FileUploadItemMetadata, + FileUploadItemPreview, + FileUploadItemProgress, + FileUploadList, + FileUploadTrigger, + type FileUploadProps, +} from '@/components/file-upload'; +import { Button } from '@/components/ui/button'; +import { useUploadCanvasFile } from '@/hooks/use-agent-request'; +import { Upload, X } from 'lucide-react'; +import * as React from 'react'; +import { toast } from 'sonner'; + +type FileUploadDirectUploadProps = { + value: Record; + onChange(value: Record): void; +}; + +export function FileUploadDirectUpload({ + onChange, +}: FileUploadDirectUploadProps) { + const [files, setFiles] = React.useState([]); + + const { uploadCanvasFile } = useUploadCanvasFile(); + + const onUpload: NonNullable = React.useCallback( + async (files, { onSuccess, onError }) => { + try { + const uploadPromises = files.map(async (file) => { + const handleError = (error?: any) => { + onError( + file, + error instanceof Error ? error : new Error('Upload failed'), + ); + }; + try { + const ret = await uploadCanvasFile([file]); + if (ret.code === 0) { + onSuccess(file); + onChange(ret.data); + } else { + handleError(); + } + } catch (error) { + handleError(error); + } + }); + + // Wait for all uploads to complete + await Promise.all(uploadPromises); + } catch (error) { + // This handles any error that might occur outside the individual upload processes + console.error('Unexpected error during upload:', error); + } + }, + [onChange, uploadCanvasFile], + ); + + const onFileReject = React.useCallback((file: File, message: string) => { + toast(message, { + description: `"${file.name.length > 20 ? `${file.name.slice(0, 20)}...` : file.name}" has been rejected`, + }); + }, []); + + return ( + + +
    +
    + +
    +

    Drag & drop files here

    +

    + Or click to browse (max 2 files) +

    +
    + + + +
    + + {files.map((file, index) => ( + +
    + + + + + +
    + +
    + ))} +
    +
    + ); +} diff --git a/web/src/pages/data-flow/flow-tooltip.tsx b/web/src/pages/data-flow/flow-tooltip.tsx new file mode 100644 index 00000000000..9386dd06b66 --- /dev/null +++ b/web/src/pages/data-flow/flow-tooltip.tsx @@ -0,0 +1,19 @@ +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { PropsWithChildren } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const RunTooltip = ({ children }: PropsWithChildren) => { + const { t } = useTranslation(); + return ( + + {children} + +

    {t('flow.testRun')}

    +
    +
    + ); +}; diff --git a/web/src/pages/data-flow/form-hooks.ts b/web/src/pages/data-flow/form-hooks.ts new file mode 100644 index 00000000000..fef7d37b973 --- /dev/null +++ b/web/src/pages/data-flow/form-hooks.ts @@ -0,0 +1,43 @@ +import { useTranslate } from '@/hooks/common-hooks'; +import { useCallback, useMemo } from 'react'; +import { Operator, RestrictedUpstreamMap } from './constant'; +import useGraphStore from './store'; + +export const useBuildFormSelectOptions = ( + operatorName: Operator, + selfId?: string, // exclude the current node +) => { + const nodes = useGraphStore((state) => state.nodes); + + const buildCategorizeToOptions = useCallback( + (toList: string[]) => { + const excludedNodes: Operator[] = [ + Operator.Note, + ...(RestrictedUpstreamMap[operatorName] ?? []), + ]; + return nodes + .filter( + (x) => + excludedNodes.every((y) => y !== x.data.label) && + x.id !== selfId && + !toList.some((y) => y === x.id), // filter out selected values ​​in other to fields from the current drop-down box options + ) + .map((x) => ({ label: x.data.name, value: x.id })); + }, + [nodes, operatorName, selfId], + ); + + return buildCategorizeToOptions; +}; + +export const useBuildSortOptions = () => { + const { t } = useTranslate('flow'); + + const options = useMemo(() => { + return ['data', 'relevance'].map((x) => ({ + value: x, + label: t(x), + })); + }, [t]); + return options; +}; diff --git a/web/src/pages/data-flow/form-sheet/form-config-map.tsx b/web/src/pages/data-flow/form-sheet/form-config-map.tsx new file mode 100644 index 00000000000..0fc1b9e2aee --- /dev/null +++ b/web/src/pages/data-flow/form-sheet/form-config-map.tsx @@ -0,0 +1,165 @@ +import { Operator } from '../constant'; +import AgentForm from '../form/agent-form'; +import AkShareForm from '../form/akshare-form'; +import ArXivForm from '../form/arxiv-form'; +import BaiduFanyiForm from '../form/baidu-fanyi-form'; +import BaiduForm from '../form/baidu-form'; +import BeginForm from '../form/begin-form'; +import BingForm from '../form/bing-form'; +import CategorizeForm from '../form/categorize-form'; +import CodeForm from '../form/code-form'; +import CrawlerForm from '../form/crawler-form'; +import DeepLForm from '../form/deepl-form'; +import DuckDuckGoForm from '../form/duckduckgo-form'; +import EmailForm from '../form/email-form'; +import ExeSQLForm from '../form/exesql-form'; +import GithubForm from '../form/github-form'; +import GoogleForm from '../form/google-form'; +import GoogleScholarForm from '../form/google-scholar-form'; +import InvokeForm from '../form/invoke-form'; +import IterationForm from '../form/iteration-form'; +import IterationStartForm from '../form/iteration-start-from'; +import Jin10Form from '../form/jin10-form'; +import KeywordExtractForm from '../form/keyword-extract-form'; +import MessageForm from '../form/message-form'; +import PubMedForm from '../form/pubmed-form'; +import QWeatherForm from '../form/qweather-form'; +import RelevantForm from '../form/relevant-form'; +import RetrievalForm from '../form/retrieval-form/next'; +import RewriteQuestionForm from '../form/rewrite-question-form'; +import SearXNGForm from '../form/searxng-form'; +import StringTransformForm from '../form/string-transform-form'; +import SwitchForm from '../form/switch-form'; +import TavilyExtractForm from '../form/tavily-extract-form'; +import TavilyForm from '../form/tavily-form'; +import TuShareForm from '../form/tushare-form'; +import UserFillUpForm from '../form/user-fill-up-form'; +import WenCaiForm from '../form/wencai-form'; +import WikipediaForm from '../form/wikipedia-form'; +import YahooFinanceForm from '../form/yahoo-finance-form'; + +export const FormConfigMap = { + [Operator.Begin]: { + component: BeginForm, + }, + [Operator.Retrieval]: { + component: RetrievalForm, + }, + [Operator.Categorize]: { + component: CategorizeForm, + }, + [Operator.Message]: { + component: MessageForm, + }, + [Operator.Relevant]: { + component: RelevantForm, + }, + [Operator.RewriteQuestion]: { + component: RewriteQuestionForm, + }, + [Operator.Code]: { + component: CodeForm, + }, + [Operator.WaitingDialogue]: { + component: CodeForm, + }, + [Operator.Agent]: { + component: AgentForm, + }, + [Operator.Baidu]: { + component: BaiduForm, + }, + [Operator.DuckDuckGo]: { + component: DuckDuckGoForm, + }, + [Operator.KeywordExtract]: { + component: KeywordExtractForm, + }, + [Operator.Wikipedia]: { + component: WikipediaForm, + }, + [Operator.PubMed]: { + component: PubMedForm, + }, + [Operator.ArXiv]: { + component: ArXivForm, + }, + [Operator.Google]: { + component: GoogleForm, + }, + [Operator.Bing]: { + component: BingForm, + }, + [Operator.GoogleScholar]: { + component: GoogleScholarForm, + }, + [Operator.DeepL]: { + component: DeepLForm, + }, + [Operator.GitHub]: { + component: GithubForm, + }, + [Operator.BaiduFanyi]: { + component: BaiduFanyiForm, + }, + [Operator.QWeather]: { + component: QWeatherForm, + }, + [Operator.ExeSQL]: { + component: ExeSQLForm, + }, + [Operator.Switch]: { + component: SwitchForm, + }, + [Operator.WenCai]: { + component: WenCaiForm, + }, + [Operator.AkShare]: { + component: AkShareForm, + }, + [Operator.YahooFinance]: { + component: YahooFinanceForm, + }, + [Operator.Jin10]: { + component: Jin10Form, + }, + [Operator.TuShare]: { + component: TuShareForm, + }, + [Operator.Crawler]: { + component: CrawlerForm, + }, + [Operator.Invoke]: { + component: InvokeForm, + }, + [Operator.SearXNG]: { + component: SearXNGForm, + }, + [Operator.Concentrator]: { + component: () => <>, + }, + [Operator.Note]: { + component: () => <>, + }, + [Operator.Email]: { + component: EmailForm, + }, + [Operator.Iteration]: { + component: IterationForm, + }, + [Operator.IterationStart]: { + component: IterationStartForm, + }, + [Operator.TavilySearch]: { + component: TavilyForm, + }, + [Operator.UserFillUp]: { + component: UserFillUpForm, + }, + [Operator.StringTransform]: { + component: StringTransformForm, + }, + [Operator.TavilyExtract]: { + component: TavilyExtractForm, + }, +}; diff --git a/web/src/pages/data-flow/form-sheet/next.tsx b/web/src/pages/data-flow/form-sheet/next.tsx new file mode 100644 index 00000000000..2d7b5ca8b47 --- /dev/null +++ b/web/src/pages/data-flow/form-sheet/next.tsx @@ -0,0 +1,134 @@ +import { Input } from '@/components/ui/input'; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, +} from '@/components/ui/sheet'; +import { useTranslate } from '@/hooks/common-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { cn } from '@/lib/utils'; +import { lowerFirst } from 'lodash'; +import { Play, X } from 'lucide-react'; +import { useMemo } from 'react'; +import { BeginId, Operator } from '../constant'; +import { AgentFormContext } from '../context'; +import { RunTooltip } from '../flow-tooltip'; +import { useHandleNodeNameChange } from '../hooks/use-change-node-name'; +import OperatorIcon from '../operator-icon'; +import useGraphStore from '../store'; +import { needsSingleStepDebugging } from '../utils'; +import { FormConfigMap } from './form-config-map'; +import SingleDebugSheet from './single-debug-sheet'; + +interface IProps { + node?: RAGFlowNodeType; + singleDebugDrawerVisible: IModalProps['visible']; + hideSingleDebugDrawer: IModalProps['hideModal']; + showSingleDebugDrawer: IModalProps['showModal']; + chatVisible: boolean; +} + +const EmptyContent = () =>
    ; + +const FormSheet = ({ + visible, + hideModal, + node, + singleDebugDrawerVisible, + chatVisible, + hideSingleDebugDrawer, + showSingleDebugDrawer, +}: IModalProps & IProps) => { + const operatorName: Operator = node?.data.label as Operator; + const clickedToolId = useGraphStore((state) => state.clickedToolId); + + const currentFormMap = FormConfigMap[operatorName]; + + const OperatorForm = currentFormMap?.component ?? EmptyContent; + + const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ + id: node?.id, + data: node?.data, + }); + + const isMcp = useMemo(() => { + return ( + operatorName === Operator.Tool && + Object.values(Operator).every((x) => x !== clickedToolId) + ); + }, [clickedToolId, operatorName]); + + const { t } = useTranslate('flow'); + + return ( + + + + +
    +
    + + + {isMcp ? ( +
    MCP Config
    + ) : ( +
    + + {node?.id === BeginId ? ( + {t(BeginId)} + ) : ( + + )} +
    + )} + + {needsSingleStepDebugging(operatorName) && ( + + + + )} + +
    + {isMcp || ( + + {t( + `${lowerFirst(operatorName === Operator.Tool ? clickedToolId : operatorName)}Description`, + )} + + )} +
    +
    +
    + {visible && ( + + + + )} +
    +
    + {singleDebugDrawerVisible && ( + + )} +
    + ); +}; + +export default FormSheet; diff --git a/web/src/pages/data-flow/form-sheet/single-debug-sheet/index.tsx b/web/src/pages/data-flow/form-sheet/single-debug-sheet/index.tsx new file mode 100644 index 00000000000..c5fe6e876c1 --- /dev/null +++ b/web/src/pages/data-flow/form-sheet/single-debug-sheet/index.tsx @@ -0,0 +1,89 @@ +import CopyToClipboard from '@/components/copy-to-clipboard'; +import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; +import { useDebugSingle, useFetchInputForm } from '@/hooks/use-agent-request'; +import { IModalProps } from '@/interfaces/common'; +import { cn } from '@/lib/utils'; +import { isEmpty } from 'lodash'; +import { X } from 'lucide-react'; +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import JsonView from 'react18-json-view'; +import 'react18-json-view/src/style.css'; +import DebugContent from '../../debug-content'; +import { transferInputsArrayToObject } from '../../form/begin-form/use-watch-change'; +import { buildBeginInputListFromObject } from '../../form/begin-form/utils'; + +interface IProps { + componentId?: string; +} + +const SingleDebugSheet = ({ + componentId, + visible, + hideModal, +}: IModalProps & IProps) => { + const { t } = useTranslation(); + const inputForm = useFetchInputForm(componentId); + const { debugSingle, data, loading } = useDebugSingle(); + + const list = useMemo(() => { + return buildBeginInputListFromObject(inputForm); + }, [inputForm]); + + const onOk = useCallback( + (nextValues: any[]) => { + if (componentId) { + debugSingle({ + component_id: componentId, + params: transferInputsArrayToObject(nextValues), + }); + } + }, + [componentId, debugSingle], + ); + + const content = JSON.stringify(data, null, 2); + + return ( + + + +
    + {t('flow.testRun')} + +
    +
    +
    + + {!isEmpty(data) ? ( +
    +
    + JSON + +
    + +
    + ) : null} +
    +
    +
    + ); +}; + +export default SingleDebugSheet; diff --git a/web/src/pages/data-flow/form/agent-form/agent-tools.tsx b/web/src/pages/data-flow/form/agent-form/agent-tools.tsx new file mode 100644 index 00000000000..9e56208236a --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/agent-tools.tsx @@ -0,0 +1,191 @@ +import { BlockButton } from '@/components/ui/button'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { cn } from '@/lib/utils'; +import { Position } from '@xyflow/react'; +import { t } from 'i18next'; +import { PencilLine, X } from 'lucide-react'; +import { + MouseEventHandler, + PropsWithChildren, + useCallback, + useContext, + useMemo, +} from 'react'; +import { Operator } from '../../constant'; +import { AgentInstanceContext } from '../../context'; +import { useFindMcpById } from '../../hooks/use-find-mcp-by-id'; +import { INextOperatorForm } from '../../interface'; +import OperatorIcon from '../../operator-icon'; +import useGraphStore from '../../store'; +import { filterDownstreamAgentNodeIds } from '../../utils/filter-downstream-nodes'; +import { ToolPopover } from './tool-popover'; +import { useDeleteAgentNodeMCP } from './tool-popover/use-update-mcp'; +import { useDeleteAgentNodeTools } from './tool-popover/use-update-tools'; +import { useGetAgentMCPIds, useGetAgentToolNames } from './use-get-tools'; + +export function ToolCard({ + children, + className, + ...props +}: PropsWithChildren & React.HTMLAttributes) { + const element = useMemo(() => { + return ( +
  • + {children} +
  • + ); + }, [children, className, props]); + + if (children === Operator.Code) { + return ( + + {element} + +

    It doesn't have any config.

    +
    +
    + ); + } + + return element; +} + +type ActionButtonProps = { + record: T; + deleteRecord(record: T): void; + edit: MouseEventHandler; +}; + +function ActionButton({ deleteRecord, record, edit }: ActionButtonProps) { + const handleDelete = useCallback(() => { + deleteRecord(record); + }, [deleteRecord, record]); + + return ( +
    + + +
    + ); +} + +export function AgentTools() { + const { toolNames } = useGetAgentToolNames(); + const { deleteNodeTool } = useDeleteAgentNodeTools(); + const { mcpIds } = useGetAgentMCPIds(); + const { findMcpById } = useFindMcpById(); + const { deleteNodeMCP } = useDeleteAgentNodeMCP(); + const { showFormDrawer } = useContext(AgentInstanceContext); + const { clickedNodeId, findAgentToolNodeById, selectNodeIds } = useGraphStore( + (state) => state, + ); + + const handleEdit: MouseEventHandler = useCallback( + (e) => { + const toolNodeId = findAgentToolNodeById(clickedNodeId); + if (toolNodeId) { + selectNodeIds([toolNodeId]); + showFormDrawer(e, toolNodeId); + } + }, + [clickedNodeId, findAgentToolNodeById, selectNodeIds, showFormDrawer], + ); + + return ( +
    + {t('flow.tools')} +
      + {toolNames.map((x) => ( + +
      + + {x} +
      + +
      + ))} + {mcpIds.map((id) => ( + + {findMcpById(id)?.name} + + + ))} +
    + + {t('flow.addTools')} + +
    + ); +} + +export function Agents({ node }: INextOperatorForm) { + const { addCanvasNode } = useContext(AgentInstanceContext); + const { deleteAgentDownstreamNodesById, edges, getNode, selectNodeIds } = + useGraphStore((state) => state); + const { showFormDrawer } = useContext(AgentInstanceContext); + + const handleEdit = useCallback( + (nodeId: string): MouseEventHandler => + (e) => { + selectNodeIds([nodeId]); + showFormDrawer(e, nodeId); + }, + [selectNodeIds, showFormDrawer], + ); + + const subBottomAgentNodeIds = useMemo(() => { + return filterDownstreamAgentNodeIds(edges, node?.id); + }, [edges, node?.id]); + + return ( +
    + {t('flow.agent')} +
      + {subBottomAgentNodeIds.map((id) => { + const currentNode = getNode(id); + + return ( + + {currentNode?.data.name} + + + ); + })} +
    + + {t('flow.addAgent')} + +
    + ); +} diff --git a/web/src/pages/data-flow/form/agent-form/dynamic-prompt.tsx b/web/src/pages/data-flow/form/agent-form/dynamic-prompt.tsx new file mode 100644 index 00000000000..1cda9fbd508 --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/dynamic-prompt.tsx @@ -0,0 +1,93 @@ +import { BlockButton, Button } from '@/components/ui/button'; +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { X } from 'lucide-react'; +import { memo } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { PromptRole } from '../../constant'; +import { PromptEditor } from '../components/prompt-editor'; + +const options = [ + { label: 'User', value: PromptRole.User }, + { label: 'Assistant', value: PromptRole.Assistant }, +]; + +const DynamicPrompt = () => { + const { t } = useTranslation(); + const form = useFormContext(); + const name = 'prompts'; + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( + + {t('flow.msg')} +
    + {fields.map((field, index) => ( +
    +
    + ( + + + + + + + + )} + /> + + ( + + +
    + +
    +
    +
    + )} + /> +
    + +
    + ))} +
    + + append({ content: '', role: PromptRole.User })} + > + Add + +
    + ); +}; + +export default memo(DynamicPrompt); diff --git a/web/src/pages/data-flow/form/agent-form/dynamic-tool.tsx b/web/src/pages/data-flow/form/agent-form/dynamic-tool.tsx new file mode 100644 index 00000000000..afda465b652 --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/dynamic-tool.tsx @@ -0,0 +1,63 @@ +import { BlockButton, Button } from '@/components/ui/button'; +import { + FormControl, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { X } from 'lucide-react'; +import { memo } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { PromptEditor } from '../components/prompt-editor'; + +const DynamicTool = () => { + const form = useFormContext(); + const name = 'tools'; + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( + +
    + {fields.map((field, index) => ( +
    +
    + ( + + +
    + +
    +
    +
    + )} + /> +
    + +
    + ))} +
    + + append({ component_name: '' })}> + Add + +
    + ); +}; + +export default memo(DynamicTool); diff --git a/web/src/pages/data-flow/form/agent-form/index.tsx b/web/src/pages/data-flow/form/agent-form/index.tsx new file mode 100644 index 00000000000..9ca0fb69ccf --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/index.tsx @@ -0,0 +1,280 @@ +import { Collapse } from '@/components/collapse'; +import { FormContainer } from '@/components/form-container'; +import { + LargeModelFilterFormSchema, + LargeModelFormField, +} from '@/components/large-model-form-field'; +import { LlmSettingSchema } from '@/components/llm-setting-items/next'; +import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, +} from '@/components/ui/form'; +import { Input, NumberInput } from '@/components/ui/input'; +import { Switch } from '@/components/ui/switch'; +import { LlmModelType } from '@/constants/knowledge'; +import { useFindLlmByUuid } from '@/hooks/use-llm-request'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useEffect, useMemo } from 'react'; +import { useForm, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { + AgentExceptionMethod, + NodeHandleId, + VariableType, + initialAgentValues, +} from '../../constant'; +import { INextOperatorForm } from '../../interface'; +import useGraphStore from '../../store'; +import { isBottomSubAgent } from '../../utils'; +import { buildOutputList } from '../../utils/build-output-list'; +import { DescriptionField } from '../components/description-field'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { PromptEditor } from '../components/prompt-editor'; +import { QueryVariable } from '../components/query-variable'; +import { AgentTools, Agents } from './agent-tools'; +import { useValues } from './use-values'; +import { useWatchFormChange } from './use-watch-change'; + +const FormSchema = z.object({ + sys_prompt: z.string(), + description: z.string().optional(), + user_prompt: z.string().optional(), + prompts: z.string().optional(), + // prompts: z + // .array( + // z.object({ + // role: z.string(), + // content: z.string(), + // }), + // ) + // .optional(), + message_history_window_size: z.coerce.number(), + tools: z + .array( + z.object({ + component_name: z.string(), + }), + ) + .optional(), + ...LlmSettingSchema, + max_retries: z.coerce.number(), + delay_after_error: z.coerce.number().optional(), + visual_files_var: z.string().optional(), + max_rounds: z.coerce.number().optional(), + exception_method: z.string().optional(), + exception_goto: z.array(z.string()).optional(), + exception_default_value: z.string().optional(), + ...LargeModelFilterFormSchema, + cite: z.boolean().optional(), +}); + +const outputList = buildOutputList(initialAgentValues.outputs); + +function AgentForm({ node }: INextOperatorForm) { + const { t } = useTranslation(); + const { edges, deleteEdgesBySourceAndSourceHandle } = useGraphStore( + (state) => state, + ); + + const defaultValues = useValues(node); + + const ExceptionMethodOptions = Object.values(AgentExceptionMethod).map( + (x) => ({ + label: t(`flow.${x}`), + value: x, + }), + ); + + const isSubAgent = useMemo(() => { + return isBottomSubAgent(edges, node?.id); + }, [edges, node?.id]); + + const form = useForm>({ + defaultValues: defaultValues, + resolver: zodResolver(FormSchema), + }); + + const llmId = useWatch({ control: form.control, name: 'llm_id' }); + + const findLlmByUuid = useFindLlmByUuid(); + + const exceptionMethod = useWatch({ + control: form.control, + name: 'exception_method', + }); + + useEffect(() => { + if (exceptionMethod !== AgentExceptionMethod.Goto) { + if (node?.id) { + deleteEdgesBySourceAndSourceHandle( + node?.id, + NodeHandleId.AgentException, + ); + } + } + }, [deleteEdgesBySourceAndSourceHandle, exceptionMethod, node?.id]); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + {isSubAgent && } + + {findLlmByUuid(llmId)?.model_type === LlmModelType.Image2text && ( + + )} + + + + ( + + {t('flow.systemPrompt')} + + + + + )} + /> + + {isSubAgent || ( + + {/* */} + ( + + {t('flow.userPrompt')} + +
    + +
    +
    +
    + )} + /> +
    + )} + + + + + + {t('flow.advancedSettings')}}> + + + ( + + + {t('flow.cite')} + + + + + + )} + /> + ( + + {t('flow.maxRetries')} + + + + + )} + /> + ( + + {t('flow.delayEfterError')} + + + + + )} + /> + ( + + {t('flow.maxRounds')} + + + + + )} + /> + ( + + {t('flow.exceptionMethod')} + + + + + )} + /> + {exceptionMethod === AgentExceptionMethod.Comment && ( + ( + + {t('flow.ExceptionDefaultValue')} + + + + + )} + /> + )} + + + +
    + + ); +} + +export default memo(AgentForm); diff --git a/web/src/pages/data-flow/form/agent-form/tool-popover/index.tsx b/web/src/pages/data-flow/form/agent-form/tool-popover/index.tsx new file mode 100644 index 00000000000..84f1ed46a12 --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/tool-popover/index.tsx @@ -0,0 +1,89 @@ +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Operator } from '@/pages/agent/constant'; +import { AgentFormContext, AgentInstanceContext } from '@/pages/agent/context'; +import useGraphStore from '@/pages/agent/store'; +import { Position } from '@xyflow/react'; +import { t } from 'i18next'; +import { PropsWithChildren, useCallback, useContext, useEffect } from 'react'; +import { useGetAgentMCPIds, useGetAgentToolNames } from '../use-get-tools'; +import { MCPCommand, ToolCommand } from './tool-command'; +import { useUpdateAgentNodeMCP } from './use-update-mcp'; +import { useUpdateAgentNodeTools } from './use-update-tools'; + +enum ToolType { + Common = 'common', + MCP = 'mcp', +} + +export function ToolPopover({ children }: PropsWithChildren) { + const { addCanvasNode } = useContext(AgentInstanceContext); + const node = useContext(AgentFormContext); + const { updateNodeTools } = useUpdateAgentNodeTools(); + const { toolNames } = useGetAgentToolNames(); + const deleteAgentToolNodeById = useGraphStore( + (state) => state.deleteAgentToolNodeById, + ); + const { mcpIds } = useGetAgentMCPIds(); + const { updateNodeMCP } = useUpdateAgentNodeMCP(); + + const handleChange = useCallback( + (value: string[]) => { + if (Array.isArray(value) && node?.id) { + updateNodeTools(value); + } + }, + [node?.id, updateNodeTools], + ); + + useEffect(() => { + const total = toolNames.length + mcpIds.length; + if (node?.id) { + if (total > 0) { + addCanvasNode(Operator.Tool, { + position: Position.Bottom, + nodeId: node?.id, + })(); + } else { + deleteAgentToolNodeById(node.id); + } + } + }, [ + addCanvasNode, + deleteAgentToolNodeById, + mcpIds.length, + node?.id, + toolNames.length, + ]); + + return ( + + {children} + + + + + {t('flow.builtIn')} + + + MCP + + + + + + + + + + + + ); +} diff --git a/web/src/pages/data-flow/form/agent-form/tool-popover/tool-command.tsx b/web/src/pages/data-flow/form/agent-form/tool-popover/tool-command.tsx new file mode 100644 index 00000000000..6118c43abeb --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/tool-popover/tool-command.tsx @@ -0,0 +1,178 @@ +import { CheckIcon } from 'lucide-react'; + +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command'; +import { useListMcpServer } from '@/hooks/use-mcp-request'; +import { cn } from '@/lib/utils'; +import { Operator } from '@/pages/agent/constant'; +import OperatorIcon from '@/pages/agent/operator-icon'; +import { t } from 'i18next'; +import { lowerFirst } from 'lodash'; +import { PropsWithChildren, useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +const Menus = [ + { + label: t('flow.search'), + list: [ + Operator.TavilySearch, + Operator.TavilyExtract, + Operator.Google, + // Operator.Bing, + Operator.DuckDuckGo, + Operator.Wikipedia, + Operator.SearXNG, + Operator.YahooFinance, + Operator.PubMed, + Operator.GoogleScholar, + Operator.ArXiv, + Operator.WenCai, + ], + }, + { + label: t('flow.communication'), + list: [Operator.Email], + }, + // { + // label: 'Productivity', + // list: [], + // }, + { + label: t('flow.developer'), + list: [Operator.GitHub, Operator.ExeSQL, Operator.Code, Operator.Retrieval], + }, +]; + +type ToolCommandProps = { + value?: string[]; + onChange?(values: string[]): void; +}; + +type ToolCommandItemProps = { + toggleOption(id: string): void; + id: string; + isSelected: boolean; +} & ToolCommandProps; + +function ToolCommandItem({ + toggleOption, + id, + isSelected, + children, +}: ToolCommandItemProps & PropsWithChildren) { + return ( + toggleOption(id)}> +
    + +
    + {children} +
    + ); +} + +function useHandleSelectChange({ onChange, value }: ToolCommandProps) { + const [currentValue, setCurrentValue] = useState([]); + + const toggleOption = useCallback( + (option: string) => { + const newSelectedValues = currentValue.includes(option) + ? currentValue.filter((value) => value !== option) + : [...currentValue, option]; + setCurrentValue(newSelectedValues); + onChange?.(newSelectedValues); + }, + [currentValue, onChange], + ); + + useEffect(() => { + if (Array.isArray(value)) { + setCurrentValue(value); + } + }, [value]); + + return { + toggleOption, + currentValue, + }; +} + +export function ToolCommand({ value, onChange }: ToolCommandProps) { + const { t } = useTranslation(); + const { toggleOption, currentValue } = useHandleSelectChange({ + onChange, + value, + }); + + return ( + + + + No results found. + {Menus.map((x) => ( + + {x.list.map((y) => { + const isSelected = currentValue.includes(y); + return ( + + <> + + {t(`flow.${lowerFirst(y)}`)} + + + ); + })} + + ))} + + + ); +} + +export function MCPCommand({ onChange, value }: ToolCommandProps) { + const { data } = useListMcpServer(); + const { toggleOption, currentValue } = useHandleSelectChange({ + onChange, + value, + }); + + return ( + + + + No results found. + {data.mcp_servers.map((item) => { + const isSelected = currentValue.includes(item.id); + + return ( + + {item.name} + + ); + })} + + + ); +} diff --git a/web/src/pages/data-flow/form/agent-form/tool-popover/use-update-mcp.ts b/web/src/pages/data-flow/form/agent-form/tool-popover/use-update-mcp.ts new file mode 100644 index 00000000000..827b9f9e117 --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/tool-popover/use-update-mcp.ts @@ -0,0 +1,74 @@ +import { useListMcpServer } from '@/hooks/use-mcp-request'; +import { IAgentForm } from '@/interfaces/database/agent'; +import { AgentFormContext } from '@/pages/agent/context'; +import useGraphStore from '@/pages/agent/store'; +import { get } from 'lodash'; +import { useCallback, useContext, useMemo } from 'react'; + +export function useGetNodeMCP() { + const node = useContext(AgentFormContext); + + return useMemo(() => { + const mcp: IAgentForm['mcp'] = get(node, 'data.form.mcp'); + return mcp; + }, [node]); +} + +export function useUpdateAgentNodeMCP() { + const { updateNodeForm } = useGraphStore((state) => state); + const node = useContext(AgentFormContext); + const mcpList = useGetNodeMCP(); + const { data } = useListMcpServer(); + const mcpServers = data.mcp_servers; + + const findMcpTools = useCallback( + (mcpId: string) => { + const mcp = mcpServers.find((x) => x.id === mcpId); + return mcp?.variables.tools; + }, + [mcpServers], + ); + + const updateNodeMCP = useCallback( + (value: string[]) => { + if (node?.id) { + const nextValue = value.reduce((pre, cur) => { + const mcp = mcpList.find((x) => x.mcp_id === cur); + const tools = findMcpTools(cur); + if (mcp) { + pre.push(mcp); + } else if (tools) { + pre.push({ + mcp_id: cur, + tools: {}, + }); + } + return pre; + }, []); + + updateNodeForm(node?.id, nextValue, ['mcp']); + } + }, + [node?.id, updateNodeForm, mcpList, findMcpTools], + ); + + return { updateNodeMCP }; +} + +export function useDeleteAgentNodeMCP() { + const { updateNodeForm } = useGraphStore((state) => state); + const mcpList = useGetNodeMCP(); + const node = useContext(AgentFormContext); + + const deleteNodeMCP = useCallback( + (value: string) => () => { + const nextMCP = mcpList.filter((x) => x.mcp_id !== value); + if (node?.id) { + updateNodeForm(node?.id, nextMCP, ['mcp']); + } + }, + [node?.id, mcpList, updateNodeForm], + ); + + return { deleteNodeMCP }; +} diff --git a/web/src/pages/data-flow/form/agent-form/tool-popover/use-update-tools.ts b/web/src/pages/data-flow/form/agent-form/tool-popover/use-update-tools.ts new file mode 100644 index 00000000000..db579561ae2 --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/tool-popover/use-update-tools.ts @@ -0,0 +1,66 @@ +import { IAgentForm } from '@/interfaces/database/agent'; +import { Operator } from '@/pages/agent/constant'; +import { AgentFormContext } from '@/pages/agent/context'; +import { useAgentToolInitialValues } from '@/pages/agent/hooks/use-agent-tool-initial-values'; +import useGraphStore from '@/pages/agent/store'; +import { get } from 'lodash'; +import { useCallback, useContext, useMemo } from 'react'; + +export function useGetNodeTools() { + const node = useContext(AgentFormContext); + + return useMemo(() => { + const tools: IAgentForm['tools'] = get(node, 'data.form.tools'); + return tools; + }, [node]); +} + +export function useUpdateAgentNodeTools() { + const { updateNodeForm } = useGraphStore((state) => state); + const node = useContext(AgentFormContext); + const tools = useGetNodeTools(); + const { initializeAgentToolValues } = useAgentToolInitialValues(); + + const updateNodeTools = useCallback( + (value: string[]) => { + if (node?.id) { + const nextValue = value.reduce((pre, cur) => { + const tool = tools.find((x) => x.component_name === cur); + pre.push( + tool + ? tool + : { + component_name: cur, + name: cur, + params: initializeAgentToolValues(cur as Operator), + }, + ); + return pre; + }, []); + + updateNodeForm(node?.id, nextValue, ['tools']); + } + }, + [initializeAgentToolValues, node?.id, tools, updateNodeForm], + ); + + return { updateNodeTools }; +} + +export function useDeleteAgentNodeTools() { + const { updateNodeForm } = useGraphStore((state) => state); + const tools = useGetNodeTools(); + const node = useContext(AgentFormContext); + + const deleteNodeTool = useCallback( + (value: string) => () => { + const nextTools = tools.filter((x) => x.component_name !== value); + if (node?.id) { + updateNodeForm(node?.id, nextTools, ['tools']); + } + }, + [node?.id, tools, updateNodeForm], + ); + + return { deleteNodeTool }; +} diff --git a/web/src/pages/data-flow/form/agent-form/use-get-tools.ts b/web/src/pages/data-flow/form/agent-form/use-get-tools.ts new file mode 100644 index 00000000000..32bf3f0efff --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/use-get-tools.ts @@ -0,0 +1,26 @@ +import { IAgentForm } from '@/interfaces/database/agent'; +import { get } from 'lodash'; +import { useContext, useMemo } from 'react'; +import { AgentFormContext } from '../../context'; + +export function useGetAgentToolNames() { + const node = useContext(AgentFormContext); + + const toolNames = useMemo(() => { + const tools: IAgentForm['tools'] = get(node, 'data.form.tools', []); + return tools.map((x) => x.component_name); + }, [node]); + + return { toolNames }; +} + +export function useGetAgentMCPIds() { + const node = useContext(AgentFormContext); + + const mcpIds = useMemo(() => { + const ids: IAgentForm['mcp'] = get(node, 'data.form.mcp', []); + return ids.map((x) => x.mcp_id); + }, [node]); + + return { mcpIds }; +} diff --git a/web/src/pages/data-flow/form/agent-form/use-values.ts b/web/src/pages/data-flow/form/agent-form/use-values.ts new file mode 100644 index 00000000000..b2d61dc9f25 --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/use-values.ts @@ -0,0 +1,33 @@ +import { useFetchModelId } from '@/hooks/logic-hooks'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { get, isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { initialAgentValues } from '../../constant'; + +export function useValues(node?: RAGFlowNodeType) { + const llmId = useFetchModelId(); + + const defaultValues = useMemo( + () => ({ + ...initialAgentValues, + llm_id: llmId, + prompts: '', + }), + [llmId], + ); + + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return defaultValues; + } + + return { + ...formData, + prompts: get(formData, 'prompts.0.content', ''), + }; + }, [defaultValues, node?.data?.form]); + + return values; +} diff --git a/web/src/pages/data-flow/form/agent-form/use-watch-change.ts b/web/src/pages/data-flow/form/agent-form/use-watch-change.ts new file mode 100644 index 00000000000..98b0ecf310b --- /dev/null +++ b/web/src/pages/data-flow/form/agent-form/use-watch-change.ts @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { PromptRole } from '../../constant'; +import useGraphStore from '../../store'; + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id && form?.formState.isDirty) { + values = form?.getValues(); + let nextValues: any = { + ...values, + prompts: [{ role: PromptRole.User, content: values.prompts }], + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/data-flow/form/akshare-form/index.tsx b/web/src/pages/data-flow/form/akshare-form/index.tsx new file mode 100644 index 00000000000..1cfd554b17b --- /dev/null +++ b/web/src/pages/data-flow/form/akshare-form/index.tsx @@ -0,0 +1,22 @@ +import { TopNFormField } from '@/components/top-n-item'; +import { Form } from '@/components/ui/form'; +import { INextOperatorForm } from '../../interface'; +import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; + +const AkShareForm = ({ form, node }: INextOperatorForm) => { + return ( +
    + { + e.preventDefault(); + }} + > + + + + + ); +}; + +export default AkShareForm; diff --git a/web/src/pages/data-flow/form/arxiv-form/index.tsx b/web/src/pages/data-flow/form/arxiv-form/index.tsx new file mode 100644 index 00000000000..a6e1b7c4570 --- /dev/null +++ b/web/src/pages/data-flow/form/arxiv-form/index.tsx @@ -0,0 +1,96 @@ +import { FormContainer } from '@/components/form-container'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useMemo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialArXivValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +export const ArXivFormPartialSchema = { + top_n: z.number(), + sort_by: z.string(), +}; + +export const FormSchema = z.object({ + ...ArXivFormPartialSchema, + query: z.string(), +}); + +export function ArXivFormWidgets() { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + const options = useMemo(() => { + return ['submittedDate', 'lastUpdatedDate', 'relevance'].map((x) => ({ + value: x, + label: t(x), + })); + }, [t]); + + return ( + <> + + ( + + {t('sortBy')} + + + + + + )} + /> + + ); +} + +const outputList = buildOutputList(initialArXivValues.outputs); + +function ArXivForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialArXivValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + +
    + +
    + + ); +} + +export default memo(ArXivForm); diff --git a/web/src/pages/data-flow/form/baidu-fanyi-form/index.tsx b/web/src/pages/data-flow/form/baidu-fanyi-form/index.tsx new file mode 100644 index 00000000000..c02b3dd857b --- /dev/null +++ b/web/src/pages/data-flow/form/baidu-fanyi-form/index.tsx @@ -0,0 +1,71 @@ +import { useTranslate } from '@/hooks/common-hooks'; +import { Form, Input, Select } from 'antd'; +import { useMemo } from 'react'; +import { IOperatorForm } from '../../interface'; +import { + BaiduFanyiDomainOptions, + BaiduFanyiSourceLangOptions, +} from '../../options'; +import DynamicInputVariable from '../components/dynamic-input-variable'; + +const BaiduFanyiForm = ({ onValuesChange, form, node }: IOperatorForm) => { + const { t } = useTranslate('flow'); + const options = useMemo(() => { + return ['translate', 'fieldtranslate'].map((x) => ({ + value: x, + label: t(`baiduSecretKeyOptions.${x}`), + })); + }, [t]); + + const baiduFanyiOptions = useMemo(() => { + return BaiduFanyiDomainOptions.map((x) => ({ + value: x, + label: t(`baiduDomainOptions.${x}`), + })); + }, [t]); + + const baiduFanyiSourceLangOptions = useMemo(() => { + return BaiduFanyiSourceLangOptions.map((x) => ({ + value: x, + label: t(`baiduSourceLangOptions.${x}`), + })); + }, [t]); + + return ( +
    + + + + + + + + + + + + {({ getFieldValue }) => + getFieldValue('trans_type') === 'fieldtranslate' && ( + + + + ) + } + + + + + + + + + ); +}; + +export default BaiduFanyiForm; diff --git a/web/src/pages/data-flow/form/baidu-form/index.tsx b/web/src/pages/data-flow/form/baidu-form/index.tsx new file mode 100644 index 00000000000..0861ef829df --- /dev/null +++ b/web/src/pages/data-flow/form/baidu-form/index.tsx @@ -0,0 +1,22 @@ +import { TopNFormField } from '@/components/top-n-item'; +import { Form } from '@/components/ui/form'; +import { INextOperatorForm } from '../../interface'; +import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; + +const BaiduForm = ({ form, node }: INextOperatorForm) => { + return ( +
    + { + e.preventDefault(); + }} + > + + + + + ); +}; + +export default BaiduForm; diff --git a/web/src/pages/data-flow/form/begin-form/begin-dynamic-options.tsx b/web/src/pages/data-flow/form/begin-form/begin-dynamic-options.tsx new file mode 100644 index 00000000000..12b2bfb4b4d --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/begin-dynamic-options.tsx @@ -0,0 +1,57 @@ +'use client'; + +import { BlockButton, Button } from '@/components/ui/button'; +import { + FormControl, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { X } from 'lucide-react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +export function BeginDynamicOptions() { + const { t } = useTranslation(); + const form = useFormContext(); + const name = 'options'; + + const { fields, remove, append } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( +
    + {fields.map((field, index) => { + const typeField = `${name}.${index}.value`; + return ( +
    + ( + + + + + + + )} + /> + +
    + ); + })} + append({ value: '' })} type="button"> + {t('flow.addField')} + +
    + ); +} diff --git a/web/src/pages/data-flow/form/begin-form/index.tsx b/web/src/pages/data-flow/form/begin-form/index.tsx new file mode 100644 index 00000000000..ad4eb9d3e54 --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/index.tsx @@ -0,0 +1,205 @@ +import { Collapse } from '@/components/collapse'; +import { Button } from '@/components/ui/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { Switch } from '@/components/ui/switch'; +import { Textarea } from '@/components/ui/textarea'; +import { FormTooltip } from '@/components/ui/tooltip'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { t } from 'i18next'; +import { Plus } from 'lucide-react'; +import { memo, useEffect, useRef } from 'react'; +import { useForm, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { AgentDialogueMode } from '../../constant'; +import { INextOperatorForm } from '../../interface'; +import { ParameterDialog } from './parameter-dialog'; +import { QueryTable } from './query-table'; +import { useEditQueryRecord } from './use-edit-query'; +import { useValues } from './use-values'; +import { useWatchFormChange } from './use-watch-change'; + +const ModeOptions = [ + { value: AgentDialogueMode.Conversational, label: t('flow.conversational') }, + { value: AgentDialogueMode.Task, label: t('flow.task') }, +]; + +function BeginForm({ node }: INextOperatorForm) { + const { t } = useTranslation(); + + const values = useValues(node); + + const FormSchema = z.object({ + enablePrologue: z.boolean().optional(), + prologue: z.string().trim().optional(), + mode: z.string(), + inputs: z + .array( + z.object({ + key: z.string(), + type: z.string(), + value: z.string(), + optional: z.boolean(), + name: z.string(), + options: z.array(z.union([z.number(), z.string(), z.boolean()])), + }), + ) + .optional(), + }); + + const form = useForm({ + defaultValues: values, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + const inputs = useWatch({ control: form.control, name: 'inputs' }); + const mode = useWatch({ control: form.control, name: 'mode' }); + + const enablePrologue = useWatch({ + control: form.control, + name: 'enablePrologue', + }); + + const previousModeRef = useRef(mode); + + useEffect(() => { + if ( + previousModeRef.current === AgentDialogueMode.Task && + mode === AgentDialogueMode.Conversational + ) { + form.setValue('enablePrologue', true); + } + previousModeRef.current = mode; + }, [mode, form]); + + const { + ok, + currentRecord, + visible, + hideModal, + showModal, + otherThanCurrentQuery, + handleDeleteRecord, + } = useEditQueryRecord({ + form, + node, + }); + + return ( +
    +
    + ( + + + {t('flow.mode')} + + + + + + + )} + /> + {mode === AgentDialogueMode.Conversational && ( + ( + + + {t('flow.openingSwitch')} + + + + + + + )} + /> + )} + {mode === AgentDialogueMode.Conversational && enablePrologue && ( + ( + + + {t('flow.openingCopy')} + + + + + + + )} + /> + )} + {/* Create a hidden field to make Form instance record this */} +
    } + /> + + {t('flow.input')} + + + } + rightContent={ + + } + > + + + {visible && ( + + )} + +
    + ); +} + +export default memo(BeginForm); diff --git a/web/src/pages/data-flow/form/begin-form/parameter-dialog.tsx b/web/src/pages/data-flow/form/begin-form/parameter-dialog.tsx new file mode 100644 index 00000000000..3b907043795 --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/parameter-dialog.tsx @@ -0,0 +1,226 @@ +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select'; +import { Switch } from '@/components/ui/switch'; +import { useTranslate } from '@/hooks/common-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { isEmpty } from 'lodash'; +import { ChangeEvent, useEffect, useMemo } from 'react'; +import { useForm, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { BeginQueryType, BeginQueryTypeIconMap } from '../../constant'; +import { BeginQuery } from '../../interface'; +import { BeginDynamicOptions } from './begin-dynamic-options'; + +type ModalFormProps = { + initialValue: BeginQuery; + otherThanCurrentQuery: BeginQuery[]; + submit(values: any): void; +}; + +const FormId = 'BeginParameterForm'; + +function ParameterForm({ + initialValue, + otherThanCurrentQuery, + submit, +}: ModalFormProps) { + const { t } = useTranslate('flow'); + const FormSchema = z.object({ + type: z.string(), + key: z + .string() + .trim() + .min(1) + .refine( + (value) => + !value || !otherThanCurrentQuery.some((x) => x.key === value), + { message: 'The key cannot be repeated!' }, + ), + optional: z.boolean(), + name: z.string().trim().min(1), + options: z + .array(z.object({ value: z.string().or(z.boolean()).or(z.number()) })) + .optional(), + }); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + mode: 'onChange', + defaultValues: { + type: BeginQueryType.Line, + optional: false, + key: '', + name: '', + options: [], + }, + }); + + const options = useMemo(() => { + return Object.values(BeginQueryType).reduce( + (pre, cur) => { + const Icon = BeginQueryTypeIconMap[cur]; + + return [ + ...pre, + { + label: ( +
    + + {t(cur.toLowerCase())} +
    + ), + value: cur, + }, + ]; + }, + [], + ); + }, []); + + const type = useWatch({ + control: form.control, + name: 'type', + }); + + useEffect(() => { + if (!isEmpty(initialValue)) { + form.reset({ + ...initialValue, + options: initialValue.options?.map((x) => ({ value: x })), + }); + } + }, [form, initialValue]); + + function onSubmit(data: z.infer) { + const values = { ...data, options: data.options?.map((x) => x.value) }; + console.log('🚀 ~ onSubmit ~ values:', values); + + submit(values); + } + + const handleKeyChange = (e: ChangeEvent) => { + const name = form.getValues().name || ''; + form.setValue('key', e.target.value.trim()); + if (!name) { + form.setValue('name', e.target.value.trim()); + } + }; + return ( +
    + + ( + + {t('type')} + + + + + + )} + /> + ( + + {t('key')} + + + + + + )} + /> + ( + + {t('name')} + + + + + + )} + /> + ( + + {t('optional')} + + + + + + )} + /> + {type === BeginQueryType.Options && ( + + )} + + + ); +} + +export function ParameterDialog({ + initialValue, + hideModal, + otherThanCurrentQuery, + submit, +}: ModalFormProps & IModalProps) { + const { t } = useTranslation(); + + return ( + + + + {t('flow.variableSettings')} + + + + + + + + ); +} diff --git a/web/src/pages/data-flow/form/begin-form/query-table.tsx b/web/src/pages/data-flow/form/begin-form/query-table.tsx new file mode 100644 index 00000000000..5701c49b1f8 --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/query-table.tsx @@ -0,0 +1,199 @@ +'use client'; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { Pencil, Trash2 } from 'lucide-react'; +import * as React from 'react'; + +import { TableEmpty } from '@/components/table-skeleton'; +import { Button } from '@/components/ui/button'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { cn } from '@/lib/utils'; +import { useTranslation } from 'react-i18next'; +import { BeginQuery } from '../../interface'; + +interface IProps { + data: BeginQuery[]; + deleteRecord(index: number): void; + showModal(index: number, record: BeginQuery): void; +} + +export function QueryTable({ data = [], deleteRecord, showModal }: IProps) { + const { t } = useTranslation(); + + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [], + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + + const columns: ColumnDef[] = [ + { + accessorKey: 'key', + header: t('flow.key'), + meta: { cellClassName: 'max-w-30' }, + cell: ({ row }) => { + const key: string = row.getValue('key'); + return ( + + +
    {key}
    +
    + +

    {key}

    +
    +
    + ); + }, + }, + { + accessorKey: 'name', + header: t('flow.name'), + meta: { cellClassName: 'max-w-30' }, + cell: ({ row }) => { + const name: string = row.getValue('name'); + return ( + + +
    {name}
    +
    + +

    {name}

    +
    +
    + ); + }, + }, + { + accessorKey: 'type', + header: t('flow.type'), + cell: ({ row }) => ( +
    + {t(`flow.${(row.getValue('type')?.toString() || '').toLowerCase()}`)} +
    + ), + }, + { + accessorKey: 'optional', + header: t('flow.optional'), + cell: ({ row }) =>
    {row.getValue('optional') ? 'Yes' : 'No'}
    , + }, + { + id: 'actions', + enableHiding: false, + header: t('common.action'), + cell: ({ row }) => { + const record = row.original; + const idx = row.index; + + return ( +
    + + +
    + ); + }, + }, + ]; + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + state: { + sorting, + columnFilters, + columnVisibility, + }, + }); + + return ( +
    +
    +
    + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + )} + +
    +
    +
    + ); +} diff --git a/web/src/pages/data-flow/form/begin-form/use-edit-query.ts b/web/src/pages/data-flow/form/begin-form/use-edit-query.ts new file mode 100644 index 00000000000..6942ba88bef --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/use-edit-query.ts @@ -0,0 +1,67 @@ +import { useSetModalState } from '@/hooks/common-hooks'; +import { useSetSelectedRecord } from '@/hooks/logic-hooks'; +import { useCallback, useMemo, useState } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { BeginQuery, INextOperatorForm } from '../../interface'; + +export const useEditQueryRecord = ({ + form, +}: INextOperatorForm & { form: UseFormReturn }) => { + const { setRecord, currentRecord } = useSetSelectedRecord(); + const { visible, hideModal, showModal } = useSetModalState(); + const [index, setIndex] = useState(-1); + const inputs: BeginQuery[] = useWatch({ + control: form.control, + name: 'inputs', + }); + + const otherThanCurrentQuery = useMemo(() => { + return inputs.filter((item, idx) => idx !== index); + }, [index, inputs]); + + const handleEditRecord = useCallback( + (record: BeginQuery) => { + const inputs: BeginQuery[] = form?.getValues('inputs') || []; + + const nextQuery: BeginQuery[] = + index > -1 ? inputs.toSpliced(index, 1, record) : [...inputs, record]; + + form.setValue('inputs', nextQuery); + + hideModal(); + }, + [form, hideModal, index], + ); + + const handleShowModal = useCallback( + (idx?: number, record?: BeginQuery) => { + setIndex(idx ?? -1); + setRecord(record ?? ({} as BeginQuery)); + showModal(); + }, + [setRecord, showModal], + ); + + const handleDeleteRecord = useCallback( + (idx: number) => { + const inputs = form?.getValues('inputs') || []; + const nextInputs = inputs.filter( + (item: BeginQuery, index: number) => index !== idx, + ); + + form.setValue('inputs', nextInputs); + }, + [form], + ); + + return { + ok: handleEditRecord, + currentRecord, + setRecord, + visible, + hideModal, + showModal: handleShowModal, + otherThanCurrentQuery, + handleDeleteRecord, + }; +}; diff --git a/web/src/pages/data-flow/form/begin-form/use-values.ts b/web/src/pages/data-flow/form/begin-form/use-values.ts new file mode 100644 index 00000000000..10326bae83c --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/use-values.ts @@ -0,0 +1,34 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { AgentDialogueMode } from '../../constant'; +import { buildBeginInputListFromObject } from './utils'; + +export function useValues(node?: RAGFlowNodeType) { + const { t } = useTranslation(); + + const defaultValues = useMemo( + () => ({ + enablePrologue: true, + prologue: t('chat.setAnOpenerInitial'), + mode: AgentDialogueMode.Conversational, + inputs: [], + }), + [t], + ); + + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return defaultValues; + } + + const inputs = buildBeginInputListFromObject(formData?.inputs); + + return { ...(formData || {}), inputs }; + }, [defaultValues, node?.data?.form]); + + return values; +} diff --git a/web/src/pages/data-flow/form/begin-form/use-watch-change.ts b/web/src/pages/data-flow/form/begin-form/use-watch-change.ts new file mode 100644 index 00000000000..f0da58068a1 --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/use-watch-change.ts @@ -0,0 +1,31 @@ +import { omit } from 'lodash'; +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { BeginQuery } from '../../interface'; +import useGraphStore from '../../store'; + +export function transferInputsArrayToObject(inputs: BeginQuery[] = []) { + return inputs.reduce>>((pre, cur) => { + pre[cur.key] = omit(cur, 'key'); + + return pre; + }, {}); +} + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + if (id) { + values = form?.getValues() || {}; + + const nextValues = { + ...values, + inputs: transferInputsArrayToObject(values.inputs), + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/data-flow/form/begin-form/utils.ts b/web/src/pages/data-flow/form/begin-form/utils.ts new file mode 100644 index 00000000000..36038c4f6d2 --- /dev/null +++ b/web/src/pages/data-flow/form/begin-form/utils.ts @@ -0,0 +1,14 @@ +import { BeginQuery } from '../../interface'; + +export function buildBeginInputListFromObject( + inputs: Record>, +) { + return Object.entries(inputs || {}).reduce( + (pre, [key, value]) => { + pre.push({ ...(value || {}), key }); + + return pre; + }, + [], + ); +} diff --git a/web/src/pages/data-flow/form/bing-form/index.tsx b/web/src/pages/data-flow/form/bing-form/index.tsx new file mode 100644 index 00000000000..fae75aa125d --- /dev/null +++ b/web/src/pages/data-flow/form/bing-form/index.tsx @@ -0,0 +1,131 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useMemo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialBingValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { BingCountryOptions, BingLanguageOptions } from '../../options'; +import { FormWrapper } from '../components/form-wrapper'; +import { QueryVariable } from '../components/query-variable'; + +export const BingFormSchema = { + channel: z.string(), + api_key: z.string(), + country: z.string(), + language: z.string(), + top_n: z.number(), +}; + +export const FormSchema = z.object({ + query: z.string().optional(), + ...BingFormSchema, +}); + +export function BingFormWidgets() { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + const options = useMemo(() => { + return ['Webpages', 'News'].map((x) => ({ label: x, value: x })); + }, []); + + return ( + <> + + ( + + {t('channel')} + + + + + + )} + /> + ( + + {t('apiKey')} + + + + + + )} + /> + ( + + {t('country')} + + + + + + )} + /> + ( + + {t('language')} + + + + + + )} + /> + + ); +} + +function BingForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialBingValues, node); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues, + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + +
    + ); +} + +export default memo(BingForm); diff --git a/web/src/pages/data-flow/form/categorize-form/dynamic-categorize.tsx b/web/src/pages/data-flow/form/categorize-form/dynamic-categorize.tsx new file mode 100644 index 00000000000..0807f7bfa6b --- /dev/null +++ b/web/src/pages/data-flow/form/categorize-form/dynamic-categorize.tsx @@ -0,0 +1,249 @@ +import { Button } from '@/components/ui/button'; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '@/components/ui/collapsible'; +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { BlurTextarea } from '@/components/ui/textarea'; +import { useTranslate } from '@/hooks/common-hooks'; +import { PlusOutlined } from '@ant-design/icons'; +import { useUpdateNodeInternals } from '@xyflow/react'; +import humanId from 'human-id'; +import trim from 'lodash/trim'; +import { ChevronsUpDown, X } from 'lucide-react'; +import { + ChangeEventHandler, + FocusEventHandler, + memo, + useCallback, + useEffect, + useState, +} from 'react'; +import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form'; +import { v4 as uuid } from 'uuid'; +import { z } from 'zod'; +import useGraphStore from '../../store'; +import DynamicExample from './dynamic-example'; +import { useCreateCategorizeFormSchema } from './use-form-schema'; + +interface IProps { + nodeId?: string; +} + +interface INameInputProps { + value?: string; + onChange?: (value: string) => void; + otherNames?: string[]; + validate(error?: string): void; +} + +const getOtherFieldValues = ( + form: UseFormReturn, + formListName: string = 'items', + index: number, + latestField: string, +) => + (form.getValues(formListName) ?? []) + .map((x: any) => x[latestField]) + .filter( + (x: string) => + x !== form.getValues(`${formListName}.${index}.${latestField}`), + ); + +const InnerNameInput = ({ + value, + onChange, + otherNames, + validate, +}: INameInputProps) => { + const [name, setName] = useState(); + const { t } = useTranslate('flow'); + + const handleNameChange: ChangeEventHandler = useCallback( + (e) => { + const val = e.target.value; + setName(val); + const trimmedVal = trim(val); + // trigger validation + if (otherNames?.some((x) => x === trimmedVal)) { + validate(t('nameRepeatedMsg')); + } else if (trimmedVal === '') { + validate(t('nameRequiredMsg')); + } else { + validate(''); + } + }, + [otherNames, validate, t], + ); + + const handleNameBlur: FocusEventHandler = useCallback( + (e) => { + const val = e.target.value; + if (otherNames?.every((x) => x !== val) && trim(val) !== '') { + onChange?.(val); + } + }, + [onChange, otherNames], + ); + + useEffect(() => { + setName(value); + }, [value]); + + return ( + + ); +}; + +const NameInput = memo(InnerNameInput); + +const InnerFormSet = ({ index }: IProps & { index: number }) => { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + const buildFieldName = useCallback( + (name: string) => { + return `items.${index}.${name}`; + }, + [index], + ); + + return ( +
    + ( + + {t('categoryName')} + + { + const fieldName = buildFieldName('name'); + if (error) { + form.setError(fieldName, { message: error }); + } else { + form.clearErrors(fieldName); + } + }} + > + + + + )} + /> + ( + + {t('description')} + + + + + + )} + /> + {/* Create a hidden field to make Form instance record this */} +
    } + /> + +
    + ); +}; + +const FormSet = memo(InnerFormSet); + +const DynamicCategorize = ({ nodeId }: IProps) => { + const updateNodeInternals = useUpdateNodeInternals(); + const FormSchema = useCreateCategorizeFormSchema(); + + const deleteCategorizeCaseEdges = useGraphStore( + (state) => state.deleteEdgesBySourceAndSourceHandle, + ); + const form = useFormContext>(); + const { t } = useTranslate('flow'); + const { fields, remove, append } = useFieldArray({ + name: 'items', + control: form.control, + }); + + const handleAdd = useCallback(() => { + append({ + name: humanId(), + description: '', + uuid: uuid(), + examples: [{ value: '' }], + }); + if (nodeId) updateNodeInternals(nodeId); + }, [append, nodeId, updateNodeInternals]); + + const handleRemove = useCallback( + (index: number) => () => { + remove(index); + if (nodeId) { + const uuid = fields[index].uuid; + deleteCategorizeCaseEdges(nodeId, uuid); + } + }, + [deleteCategorizeCaseEdges, fields, nodeId, remove], + ); + + return ( +
    + {fields.map((field, index) => ( + +
    +

    + {form.getValues(`items.${index}.name`)} +

    + +
    + + +
    +
    +
    + + + +
    + ))} + + +
    + ); +}; + +export default memo(DynamicCategorize); diff --git a/web/src/pages/data-flow/form/categorize-form/dynamic-example.tsx b/web/src/pages/data-flow/form/categorize-form/dynamic-example.tsx new file mode 100644 index 00000000000..35d95cbc6c8 --- /dev/null +++ b/web/src/pages/data-flow/form/categorize-form/dynamic-example.tsx @@ -0,0 +1,68 @@ +import { Button } from '@/components/ui/button'; +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Textarea } from '@/components/ui/textarea'; +import { Plus, X } from 'lucide-react'; +import { memo } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +type DynamicExampleProps = { name: string }; + +const DynamicExample = ({ name }: DynamicExampleProps) => { + const { t } = useTranslation(); + const form = useFormContext(); + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( + + {t('flow.examples')} +
    + {fields.map((field, index) => ( +
    + ( + + + + + + )} + /> + {index === 0 ? ( + + ) : ( + + )} +
    + ))} +
    + +
    + ); +}; + +export default memo(DynamicExample); diff --git a/web/src/pages/data-flow/form/categorize-form/index.tsx b/web/src/pages/data-flow/form/categorize-form/index.tsx new file mode 100644 index 00000000000..c36ff452946 --- /dev/null +++ b/web/src/pages/data-flow/form/categorize-form/index.tsx @@ -0,0 +1,48 @@ +import { FormContainer } from '@/components/form-container'; +import { LargeModelFormField } from '@/components/large-model-form-field'; +import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; +import { Form } from '@/components/ui/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm } from 'react-hook-form'; +import { initialCategorizeValues } from '../../constant'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; +import DynamicCategorize from './dynamic-categorize'; +import { useCreateCategorizeFormSchema } from './use-form-schema'; +import { useValues } from './use-values'; +import { useWatchFormChange } from './use-watch-change'; + +const outputList = buildOutputList(initialCategorizeValues.outputs); + +function CategorizeForm({ node }: INextOperatorForm) { + const values = useValues(node); + + const FormSchema = useCreateCategorizeFormSchema(); + + const form = useForm({ + defaultValues: values, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + + +
    + ); +} + +export default memo(CategorizeForm); diff --git a/web/src/pages/data-flow/form/categorize-form/use-form-schema.ts b/web/src/pages/data-flow/form/categorize-form/use-form-schema.ts new file mode 100644 index 00000000000..9e56bb18b21 --- /dev/null +++ b/web/src/pages/data-flow/form/categorize-form/use-form-schema.ts @@ -0,0 +1,32 @@ +import { LlmSettingSchema } from '@/components/llm-setting-items/next'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +export function useCreateCategorizeFormSchema() { + const { t } = useTranslation(); + + const FormSchema = z.object({ + query: z.string().optional(), + parameter: z.string().optional(), + ...LlmSettingSchema, + message_history_window_size: z.coerce.number(), + items: z.array( + z + .object({ + name: z.string().min(1, t('flow.nameMessage')).trim(), + description: z.string().optional(), + uuid: z.string(), + examples: z + .array( + z.object({ + value: z.string(), + }), + ) + .optional(), + }) + .optional(), + ), + }); + + return FormSchema; +} diff --git a/web/src/pages/data-flow/form/categorize-form/use-values.ts b/web/src/pages/data-flow/form/categorize-form/use-values.ts new file mode 100644 index 00000000000..a920ec4cce4 --- /dev/null +++ b/web/src/pages/data-flow/form/categorize-form/use-values.ts @@ -0,0 +1,34 @@ +import { ModelVariableType } from '@/constants/knowledge'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty, isPlainObject } from 'lodash'; +import { useMemo } from 'react'; + +const defaultValues = { + parameter: ModelVariableType.Precise, + message_history_window_size: 1, + temperatureEnabled: true, + topPEnabled: true, + presencePenaltyEnabled: true, + frequencyPenaltyEnabled: true, + maxTokensEnabled: true, + items: [], +}; + +export function useValues(node?: RAGFlowNodeType) { + const values = useMemo(() => { + const formData = node?.data?.form; + if (isEmpty(formData)) { + return defaultValues; + } + if (isPlainObject(formData)) { + // const nextValues = { + // ...omit(formData, 'category_description'), + // items, + // }; + + return formData; + } + }, [node]); + + return values; +} diff --git a/web/src/pages/data-flow/form/categorize-form/use-watch-change.ts b/web/src/pages/data-flow/form/categorize-form/use-watch-change.ts new file mode 100644 index 00000000000..a97b80a77f2 --- /dev/null +++ b/web/src/pages/data-flow/form/categorize-form/use-watch-change.ts @@ -0,0 +1,17 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../../store'; + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id) { + values = form?.getValues(); + + updateNodeForm(id, { ...values, items: values.items?.slice() || [] }); + } + }, [id, updateNodeForm, values]); +} diff --git a/web/src/pages/data-flow/form/code-form/index.tsx b/web/src/pages/data-flow/form/code-form/index.tsx new file mode 100644 index 00000000000..2883fdf467b --- /dev/null +++ b/web/src/pages/data-flow/form/code-form/index.tsx @@ -0,0 +1,168 @@ +import Editor, { loader } from '@monaco-editor/react'; +import { INextOperatorForm } from '../../interface'; + +import { FormContainer } from '@/components/form-container'; +import { useIsDarkTheme } from '@/components/theme-provider'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { ProgrammingLanguage } from '@/constants/agent'; +import { ICodeForm } from '@/interfaces/database/agent'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { + DynamicInputVariable, + TypeOptions, + VariableTitle, +} from './next-variable'; +import { FormSchema, FormSchemaType } from './schema'; +import { useValues } from './use-values'; +import { + useHandleLanguageChange, + useWatchFormChange, +} from './use-watch-change'; + +loader.config({ paths: { vs: '/vs' } }); + +const options = [ + ProgrammingLanguage.Python, + ProgrammingLanguage.Javascript, +].map((x) => ({ value: x, label: x })); + +const DynamicFieldName = 'outputs'; + +function CodeForm({ node }: INextOperatorForm) { + const formData = node?.data.form as ICodeForm; + const { t } = useTranslation(); + const values = useValues(node); + const isDarkTheme = useIsDarkTheme(); + + const form = useForm({ + defaultValues: values, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + const handleLanguageChange = useHandleLanguageChange(node?.id, form); + + return ( +
    + + + ( + + + Code + ( + + + { + field.onChange(val); + handleLanguageChange(val); + }} + options={options} + /> + + + + )} + /> + + + + + + + )} + /> + + {formData.lang === ProgrammingLanguage.Python ? ( + + ) : ( +
    + + + ( + + Name + + + + + + )} + /> + ( + + Type + + + + + + )} + /> + +
    + )} +
    +
    + +
    +
    + ); +} + +export default memo(CodeForm); diff --git a/web/src/pages/data-flow/form/code-form/next-variable.tsx b/web/src/pages/data-flow/form/code-form/next-variable.tsx new file mode 100644 index 00000000000..39a2dd4a48d --- /dev/null +++ b/web/src/pages/data-flow/form/code-form/next-variable.tsx @@ -0,0 +1,128 @@ +'use client'; + +import { FormContainer } from '@/components/form-container'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { BlockButton, Button } from '@/components/ui/button'; +import { + FormControl, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { BlurInput } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { Separator } from '@/components/ui/separator'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { X } from 'lucide-react'; +import { ReactNode } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; + +interface IProps { + node?: RAGFlowNodeType; + name?: string; + isOutputs: boolean; +} + +export const TypeOptions = [ + 'String', + 'Number', + 'Boolean', + 'Array', + 'Array', + 'Object', +].map((x) => ({ label: x, value: x })); + +export function DynamicVariableForm({ name = 'arguments', isOutputs }: IProps) { + const { t } = useTranslation(); + const form = useFormContext(); + + const { fields, remove, append } = useFieldArray({ + name: name, + control: form.control, + }); + + const nextOptions = useBuildQueryVariableOptions(); + + return ( +
    + {fields.map((field, index) => { + const typeField = `${name}.${index}.name`; + return ( +
    + ( + + + + + + + )} + /> + + ( + + + {isOutputs ? ( + + ) : ( + + )} + + + + )} + /> + +
    + ); + })} + append({ name: '', type: undefined })}> + {t('flow.addVariable')} + +
    + ); +} + +export function VariableTitle({ title }: { title: ReactNode }) { + return
    {title}
    ; +} + +export function DynamicInputVariable({ + node, + name, + title, + isOutputs = false, +}: IProps & { title: ReactNode }) { + return ( +
    + + + + +
    + ); +} diff --git a/web/src/pages/data-flow/form/code-form/schema.ts b/web/src/pages/data-flow/form/code-form/schema.ts new file mode 100644 index 00000000000..fe694444e20 --- /dev/null +++ b/web/src/pages/data-flow/form/code-form/schema.ts @@ -0,0 +1,14 @@ +import { ProgrammingLanguage } from '@/constants/agent'; +import { z } from 'zod'; + +export const FormSchema = z.object({ + lang: z.enum([ProgrammingLanguage.Python, ProgrammingLanguage.Javascript]), + script: z.string(), + arguments: z.array(z.object({ name: z.string(), type: z.string() })), + outputs: z.union([ + z.array(z.object({ name: z.string(), type: z.string() })).optional(), + z.object({ name: z.string(), type: z.string() }), + ]), +}); + +export type FormSchemaType = z.infer; diff --git a/web/src/pages/data-flow/form/code-form/use-values.ts b/web/src/pages/data-flow/form/code-form/use-values.ts new file mode 100644 index 00000000000..ea6f2d67cf8 --- /dev/null +++ b/web/src/pages/data-flow/form/code-form/use-values.ts @@ -0,0 +1,47 @@ +import { ProgrammingLanguage } from '@/constants/agent'; +import { ICodeForm } from '@/interfaces/database/agent'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { initialCodeValues } from '../../constant'; + +function convertToArray(args: Record) { + return Object.entries(args).map(([key, value]) => ({ + name: key, + type: value, + })); +} + +type OutputsFormType = { name: string; type: string }; + +function convertOutputsToArray({ lang, outputs = {} }: ICodeForm) { + if (lang === ProgrammingLanguage.Python) { + return Object.entries(outputs).map(([key, val]) => ({ + name: key, + type: val.type, + })); + } + return Object.entries(outputs).reduce((pre, [key, val]) => { + pre.name = key; + pre.type = val.type; + return pre; + }, {} as OutputsFormType); +} + +export function useValues(node?: RAGFlowNodeType) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return initialCodeValues; + } + + return { + ...formData, + arguments: convertToArray(formData.arguments), + outputs: convertOutputsToArray(formData), + }; + }, [node?.data?.form]); + + return values; +} diff --git a/web/src/pages/data-flow/form/code-form/use-watch-change.ts b/web/src/pages/data-flow/form/code-form/use-watch-change.ts new file mode 100644 index 00000000000..80e0c8b15d7 --- /dev/null +++ b/web/src/pages/data-flow/form/code-form/use-watch-change.ts @@ -0,0 +1,95 @@ +import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; +import { ICodeForm } from '@/interfaces/database/agent'; +import { isEmpty } from 'lodash'; +import { useCallback, useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../../store'; +import { FormSchemaType } from './schema'; + +function convertToObject(list: FormSchemaType['arguments'] = []) { + return list.reduce>((pre, cur) => { + pre[cur.name] = cur.type; + + return pre; + }, {}); +} + +type ArrayOutputs = Extract>; + +type ObjectOutputs = Exclude>; + +function convertOutputsToObject({ lang, outputs }: FormSchemaType) { + if (lang === ProgrammingLanguage.Python) { + return (outputs as ArrayOutputs).reduce( + (pre, cur) => { + pre[cur.name] = { + value: '', + type: cur.type, + }; + + return pre; + }, + {}, + ); + } + const outputsObject = outputs as ObjectOutputs; + if (isEmpty(outputsObject)) { + return {}; + } + return { + [outputsObject.name]: { + value: '', + type: outputsObject.type, + }, + }; +} + +export function useWatchFormChange( + id?: string, + form?: UseFormReturn, +) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id) { + values = form?.getValues() || {}; + let nextValues: any = { + ...values, + arguments: convertToObject( + values?.arguments as FormSchemaType['arguments'], + ), + outputs: convertOutputsToObject(values as FormSchemaType), + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} + +export function useHandleLanguageChange( + id?: string, + form?: UseFormReturn, +) { + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + const handleLanguageChange = useCallback( + (lang: string) => { + if (id) { + const script = CodeTemplateStrMap[lang as ProgrammingLanguage]; + form?.setValue('script', script); + form?.setValue( + 'outputs', + (lang === ProgrammingLanguage.Python + ? [] + : {}) as FormSchemaType['outputs'], + ); + updateNodeForm(id, script, ['script']); + } + }, + [form, id, updateNodeForm], + ); + + return handleLanguageChange; +} diff --git a/web/src/pages/data-flow/form/components/api-key-field.tsx b/web/src/pages/data-flow/form/components/api-key-field.tsx new file mode 100644 index 00000000000..f9debfc4f3c --- /dev/null +++ b/web/src/pages/data-flow/form/components/api-key-field.tsx @@ -0,0 +1,32 @@ +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { t } from 'i18next'; +import { useFormContext } from 'react-hook-form'; + +interface IApiKeyFieldProps { + placeholder?: string; +} +export function ApiKeyField({ placeholder }: IApiKeyFieldProps) { + const form = useFormContext(); + return ( + ( + + {t('flow.apiKey')} + + + + + + )} + /> + ); +} diff --git a/web/src/pages/data-flow/form/components/description-field.tsx b/web/src/pages/data-flow/form/components/description-field.tsx new file mode 100644 index 00000000000..8fa2eef6402 --- /dev/null +++ b/web/src/pages/data-flow/form/components/description-field.tsx @@ -0,0 +1,27 @@ +import { + FormControl, + FormField, + FormItem, + FormLabel, +} from '@/components/ui/form'; +import { Textarea } from '@/components/ui/textarea'; +import { t } from 'i18next'; +import { useFormContext } from 'react-hook-form'; + +export function DescriptionField() { + const form = useFormContext(); + return ( + ( + + {t('flow.description')} + + + + + )} + /> + ); +} diff --git a/web/src/pages/data-flow/form/components/dynamic-input-variable.tsx b/web/src/pages/data-flow/form/components/dynamic-input-variable.tsx new file mode 100644 index 00000000000..a5781fd16f9 --- /dev/null +++ b/web/src/pages/data-flow/form/components/dynamic-input-variable.tsx @@ -0,0 +1,127 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; +import { Button, Collapse, Flex, Form, Input, Select } from 'antd'; +import { PropsWithChildren, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useBuildVariableOptions } from '../../hooks/use-get-begin-query'; + +import styles from './index.less'; + +interface IProps { + node?: RAGFlowNodeType; +} + +enum VariableType { + Reference = 'reference', + Input = 'input', +} + +const getVariableName = (type: string) => + type === VariableType.Reference ? 'component_id' : 'value'; + +const DynamicVariableForm = ({ node }: IProps) => { + const { t } = useTranslation(); + const valueOptions = useBuildVariableOptions(node?.id, node?.parentId); + const form = Form.useFormInstance(); + + const options = [ + { value: VariableType.Reference, label: t('flow.reference') }, + { value: VariableType.Input, label: t('flow.text') }, + ]; + + const handleTypeChange = useCallback( + (name: number) => () => { + setTimeout(() => { + form.setFieldValue(['query', name, 'component_id'], undefined); + form.setFieldValue(['query', name, 'value'], undefined); + }, 0); + }, + [form], + ); + + return ( + + {(fields, { add, remove }) => ( + <> + {fields.map(({ key, name, ...restField }) => ( + + + + + + {({ getFieldValue }) => { + const type = getFieldValue(['query', name, 'type']); + return ( + + {type === VariableType.Reference ? ( + + ) : ( + + )} + + ); + }} + + remove(name)} /> + + ))} + + + + + )} + + ); +}; + +export function FormCollapse({ + children, + title, +}: PropsWithChildren<{ title: string }>) { + return ( + {title}, + children, + }, + ]} + /> + ); +} + +const DynamicInputVariable = ({ node }: IProps) => { + const { t } = useTranslation(); + return ( + + + + ); +}; + +export default DynamicInputVariable; diff --git a/web/src/pages/data-flow/form/components/form-wrapper.tsx b/web/src/pages/data-flow/form/components/form-wrapper.tsx new file mode 100644 index 00000000000..d1985b18e07 --- /dev/null +++ b/web/src/pages/data-flow/form/components/form-wrapper.tsx @@ -0,0 +1,16 @@ +type FormProps = React.ComponentProps<'form'>; + +export function FormWrapper({ children, ...props }: FormProps) { + return ( +
    { + e.preventDefault(); + }} + {...props} + > + {children} +
    + ); +} diff --git a/web/src/pages/data-flow/form/components/index.less b/web/src/pages/data-flow/form/components/index.less new file mode 100644 index 00000000000..344514d9e2d --- /dev/null +++ b/web/src/pages/data-flow/form/components/index.less @@ -0,0 +1,22 @@ +.dynamicInputVariable { + background-color: #ebe9e950; + :global(.ant-collapse-content) { + background-color: #f6f6f657; + } + margin-bottom: 20px; + .title { + font-weight: 600; + font-size: 16px; + } + .variableType { + width: 30%; + } + .variableValue { + flex: 1; + } + + .addButton { + color: rgb(22, 119, 255); + font-weight: 600; + } +} diff --git a/web/src/pages/data-flow/form/components/next-dynamic-input-variable.tsx b/web/src/pages/data-flow/form/components/next-dynamic-input-variable.tsx new file mode 100644 index 00000000000..8b4cbd8a95b --- /dev/null +++ b/web/src/pages/data-flow/form/components/next-dynamic-input-variable.tsx @@ -0,0 +1,135 @@ +'use client'; + +import { SideDown } from '@/assets/icon/next-icon'; +import { Button } from '@/components/ui/button'; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '@/components/ui/collapsible'; +import { + FormControl, + FormDescription, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { Plus, Trash2 } from 'lucide-react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useBuildVariableOptions } from '../../hooks/use-get-begin-query'; + +interface IProps { + node?: RAGFlowNodeType; +} + +enum VariableType { + Reference = 'reference', + Input = 'input', +} + +const getVariableName = (type: string) => + type === VariableType.Reference ? 'component_id' : 'value'; + +export function DynamicVariableForm({ node }: IProps) { + const { t } = useTranslation(); + const form = useFormContext(); + const { fields, remove, append } = useFieldArray({ + name: 'query', + control: form.control, + }); + + const valueOptions = useBuildVariableOptions(node?.id, node?.parentId); + + const options = [ + { value: VariableType.Reference, label: t('flow.reference') }, + { value: VariableType.Input, label: t('flow.text') }, + ]; + + return ( +
    + {fields.map((field, index) => { + const typeField = `query.${index}.type`; + const typeValue = form.watch(typeField); + return ( +
    + ( + + + + { + field.onChange(val); + form.resetField(`query.${index}.value`); + form.resetField(`query.${index}.component_id`); + }} + > + + + + )} + /> + ( + + + + {typeValue === VariableType.Reference ? ( + + ) : ( + + )} + + + + )} + /> + remove(index)} + /> +
    + ); + })} + +
    + ); +} + +export function DynamicInputVariable({ node }: IProps) { + const { t } = useTranslation(); + + return ( + + + + {t('flow.input')} + + + + + + + + ); +} diff --git a/web/src/pages/data-flow/form/components/output.tsx b/web/src/pages/data-flow/form/components/output.tsx new file mode 100644 index 00000000000..c481c7a3884 --- /dev/null +++ b/web/src/pages/data-flow/form/components/output.tsx @@ -0,0 +1,35 @@ +import { t } from 'i18next'; + +export type OutputType = { + title: string; + type?: string; +}; + +type OutputProps = { + list: Array; +}; + +export function transferOutputs(outputs: Record) { + return Object.entries(outputs).map(([key, value]) => ({ + title: key, + type: value?.type, + })); +} + +export function Output({ list }: OutputProps) { + return ( +
    +
    {t('flow.output')}
    +
      + {list.map((x, idx) => ( +
    • + {x.title}: {x.type} +
    • + ))} +
    +
    + ); +} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/constant.ts b/web/src/pages/data-flow/form/components/prompt-editor/constant.ts new file mode 100644 index 00000000000..b6cf30ed9cd --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/constant.ts @@ -0,0 +1 @@ +export const ProgrammaticTag = 'programmatic'; diff --git a/web/src/pages/data-flow/form/components/prompt-editor/index.css b/web/src/pages/data-flow/form/components/prompt-editor/index.css new file mode 100644 index 00000000000..8f305064721 --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/index.css @@ -0,0 +1,76 @@ +.typeahead-popover { + background: #fff; + box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3); + border-radius: 8px; + position: fixed; + z-index: 1000; +} + +.typeahead-popover ul { + list-style: none; + margin: 0; + max-height: 200px; + overflow-y: scroll; +} + +.typeahead-popover ul::-webkit-scrollbar { + display: none; +} + +.typeahead-popover ul { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.typeahead-popover ul li { + margin: 0; + min-width: 180px; + font-size: 14px; + outline: none; + cursor: pointer; + border-radius: 8px; +} + +.typeahead-popover ul li.selected { + background: #eee; +} + +.typeahead-popover li { + margin: 0 8px 0 8px; + color: #050505; + cursor: pointer; + line-height: 16px; + font-size: 15px; + display: flex; + align-content: center; + flex-direction: row; + flex-shrink: 0; + background-color: #fff; + border: 0; +} + +.typeahead-popover li.active { + display: flex; + width: 20px; + height: 20px; + background-size: contain; +} + +.typeahead-popover li .text { + display: flex; + line-height: 20px; + flex-grow: 1; + min-width: 150px; +} + +.typeahead-popover li .icon { + display: flex; + width: 20px; + height: 20px; + user-select: none; + margin-right: 8px; + line-height: 16px; + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/index.tsx b/web/src/pages/data-flow/form/components/prompt-editor/index.tsx new file mode 100644 index 00000000000..caf6914b45c --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/index.tsx @@ -0,0 +1,181 @@ +import { CodeHighlightNode, CodeNode } from '@lexical/code'; +import { + InitialConfigType, + LexicalComposer, +} from '@lexical/react/LexicalComposer'; +import { ContentEditable } from '@lexical/react/LexicalContentEditable'; +import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'; +import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; +import { HeadingNode, QuoteNode } from '@lexical/rich-text'; +import { + $getRoot, + $getSelection, + EditorState, + Klass, + LexicalNode, +} from 'lexical'; + +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { cn } from '@/lib/utils'; +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; +import { Variable } from 'lucide-react'; +import { ReactNode, useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PasteHandlerPlugin } from './paste-handler-plugin'; +import theme from './theme'; +import { VariableNode } from './variable-node'; +import { VariableOnChangePlugin } from './variable-on-change-plugin'; +import VariablePickerMenuPlugin from './variable-picker-plugin'; + +// Catch any errors that occur during Lexical updates and log them +// or throw them as needed. If you don't throw them, Lexical will +// try to recover gracefully without losing user data. +function onError(error: Error) { + console.error(error); +} + +const Nodes: Array> = [ + HeadingNode, + QuoteNode, + CodeHighlightNode, + CodeNode, + VariableNode, +]; + +type PromptContentProps = { showToolbar?: boolean; multiLine?: boolean }; + +type IProps = { + value?: string; + onChange?: (value?: string) => void; + placeholder?: ReactNode; +} & PromptContentProps; + +function PromptContent({ + showToolbar = true, + multiLine = true, +}: PromptContentProps) { + const [editor] = useLexicalComposerContext(); + const [isBlur, setIsBlur] = useState(false); + const { t } = useTranslation(); + + const insertTextAtCursor = useCallback(() => { + editor.update(() => { + const selection = $getSelection(); + + if (selection !== null) { + selection.insertText(' /'); + } + }); + }, [editor]); + + const handleVariableIconClick = useCallback(() => { + insertTextAtCursor(); + }, [insertTextAtCursor]); + + const handleBlur = useCallback(() => { + setIsBlur(true); + }, []); + + const handleFocus = useCallback(() => { + setIsBlur(false); + }, []); + + return ( +
    + {showToolbar && ( +
    + + + + + + + +

    {t('flow.insertVariableTip')}

    +
    +
    +
    + )} + +
    + ); +} + +export function PromptEditor({ + value, + onChange, + placeholder, + showToolbar, + multiLine = true, +}: IProps) { + const { t } = useTranslation(); + const initialConfig: InitialConfigType = { + namespace: 'PromptEditor', + theme, + onError, + nodes: Nodes, + }; + + const onValueChange = useCallback( + (editorState: EditorState) => { + editorState?.read(() => { + // const listNodes = $nodesOfType(VariableNode); // to be removed + // const allNodes = $dfs(); + + const text = $getRoot().getTextContent(); + + onChange?.(text); + }); + }, + [onChange], + ); + + return ( +
    + + + } + placeholder={ +
    + {placeholder || t('common.promptPlaceholder')} +
    + } + ErrorBoundary={LexicalErrorBoundary} + /> + + + +
    +
    + ); +} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/paste-handler-plugin.tsx b/web/src/pages/data-flow/form/components/prompt-editor/paste-handler-plugin.tsx new file mode 100644 index 00000000000..a45a5e5fbfa --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/paste-handler-plugin.tsx @@ -0,0 +1,83 @@ +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; +import { + $createParagraphNode, + $createTextNode, + $getSelection, + $isRangeSelection, + PASTE_COMMAND, +} from 'lexical'; +import { useEffect } from 'react'; + +function PasteHandlerPlugin() { + const [editor] = useLexicalComposerContext(); + useEffect(() => { + const removeListener = editor.registerCommand( + PASTE_COMMAND, + (clipboardEvent: ClipboardEvent) => { + const clipboardData = clipboardEvent.clipboardData; + if (!clipboardData) { + return false; + } + + const text = clipboardData.getData('text/plain'); + if (!text) { + return false; + } + + // Check if text contains line breaks + if (text.includes('\n')) { + editor.update(() => { + const selection = $getSelection(); + if (selection && $isRangeSelection(selection)) { + // Normalize line breaks, merge multiple consecutive line breaks into a single line break + const normalizedText = text.replace(/\n{2,}/g, '\n'); + + // Clear current selection + selection.removeText(); + + // Create a paragraph node to contain all content + const paragraph = $createParagraphNode(); + + // Split text by line breaks + const lines = normalizedText.split('\n'); + + // Process each line + lines.forEach((lineText, index) => { + // Add line text (if any) + if (lineText) { + const textNode = $createTextNode(lineText); + paragraph.append(textNode); + } + + // If not the last line, add a line break + if (index < lines.length - 1) { + const lineBreak = $createTextNode('\n'); + paragraph.append(lineBreak); + } + }); + + // Insert paragraph + selection.insertNodes([paragraph]); + } + }); + + // Prevent default paste behavior + clipboardEvent.preventDefault(); + return true; + } + + // If no line breaks, use default behavior + return false; + }, + 4, + ); + + return () => { + removeListener(); + }; + }, [editor]); + + return null; +} + +export { PasteHandlerPlugin }; diff --git a/web/src/pages/data-flow/form/components/prompt-editor/theme.ts b/web/src/pages/data-flow/form/components/prompt-editor/theme.ts new file mode 100644 index 00000000000..1cc2bc15528 --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/theme.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +export default { + code: 'editor-code', + heading: { + h1: 'editor-heading-h1', + h2: 'editor-heading-h2', + h3: 'editor-heading-h3', + h4: 'editor-heading-h4', + h5: 'editor-heading-h5', + }, + image: 'editor-image', + link: 'editor-link', + list: { + listitem: 'editor-listitem', + nested: { + listitem: 'editor-nested-listitem', + }, + ol: 'editor-list-ol', + ul: 'editor-list-ul', + }, + ltr: 'ltr', + paragraph: 'editor-paragraph', + placeholder: 'editor-placeholder', + quote: 'editor-quote', + rtl: 'rtl', + text: { + bold: 'editor-text-bold', + code: 'editor-text-code', + hashtag: 'editor-text-hashtag', + italic: 'editor-text-italic', + overflowed: 'editor-text-overflowed', + strikethrough: 'editor-text-strikethrough', + underline: 'editor-text-underline', + underlineStrikethrough: 'editor-text-underlineStrikethrough', + }, +}; diff --git a/web/src/pages/data-flow/form/components/prompt-editor/variable-node.tsx b/web/src/pages/data-flow/form/components/prompt-editor/variable-node.tsx new file mode 100644 index 00000000000..177c370c9c6 --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/variable-node.tsx @@ -0,0 +1,91 @@ +import { BeginId } from '@/pages/flow/constant'; +import { DecoratorNode, LexicalNode, NodeKey } from 'lexical'; +import { ReactNode } from 'react'; +const prefix = BeginId + '@'; + +export class VariableNode extends DecoratorNode { + __value: string; + __label: string; + key?: NodeKey; + __parentLabel?: string | ReactNode; + __icon?: ReactNode; + + static getType(): string { + return 'variable'; + } + + static clone(node: VariableNode): VariableNode { + return new VariableNode( + node.__value, + node.__label, + node.__key, + node.__parentLabel, + node.__icon, + ); + } + + constructor( + value: string, + label: string, + key?: NodeKey, + parent?: string | ReactNode, + icon?: ReactNode, + ) { + super(key); + this.__value = value; + this.__label = label; + this.__parentLabel = parent; + this.__icon = icon; + } + + createDOM(): HTMLElement { + const dom = document.createElement('span'); + dom.className = 'mr-1'; + + return dom; + } + + updateDOM(): false { + return false; + } + + decorate(): ReactNode { + let content: ReactNode = ( +
    {this.__label}
    + ); + if (this.__parentLabel) { + content = ( +
    +
    {this.__icon}
    +
    {this.__parentLabel}
    +
    /
    + {content} +
    + ); + } + return ( +
    + {content} +
    + ); + } + + getTextContent(): string { + return `{${this.__value}}`; + } +} + +export function $createVariableNode( + value: string, + label: string, + parentLabel: string | ReactNode, + icon?: ReactNode, +): VariableNode { + return new VariableNode(value, label, undefined, parentLabel, icon); +} + +export function $isVariableNode( + node: LexicalNode | null | undefined, +): node is VariableNode { + return node instanceof VariableNode; +} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/variable-on-change-plugin.tsx b/web/src/pages/data-flow/form/components/prompt-editor/variable-on-change-plugin.tsx new file mode 100644 index 00000000000..86fa66db4f8 --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/variable-on-change-plugin.tsx @@ -0,0 +1,35 @@ +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; +import { EditorState, LexicalEditor } from 'lexical'; +import { useEffect } from 'react'; +import { ProgrammaticTag } from './constant'; + +interface IProps { + onChange: ( + editorState: EditorState, + editor?: LexicalEditor, + tags?: Set, + ) => void; +} + +export function VariableOnChangePlugin({ onChange }: IProps) { + // Access the editor through the LexicalComposerContext + const [editor] = useLexicalComposerContext(); + // Wrap our listener in useEffect to handle the teardown and avoid stale references. + useEffect(() => { + // most listeners return a teardown function that can be called to clean them up. + return editor.registerUpdateListener( + ({ editorState, tags, dirtyElements }) => { + // Check if there is a "programmatic" tag + const isProgrammaticUpdate = tags.has(ProgrammaticTag); + + // The onchange event is only triggered when the data is manually updated + // Otherwise, the content will be displayed incorrectly. + if (dirtyElements.size > 0 && !isProgrammaticUpdate) { + onChange(editorState); + } + }, + ); + }, [editor, onChange]); + + return null; +} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/variable-picker-plugin.tsx b/web/src/pages/data-flow/form/components/prompt-editor/variable-picker-plugin.tsx new file mode 100644 index 00000000000..f429981c780 --- /dev/null +++ b/web/src/pages/data-flow/form/components/prompt-editor/variable-picker-plugin.tsx @@ -0,0 +1,297 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; +import { + LexicalTypeaheadMenuPlugin, + MenuOption, + useBasicTypeaheadTriggerMatch, +} from '@lexical/react/LexicalTypeaheadMenuPlugin'; +import { + $createParagraphNode, + $createTextNode, + $getRoot, + $getSelection, + $isRangeSelection, + TextNode, +} from 'lexical'; +import React, { + ReactElement, + ReactNode, + useCallback, + useEffect, + useRef, +} from 'react'; +import * as ReactDOM from 'react-dom'; + +import { $createVariableNode } from './variable-node'; + +import { useBuildQueryVariableOptions } from '@/pages/agent/hooks/use-get-begin-query'; +import { ProgrammaticTag } from './constant'; +import './index.css'; +class VariableInnerOption extends MenuOption { + label: string; + value: string; + parentLabel: string | JSX.Element; + icon?: ReactNode; + + constructor( + label: string, + value: string, + parentLabel: string | JSX.Element, + icon?: ReactNode, + ) { + super(value); + this.label = label; + this.value = value; + this.parentLabel = parentLabel; + this.icon = icon; + } +} + +class VariableOption extends MenuOption { + label: ReactElement | string; + title: string; + options: VariableInnerOption[]; + + constructor( + label: ReactElement | string, + title: string, + options: VariableInnerOption[], + ) { + super(title); + this.label = label; + this.title = title; + this.options = options; + } +} + +function VariablePickerMenuItem({ + index, + option, + selectOptionAndCleanUp, +}: { + index: number; + option: VariableOption; + selectOptionAndCleanUp: ( + option: VariableOption | VariableInnerOption, + ) => void; +}) { + return ( +
  • +
    + {option.title} +
      + {option.options.map((x) => ( +
    • selectOptionAndCleanUp(x)} + className="hover:bg-slate-300 p-1" + > + {x.label} +
    • + ))} +
    +
    +
  • + ); +} + +export default function VariablePickerMenuPlugin({ + value, +}: { + value?: string; +}): JSX.Element { + const [editor] = useLexicalComposerContext(); + const isFirstRender = useRef(true); + + const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', { + minLength: 0, + }); + + const [queryString, setQueryString] = React.useState(''); + + const options = useBuildQueryVariableOptions(); + + const buildNextOptions = useCallback(() => { + let filteredOptions = options; + if (queryString) { + const lowerQuery = queryString.toLowerCase(); + filteredOptions = options + .map((x) => ({ + ...x, + options: x.options.filter( + (y) => + y.label.toLowerCase().includes(lowerQuery) || + y.value.toLowerCase().includes(lowerQuery), + ), + })) + .filter((x) => x.options.length > 0); + } + + const nextOptions: VariableOption[] = filteredOptions.map( + (x) => + new VariableOption( + x.label, + x.title, + x.options.map((y) => { + return new VariableInnerOption(y.label, y.value, x.label, y.icon); + }), + ), + ); + return nextOptions; + }, [options, queryString]); + + const findItemByValue = useCallback( + (value: string) => { + const children = options.reduce< + Array<{ + label: string; + value: string; + parentLabel?: string | ReactNode; + icon?: ReactNode; + }> + >((pre, cur) => { + return pre.concat(cur.options); + }, []); + + return children.find((x) => x.value === value); + }, + [options], + ); + + const onSelectOption = useCallback( + ( + selectedOption: VariableOption | VariableInnerOption, + nodeToRemove: TextNode | null, + closeMenu: () => void, + ) => { + editor.update(() => { + const selection = $getSelection(); + + if (!$isRangeSelection(selection) || selectedOption === null) { + return; + } + + if (nodeToRemove) { + nodeToRemove.remove(); + } + const variableNode = $createVariableNode( + (selectedOption as VariableInnerOption).value, + selectedOption.label as string, + selectedOption.parentLabel as string | ReactNode, + selectedOption.icon as ReactNode, + ); + selection.insertNodes([variableNode]); + + closeMenu(); + }); + }, + [editor], + ); + + const parseTextToVariableNodes = useCallback( + (text: string) => { + const paragraph = $createParagraphNode(); + + // Regular expression to match content within {} + const regex = /{([^}]*)}/g; + let match; + let lastIndex = 0; + while ((match = regex.exec(text)) !== null) { + const { 1: content, index, 0: template } = match; + + // Add the previous text part (if any) + if (index > lastIndex) { + const textNode = $createTextNode(text.slice(lastIndex, index)); + + paragraph.append(textNode); + } + + // Add variable node or text node + const nodeItem = findItemByValue(content); + + if (nodeItem) { + paragraph.append( + $createVariableNode( + content, + nodeItem.label, + nodeItem.parentLabel, + nodeItem.icon, + ), + ); + } else { + paragraph.append($createTextNode(template)); + } + + // Update index + lastIndex = regex.lastIndex; + } + + // Add the last part of text (if any) + if (lastIndex < text.length) { + const textNode = $createTextNode(text.slice(lastIndex)); + paragraph.append(textNode); + } + + $getRoot().clear().append(paragraph); + + if ($isRangeSelection($getSelection())) { + $getRoot().selectEnd(); + } + }, + [findItemByValue], + ); + + useEffect(() => { + if (editor && value && isFirstRender.current) { + isFirstRender.current = false; + editor.update( + () => { + parseTextToVariableNodes(value); + }, + { tag: ProgrammaticTag }, + ); + } + }, [parseTextToVariableNodes, editor, value]); + + return ( + + onQueryChange={setQueryString} + onSelectOption={onSelectOption} + triggerFn={checkForTriggerMatch} + options={buildNextOptions()} + menuRenderFn={(anchorElementRef, { selectOptionAndCleanUp }) => { + const nextOptions = buildNextOptions(); + return anchorElementRef.current && nextOptions.length + ? ReactDOM.createPortal( +
    +
      + {nextOptions.map((option, i: number) => ( + + ))} +
    +
    , + anchorElementRef.current, + ) + : null; + }} + /> + ); +} diff --git a/web/src/pages/data-flow/form/components/query-variable.tsx b/web/src/pages/data-flow/form/components/query-variable.tsx new file mode 100644 index 00000000000..dafcb4cde79 --- /dev/null +++ b/web/src/pages/data-flow/form/components/query-variable.tsx @@ -0,0 +1,66 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { toLower } from 'lodash'; +import { ReactNode, useMemo } from 'react'; +import { useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { VariableType } from '../../constant'; +import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; + +type QueryVariableProps = { + name?: string; + type?: VariableType; + label?: ReactNode; +}; + +export function QueryVariable({ + name = 'query', + type, + label, +}: QueryVariableProps) { + const { t } = useTranslation(); + const form = useFormContext(); + + const nextOptions = useBuildQueryVariableOptions(); + + const finalOptions = useMemo(() => { + return type + ? nextOptions.map((x) => { + return { + ...x, + options: x.options.filter((y) => toLower(y.type).includes(type)), + }; + }) + : nextOptions; + }, [nextOptions, type]); + + return ( + ( + + {label || ( + + {t('flow.query')} + + )} + + + + + + )} + /> + ); +} diff --git a/web/src/pages/data-flow/form/crawler-form/index.tsx b/web/src/pages/data-flow/form/crawler-form/index.tsx new file mode 100644 index 00000000000..8c8da6b086b --- /dev/null +++ b/web/src/pages/data-flow/form/crawler-form/index.tsx @@ -0,0 +1,105 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useMemo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialCrawlerValues } from '../../constant'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { CrawlerResultOptions } from '../../options'; +import { QueryVariable } from '../components/query-variable'; + +export function CrawlerProxyFormField() { + const { t } = useTranslate('flow'); + const form = useFormContext(); + + return ( + ( + + {t('proxy')} + + + + + + )} + /> + ); +} + +export function CrawlerExtractTypeFormField() { + const { t } = useTranslate('flow'); + const form = useFormContext(); + const crawlerResultOptions = useMemo(() => { + return CrawlerResultOptions.map((x) => ({ + value: x, + label: t(`crawlerResultOptions.${x}`), + })); + }, [t]); + + return ( + ( + + {t('extractType')} + + + + + + )} + /> + ); +} + +export const CrawlerFormSchema = { + proxy: z.string().url(), + extract_type: z.string(), +}; + +const FormSchema = z.object({ + query: z.string().optional(), + ...CrawlerFormSchema, +}); + +function CrawlerForm({ node }: INextOperatorForm) { + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: initialCrawlerValues, + mode: 'onChange', + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + { + e.preventDefault(); + }} + > + + + +
    + + ); +} + +export default memo(CrawlerForm); diff --git a/web/src/pages/data-flow/form/deepl-form/index.tsx b/web/src/pages/data-flow/form/deepl-form/index.tsx new file mode 100644 index 00000000000..55240b3c120 --- /dev/null +++ b/web/src/pages/data-flow/form/deepl-form/index.tsx @@ -0,0 +1,36 @@ +import TopNItem from '@/components/top-n-item'; +import { useTranslate } from '@/hooks/common-hooks'; +import { Form, Select } from 'antd'; +import { useBuildSortOptions } from '../../form-hooks'; +import { IOperatorForm } from '../../interface'; +import { DeepLSourceLangOptions, DeepLTargetLangOptions } from '../../options'; +import DynamicInputVariable from '../components/dynamic-input-variable'; + +const DeepLForm = ({ onValuesChange, form, node }: IOperatorForm) => { + const { t } = useTranslate('flow'); + const options = useBuildSortOptions(); + + return ( +
    + + + + + + + + + + + +
    + ); +}; + +export default DeepLForm; diff --git a/web/src/pages/data-flow/form/duckduckgo-form/index.tsx b/web/src/pages/data-flow/form/duckduckgo-form/index.tsx new file mode 100644 index 00000000000..776635d3cd3 --- /dev/null +++ b/web/src/pages/data-flow/form/duckduckgo-form/index.tsx @@ -0,0 +1,91 @@ +import { FormContainer } from '@/components/form-container'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useMemo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { Channel, initialDuckValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +export const DuckDuckGoFormPartialSchema = { + top_n: z.string(), + channel: z.string(), +}; + +const FormSchema = z.object({ + query: z.string(), + ...DuckDuckGoFormPartialSchema, +}); + +export function DuckDuckGoWidgets() { + const { t } = useTranslate('flow'); + const form = useFormContext(); + + const options = useMemo(() => { + return Object.values(Channel).map((x) => ({ value: x, label: t(x) })); + }, [t]); + + return ( + <> + + ( + + {t('channel')} + + + + + + )} + /> + + ); +} + +const outputList = buildOutputList(initialDuckValues.outputs); + +function DuckDuckGoForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialDuckValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + +
    + +
    +
    + ); +} + +export default memo(DuckDuckGoForm); diff --git a/web/src/pages/data-flow/form/email-form/index.tsx b/web/src/pages/data-flow/form/email-form/index.tsx new file mode 100644 index 00000000000..b142dae7651 --- /dev/null +++ b/web/src/pages/data-flow/form/email-form/index.tsx @@ -0,0 +1,161 @@ +import { FormContainer } from '@/components/form-container'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { ReactNode } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialEmailValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { PromptEditor } from '../components/prompt-editor'; + +interface InputFormFieldProps { + name: string; + label: ReactNode; + type?: string; +} + +function InputFormField({ name, label, type }: InputFormFieldProps) { + const form = useFormContext(); + + return ( + ( + + {label} + + + + + + )} + /> + ); +} + +function PromptFormField({ name, label }: InputFormFieldProps) { + const form = useFormContext(); + + return ( + ( + + {label} + + + + + + )} + /> + ); +} +export function EmailFormWidgets() { + const { t } = useTranslate('flow'); + + return ( + <> + + + + + + + ); +} + +export const EmailFormPartialSchema = { + smtp_server: z.string(), + smtp_port: z.number(), + email: z.string(), + password: z.string(), + sender_name: z.string(), +}; + +const FormSchema = z.object({ + to_email: z.string(), + cc_email: z.string(), + content: z.string(), + subject: z.string(), + ...EmailFormPartialSchema, +}); + +const outputList = buildOutputList(initialEmailValues.outputs); + +const EmailForm = ({ node }: INextOperatorForm) => { + const { t } = useTranslate('flow'); + const defaultValues = useFormValues(initialEmailValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + + +
    + +
    +
    + ); +}; + +export default EmailForm; diff --git a/web/src/pages/data-flow/form/exesql-form/index.tsx b/web/src/pages/data-flow/form/exesql-form/index.tsx new file mode 100644 index 00000000000..5a6cb5ba693 --- /dev/null +++ b/web/src/pages/data-flow/form/exesql-form/index.tsx @@ -0,0 +1,167 @@ +import NumberInput from '@/components/originui/number-input'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { ButtonLoading } from '@/components/ui/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialExeSqlValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { ExeSQLOptions } from '../../options'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; +import { FormSchema, useSubmitForm } from './use-submit-form'; + +const outputList = buildOutputList(initialExeSqlValues.outputs); + +export function ExeSQLFormWidgets({ loading }: { loading: boolean }) { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + return ( + <> + ( + + {t('dbType')} + + + + + + )} + /> + ( + + {t('database')} + + + + + + )} + /> + ( + + {t('username')} + + + + + + )} + /> + ( + + {t('host')} + + + + + + )} + /> + ( + + {t('port')} + + + + + + )} + /> + ( + + {t('password')} + + + + + + )} + /> + + ( + + {t('maxRecords')} + + + + + + )} + /> + +
    + + {t('test')} + +
    + + ); +} + +function ExeSQLForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialExeSqlValues, node); + + const { onSubmit, loading } = useSubmitForm(); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues, + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + +
    + +
    +
    + ); +} + +export default memo(ExeSQLForm); diff --git a/web/src/pages/data-flow/form/exesql-form/use-submit-form.ts b/web/src/pages/data-flow/form/exesql-form/use-submit-form.ts new file mode 100644 index 00000000000..8be69c7b067 --- /dev/null +++ b/web/src/pages/data-flow/form/exesql-form/use-submit-form.ts @@ -0,0 +1,31 @@ +import { useTestDbConnect } from '@/hooks/use-agent-request'; +import { useCallback } from 'react'; +import { z } from 'zod'; + +export const ExeSQLFormSchema = { + db_type: z.string().min(1), + database: z.string().min(1), + username: z.string().min(1), + host: z.string().min(1), + port: z.number(), + password: z.string().min(1), + max_records: z.number(), +}; + +export const FormSchema = z.object({ + sql: z.string().optional(), + ...ExeSQLFormSchema, +}); + +export function useSubmitForm() { + const { testDbConnect, loading } = useTestDbConnect(); + + const onSubmit = useCallback( + async (data: z.infer) => { + testDbConnect(data); + }, + [testDbConnect], + ); + + return { loading, onSubmit }; +} diff --git a/web/src/pages/data-flow/form/github-form/index.tsx b/web/src/pages/data-flow/form/github-form/index.tsx new file mode 100644 index 00000000000..2e514a3e774 --- /dev/null +++ b/web/src/pages/data-flow/form/github-form/index.tsx @@ -0,0 +1,52 @@ +import { FormContainer } from '@/components/form-container'; +import { TopNFormField } from '@/components/top-n-item'; +import { Form } from '@/components/ui/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { initialGithubValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +export const FormSchema = z.object({ + query: z.string(), + top_n: z.number(), +}); + +const outputList = buildOutputList(initialGithubValues.outputs); + +function GithubForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialGithubValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + mode: 'onChange', + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + +
    + +
    +
    + ); +} + +export default memo(GithubForm); diff --git a/web/src/pages/data-flow/form/google-form/index.tsx b/web/src/pages/data-flow/form/google-form/index.tsx new file mode 100644 index 00000000000..cec5ae4c732 --- /dev/null +++ b/web/src/pages/data-flow/form/google-form/index.tsx @@ -0,0 +1,139 @@ +import { FormContainer } from '@/components/form-container'; +import NumberInput from '@/components/originui/number-input'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialGoogleValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; +import { buildOutputList } from '../../utils/build-output-list'; +import { ApiKeyField } from '../components/api-key-field'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +const outputList = buildOutputList(initialGoogleValues.outputs); + +export const GoogleFormPartialSchema = { + api_key: z.string(), + country: z.string(), + language: z.string(), +}; + +export const FormSchema = z.object({ + ...GoogleFormPartialSchema, + q: z.string(), + start: z.number(), + num: z.number(), +}); + +export function GoogleFormWidgets() { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + return ( + <> + ( + + {t('country')} + + + + + + )} + /> + ( + + {t('language')} + + + + + + )} + /> + + ); +} + +const GoogleForm = ({ node }: INextOperatorForm) => { + const { t } = useTranslate('flow'); + const defaultValues = useFormValues(initialGoogleValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + ( + + {t('flowStart')} + + + + + + )} + /> + ( + + {t('flowNum')} + + + + + + )} + /> + + + +
    + +
    +
    + ); +}; + +export default GoogleForm; diff --git a/web/src/pages/data-flow/form/google-scholar-form/index.tsx b/web/src/pages/data-flow/form/google-scholar-form/index.tsx new file mode 100644 index 00000000000..00f54056a1d --- /dev/null +++ b/web/src/pages/data-flow/form/google-scholar-form/index.tsx @@ -0,0 +1,166 @@ +import { FormContainer } from '@/components/form-container'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Switch } from '@/components/ui/switch'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { DatePicker, DatePickerProps } from 'antd'; +import dayjs from 'dayjs'; +import { memo, useCallback, useMemo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialGoogleScholarValues } from '../../constant'; +import { useBuildSortOptions } from '../../form-hooks'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +// TODO: To be replaced +const YearPicker = ({ + onChange, + value, +}: { + onChange?: (val: number | undefined) => void; + value?: number | undefined; +}) => { + const handleChange: DatePickerProps['onChange'] = useCallback( + (val: any) => { + const nextVal = val?.format('YYYY'); + onChange?.(nextVal ? Number(nextVal) : undefined); + }, + [onChange], + ); + // The year needs to be converted into a number and saved to the backend + const nextValue = useMemo(() => { + if (value) { + return dayjs(value.toString()); + } + return undefined; + }, [value]); + + return ; +}; + +export function GoogleScholarFormWidgets() { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + const options = useBuildSortOptions(); + + return ( + <> + + ( + + {t('sortBy')} + + + + + + )} + /> + ( + + {t('yearLow')} + + + + + + )} + /> + ( + + {t('yearHigh')} + + + + + + )} + /> + ( + + {t('patents')} + + + + + + )} + /> + + ); +} + +export const GoogleScholarFormPartialSchema = { + top_n: z.number(), + sort_by: z.string(), + year_low: z.number(), + year_high: z.number(), + patents: z.boolean(), +}; + +export const FormSchema = z.object({ + ...GoogleScholarFormPartialSchema, + query: z.string(), +}); + +const outputList = buildOutputList(initialGoogleScholarValues.outputs); + +function GoogleScholarForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialGoogleScholarValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + +
    + +
    +
    + ); +} + +export default memo(GoogleScholarForm); diff --git a/web/src/pages/data-flow/form/invoke-form/hooks.ts b/web/src/pages/data-flow/form/invoke-form/hooks.ts new file mode 100644 index 00000000000..951cd42ae72 --- /dev/null +++ b/web/src/pages/data-flow/form/invoke-form/hooks.ts @@ -0,0 +1,97 @@ +import get from 'lodash/get'; +import { + ChangeEventHandler, + MouseEventHandler, + useCallback, + useMemo, +} from 'react'; +import { v4 as uuid } from 'uuid'; +import { IGenerateParameter, IInvokeVariable } from '../../interface'; +import useGraphStore from '../../store'; + +export const useHandleOperateParameters = (nodeId: string) => { + const { getNode, updateNodeForm } = useGraphStore((state) => state); + const node = getNode(nodeId); + const dataSource: IGenerateParameter[] = useMemo( + () => get(node, 'data.form.variables', []) as IGenerateParameter[], + [node], + ); + + const changeValue = useCallback( + (row: IInvokeVariable, field: string, value: string) => { + const newData = [...dataSource]; + const index = newData.findIndex((item) => row.id === item.id); + const item = newData[index]; + newData.splice(index, 1, { + ...item, + [field]: value, + }); + + updateNodeForm(nodeId, { variables: newData }); + }, + [dataSource, nodeId, updateNodeForm], + ); + + const handleComponentIdChange = useCallback( + (row: IInvokeVariable) => (value: string) => { + changeValue(row, 'component_id', value); + }, + [changeValue], + ); + + const handleValueChange = useCallback( + (row: IInvokeVariable): ChangeEventHandler => + (e) => { + changeValue(row, 'value', e.target.value); + }, + [changeValue], + ); + + const handleRemove = useCallback( + (id?: string) => () => { + const newData = dataSource.filter((item) => item.id !== id); + updateNodeForm(nodeId, { variables: newData }); + }, + [updateNodeForm, nodeId, dataSource], + ); + + const handleAdd: MouseEventHandler = useCallback( + (e) => { + e.preventDefault(); + e.stopPropagation(); + updateNodeForm(nodeId, { + variables: [ + ...dataSource, + { + id: uuid(), + key: '', + component_id: undefined, + value: '', + }, + ], + }); + }, + [dataSource, nodeId, updateNodeForm], + ); + + const handleSave = (row: IGenerateParameter) => { + const newData = [...dataSource]; + const index = newData.findIndex((item) => row.id === item.id); + const item = newData[index]; + newData.splice(index, 1, { + ...item, + ...row, + }); + + updateNodeForm(nodeId, { variables: newData }); + }; + + return { + handleAdd, + handleRemove, + handleComponentIdChange, + handleValueChange, + handleSave, + dataSource, + }; +}; diff --git a/web/src/pages/data-flow/form/invoke-form/index.tsx b/web/src/pages/data-flow/form/invoke-form/index.tsx new file mode 100644 index 00000000000..3d67ec03046 --- /dev/null +++ b/web/src/pages/data-flow/form/invoke-form/index.tsx @@ -0,0 +1,226 @@ +import { Collapse } from '@/components/collapse'; +import { FormContainer } from '@/components/form-container'; +import NumberInput from '@/components/originui/number-input'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { Button } from '@/components/ui/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Switch } from '@/components/ui/switch'; +import { zodResolver } from '@hookform/resolvers/zod'; +import Editor, { loader } from '@monaco-editor/react'; +import { Plus } from 'lucide-react'; +import { memo } from 'react'; +import { useForm, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { initialInvokeValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { FormSchema, FormSchemaType } from './schema'; +import { useEditVariableRecord } from './use-edit-variable'; +import { VariableDialog } from './variable-dialog'; +import { VariableTable } from './variable-table'; + +loader.config({ paths: { vs: '/vs' } }); + +enum Method { + GET = 'GET', + POST = 'POST', + PUT = 'PUT', +} + +const MethodOptions = [Method.GET, Method.POST, Method.PUT].map((x) => ({ + label: x, + value: x, +})); + +interface TimeoutInputProps { + value?: number; + onChange?: (value: number | null) => void; +} + +const TimeoutInput = ({ value, onChange }: TimeoutInputProps) => { + const { t } = useTranslation(); + return ( +
    + {t('flow.seconds')} +
    + ); +}; + +const outputList = buildOutputList(initialInvokeValues.outputs); + +function InvokeForm({ node }: INextOperatorForm) { + const { t } = useTranslation(); + const defaultValues = useFormValues(initialInvokeValues, node); + + const form = useForm({ + defaultValues, + resolver: zodResolver(FormSchema), + mode: 'onChange', + }); + + const { + visible, + hideModal, + showModal, + ok, + currentRecord, + otherThanCurrentQuery, + handleDeleteRecord, + } = useEditVariableRecord({ + form, + node, + }); + + const variables = useWatch({ control: form.control, name: 'variables' }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + ( + + {t('flow.url')} + + + + + + )} + /> + ( + + {t('flow.method')} + + + + + + )} + /> + ( + + {t('flow.timeout')} + + + + + + )} + /> + ( + + {t('flow.headers')} + + + + + + )} + /> + ( + + {t('flow.proxy')} + + + + + + )} + /> + ( + + + {t('flow.cleanHtml')} + + + + + + + )} + /> + {/* Create a hidden field to make Form instance record this */} +
    } + /> +
    + {t('flow.parameter')}
    } + rightContent={ + + } + > + + + {visible && ( + + )} + +
    + +
    + + ); +} + +export default memo(InvokeForm); diff --git a/web/src/pages/data-flow/form/invoke-form/schema.ts b/web/src/pages/data-flow/form/invoke-form/schema.ts new file mode 100644 index 00000000000..a3b11aff268 --- /dev/null +++ b/web/src/pages/data-flow/form/invoke-form/schema.ts @@ -0,0 +1,21 @@ +import { z } from 'zod'; + +export const VariableFormSchema = z.object({ + key: z.string(), + ref: z.string(), + value: z.string(), +}); + +export const FormSchema = z.object({ + url: z.string().url(), + method: z.string(), + timeout: z.number(), + headers: z.string(), + proxy: z.string().url(), + clean_html: z.boolean(), + variables: z.array(VariableFormSchema), +}); + +export type FormSchemaType = z.infer; + +export type VariableFormSchemaType = z.infer; diff --git a/web/src/pages/data-flow/form/invoke-form/use-edit-variable.ts b/web/src/pages/data-flow/form/invoke-form/use-edit-variable.ts new file mode 100644 index 00000000000..40b371894ff --- /dev/null +++ b/web/src/pages/data-flow/form/invoke-form/use-edit-variable.ts @@ -0,0 +1,70 @@ +import { useSetModalState } from '@/hooks/common-hooks'; +import { useSetSelectedRecord } from '@/hooks/logic-hooks'; +import { useCallback, useMemo, useState } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { INextOperatorForm } from '../../interface'; +import { FormSchemaType, VariableFormSchemaType } from './schema'; + +export const useEditVariableRecord = ({ + form, +}: INextOperatorForm & { form: UseFormReturn }) => { + const { setRecord, currentRecord } = + useSetSelectedRecord(); + + const { visible, hideModal, showModal } = useSetModalState(); + const [index, setIndex] = useState(-1); + const variables = useWatch({ + control: form.control, + name: 'variables', + }); + + const otherThanCurrentQuery = useMemo(() => { + return variables.filter((item, idx) => idx !== index); + }, [index, variables]); + + const handleEditRecord = useCallback( + (record: VariableFormSchemaType) => { + const variables = form?.getValues('variables') || []; + + const nextVaribales = + index > -1 + ? variables.toSpliced(index, 1, record) + : [...variables, record]; + + form.setValue('variables', nextVaribales); + + hideModal(); + }, + [form, hideModal, index], + ); + + const handleShowModal = useCallback( + (idx?: number, record?: VariableFormSchemaType) => { + setIndex(idx ?? -1); + setRecord(record ?? ({} as VariableFormSchemaType)); + showModal(); + }, + [setRecord, showModal], + ); + + const handleDeleteRecord = useCallback( + (idx: number) => { + const variables = form?.getValues('variables') || []; + const nextVariables = variables.filter((item, index) => index !== idx); + + form.setValue('variables', nextVariables); + }, + [form], + ); + + return { + ok: handleEditRecord, + currentRecord, + setRecord, + visible, + hideModal, + showModal: handleShowModal, + otherThanCurrentQuery, + handleDeleteRecord, + }; +}; diff --git a/web/src/pages/data-flow/form/invoke-form/variable-dialog.tsx b/web/src/pages/data-flow/form/invoke-form/variable-dialog.tsx new file mode 100644 index 00000000000..03c4d83b037 --- /dev/null +++ b/web/src/pages/data-flow/form/invoke-form/variable-dialog.tsx @@ -0,0 +1,143 @@ +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { IModalProps } from '@/interfaces/common'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { isEmpty } from 'lodash'; +import { useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { QueryVariable } from '../components/query-variable'; +import { VariableFormSchemaType } from './schema'; + +type ModalFormProps = { + initialValue: VariableFormSchemaType; + otherThanCurrentQuery: VariableFormSchemaType[]; + submit(values: any): void; +}; + +const FormId = 'BeginParameterForm'; + +function VariableForm({ + initialValue, + otherThanCurrentQuery, + submit, +}: ModalFormProps) { + const { t } = useTranslation(); + const FormSchema = z.object({ + key: z + .string() + .trim() + .min(1) + .refine( + (value) => + !value || !otherThanCurrentQuery.some((x) => x.key === value), + { message: 'The key cannot be repeated!' }, + ), + ref: z.string(), + value: z.string(), + }); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + mode: 'onChange', + defaultValues: { + key: '', + value: '', + ref: '', + }, + }); + + useEffect(() => { + if (!isEmpty(initialValue)) { + form.reset(initialValue); + } + }, [form, initialValue]); + + function onSubmit(data: z.infer) { + submit(data); + } + + return ( +
    + + ( + + {t('flow.key')} + + + + + + )} + /> + + ( + + {t('flow.value')} + + + + + + )} + /> + + + ); +} + +export function VariableDialog({ + initialValue, + hideModal, + otherThanCurrentQuery, + submit, +}: ModalFormProps & IModalProps) { + const { t } = useTranslation(); + + return ( + + + + {t('flow.variableSettings')} + + + + + + + + ); +} diff --git a/web/src/pages/data-flow/form/invoke-form/variable-table.tsx b/web/src/pages/data-flow/form/invoke-form/variable-table.tsx new file mode 100644 index 00000000000..33a74767002 --- /dev/null +++ b/web/src/pages/data-flow/form/invoke-form/variable-table.tsx @@ -0,0 +1,199 @@ +'use client'; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { Pencil, Trash2 } from 'lucide-react'; +import * as React from 'react'; + +import { TableEmpty } from '@/components/table-skeleton'; +import { Button } from '@/components/ui/button'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { cn } from '@/lib/utils'; +import { useTranslation } from 'react-i18next'; +import { useGetVariableLabelByValue } from '../../hooks/use-get-begin-query'; +import { VariableFormSchemaType } from './schema'; + +interface IProps { + data: VariableFormSchemaType[]; + deleteRecord(index: number): void; + showModal(index: number, record: VariableFormSchemaType): void; + nodeId?: string; +} + +export function VariableTable({ + data = [], + deleteRecord, + showModal, + nodeId, +}: IProps) { + const { t } = useTranslation(); + const getLabel = useGetVariableLabelByValue(nodeId!); + + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [], + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + + const columns: ColumnDef[] = [ + { + accessorKey: 'key', + header: t('flow.key'), + meta: { cellClassName: 'max-w-30' }, + cell: ({ row }) => { + const key: string = row.getValue('key'); + return ( + + +
    {key}
    +
    + +

    {key}

    +
    +
    + ); + }, + }, + { + accessorKey: 'ref', + header: t('flow.ref'), + meta: { cellClassName: 'max-w-30' }, + cell: ({ row }) => { + const ref: string = row.getValue('ref'); + const label = getLabel(ref); + return ( + + +
    {label}
    +
    + +

    {label}

    +
    +
    + ); + }, + }, + { + accessorKey: 'value', + header: t('flow.value'), + cell: ({ row }) =>
    {row.getValue('value')}
    , + }, + { + id: 'actions', + enableHiding: false, + header: t('common.action'), + cell: ({ row }) => { + const record = row.original; + const idx = row.index; + + return ( +
    + + +
    + ); + }, + }, + ]; + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + state: { + sorting, + columnFilters, + columnVisibility, + }, + }); + + return ( +
    +
    + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + )} + +
    +
    +
    + ); +} diff --git a/web/src/pages/data-flow/form/iteration-form/dynamic-output.tsx b/web/src/pages/data-flow/form/iteration-form/dynamic-output.tsx new file mode 100644 index 00000000000..c31be8fd062 --- /dev/null +++ b/web/src/pages/data-flow/form/iteration-form/dynamic-output.tsx @@ -0,0 +1,128 @@ +'use client'; + +import { FormContainer } from '@/components/form-container'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { BlockButton, Button } from '@/components/ui/button'; +import { + FormControl, + FormField, + FormItem, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Separator } from '@/components/ui/separator'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { t } from 'i18next'; +import { X } from 'lucide-react'; +import { ReactNode, useCallback, useMemo } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useBuildSubNodeOutputOptions } from './use-build-options'; + +interface IProps { + node?: RAGFlowNodeType; +} + +export function DynamicOutputForm({ node }: IProps) { + const { t } = useTranslation(); + const form = useFormContext(); + const options = useBuildSubNodeOutputOptions(node?.id); + const name = 'outputs'; + + const flatOptions = useMemo(() => { + return options.reduce<{ label: string; value: string; type: string }[]>( + (pre, cur) => { + pre.push(...cur.options); + return pre; + }, + [], + ); + }, [options]); + + const findType = useCallback( + (val: string) => { + const type = flatOptions.find((x) => x.value === val)?.type; + if (type) { + return `Array<${type}>`; + } + }, + [flatOptions], + ); + + const { fields, remove, append } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( +
    + {fields.map((field, index) => { + const nameField = `${name}.${index}.name`; + const typeField = `${name}.${index}.type`; + return ( +
    + ( + + + + + + + )} + /> + + ( + + + { + form.setValue(typeField, findType(val)); + field.onChange(val); + }} + > + + + + )} + /> +
    } + /> + +
    + ); + })} + append({ name: '', ref: undefined })}> + {t('common.add')} + +
    + ); +} + +export function VariableTitle({ title }: { title: ReactNode }) { + return
    {title}
    ; +} + +export function DynamicOutput({ node }: IProps) { + return ( + + + + + ); +} diff --git a/web/src/pages/data-flow/form/iteration-form/index.tsx b/web/src/pages/data-flow/form/iteration-form/index.tsx new file mode 100644 index 00000000000..c70b764fb00 --- /dev/null +++ b/web/src/pages/data-flow/form/iteration-form/index.tsx @@ -0,0 +1,57 @@ +import { FormContainer } from '@/components/form-container'; +import { Form } from '@/components/ui/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useMemo } from 'react'; +import { useForm, useWatch } from 'react-hook-form'; +import { z } from 'zod'; +import { VariableType } from '../../constant'; +import { INextOperatorForm } from '../../interface'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; +import { DynamicOutput } from './dynamic-output'; +import { OutputArray } from './interface'; +import { useValues } from './use-values'; +import { useWatchFormChange } from './use-watch-form-change'; + +const FormSchema = z.object({ + query: z.string().optional(), + outputs: z.array(z.object({ name: z.string(), value: z.any() })).optional(), +}); + +function IterationForm({ node }: INextOperatorForm) { + const defaultValues = useValues(node); + + const form = useForm({ + defaultValues: defaultValues, + resolver: zodResolver(FormSchema), + }); + + const outputs: OutputArray = useWatch({ + control: form?.control, + name: 'outputs', + }); + + const outputList = useMemo(() => { + return outputs.map((x) => ({ title: x.name, type: x?.type })); + }, [outputs]); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + +
    + ); +} + +export default memo(IterationForm); diff --git a/web/src/pages/data-flow/form/iteration-form/interface.ts b/web/src/pages/data-flow/form/iteration-form/interface.ts new file mode 100644 index 00000000000..25f22aab047 --- /dev/null +++ b/web/src/pages/data-flow/form/iteration-form/interface.ts @@ -0,0 +1,2 @@ +export type OutputArray = Array<{ name: string; ref: string; type?: string }>; +export type OutputObject = Record; diff --git a/web/src/pages/data-flow/form/iteration-form/use-build-options.ts b/web/src/pages/data-flow/form/iteration-form/use-build-options.ts new file mode 100644 index 00000000000..3439000d4e8 --- /dev/null +++ b/web/src/pages/data-flow/form/iteration-form/use-build-options.ts @@ -0,0 +1,31 @@ +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { Operator } from '../../constant'; +import { buildOutputOptions } from '../../hooks/use-get-begin-query'; +import useGraphStore from '../../store'; + +export function useBuildSubNodeOutputOptions(nodeId?: string) { + const { nodes } = useGraphStore((state) => state); + + const nodeOutputOptions = useMemo(() => { + if (!nodeId) { + return []; + } + + const subNodeWithOutputList = nodes.filter( + (x) => + x.parentId === nodeId && + x.data.label !== Operator.IterationStart && + !isEmpty(x.data?.form?.outputs), + ); + + return subNodeWithOutputList.map((x) => ({ + label: x.data.name, + value: x.id, + title: x.data.name, + options: buildOutputOptions(x.data.form.outputs, x.id), + })); + }, [nodeId, nodes]); + + return nodeOutputOptions; +} diff --git a/web/src/pages/data-flow/form/iteration-form/use-values.ts b/web/src/pages/data-flow/form/iteration-form/use-values.ts new file mode 100644 index 00000000000..29cd0632416 --- /dev/null +++ b/web/src/pages/data-flow/form/iteration-form/use-values.ts @@ -0,0 +1,27 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { initialIterationValues } from '../../constant'; +import { OutputObject } from './interface'; + +function convertToArray(outputObject: OutputObject) { + return Object.entries(outputObject).map(([key, value]) => ({ + name: key, + ref: value.ref, + type: value.type, + })); +} + +export function useValues(node?: RAGFlowNodeType) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return { ...initialIterationValues, outputs: [] }; + } + + return { ...formData, outputs: convertToArray(formData.outputs) }; + }, [node?.data?.form]); + + return values; +} diff --git a/web/src/pages/data-flow/form/iteration-form/use-watch-form-change.ts b/web/src/pages/data-flow/form/iteration-form/use-watch-form-change.ts new file mode 100644 index 00000000000..4a780667ede --- /dev/null +++ b/web/src/pages/data-flow/form/iteration-form/use-watch-form-change.ts @@ -0,0 +1,30 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../../store'; +import { OutputArray, OutputObject } from './interface'; + +export function transferToObject(list: OutputArray) { + return list.reduce((pre, cur) => { + pre[cur.name] = { ref: cur.ref, type: cur.type }; + return pre; + }, {}); +} + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id && form?.formState.isDirty) { + values = form?.getValues(); + console.log('🚀 ~ useEffect ~ values:', values); + let nextValues: any = { + ...values, + outputs: transferToObject(values.outputs), + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/data-flow/form/iteration-start-from/index.tsx b/web/src/pages/data-flow/form/iteration-start-from/index.tsx new file mode 100644 index 00000000000..ba58b92d1eb --- /dev/null +++ b/web/src/pages/data-flow/form/iteration-start-from/index.tsx @@ -0,0 +1,23 @@ +import { Output, OutputType } from '@/pages/agent/form/components/output'; +import { memo } from 'react'; +import { initialIterationStartValues } from '../../constant'; + +const outputs = initialIterationStartValues.outputs; + +const outputList = Object.entries(outputs).reduce( + (pre, [key, value]) => { + pre.push({ title: key, type: value.type }); + + return pre; + }, + [], +); +function IterationStartForm() { + return ( +
    + +
    + ); +} + +export default memo(IterationStartForm); diff --git a/web/src/pages/data-flow/form/jin10-form/index.tsx b/web/src/pages/data-flow/form/jin10-form/index.tsx new file mode 100644 index 00000000000..2bc6d774a38 --- /dev/null +++ b/web/src/pages/data-flow/form/jin10-form/index.tsx @@ -0,0 +1,145 @@ +import { useTranslate } from '@/hooks/common-hooks'; +import { Form, Input, Select } from 'antd'; +import { useMemo } from 'react'; +import { IOperatorForm } from '../../interface'; +import { + Jin10CalendarDatashapeOptions, + Jin10CalendarTypeOptions, + Jin10FlashTypeOptions, + Jin10SymbolsDatatypeOptions, + Jin10SymbolsTypeOptions, + Jin10TypeOptions, +} from '../../options'; +import DynamicInputVariable from '../components/dynamic-input-variable'; + +const Jin10Form = ({ onValuesChange, form, node }: IOperatorForm) => { + const { t } = useTranslate('flow'); + + const jin10TypeOptions = useMemo(() => { + return Jin10TypeOptions.map((x) => ({ + value: x, + label: t(`jin10TypeOptions.${x}`), + })); + }, [t]); + + const jin10FlashTypeOptions = useMemo(() => { + return Jin10FlashTypeOptions.map((x) => ({ + value: x, + label: t(`jin10FlashTypeOptions.${x}`), + })); + }, [t]); + + const jin10CalendarTypeOptions = useMemo(() => { + return Jin10CalendarTypeOptions.map((x) => ({ + value: x, + label: t(`jin10CalendarTypeOptions.${x}`), + })); + }, [t]); + + const jin10CalendarDatashapeOptions = useMemo(() => { + return Jin10CalendarDatashapeOptions.map((x) => ({ + value: x, + label: t(`jin10CalendarDatashapeOptions.${x}`), + })); + }, [t]); + + const jin10SymbolsTypeOptions = useMemo(() => { + return Jin10SymbolsTypeOptions.map((x) => ({ + value: x, + label: t(`jin10SymbolsTypeOptions.${x}`), + })); + }, [t]); + + const jin10SymbolsDatatypeOptions = useMemo(() => { + return Jin10SymbolsDatatypeOptions.map((x) => ({ + value: x, + label: t(`jin10SymbolsDatatypeOptions.${x}`), + })); + }, [t]); + + return ( +
    + + + + + + + + + {({ getFieldValue }) => { + const type = getFieldValue('type'); + switch (type) { + case 'flash': + return ( + <> + + + + + + + + + + + ); + + case 'calendar': + return ( + <> + + + + + + + + ); + + case 'symbols': + return ( + <> + + + + + + + + ); + + case 'news': + return ( + <> + + + + + + + + ); + + default: + return <>; + } + }} + +
    + ); +}; + +export default Jin10Form; diff --git a/web/src/pages/data-flow/form/keyword-extract-form/index.tsx b/web/src/pages/data-flow/form/keyword-extract-form/index.tsx new file mode 100644 index 00000000000..bda5d44f510 --- /dev/null +++ b/web/src/pages/data-flow/form/keyword-extract-form/index.tsx @@ -0,0 +1,48 @@ +import { NextLLMSelect } from '@/components/llm-select/next'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { useTranslation } from 'react-i18next'; +import { INextOperatorForm } from '../../interface'; +import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; + +const KeywordExtractForm = ({ form, node }: INextOperatorForm) => { + const { t } = useTranslation(); + + return ( +
    + { + e.preventDefault(); + }} + > + + ( + + + {t('chat.model')} + + + + + + + )} + /> + + + + ); +}; + +export default KeywordExtractForm; diff --git a/web/src/pages/data-flow/form/message-form/index.tsx b/web/src/pages/data-flow/form/message-form/index.tsx new file mode 100644 index 00000000000..05c831a84aa --- /dev/null +++ b/web/src/pages/data-flow/form/message-form/index.tsx @@ -0,0 +1,101 @@ +import { FormContainer } from '@/components/form-container'; +import { BlockButton, Button } from '@/components/ui/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { X } from 'lucide-react'; +import { memo } from 'react'; +import { useFieldArray, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { INextOperatorForm } from '../../interface'; +import { FormWrapper } from '../components/form-wrapper'; +import { PromptEditor } from '../components/prompt-editor'; +import { useValues } from './use-values'; +import { useWatchFormChange } from './use-watch-change'; + +function MessageForm({ node }: INextOperatorForm) { + const { t } = useTranslation(); + + const values = useValues(node); + + const FormSchema = z.object({ + content: z + .array( + z.object({ + value: z.string(), + }), + ) + .optional(), + }); + + const form = useForm({ + defaultValues: values, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + const { fields, append, remove } = useFieldArray({ + name: 'content', + control: form.control, + }); + + return ( +
    + + + + {t('flow.msg')} +
    + {fields.map((field, index) => ( +
    + ( + + + {/* */} + + + + )} + /> + {fields.length > 1 && ( + + )} +
    + ))} + + append({ value: '' })} // "" will cause the inability to add, refer to: https://github.com/orgs/react-hook-form/discussions/8485#discussioncomment-2961861 + > + {t('flow.addMessage')} + +
    + +
    +
    +
    +
    + ); +} + +export default memo(MessageForm); diff --git a/web/src/pages/data-flow/form/message-form/use-values.ts b/web/src/pages/data-flow/form/message-form/use-values.ts new file mode 100644 index 00000000000..6a90881becf --- /dev/null +++ b/web/src/pages/data-flow/form/message-form/use-values.ts @@ -0,0 +1,22 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { initialMessageValues } from '../../constant'; +import { convertToObjectArray } from '../../utils'; + +export function useValues(node?: RAGFlowNodeType) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return initialMessageValues; + } + + return { + ...formData, + content: convertToObjectArray(formData.content), + }; + }, [node]); + + return values; +} diff --git a/web/src/pages/data-flow/form/message-form/use-watch-change.ts b/web/src/pages/data-flow/form/message-form/use-watch-change.ts new file mode 100644 index 00000000000..10c35c653c1 --- /dev/null +++ b/web/src/pages/data-flow/form/message-form/use-watch-change.ts @@ -0,0 +1,24 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../../store'; +import { convertToStringArray } from '../../utils'; + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id && form?.formState.isDirty) { + values = form?.getValues(); + let nextValues: any = values; + + nextValues = { + ...values, + content: convertToStringArray(values.content), + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/data-flow/form/pubmed-form/index.tsx b/web/src/pages/data-flow/form/pubmed-form/index.tsx new file mode 100644 index 00000000000..a2c35d55c6c --- /dev/null +++ b/web/src/pages/data-flow/form/pubmed-form/index.tsx @@ -0,0 +1,90 @@ +import { FormContainer } from '@/components/form-container'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialPubMedValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +export const PubMedFormPartialSchema = { + top_n: z.number(), + email: z.string().email(), +}; + +export const FormSchema = z.object({ + ...PubMedFormPartialSchema, + query: z.string(), +}); + +export function PubMedFormWidgets() { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + return ( + <> + + ( + + {t('email')} + + + + + + )} + /> + + ); +} + +const outputList = buildOutputList(initialPubMedValues.outputs); + +function PubMedForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialPubMedValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + mode: 'onChange', + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + +
    + +
    +
    + ); +} + +export default memo(PubMedForm); diff --git a/web/src/pages/data-flow/form/qweather-form/index.tsx b/web/src/pages/data-flow/form/qweather-form/index.tsx new file mode 100644 index 00000000000..eee088762ad --- /dev/null +++ b/web/src/pages/data-flow/form/qweather-form/index.tsx @@ -0,0 +1,157 @@ +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { INextOperatorForm } from '../../interface'; +import { + QWeatherLangOptions, + QWeatherTimePeriodOptions, + QWeatherTypeOptions, + QWeatherUserTypeOptions, +} from '../../options'; +import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; + +enum FormFieldName { + Type = 'type', + UserType = 'user_type', +} + +const QWeatherForm = ({ form, node }: INextOperatorForm) => { + const { t } = useTranslation(); + const typeValue = form.watch(FormFieldName.Type); + + const qWeatherLangOptions = useMemo(() => { + return QWeatherLangOptions.map((x) => ({ + value: x, + label: t(`flow.qWeatherLangOptions.${x}`), + })); + }, [t]); + + const qWeatherTypeOptions = useMemo(() => { + return QWeatherTypeOptions.map((x) => ({ + value: x, + label: t(`flow.qWeatherTypeOptions.${x}`), + })); + }, [t]); + + const qWeatherUserTypeOptions = useMemo(() => { + return QWeatherUserTypeOptions.map((x) => ({ + value: x, + label: t(`flow.qWeatherUserTypeOptions.${x}`), + })); + }, [t]); + + const getQWeatherTimePeriodOptions = useCallback(() => { + let options = QWeatherTimePeriodOptions; + const userType = form.getValues(FormFieldName.UserType); + if (userType === 'free') { + options = options.slice(0, 3); + } + return options.map((x) => ({ + value: x, + label: t(`flow.qWeatherTimePeriodOptions.${x}`), + })); + }, [form, t]); + + return ( +
    + { + e.preventDefault(); + }} + > + + ( + + {t('flow.webApiKey')} + + + + + + )} + /> + ( + + {t('flow.lang')} + + + + + + )} + /> + ( + + {t('flow.type')} + + + + + + )} + /> + ( + + {t('flow.userType')} + + + + + + )} + /> + {typeValue === 'weather' && ( + ( + + {t('flow.timePeriod')} + + + + + + )} + /> + )} + + + ); +}; + +export default QWeatherForm; diff --git a/web/src/pages/data-flow/form/relevant-form/hooks.ts b/web/src/pages/data-flow/form/relevant-form/hooks.ts new file mode 100644 index 00000000000..413a0ac3834 --- /dev/null +++ b/web/src/pages/data-flow/form/relevant-form/hooks.ts @@ -0,0 +1,41 @@ +import pick from 'lodash/pick'; +import { useCallback, useEffect } from 'react'; +import { IOperatorForm } from '../../interface'; +import useGraphStore from '../../store'; + +export const useBuildRelevantOptions = () => { + const nodes = useGraphStore((state) => state.nodes); + + const buildRelevantOptions = useCallback( + (toList: string[]) => { + return nodes + .filter( + (x) => !toList.some((y) => y === x.id), // filter out selected values ​​in other to fields from the current drop-down box options + ) + .map((x) => ({ label: x.data.name, value: x.id })); + }, + [nodes], + ); + + return buildRelevantOptions; +}; + +/** + * monitor changes in the connection and synchronize the target to the yes and no fields of the form + * similar to the categorize-form's useHandleFormValuesChange method + * @param param0 + */ +export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => { + const getNode = useGraphStore((state) => state.getNode); + const node = getNode(nodeId); + + const watchFormChanges = useCallback(() => { + if (node) { + form?.setFieldsValue(pick(node, ['yes', 'no'])); + } + }, [node, form]); + + useEffect(() => { + watchFormChanges(); + }, [watchFormChanges]); +}; diff --git a/web/src/pages/data-flow/form/relevant-form/index.tsx b/web/src/pages/data-flow/form/relevant-form/index.tsx new file mode 100644 index 00000000000..e2366f6f05e --- /dev/null +++ b/web/src/pages/data-flow/form/relevant-form/index.tsx @@ -0,0 +1,49 @@ +import LLMSelect from '@/components/llm-select'; +import { useTranslate } from '@/hooks/common-hooks'; +import { Form, Select } from 'antd'; +import { Operator } from '../../constant'; +import { useBuildFormSelectOptions } from '../../form-hooks'; +import { IOperatorForm } from '../../interface'; +import { useWatchConnectionChanges } from './hooks'; + +const RelevantForm = ({ onValuesChange, form, node }: IOperatorForm) => { + const { t } = useTranslate('flow'); + const buildRelevantOptions = useBuildFormSelectOptions( + Operator.Relevant, + node?.id, + ); + useWatchConnectionChanges({ nodeId: node?.id, form }); + + return ( +
    + + + + + + +
    + ); +}; + +export default RelevantForm; diff --git a/web/src/pages/data-flow/form/retrieval-form/next.tsx b/web/src/pages/data-flow/form/retrieval-form/next.tsx new file mode 100644 index 00000000000..425d08e4b3f --- /dev/null +++ b/web/src/pages/data-flow/form/retrieval-form/next.tsx @@ -0,0 +1,126 @@ +import { Collapse } from '@/components/collapse'; +import { CrossLanguageFormField } from '@/components/cross-language-form-field'; +import { FormContainer } from '@/components/form-container'; +import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { RerankFormFields } from '@/components/rerank'; +import { SimilaritySliderFormField } from '@/components/similarity-slider'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Textarea } from '@/components/ui/textarea'; +import { UseKnowledgeGraphFormField } from '@/components/use-knowledge-graph-item'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useMemo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { initialRetrievalValues } from '../../constant'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { PromptEditor } from '../components/prompt-editor'; +import { useValues } from './use-values'; + +export const RetrievalPartialSchema = { + similarity_threshold: z.coerce.number(), + keywords_similarity_weight: z.coerce.number(), + top_n: z.coerce.number(), + top_k: z.coerce.number(), + kb_ids: z.array(z.string()), + rerank_id: z.string(), + empty_response: z.string(), + cross_languages: z.array(z.string()), + use_kg: z.boolean(), +}; + +export const FormSchema = z.object({ + query: z.string().optional(), + ...RetrievalPartialSchema, +}); + +export function EmptyResponseField() { + const { t } = useTranslation(); + const form = useFormContext(); + + return ( + ( + + + {t('chat.emptyResponse')} + + + + + + + )} + /> + + {/* Create a hidden field to make Form instance record this */} +
    } + /> + + {t('flow.input')} + +
    + } + rightContent={ + + } + > + + + + {visible && ( + + )} + + +
    + ); +} + +export default memo(UserFillUpForm); diff --git a/web/src/pages/data-flow/form/user-fill-up-form/use-values.ts b/web/src/pages/data-flow/form/user-fill-up-form/use-values.ts new file mode 100644 index 00000000000..0af1c78c35b --- /dev/null +++ b/web/src/pages/data-flow/form/user-fill-up-form/use-values.ts @@ -0,0 +1,21 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; +import { initialUserFillUpValues } from '../../constant'; +import { buildBeginInputListFromObject } from '../begin-form/utils'; + +export function useValues(node?: RAGFlowNodeType) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return initialUserFillUpValues; + } + + const inputs = buildBeginInputListFromObject(formData?.inputs); + + return { ...(formData || {}), inputs }; + }, [node?.data?.form]); + + return values; +} diff --git a/web/src/pages/data-flow/form/user-fill-up-form/use-watch-change.ts b/web/src/pages/data-flow/form/user-fill-up-form/use-watch-change.ts new file mode 100644 index 00000000000..bdf4b9a9195 --- /dev/null +++ b/web/src/pages/data-flow/form/user-fill-up-form/use-watch-change.ts @@ -0,0 +1,35 @@ +import { omit } from 'lodash'; +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { BeginQuery } from '../../interface'; +import useGraphStore from '../../store'; + +function transferInputsArrayToObject(inputs: BeginQuery[] = []) { + return inputs.reduce>>((pre, cur) => { + pre[cur.key] = omit(cur, 'key'); + + return pre; + }, {}); +} + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // TODO: This should only be executed when the form changes + if (id) { + values = form?.getValues() || {}; + + const inputs = transferInputsArrayToObject(values.inputs); + + const nextValues = { + ...values, + inputs, + outputs: inputs, + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/data-flow/form/wencai-form/index.tsx b/web/src/pages/data-flow/form/wencai-form/index.tsx new file mode 100644 index 00000000000..a4e668ed97c --- /dev/null +++ b/web/src/pages/data-flow/form/wencai-form/index.tsx @@ -0,0 +1,97 @@ +import { FormContainer } from '@/components/form-container'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo, useMemo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { initialWenCaiValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { WenCaiQueryTypeOptions } from '../../options'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +export const WenCaiPartialSchema = { + top_n: z.number(), + query_type: z.string(), +}; + +export const FormSchema = z.object({ + ...WenCaiPartialSchema, + query: z.string(), +}); + +export function WenCaiFormWidgets() { + const { t } = useTranslation(); + const form = useFormContext(); + + const wenCaiQueryTypeOptions = useMemo(() => { + return WenCaiQueryTypeOptions.map((x) => ({ + value: x, + label: t(`flow.wenCaiQueryTypeOptions.${x}`), + })); + }, [t]); + + return ( + <> + + ( + + {t('flow.queryType')} + + + + + + )} + /> + + ); +} + +const outputList = buildOutputList(initialWenCaiValues.outputs); + +function WenCaiForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialWenCaiValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + +
    + +
    +
    + ); +} + +export default memo(WenCaiForm); diff --git a/web/src/pages/data-flow/form/wikipedia-form/index.tsx b/web/src/pages/data-flow/form/wikipedia-form/index.tsx new file mode 100644 index 00000000000..1603d802e35 --- /dev/null +++ b/web/src/pages/data-flow/form/wikipedia-form/index.tsx @@ -0,0 +1,88 @@ +import { FormContainer } from '@/components/form-container'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { TopNFormField } from '@/components/top-n-item'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialWikipediaValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { LanguageOptions } from '../../options'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +export const WikipediaFormPartialSchema = { + top_n: z.string(), + language: z.string(), +}; + +const FormSchema = z.object({ + query: z.string(), + ...WikipediaFormPartialSchema, +}); + +export function WikipediaFormWidgets() { + const { t } = useTranslate('common'); + const form = useFormContext(); + + return ( + <> + + ( + + {t('language')} + + + + + + )} + /> + + ); +} + +const outputList = buildOutputList(initialWikipediaValues.outputs); + +function WikipediaForm({ node }: INextOperatorForm) { + const defaultValues = useFormValues(initialWikipediaValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + +
    + +
    +
    + ); +} + +export default memo(WikipediaForm); diff --git a/web/src/pages/data-flow/form/yahoo-finance-form/index.tsx b/web/src/pages/data-flow/form/yahoo-finance-form/index.tsx new file mode 100644 index 00000000000..68a34836b1b --- /dev/null +++ b/web/src/pages/data-flow/form/yahoo-finance-form/index.tsx @@ -0,0 +1,125 @@ +import { FormContainer } from '@/components/form-container'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Switch } from '@/components/ui/switch'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { ReactNode } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialYahooFinanceValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +export const YahooFinanceFormPartialSchema = { + info: z.boolean(), + history: z.boolean(), + financials: z.boolean(), + balance_sheet: z.boolean(), + cash_flow_statement: z.boolean(), + news: z.boolean(), +}; + +const FormSchema = z.object({ + stock_code: z.string(), + ...YahooFinanceFormPartialSchema, +}); + +interface SwitchFormFieldProps { + name: string; + label: ReactNode; +} +function SwitchFormField({ name, label }: SwitchFormFieldProps) { + const form = useFormContext(); + + return ( + ( + + {label} + + + + + + )} + /> + ); +} + +export function YahooFinanceFormWidgets() { + const { t } = useTranslate('flow'); + return ( + <> + + + + + + + + + + ); +} + +const outputList = buildOutputList(initialYahooFinanceValues.outputs); + +const YahooFinanceForm = ({ node }: INextOperatorForm) => { + const { t } = useTranslate('flow'); + + const defaultValues = useFormValues(initialYahooFinanceValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + + +
    + +
    +
    + ); +}; + +export default YahooFinanceForm; diff --git a/web/src/pages/data-flow/hooks.tsx b/web/src/pages/data-flow/hooks.tsx new file mode 100644 index 00000000000..5d153762307 --- /dev/null +++ b/web/src/pages/data-flow/hooks.tsx @@ -0,0 +1,405 @@ +import { + Connection, + Edge, + getOutgoers, + Node, + Position, + ReactFlowInstance, +} from '@xyflow/react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +// import { shallow } from 'zustand/shallow'; +import { useFetchModelId } from '@/hooks/logic-hooks'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { humanId } from 'human-id'; +import { get, lowerFirst } from 'lodash'; +import { useTranslation } from 'react-i18next'; +import { + initialAgentValues, + initialAkShareValues, + initialArXivValues, + initialBaiduFanyiValues, + initialBaiduValues, + initialBeginValues, + initialBingValues, + initialCategorizeValues, + initialCodeValues, + initialConcentratorValues, + initialCrawlerValues, + initialDeepLValues, + initialDuckValues, + initialEmailValues, + initialExeSqlValues, + initialGithubValues, + initialGoogleScholarValues, + initialGoogleValues, + initialInvokeValues, + initialIterationValues, + initialJin10Values, + initialKeywordExtractValues, + initialMessageValues, + initialNoteValues, + initialPubMedValues, + initialQWeatherValues, + initialRelevantValues, + initialRetrievalValues, + initialRewriteQuestionValues, + initialSearXNGValues, + initialStringTransformValues, + initialSwitchValues, + initialTavilyExtractValues, + initialTavilyValues, + initialTuShareValues, + initialUserFillUpValues, + initialWaitingDialogueValues, + initialWenCaiValues, + initialWikipediaValues, + initialYahooFinanceValues, + NodeMap, + Operator, + RestrictedUpstreamMap, +} from './constant'; +import useGraphStore, { RFState } from './store'; +import { + generateNodeNamesWithIncreasingIndex, + getNodeDragHandle, + getRelativePositionToIterationNode, + replaceIdWithText, +} from './utils'; + +const selector = (state: RFState) => ({ + nodes: state.nodes, + edges: state.edges, + onNodesChange: state.onNodesChange, + onEdgesChange: state.onEdgesChange, + onConnect: state.onConnect, + setNodes: state.setNodes, + onSelectionChange: state.onSelectionChange, + onEdgeMouseEnter: state.onEdgeMouseEnter, + onEdgeMouseLeave: state.onEdgeMouseLeave, +}); + +export const useSelectCanvasData = () => { + // return useStore(useShallow(selector)); // throw error + // return useStore(selector, shallow); + return useGraphStore(selector); +}; + +export const useInitializeOperatorParams = () => { + const llmId = useFetchModelId(); + + const initialFormValuesMap = useMemo(() => { + return { + [Operator.Begin]: initialBeginValues, + [Operator.Retrieval]: initialRetrievalValues, + [Operator.Categorize]: { ...initialCategorizeValues, llm_id: llmId }, + [Operator.Relevant]: { ...initialRelevantValues, llm_id: llmId }, + [Operator.RewriteQuestion]: { + ...initialRewriteQuestionValues, + llm_id: llmId, + }, + [Operator.Message]: initialMessageValues, + [Operator.KeywordExtract]: { + ...initialKeywordExtractValues, + llm_id: llmId, + }, + [Operator.DuckDuckGo]: initialDuckValues, + [Operator.Baidu]: initialBaiduValues, + [Operator.Wikipedia]: initialWikipediaValues, + [Operator.PubMed]: initialPubMedValues, + [Operator.ArXiv]: initialArXivValues, + [Operator.Google]: initialGoogleValues, + [Operator.Bing]: initialBingValues, + [Operator.GoogleScholar]: initialGoogleScholarValues, + [Operator.DeepL]: initialDeepLValues, + [Operator.SearXNG]: initialSearXNGValues, + [Operator.GitHub]: initialGithubValues, + [Operator.BaiduFanyi]: initialBaiduFanyiValues, + [Operator.QWeather]: initialQWeatherValues, + [Operator.ExeSQL]: { ...initialExeSqlValues, llm_id: llmId }, + [Operator.Switch]: initialSwitchValues, + [Operator.WenCai]: initialWenCaiValues, + [Operator.AkShare]: initialAkShareValues, + [Operator.YahooFinance]: initialYahooFinanceValues, + [Operator.Jin10]: initialJin10Values, + [Operator.Concentrator]: initialConcentratorValues, + [Operator.TuShare]: initialTuShareValues, + [Operator.Note]: initialNoteValues, + [Operator.Crawler]: initialCrawlerValues, + [Operator.Invoke]: initialInvokeValues, + [Operator.Email]: initialEmailValues, + [Operator.Iteration]: initialIterationValues, + [Operator.IterationStart]: initialIterationValues, + [Operator.Code]: initialCodeValues, + [Operator.WaitingDialogue]: initialWaitingDialogueValues, + [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, + [Operator.TavilySearch]: initialTavilyValues, + [Operator.TavilyExtract]: initialTavilyExtractValues, + [Operator.Tool]: {}, + [Operator.UserFillUp]: initialUserFillUpValues, + [Operator.StringTransform]: initialStringTransformValues, + }; + }, [llmId]); + + const initializeOperatorParams = useCallback( + (operatorName: Operator) => { + return initialFormValuesMap[operatorName]; + }, + [initialFormValuesMap], + ); + + return initializeOperatorParams; +}; + +export const useHandleDrag = () => { + const handleDragStart = useCallback( + (operatorId: string) => (ev: React.DragEvent) => { + ev.dataTransfer.setData('application/@xyflow/react', operatorId); + ev.dataTransfer.effectAllowed = 'move'; + }, + [], + ); + + return { handleDragStart }; +}; + +export const useGetNodeName = () => { + const { t } = useTranslation(); + + return (type: string) => { + const name = t(`flow.${lowerFirst(type)}`); + return name; + }; +}; + +export const useHandleDrop = () => { + const addNode = useGraphStore((state) => state.addNode); + const nodes = useGraphStore((state) => state.nodes); + const [reactFlowInstance, setReactFlowInstance] = + useState>(); + const initializeOperatorParams = useInitializeOperatorParams(); + const getNodeName = useGetNodeName(); + + const onDragOver = useCallback((event: React.DragEvent) => { + event.preventDefault(); + event.dataTransfer.dropEffect = 'move'; + }, []); + + const onDrop = useCallback( + (event: React.DragEvent) => { + event.preventDefault(); + + const type = event.dataTransfer.getData('application/@xyflow/react'); + + // check if the dropped element is valid + if (typeof type === 'undefined' || !type) { + return; + } + + // reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition + // and you don't need to subtract the reactFlowBounds.left/top anymore + // details: https://@xyflow/react.dev/whats-new/2023-11-10 + const position = reactFlowInstance?.screenToFlowPosition({ + x: event.clientX, + y: event.clientY, + }); + const newNode: Node = { + id: `${type}:${humanId()}`, + type: NodeMap[type as Operator] || 'ragNode', + position: position || { + x: 0, + y: 0, + }, + data: { + label: `${type}`, + name: generateNodeNamesWithIncreasingIndex(getNodeName(type), nodes), + form: initializeOperatorParams(type as Operator), + }, + sourcePosition: Position.Right, + targetPosition: Position.Left, + dragHandle: getNodeDragHandle(type), + }; + + if (type === Operator.Iteration) { + newNode.width = 500; + newNode.height = 250; + const iterationStartNode: Node = { + id: `${Operator.IterationStart}:${humanId()}`, + type: 'iterationStartNode', + position: { x: 50, y: 100 }, + // draggable: false, + data: { + label: Operator.IterationStart, + name: Operator.IterationStart, + form: {}, + }, + parentId: newNode.id, + extent: 'parent', + }; + addNode(newNode); + addNode(iterationStartNode); + } else { + const subNodeOfIteration = getRelativePositionToIterationNode( + nodes, + position, + ); + if (subNodeOfIteration) { + newNode.parentId = subNodeOfIteration.parentId; + newNode.position = subNodeOfIteration.position; + newNode.extent = 'parent'; + } + addNode(newNode); + } + }, + [reactFlowInstance, getNodeName, nodes, initializeOperatorParams, addNode], + ); + + return { onDrop, onDragOver, setReactFlowInstance, reactFlowInstance }; +}; + +export const useValidateConnection = () => { + const { getOperatorTypeFromId, getParentIdById, edges, nodes } = + useGraphStore((state) => state); + + const isSameNodeChild = useCallback( + (connection: Connection | Edge) => { + const sourceParentId = getParentIdById(connection.source); + const targetParentId = getParentIdById(connection.target); + if (sourceParentId || targetParentId) { + return sourceParentId === targetParentId; + } + return true; + }, + [getParentIdById], + ); + + const hasCanvasCycle = useCallback( + (connection: Connection | Edge) => { + const target = nodes.find((node) => node.id === connection.target); + const hasCycle = (node: RAGFlowNodeType, visited = new Set()) => { + if (visited.has(node.id)) return false; + + visited.add(node.id); + + for (const outgoer of getOutgoers(node, nodes, edges)) { + if (outgoer.id === connection.source) return true; + if (hasCycle(outgoer, visited)) return true; + } + }; + + if (target?.id === connection.source) return false; + + return target ? !hasCycle(target) : false; + }, + [edges, nodes], + ); + + // restricted lines cannot be connected successfully. + const isValidConnection = useCallback( + (connection: Connection | Edge) => { + // node cannot connect to itself + const isSelfConnected = connection.target === connection.source; + + // limit the connection between two nodes to only one connection line in one direction + // const hasLine = edges.some( + // (x) => x.source === connection.source && x.target === connection.target, + // ); + + const ret = + !isSelfConnected && + RestrictedUpstreamMap[ + getOperatorTypeFromId(connection.source) as Operator + ]?.every((x) => x !== getOperatorTypeFromId(connection.target)) && + isSameNodeChild(connection) && + hasCanvasCycle(connection); + return ret; + }, + [getOperatorTypeFromId, hasCanvasCycle, isSameNodeChild], + ); + + return isValidConnection; +}; + +export const useReplaceIdWithName = () => { + const getNode = useGraphStore((state) => state.getNode); + + const replaceIdWithName = useCallback( + (id?: string) => { + return getNode(id)?.data.name; + }, + [getNode], + ); + + return replaceIdWithName; +}; + +export const useReplaceIdWithText = (output: unknown) => { + const getNameById = useReplaceIdWithName(); + + return { + replacedOutput: replaceIdWithText(output, getNameById), + getNameById, + }; +}; + +export const useDuplicateNode = () => { + const duplicateNodeById = useGraphStore((store) => store.duplicateNode); + const getNodeName = useGetNodeName(); + + const duplicateNode = useCallback( + (id: string, label: string) => { + duplicateNodeById(id, getNodeName(label)); + }, + [duplicateNodeById, getNodeName], + ); + + return duplicateNode; +}; + +export const useCopyPaste = () => { + const nodes = useGraphStore((state) => state.nodes); + const duplicateNode = useDuplicateNode(); + + const onCopyCapture = useCallback( + (event: ClipboardEvent) => { + if (get(event, 'srcElement.tagName') !== 'BODY') return; + + event.preventDefault(); + const nodesStr = JSON.stringify( + nodes.filter((n) => n.selected && n.data.label !== Operator.Begin), + ); + + event.clipboardData?.setData('agent:nodes', nodesStr); + }, + [nodes], + ); + + const onPasteCapture = useCallback( + (event: ClipboardEvent) => { + const nodes = JSON.parse( + event.clipboardData?.getData('agent:nodes') || '[]', + ) as RAGFlowNodeType[] | undefined; + + if (Array.isArray(nodes) && nodes.length) { + event.preventDefault(); + nodes.forEach((n) => { + duplicateNode(n.id, n.data.label); + }); + } + }, + [duplicateNode], + ); + + useEffect(() => { + window.addEventListener('copy', onCopyCapture); + return () => { + window.removeEventListener('copy', onCopyCapture); + }; + }, [onCopyCapture]); + + useEffect(() => { + window.addEventListener('paste', onPasteCapture); + return () => { + window.removeEventListener('paste', onPasteCapture); + }; + }, [onPasteCapture]); +}; diff --git a/web/src/pages/data-flow/hooks/use-add-node.ts b/web/src/pages/data-flow/hooks/use-add-node.ts new file mode 100644 index 00000000000..625eeb5cded --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-add-node.ts @@ -0,0 +1,462 @@ +import { useFetchModelId } from '@/hooks/logic-hooks'; +import { Connection, Node, Position, ReactFlowInstance } from '@xyflow/react'; +import humanId from 'human-id'; +import { t } from 'i18next'; +import { lowerFirst } from 'lodash'; +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + NodeHandleId, + NodeMap, + Operator, + initialAgentValues, + initialAkShareValues, + initialArXivValues, + initialBaiduFanyiValues, + initialBaiduValues, + initialBeginValues, + initialBingValues, + initialCategorizeValues, + initialCodeValues, + initialConcentratorValues, + initialCrawlerValues, + initialDeepLValues, + initialDuckValues, + initialEmailValues, + initialExeSqlValues, + initialGithubValues, + initialGoogleScholarValues, + initialGoogleValues, + initialInvokeValues, + initialIterationStartValues, + initialIterationValues, + initialJin10Values, + initialKeywordExtractValues, + initialMessageValues, + initialNoteValues, + initialPubMedValues, + initialQWeatherValues, + initialRelevantValues, + initialRetrievalValues, + initialRewriteQuestionValues, + initialSearXNGValues, + initialStringTransformValues, + initialSwitchValues, + initialTavilyExtractValues, + initialTavilyValues, + initialTuShareValues, + initialUserFillUpValues, + initialWaitingDialogueValues, + initialWenCaiValues, + initialWikipediaValues, + initialYahooFinanceValues, +} from '../constant'; +import useGraphStore from '../store'; +import { + generateNodeNamesWithIncreasingIndex, + getNodeDragHandle, +} from '../utils'; + +function isBottomSubAgent(type: string, position: Position) { + return ( + (type === Operator.Agent && position === Position.Bottom) || + type === Operator.Tool + ); +} +export const useInitializeOperatorParams = () => { + const llmId = useFetchModelId(); + + const initialFormValuesMap = useMemo(() => { + return { + [Operator.Begin]: initialBeginValues, + [Operator.Retrieval]: initialRetrievalValues, + [Operator.Categorize]: { ...initialCategorizeValues, llm_id: llmId }, + [Operator.Relevant]: { ...initialRelevantValues, llm_id: llmId }, + [Operator.RewriteQuestion]: { + ...initialRewriteQuestionValues, + llm_id: llmId, + }, + [Operator.Message]: initialMessageValues, + [Operator.KeywordExtract]: { + ...initialKeywordExtractValues, + llm_id: llmId, + }, + [Operator.DuckDuckGo]: initialDuckValues, + [Operator.Baidu]: initialBaiduValues, + [Operator.Wikipedia]: initialWikipediaValues, + [Operator.PubMed]: initialPubMedValues, + [Operator.ArXiv]: initialArXivValues, + [Operator.Google]: initialGoogleValues, + [Operator.Bing]: initialBingValues, + [Operator.GoogleScholar]: initialGoogleScholarValues, + [Operator.DeepL]: initialDeepLValues, + [Operator.SearXNG]: initialSearXNGValues, + [Operator.GitHub]: initialGithubValues, + [Operator.BaiduFanyi]: initialBaiduFanyiValues, + [Operator.QWeather]: initialQWeatherValues, + [Operator.ExeSQL]: initialExeSqlValues, + [Operator.Switch]: initialSwitchValues, + [Operator.WenCai]: initialWenCaiValues, + [Operator.AkShare]: initialAkShareValues, + [Operator.YahooFinance]: initialYahooFinanceValues, + [Operator.Jin10]: initialJin10Values, + [Operator.Concentrator]: initialConcentratorValues, + [Operator.TuShare]: initialTuShareValues, + [Operator.Note]: initialNoteValues, + [Operator.Crawler]: initialCrawlerValues, + [Operator.Invoke]: initialInvokeValues, + [Operator.Email]: initialEmailValues, + [Operator.Iteration]: initialIterationValues, + [Operator.IterationStart]: initialIterationStartValues, + [Operator.Code]: initialCodeValues, + [Operator.WaitingDialogue]: initialWaitingDialogueValues, + [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, + [Operator.Tool]: {}, + [Operator.TavilySearch]: initialTavilyValues, + [Operator.UserFillUp]: initialUserFillUpValues, + [Operator.StringTransform]: initialStringTransformValues, + [Operator.TavilyExtract]: initialTavilyExtractValues, + }; + }, [llmId]); + + const initializeOperatorParams = useCallback( + (operatorName: Operator, position: Position) => { + const initialValues = initialFormValuesMap[operatorName]; + if (isBottomSubAgent(operatorName, position)) { + return { + ...initialValues, + description: t('flow.descriptionMessage'), + user_prompt: t('flow.userPromptDefaultValue'), + }; + } + + return initialValues; + }, + [initialFormValuesMap], + ); + + return { initializeOperatorParams, initialFormValuesMap }; +}; + +export const useGetNodeName = () => { + const { t } = useTranslation(); + + return (type: string) => { + const name = t(`flow.${lowerFirst(type)}`); + return name; + }; +}; + +export function useCalculateNewlyChildPosition() { + const getNode = useGraphStore((state) => state.getNode); + const nodes = useGraphStore((state) => state.nodes); + const edges = useGraphStore((state) => state.edges); + + const calculateNewlyBackChildPosition = useCallback( + (id?: string, sourceHandle?: string) => { + const parentNode = getNode(id); + + // Calculate the coordinates of child nodes to prevent newly added child nodes from covering other child nodes + const allChildNodeIds = edges + .filter((x) => x.source === id && x.sourceHandle === sourceHandle) + .map((x) => x.target); + + const yAxises = nodes + .filter((x) => allChildNodeIds.some((y) => y === x.id)) + .map((x) => x.position.y); + + const maxY = Math.max(...yAxises); + + const position = { + y: yAxises.length > 0 ? maxY + 150 : parentNode?.position.y || 0, + x: (parentNode?.position.x || 0) + 300, + }; + + return position; + }, + [edges, getNode, nodes], + ); + + return { calculateNewlyBackChildPosition }; +} + +function useAddChildEdge() { + const addEdge = useGraphStore((state) => state.addEdge); + + const addChildEdge = useCallback( + (position: Position = Position.Right, edge: Partial) => { + if ( + position === Position.Right && + edge.source && + edge.target && + edge.sourceHandle + ) { + addEdge({ + source: edge.source, + target: edge.target, + sourceHandle: edge.sourceHandle, + targetHandle: NodeHandleId.End, + }); + } + }, + [addEdge], + ); + + return { addChildEdge }; +} + +function useAddToolNode() { + const { nodes, edges, addEdge, getNode, addNode } = useGraphStore( + (state) => state, + ); + + const addToolNode = useCallback( + (newNode: Node, nodeId?: string): boolean => { + const agentNode = getNode(nodeId); + + if (agentNode) { + const childToolNodeIds = edges + .filter( + (x) => x.source === nodeId && x.sourceHandle === NodeHandleId.Tool, + ) + .map((x) => x.target); + + if ( + childToolNodeIds.length > 0 && + nodes.some((x) => x.id === childToolNodeIds[0]) + ) { + return false; + } + + newNode.position = { + x: agentNode.position.x - 82, + y: agentNode.position.y + 140, + }; + + addNode(newNode); + if (nodeId) { + addEdge({ + source: nodeId, + target: newNode.id, + sourceHandle: NodeHandleId.Tool, + targetHandle: NodeHandleId.End, + }); + } + return true; + } + return false; + }, + [addEdge, addNode, edges, getNode, nodes], + ); + + return { addToolNode }; +} + +function useResizeIterationNode() { + const { getNode, nodes, updateNode } = useGraphStore((state) => state); + + const resizeIterationNode = useCallback( + (type: string, position: Position, parentId?: string) => { + const parentNode = getNode(parentId); + if (parentNode && !isBottomSubAgent(type, position)) { + const MoveRightDistance = 310; + const childNodeList = nodes.filter((x) => x.parentId === parentId); + const maxX = Math.max(...childNodeList.map((x) => x.position.x)); + if (maxX + MoveRightDistance > parentNode.position.x) { + updateNode({ + ...parentNode, + width: (parentNode.width || 0) + MoveRightDistance, + position: { + x: parentNode.position.x + MoveRightDistance / 2, + y: parentNode.position.y, + }, + }); + } + } + }, + [getNode, nodes, updateNode], + ); + + return { resizeIterationNode }; +} +type CanvasMouseEvent = Pick< + React.MouseEvent, + 'clientX' | 'clientY' +>; + +export function useAddNode(reactFlowInstance?: ReactFlowInstance) { + const { edges, nodes, addEdge, addNode, getNode } = useGraphStore( + (state) => state, + ); + const getNodeName = useGetNodeName(); + const { initializeOperatorParams } = useInitializeOperatorParams(); + const { calculateNewlyBackChildPosition } = useCalculateNewlyChildPosition(); + const { addChildEdge } = useAddChildEdge(); + const { addToolNode } = useAddToolNode(); + const { resizeIterationNode } = useResizeIterationNode(); + // const [reactFlowInstance, setReactFlowInstance] = + // useState>(); + + const addCanvasNode = useCallback( + ( + type: string, + params: { + nodeId?: string; + position: Position; + id?: string; + isFromConnectionDrag?: boolean; + } = { + position: Position.Right, + }, + ) => + (event?: CanvasMouseEvent): string | undefined => { + const nodeId = params.nodeId; + const node = getNode(nodeId); + + // reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition + // and you don't need to subtract the reactFlowBounds.left/top anymore + // details: https://@xyflow/react.dev/whats-new/2023-11-10 + let position = reactFlowInstance?.screenToFlowPosition({ + x: event?.clientX || 0, + y: event?.clientY || 0, + }); + + if ( + params.position === Position.Right && + type !== Operator.Note && + !params.isFromConnectionDrag + ) { + position = calculateNewlyBackChildPosition(nodeId, params.id); + } + + const newNode: Node = { + id: `${type}:${humanId()}`, + type: NodeMap[type as Operator] || 'ragNode', + position: position || { + x: 0, + y: 0, + }, + data: { + label: `${type}`, + name: generateNodeNamesWithIncreasingIndex( + getNodeName(type), + nodes, + ), + form: initializeOperatorParams(type as Operator, params.position), + }, + sourcePosition: Position.Right, + targetPosition: Position.Left, + dragHandle: getNodeDragHandle(type), + }; + + if (node && node.parentId) { + newNode.parentId = node.parentId; + newNode.extent = 'parent'; + const parentNode = getNode(node.parentId); + if (parentNode && !isBottomSubAgent(type, params.position)) { + resizeIterationNode(type, params.position, node.parentId); + } + } + + if (type === Operator.Iteration) { + newNode.width = 500; + newNode.height = 250; + const iterationStartNode: Node = { + id: `${Operator.IterationStart}:${humanId()}`, + type: 'iterationStartNode', + position: { x: 50, y: 100 }, + // draggable: false, + data: { + label: Operator.IterationStart, + name: Operator.IterationStart, + form: initialIterationStartValues, + }, + parentId: newNode.id, + extent: 'parent', + }; + addNode(newNode); + addNode(iterationStartNode); + if (nodeId) { + addEdge({ + source: nodeId, + target: newNode.id, + sourceHandle: NodeHandleId.Start, + targetHandle: NodeHandleId.End, + }); + } + return newNode.id; + } else if ( + type === Operator.Agent && + params.position === Position.Bottom + ) { + const agentNode = getNode(nodeId); + if (agentNode) { + // Calculate the coordinates of child nodes to prevent newly added child nodes from covering other child nodes + const allChildAgentNodeIds = edges + .filter( + (x) => + x.source === nodeId && + x.sourceHandle === NodeHandleId.AgentBottom, + ) + .map((x) => x.target); + + const xAxises = nodes + .filter((x) => allChildAgentNodeIds.some((y) => y === x.id)) + .map((x) => x.position.x); + + const maxX = Math.max(...xAxises); + + newNode.position = { + x: xAxises.length > 0 ? maxX + 262 : agentNode.position.x + 82, + y: agentNode.position.y + 140, + }; + } + addNode(newNode); + if (nodeId) { + addEdge({ + source: nodeId, + target: newNode.id, + sourceHandle: NodeHandleId.AgentBottom, + targetHandle: NodeHandleId.AgentTop, + }); + } + return newNode.id; + } else if (type === Operator.Tool) { + const toolNodeAdded = addToolNode(newNode, params.nodeId); + return toolNodeAdded ? newNode.id : undefined; + } else { + addNode(newNode); + addChildEdge(params.position, { + source: params.nodeId, + target: newNode.id, + sourceHandle: params.id, + }); + } + + return newNode.id; + }, + [ + addChildEdge, + addEdge, + addNode, + addToolNode, + calculateNewlyBackChildPosition, + edges, + getNode, + getNodeName, + initializeOperatorParams, + nodes, + reactFlowInstance, + resizeIterationNode, + ], + ); + + const addNoteNode = useCallback( + (e: CanvasMouseEvent) => { + addCanvasNode(Operator.Note)(e); + }, + [addCanvasNode], + ); + + return { addCanvasNode, addNoteNode }; +} diff --git a/web/src/pages/data-flow/hooks/use-agent-tool-initial-values.ts b/web/src/pages/data-flow/hooks/use-agent-tool-initial-values.ts new file mode 100644 index 00000000000..05864184d99 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-agent-tool-initial-values.ts @@ -0,0 +1,70 @@ +import { omit, pick } from 'lodash'; +import { useCallback } from 'react'; +import { Operator } from '../constant'; +import { useInitializeOperatorParams } from './use-add-node'; + +export function useAgentToolInitialValues() { + const { initialFormValuesMap } = useInitializeOperatorParams(); + + const initializeAgentToolValues = useCallback( + (operatorName: Operator) => { + const initialValues = initialFormValuesMap[operatorName]; + + switch (operatorName) { + case Operator.Retrieval: + return { + ...omit(initialValues, 'query'), + description: '', + }; + case (Operator.TavilySearch, Operator.TavilyExtract): + return { + api_key: '', + }; + case Operator.ExeSQL: + return omit(initialValues, 'sql'); + case Operator.Bing: + return omit(initialValues, 'query'); + case Operator.YahooFinance: + return omit(initialValues, 'stock_code'); + + case Operator.Email: + return pick( + initialValues, + 'smtp_server', + 'smtp_port', + 'email', + 'password', + 'sender_name', + ); + + case Operator.DuckDuckGo: + return pick(initialValues, 'top_n', 'channel'); + + case Operator.Wikipedia: + return pick(initialValues, 'top_n', 'language'); + case Operator.Google: + return pick(initialValues, 'api_key', 'country', 'language'); + case Operator.GoogleScholar: + return omit(initialValues, 'query', 'outputs'); + case Operator.ArXiv: + return pick(initialValues, 'top_n', 'sort_by'); + case Operator.PubMed: + return pick(initialValues, 'top_n', 'email'); + case Operator.GitHub: + return pick(initialValues, 'top_n'); + case Operator.WenCai: + return pick(initialValues, 'top_n', 'query_type'); + case Operator.Code: + return {}; + case Operator.SearXNG: + return pick(initialValues, 'searxng_url', 'top_n'); + + default: + return initialValues; + } + }, + [initialFormValuesMap], + ); + + return { initializeAgentToolValues }; +} diff --git a/web/src/pages/data-flow/hooks/use-before-delete.tsx b/web/src/pages/data-flow/hooks/use-before-delete.tsx new file mode 100644 index 00000000000..d08333c8610 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-before-delete.tsx @@ -0,0 +1,82 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { Node, OnBeforeDelete } from '@xyflow/react'; +import { Operator } from '../constant'; +import useGraphStore from '../store'; +import { deleteAllDownstreamAgentsAndTool } from '../utils/delete-node'; + +const UndeletableNodes = [Operator.Begin, Operator.IterationStart]; + +export function useBeforeDelete() { + const { getOperatorTypeFromId, getNode } = useGraphStore((state) => state); + + const agentPredicate = (node: Node) => { + return getOperatorTypeFromId(node.id) === Operator.Agent; + }; + + const handleBeforeDelete: OnBeforeDelete = async ({ + nodes, // Nodes to be deleted + edges, // Edges to be deleted + }) => { + const toBeDeletedNodes = nodes.filter((node) => { + const operatorType = node.data?.label as Operator; + if (operatorType === Operator.Begin) { + return false; + } + + if ( + operatorType === Operator.IterationStart && + !nodes.some((x) => x.id === node.parentId) + ) { + return false; + } + + return true; + }); + + const toBeDeletedEdges = edges.filter((edge) => { + const sourceType = getOperatorTypeFromId(edge.source) as Operator; + const downStreamNodes = nodes.filter((x) => x.id === edge.target); + + // This edge does not need to be deleted, the range of edges that do not need to be deleted is smaller, so consider the case where it does not need to be deleted + if ( + UndeletableNodes.includes(sourceType) && // Upstream node is Begin or IterationStart + downStreamNodes.length === 0 // Downstream node does not exist in the nodes to be deleted + ) { + if (!nodes.some((x) => x.id === edge.source)) { + return true; // Can be deleted + } + return false; // Cannot be deleted + } + + return true; + }); + + // Delete the agent and tool nodes downstream of the agent node + if (nodes.some(agentPredicate)) { + nodes.filter(agentPredicate).forEach((node) => { + const { downstreamAgentAndToolEdges, downstreamAgentAndToolNodeIds } = + deleteAllDownstreamAgentsAndTool(node.id, edges); + + downstreamAgentAndToolNodeIds.forEach((nodeId) => { + const currentNode = getNode(nodeId); + if (toBeDeletedNodes.every((x) => x.id !== nodeId) && currentNode) { + toBeDeletedNodes.push(currentNode); + } + }); + + downstreamAgentAndToolEdges.forEach((edge) => { + if (toBeDeletedEdges.every((x) => x.id !== edge.id)) { + toBeDeletedEdges.push(edge); + } + }); + }, []); + } + + return { + nodes: toBeDeletedNodes, + edges: toBeDeletedEdges, + }; + }; + + return { handleBeforeDelete }; +} diff --git a/web/src/pages/data-flow/hooks/use-build-dsl.ts b/web/src/pages/data-flow/hooks/use-build-dsl.ts new file mode 100644 index 00000000000..eb32b2317ca --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-build-dsl.ts @@ -0,0 +1,29 @@ +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { useCallback } from 'react'; +import useGraphStore from '../store'; +import { buildDslComponentsByGraph } from '../utils'; + +export const useBuildDslData = () => { + const { data } = useFetchAgent(); + const { nodes, edges } = useGraphStore((state) => state); + + const buildDslData = useCallback( + (currentNodes?: RAGFlowNodeType[]) => { + const dslComponents = buildDslComponentsByGraph( + currentNodes ?? nodes, + edges, + data.dsl.components, + ); + + return { + ...data.dsl, + graph: { nodes: currentNodes ?? nodes, edges }, + components: dslComponents, + }; + }, + [data.dsl, edges, nodes], + ); + + return { buildDslData }; +}; diff --git a/web/src/pages/data-flow/hooks/use-cache-chat-log.ts b/web/src/pages/data-flow/hooks/use-cache-chat-log.ts new file mode 100644 index 00000000000..c61079003f4 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-cache-chat-log.ts @@ -0,0 +1,88 @@ +import { + IEventList, + INodeEvent, + MessageEventType, +} from '@/hooks/use-send-message'; +import { useCallback, useEffect, useMemo, useState } from 'react'; + +export const ExcludeTypes = [ + MessageEventType.Message, + MessageEventType.MessageEnd, +]; + +export function useCacheChatLog() { + const [eventList, setEventList] = useState([]); + const [messageIdPool, setMessageIdPool] = useState< + Record + >({}); + + const [currentMessageId, setCurrentMessageId] = useState(''); + useEffect(() => { + setMessageIdPool((prev) => ({ ...prev, [currentMessageId]: eventList })); + }, [currentMessageId, eventList]); + + const filterEventListByMessageId = useCallback( + (messageId: string) => { + return messageIdPool[messageId]?.filter( + (x) => x.message_id === messageId, + ); + }, + [messageIdPool], + ); + + const filterEventListByEventType = useCallback( + (eventType: string) => { + return messageIdPool[currentMessageId]?.filter( + (x) => x.event === eventType, + ); + }, + [messageIdPool, currentMessageId], + ); + + const clearEventList = useCallback(() => { + setEventList([]); + setMessageIdPool({}); + }, []); + + const addEventList = useCallback((events: IEventList, message_id: string) => { + setEventList((x) => { + const list = [...x, ...events]; + setMessageIdPool((prev) => ({ ...prev, [message_id]: list })); + return list; + }); + }, []); + + const currentEventListWithoutMessage = useMemo(() => { + const list = messageIdPool[currentMessageId]?.filter( + (x) => + x.message_id === currentMessageId && + ExcludeTypes.every((y) => y !== x.event), + ); + return list as INodeEvent[]; + }, [currentMessageId, messageIdPool]); + + const currentEventListWithoutMessageById = useCallback( + (messageId: string) => { + const list = messageIdPool[messageId]?.filter( + (x) => + x.message_id === messageId && + ExcludeTypes.every((y) => y !== x.event), + ); + return list as INodeEvent[]; + }, + [messageIdPool], + ); + + return { + eventList, + currentEventListWithoutMessage, + currentEventListWithoutMessageById, + setEventList, + clearEventList, + addEventList, + filterEventListByEventType, + filterEventListByMessageId, + setCurrentMessageId, + currentMessageId, + }; +} diff --git a/web/src/pages/data-flow/hooks/use-change-node-name.ts b/web/src/pages/data-flow/hooks/use-change-node-name.ts new file mode 100644 index 00000000000..61a5653d732 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-change-node-name.ts @@ -0,0 +1,120 @@ +import message from '@/components/ui/message'; +import { trim } from 'lodash'; +import { + ChangeEvent, + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import { Operator } from '../constant'; +import useGraphStore from '../store'; +import { getAgentNodeTools } from '../utils'; + +export function useHandleTooNodeNameChange({ + id, + name, + setName, +}: { + id?: string; + name?: string; + setName: Dispatch>; +}) { + const { clickedToolId, findUpstreamNodeById, updateNodeForm } = useGraphStore( + (state) => state, + ); + const agentNode = findUpstreamNodeById(id); + const tools = getAgentNodeTools(agentNode); + + const previousName = useMemo(() => { + const tool = tools.find((x) => x.component_name === clickedToolId); + return tool?.name || tool?.component_name; + }, [clickedToolId, tools]); + + const handleToolNameBlur = useCallback(() => { + const trimmedName = trim(name); + const existsSameName = tools.some((x) => x.name === trimmedName); + if (trimmedName === '' || existsSameName) { + if (existsSameName && previousName !== name) { + message.error('The name cannot be repeated'); + } + setName(previousName || ''); + return; + } + + if (agentNode?.id) { + const nextTools = tools.map((x) => { + if (x.component_name === clickedToolId) { + return { + ...x, + name, + }; + } + return x; + }); + updateNodeForm(agentNode?.id, nextTools, ['tools']); + } + }, [ + agentNode?.id, + clickedToolId, + name, + previousName, + setName, + tools, + updateNodeForm, + ]); + + return { handleToolNameBlur, previousToolName: previousName }; +} + +export const useHandleNodeNameChange = ({ + id, + data, +}: { + id?: string; + data: any; +}) => { + const [name, setName] = useState(''); + const { updateNodeName, nodes, getOperatorTypeFromId } = useGraphStore( + (state) => state, + ); + const previousName = data?.name; + const isToolNode = getOperatorTypeFromId(id) === Operator.Tool; + + const { handleToolNameBlur, previousToolName } = useHandleTooNodeNameChange({ + id, + name, + setName, + }); + + const handleNameBlur = useCallback(() => { + const existsSameName = nodes.some((x) => x.data.name === name); + if (trim(name) === '' || existsSameName) { + if (existsSameName && previousName !== name) { + message.error('The name cannot be repeated'); + } + setName(previousName); + return; + } + + if (id) { + updateNodeName(id, name); + } + }, [name, id, updateNodeName, previousName, nodes]); + + const handleNameChange = useCallback((e: ChangeEvent) => { + setName(e.target.value); + }, []); + + useEffect(() => { + setName(isToolNode ? previousToolName : previousName); + }, [isToolNode, previousName, previousToolName]); + + return { + name, + handleNameBlur: isToolNode ? handleToolNameBlur : handleNameBlur, + handleNameChange, + }; +}; diff --git a/web/src/pages/data-flow/hooks/use-chat-logic.ts b/web/src/pages/data-flow/hooks/use-chat-logic.ts new file mode 100644 index 00000000000..42b0533cb76 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-chat-logic.ts @@ -0,0 +1,60 @@ +import { MessageType } from '@/constants/chat'; +import { Message } from '@/interfaces/database/chat'; +import { IMessage } from '@/pages/chat/interface'; +import { get } from 'lodash'; +import { useCallback, useMemo } from 'react'; +import { BeginQuery } from '../interface'; +import { buildBeginQueryWithObject } from '../utils'; +type IAwaitCompentData = { + derivedMessages: IMessage[]; + sendFormMessage: (params: { + inputs: Record; + id: string; + }) => void; + canvasId: string; +}; +const useAwaitCompentData = (props: IAwaitCompentData) => { + const { derivedMessages, sendFormMessage, canvasId } = props; + + const getInputs = useCallback((message: Message) => { + return get(message, 'data.inputs', {}) as Record; + }, []); + + const buildInputList = useCallback( + (message: Message) => { + return Object.entries(getInputs(message)).map(([key, val]) => { + return { + ...val, + key, + }; + }); + }, + [getInputs], + ); + + const handleOk = useCallback( + (message: Message) => (values: BeginQuery[]) => { + const inputs = getInputs(message); + const nextInputs = buildBeginQueryWithObject(inputs, values); + sendFormMessage({ + inputs: nextInputs, + id: canvasId, + }); + }, + [getInputs, sendFormMessage, canvasId], + ); + + const isWaitting = useMemo(() => { + const temp = derivedMessages?.some((message, i) => { + const flag = + message.role === MessageType.Assistant && + derivedMessages.length - 1 === i && + message.data; + return flag; + }); + return temp; + }, [derivedMessages]); + return { getInputs, buildInputList, handleOk, isWaitting }; +}; + +export { useAwaitCompentData }; diff --git a/web/src/pages/data-flow/hooks/use-export-json.ts b/web/src/pages/data-flow/hooks/use-export-json.ts new file mode 100644 index 00000000000..1efe6bb50dd --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-export-json.ts @@ -0,0 +1,71 @@ +import { useToast } from '@/components/hooks/use-toast'; +import { FileMimeType, Platform } from '@/constants/common'; +import { useSetModalState } from '@/hooks/common-hooks'; +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { IGraph } from '@/interfaces/database/flow'; +import { downloadJsonFile } from '@/utils/file-util'; +import { message } from 'antd'; +import isEmpty from 'lodash/isEmpty'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useBuildDslData } from './use-build-dsl'; +import { useSetGraphInfo } from './use-set-graph'; + +export const useHandleExportOrImportJsonFile = () => { + const { buildDslData } = useBuildDslData(); + const { + visible: fileUploadVisible, + hideModal: hideFileUploadModal, + showModal: showFileUploadModal, + } = useSetModalState(); + const setGraphInfo = useSetGraphInfo(); + const { data } = useFetchAgent(); + const { t } = useTranslation(); + const { toast } = useToast(); + + const onFileUploadOk = useCallback( + async ({ + fileList, + platform, + }: { + fileList: File[]; + platform: Platform; + }) => { + console.log('🚀 ~ useHandleExportOrImportJsonFile ~ platform:', platform); + if (fileList.length > 0) { + const file = fileList[0]; + if (file.type !== FileMimeType.Json) { + toast({ title: t('flow.jsonUploadTypeErrorMessage') }); + return; + } + + const graphStr = await file.text(); + const errorMessage = t('flow.jsonUploadContentErrorMessage'); + try { + const graph = JSON.parse(graphStr); + if (graphStr && !isEmpty(graph) && Array.isArray(graph?.nodes)) { + setGraphInfo(graph ?? ({} as IGraph)); + hideFileUploadModal(); + } else { + message.error(errorMessage); + } + } catch (error) { + message.error(errorMessage); + } + } + }, + [hideFileUploadModal, setGraphInfo, t, toast], + ); + + const handleExportJson = useCallback(() => { + downloadJsonFile(buildDslData().graph, `${data.title}.json`); + }, [buildDslData, data.title]); + + return { + fileUploadVisible, + handleExportJson, + handleImportJson: showFileUploadModal, + hideFileUploadModal, + onFileUploadOk, + }; +}; diff --git a/web/src/pages/data-flow/hooks/use-fetch-data.ts b/web/src/pages/data-flow/hooks/use-fetch-data.ts new file mode 100644 index 00000000000..5a1ca40cb51 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-fetch-data.ts @@ -0,0 +1,19 @@ +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { IGraph } from '@/interfaces/database/flow'; +import { useEffect } from 'react'; +import { useSetGraphInfo } from './use-set-graph'; + +export const useFetchDataOnMount = () => { + const { loading, data, refetch } = useFetchAgent(); + const setGraphInfo = useSetGraphInfo(); + + useEffect(() => { + setGraphInfo(data?.dsl?.graph ?? ({} as IGraph)); + }, [setGraphInfo, data]); + + useEffect(() => { + refetch(); + }, [refetch]); + + return { loading, flowDetail: data }; +}; diff --git a/web/src/pages/data-flow/hooks/use-find-mcp-by-id.ts b/web/src/pages/data-flow/hooks/use-find-mcp-by-id.ts new file mode 100644 index 00000000000..e4c5aed5a33 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-find-mcp-by-id.ts @@ -0,0 +1,12 @@ +import { useListMcpServer } from '@/hooks/use-mcp-request'; + +export function useFindMcpById() { + const { data } = useListMcpServer(); + + const findMcpById = (id: string) => + data.mcp_servers.find((item) => item.id === id); + + return { + findMcpById, + }; +} diff --git a/web/src/pages/data-flow/hooks/use-form-values.ts b/web/src/pages/data-flow/hooks/use-form-values.ts new file mode 100644 index 00000000000..edb2abbbd59 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-form-values.ts @@ -0,0 +1,20 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty } from 'lodash'; +import { useMemo } from 'react'; + +export function useFormValues( + defaultValues: Record, + node?: RAGFlowNodeType, +) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return defaultValues; + } + + return formData; + }, [defaultValues, node?.data?.form]); + + return values; +} diff --git a/web/src/pages/data-flow/hooks/use-get-begin-query.tsx b/web/src/pages/data-flow/hooks/use-get-begin-query.tsx new file mode 100644 index 00000000000..83cda207803 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-get-begin-query.tsx @@ -0,0 +1,317 @@ +import { AgentGlobals } from '@/constants/agent'; +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { Edge } from '@xyflow/react'; +import { DefaultOptionType } from 'antd/es/select'; +import { t } from 'i18next'; +import { isEmpty } from 'lodash'; +import get from 'lodash/get'; +import { + ReactNode, + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; +import { + AgentDialogueMode, + BeginId, + BeginQueryType, + Operator, + VariableType, +} from '../constant'; +import { AgentFormContext } from '../context'; +import { buildBeginInputListFromObject } from '../form/begin-form/utils'; +import { BeginQuery } from '../interface'; +import OperatorIcon from '../operator-icon'; +import useGraphStore from '../store'; + +export function useSelectBeginNodeDataInputs() { + const getNode = useGraphStore((state) => state.getNode); + + return buildBeginInputListFromObject( + getNode(BeginId)?.data?.form?.inputs ?? {}, + ); +} + +export function useIsTaskMode() { + const getNode = useGraphStore((state) => state.getNode); + + return useMemo(() => { + const node = getNode(BeginId); + return node?.data?.form?.mode === AgentDialogueMode.Task; + }, [getNode]); +} + +export const useGetBeginNodeDataQuery = () => { + const getNode = useGraphStore((state) => state.getNode); + + const getBeginNodeDataQuery = useCallback(() => { + return buildBeginInputListFromObject( + get(getNode(BeginId), 'data.form.inputs', {}), + ); + }, [getNode]); + + return getBeginNodeDataQuery; +}; + +export const useGetBeginNodeDataInputs = () => { + const getNode = useGraphStore((state) => state.getNode); + + const inputs = get(getNode(BeginId), 'data.form.inputs', {}); + + const beginNodeDataInputs = useMemo(() => { + return buildBeginInputListFromObject(inputs); + }, [inputs]); + + return beginNodeDataInputs; +}; + +export const useGetBeginNodeDataQueryIsSafe = () => { + const [isBeginNodeDataQuerySafe, setIsBeginNodeDataQuerySafe] = + useState(false); + const inputs = useSelectBeginNodeDataInputs(); + const nodes = useGraphStore((state) => state.nodes); + + useEffect(() => { + const query: BeginQuery[] = inputs; + const isSafe = !query.some((q) => !q.optional && q.type === 'file'); + setIsBeginNodeDataQuerySafe(isSafe); + }, [inputs, nodes]); + + return isBeginNodeDataQuerySafe; +}; + +function filterAllUpstreamNodeIds(edges: Edge[], nodeIds: string[]) { + return nodeIds.reduce((pre, nodeId) => { + const currentEdges = edges.filter((x) => x.target === nodeId); + + const upstreamNodeIds: string[] = currentEdges.map((x) => x.source); + + const ids = upstreamNodeIds.concat( + filterAllUpstreamNodeIds(edges, upstreamNodeIds), + ); + + ids.forEach((x) => { + if (pre.every((y) => y !== x)) { + pre.push(x); + } + }); + + return pre; + }, []); +} + +export function buildOutputOptions( + outputs: Record = {}, + nodeId?: string, + parentLabel?: string | ReactNode, + icon?: ReactNode, +) { + return Object.keys(outputs).map((x) => ({ + label: x, + value: `${nodeId}@${x}`, + parentLabel, + icon, + type: outputs[x]?.type, + })); +} + +export function useBuildNodeOutputOptions(nodeId?: string) { + const nodes = useGraphStore((state) => state.nodes); + const edges = useGraphStore((state) => state.edges); + + const nodeOutputOptions = useMemo(() => { + if (!nodeId) { + return []; + } + const upstreamIds = filterAllUpstreamNodeIds(edges, [nodeId]); + + const nodeWithOutputList = nodes.filter( + (x) => + upstreamIds.some((y) => y === x.id) && !isEmpty(x.data?.form?.outputs), + ); + + return nodeWithOutputList + .filter((x) => x.id !== nodeId) + .map((x) => ({ + label: x.data.name, + value: x.id, + title: x.data.name, + options: buildOutputOptions( + x.data.form.outputs, + x.id, + x.data.name, + , + ), + })); + }, [edges, nodeId, nodes]); + + return nodeOutputOptions; +} + +// exclude nodes with branches +const ExcludedNodes = [ + Operator.Categorize, + Operator.Relevant, + Operator.Begin, + Operator.Note, +]; + +const StringList = [ + BeginQueryType.Line, + BeginQueryType.Paragraph, + BeginQueryType.Options, +]; + +function transferToVariableType(type: string) { + if (StringList.some((x) => x === type)) { + return VariableType.String; + } + return type; +} + +export function useBuildBeginVariableOptions() { + const inputs = useSelectBeginNodeDataInputs(); + + const options = useMemo(() => { + return [ + { + label: {t('flow.beginInput')}, + title: t('flow.beginInput'), + options: inputs.map((x) => ({ + label: x.name, + parentLabel: {t('flow.beginInput')}, + icon: , + value: `begin@${x.key}`, + type: transferToVariableType(x.type), + })), + }, + ]; + }, [inputs]); + + return options; +} + +export const useBuildVariableOptions = (nodeId?: string, parentId?: string) => { + const nodeOutputOptions = useBuildNodeOutputOptions(nodeId); + const parentNodeOutputOptions = useBuildNodeOutputOptions(parentId); + const beginOptions = useBuildBeginVariableOptions(); + + const options = useMemo(() => { + return [...beginOptions, ...nodeOutputOptions, ...parentNodeOutputOptions]; + }, [beginOptions, nodeOutputOptions, parentNodeOutputOptions]); + + return options; +}; + +export function useBuildQueryVariableOptions(n?: RAGFlowNodeType) { + const { data } = useFetchAgent(); + const node = useContext(AgentFormContext) || n; + const options = useBuildVariableOptions(node?.id, node?.parentId); + const nextOptions = useMemo(() => { + const globals = data?.dsl?.globals ?? {}; + const globalOptions = Object.entries(globals).map(([key, value]) => ({ + label: key, + value: key, + icon: , + parentLabel: {t('flow.beginInput')}, + type: Array.isArray(value) + ? `${VariableType.Array}${key === AgentGlobals.SysFiles ? '' : ''}` + : typeof value, + })); + return [ + { ...options[0], options: [...options[0]?.options, ...globalOptions] }, + ...options.slice(1), + ]; + }, [data.dsl?.globals, options]); + + return nextOptions; +} + +export function useBuildComponentIdOptions(nodeId?: string, parentId?: string) { + const nodes = useGraphStore((state) => state.nodes); + + // Limit the nodes inside iteration to only reference peer nodes with the same parentId and other external nodes other than their parent nodes + const filterChildNodesToSameParentOrExternal = useCallback( + (node: RAGFlowNodeType) => { + // Node inside iteration + if (parentId) { + return ( + (node.parentId === parentId || node.parentId === undefined) && + node.id !== parentId + ); + } + + return node.parentId === undefined; // The outermost node + }, + [parentId], + ); + + const componentIdOptions = useMemo(() => { + return nodes + .filter( + (x) => + x.id !== nodeId && + !ExcludedNodes.some((y) => y === x.data.label) && + filterChildNodesToSameParentOrExternal(x), + ) + .map((x) => ({ label: x.data.name, value: x.id })); + }, [nodes, nodeId, filterChildNodesToSameParentOrExternal]); + + return [ + { + label: Component Output, + title: 'Component Output', + options: componentIdOptions, + }, + ]; +} + +export function useBuildComponentIdAndBeginOptions( + nodeId?: string, + parentId?: string, +) { + const componentIdOptions = useBuildComponentIdOptions(nodeId, parentId); + const beginOptions = useBuildBeginVariableOptions(); + + return [...beginOptions, ...componentIdOptions]; +} + +export const useGetComponentLabelByValue = (nodeId: string) => { + const options = useBuildComponentIdAndBeginOptions(nodeId); + + const flattenOptions = useMemo(() => { + return options.reduce((pre, cur) => { + return [...pre, ...cur.options]; + }, []); + }, [options]); + + const getLabel = useCallback( + (val?: string) => { + return flattenOptions.find((x) => x.value === val)?.label; + }, + [flattenOptions], + ); + return getLabel; +}; + +export function useGetVariableLabelByValue(nodeId: string) { + const { getNode } = useGraphStore((state) => state); + const nextOptions = useBuildQueryVariableOptions(getNode(nodeId)); + + const flattenOptions = useMemo(() => { + return nextOptions.reduce((pre, cur) => { + return [...pre, ...cur.options]; + }, []); + }, [nextOptions]); + + const getLabel = useCallback( + (val?: string) => { + return flattenOptions.find((x) => x.value === val)?.label; + }, + [flattenOptions], + ); + return getLabel; +} diff --git a/web/src/pages/data-flow/hooks/use-iteration.ts b/web/src/pages/data-flow/hooks/use-iteration.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/web/src/pages/data-flow/hooks/use-move-note.ts b/web/src/pages/data-flow/hooks/use-move-note.ts new file mode 100644 index 00000000000..2c1d1b072af --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-move-note.ts @@ -0,0 +1,35 @@ +import { useMouse } from 'ahooks'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +export function useMoveNote() { + const ref = useRef(null); + const mouse = useMouse(); + const [imgVisible, setImgVisible] = useState(false); + + const toggleVisible = useCallback((visible: boolean) => { + setImgVisible(visible); + }, []); + + const showImage = useCallback(() => { + toggleVisible(true); + }, [toggleVisible]); + + const hideImage = useCallback(() => { + toggleVisible(false); + }, [toggleVisible]); + + useEffect(() => { + if (ref.current) { + ref.current.style.top = `${mouse.clientY - 70}px`; + ref.current.style.left = `${mouse.clientX + 10}px`; + } + }, [mouse.clientX, mouse.clientY]); + + return { + ref, + showImage, + hideImage, + mouse, + imgVisible, + }; +} diff --git a/web/src/pages/data-flow/hooks/use-open-document.ts b/web/src/pages/data-flow/hooks/use-open-document.ts new file mode 100644 index 00000000000..384529c1597 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-open-document.ts @@ -0,0 +1,12 @@ +import { useCallback } from 'react'; + +export function useOpenDocument() { + const openDocument = useCallback(() => { + window.open( + 'https://ragflow.io/docs/dev/category/agent-components', + '_blank', + ); + }, []); + + return openDocument; +} diff --git a/web/src/pages/data-flow/hooks/use-save-graph.ts b/web/src/pages/data-flow/hooks/use-save-graph.ts new file mode 100644 index 00000000000..8ce4dc15e06 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-save-graph.ts @@ -0,0 +1,89 @@ +import { + useFetchAgent, + useResetAgent, + useSetAgent, +} from '@/hooks/use-agent-request'; +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { formatDate } from '@/utils/date'; +import { useDebounceEffect } from 'ahooks'; +import { useCallback, useEffect, useState } from 'react'; +import { useParams } from 'umi'; +import useGraphStore from '../store'; +import { useBuildDslData } from './use-build-dsl'; + +export const useSaveGraph = (showMessage: boolean = true) => { + const { data } = useFetchAgent(); + const { setAgent, loading } = useSetAgent(showMessage); + const { id } = useParams(); + const { buildDslData } = useBuildDslData(); + + const saveGraph = useCallback( + async (currentNodes?: RAGFlowNodeType[]) => { + return setAgent({ + id, + title: data.title, + dsl: buildDslData(currentNodes), + }); + }, + [setAgent, data, id, buildDslData], + ); + + return { saveGraph, loading }; +}; + +export const useSaveGraphBeforeOpeningDebugDrawer = (show: () => void) => { + const { saveGraph, loading } = useSaveGraph(); + const { resetAgent } = useResetAgent(); + + const handleRun = useCallback( + async (nextNodes?: RAGFlowNodeType[]) => { + const saveRet = await saveGraph(nextNodes); + if (saveRet?.code === 0) { + // Call the reset api before opening the run drawer each time + const resetRet = await resetAgent(); + // After resetting, all previous messages will be cleared. + if (resetRet?.code === 0) { + show(); + } + } + }, + [saveGraph, resetAgent, show], + ); + + return { handleRun, loading }; +}; + +export const useWatchAgentChange = (chatDrawerVisible: boolean) => { + const [time, setTime] = useState(); + const nodes = useGraphStore((state) => state.nodes); + const edges = useGraphStore((state) => state.edges); + const { saveGraph } = useSaveGraph(false); + const { data: flowDetail } = useFetchAgent(); + + const setSaveTime = useCallback((updateTime: number) => { + setTime(formatDate(updateTime)); + }, []); + + useEffect(() => { + setSaveTime(flowDetail?.update_time); + }, [flowDetail, setSaveTime]); + + const saveAgent = useCallback(async () => { + if (!chatDrawerVisible) { + const ret = await saveGraph(); + setSaveTime(ret.data.update_time); + } + }, [chatDrawerVisible, saveGraph, setSaveTime]); + + useDebounceEffect( + () => { + saveAgent(); + }, + [nodes, edges], + { + wait: 1000 * 20, + }, + ); + + return time; +}; diff --git a/web/src/pages/data-flow/hooks/use-send-shared-message.ts b/web/src/pages/data-flow/hooks/use-send-shared-message.ts new file mode 100644 index 00000000000..d8e2d61de31 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-send-shared-message.ts @@ -0,0 +1,79 @@ +import { SharedFrom } from '@/constants/chat'; +import { useSetModalState } from '@/hooks/common-hooks'; +import { IEventList } from '@/hooks/use-send-message'; +import { + buildRequestBody, + useSendAgentMessage, +} from '@/pages/agent/chat/use-send-agent-message'; +import trim from 'lodash/trim'; +import { useCallback, useState } from 'react'; +import { useSearchParams } from 'umi'; + +export const useSendButtonDisabled = (value: string) => { + return trim(value) === ''; +}; + +export const useGetSharedChatSearchParams = () => { + const [searchParams] = useSearchParams(); + const data_prefix = 'data_'; + const data = Object.fromEntries( + searchParams + .entries() + .filter(([key]) => key.startsWith(data_prefix)) + .map(([key, value]) => [key.replace(data_prefix, ''), value]), + ); + return { + from: searchParams.get('from') as SharedFrom, + sharedId: searchParams.get('shared_id'), + locale: searchParams.get('locale'), + data: data, + visibleAvatar: searchParams.get('visible_avatar') + ? searchParams.get('visible_avatar') !== '1' + : true, + }; +}; + +export const useSendNextSharedMessage = ( + addEventList: (data: IEventList, messageId: string) => void, + isTaskMode: boolean, +) => { + const { from, sharedId: conversationId } = useGetSharedChatSearchParams(); + const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`; + + const [params, setParams] = useState([]); + + const { + visible: parameterDialogVisible, + hideModal: hideParameterDialog, + showModal: showParameterDialog, + } = useSetModalState(); + + const ret = useSendAgentMessage(url, addEventList, params, true); + + const ok = useCallback( + (params: any[]) => { + if (isTaskMode) { + const msgBody = buildRequestBody(''); + + ret.sendMessage({ + message: msgBody, + beginInputs: params, + }); + } else { + setParams(params); + } + + hideParameterDialog(); + }, + [hideParameterDialog, isTaskMode, ret], + ); + + return { + ...ret, + hasError: false, + parameterDialogVisible, + hideParameterDialog, + showParameterDialog, + ok, + }; +}; diff --git a/web/src/pages/data-flow/hooks/use-set-graph.ts b/web/src/pages/data-flow/hooks/use-set-graph.ts new file mode 100644 index 00000000000..6dd68a330d4 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-set-graph.ts @@ -0,0 +1,17 @@ +import { IGraph } from '@/interfaces/database/flow'; +import { useCallback } from 'react'; +import useGraphStore from '../store'; + +export const useSetGraphInfo = () => { + const { setEdges, setNodes } = useGraphStore((state) => state); + const setGraphInfo = useCallback( + ({ nodes = [], edges = [] }: IGraph) => { + if (nodes.length || edges.length) { + setNodes(nodes); + setEdges(edges); + } + }, + [setEdges, setNodes], + ); + return setGraphInfo; +}; diff --git a/web/src/pages/data-flow/hooks/use-show-dialog.ts b/web/src/pages/data-flow/hooks/use-show-dialog.ts new file mode 100644 index 00000000000..6178e3fbc69 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-show-dialog.ts @@ -0,0 +1,91 @@ +import { useFetchTokenListBeforeOtherStep } from '@/components/embed-dialog/use-show-embed-dialog'; +import { SharedFrom } from '@/constants/chat'; +import { useShowDeleteConfirm } from '@/hooks/common-hooks'; +import { + useCreateSystemToken, + useFetchSystemTokenList, + useRemoveSystemToken, +} from '@/hooks/user-setting-hooks'; +import { IStats } from '@/interfaces/database/chat'; +import { useQueryClient } from '@tanstack/react-query'; +import { useCallback } from 'react'; + +export const useOperateApiKey = (idKey: string, dialogId?: string) => { + const { removeToken } = useRemoveSystemToken(); + const { createToken, loading: creatingLoading } = useCreateSystemToken(); + const { data: tokenList, loading: listLoading } = useFetchSystemTokenList(); + + const showDeleteConfirm = useShowDeleteConfirm(); + + const onRemoveToken = (token: string) => { + showDeleteConfirm({ + onOk: () => removeToken(token), + }); + }; + + const onCreateToken = useCallback(() => { + createToken({ [idKey]: dialogId }); + }, [createToken, idKey, dialogId]); + + return { + removeToken: onRemoveToken, + createToken: onCreateToken, + tokenList, + creatingLoading, + listLoading, + }; +}; + +type ChartStatsType = { + [k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>; +}; + +export const useSelectChartStatsList = (): ChartStatsType => { + const queryClient = useQueryClient(); + const data = queryClient.getQueriesData({ queryKey: ['fetchStats'] }); + const stats: IStats = (data.length > 0 ? data[0][1] : {}) as IStats; + + return Object.keys(stats).reduce((pre, cur) => { + const item = stats[cur as keyof IStats]; + if (item.length > 0) { + pre[cur as keyof IStats] = item.map((x) => ({ + xAxis: x[0] as string, + yAxis: x[1] as number, + })); + } + return pre; + }, {} as ChartStatsType); +}; + +const getUrlWithToken = (token: string, from: string = 'chat') => { + const { protocol, host } = window.location; + return `${protocol}//${host}/chat/share?shared_id=${token}&from=${from}`; +}; + +export const usePreviewChat = (idKey: string) => { + const { handleOperate } = useFetchTokenListBeforeOtherStep(); + + const open = useCallback( + (t: string) => { + window.open( + getUrlWithToken( + t, + idKey === 'canvasId' ? SharedFrom.Agent : SharedFrom.Chat, + ), + '_blank', + ); + }, + [idKey], + ); + + const handlePreview = useCallback(async () => { + const token = await handleOperate(); + if (token) { + open(token); + } + }, [handleOperate, open]); + + return { + handlePreview, + }; +}; diff --git a/web/src/pages/data-flow/hooks/use-show-drawer.tsx b/web/src/pages/data-flow/hooks/use-show-drawer.tsx new file mode 100644 index 00000000000..6789e86ba6f --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-show-drawer.tsx @@ -0,0 +1,186 @@ +import { useSetModalState } from '@/hooks/common-hooks'; +import { NodeMouseHandler } from '@xyflow/react'; +import get from 'lodash/get'; +import React, { useCallback, useEffect } from 'react'; +import { Operator } from '../constant'; +import useGraphStore from '../store'; +import { useCacheChatLog } from './use-cache-chat-log'; +import { useGetBeginNodeDataInputs } from './use-get-begin-query'; +import { useSaveGraph } from './use-save-graph'; + +export const useShowFormDrawer = () => { + const { + clickedNodeId: clickNodeId, + setClickedNodeId, + getNode, + setClickedToolId, + } = useGraphStore((state) => state); + const { + visible: formDrawerVisible, + hideModal: hideFormDrawer, + showModal: showFormDrawer, + } = useSetModalState(); + + const handleShow = useCallback( + (e: React.MouseEvent, nodeId: string) => { + const tool = get(e.target, 'dataset.tool'); + // TODO: Operator type judgment should be used + if (nodeId.startsWith(Operator.Tool) && !tool) { + return; + } + setClickedNodeId(nodeId); + setClickedToolId(tool); + showFormDrawer(); + }, + [setClickedNodeId, setClickedToolId, showFormDrawer], + ); + + return { + formDrawerVisible, + hideFormDrawer, + showFormDrawer: handleShow, + clickedNode: getNode(clickNodeId), + }; +}; + +export const useShowSingleDebugDrawer = () => { + const { visible, showModal, hideModal } = useSetModalState(); + const { saveGraph } = useSaveGraph(); + + const showSingleDebugDrawer = useCallback(async () => { + const saveRet = await saveGraph(); + if (saveRet?.code === 0) { + showModal(); + } + }, [saveGraph, showModal]); + + return { + singleDebugDrawerVisible: visible, + hideSingleDebugDrawer: hideModal, + showSingleDebugDrawer, + }; +}; + +const ExcludedNodes = [Operator.Note]; + +export function useShowDrawer({ + drawerVisible, + hideDrawer, +}: { + drawerVisible: boolean; + hideDrawer(): void; +}) { + const { + visible: runVisible, + showModal: showRunModal, + hideModal: hideRunModal, + } = useSetModalState(); + const { + visible: chatVisible, + showModal: showChatModal, + hideModal: hideChatModal, + } = useSetModalState(); + const { + singleDebugDrawerVisible, + showSingleDebugDrawer, + hideSingleDebugDrawer, + } = useShowSingleDebugDrawer(); + const { formDrawerVisible, hideFormDrawer, showFormDrawer, clickedNode } = + useShowFormDrawer(); + const inputs = useGetBeginNodeDataInputs(); + + useEffect(() => { + if (drawerVisible) { + if (inputs.length > 0) { + showRunModal(); + hideChatModal(); + } else { + showChatModal(); + hideRunModal(); + } + } + }, [ + hideChatModal, + hideRunModal, + showChatModal, + showRunModal, + drawerVisible, + inputs, + ]); + + const hideRunOrChatDrawer = useCallback(() => { + hideChatModal(); + hideRunModal(); + hideDrawer(); + }, [hideChatModal, hideDrawer, hideRunModal]); + + const onPaneClick = useCallback(() => { + hideFormDrawer(); + }, [hideFormDrawer]); + + const onNodeClick: NodeMouseHandler = useCallback( + (e, node) => { + if (!ExcludedNodes.some((x) => x === node.data.label)) { + hideSingleDebugDrawer(); + // hideRunOrChatDrawer(); + showFormDrawer(e, node.id); + } + // handle single debug icon click + if ( + get(e.target, 'dataset.play') === 'true' || + get(e.target, 'parentNode.dataset.play') === 'true' + ) { + showSingleDebugDrawer(); + } + }, + [hideSingleDebugDrawer, showFormDrawer, showSingleDebugDrawer], + ); + + return { + chatVisible, + runVisible, + onPaneClick, + singleDebugDrawerVisible, + showSingleDebugDrawer, + hideSingleDebugDrawer, + formDrawerVisible, + showFormDrawer, + clickedNode, + onNodeClick, + hideFormDrawer, + hideRunOrChatDrawer, + showChatModal, + }; +} + +export function useShowLogSheet({ + setCurrentMessageId, +}: Pick, 'setCurrentMessageId'>) { + const { visible, showModal, hideModal } = useSetModalState(); + + const handleShow = useCallback( + (messageId: string) => { + setCurrentMessageId(messageId); + showModal(); + }, + [setCurrentMessageId, showModal], + ); + + return { + logSheetVisible: visible, + hideLogSheet: hideModal, + showLogSheet: handleShow, + }; +} + +export function useHideFormSheetOnNodeDeletion({ + hideFormDrawer, +}: Pick, 'hideFormDrawer'>) { + const { nodes, clickedNodeId } = useGraphStore((state) => state); + + useEffect(() => { + if (!nodes.some((x) => x.id === clickedNodeId)) { + hideFormDrawer(); + } + }, [clickedNodeId, hideFormDrawer, nodes]); +} diff --git a/web/src/pages/data-flow/hooks/use-watch-form-change.ts b/web/src/pages/data-flow/hooks/use-watch-form-change.ts new file mode 100644 index 00000000000..534c2e2a9c3 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-watch-form-change.ts @@ -0,0 +1,18 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../store'; + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id) { + values = form?.getValues() || {}; + let nextValues: any = values; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} diff --git a/web/src/pages/data-flow/index.tsx b/web/src/pages/data-flow/index.tsx new file mode 100644 index 00000000000..4a1fc81aee4 --- /dev/null +++ b/web/src/pages/data-flow/index.tsx @@ -0,0 +1,187 @@ +import { PageHeader } from '@/components/page-header'; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from '@/components/ui/breadcrumb'; +import { Button, ButtonLoading } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { useSetModalState } from '@/hooks/common-hooks'; +import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; +import { ReactFlowProvider } from '@xyflow/react'; +import { + ChevronDown, + CirclePlay, + Download, + History, + LaptopMinimalCheck, + Settings, + Upload, +} from 'lucide-react'; +import { ComponentPropsWithoutRef, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import AgentCanvas from './canvas'; +import { DropdownProvider } from './canvas/context'; +import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; +import { useFetchDataOnMount } from './hooks/use-fetch-data'; +import { useGetBeginNodeDataInputs } from './hooks/use-get-begin-query'; +import { + useSaveGraph, + useSaveGraphBeforeOpeningDebugDrawer, + useWatchAgentChange, +} from './hooks/use-save-graph'; +import { SettingDialog } from './setting-dialog'; +import { UploadAgentDialog } from './upload-agent-dialog'; +import { useAgentHistoryManager } from './use-agent-history-manager'; +import { VersionDialog } from './version-dialog'; + +function AgentDropdownMenuItem({ + children, + ...props +}: ComponentPropsWithoutRef) { + return ( + + {children} + + ); +} + +export default function DataFlow() { + const { navigateToAgents } = useNavigatePage(); + const { + visible: chatDrawerVisible, + hideModal: hideChatDrawer, + showModal: showChatDrawer, + } = useSetModalState(); + const { t } = useTranslation(); + useAgentHistoryManager(); + const { + handleExportJson, + handleImportJson, + fileUploadVisible, + onFileUploadOk, + hideFileUploadModal, + } = useHandleExportOrImportJsonFile(); + const { saveGraph, loading } = useSaveGraph(); + const { flowDetail: agentDetail } = useFetchDataOnMount(); + const inputs = useGetBeginNodeDataInputs(); + const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); + const handleRunAgent = useCallback(() => { + if (inputs.length > 0) { + showChatDrawer(); + } else { + handleRun(); + } + }, [handleRun, inputs, showChatDrawer]); + const { + visible: versionDialogVisible, + hideModal: hideVersionDialog, + showModal: showVersionDialog, + } = useSetModalState(); + + const { + visible: settingDialogVisible, + hideModal: hideSettingDialog, + showModal: showSettingDialog, + } = useSetModalState(); + + const time = useWatchAgentChange(chatDrawerVisible); + + return ( +
    + +
    + + + + + Agent + + + + + {agentDetail.title} + + + +
    + {t('flow.autosaved')} {time} +
    +
    +
    + saveGraph()} + loading={loading} + > + {t('flow.save')} + + + + + + + + + + + + {t('flow.import')} + + + + + {t('flow.export')} + + + + + {t('flow.setting')} + + + +
    +
    + + + + + + {fileUploadVisible && ( + + )} + + {versionDialogVisible && ( + + + + )} + {settingDialogVisible && ( + + )} +
    + ); +} diff --git a/web/src/pages/data-flow/interface.ts b/web/src/pages/data-flow/interface.ts new file mode 100644 index 00000000000..0a6cba2f016 --- /dev/null +++ b/web/src/pages/data-flow/interface.ts @@ -0,0 +1,43 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { FormInstance } from 'antd'; + +export interface IOperatorForm { + onValuesChange?(changedValues: any, values: any): void; + form?: FormInstance; + node?: RAGFlowNodeType; + nodeId?: string; +} + +export interface INextOperatorForm { + node?: RAGFlowNodeType; + nodeId?: string; +} + +export interface IGenerateParameter { + id?: string; + key: string; + component_id?: string; +} + +export interface IInvokeVariable extends IGenerateParameter { + value?: string; +} + +export type IPosition = { top: number; right: number; idx: number }; + +export interface BeginQuery { + key: string; + type: string; + value: string; + optional: boolean; + name: string; + options: (number | string | boolean)[]; +} + +export type IInputs = { + avatar: string; + title: string; + inputs: Record; + prologue: string; + mode: string; +}; diff --git a/web/src/pages/data-flow/operator-icon.tsx b/web/src/pages/data-flow/operator-icon.tsx new file mode 100644 index 00000000000..b8ebc34ba2b --- /dev/null +++ b/web/src/pages/data-flow/operator-icon.tsx @@ -0,0 +1,87 @@ +import { ReactComponent as ArxivIcon } from '@/assets/svg/arxiv.svg'; +import { ReactComponent as BingIcon } from '@/assets/svg/bing.svg'; +import { ReactComponent as CrawlerIcon } from '@/assets/svg/crawler.svg'; +import { ReactComponent as DuckIcon } from '@/assets/svg/duck.svg'; +import { ReactComponent as GithubIcon } from '@/assets/svg/github.svg'; +import { ReactComponent as GoogleScholarIcon } from '@/assets/svg/google-scholar.svg'; +import { ReactComponent as GoogleIcon } from '@/assets/svg/google.svg'; +import { ReactComponent as PubMedIcon } from '@/assets/svg/pubmed.svg'; +import { ReactComponent as SearXNGIcon } from '@/assets/svg/searxng.svg'; +import { ReactComponent as TavilyIcon } from '@/assets/svg/tavily.svg'; +import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg'; +import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg'; +import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg'; + +import { IconFont } from '@/components/icon-font'; +import { cn } from '@/lib/utils'; +import { HousePlus } from 'lucide-react'; +import { Operator } from './constant'; + +interface IProps { + name: Operator; + className?: string; +} + +export const OperatorIconMap = { + [Operator.Retrieval]: 'KR', + [Operator.Begin]: 'house-plus', + [Operator.Categorize]: 'a-QuestionClassification', + [Operator.Message]: 'reply', + [Operator.Iteration]: 'loop', + [Operator.Switch]: 'condition', + [Operator.Code]: 'code-set', + [Operator.Agent]: 'agent-ai', + [Operator.UserFillUp]: 'await', + [Operator.StringTransform]: 'a-textprocessing', + [Operator.Note]: 'notebook-pen', + [Operator.ExeSQL]: 'executesql-0', + [Operator.Invoke]: 'httprequest-0', + [Operator.Email]: 'sendemail-0', +}; + +export const SVGIconMap = { + [Operator.ArXiv]: ArxivIcon, + [Operator.GitHub]: GithubIcon, + [Operator.Bing]: BingIcon, + [Operator.DuckDuckGo]: DuckIcon, + [Operator.Google]: GoogleIcon, + [Operator.GoogleScholar]: GoogleScholarIcon, + [Operator.PubMed]: PubMedIcon, + [Operator.SearXNG]: SearXNGIcon, + [Operator.TavilyExtract]: TavilyIcon, + [Operator.TavilySearch]: TavilyIcon, + [Operator.Wikipedia]: WikipediaIcon, + [Operator.YahooFinance]: YahooFinanceIcon, + [Operator.WenCai]: WenCaiIcon, + [Operator.Crawler]: CrawlerIcon, +}; + +const Empty = () => { + return
    ; +}; + +const OperatorIcon = ({ name, className }: IProps) => { + const Icon = OperatorIconMap[name as keyof typeof OperatorIconMap] || Empty; + const SvgIcon = SVGIconMap[name as keyof typeof SVGIconMap] || Empty; + + if (name === Operator.Begin) { + return ( +
    + +
    + ); + } + + return typeof Icon === 'string' ? ( + + ) : ( + + ); +}; + +export default OperatorIcon; diff --git a/web/src/pages/data-flow/options.ts b/web/src/pages/data-flow/options.ts new file mode 100644 index 00000000000..4ad5a645764 --- /dev/null +++ b/web/src/pages/data-flow/options.ts @@ -0,0 +1,2174 @@ +import { upperFirst } from 'lodash'; + +export const LanguageOptions = [ + { + value: 'af', + label: 'Afrikaans', + }, + { + value: 'pl', + label: 'Polski', + }, + { + value: 'ar', + label: 'العربية', + }, + { + value: 'ast', + label: 'Asturianu', + }, + { + value: 'az', + label: 'Azərbaycanca', + }, + { + value: 'bg', + label: 'Български', + }, + { + value: 'nan', + label: '閩南語 / Bân-lâm-gú', + }, + { + value: 'bn', + label: 'বাংলা', + }, + { + value: 'be', + label: 'Беларуская', + }, + { + value: 'ca', + label: 'Català', + }, + { + value: 'cs', + label: 'Čeština', + }, + { + value: 'cy', + label: 'Cymraeg', + }, + { + value: 'da', + label: 'Dansk', + }, + { + value: 'de', + label: 'Deutsch', + }, + { + value: 'fr', + label: 'Français', + }, + { + value: 'et', + label: 'Eesti', + }, + { + value: 'el', + label: 'Ελληνικά', + }, + { + value: 'en', + label: 'English', + }, + { + value: 'es', + label: 'Español', + }, + { + value: 'eo', + label: 'Esperanto', + }, + { + value: 'eu', + label: 'Euskara', + }, + { + value: 'fa', + label: 'فارسی', + }, + { + value: 'fr', + label: 'Français', + }, + { + value: 'gl', + label: 'Galego', + }, + { + value: 'ko', + label: '한국어', + }, + { + value: 'hy', + label: 'Հայերեն', + }, + { + value: 'hi', + label: 'हिन्दी', + }, + { + value: 'hr', + label: 'Hrvatski', + }, + { + value: 'id', + label: 'Bahasa Indonesia', + }, + { + value: 'it', + label: 'Italiano', + }, + { + value: 'he', + label: 'עברית', + }, + { + value: 'ka', + label: 'ქართული', + }, + { + value: 'lld', + label: 'Ladin', + }, + { + value: 'la', + label: 'Latina', + }, + { + value: 'lv', + label: 'Latviešu', + }, + { + value: 'lt', + label: 'Lietuvių', + }, + { + value: 'hu', + label: 'Magyar', + }, + { + value: 'mk', + label: 'Македонски', + }, + { + value: 'arz', + label: 'مصرى', + }, + { + value: 'ms', + label: 'Bahasa Melayu', + }, + { + value: 'min', + label: 'Bahaso Minangkabau', + }, + { + value: 'my', + label: 'မြန်မာဘာသာ', + }, + { + value: 'nl', + label: 'Nederlands', + }, + { + value: 'ja', + label: '日本語', + }, + { + value: 'no', + label: 'Norsk (bokmål)', + }, + { + value: 'nn', + label: 'Norsk (nynorsk)', + }, + { + value: 'ce', + label: 'Нохчийн', + }, + { + value: 'uz', + label: 'Oʻzbekcha / Ўзбекча', + }, + { + value: 'pt', + label: 'Português', + }, + { + value: 'kk', + label: 'Қазақша / Qazaqşa / قازاقشا', + }, + { + value: 'ro', + label: 'Română', + }, + { + value: 'ru', + label: 'Русский', + }, + { + value: 'ceb', + label: 'Sinugboanong Binisaya', + }, + { + value: 'sk', + label: 'Slovenčina', + }, + { + value: 'sl', + label: 'Slovenščina', + }, + { + value: 'sr', + label: 'Српски / Srpski', + }, + { + value: 'sh', + label: 'Srpskohrvatski / Српскохрватски', + }, + { + value: 'fi', + label: 'Suomi', + }, + { + value: 'sv', + label: 'Svenska', + }, + { + value: 'ta', + label: 'தமிழ்', + }, + { + value: 'tt', + label: 'Татарча / Tatarça', + }, + { + value: 'th', + label: 'ภาษาไทย', + }, + { + value: 'tg', + label: 'Тоҷикӣ', + }, + { + value: 'azb', + label: 'تۆرکجه', + }, + { + value: 'tr', + label: 'Türkçe', + }, + { + value: 'uk', + label: 'Українська', + }, + { + value: 'ur', + label: 'اردو', + }, + { + value: 'vi', + label: 'Tiếng Việt', + }, + { + value: 'war', + label: 'Winaray', + }, + { + value: 'zh', + label: '中文', + }, + { + value: 'yue', + label: '粵語', + }, +]; + +export const GoogleLanguageOptions = [ + { + language_code: 'af', + language_name: 'Afrikaans', + }, + { + language_code: 'ak', + language_name: 'Akan', + }, + { + language_code: 'sq', + language_name: 'Albanian', + }, + { + language_code: 'ws', + language_name: 'Samoa', + }, + { + language_code: 'am', + language_name: 'Amharic', + }, + { + language_code: 'ar', + language_name: 'Arabic', + }, + { + language_code: 'hy', + language_name: 'Armenian', + }, + { + language_code: 'az', + language_name: 'Azerbaijani', + }, + { + language_code: 'eu', + language_name: 'Basque', + }, + { + language_code: 'be', + language_name: 'Belarusian', + }, + { + language_code: 'bem', + language_name: 'Bemba', + }, + { + language_code: 'bn', + language_name: 'Bengali', + }, + { + language_code: 'bh', + language_name: 'Bihari', + }, + { + language_code: 'xx-bork', + language_name: 'Bork, bork, bork!', + }, + { + language_code: 'bs', + language_name: 'Bosnian', + }, + { + language_code: 'br', + language_name: 'Breton', + }, + { + language_code: 'bg', + language_name: 'Bulgarian', + }, + { + language_code: 'bt', + language_name: 'Bhutanese', + }, + { + language_code: 'km', + language_name: 'Cambodian', + }, + { + language_code: 'ca', + language_name: 'Catalan', + }, + { + language_code: 'chr', + language_name: 'Cherokee', + }, + { + language_code: 'ny', + language_name: 'Chichewa', + }, + { + language_code: 'zh-cn', + language_name: 'Chinese (Simplified)', + }, + { + language_code: 'zh-tw', + language_name: 'Chinese (Traditional)', + }, + { + language_code: 'co', + language_name: 'Corsican', + }, + { + language_code: 'hr', + language_name: 'Croatian', + }, + { + language_code: 'cs', + language_name: 'Czech', + }, + { + language_code: 'da', + language_name: 'Danish', + }, + { + language_code: 'nl', + language_name: 'Dutch', + }, + { + language_code: 'xx-elmer', + language_name: 'Elmer Fudd', + }, + { + language_code: 'en', + language_name: 'English', + }, + { + language_code: 'eo', + language_name: 'Esperanto', + }, + { + language_code: 'et', + language_name: 'Estonian', + }, + { + language_code: 'ee', + language_name: 'Ewe', + }, + { + language_code: 'fo', + language_name: 'Faroese', + }, + { + language_code: 'tl', + language_name: 'Filipino', + }, + { + language_code: 'fi', + language_name: 'Finnish', + }, + { + language_code: 'fr', + language_name: 'French', + }, + { + language_code: 'fy', + language_name: 'Frisian', + }, + { + language_code: 'gaa', + language_name: 'Ga', + }, + { + language_code: 'gl', + language_name: 'Galician', + }, + { + language_code: 'ka', + language_name: 'Georgian', + }, + { + language_code: 'de', + language_name: 'German', + }, + { + language_code: 'el', + language_name: 'Greek', + }, + { + language_code: 'kl', + language_name: 'Greenlandic', + }, + { + language_code: 'gn', + language_name: 'Guarani', + }, + { + language_code: 'gu', + language_name: 'Gujarati', + }, + { + language_code: 'xx-hacker', + language_name: 'Hacker', + }, + { + language_code: 'ht', + language_name: 'Haitian Creole', + }, + { + language_code: 'ha', + language_name: 'Hausa', + }, + { + language_code: 'haw', + language_name: 'Hawaiian', + }, + { + language_code: 'iw', + language_name: 'Hebrew', + }, + { + language_code: 'hi', + language_name: 'Hindi', + }, + { + language_code: 'hu', + language_name: 'Hungarian', + }, + { + language_code: 'is', + language_name: 'Icelandic', + }, + { + language_code: 'ig', + language_name: 'Igbo', + }, + { + language_code: 'id', + language_name: 'Indonesian', + }, + { + language_code: 'ia', + language_name: 'Interlingua', + }, + { + language_code: 'ga', + language_name: 'Irish', + }, + { + language_code: 'it', + language_name: 'Italian', + }, + { + language_code: 'ja', + language_name: 'Japanese', + }, + { + language_code: 'jw', + language_name: 'Javanese', + }, + { + language_code: 'kn', + language_name: 'Kannada', + }, + { + language_code: 'kk', + language_name: 'Kazakh', + }, + { + language_code: 'rw', + language_name: 'Kinyarwanda', + }, + { + language_code: 'rn', + language_name: 'Kirundi', + }, + { + language_code: 'xx-klingon', + language_name: 'Klingon', + }, + { + language_code: 'kg', + language_name: 'Kongo', + }, + { + language_code: 'ko', + language_name: 'Korean', + }, + { + language_code: 'kri', + language_name: 'Krio (Sierra Leone)', + }, + { + language_code: 'ku', + language_name: 'Kurdish', + }, + { + language_code: 'ckb', + language_name: 'Kurdish (Soranî)', + }, + { + language_code: 'ky', + language_name: 'Kyrgyz', + }, + { + language_code: 'lo', + language_name: 'Laothian', + }, + { + language_code: 'la', + language_name: 'Latin', + }, + { + language_code: 'lv', + language_name: 'Latvian', + }, + { + language_code: 'ln', + language_name: 'Lingala', + }, + { + language_code: 'lt', + language_name: 'Lithuanian', + }, + { + language_code: 'loz', + language_name: 'Lozi', + }, + { + language_code: 'lg', + language_name: 'Luganda', + }, + { + language_code: 'ach', + language_name: 'Luo', + }, + { + language_code: 'mk', + language_name: 'Macedonian', + }, + { + language_code: 'mg', + language_name: 'Malagasy', + }, + { + language_code: 'ms', + language_name: 'Malay', + }, + { + language_code: 'ml', + language_name: 'Malayalam', + }, + { + language_code: 'mt', + language_name: 'Maltese', + }, + { + language_code: 'mv', + language_name: 'Maldives', + }, + { + language_code: 'mi', + language_name: 'Maori', + }, + { + language_code: 'mr', + language_name: 'Marathi', + }, + { + language_code: 'mfe', + language_name: 'Mauritian Creole', + }, + { + language_code: 'mo', + language_name: 'Moldavian', + }, + { + language_code: 'mn', + language_name: 'Mongolian', + }, + { + language_code: 'sr-me', + language_name: 'Montenegrin', + }, + { + language_code: 'my', + language_name: 'Myanmar', + }, + { + language_code: 'ne', + language_name: 'Nepali', + }, + { + language_code: 'pcm', + language_name: 'Nigerian Pidgin', + }, + { + language_code: 'nso', + language_name: 'Northern Sotho', + }, + { + language_code: 'no', + language_name: 'Norwegian', + }, + { + language_code: 'nn', + language_name: 'Norwegian (Nynorsk)', + }, + { + language_code: 'oc', + language_name: 'Occitan', + }, + { + language_code: 'or', + language_name: 'Oriya', + }, + { + language_code: 'om', + language_name: 'Oromo', + }, + { + language_code: 'ps', + language_name: 'Pashto', + }, + { + language_code: 'fa', + language_name: 'Persian', + }, + { + language_code: 'xx-pirate', + language_name: 'Pirate', + }, + { + language_code: 'pl', + language_name: 'Polish', + }, + { + language_code: 'pt', + language_name: 'Portuguese', + }, + { + language_code: 'pt-br', + language_name: 'Portuguese (Brazil)', + }, + { + language_code: 'pt-pt', + language_name: 'Portuguese (Portugal)', + }, + { + language_code: 'pa', + language_name: 'Punjabi', + }, + { + language_code: 'qu', + language_name: 'Quechua', + }, + { + language_code: 'ro', + language_name: 'Romanian', + }, + { + language_code: 'rm', + language_name: 'Romansh', + }, + { + language_code: 'nyn', + language_name: 'Runyakitara', + }, + { + language_code: 'ru', + language_name: 'Russian', + }, + { + language_code: 'gd', + language_name: 'Scots Gaelic', + }, + { + language_code: 'sr', + language_name: 'Serbian', + }, + { + language_code: 'sh', + language_name: 'Serbo-Croatian', + }, + { + language_code: 'st', + language_name: 'Sesotho', + }, + { + language_code: 'tn', + language_name: 'Setswana', + }, + { + language_code: 'crs', + language_name: 'Seychellois Creole', + }, + { + language_code: 'sn', + language_name: 'Shona', + }, + { + language_code: 'sd', + language_name: 'Sindhi', + }, + { + language_code: 'si', + language_name: 'Sinhalese', + }, + { + language_code: 'sk', + language_name: 'Slovak', + }, + { + language_code: 'sl', + language_name: 'Slovenian', + }, + { + language_code: 'so', + language_name: 'Somali', + }, + { + language_code: 'es', + language_name: 'Spanish', + }, + { + language_code: 'es-419', + language_name: 'Spanish (Latin American)', + }, + { + language_code: 'su', + language_name: 'Sundanese', + }, + { + language_code: 'sw', + language_name: 'Swahili', + }, + { + language_code: 'sv', + language_name: 'Swedish', + }, + { + language_code: 'tg', + language_name: 'Tajik', + }, + { + language_code: 'ta', + language_name: 'Tamil', + }, + { + language_code: 'tt', + language_name: 'Tatar', + }, + { + language_code: 'te', + language_name: 'Telugu', + }, + { + language_code: 'th', + language_name: 'Thai', + }, + { + language_code: 'ti', + language_name: 'Tigrinya', + }, + { + language_code: 'to', + language_name: 'Tonga', + }, + { + language_code: 'lua', + language_name: 'Tshiluba', + }, + { + language_code: 'tum', + language_name: 'Tumbuka', + }, + { + language_code: 'tr', + language_name: 'Turkish', + }, + { + language_code: 'tk', + language_name: 'Turkmen', + }, + { + language_code: 'tw', + language_name: 'Twi', + }, + { + language_code: 'ug', + language_name: 'Uighur', + }, + { + language_code: 'uk', + language_name: 'Ukrainian', + }, + { + language_code: 'ur', + language_name: 'Urdu', + }, + { + language_code: 'uz', + language_name: 'Uzbek', + }, + { + language_code: 'vu', + language_name: 'Vanuatu', + }, + { + language_code: 'vi', + language_name: 'Vietnamese', + }, + { + language_code: 'cy', + language_name: 'Welsh', + }, + { + language_code: 'wo', + language_name: 'Wolof', + }, + { + language_code: 'xh', + language_name: 'Xhosa', + }, + { + language_code: 'yi', + language_name: 'Yiddish', + }, + { + language_code: 'yo', + language_name: 'Yoruba', + }, + { + language_code: 'zu', + language_name: 'Zulu', + }, +].map((x) => ({ label: x.language_name, value: x.language_code })); + +export const GoogleCountryOptions = [ + { + country_code: 'af', + country_name: 'Afghanistan', + }, + { + country_code: 'al', + country_name: 'Albania', + }, + { + country_code: 'dz', + country_name: 'Algeria', + }, + { + country_code: 'as', + country_name: 'American Samoa', + }, + { + country_code: 'ad', + country_name: 'Andorra', + }, + { + country_code: 'ao', + country_name: 'Angola', + }, + { + country_code: 'ai', + country_name: 'Anguilla', + }, + { + country_code: 'aq', + country_name: 'Antarctica', + }, + { + country_code: 'ag', + country_name: 'Antigua and Barbuda', + }, + { + country_code: 'ar', + country_name: 'Argentina', + }, + { + country_code: 'am', + country_name: 'Armenia', + }, + { + country_code: 'aw', + country_name: 'Aruba', + }, + { + country_code: 'au', + country_name: 'Australia', + }, + { + country_code: 'at', + country_name: 'Austria', + }, + { + country_code: 'az', + country_name: 'Azerbaijan', + }, + { + country_code: 'bs', + country_name: 'Bahamas', + }, + { + country_code: 'bh', + country_name: 'Bahrain', + }, + { + country_code: 'bd', + country_name: 'Bangladesh', + }, + { + country_code: 'bb', + country_name: 'Barbados', + }, + { + country_code: 'by', + country_name: 'Belarus', + }, + { + country_code: 'be', + country_name: 'Belgium', + }, + { + country_code: 'bz', + country_name: 'Belize', + }, + { + country_code: 'bj', + country_name: 'Benin', + }, + { + country_code: 'bm', + country_name: 'Bermuda', + }, + { + country_code: 'bt', + country_name: 'Bhutan', + }, + { + country_code: 'bo', + country_name: 'Bolivia', + }, + { + country_code: 'ba', + country_name: 'Bosnia and Herzegovina', + }, + { + country_code: 'bw', + country_name: 'Botswana', + }, + { + country_code: 'bv', + country_name: 'Bouvet Island', + }, + { + country_code: 'br', + country_name: 'Brazil', + }, + { + country_code: 'io', + country_name: 'British Indian Ocean Territory', + }, + { + country_code: 'bn', + country_name: 'Brunei Darussalam', + }, + { + country_code: 'bg', + country_name: 'Bulgaria', + }, + { + country_code: 'bf', + country_name: 'Burkina Faso', + }, + { + country_code: 'bi', + country_name: 'Burundi', + }, + { + country_code: 'kh', + country_name: 'Cambodia', + }, + { + country_code: 'cm', + country_name: 'Cameroon', + }, + { + country_code: 'ca', + country_name: 'Canada', + }, + { + country_code: 'cv', + country_name: 'Cape Verde', + }, + { + country_code: 'ky', + country_name: 'Cayman Islands', + }, + { + country_code: 'cf', + country_name: 'Central African Republic', + }, + { + country_code: 'td', + country_name: 'Chad', + }, + { + country_code: 'cl', + country_name: 'Chile', + }, + { + country_code: 'cn', + country_name: 'China', + }, + { + country_code: 'cx', + country_name: 'Christmas Island', + }, + { + country_code: 'cc', + country_name: 'Cocos (Keeling) Islands', + }, + { + country_code: 'co', + country_name: 'Colombia', + }, + { + country_code: 'km', + country_name: 'Comoros', + }, + { + country_code: 'cg', + country_name: 'Congo', + }, + { + country_code: 'cd', + country_name: 'Congo, the Democratic Republic of the', + }, + { + country_code: 'ck', + country_name: 'Cook Islands', + }, + { + country_code: 'cr', + country_name: 'Costa Rica', + }, + { + country_code: 'ci', + country_name: "Cote D'ivoire", + }, + { + country_code: 'hr', + country_name: 'Croatia', + }, + { + country_code: 'cu', + country_name: 'Cuba', + }, + { + country_code: 'cy', + country_name: 'Cyprus', + }, + { + country_code: 'cz', + country_name: 'Czech Republic', + }, + { + country_code: 'dk', + country_name: 'Denmark', + }, + { + country_code: 'dj', + country_name: 'Djibouti', + }, + { + country_code: 'dm', + country_name: 'Dominica', + }, + { + country_code: 'do', + country_name: 'Dominican Republic', + }, + { + country_code: 'ec', + country_name: 'Ecuador', + }, + { + country_code: 'eg', + country_name: 'Egypt', + }, + { + country_code: 'sv', + country_name: 'El Salvador', + }, + { + country_code: 'gq', + country_name: 'Equatorial Guinea', + }, + { + country_code: 'er', + country_name: 'Eritrea', + }, + { + country_code: 'ee', + country_name: 'Estonia', + }, + { + country_code: 'et', + country_name: 'Ethiopia', + }, + { + country_code: 'fk', + country_name: 'Falkland Islands (Malvinas)', + }, + { + country_code: 'fo', + country_name: 'Faroe Islands', + }, + { + country_code: 'fj', + country_name: 'Fiji', + }, + { + country_code: 'fi', + country_name: 'Finland', + }, + { + country_code: 'fr', + country_name: 'France', + }, + { + country_code: 'gf', + country_name: 'French Guiana', + }, + { + country_code: 'pf', + country_name: 'French Polynesia', + }, + { + country_code: 'tf', + country_name: 'French Southern Territories', + }, + { + country_code: 'ga', + country_name: 'Gabon', + }, + { + country_code: 'gm', + country_name: 'Gambia', + }, + { + country_code: 'ge', + country_name: 'Georgia', + }, + { + country_code: 'de', + country_name: 'Germany', + }, + { + country_code: 'gh', + country_name: 'Ghana', + }, + { + country_code: 'gi', + country_name: 'Gibraltar', + }, + { + country_code: 'gr', + country_name: 'Greece', + }, + { + country_code: 'gl', + country_name: 'Greenland', + }, + { + country_code: 'gd', + country_name: 'Grenada', + }, + { + country_code: 'gp', + country_name: 'Guadeloupe', + }, + { + country_code: 'gu', + country_name: 'Guam', + }, + { + country_code: 'gt', + country_name: 'Guatemala', + }, + { + country_code: 'gn', + country_name: 'Guinea', + }, + { + country_code: 'gw', + country_name: 'Guinea-Bissau', + }, + { + country_code: 'gy', + country_name: 'Guyana', + }, + { + country_code: 'ht', + country_name: 'Haiti', + }, + { + country_code: 'hm', + country_name: 'Heard Island and Mcdonald Islands', + }, + { + country_code: 'va', + country_name: 'Holy See (Vatican City State)', + }, + { + country_code: 'hn', + country_name: 'Honduras', + }, + { + country_code: 'hk', + country_name: 'Hong Kong', + }, + { + country_code: 'hu', + country_name: 'Hungary', + }, + { + country_code: 'is', + country_name: 'Iceland', + }, + { + country_code: 'in', + country_name: 'India', + }, + { + country_code: 'id', + country_name: 'Indonesia', + }, + { + country_code: 'ir', + country_name: 'Iran, Islamic Republic of', + }, + { + country_code: 'iq', + country_name: 'Iraq', + }, + { + country_code: 'ie', + country_name: 'Ireland', + }, + { + country_code: 'il', + country_name: 'Israel', + }, + { + country_code: 'it', + country_name: 'Italy', + }, + { + country_code: 'jm', + country_name: 'Jamaica', + }, + { + country_code: 'jp', + country_name: 'Japan', + }, + { + country_code: 'jo', + country_name: 'Jordan', + }, + { + country_code: 'kz', + country_name: 'Kazakhstan', + }, + { + country_code: 'ke', + country_name: 'Kenya', + }, + { + country_code: 'ki', + country_name: 'Kiribati', + }, + { + country_code: 'kp', + country_name: "Korea, Democratic People's Republic of", + }, + { + country_code: 'kr', + country_name: 'Korea, Republic of', + }, + { + country_code: 'kw', + country_name: 'Kuwait', + }, + { + country_code: 'kg', + country_name: 'Kyrgyzstan', + }, + { + country_code: 'la', + country_name: "Lao People's Democratic Republic", + }, + { + country_code: 'lv', + country_name: 'Latvia', + }, + { + country_code: 'lb', + country_name: 'Lebanon', + }, + { + country_code: 'ls', + country_name: 'Lesotho', + }, + { + country_code: 'lr', + country_name: 'Liberia', + }, + { + country_code: 'ly', + country_name: 'Libyan Arab Jamahiriya', + }, + { + country_code: 'li', + country_name: 'Liechtenstein', + }, + { + country_code: 'lt', + country_name: 'Lithuania', + }, + { + country_code: 'lu', + country_name: 'Luxembourg', + }, + { + country_code: 'mo', + country_name: 'Macao', + }, + { + country_code: 'mk', + country_name: 'Macedonia, the Former Yugosalv Republic of', + }, + { + country_code: 'mg', + country_name: 'Madagascar', + }, + { + country_code: 'mw', + country_name: 'Malawi', + }, + { + country_code: 'my', + country_name: 'Malaysia', + }, + { + country_code: 'mv', + country_name: 'Maldives', + }, + { + country_code: 'ml', + country_name: 'Mali', + }, + { + country_code: 'mt', + country_name: 'Malta', + }, + { + country_code: 'mh', + country_name: 'Marshall Islands', + }, + { + country_code: 'mq', + country_name: 'Martinique', + }, + { + country_code: 'mr', + country_name: 'Mauritania', + }, + { + country_code: 'mu', + country_name: 'Mauritius', + }, + { + country_code: 'yt', + country_name: 'Mayotte', + }, + { + country_code: 'mx', + country_name: 'Mexico', + }, + { + country_code: 'fm', + country_name: 'Micronesia, Federated States of', + }, + { + country_code: 'md', + country_name: 'Moldova, Republic of', + }, + { + country_code: 'mc', + country_name: 'Monaco', + }, + { + country_code: 'mn', + country_name: 'Mongolia', + }, + { + country_code: 'ms', + country_name: 'Montserrat', + }, + { + country_code: 'ma', + country_name: 'Morocco', + }, + { + country_code: 'mz', + country_name: 'Mozambique', + }, + { + country_code: 'mm', + country_name: 'Myanmar', + }, + { + country_code: 'na', + country_name: 'Namibia', + }, + { + country_code: 'nr', + country_name: 'Nauru', + }, + { + country_code: 'np', + country_name: 'Nepal', + }, + { + country_code: 'nl', + country_name: 'Netherlands', + }, + { + country_code: 'an', + country_name: 'Netherlands Antilles', + }, + { + country_code: 'nc', + country_name: 'New Caledonia', + }, + { + country_code: 'nz', + country_name: 'New Zealand', + }, + { + country_code: 'ni', + country_name: 'Nicaragua', + }, + { + country_code: 'ne', + country_name: 'Niger', + }, + { + country_code: 'ng', + country_name: 'Nigeria', + }, + { + country_code: 'nu', + country_name: 'Niue', + }, + { + country_code: 'nf', + country_name: 'Norfolk Island', + }, + { + country_code: 'mp', + country_name: 'Northern Mariana Islands', + }, + { + country_code: 'no', + country_name: 'Norway', + }, + { + country_code: 'om', + country_name: 'Oman', + }, + { + country_code: 'pk', + country_name: 'Pakistan', + }, + { + country_code: 'pw', + country_name: 'Palau', + }, + { + country_code: 'ps', + country_name: 'Palestinian Territory, Occupied', + }, + { + country_code: 'pa', + country_name: 'Panama', + }, + { + country_code: 'pg', + country_name: 'Papua New Guinea', + }, + { + country_code: 'py', + country_name: 'Paraguay', + }, + { + country_code: 'pe', + country_name: 'Peru', + }, + { + country_code: 'ph', + country_name: 'Philippines', + }, + { + country_code: 'pn', + country_name: 'Pitcairn', + }, + { + country_code: 'pl', + country_name: 'Poland', + }, + { + country_code: 'pt', + country_name: 'Portugal', + }, + { + country_code: 'pr', + country_name: 'Puerto Rico', + }, + { + country_code: 'qa', + country_name: 'Qatar', + }, + { + country_code: 're', + country_name: 'Reunion', + }, + { + country_code: 'ro', + country_name: 'Romania', + }, + { + country_code: 'ru', + country_name: 'Russian Federation', + }, + { + country_code: 'rw', + country_name: 'Rwanda', + }, + { + country_code: 'sh', + country_name: 'Saint Helena', + }, + { + country_code: 'kn', + country_name: 'Saint Kitts and Nevis', + }, + { + country_code: 'lc', + country_name: 'Saint Lucia', + }, + { + country_code: 'pm', + country_name: 'Saint Pierre and Miquelon', + }, + { + country_code: 'vc', + country_name: 'Saint Vincent and the Grenadines', + }, + { + country_code: 'ws', + country_name: 'Samoa', + }, + { + country_code: 'sm', + country_name: 'San Marino', + }, + { + country_code: 'st', + country_name: 'Sao Tome and Principe', + }, + { + country_code: 'sa', + country_name: 'Saudi Arabia', + }, + { + country_code: 'sn', + country_name: 'Senegal', + }, + { + country_code: 'rs', + country_name: 'Serbia and Montenegro', + }, + { + country_code: 'sc', + country_name: 'Seychelles', + }, + { + country_code: 'sl', + country_name: 'Sierra Leone', + }, + { + country_code: 'sg', + country_name: 'Singapore', + }, + { + country_code: 'sk', + country_name: 'Slovakia', + }, + { + country_code: 'si', + country_name: 'Slovenia', + }, + { + country_code: 'sb', + country_name: 'Solomon Islands', + }, + { + country_code: 'so', + country_name: 'Somalia', + }, + { + country_code: 'za', + country_name: 'South Africa', + }, + { + country_code: 'gs', + country_name: 'South Georgia and the South Sandwich Islands', + }, + { + country_code: 'es', + country_name: 'Spain', + }, + { + country_code: 'lk', + country_name: 'Sri Lanka', + }, + { + country_code: 'sd', + country_name: 'Sudan', + }, + { + country_code: 'sr', + country_name: 'Suriname', + }, + { + country_code: 'sj', + country_name: 'Svalbard and Jan Mayen', + }, + { + country_code: 'sz', + country_name: 'Swaziland', + }, + { + country_code: 'se', + country_name: 'Sweden', + }, + { + country_code: 'ch', + country_name: 'Switzerland', + }, + { + country_code: 'sy', + country_name: 'Syrian Arab Republic', + }, + { + country_code: 'tw', + country_name: 'Taiwan, Province of China', + }, + { + country_code: 'tj', + country_name: 'Tajikistan', + }, + { + country_code: 'tz', + country_name: 'Tanzania, United Republic of', + }, + { + country_code: 'th', + country_name: 'Thailand', + }, + { + country_code: 'tl', + country_name: 'Timor-Leste', + }, + { + country_code: 'tg', + country_name: 'Togo', + }, + { + country_code: 'tk', + country_name: 'Tokelau', + }, + { + country_code: 'to', + country_name: 'Tonga', + }, + { + country_code: 'tt', + country_name: 'Trinidad and Tobago', + }, + { + country_code: 'tn', + country_name: 'Tunisia', + }, + { + country_code: 'tr', + country_name: 'Turkiye', + }, + { + country_code: 'tm', + country_name: 'Turkmenistan', + }, + { + country_code: 'tc', + country_name: 'Turks and Caicos Islands', + }, + { + country_code: 'tv', + country_name: 'Tuvalu', + }, + { + country_code: 'ug', + country_name: 'Uganda', + }, + { + country_code: 'ua', + country_name: 'Ukraine', + }, + { + country_code: 'ae', + country_name: 'United Arab Emirates', + }, + { + country_code: 'uk', + country_name: 'United Kingdom', + }, + { + country_code: 'gb', + country_name: 'United Kingdom', + }, + { + country_code: 'us', + country_name: 'United States', + }, + { + country_code: 'um', + country_name: 'United States Minor Outlying Islands', + }, + { + country_code: 'uy', + country_name: 'Uruguay', + }, + { + country_code: 'uz', + country_name: 'Uzbekistan', + }, + { + country_code: 'vu', + country_name: 'Vanuatu', + }, + { + country_code: 've', + country_name: 'Venezuela', + }, + { + country_code: 'vn', + country_name: 'Viet Nam', + }, + { + country_code: 'vg', + country_name: 'Virgin Islands, British', + }, + { + country_code: 'vi', + country_name: 'Virgin Islands, U.S.', + }, + { + country_code: 'wf', + country_name: 'Wallis and Futuna', + }, + { + country_code: 'eh', + country_name: 'Western Sahara', + }, + { + country_code: 'ye', + country_name: 'Yemen', + }, + { + country_code: 'zm', + country_name: 'Zambia', + }, + { + country_code: 'zw', + country_name: 'Zimbabwe', + }, +].map((x) => ({ label: x.country_name, value: x.country_code })); + +export const BingCountryOptions = [ + { label: 'Argentina AR', value: 'AR' }, + { label: 'Australia AU', value: 'AU' }, + { label: 'Austria AT', value: 'AT' }, + { label: 'Belgium BE', value: 'BE' }, + { label: 'Brazil BR', value: 'BR' }, + { label: 'Canada CA', value: 'CA' }, + { label: 'Chile CL', value: 'CL' }, + { label: 'Denmark DK', value: 'DK' }, + { label: 'Finland FI', value: 'FI' }, + { label: 'France FR', value: 'FR' }, + { label: 'Germany DE', value: 'DE' }, + { label: 'Hong Kong SAR HK', value: 'HK' }, + { label: 'India IN', value: 'IN' }, + { label: 'Indonesia ID', value: 'ID' }, + { label: 'Italy IT', value: 'IT' }, + { label: 'Japan JP', value: 'JP' }, + { label: 'Korea KR', value: 'KR' }, + { label: 'Malaysia MY', value: 'MY' }, + { label: 'Mexico MX', value: 'MX' }, + { label: 'Netherlands NL', value: 'NL' }, + { label: 'New Zealand NZ', value: 'NZ' }, + { label: 'Norway NO', value: 'NO' }, + { label: "People's Republic of China CN", value: 'CN' }, + { label: 'Poland PL', value: 'PL' }, + { label: 'Portugal PT', value: 'PT' }, + { label: 'Republic of the Philippines PH', value: 'PH' }, + { label: 'Russia RU', value: 'RU' }, + { label: 'Saudi Arabia SA', value: 'SA' }, + { label: 'South Africa ZA', value: 'ZA' }, + { label: 'Spain ES', value: 'ES' }, + { label: 'Sweden SE', value: 'SE' }, + { label: 'Switzerland CH', value: 'CH' }, + { label: 'Taiwan TW', value: 'TW' }, + { label: 'Türkiye TR', value: 'TR' }, + { label: 'United Kingdom GB', value: 'GB' }, + { label: 'United States US', value: 'US' }, +]; + +export const BingLanguageOptions = [ + { label: 'Arabic ar', value: 'ar' }, + { label: 'Basque eu', value: 'eu' }, + { label: 'Bengali bn', value: 'bn' }, + { label: 'Bulgarian bg', value: 'bg' }, + { label: 'Catalan ca', value: 'ca' }, + { label: 'Chinese (Simplified) zh-hans', value: 'ns' }, + { label: 'Chinese (Traditional) zh-hant', value: 'nt' }, + { label: 'Croatian hr', value: 'hr' }, + { label: 'Czech cs', value: 'cs' }, + { label: 'Danish da', value: 'da' }, + { label: 'Dutch nl', value: 'nl' }, + { label: 'English en', value: 'en' }, + { label: 'English-United Kingdom en-gb', value: 'gb' }, + { label: 'Estonian et', value: 'et' }, + { label: 'Finnish fi', value: 'fi' }, + { label: 'French fr', value: 'fr' }, + { label: 'Galician gl', value: 'gl' }, + { label: 'German de', value: 'de' }, + { label: 'Gujarati gu', value: 'gu' }, + { label: 'Hebrew he', value: 'he' }, + { label: 'Hindi hi', value: 'hi' }, + { label: 'Hungarian hu', value: 'hu' }, + { label: 'Icelandic is', value: 'is' }, + { label: 'Italian it', value: 'it' }, + { label: 'Japanese jp', value: 'jp' }, + { label: 'Kannada kn', value: 'kn' }, + { label: 'Korean ko', value: 'ko' }, + { label: 'Latvian lv', value: 'lv' }, + { label: 'Lithuanian lt', value: 'lt' }, + { label: 'Malay ms', value: 'ms' }, + { label: 'Malayalam ml', value: 'ml' }, + { label: 'Marathi mr', value: 'mr' }, + { label: 'Norwegian (Bokmål) nb', value: 'nb' }, + { label: 'Polish pl', value: 'pl' }, + { label: 'Portuguese (Brazil) pt-br', value: 'br' }, + { label: 'Portuguese (Portugal) pt-pt', value: 'pt' }, + { label: 'Punjabi pa', value: 'pa' }, + { label: 'Romanian ro', value: 'ro' }, + { label: 'Russian ru', value: 'ru' }, + { label: 'Serbian (Cyrylic) sr', value: 'sr' }, + { label: 'Slovak sk', value: 'sk' }, + { label: 'Slovenian sl', value: 'sl' }, + { label: 'Spanish es', value: 'es' }, + { label: 'Swedish sv', value: 'sv' }, + { label: 'Tamil ta', value: 'ta' }, + { label: 'Telugu te', value: 'te' }, + { label: 'Thai th', value: 'th' }, + { label: 'Turkish tr', value: 'tr' }, + { label: 'Ukrainian uk', value: 'uk' }, + { label: 'Vietnamese vi', value: 'vi' }, +]; + +export const DeepLSourceLangOptions = [ + { label: 'Arabic [1]', value: 'AR' }, + { label: 'Bulgarian', value: 'BG' }, + { label: 'Czech', value: 'CS' }, + { label: 'Danish', value: 'DA' }, + { label: 'German', value: 'DE' }, + { label: 'Greek', value: 'EL' }, + { label: 'English', value: 'EN' }, + { label: 'Spanish', value: 'ES' }, + { label: 'Estonian', value: 'ET' }, + { label: 'Finnish', value: 'FI' }, + { label: 'French', value: 'FR' }, + { label: 'Hungarian', value: 'HU' }, + { label: 'Indonesian', value: 'ID' }, + { label: 'Italian', value: 'IT' }, + { label: 'Japanese', value: 'JA' }, + { label: 'Korean', value: 'KO' }, + { label: 'Lithuanian', value: 'LT' }, + { label: 'Latvian', value: 'LV' }, + { label: 'Norwegian Bokmål', value: 'NB' }, + { label: 'Dutch', value: 'NL' }, + { label: 'Polish', value: 'PL' }, + { label: 'Portuguese (all Portuguese varieties mixed)', value: 'PT' }, + { label: 'Romanian', value: 'RO' }, + { label: 'Russian', value: 'RU' }, + { label: 'Slovak', value: 'SK' }, + { label: 'Slovenian', value: 'SL' }, + { label: 'Swedish', value: 'SV' }, + { label: 'Turkish', value: 'TR' }, + { label: 'Ukrainian', value: 'UK' }, + { label: 'Chinese', value: 'ZH' }, +]; +export const DeepLTargetLangOptions = [ + { label: 'Arabic [1]', value: 'AR' }, + { label: 'Bulgarian', value: 'BG' }, + { label: 'Czech', value: 'CS' }, + { label: 'Danish', value: 'DA' }, + { label: 'German', value: 'DE' }, + { label: 'Greek', value: 'EL' }, + { label: 'English (British)', value: 'EN-GB' }, + { label: 'English (American)', value: 'EN-US' }, + { label: 'Spanish', value: 'ES' }, + { label: 'Estonian', value: 'ET' }, + { label: 'Finnish', value: 'FI' }, + { label: 'French', value: 'FR' }, + { label: 'Hungarian', value: 'HU' }, + { label: 'Indonesian', value: 'ID' }, + { label: 'Italian', value: 'IT' }, + { label: 'Japanese', value: 'JA' }, + { label: 'Korean', value: 'KO' }, + { label: 'Lithuanian', value: 'LT' }, + { label: 'Latvian', value: 'LV' }, + { label: 'Norwegian Bokmål', value: 'NB' }, + { label: 'Dutch', value: 'NL' }, + { label: 'Polish', value: 'PL' }, + { label: 'Portuguese (Brazilian)', value: 'PT-BR' }, + { + label: + 'Portuguese (all Portuguese varieties excluding Brazilian Portuguese)', + value: 'PT-PT', + }, + { label: 'Romanian', value: 'RO' }, + { label: 'Russian', value: 'RU' }, + { label: 'Slovak', value: 'SK' }, + { label: 'Slovenian', value: 'SL' }, + { label: 'Swedish', value: 'SV' }, + { label: 'Turkish', value: 'TR' }, + { label: 'Ukrainian', value: 'UK' }, + { label: 'Chinese (simplified)', value: 'ZH' }, +]; + +export const BaiduFanyiDomainOptions = [ + 'it', + 'finance', + 'machinery', + 'senimed', + 'novel', + 'academic', + 'aerospace', + 'wiki', + 'news', + 'law', + 'contract', +]; + +export const BaiduFanyiSourceLangOptions = [ + 'auto', + 'zh', + 'en', + 'yue', + 'wyw', + 'jp', + 'kor', + 'fra', + 'spa', + 'th', + 'ara', + 'ru', + 'pt', + 'de', + 'it', + 'el', + 'nl', + 'pl', + 'bul', + 'est', + 'dan', + 'fin', + 'cs', + 'rom', + 'slo', + 'swe', + 'hu', + 'cht', + 'vie', +]; + +export const QWeatherLangOptions = [ + 'zh', + 'zh-hant', + 'en', + 'de', + 'es', + 'fr', + 'it', + 'ja', + 'ko', + 'ru', + 'hi', + 'th', + 'ar', + 'pt', + 'bn', + 'ms', + 'nl', + 'el', + 'la', + 'sv', + 'id', + 'pl', + 'tr', + 'cs', + 'et', + 'vi', + 'fil', + 'fi', + 'he', + 'is', + 'nb', +]; + +export const QWeatherTypeOptions = ['weather', 'indices', 'airquality']; + +export const QWeatherUserTypeOptions = ['free', 'paid']; + +export const QWeatherTimePeriodOptions = [ + 'now', + '3d', + '7d', + '10d', + '15d', + '30d', +]; + +export const ExeSQLOptions = ['mysql', 'postgresql', 'mariadb', 'mssql'].map( + (x) => ({ + label: upperFirst(x), + value: x, + }), +); + +export const WenCaiQueryTypeOptions = [ + 'stock', + 'zhishu', + 'fund', + 'hkstock', + 'usstock', + 'threeboard', + 'conbond', + 'insurance', + 'futures', + 'lccp', + 'foreign_exchange', +]; + +export const Jin10TypeOptions = ['flash', 'calendar', 'symbols', 'news']; +export const Jin10FlashTypeOptions = new Array(5) + .fill(1) + .map((x, idx) => (idx + 1).toString()); +export const Jin10CalendarTypeOptions = ['cj', 'qh', 'hk', 'us']; +export const Jin10CalendarDatashapeOptions = ['data', 'event', 'holiday']; +export const Jin10SymbolsTypeOptions = ['GOODS', 'FOREX', 'FUTURE', 'CRYPTO']; +export const Jin10SymbolsDatatypeOptions = ['symbols', 'quotes']; +export const TuShareSrcOptions = [ + 'sina', + 'wallstreetcn', + '10jqka', + 'eastmoney', + 'yuncaijing', + 'fenghuang', + 'jinrongjie', +]; +export const CrawlerResultOptions = ['markdown', 'html', 'content']; diff --git a/web/src/pages/data-flow/run-sheet/index.tsx b/web/src/pages/data-flow/run-sheet/index.tsx new file mode 100644 index 00000000000..cac62d008ca --- /dev/null +++ b/web/src/pages/data-flow/run-sheet/index.tsx @@ -0,0 +1,69 @@ +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, +} from '@/components/ui/sheet'; +import { IModalProps } from '@/interfaces/common'; +import { cn } from '@/lib/utils'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { BeginId } from '../constant'; +import DebugContent from '../debug-content'; +import { useGetBeginNodeDataInputs } from '../hooks/use-get-begin-query'; +import { useSaveGraphBeforeOpeningDebugDrawer } from '../hooks/use-save-graph'; +import { BeginQuery } from '../interface'; +import useGraphStore from '../store'; +import { buildBeginQueryWithObject } from '../utils'; + +const RunSheet = ({ + hideModal, + showModal: showChatModal, +}: IModalProps) => { + const { t } = useTranslation(); + const { updateNodeForm, getNode } = useGraphStore((state) => state); + + const inputs = useGetBeginNodeDataInputs(); + + const { handleRun, loading } = useSaveGraphBeforeOpeningDebugDrawer( + showChatModal!, + ); + + const handleRunAgent = useCallback( + (nextValues: BeginQuery[]) => { + const beginNode = getNode(BeginId); + const inputs: Record = beginNode?.data.form.inputs; + + const nextInputs = buildBeginQueryWithObject(inputs, nextValues); + + const currentNodes = updateNodeForm(BeginId, nextInputs, ['inputs']); + handleRun(currentNodes); + hideModal?.(); + }, + [getNode, handleRun, hideModal, updateNodeForm], + ); + + const onOk = useCallback( + async (nextValues: any[]) => { + handleRunAgent(nextValues); + }, + [handleRunAgent], + ); + + return ( + + + + {t('flow.testRun')} + + + + + ); +}; + +export default RunSheet; diff --git a/web/src/pages/data-flow/setting-dialog/index.tsx b/web/src/pages/data-flow/setting-dialog/index.tsx new file mode 100644 index 00000000000..6d0e1e97680 --- /dev/null +++ b/web/src/pages/data-flow/setting-dialog/index.tsx @@ -0,0 +1,53 @@ +import { ButtonLoading } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { useSetAgentSetting } from '@/hooks/use-agent-request'; +import { IModalProps } from '@/interfaces/common'; +import { transformFile2Base64 } from '@/utils/file-util'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + AgentSettingId, + SettingForm, + SettingFormSchemaType, +} from './setting-form'; + +export function SettingDialog({ hideModal }: IModalProps) { + const { t } = useTranslation(); + const { setAgentSetting } = useSetAgentSetting(); + + const submit = useCallback( + async (values: SettingFormSchemaType) => { + const avatar = values.avatar; + const code = await setAgentSetting({ + ...values, + avatar: avatar.length > 0 ? await transformFile2Base64(avatar[0]) : '', + }); + if (code === 0) { + hideModal?.(); + } + }, + [hideModal, setAgentSetting], + ); + + return ( + + + + Are you absolutely sure? + + + + + {t('common.save')} + + + + + ); +} diff --git a/web/src/pages/data-flow/setting-dialog/setting-form.tsx b/web/src/pages/data-flow/setting-dialog/setting-form.tsx new file mode 100644 index 00000000000..e39ad24164d --- /dev/null +++ b/web/src/pages/data-flow/setting-dialog/setting-form.tsx @@ -0,0 +1,158 @@ +import { z } from 'zod'; + +import { + FileUpload, + FileUploadDropzone, + FileUploadItem, + FileUploadItemDelete, + FileUploadItemMetadata, + FileUploadItemPreview, + FileUploadList, + FileUploadTrigger, +} from '@/components/file-upload'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Button } from '@/components/ui/button'; +import { Form, FormControl, FormItem, FormLabel } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +import { Textarea } from '@/components/ui/textarea'; +import { useTranslate } from '@/hooks/common-hooks'; +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { transformBase64ToFile } from '@/utils/file-util'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { CloudUpload, X } from 'lucide-react'; +import { useEffect } from 'react'; +import { useForm } from 'react-hook-form'; + +const formSchema = z.object({ + title: z.string().min(1, {}), + avatar: z.array(z.custom()).optional().nullable(), + description: z.string().optional().nullable(), + permission: z.string(), +}); + +export type SettingFormSchemaType = z.infer; + +export const AgentSettingId = 'agentSettingId'; + +type SettingFormProps = { + submit: (values: SettingFormSchemaType) => void; +}; + +export function SettingForm({ submit }: SettingFormProps) { + const { t } = useTranslate('flow.settings'); + const { data } = useFetchAgent(); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + title: '', + permission: 'me', + }, + }); + + useEffect(() => { + form.reset({ + title: data?.title, + description: data?.description, + avatar: data.avatar ? [transformBase64ToFile(data.avatar)] : [], + permission: data?.permission, + }); + }, [data, form]); + + return ( +
    + + + + + + {(field) => ( + { + form.setError('avatar', { + message, + }); + }} + multiple + > + + + Drag and drop or + + + + to upload + + + {field.value?.map((file: File, index: number) => ( + + + + + + + + ))} + + + )} + + + @@ -82,7 +84,9 @@ export default function ChatBasicSetting() { name={'prompt_config.prologue'} render={({ field }) => ( - {t('setAnOpener')} + + {t('setAnOpener')} + @@ -93,14 +97,17 @@ export default function ChatBasicSetting() { diff --git a/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx b/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx index 9a7ed285c00..83607333575 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-prompt-engine.tsx @@ -37,11 +37,12 @@ export function ChatPromptEngine() { )} /> - + -

    Apply model configs

    +

    {t('chat.applyModelConfigs')}

    {!isLatestChat || chatBoxIds.length === 3 ? ( diff --git a/web/src/pages/next-chats/chat/index.tsx b/web/src/pages/next-chats/chat/index.tsx index 34be965ae9d..f384a76269a 100644 --- a/web/src/pages/next-chats/chat/index.tsx +++ b/web/src/pages/next-chats/chat/index.tsx @@ -63,10 +63,10 @@ export default function Chat() {
    - Multiple Models ({chatBoxIds.length}/3) + {t('chat.multipleModels')} ({chatBoxIds.length}/3)
    - Multiple Models + {t('chat.multipleModels')} diff --git a/web/src/pages/next-chats/chat/sessions.tsx b/web/src/pages/next-chats/chat/sessions.tsx index 7fde0ad2ac9..98b707374ed 100644 --- a/web/src/pages/next-chats/chat/sessions.tsx +++ b/web/src/pages/next-chats/chat/sessions.tsx @@ -71,7 +71,7 @@ export function Sessions({
    - Conversations + {t('chat.conversations')} {conversationList.length} diff --git a/web/src/pages/next-chats/index.tsx b/web/src/pages/next-chats/index.tsx index 446a2867787..5def23e213a 100644 --- a/web/src/pages/next-chats/index.tsx +++ b/web/src/pages/next-chats/index.tsx @@ -38,7 +38,7 @@ export default function ChatList() {
    - Search + {t('header.search')} diff --git a/web/src/pages/next-search/search-setting.tsx b/web/src/pages/next-search/search-setting.tsx index 183130683c2..8a9e136cb66 100644 --- a/web/src/pages/next-search/search-setting.tsx +++ b/web/src/pages/next-search/search-setting.tsx @@ -397,7 +397,11 @@ const SearchSetting: React.FC = ({ name="search_config.similarity_threshold" render={({ field }) => ( - Similarity Threshold + + {t('knowledgeDetails.similarityThreshold')} +
    = ({ name="search_config.vector_similarity_weight" render={({ field }) => ( - - *Vector - Similarity Weight + + * + {t('knowledgeDetails.vectorSimilarityWeight')}

    - {t('relatedSearch')} + {t('search.relatedSearch')}

    {relatedQuestions?.map((x, idx) => ( diff --git a/web/src/pages/next-searches/index.tsx b/web/src/pages/next-searches/index.tsx index 935fcee7ca9..713ed7e64d7 100644 --- a/web/src/pages/next-searches/index.tsx +++ b/web/src/pages/next-searches/index.tsx @@ -49,7 +49,7 @@ export default function SearchList() {
    handleSearchChange(e.target.value)} > diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 27de063aeaf..c04ca939d1f 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -17,6 +17,15 @@ module.exports = { '2xl': '1400px', }, }, + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1400px', + '3xl': '1780px', + '4xl': '1980px', + }, extend: { colors: { border: 'var(--colors-outline-neutral-strong)', From 9b724b3b5efd684db7fb40f0e8205df8004c7daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E9=9C=B2=E5=85=88=E7=94=9F?= Date: Fri, 5 Sep 2025 09:57:39 +0800 Subject: [PATCH 0621/1187] Fix python_version in show_env.sh when its meets python3. (#9894) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) Signed-off-by: zhanluxianshen --- show_env.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/show_env.sh b/show_env.sh index 83c47635cbf..84e37b3d7aa 100644 --- a/show_env.sh +++ b/show_env.sh @@ -41,12 +41,7 @@ else fi # get python version -python_version='' -if command -v python &> /dev/null; then - python_version=$(python --version | cut -d ' ' -f2) -else - python_version="Python not installed" -fi +python_version=$(python3 --version 2>&1 || python --version 2>&1 || echo "Python not installed") # Print all information echo "Current Repository: $git_repo_name" From 1ee9c0b8d92af14929c62709f12d6ab5a6d5171c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E9=9C=B2=E5=85=88=E7=94=9F?= Date: Fri, 5 Sep 2025 09:58:03 +0800 Subject: [PATCH 0622/1187] fix xss in excel_parser (#9909) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] Refactoring - [x] Performance Improvement Signed-off-by: zhanluxianshen --- deepdoc/parser/excel_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepdoc/parser/excel_parser.py b/deepdoc/parser/excel_parser.py index 91bcad7a7c3..29bf4e2e69e 100644 --- a/deepdoc/parser/excel_parser.py +++ b/deepdoc/parser/excel_parser.py @@ -124,7 +124,7 @@ def _fmt(v): if c.value is None: tb += "" else: - tb += f"{c.value}" + tb += f"{escape(_fmt(c.value))}" tb += "" tb += "\n" tb_chunks.append(tb) From ddaed541ff0fc9e0aeca01ca82e4bac2b5e2869a Mon Sep 17 00:00:00 2001 From: Syed Shahmeer Ali Date: Fri, 5 Sep 2025 06:58:30 +0500 Subject: [PATCH 0623/1187] Fix S3 client initialization with signature_version and addressing_style (#9911) ### What problem does this PR solve? Moved `signature_version` and `addressing_style` parameters to a `Config` object from `botocore.config` `signature_version` is now passed as `Config(signature_version='v4')` `addressing_style` is now passed as `Config(s3={'addressing_style': 'path'})` The `Config` object is then passed to `boto3.client()` via the `config` parameter ## Changes Made - Modified `rag/utils/s3_conn.py` in the `__open__()` method - Updated parameter handling logic to use `config_kwargs` dictionary - Maintained backward compatibility for configurations without these parameters ## Related Issue Fixes #9910 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) Co-authored-by: Syed Shahmeer Ali --- rag/utils/s3_conn.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rag/utils/s3_conn.py b/rag/utils/s3_conn.py index 74049c7c1d3..2a1e08185e1 100644 --- a/rag/utils/s3_conn.py +++ b/rag/utils/s3_conn.py @@ -80,10 +80,13 @@ def __open__(self): s3_params['region_name'] = self.region_name if self.endpoint_url: s3_params['endpoint_url'] = self.endpoint_url + + # Configure signature_version and addressing_style through Config object if self.signature_version: - s3_params['signature_version'] = self.signature_version + config_kwargs['signature_version'] = self.signature_version if self.addressing_style: - s3_params['addressing_style'] = self.addressing_style + config_kwargs['s3'] = {'addressing_style': self.addressing_style} + if config_kwargs: s3_params['config'] = Config(**config_kwargs) From b14052e5a28c4b5f56efc2dff4b824b4da587afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E9=9C=B2=E5=85=88=E7=94=9F?= Date: Fri, 5 Sep 2025 09:59:27 +0800 Subject: [PATCH 0624/1187] code cleans. (#9916) ### What problem does this PR solve? ### Type of change - [x] Refactoring - [x] Performance Improvement Signed-off-by: zhanluxianshen --- example/sdk/dataset_example.py | 4 +--- graphrag/general/entity_embedding.py | 6 +++--- graphrag/search.py | 4 ++-- graphrag/utils.py | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/example/sdk/dataset_example.py b/example/sdk/dataset_example.py index 252ed02c038..3a0504d8d8f 100644 --- a/example/sdk/dataset_example.py +++ b/example/sdk/dataset_example.py @@ -36,10 +36,8 @@ updated_dataset = dataset_instance.update(updated_message) # get the dataset (list datasets) - dataset_list = ragflow_instance.list_datasets(id=dataset_instance.id) - dataset_instance_2 = dataset_list[0] print(dataset_instance) - print(dataset_instance_2) + print(updated_dataset) # delete the dataset (delete datasets) to_be_deleted_datasets = [dataset_instance.id] diff --git a/graphrag/general/entity_embedding.py b/graphrag/general/entity_embedding.py index 746e2442f3a..78c7fc31d40 100644 --- a/graphrag/general/entity_embedding.py +++ b/graphrag/general/entity_embedding.py @@ -21,7 +21,7 @@ class NodeEmbeddings: embeddings: np.ndarray -def embed_nod2vec( +def embed_node2vec( graph: nx.Graph | nx.DiGraph, dimensions: int = 1536, num_walks: int = 10, @@ -44,13 +44,13 @@ def embed_nod2vec( return NodeEmbeddings(embeddings=lcc_tensors[0], nodes=lcc_tensors[1]) -def run(graph: nx.Graph, args: dict[str, Any]) -> NodeEmbeddings: +def run(graph: nx.Graph, args: dict[str, Any]) -> dict: """Run method definition.""" if args.get("use_lcc", True): graph = stable_largest_connected_component(graph) # create graph embedding using node2vec - embeddings = embed_nod2vec( + embeddings = embed_node2vec( graph=graph, dimensions=args.get("dimensions", 1536), num_walks=args.get("num_walks", 10), diff --git a/graphrag/search.py b/graphrag/search.py index d39f482d02d..ebc8f4a88e5 100644 --- a/graphrag/search.py +++ b/graphrag/search.py @@ -23,7 +23,7 @@ from api.utils import get_uuid from graphrag.query_analyze_prompt import PROMPTS -from graphrag.utils import get_entity_type2sampels, get_llm_cache, set_llm_cache, get_relation +from graphrag.utils import get_entity_type2samples, get_llm_cache, set_llm_cache, get_relation from rag.utils import num_tokens_from_string, get_float from rag.utils.doc_store_conn import OrderByExpr @@ -42,7 +42,7 @@ def _chat(self, llm_bdl, system, history, gen_conf): return response def query_rewrite(self, llm, question, idxnms, kb_ids): - ty2ents = trio.run(lambda: get_entity_type2sampels(idxnms, kb_ids)) + ty2ents = trio.run(lambda: get_entity_type2samples(idxnms, kb_ids)) hint_prompt = PROMPTS["minirag_query2kwd"].format(query=question, TYPE_POOL=json.dumps(ty2ents, ensure_ascii=False, indent=2)) result = self._chat(llm, hint_prompt, [{"role": "user", "content": "Output:"}], {}) diff --git a/graphrag/utils.py b/graphrag/utils.py index 6b80d7fe8b1..6abe5f9a968 100644 --- a/graphrag/utils.py +++ b/graphrag/utils.py @@ -561,7 +561,7 @@ def merge_tuples(list1, list2): return result -async def get_entity_type2sampels(idxnms, kb_ids: list): +async def get_entity_type2samples(idxnms, kb_ids: list): es_res = await trio.to_thread.run_sync(lambda: settings.retrievaler.search({"knowledge_graph_kwd": "ty2ents", "kb_id": kb_ids, "size": 10000, "fields": ["content_with_weight"]}, idxnms, kb_ids)) res = defaultdict(list) From 8e30a75e5cdbd0790ae3f13d29e83b37d66b6ec6 Mon Sep 17 00:00:00 2001 From: Kevin Wang <41811631+kevinhonor@users.noreply.github.com> Date: Fri, 5 Sep 2025 10:20:36 +0800 Subject: [PATCH 0625/1187] Update .env (#9923) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- docker/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/.env b/docker/.env index 7988b71f199..d5acaad666e 100644 --- a/docker/.env +++ b/docker/.env @@ -115,7 +115,7 @@ RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4-slim # RAGFLOW_IMAGE=registry.cn-hangzhou.aliyuncs.com/infiniflow/ragflow:nightly # The local time zone. -TIMEZONE='Asia/Shanghai' +TIMEZONE=Asia/Shanghai # Uncomment the following line if you have limited access to huggingface.co: # HF_ENDPOINT=https://hf-mirror.com From 677c99b090f76e4a1cc94447841b0adab3b03afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E6=B5=B7=E8=92=BC=E7=81=86?= Date: Fri, 5 Sep 2025 11:12:15 +0800 Subject: [PATCH 0626/1187] Feat: Add metadata filtering function for /api/v1/retrieval (#9877) -Added the metadata_dedition parameter in the document retrieval interface to filter document metadata -Updated the API documentation and added explanations for the metadata_dedition parameter ### What problem does this PR solve? Make /api/v1/retrieval api also can use metadata filter ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/sdk/doc.py | 9 +++++++++ docs/references/http_api_reference.md | 6 ++++-- docs/references/python_api_reference.md | 13 +++++-------- sdk/python/ragflow_sdk/ragflow.py | 6 ++++-- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/api/apps/sdk/doc.py b/api/apps/sdk/doc.py index 454633f078b..50e6f7bba6b 100644 --- a/api/apps/sdk/doc.py +++ b/api/apps/sdk/doc.py @@ -35,6 +35,8 @@ from api.db.services.llm_service import LLMBundle from api.db.services.tenant_llm_service import TenantLLMService from api.db.services.task_service import TaskService, queue_tasks +from api.db.services.dialog_service import meta_filter +from api.apps.sdk.dify_retrieval import convert_conditions from api.utils.api_utils import check_duplicate_ids, construct_json_result, get_error_data_result, get_parser_config, get_result, server_error_response, token_required from rag.app.qa import beAdoc, rmPrefix from rag.app.tag import label_question @@ -1350,6 +1352,9 @@ def retrieval_test(tenant_id): highlight: type: boolean description: Whether to highlight matched content. + metadata_condition: + type: object + description: metadata filter condition. - in: header name: Authorization type: string @@ -1413,6 +1418,10 @@ def retrieval_test(tenant_id): for doc_id in doc_ids: if doc_id not in doc_ids_list: return get_error_data_result(f"The datasets don't own the document {doc_id}") + if not doc_ids: + metadata_condition = req.get("metadata_condition", {}) + metas = DocumentService.get_meta_by_kbs(kb_ids) + doc_ids = meta_filter(metas, convert_conditions(metadata_condition)) similarity_threshold = float(req.get("similarity_threshold", 0.2)) vector_similarity_weight = float(req.get("vector_similarity_weight", 0.3)) top = int(req.get("top_k", 1024)) diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index 6dbeaf4441e..791701ebf2f 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -1808,7 +1808,8 @@ Retrieves chunks from specified datasets. - `"rerank_id"`: `string` - `"keyword"`: `boolean` - `"highlight"`: `boolean` - - `"cross_languages"`: `list[string]` + - `"cross_languages"`: `list[string]` + - `"metadata_condition"`: `object` ##### Request example @@ -1855,7 +1856,8 @@ curl --request POST \ - `false`: Disable highlighting of matched terms (default). - `"cross_languages"`: (*Body parameter*) `list[string]` The languages that should be translated into, in order to achieve keywords retrievals in different languages. - +- `"metadata_condition"`: (*Body parameter*), `object` + The metadata condition for filtering chunks. #### Response Success: diff --git a/docs/references/python_api_reference.md b/docs/references/python_api_reference.md index d13b0516e57..1d788f6abad 100644 --- a/docs/references/python_api_reference.md +++ b/docs/references/python_api_reference.md @@ -921,7 +921,7 @@ chunk.update({"content":"sdfx..."}) ### Retrieve chunks ```python -RAGFlow.retrieve(question:str="", dataset_ids:list[str]=None, document_ids=list[str]=None, page:int=1, page_size:int=30, similarity_threshold:float=0.2, vector_similarity_weight:float=0.3, top_k:int=1024,rerank_id:str=None,keyword:bool=False,highlight:bool=False) -> list[Chunk] +RAGFlow.retrieve(question:str="", dataset_ids:list[str]=None, document_ids=list[str]=None, page:int=1, page_size:int=30, similarity_threshold:float=0.2, vector_similarity_weight:float=0.3, top_k:int=1024,rerank_id:str=None,keyword:bool=False,cross_languages:list[str]=None,metadata_condition: dict=None) -> list[Chunk] ``` Retrieves chunks from specified datasets. @@ -971,17 +971,14 @@ Indicates whether to enable keyword-based matching: - `True`: Enable keyword-based matching. - `False`: Disable keyword-based matching (default). -##### highlight: `bool` - -Specifies whether to enable highlighting of matched terms in the results: - -- `True`: Enable highlighting of matched terms. -- `False`: Disable highlighting of matched terms (default). - ##### cross_languages: `list[string]` The languages that should be translated into, in order to achieve keywords retrievals in different languages. +##### metadata_condition: `dict` + +filter condition for meta_fields + #### Returns - Success: A list of `Chunk` objects representing the document chunks. diff --git a/sdk/python/ragflow_sdk/ragflow.py b/sdk/python/ragflow_sdk/ragflow.py index b38851aad39..f200a6b5c67 100644 --- a/sdk/python/ragflow_sdk/ragflow.py +++ b/sdk/python/ragflow_sdk/ragflow.py @@ -197,7 +197,8 @@ def retrieve( top_k=1024, rerank_id: str | None = None, keyword: bool = False, - cross_languages: list[str]|None = None + cross_languages: list[str]|None = None, + metadata_condition: dict | None = None, ): if document_ids is None: document_ids = [] @@ -212,7 +213,8 @@ def retrieve( "question": question, "dataset_ids": dataset_ids, "document_ids": document_ids, - "cross_languages": cross_languages + "cross_languages": cross_languages, + "metadata_condition": metadata_condition } # Send a POST request to the backend service (using requests library as an example, actual implementation may vary) res = self.post("/retrieval", json=data_json) From 4e16936fa4a39a242b0177b282ff862da22f6d1c Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Fri, 5 Sep 2025 12:29:44 +0800 Subject: [PATCH 0627/1187] Refactor: Use re compile for weight method (#9929) ### What problem does this PR solve? Use re compile for the weight method ### Type of change - [x] Refactoring - [x] Performance Improvement --- rag/nlp/term_weight.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/rag/nlp/term_weight.py b/rag/nlp/term_weight.py index 6ab49a2e3c9..33ee626604c 100644 --- a/rag/nlp/term_weight.py +++ b/rag/nlp/term_weight.py @@ -160,15 +160,15 @@ def split(self, txt): return tks def weights(self, tks, preprocess=True): - def skill(t): - if t not in self.sk: - return 1 - return 6 + num_pattern = re.compile(r"[0-9,.]{2,}$") + short_letter_pattern = re.compile(r"[a-z]{1,2}$") + num_space_pattern = re.compile(r"[0-9. -]{2,}$") + letter_pattern = re.compile(r"[a-z. -]+$") def ner(t): - if re.match(r"[0-9,.]{2,}$", t): + if num_pattern.match(t): return 2 - if re.match(r"[a-z]{1,2}$", t): + if short_letter_pattern.match(t): return 0.01 if not self.ne or t not in self.ne: return 1 @@ -189,10 +189,10 @@ def postag(t): return 1 def freq(t): - if re.match(r"[0-9. -]{2,}$", t): + if num_space_pattern.match(t): return 3 s = rag_tokenizer.freq(t) - if not s and re.match(r"[a-z. -]+$", t): + if not s and letter_pattern.match(t): return 300 if not s: s = 0 @@ -207,11 +207,11 @@ def freq(t): return max(s, 10) def df(t): - if re.match(r"[0-9. -]{2,}$", t): + if num_space_pattern.match(t): return 5 if t in self.df: return self.df[t] + 3 - elif re.match(r"[a-z. -]+$", t): + elif letter_pattern.match(t): return 300 elif len(t) >= 4: s = [tt for tt in rag_tokenizer.fine_grained_tokenize(t).split() if len(tt) > 1] From 6ff7cfe005a3883e4aa8efa49144a67bb921cf7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E9=9C=B2=E5=85=88=E7=94=9F?= Date: Fri, 5 Sep 2025 12:31:44 +0800 Subject: [PATCH 0628/1187] Fix bugs for agent/tools. (#9930) ### What problem does this PR solve? 1 Fix typos 2 Fix agent/tools/crawler.py return bug. 3 Fix agent/tools/deepl.py component_name bug. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] Refactoring - [x] Performance Improvement Signed-off-by: zhanluxianshen --- agent/canvas.py | 2 +- agent/tools/base.py | 2 +- agent/tools/crawler.py | 2 +- agent/tools/deepl.py | 2 +- agent/tools/retrieval.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/agent/canvas.py b/agent/canvas.py index b0e9edbf4e8..6c99b4d8e1c 100644 --- a/agent/canvas.py +++ b/agent/canvas.py @@ -481,7 +481,7 @@ def tool_use_callback(self, agent_id: str, func_name: str, params: dict, result: except Exception as e: logging.exception(e) - def add_refernce(self, chunks: list[object], doc_infos: list[object]): + def add_reference(self, chunks: list[object], doc_infos: list[object]): if not self.retrieval: self.retrieval = [{"chunks": {}, "doc_aggs": {}}] diff --git a/agent/tools/base.py b/agent/tools/base.py index 8e6b78dd022..0d946a696c3 100644 --- a/agent/tools/base.py +++ b/agent/tools/base.py @@ -166,7 +166,7 @@ def _retrieve_chunks(self, res_list: list, get_title, get_url, get_content, get_ "count": 1, "url": url }) - self._canvas.add_refernce(chunks, aggs) + self._canvas.add_reference(chunks, aggs) self.set_output("formalized_content", "\n".join(kb_prompt({"chunks": chunks, "doc_aggs": aggs}, 200000, True))) def thoughts(self) -> str: diff --git a/agent/tools/crawler.py b/agent/tools/crawler.py index b3601b10b69..869fae4a39a 100644 --- a/agent/tools/crawler.py +++ b/agent/tools/crawler.py @@ -64,5 +64,5 @@ async def get_web(self, url): elif self._param.extract_type == 'markdown': return result.markdown elif self._param.extract_type == 'content': - result.extracted_content + return result.extracted_content return result.markdown diff --git a/agent/tools/deepl.py b/agent/tools/deepl.py index 31e92729c37..41d12341d59 100644 --- a/agent/tools/deepl.py +++ b/agent/tools/deepl.py @@ -43,7 +43,7 @@ def check(self): class DeepL(ComponentBase, ABC): - component_name = "GitHub" + component_name = "DeepL" def _run(self, history, **kwargs): ans = self.get_input() diff --git a/agent/tools/retrieval.py b/agent/tools/retrieval.py index 27ddfe4b2f7..a6e1e4fcd15 100644 --- a/agent/tools/retrieval.py +++ b/agent/tools/retrieval.py @@ -163,7 +163,7 @@ def _invoke(self, **kwargs): self.set_output("formalized_content", self._param.empty_response) return - self._canvas.add_refernce(kbinfos["chunks"], kbinfos["doc_aggs"]) + self._canvas.add_reference(kbinfos["chunks"], kbinfos["doc_aggs"]) form_cnt = "\n".join(kb_prompt(kbinfos, 200000, True)) self.set_output("formalized_content", form_cnt) return form_cnt From 79ca25ec7ef093369d17ea60011d600bb826b450 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 5 Sep 2025 15:48:57 +0800 Subject: [PATCH 0629/1187] Feat: Allow users to select prompt word templates in agent operators. #9935 (#9936) ### What problem does this PR solve? Feat: Allow users to select prompt word templates in agent operators. #9935 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/hooks/use-agent-request.ts | 22 ++++++++++ web/src/locales/en.ts | 1 + web/src/locales/zh.ts | 1 + web/src/pages/agent/form/agent-form/index.tsx | 5 +++ .../agent-form/use-build-prompt-options.ts | 30 ++++++++++++++ .../form/components/prompt-editor/index.tsx | 13 ++++-- .../prompt-editor/variable-node.tsx | 2 - .../variable-on-change-plugin.tsx | 6 ++- .../prompt-editor/variable-picker-plugin.tsx | 40 ++++++++++++++----- web/src/services/agent-service.ts | 5 +++ web/src/utils/api.ts | 1 + 11 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 web/src/pages/agent/form/agent-form/use-build-prompt-options.ts diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index a40ef0f16c4..6a3f53eca8e 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -51,6 +51,7 @@ export const enum AgentApiAction { FetchAgentAvatar = 'fetchAgentAvatar', FetchExternalAgentInputs = 'fetchExternalAgentInputs', SetAgentSetting = 'setAgentSetting', + FetchPrompt = 'fetchPrompt', } export const EmptyDsl = { @@ -637,3 +638,24 @@ export const useSetAgentSetting = () => { return { data, loading, setAgentSetting: mutateAsync }; }; + +export const useFetchPrompt = () => { + const { + data, + isFetching: loading, + refetch, + } = useQuery>({ + queryKey: [AgentApiAction.FetchPrompt], + refetchOnReconnect: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + gcTime: 0, + queryFn: async () => { + const { data } = await agentService.fetchPrompt(); + + return data?.data ?? {}; + }, + }); + + return { data, loading, refetch }; +}; diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 2edf2404cbe..b94d1c9c56d 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1518,6 +1518,7 @@ This delimiter is used to split the input text into several text pieces echo of sqlStatement: 'SQL Statement', sqlStatementTip: 'Write your SQL query here. You can use variables, raw SQL, or mix both using variable syntax.', + frameworkPrompts: 'Framework Prompts', }, llmTools: { bad_calculator: { diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 973a9ac9a1c..bfae19a6b68 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1433,6 +1433,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 sqlStatement: 'SQL 语句', sqlStatementTip: '在此处编写您的 SQL 查询。您可以使用变量、原始 SQL,或使用变量语法混合使用两者。', + frameworkPrompts: '框架提示词', }, footer: { profile: 'All rights reserved @ React', diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 9ca0fb69ccf..2e0eb5fd200 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -39,6 +39,7 @@ import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; import { QueryVariable } from '../components/query-variable'; import { AgentTools, Agents } from './agent-tools'; +import { useBuildPromptExtraPromptOptions } from './use-build-prompt-options'; import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; @@ -85,6 +86,9 @@ function AgentForm({ node }: INextOperatorForm) { const defaultValues = useValues(node); + const { extraOptions } = useBuildPromptExtraPromptOptions(); + console.log('🚀 ~ AgentForm ~ prompts:', extraOptions); + const ExceptionMethodOptions = Object.values(AgentExceptionMethod).map( (x) => ({ label: t(`flow.${x}`), @@ -150,6 +154,7 @@ function AgentForm({ node }: INextOperatorForm) { {...field} placeholder={t('flow.messagePlaceholder')} showToolbar={false} + extraOptions={extraOptions} > diff --git a/web/src/pages/agent/form/agent-form/use-build-prompt-options.ts b/web/src/pages/agent/form/agent-form/use-build-prompt-options.ts new file mode 100644 index 00000000000..9c34ce77c06 --- /dev/null +++ b/web/src/pages/agent/form/agent-form/use-build-prompt-options.ts @@ -0,0 +1,30 @@ +import { useFetchPrompt } from '@/hooks/use-agent-request'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const PromptIdentity = 'RAGFlow-Prompt'; + +function wrapPromptWithTag(text: string, tag: string) { + const capitalTag = tag.toUpperCase(); + return `<${capitalTag}> + ${text} +`; +} + +export function useBuildPromptExtraPromptOptions() { + const { data: prompts } = useFetchPrompt(); + const { t } = useTranslation(); + + const options = useMemo(() => { + return Object.entries(prompts || {}).map(([key, value]) => ({ + label: key, + value: wrapPromptWithTag(value, key), + })); + }, [prompts]); + + const extraOptions = [ + { label: PromptIdentity, title: t('flow.frameworkPrompts'), options }, + ]; + + return { extraOptions }; +} diff --git a/web/src/pages/agent/form/components/prompt-editor/index.tsx b/web/src/pages/agent/form/components/prompt-editor/index.tsx index caf6914b45c..ffab9b44e55 100644 --- a/web/src/pages/agent/form/components/prompt-editor/index.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/index.tsx @@ -29,7 +29,9 @@ import { PasteHandlerPlugin } from './paste-handler-plugin'; import theme from './theme'; import { VariableNode } from './variable-node'; import { VariableOnChangePlugin } from './variable-on-change-plugin'; -import VariablePickerMenuPlugin from './variable-picker-plugin'; +import VariablePickerMenuPlugin, { + VariablePickerMenuPluginProps, +} from './variable-picker-plugin'; // Catch any errors that occur during Lexical updates and log them // or throw them as needed. If you don't throw them, Lexical will @@ -52,7 +54,8 @@ type IProps = { value?: string; onChange?: (value?: string) => void; placeholder?: ReactNode; -} & PromptContentProps; +} & PromptContentProps & + Pick; function PromptContent({ showToolbar = true, @@ -122,6 +125,7 @@ export function PromptEditor({ placeholder, showToolbar, multiLine = true, + extraOptions, }: IProps) { const { t } = useTranslation(); const initialConfig: InitialConfigType = { @@ -170,7 +174,10 @@ export function PromptEditor({ } ErrorBoundary={LexicalErrorBoundary} /> - + { __value: string; diff --git a/web/src/pages/agent/form/components/prompt-editor/variable-on-change-plugin.tsx b/web/src/pages/agent/form/components/prompt-editor/variable-on-change-plugin.tsx index 86fa66db4f8..002face8da7 100644 --- a/web/src/pages/agent/form/components/prompt-editor/variable-on-change-plugin.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/variable-on-change-plugin.tsx @@ -3,7 +3,7 @@ import { EditorState, LexicalEditor } from 'lexical'; import { useEffect } from 'react'; import { ProgrammaticTag } from './constant'; -interface IProps { +interface VariableOnChangePluginProps { onChange: ( editorState: EditorState, editor?: LexicalEditor, @@ -11,7 +11,9 @@ interface IProps { ) => void; } -export function VariableOnChangePlugin({ onChange }: IProps) { +export function VariableOnChangePlugin({ + onChange, +}: VariableOnChangePluginProps) { // Access the editor through the LexicalComposerContext const [editor] = useLexicalComposerContext(); // Wrap our listener in useEffect to handle the teardown and avoid stale references. diff --git a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx index f429981c780..7488d991a32 100644 --- a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx @@ -32,6 +32,7 @@ import * as ReactDOM from 'react-dom'; import { $createVariableNode } from './variable-node'; import { useBuildQueryVariableOptions } from '@/pages/agent/hooks/use-get-begin-query'; +import { PromptIdentity } from '../../agent-form/use-build-prompt-options'; import { ProgrammaticTag } from './constant'; import './index.css'; class VariableInnerOption extends MenuOption { @@ -108,11 +109,18 @@ function VariablePickerMenuItem({ ); } +export type VariablePickerMenuPluginProps = { + value?: string; + extraOptions?: Array<{ + label: string; + title: string; + options: Array<{ label: string; value: string; icon?: ReactNode }>; + }>; +}; export default function VariablePickerMenuPlugin({ value, -}: { - value?: string; -}): JSX.Element { + extraOptions, +}: VariablePickerMenuPluginProps): JSX.Element { const [editor] = useLexicalComposerContext(); const isFirstRender = useRef(true); @@ -122,10 +130,10 @@ export default function VariablePickerMenuPlugin({ const [queryString, setQueryString] = React.useState(''); - const options = useBuildQueryVariableOptions(); + let options = useBuildQueryVariableOptions(); const buildNextOptions = useCallback(() => { - let filteredOptions = options; + let filteredOptions = [...options, ...(extraOptions ?? [])]; if (queryString) { const lowerQuery = queryString.toLowerCase(); filteredOptions = options @@ -140,7 +148,7 @@ export default function VariablePickerMenuPlugin({ .filter((x) => x.options.length > 0); } - const nextOptions: VariableOption[] = filteredOptions.map( + const finalOptions: VariableOption[] = filteredOptions.map( (x) => new VariableOption( x.label, @@ -150,8 +158,8 @@ export default function VariablePickerMenuPlugin({ }), ), ); - return nextOptions; - }, [options, queryString]); + return finalOptions; + }, [extraOptions, options, queryString]); const findItemByValue = useCallback( (value: string) => { @@ -173,7 +181,7 @@ export default function VariablePickerMenuPlugin({ const onSelectOption = useCallback( ( - selectedOption: VariableOption | VariableInnerOption, + selectedOption: VariableInnerOption, nodeToRemove: TextNode | null, closeMenu: () => void, ) => { @@ -193,7 +201,11 @@ export default function VariablePickerMenuPlugin({ selectedOption.parentLabel as string | ReactNode, selectedOption.icon as ReactNode, ); - selection.insertNodes([variableNode]); + if (selectedOption.parentLabel === PromptIdentity) { + selection.insertText(selectedOption.value); + } else { + selection.insertNodes([variableNode]); + } closeMenu(); }); @@ -269,7 +281,13 @@ export default function VariablePickerMenuPlugin({ return ( onQueryChange={setQueryString} - onSelectOption={onSelectOption} + onSelectOption={(option, textNodeContainingQuery, closeMenu) => + onSelectOption( + option as VariableInnerOption, // Only the second level menu can be selected + textNodeContainingQuery, + closeMenu, + ) + } triggerFn={checkForTriggerMatch} options={buildNextOptions()} menuRenderFn={(anchorElementRef, { selectOptionAndCleanUp }) => { diff --git a/web/src/services/agent-service.ts b/web/src/services/agent-service.ts index 978100b1a71..6c0b3aceb32 100644 --- a/web/src/services/agent-service.ts +++ b/web/src/services/agent-service.ts @@ -25,6 +25,7 @@ const { fetchAgentAvatar, fetchAgentLogs, fetchExternalAgentInputs, + prompt, } = api; const methods = { @@ -112,6 +113,10 @@ const methods = { url: fetchExternalAgentInputs, method: 'get', }, + fetchPrompt: { + url: prompt, + method: 'get', + }, } as const; const agentService = registerNextServer(methods); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index cbb0588b37c..cf294a67c36 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -164,6 +164,7 @@ export default { `${api_host}/canvas/${canvasId}/sessions`, fetchExternalAgentInputs: (canvasId: string) => `${ExternalApi}${api_host}/agentbots/${canvasId}/inputs`, + prompt: `${api_host}/canvas/prompts`, // mcp server listMcpServer: `${api_host}/mcp_server/list`, From 9aa8cfb73adf1f592d96a6702d7758b669b6e458 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 5 Sep 2025 18:43:33 +0800 Subject: [PATCH 0630/1187] Feat: Use sonner to replace the requested prompt message component #3221 (#9951) ### What problem does this PR solve? Feat: Use sonner to replace the requested prompt message component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/ui/message.ts | 6 ++++++ web/src/hooks/file-manager-hooks.ts | 3 ++- web/src/hooks/login-hooks.ts | 6 ++---- web/src/hooks/use-document-request.ts | 2 +- web/src/hooks/use-file-request.ts | 3 ++- web/src/hooks/user-setting-hooks.tsx | 3 ++- web/src/pages/agent/form-sheet/next.tsx | 2 +- web/src/pages/agent/form/agent-form/index.tsx | 1 - web/src/pages/agent/hooks/use-calculate-sheet-right.ts | 8 ++++++++ web/src/pages/agent/log-sheet/index.tsx | 3 ++- 10 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 web/src/pages/agent/hooks/use-calculate-sheet-right.ts diff --git a/web/src/components/ui/message.ts b/web/src/components/ui/message.ts index 24c834ffbd2..623500cea71 100644 --- a/web/src/components/ui/message.ts +++ b/web/src/components/ui/message.ts @@ -1,28 +1,34 @@ import { toast } from 'sonner'; +const duration = { duration: 1500 }; + const message = { success: (msg: string) => { toast.success(msg, { position: 'top-center', closeButton: false, + ...duration, }); }, error: (msg: string) => { toast.error(msg, { position: 'top-center', closeButton: false, + ...duration, }); }, warning: (msg: string) => { toast.warning(msg, { position: 'top-center', closeButton: false, + ...duration, }); }, info: (msg: string) => { toast.info(msg, { position: 'top-center', closeButton: false, + ...duration, }); }, }; diff --git a/web/src/hooks/file-manager-hooks.ts b/web/src/hooks/file-manager-hooks.ts index 4438609c5bd..32cf21a184f 100644 --- a/web/src/hooks/file-manager-hooks.ts +++ b/web/src/hooks/file-manager-hooks.ts @@ -1,10 +1,11 @@ +import message from '@/components/ui/message'; import { ResponseType } from '@/interfaces/database/base'; import { IFolder } from '@/interfaces/database/file-manager'; import { IConnectRequestBody } from '@/interfaces/request/file-manager'; import fileManagerService from '@/services/file-manager-service'; import { downloadFileFromBlob } from '@/utils/file-util'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { PaginationProps, UploadFile, message } from 'antd'; +import { PaginationProps, UploadFile } from 'antd'; import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useSearchParams } from 'umi'; diff --git a/web/src/hooks/login-hooks.ts b/web/src/hooks/login-hooks.ts index b5fa0ae6493..4bd63cc174f 100644 --- a/web/src/hooks/login-hooks.ts +++ b/web/src/hooks/login-hooks.ts @@ -1,3 +1,4 @@ +import message from '@/components/ui/message'; import { Authorization } from '@/constants/authorization'; import userService, { getLoginChannels, @@ -5,7 +6,7 @@ import userService, { } from '@/services/user-service'; import authorizationUtil, { redirectToLogin } from '@/utils/authorization-util'; import { useMutation, useQuery } from '@tanstack/react-query'; -import { Form, message } from 'antd'; +import { Form } from 'antd'; import { FormInstance } from 'antd/lib'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -50,8 +51,6 @@ export const useLoginWithChannel = () => { }; export const useLogin = () => { - const { t } = useTranslation(); - const { data, isPending: loading, @@ -62,7 +61,6 @@ export const useLogin = () => { const { data: res = {}, response } = await userService.login(params); if (res.code === 0) { const { data } = res; - message.success(t('message.logged')); const authorization = response.headers.get(Authorization); const token = data.access_token; const userInfo = { diff --git a/web/src/hooks/use-document-request.ts b/web/src/hooks/use-document-request.ts index e8f08eca006..bb09c441149 100644 --- a/web/src/hooks/use-document-request.ts +++ b/web/src/hooks/use-document-request.ts @@ -1,4 +1,5 @@ import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit'; +import message from '@/components/ui/message'; import { ResponseType } from '@/interfaces/database/base'; import { IDocumentInfo, @@ -12,7 +13,6 @@ import i18n from '@/locales/config'; import kbService, { listDocument } from '@/services/knowledge-service'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useDebounce } from 'ahooks'; -import { message } from 'antd'; import { get } from 'lodash'; import { useCallback, useMemo, useState } from 'react'; import { useParams } from 'umi'; diff --git a/web/src/hooks/use-file-request.ts b/web/src/hooks/use-file-request.ts index 9146f5e5a6a..68455f3abef 100644 --- a/web/src/hooks/use-file-request.ts +++ b/web/src/hooks/use-file-request.ts @@ -1,3 +1,4 @@ +import message from '@/components/ui/message'; import { IFetchFileListResult, IFolder, @@ -5,7 +6,7 @@ import { import fileManagerService from '@/services/file-manager-service'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useDebounce } from 'ahooks'; -import { PaginationProps, message } from 'antd'; +import { PaginationProps } from 'antd'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useSearchParams } from 'umi'; diff --git a/web/src/hooks/user-setting-hooks.tsx b/web/src/hooks/user-setting-hooks.tsx index 98ac4185054..bc4e177dbda 100644 --- a/web/src/hooks/user-setting-hooks.tsx +++ b/web/src/hooks/user-setting-hooks.tsx @@ -1,3 +1,4 @@ +import message from '@/components/ui/message'; import { LanguageTranslationMap } from '@/constants/common'; import { ResponseGetType } from '@/interfaces/database/base'; import { IToken } from '@/interfaces/database/chat'; @@ -18,7 +19,7 @@ import userService, { listTenantUser, } from '@/services/user-service'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { Modal, message } from 'antd'; +import { Modal } from 'antd'; import DOMPurify from 'dompurify'; import { isEmpty } from 'lodash'; import { useCallback, useMemo, useState } from 'react'; diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index 2d7b5ca8b47..69b6af45542 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -65,7 +65,7 @@ const FormSheet = ({ return ( ({ diff --git a/web/src/pages/agent/hooks/use-calculate-sheet-right.ts b/web/src/pages/agent/hooks/use-calculate-sheet-right.ts new file mode 100644 index 00000000000..3fa5b98d4f5 --- /dev/null +++ b/web/src/pages/agent/hooks/use-calculate-sheet-right.ts @@ -0,0 +1,8 @@ +import { useSize } from 'ahooks'; + +export function useCalculateSheetRight() { + const size = useSize(document.querySelector('body')); + const bodyWidth = size?.width ?? 0; + + return bodyWidth > 1800 ? 'right-[620px]' : `right-1/3`; +} diff --git a/web/src/pages/agent/log-sheet/index.tsx b/web/src/pages/agent/log-sheet/index.tsx index cbe63ece715..138a53e096c 100644 --- a/web/src/pages/agent/log-sheet/index.tsx +++ b/web/src/pages/agent/log-sheet/index.tsx @@ -5,6 +5,7 @@ import { SheetTitle, } from '@/components/ui/sheet'; import { IModalProps } from '@/interfaces/common'; +import { cn } from '@/lib/utils'; import { NotebookText } from 'lucide-react'; import 'react18-json-view/src/style.css'; import { useCacheChatLog } from '../hooks/use-cache-chat-log'; @@ -24,7 +25,7 @@ export function LogSheet({ }: LogSheetProps) { return ( - + From 45f52e85d720b539ac17c1153056a766b2923373 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Fri, 5 Sep 2025 18:50:46 +0800 Subject: [PATCH 0631/1187] Feat: refine dataflow and initialize dataflow app (#9952) ### What problem does this PR solve? Refine dataflow and initialize dataflow app. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/canvas_app.py | 10 +- api/apps/dataflow_app.py | 353 ++++++++++++++++++ api/db/services/task_service.py | 82 ++-- conf/llm_factories.json | 6 +- rag/flow/__init__.py | 31 +- rag/flow/base.py | 20 +- rag/flow/chunker/__init__.py | 15 + rag/flow/{ => chunker}/chunker.py | 109 ++++-- rag/flow/chunker/schema.py | 37 ++ rag/flow/{begin.py => file.py} | 5 +- rag/flow/parser.py | 107 ------ rag/flow/parser/__init__.py | 14 + rag/flow/parser/parser.py | 154 ++++++++ rag/flow/parser/schema.py | 25 ++ rag/flow/pipeline.py | 43 +-- rag/flow/tests/client.py | 18 +- .../tests/dsl_examples/general_pdf_all.json | 31 +- rag/flow/tokenizer/__init__.py | 14 + rag/flow/tokenizer/schema.py | 51 +++ rag/flow/{ => tokenizer}/tokenizer.py | 59 ++- rag/svr/task_executor.py | 31 +- 21 files changed, 959 insertions(+), 256 deletions(-) create mode 100644 api/apps/dataflow_app.py create mode 100644 rag/flow/chunker/__init__.py rename rag/flow/{ => chunker}/chunker.py (63%) create mode 100644 rag/flow/chunker/schema.py rename rag/flow/{begin.py => file.py} (92%) delete mode 100644 rag/flow/parser.py create mode 100644 rag/flow/parser/__init__.py create mode 100644 rag/flow/parser/parser.py create mode 100644 rag/flow/parser/schema.py create mode 100644 rag/flow/tokenizer/__init__.py create mode 100644 rag/flow/tokenizer/schema.py rename rag/flow/{ => tokenizer}/tokenizer.py (71%) diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py index 22d99e3e8ce..a220af096e0 100644 --- a/api/apps/canvas_app.py +++ b/api/apps/canvas_app.py @@ -418,12 +418,10 @@ def setting(): return get_data_error_result(message="canvas not found.") flow = flow.to_dict() flow["title"] = req["title"] - if req["description"]: - flow["description"] = req["description"] - if req["permission"]: - flow["permission"] = req["permission"] - if req["avatar"]: - flow["avatar"] = req["avatar"] + + for key in ["description", "permission", "avatar"]: + if value := req.get(key): + flow[key] = value num= UserCanvasService.update_by_id(req["id"], flow) return get_json_result(data=num) diff --git a/api/apps/dataflow_app.py b/api/apps/dataflow_app.py new file mode 100644 index 00000000000..49bc8687bdd --- /dev/null +++ b/api/apps/dataflow_app.py @@ -0,0 +1,353 @@ +# +# Copyright 2024 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 json +import re +import sys +import time +from functools import partial + +import trio +from flask import request +from flask_login import current_user, login_required + +from agent.canvas import Canvas +from agent.component import LLM +from api.db import CanvasCategory, FileType +from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService +from api.db.services.document_service import DocumentService +from api.db.services.file_service import FileService +from api.db.services.task_service import queue_dataflow +from api.db.services.user_canvas_version import UserCanvasVersionService +from api.db.services.user_service import TenantService +from api.settings import RetCode +from api.utils import get_uuid +from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request +from api.utils.file_utils import filename_type, read_potential_broken_pdf +from rag.flow.pipeline import Pipeline + + +@manager.route("/templates", methods=["GET"]) # noqa: F821 +@login_required +def templates(): + return get_json_result(data=[c.to_dict() for c in CanvasTemplateService.query(canvas_category=CanvasCategory.DataFlow)]) + + +@manager.route("/list", methods=["GET"]) # noqa: F821 +@login_required +def canvas_list(): + return get_json_result(data=sorted([c.to_dict() for c in UserCanvasService.query(user_id=current_user.id, canvas_category=CanvasCategory.DataFlow)], key=lambda x: x["update_time"] * -1)) + + +@manager.route("/rm", methods=["POST"]) # noqa: F821 +@validate_request("canvas_ids") +@login_required +def rm(): + for i in request.json["canvas_ids"]: + if not UserCanvasService.accessible(i, current_user.id): + return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR) + UserCanvasService.delete_by_id(i) + return get_json_result(data=True) + + +@manager.route("/set", methods=["POST"]) # noqa: F821 +@validate_request("dsl", "title") +@login_required +def save(): + req = request.json + if not isinstance(req["dsl"], str): + req["dsl"] = json.dumps(req["dsl"], ensure_ascii=False) + req["dsl"] = json.loads(req["dsl"]) + req["canvas_category"] = CanvasCategory.DataFlow + if "id" not in req: + req["user_id"] = current_user.id + if UserCanvasService.query(user_id=current_user.id, title=req["title"].strip(), canvas_category=CanvasCategory.DataFlow): + return get_data_error_result(message=f"{req['title'].strip()} already exists.") + req["id"] = get_uuid() + + if not UserCanvasService.save(**req): + return get_data_error_result(message="Fail to save canvas.") + else: + if not UserCanvasService.accessible(req["id"], current_user.id): + return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR) + UserCanvasService.update_by_id(req["id"], req) + # save version + UserCanvasVersionService.insert(user_canvas_id=req["id"], dsl=req["dsl"], title="{0}_{1}".format(req["title"], time.strftime("%Y_%m_%d_%H_%M_%S"))) + UserCanvasVersionService.delete_all_versions(req["id"]) + return get_json_result(data=req) + + +@manager.route("/get/", methods=["GET"]) # noqa: F821 +@login_required +def get(canvas_id): + if not UserCanvasService.accessible(canvas_id, current_user.id): + return get_data_error_result(message="canvas not found.") + e, c = UserCanvasService.get_by_tenant_id(canvas_id) + return get_json_result(data=c) + + +@manager.route("/run", methods=["POST"]) # noqa: F821 +@validate_request("id") +@login_required +def run(): + req = request.json + flow_id = req.get("id", "") + doc_id = req.get("doc_id", "") + if not all([flow_id, doc_id]): + return get_data_error_result(message="id and doc_id are required.") + + if not DocumentService.get_by_id(doc_id): + return get_data_error_result(message=f"Document for {doc_id} not found.") + + user_id = req.get("user_id", current_user.id) + if not UserCanvasService.accessible(flow_id, current_user.id): + return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR) + + e, cvs = UserCanvasService.get_by_id(flow_id) + if not e: + return get_data_error_result(message="canvas not found.") + + if not isinstance(cvs.dsl, str): + cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) + + task_id = get_uuid() + + ok, error_message = queue_dataflow(dsl=cvs.dsl, tenant_id=user_id, doc_id=doc_id, task_id=task_id, flow_id=flow_id, priority=0) + if not ok: + return server_error_response(error_message) + + return get_json_result(data={"task_id": task_id, "flow_id": flow_id}) + + +@manager.route("/reset", methods=["POST"]) # noqa: F821 +@validate_request("id") +@login_required +def reset(): + req = request.json + flow_id = req.get("id", "") + if not flow_id: + return get_data_error_result(message="id is required.") + + if not UserCanvasService.accessible(flow_id, current_user.id): + return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR) + + task_id = req.get("task_id", "") + + try: + e, user_canvas = UserCanvasService.get_by_id(req["id"]) + if not e: + return get_data_error_result(message="canvas not found.") + + dataflow = Pipeline(dsl=json.dumps(user_canvas.dsl), tenant_id=current_user.id, flow_id=flow_id, task_id=task_id) + dataflow.reset() + req["dsl"] = json.loads(str(dataflow)) + UserCanvasService.update_by_id(req["id"], {"dsl": req["dsl"]}) + return get_json_result(data=req["dsl"]) + except Exception as e: + return server_error_response(e) + + +@manager.route("/upload/", methods=["POST"]) # noqa: F821 +def upload(canvas_id): + e, cvs = UserCanvasService.get_by_tenant_id(canvas_id) + if not e: + return get_data_error_result(message="canvas not found.") + + user_id = cvs["user_id"] + + def structured(filename, filetype, blob, content_type): + nonlocal user_id + if filetype == FileType.PDF.value: + blob = read_potential_broken_pdf(blob) + + location = get_uuid() + FileService.put_blob(user_id, location, blob) + + return { + "id": location, + "name": filename, + "size": sys.getsizeof(blob), + "extension": filename.split(".")[-1].lower(), + "mime_type": content_type, + "created_by": user_id, + "created_at": time.time(), + "preview_url": None, + } + + if request.args.get("url"): + from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CrawlResult, DefaultMarkdownGenerator, PruningContentFilter + + try: + url = request.args.get("url") + filename = re.sub(r"\?.*", "", url.split("/")[-1]) + + async def adownload(): + browser_config = BrowserConfig( + headless=True, + verbose=False, + ) + async with AsyncWebCrawler(config=browser_config) as crawler: + crawler_config = CrawlerRunConfig(markdown_generator=DefaultMarkdownGenerator(content_filter=PruningContentFilter()), pdf=True, screenshot=False) + result: CrawlResult = await crawler.arun(url=url, config=crawler_config) + return result + + page = trio.run(adownload()) + if page.pdf: + if filename.split(".")[-1].lower() != "pdf": + filename += ".pdf" + return get_json_result(data=structured(filename, "pdf", page.pdf, page.response_headers["content-type"])) + + return get_json_result(data=structured(filename, "html", str(page.markdown).encode("utf-8"), page.response_headers["content-type"], user_id)) + + except Exception as e: + return server_error_response(e) + + file = request.files["file"] + try: + DocumentService.check_doc_health(user_id, file.filename) + return get_json_result(data=structured(file.filename, filename_type(file.filename), file.read(), file.content_type)) + except Exception as e: + return server_error_response(e) + + +@manager.route("/input_form", methods=["GET"]) # noqa: F821 +@login_required +def input_form(): + flow_id = request.args.get("id") + cpn_id = request.args.get("component_id") + try: + e, user_canvas = UserCanvasService.get_by_id(flow_id) + if not e: + return get_data_error_result(message="canvas not found.") + if not UserCanvasService.query(user_id=current_user.id, id=flow_id): + return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR) + + dataflow = Pipeline(dsl=json.dumps(user_canvas.dsl), tenant_id=current_user.id, flow_id=flow_id, task_id="") + + return get_json_result(data=dataflow.get_component_input_form(cpn_id)) + except Exception as e: + return server_error_response(e) + + +@manager.route("/debug", methods=["POST"]) # noqa: F821 +@validate_request("id", "component_id", "params") +@login_required +def debug(): + req = request.json + if not UserCanvasService.accessible(req["id"], current_user.id): + return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR) + try: + e, user_canvas = UserCanvasService.get_by_id(req["id"]) + canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id) + canvas.reset() + canvas.message_id = get_uuid() + component = canvas.get_component(req["component_id"])["obj"] + component.reset() + + if isinstance(component, LLM): + component.set_debug_inputs(req["params"]) + component.invoke(**{k: o["value"] for k, o in req["params"].items()}) + outputs = component.output() + for k in outputs.keys(): + if isinstance(outputs[k], partial): + txt = "" + for c in outputs[k](): + txt += c + outputs[k] = txt + return get_json_result(data=outputs) + except Exception as e: + return server_error_response(e) + + +# api get list version dsl of canvas +@manager.route("/getlistversion/", methods=["GET"]) # noqa: F821 +@login_required +def getlistversion(canvas_id): + try: + list = sorted([c.to_dict() for c in UserCanvasVersionService.list_by_canvas_id(canvas_id)], key=lambda x: x["update_time"] * -1) + return get_json_result(data=list) + except Exception as e: + return get_data_error_result(message=f"Error getting history files: {e}") + + +# api get version dsl of canvas +@manager.route("/getversion/", methods=["GET"]) # noqa: F821 +@login_required +def getversion(version_id): + try: + e, version = UserCanvasVersionService.get_by_id(version_id) + if version: + return get_json_result(data=version.to_dict()) + except Exception as e: + return get_json_result(data=f"Error getting history file: {e}") + + +@manager.route("/listteam", methods=["GET"]) # noqa: F821 +@login_required +def list_canvas(): + keywords = request.args.get("keywords", "") + page_number = int(request.args.get("page", 1)) + items_per_page = int(request.args.get("page_size", 150)) + orderby = request.args.get("orderby", "create_time") + desc = request.args.get("desc", True) + try: + tenants = TenantService.get_joined_tenants_by_user_id(current_user.id) + canvas, total = UserCanvasService.get_by_tenant_ids( + [m["tenant_id"] for m in tenants], current_user.id, page_number, items_per_page, orderby, desc, keywords, canvas_category=CanvasCategory.DataFlow + ) + return get_json_result(data={"canvas": canvas, "total": total}) + except Exception as e: + return server_error_response(e) + + +@manager.route("/setting", methods=["POST"]) # noqa: F821 +@validate_request("id", "title", "permission") +@login_required +def setting(): + req = request.json + req["user_id"] = current_user.id + + if not UserCanvasService.accessible(req["id"], current_user.id): + return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR) + + e, flow = UserCanvasService.get_by_id(req["id"]) + if not e: + return get_data_error_result(message="canvas not found.") + flow = flow.to_dict() + flow["title"] = req["title"] + for key in ("description", "permission", "avatar"): + if value := req.get(key): + flow[key] = value + + num = UserCanvasService.update_by_id(req["id"], flow) + return get_json_result(data=num) + + +@manager.route("/trace", methods=["GET"]) # noqa: F821 +def trace(): + dataflow_id = request.args.get("dataflow_id") + task_id = request.args.get("task_id") + if not all([dataflow_id, task_id]): + return get_data_error_result(message="dataflow_id and task_id are required.") + + e, dataflow_canvas = UserCanvasService.get_by_id(dataflow_id) + if not e: + return get_data_error_result(message="dataflow not found.") + + dsl_str = json.dumps(dataflow_canvas.dsl, ensure_ascii=False) + dataflow = Pipeline(dsl=dsl_str, tenant_id=dataflow_canvas.user_id, flow_id=dataflow_id, task_id=task_id) + log = dataflow.fetch_logs() + + return get_json_result(data=log) diff --git a/api/db/services/task_service.py b/api/db/services/task_service.py index 207b6355d58..46087f8ba39 100644 --- a/api/db/services/task_service.py +++ b/api/db/services/task_service.py @@ -54,15 +54,15 @@ def trim_header_by_lines(text: str, max_length) -> str: class TaskService(CommonService): """Service class for managing document processing tasks. - + This class extends CommonService to provide specialized functionality for document processing task management, including task creation, progress tracking, and chunk management. It handles various document types (PDF, Excel, etc.) and manages their processing lifecycle. - + The class implements a robust task queue system with retry mechanisms and progress tracking, supporting both synchronous and asynchronous task execution. - + Attributes: model: The Task model class for database operations. """ @@ -72,14 +72,14 @@ class TaskService(CommonService): @DB.connection_context() def get_task(cls, task_id): """Retrieve detailed task information by task ID. - + This method fetches comprehensive task details including associated document, knowledge base, and tenant information. It also handles task retry logic and progress updates. - + Args: task_id (str): The unique identifier of the task to retrieve. - + Returns: dict: Task details dictionary containing all task information and related metadata. Returns None if task is not found or has exceeded retry limit. @@ -139,13 +139,13 @@ def get_task(cls, task_id): @DB.connection_context() def get_tasks(cls, doc_id: str): """Retrieve all tasks associated with a document. - + This method fetches all processing tasks for a given document, ordered by page number and creation time. It includes task progress and chunk information. - + Args: doc_id (str): The unique identifier of the document. - + Returns: list[dict]: List of task dictionaries containing task details. Returns None if no tasks are found. @@ -170,10 +170,10 @@ def get_tasks(cls, doc_id: str): @DB.connection_context() def update_chunk_ids(cls, id: str, chunk_ids: str): """Update the chunk IDs associated with a task. - + This method updates the chunk_ids field of a task, which stores the IDs of processed document chunks in a space-separated string format. - + Args: id (str): The unique identifier of the task. chunk_ids (str): Space-separated string of chunk identifiers. @@ -184,11 +184,11 @@ def update_chunk_ids(cls, id: str, chunk_ids: str): @DB.connection_context() def get_ongoing_doc_name(cls): """Get names of documents that are currently being processed. - + This method retrieves information about documents that are in the processing state, including their locations and associated IDs. It uses database locking to ensure thread safety when accessing the task information. - + Returns: list[tuple]: A list of tuples, each containing (parent_id/kb_id, location) for documents currently being processed. Returns empty list if @@ -238,14 +238,14 @@ def get_ongoing_doc_name(cls): @DB.connection_context() def do_cancel(cls, id): """Check if a task should be cancelled based on its document status. - + This method determines whether a task should be cancelled by checking the associated document's run status and progress. A task should be cancelled if its document is marked for cancellation or has negative progress. - + Args: id (str): The unique identifier of the task to check. - + Returns: bool: True if the task should be cancelled, False otherwise. """ @@ -311,18 +311,18 @@ def update_progress(cls, id, info): def queue_tasks(doc: dict, bucket: str, name: str, priority: int): """Create and queue document processing tasks. - + This function creates processing tasks for a document based on its type and configuration. It handles different document types (PDF, Excel, etc.) differently and manages task chunking and configuration. It also implements task reuse optimization by checking for previously completed tasks. - + Args: doc (dict): Document dictionary containing metadata and configuration. bucket (str): Storage bucket name where the document is stored. name (str): File name of the document. priority (int, optional): Priority level for task queueing (default is 0). - + Note: - For PDF documents, tasks are created per page range based on configuration - For Excel documents, tasks are created per row range @@ -410,19 +410,19 @@ def new_task(): def reuse_prev_task_chunks(task: dict, prev_tasks: list[dict], chunking_config: dict): """Attempt to reuse chunks from previous tasks for optimization. - + This function checks if chunks from previously completed tasks can be reused for the current task, which can significantly improve processing efficiency. It matches tasks based on page ranges and configuration digests. - + Args: task (dict): Current task dictionary to potentially reuse chunks for. prev_tasks (list[dict]): List of previous task dictionaries to check for reuse. chunking_config (dict): Configuration dictionary for chunk processing. - + Returns: int: Number of chunks successfully reused. Returns 0 if no chunks could be reused. - + Note: Chunks can only be reused if: - A previous task exists with matching page range and configuration digest @@ -470,3 +470,39 @@ def has_canceled(task_id): except Exception as e: logging.exception(e) return False + + +def queue_dataflow(dsl:str, tenant_id:str, doc_id:str, task_id:str, flow_id:str, priority: int, callback=None) -> tuple[bool, str]: + """ + Returns a tuple (success: bool, error_message: str). + """ + _ = callback + + task = dict( + id=get_uuid() if not task_id else task_id, + doc_id=doc_id, + from_page=0, + to_page=100000000, + task_type="dataflow", + priority=priority, + ) + + TaskService.model.delete().where(TaskService.model.id == task["id"]).execute() + bulk_insert_into_db(model=Task, data_source=[task], replace_on_conflict=True) + + kb_id = DocumentService.get_knowledgebase_id(doc_id) + if not kb_id: + return False, f"Can't find KB of this document: {doc_id}" + + task["kb_id"] = kb_id + task["tenant_id"] = tenant_id + task["task_type"] = "dataflow" + task["dsl"] = dsl + task["dataflow_id"] = get_uuid() if not flow_id else flow_id + + if not REDIS_CONN.queue_product( + get_svr_queue_name(priority), message=task + ): + return False, "Can't access Redis. Please check the Redis' status." + + return True, "" diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 1c0ea19b6ce..71f48609a84 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -2690,21 +2690,21 @@ "status": "1", "llm": [ { - "llm_name": "Qwen3-Embedding-8B", + "llm_name": "Qwen/Qwen3-Embedding-8B", "tags": "TEXT EMBEDDING,TEXT RE-RANK,32k", "max_tokens": 32000, "model_type": "embedding", "is_tools": false }, { - "llm_name": "Qwen3-Embedding-4B", + "llm_name": "Qwen/Qwen3-Embedding-4B", "tags": "TEXT EMBEDDING,TEXT RE-RANK,32k", "max_tokens": 32000, "model_type": "embedding", "is_tools": false }, { - "llm_name": "Qwen3-Embedding-0.6B", + "llm_name": "Qwen/Qwen3-Embedding-0.6B", "tags": "TEXT EMBEDDING,TEXT RE-RANK,32k", "max_tokens": 32000, "model_type": "embedding", diff --git a/rag/flow/__init__.py b/rag/flow/__init__.py index 318507ac49a..ca6202e15e2 100644 --- a/rag/flow/__init__.py +++ b/rag/flow/__init__.py @@ -14,36 +14,45 @@ # limitations under the License. # -import os import importlib import inspect +import pkgutil +from pathlib import Path from types import ModuleType from typing import Dict, Type -_package_path = os.path.dirname(__file__) __all_classes: Dict[str, Type] = {} +_pkg_dir = Path(__file__).resolve().parent +_pkg_name = __name__ + + +def _should_skip_module(mod_name: str) -> bool: + leaf = mod_name.rsplit(".", 1)[-1] + return leaf in {"__init__"} or leaf.startswith("__") or leaf.startswith("_") or leaf.startswith("base") + + def _import_submodules() -> None: - for filename in os.listdir(_package_path): # noqa: F821 - if filename.startswith("__") or not filename.endswith(".py") or filename.startswith("base"): + for modinfo in pkgutil.walk_packages([str(_pkg_dir)], prefix=_pkg_name + "."): # noqa: F821 + mod_name = modinfo.name + if _should_skip_module(mod_name): # noqa: F821 continue - module_name = filename[:-3] - try: - module = importlib.import_module(f".{module_name}", package=__name__) + module = importlib.import_module(mod_name) _extract_classes_from_module(module) # noqa: F821 except ImportError as e: - print(f"Warning: Failed to import module {module_name}: {str(e)}") + print(f"Warning: Failed to import module {mod_name}: {e}") + def _extract_classes_from_module(module: ModuleType) -> None: for name, obj in inspect.getmembers(module): - if (inspect.isclass(obj) and - obj.__module__ == module.__name__ and not name.startswith("_")): + if inspect.isclass(obj) and obj.__module__ == module.__name__ and not name.startswith("_"): __all_classes[name] = obj globals()[name] = obj + _import_submodules() __all__ = list(__all_classes.keys()) + ["__all_classes"] -del _package_path, _import_submodules, _extract_classes_from_module \ No newline at end of file +del _pkg_dir, _pkg_name, _import_submodules, _extract_classes_from_module diff --git a/rag/flow/base.py b/rag/flow/base.py index d0c486f3be4..89b37b50123 100644 --- a/rag/flow/base.py +++ b/rag/flow/base.py @@ -1,5 +1,5 @@ # -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# 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. @@ -13,13 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import time -import os import logging +import os +import time from functools import partial from typing import Any + import trio -from agent.component.base import ComponentParamBase, ComponentBase + +from agent.component.base import ComponentBase, ComponentParamBase from api.utils.api_utils import timeout @@ -31,14 +33,16 @@ def __init__(self): class ProcessBase(ComponentBase): - def __init__(self, pipeline, id, param: ProcessParamBase): super().__init__(pipeline, id, param) - self.callback = partial(self._canvas.callback, self.component_name) + if hasattr(self._canvas, "callback"): + self.callback = partial(self._canvas.callback, self.component_name) + else: + self.callback = partial(lambda *args, **kwargs: None, self.component_name) async def invoke(self, **kwargs) -> dict[str, Any]: self.set_output("_created_time", time.perf_counter()) - for k,v in kwargs.items(): + for k, v in kwargs.items(): self.set_output(k, v) try: with trio.fail_after(self._param.timeout): @@ -54,6 +58,6 @@ async def invoke(self, **kwargs) -> dict[str, Any]: self.set_output("_elapsed_time", time.perf_counter() - self.output("_created_time")) return self.output() - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)) + @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10 * 60)) async def _invoke(self, **kwargs): raise NotImplementedError() diff --git a/rag/flow/chunker/__init__.py b/rag/flow/chunker/__init__.py new file mode 100644 index 00000000000..b4663378e88 --- /dev/null +++ b/rag/flow/chunker/__init__.py @@ -0,0 +1,15 @@ +# +# 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. + diff --git a/rag/flow/chunker.py b/rag/flow/chunker/chunker.py similarity index 63% rename from rag/flow/chunker.py rename to rag/flow/chunker/chunker.py index d869c94ec25..f853fc9e77e 100644 --- a/rag/flow/chunker.py +++ b/rag/flow/chunker/chunker.py @@ -1,5 +1,5 @@ # -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# 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. @@ -13,12 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. import random + import trio + from api.db import LLMType from api.db.services.llm_service import LLMBundle from deepdoc.parser.pdf_parser import RAGFlowPdfParser -from graphrag.utils import get_llm_cache, chat_limiter, set_llm_cache +from graphrag.utils import chat_limiter, get_llm_cache, set_llm_cache from rag.flow.base import ProcessBase, ProcessParamBase +from rag.flow.chunker.schema import ChunkerFromUpstream from rag.nlp import naive_merge, naive_merge_with_images from rag.prompts.prompts import keyword_extraction, question_proposal @@ -26,7 +29,23 @@ class ChunkerParam(ProcessParamBase): def __init__(self): super().__init__() - self.method_options = ["general", "q&a", "resume", "manual", "table", "paper", "book", "laws", "presentation", "one"] + self.method_options = [ + # General + "general", + "onetable", + # Customer Service + "q&a", + "manual", + # Recruitment + "resume", + # Education & Research + "book", + "paper", + "laws", + "presentation", + # Other + # "Tag" # TODO: Other method + ] self.method = "general" self.chunk_token_size = 512 self.delimiter = "\n" @@ -35,10 +54,7 @@ def __init__(self): self.auto_keywords = 0 self.auto_questions = 0 self.tag_sets = [] - self.llm_setting = { - "llm_name": "", - "lang": "Chinese" - } + self.llm_setting = {"llm_name": "", "lang": "Chinese"} def check(self): self.check_valid_value(self.method.lower(), "Chunk method abnormal.", self.method_options) @@ -48,53 +64,79 @@ def check(self): self.check_nonnegative_number(self.auto_questions, "Auto-question value: (0, 10]") self.check_decimal_float(self.overlapped_percent, "Overlapped percentage: [0, 1)") + def get_input_form(self) -> dict[str, dict]: + return {} + class Chunker(ProcessBase): component_name = "Chunker" - def _general(self, **kwargs): - self.callback(random.randint(1,5)/100., "Start to chunk via `General`.") - if kwargs.get("output_format") in ["markdown", "text"]: - cks = naive_merge(kwargs.get(kwargs["output_format"]), self._param.chunk_token_size, self._param.delimiter, self._param.overlapped_percent) + def _general(self, from_upstream: ChunkerFromUpstream): + self.callback(random.randint(1, 5) / 100.0, "Start to chunk via `General`.") + if from_upstream.output_format in ["markdown", "text"]: + if from_upstream.output_format == "markdown": + payload = from_upstream.markdown_result + else: # == "text" + payload = from_upstream.text_result + + if not payload: + payload = "" + + cks = naive_merge( + payload, + self._param.chunk_token_size, + self._param.delimiter, + self._param.overlapped_percent, + ) return [{"text": c} for c in cks] sections, section_images = [], [] - for o in kwargs["json"]: - sections.append((o["text"], o.get("position_tag",""))) + for o in from_upstream.json_result or []: + sections.append((o.get("text", ""), o.get("position_tag", ""))) section_images.append(o.get("image")) - chunks, images = naive_merge_with_images(sections, section_images,self._param.chunk_token_size, self._param.delimiter, self._param.overlapped_percent) - return [{ - "text": RAGFlowPdfParser.remove_tag(c), - "image": img, - "positions": RAGFlowPdfParser.extract_positions(c) - } for c,img in zip(chunks,images)] - - def _q_and_a(self, **kwargs): + chunks, images = naive_merge_with_images( + sections, + section_images, + self._param.chunk_token_size, + self._param.delimiter, + self._param.overlapped_percent, + ) + + return [ + { + "text": RAGFlowPdfParser.remove_tag(c), + "image": img, + "positions": RAGFlowPdfParser.extract_positions(c), + } + for c, img in zip(chunks, images) + ] + + def _q_and_a(self, from_upstream: ChunkerFromUpstream): pass - def _resume(self, **kwargs): + def _resume(self, from_upstream: ChunkerFromUpstream): pass - def _manual(self, **kwargs): + def _manual(self, from_upstream: ChunkerFromUpstream): pass - def _table(self, **kwargs): + def _table(self, from_upstream: ChunkerFromUpstream): pass - def _paper(self, **kwargs): + def _paper(self, from_upstream: ChunkerFromUpstream): pass - def _book(self, **kwargs): + def _book(self, from_upstream: ChunkerFromUpstream): pass - def _laws(self, **kwargs): + def _laws(self, from_upstream: ChunkerFromUpstream): pass - def _presentation(self, **kwargs): + def _presentation(self, from_upstream: ChunkerFromUpstream): pass - def _one(self, **kwargs): + def _one(self, from_upstream: ChunkerFromUpstream): pass async def _invoke(self, **kwargs): @@ -110,7 +152,14 @@ async def _invoke(self, **kwargs): "presentation": self._presentation, "one": self._one, } - chunks = function_map[self._param.method](**kwargs) + + try: + from_upstream = ChunkerFromUpstream.model_validate(kwargs) + except Exception as e: + self.set_output("_ERROR", f"Input error: {str(e)}") + return + + chunks = function_map[self._param.method](from_upstream) llm_setting = self._param.llm_setting async def auto_keywords(): diff --git a/rag/flow/chunker/schema.py b/rag/flow/chunker/schema.py new file mode 100644 index 00000000000..0f0e3042cde --- /dev/null +++ b/rag/flow/chunker/schema.py @@ -0,0 +1,37 @@ +# +# 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. +from typing import Any, Literal + +from pydantic import BaseModel, ConfigDict, Field + + +class ChunkerFromUpstream(BaseModel): + created_time: float | None = Field(default=None, alias="_created_time") + elapsed_time: float | None = Field(default=None, alias="_elapsed_time") + + name: str + blob: bytes + + output_format: Literal["json", "markdown", "text", "html"] | None = Field(default=None) + + json_result: list[dict[str, Any]] | None = Field(default=None, alias="json") + markdown_result: str | None = Field(default=None, alias="markdown") + text_result: str | None = Field(default=None, alias="text") + html_result: str | None = Field(default=None, alias="html") + + model_config = ConfigDict(populate_by_name=True, extra="forbid") + + # def to_dict(self, *, exclude_none: bool = True) -> dict: + # return self.model_dump(by_alias=True, exclude_none=exclude_none) diff --git a/rag/flow/begin.py b/rag/flow/file.py similarity index 92% rename from rag/flow/begin.py rename to rag/flow/file.py index 3b3622fb9d3..584b0ff9c17 100644 --- a/rag/flow/begin.py +++ b/rag/flow/file.py @@ -1,5 +1,5 @@ # -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# 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. @@ -27,6 +27,9 @@ def __init__(self): def check(self): pass + def get_input_form(self) -> dict[str, dict]: + return {} + class File(ProcessBase): component_name = "File" diff --git a/rag/flow/parser.py b/rag/flow/parser.py deleted file mode 100644 index a991a969dd7..00000000000 --- a/rag/flow/parser.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# Copyright 2024 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 random -import trio -from api.db import LLMType -from api.db.services.llm_service import LLMBundle -from deepdoc.parser.pdf_parser import RAGFlowPdfParser, PlainParser, VisionParser -from rag.flow.base import ProcessBase, ProcessParamBase -from rag.llm.cv_model import Base as VLM -from deepdoc.parser import ExcelParser - - -class ParserParam(ProcessParamBase): - def __init__(self): - super().__init__() - self.setups = { - "pdf": { - "parse_method": "deepdoc", # deepdoc/plain_text/vlm - "vlm_name": "", - "lang": "Chinese", - "suffix": ["pdf"], - "output_format": "json" - }, - "excel": { - "output_format": "html" - }, - "ppt": {}, - "image": { - "parse_method": "ocr" - }, - "email": {}, - "text": {}, - "audio": {}, - "video": {}, - } - - def check(self): - if self.setups["pdf"].get("parse_method") not in ["deepdoc", "plain_text"]: - assert self.setups["pdf"].get("vlm_name"), "No VLM specified." - assert self.setups["pdf"].get("lang"), "No language specified." - - -class Parser(ProcessBase): - component_name = "Parser" - - def _pdf(self, blob): - self.callback(random.randint(1,5)/100., "Start to work on a PDF.") - conf = self._param.setups["pdf"] - self.set_output("output_format", conf["output_format"]) - if conf.get("parse_method") == "deepdoc": - bboxes = RAGFlowPdfParser().parse_into_bboxes(blob, callback=self.callback) - elif conf.get("parse_method") == "plain_text": - lines,_ = PlainParser()(blob) - bboxes = [{"text": t} for t,_ in lines] - else: - assert conf.get("vlm_name") - vision_model = LLMBundle(self._canvas.tenant_id, LLMType.IMAGE2TEXT, llm_name=conf.get("vlm_name"), lang=self.setups["pdf"].get("lang")) - lines, _ = VisionParser(vision_model=vision_model)(bin, callback=self.callback) - bboxes = [] - for t, poss in lines: - pn, x0, x1, top, bott = poss.split(" ") - bboxes.append({"page_number": int(pn), "x0": int(x0), "x1": int(x1), "top": int(top), "bottom": int(bott), "text": t}) - - self.set_output("json", bboxes) - mkdn = "" - for b in bboxes: - if b.get("layout_type", "") == "title": - mkdn += "\n## " - if b.get("layout_type", "") == "figure": - mkdn += "\n![Image]({})".format(VLM.image2base64(b["image"])) - continue - mkdn += b.get("text", "") + "\n" - self.set_output("markdown", mkdn) - - def _excel(self, blob): - self.callback(random.randint(1,5)/100., "Start to work on a Excel.") - conf = self._param.setups["excel"] - excel_parser = ExcelParser() - if conf.get("output_format") == "html": - html = excel_parser.html(blob,1000000000) - self.set_output("html", html) - elif conf.get("output_format") == "json": - self.set_output("json", [{"text": txt} for txt in excel_parser(blob) if txt]) - elif conf.get("output_format") == "markdown": - self.set_output("markdown", excel_parser.markdown(blob)) - - async def _invoke(self, **kwargs): - function_map = { - "pdf": self._pdf, - } - for p_type, conf in self._param.setups.items(): - if kwargs.get("name", "").split(".")[-1].lower() not in conf.get("suffix", []): - continue - await trio.to_thread.run_sync(function_map[p_type], kwargs["blob"]) - break \ No newline at end of file diff --git a/rag/flow/parser/__init__.py b/rag/flow/parser/__init__.py new file mode 100644 index 00000000000..e6ddad7938c --- /dev/null +++ b/rag/flow/parser/__init__.py @@ -0,0 +1,14 @@ +# +# 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. diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py new file mode 100644 index 00000000000..fd65665fa31 --- /dev/null +++ b/rag/flow/parser/parser.py @@ -0,0 +1,154 @@ +# +# 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 random + +import trio + +from api.db import LLMType +from api.db.services.llm_service import LLMBundle +from deepdoc.parser import ExcelParser +from deepdoc.parser.pdf_parser import PlainParser, RAGFlowPdfParser, VisionParser +from rag.flow.base import ProcessBase, ProcessParamBase +from rag.flow.parser.schema import ParserFromUpstream +from rag.llm.cv_model import Base as VLM + + +class ParserParam(ProcessParamBase): + def __init__(self): + super().__init__() + self.allowed_output_format = { + "pdf": ["json", "markdown"], + "excel": ["json", "markdown", "html"], + "ppt": [], + "image": [], + "email": [], + "text": [], + "audio": [], + "video": [], + } + + self.setups = { + "pdf": { + "parse_method": "deepdoc", # deepdoc/plain_text/vlm + "vlm_name": "", + "lang": "Chinese", + "suffix": ["pdf"], + "output_format": "json", + }, + "excel": { + "output_format": "html", + "suffix": ["xls", "xlsx", "csv"], + }, + "ppt": {}, + "image": { + "parse_method": "ocr", + }, + "email": {}, + "text": {}, + "audio": {}, + "video": {}, + } + + def check(self): + pdf_config = self.setups.get("pdf", {}) + if pdf_config: + pdf_parse_method = pdf_config.get("parse_method", "") + self.check_valid_value(pdf_parse_method.lower(), "Parse method abnormal.", ["deepdoc", "plain_text", "vlm"]) + + if pdf_parse_method not in ["deepdoc", "plain_text"]: + self.check_empty(pdf_config.get("vlm_name"), "VLM") + + pdf_language = pdf_config.get("lang", "") + self.check_empty(pdf_language, "Language") + + pdf_output_format = pdf_config.get("output_format", "") + self.check_valid_value(pdf_output_format, "PDF output format abnormal.", self.allowed_output_format["pdf"]) + + excel_config = self.setups.get("excel", "") + if excel_config: + excel_output_format = excel_config.get("output_format", "") + self.check_valid_value(excel_output_format, "Excel output format abnormal.", self.allowed_output_format["excel"]) + + image_config = self.setups.get("image", "") + if image_config: + image_parse_method = image_config.get("parse_method", "") + self.check_valid_value(image_parse_method.lower(), "Parse method abnormal.", ["ocr"]) + + def get_input_form(self) -> dict[str, dict]: + return {} + + +class Parser(ProcessBase): + component_name = "Parser" + + def _pdf(self, blob): + self.callback(random.randint(1, 5) / 100.0, "Start to work on a PDF.") + conf = self._param.setups["pdf"] + self.set_output("output_format", conf["output_format"]) + if conf.get("parse_method") == "deepdoc": + bboxes = RAGFlowPdfParser().parse_into_bboxes(blob, callback=self.callback) + elif conf.get("parse_method") == "plain_text": + lines, _ = PlainParser()(blob) + bboxes = [{"text": t} for t, _ in lines] + else: + assert conf.get("vlm_name") + vision_model = LLMBundle(self._canvas._tenant_id, LLMType.IMAGE2TEXT, llm_name=conf.get("vlm_name"), lang=self._param.setups["pdf"].get("lang")) + lines, _ = VisionParser(vision_model=vision_model)(blob, callback=self.callback) + bboxes = [] + for t, poss in lines: + pn, x0, x1, top, bott = poss.split(" ") + bboxes.append({"page_number": int(pn), "x0": float(x0), "x1": float(x1), "top": float(top), "bottom": float(bott), "text": t}) + if conf.get("output_format") == "json": + self.set_output("json", bboxes) + if conf.get("output_format") == "markdown": + mkdn = "" + for b in bboxes: + if b.get("layout_type", "") == "title": + mkdn += "\n## " + if b.get("layout_type", "") == "figure": + mkdn += "\n![Image]({})".format(VLM.image2base64(b["image"])) + continue + mkdn += b.get("text", "") + "\n" + self.set_output("markdown", mkdn) + + def _excel(self, blob): + self.callback(random.randint(1, 5) / 100.0, "Start to work on a Excel.") + conf = self._param.setups["excel"] + self.set_output("output_format", conf["output_format"]) + excel_parser = ExcelParser() + if conf.get("output_format") == "html": + html = excel_parser.html(blob, 1000000000) + self.set_output("html", html) + elif conf.get("output_format") == "json": + self.set_output("json", [{"text": txt} for txt in excel_parser(blob) if txt]) + elif conf.get("output_format") == "markdown": + self.set_output("markdown", excel_parser.markdown(blob)) + + async def _invoke(self, **kwargs): + function_map = { + "pdf": self._pdf, + "excel": self._excel, + } + try: + from_upstream = ParserFromUpstream.model_validate(kwargs) + except Exception as e: + self.set_output("_ERROR", f"Input error: {str(e)}") + return + + for p_type, conf in self._param.setups.items(): + if from_upstream.name.split(".")[-1].lower() not in conf.get("suffix", []): + continue + await trio.to_thread.run_sync(function_map[p_type], from_upstream.blob) + break diff --git a/rag/flow/parser/schema.py b/rag/flow/parser/schema.py new file mode 100644 index 00000000000..37292e058df --- /dev/null +++ b/rag/flow/parser/schema.py @@ -0,0 +1,25 @@ +# +# 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. +from pydantic import BaseModel, ConfigDict, Field + + +class ParserFromUpstream(BaseModel): + created_time: float | None = Field(default=None, alias="_created_time") + elapsed_time: float | None = Field(default=None, alias="_elapsed_time") + + name: str + blob: bytes + + model_config = ConfigDict(populate_by_name=True, extra="forbid") diff --git a/rag/flow/pipeline.py b/rag/flow/pipeline.py index c57d04e2ac0..9f88d29ea47 100644 --- a/rag/flow/pipeline.py +++ b/rag/flow/pipeline.py @@ -1,5 +1,5 @@ # -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# 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. @@ -18,14 +18,15 @@ import logging import random import time + import trio + from agent.canvas import Graph from api.db.services.document_service import DocumentService from rag.utils.redis_conn import REDIS_CONN class Pipeline(Graph): - def __init__(self, dsl: str, tenant_id=None, doc_id=None, task_id=None, flow_id=None): super().__init__(dsl, tenant_id, task_id) self._doc_id = doc_id @@ -35,7 +36,7 @@ def __init__(self, dsl: str, tenant_id=None, doc_id=None, task_id=None, flow_id= self._kb_id = DocumentService.get_knowledgebase_id(doc_id) assert self._kb_id, f"Can't find KB of this document: {doc_id}" - def callback(self, component_name: str, progress: float|int|None=None, message: str = "") -> None: + def callback(self, component_name: str, progress: float | int | None = None, message: str = "") -> None: log_key = f"{self._flow_id}-{self.task_id}-logs" try: bin = REDIS_CONN.get(log_key) @@ -44,16 +45,10 @@ def callback(self, component_name: str, progress: float|int|None=None, message: if obj[-1]["component_name"] == component_name: obj[-1]["trace"].append({"progress": progress, "message": message, "datetime": datetime.datetime.now().strftime("%H:%M:%S")}) else: - obj.append({ - "component_name": component_name, - "trace": [{"progress": progress, "message": message, "datetime": datetime.datetime.now().strftime("%H:%M:%S")}] - }) + obj.append({"component_name": component_name, "trace": [{"progress": progress, "message": message, "datetime": datetime.datetime.now().strftime("%H:%M:%S")}]}) else: - obj = [{ - "component_name": component_name, - "trace": [{"progress": progress, "message": message, "datetime": datetime.datetime.now().strftime("%H:%M:%S")}] - }] - REDIS_CONN.set_obj(log_key, obj, 60*10) + obj = [{"component_name": component_name, "trace": [{"progress": progress, "message": message, "datetime": datetime.datetime.now().strftime("%H:%M:%S")}]}] + REDIS_CONN.set_obj(log_key, obj, 60 * 10) except Exception as e: logging.exception(e) @@ -71,21 +66,19 @@ def reset(self): super().reset() log_key = f"{self._flow_id}-{self.task_id}-logs" try: - REDIS_CONN.set_obj(log_key, [], 60*10) + REDIS_CONN.set_obj(log_key, [], 60 * 10) except Exception as e: logging.exception(e) async def run(self, **kwargs): st = time.perf_counter() if not self.path: - self.path.append("begin") + self.path.append("File") if self._doc_id: - DocumentService.update_by_id(self._doc_id, { - "progress": random.randint(0,5)/100., - "progress_msg": "Start the pipeline...", - "process_begin_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - }) + DocumentService.update_by_id( + self._doc_id, {"progress": random.randint(0, 5) / 100.0, "progress_msg": "Start the pipeline...", "process_begin_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")} + ) self.error = "" idx = len(self.path) - 1 @@ -99,23 +92,21 @@ async def run(self, **kwargs): self.path.extend(cpn_obj.get_downstream()) while idx < len(self.path) and not self.error: - last_cpn = self.get_component_obj(self.path[idx-1]) + last_cpn = self.get_component_obj(self.path[idx - 1]) cpn_obj = self.get_component_obj(self.path[idx]) + async def invoke(): nonlocal last_cpn, cpn_obj await cpn_obj.invoke(**last_cpn.output()) + async with trio.open_nursery() as nursery: nursery.start_soon(invoke) if cpn_obj.error(): self.error = "[ERROR]" + cpn_obj.error() + self.callback(cpn_obj.component_name, -1, self.error) break idx += 1 self.path.extend(cpn_obj.get_downstream()) if self._doc_id: - DocumentService.update_by_id(self._doc_id, { - "progress": 1 if not self.error else -1, - "progress_msg": "Pipeline finished...\n" + self.error, - "process_duration": time.perf_counter() - st - }) - + DocumentService.update_by_id(self._doc_id, {"progress": 1 if not self.error else -1, "progress_msg": "Pipeline finished...\n" + self.error, "process_duration": time.perf_counter() - st}) diff --git a/rag/flow/tests/client.py b/rag/flow/tests/client.py index eedaf7efc28..cf4a4db3744 100644 --- a/rag/flow/tests/client.py +++ b/rag/flow/tests/client.py @@ -1,5 +1,5 @@ # -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# 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. @@ -18,12 +18,14 @@ import os import time from concurrent.futures import ThreadPoolExecutor + import trio + from api import settings from rag.flow.pipeline import Pipeline -def print_logs(pipeline): +def print_logs(pipeline: Pipeline): last_logs = "[]" while True: time.sleep(5) @@ -34,16 +36,16 @@ def print_logs(pipeline): last_logs = logs_str -if __name__ == '__main__': +if __name__ == "__main__": parser = argparse.ArgumentParser() dsl_default_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), "dsl_examples", "general_pdf_all.json", ) - parser.add_argument('-s', '--dsl', default=dsl_default_path, help="input dsl", action='store', required=True) - parser.add_argument('-d', '--doc_id', default=False, help="Document ID", action='store', required=True) - parser.add_argument('-t', '--tenant_id', default=False, help="Tenant ID", action='store', required=True) + parser.add_argument("-s", "--dsl", default=dsl_default_path, help="input dsl", action="store", required=False) + parser.add_argument("-d", "--doc_id", default=False, help="Document ID", action="store", required=True) + parser.add_argument("-t", "--tenant_id", default=False, help="Tenant ID", action="store", required=True) args = parser.parse_args() settings.init_settings() @@ -53,5 +55,7 @@ def print_logs(pipeline): exe = ThreadPoolExecutor(max_workers=5) thr = exe.submit(print_logs, pipeline) + # queue_dataflow(dsl=open(args.dsl, "r").read(), tenant_id=args.tenant_id, doc_id=args.doc_id, task_id="xxxx", flow_id="xxx", priority=0) + trio.run(pipeline.run) - thr.result() \ No newline at end of file + thr.result() diff --git a/rag/flow/tests/dsl_examples/general_pdf_all.json b/rag/flow/tests/dsl_examples/general_pdf_all.json index 5c29433ed55..7142e55478a 100644 --- a/rag/flow/tests/dsl_examples/general_pdf_all.json +++ b/rag/flow/tests/dsl_examples/general_pdf_all.json @@ -1,15 +1,15 @@ { "components": { - "begin": { + "File": { "obj":{ "component_name": "File", "params": { } }, - "downstream": ["parser:0"], + "downstream": ["Parser:0"], "upstream": [] }, - "parser:0": { + "Parser:0": { "obj": { "component_name": "Parser", "params": { @@ -22,14 +22,22 @@ "pdf" ], "output_format": "json" + }, + "excel": { + "output_format": "html", + "suffix": [ + "xls", + "xlsx", + "csv" + ] } } } }, - "downstream": ["chunker:0"], - "upstream": ["begin"] + "downstream": ["Chunker:0"], + "upstream": ["Begin"] }, - "chunker:0": { + "Chunker:0": { "obj": { "component_name": "Chunker", "params": { @@ -37,18 +45,19 @@ "auto_keywords": 5 } }, - "downstream": ["tokenizer:0"], - "upstream": ["chunker:0"] + "downstream": ["Tokenizer:0"], + "upstream": ["Parser:0"] }, - "tokenizer:0": { + "Tokenizer:0": { "obj": { "component_name": "Tokenizer", "params": { } }, "downstream": [], - "upstream": ["chunker:0"] + "upstream": ["Chunker:0"] } }, "path": [] -} \ No newline at end of file +} + diff --git a/rag/flow/tokenizer/__init__.py b/rag/flow/tokenizer/__init__.py new file mode 100644 index 00000000000..e6ddad7938c --- /dev/null +++ b/rag/flow/tokenizer/__init__.py @@ -0,0 +1,14 @@ +# +# 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. diff --git a/rag/flow/tokenizer/schema.py b/rag/flow/tokenizer/schema.py new file mode 100644 index 00000000000..508fa002c61 --- /dev/null +++ b/rag/flow/tokenizer/schema.py @@ -0,0 +1,51 @@ +# +# 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. +from typing import Any, Literal + +from pydantic import BaseModel, ConfigDict, Field, model_validator + + +class TokenizerFromUpstream(BaseModel): + created_time: float | None = Field(default=None, alias="_created_time") + elapsed_time: float | None = Field(default=None, alias="_elapsed_time") + + name: str = "" + blob: bytes + + output_format: Literal["json", "markdown", "text", "html"] | None = Field(default=None) + + chunks: list[dict[str, Any]] | None = Field(default=None) + + json_result: list[dict[str, Any]] | None = Field(default=None, alias="json") + markdown_result: str | None = Field(default=None, alias="markdown") + text_result: str | None = Field(default=None, alias="text") + html_result: str | None = Field(default=None, alias="html") + + model_config = ConfigDict(populate_by_name=True, extra="forbid") + + @model_validator(mode="after") + def _check_payloads(self) -> "TokenizerFromUpstream": + if self.chunks: + return self + + if self.output_format in {"markdown", "text"}: + if self.output_format == "markdown" and not self.markdown_result: + raise ValueError("output_format=markdown requires a markdown payload (field: 'markdown' or 'markdown_result').") + if self.output_format == "text" and not self.text_result: + raise ValueError("output_format=text requires a text payload (field: 'text' or 'text_result').") + else: + if not self.json_result: + raise ValueError("When no chunks are provided and output_format is not markdown/text, a JSON list payload is required (field: 'json' or 'json_result').") + return self diff --git a/rag/flow/tokenizer.py b/rag/flow/tokenizer/tokenizer.py similarity index 71% rename from rag/flow/tokenizer.py rename to rag/flow/tokenizer/tokenizer.py index 0f11e8ece6e..5ad20977600 100644 --- a/rag/flow/tokenizer.py +++ b/rag/flow/tokenizer/tokenizer.py @@ -1,5 +1,5 @@ # -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# 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. @@ -12,6 +12,7 @@ # 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 random import re @@ -24,6 +25,7 @@ from api.db.services.user_service import TenantService from api.utils.api_utils import timeout from rag.flow.base import ProcessBase, ProcessParamBase +from rag.flow.tokenizer.schema import TokenizerFromUpstream from rag.nlp import rag_tokenizer from rag.settings import EMBEDDING_BATCH_SIZE from rag.svr.task_executor import embed_limiter @@ -40,6 +42,9 @@ def check(self): for v in self.search_method: self.check_valid_value(v.lower(), "Chunk method abnormal.", ["full_text", "embedding"]) + def get_input_form(self) -> dict[str, dict]: + return {} + class Tokenizer(ProcessBase): component_name = "Tokenizer" @@ -67,19 +72,19 @@ async def _embedding(self, name, chunks): @timeout(60) def batch_encode(txts): nonlocal embedding_model - return embedding_model.encode([truncate(c, embedding_model.max_length-10) for c in txts]) + return embedding_model.encode([truncate(c, embedding_model.max_length - 10) for c in txts]) cnts_ = np.array([]) for i in range(0, len(texts), EMBEDDING_BATCH_SIZE): async with embed_limiter: - vts, c = await trio.to_thread.run_sync(lambda: batch_encode(texts[i: i + EMBEDDING_BATCH_SIZE])) + vts, c = await trio.to_thread.run_sync(lambda: batch_encode(texts[i : i + EMBEDDING_BATCH_SIZE])) if len(cnts_) == 0: cnts_ = vts else: cnts_ = np.concatenate((cnts_, vts), axis=0) token_count += c if i % 33 == 32: - self.callback(i*1./len(texts)/parts/EMBEDDING_BATCH_SIZE + 0.5*(parts-1)) + self.callback(i * 1.0 / len(texts) / parts / EMBEDDING_BATCH_SIZE + 0.5 * (parts - 1)) cnts = cnts_ title_w = float(self._param.filename_embd_weight) @@ -92,11 +97,17 @@ def batch_encode(txts): return chunks, token_count async def _invoke(self, **kwargs): + try: + from_upstream = TokenizerFromUpstream.model_validate(kwargs) + except Exception as e: + self.set_output("_ERROR", f"Input error: {str(e)}") + return + parts = sum(["full_text" in self._param.search_method, "embedding" in self._param.search_method]) if "full_text" in self._param.search_method: - self.callback(random.randint(1,5)/100., "Start to tokenize.") - if kwargs.get("chunks"): - chunks = kwargs["chunks"] + self.callback(random.randint(1, 5) / 100.0, "Start to tokenize.") + if from_upstream.chunks: + chunks = from_upstream.chunks for i, ck in enumerate(chunks): if ck.get("questions"): ck["question_tks"] = rag_tokenizer.tokenize("\n".join(ck["questions"])) @@ -105,30 +116,40 @@ async def _invoke(self, **kwargs): ck["content_ltks"] = rag_tokenizer.tokenize(ck["text"]) ck["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(ck["content_ltks"]) if i % 100 == 99: - self.callback(i*1./len(chunks)/parts) - elif kwargs.get("output_format") in ["markdown", "text"]: - ck = { - "text": kwargs.get(kwargs["output_format"], "") - } - if "full_text" in self._param.search_method: + self.callback(i * 1.0 / len(chunks) / parts) + elif from_upstream.output_format in ["markdown", "text"]: + if from_upstream.output_format == "markdown": + payload = from_upstream.markdown_result + else: # == "text" + payload = from_upstream.text_result + + if not payload: + return "" + + ck = {"text": payload} + if "full_text" in self._param.search_method: ck["content_ltks"] = rag_tokenizer.tokenize(kwargs.get(kwargs["output_format"], "")) ck["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(ck["content_ltks"]) chunks = [ck] else: - chunks = kwargs["json"] + chunks = from_upstream.json_result for i, ck in enumerate(chunks): ck["content_ltks"] = rag_tokenizer.tokenize(ck["text"]) ck["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(ck["content_ltks"]) if i % 100 == 99: - self.callback(i*1./len(chunks)/parts) + self.callback(i * 1.0 / len(chunks) / parts) - self.callback(1./parts, "Finish tokenizing.") + self.callback(1.0 / parts, "Finish tokenizing.") if "embedding" in self._param.search_method: - self.callback(random.randint(1,5)/100. + 0.5*(parts-1), "Start embedding inference.") - chunks, token_count = await self._embedding(kwargs.get("name", ""), chunks) + self.callback(random.randint(1, 5) / 100.0 + 0.5 * (parts - 1), "Start embedding inference.") + + if from_upstream.name.strip() == "": + logging.warning("Tokenizer: empty name provided from upstream, embedding may be not accurate.") + + chunks, token_count = await self._embedding(from_upstream.name, chunks) self.set_output("embedding_token_consumption", token_count) - self.callback(1., "Finish embedding.") + self.callback(1.0, "Finish embedding.") self.set_output("chunks", chunks) diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index e9151b70d9b..84c73d2b695 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -21,10 +21,12 @@ import threading import time +from api.utils import get_uuid from api.utils.api_utils import timeout from api.utils.log_utils import init_root_logger, get_project_base_directory from graphrag.general.index import run_graphrag from graphrag.utils import get_llm_cache, set_llm_cache, get_tags_from_cache, set_tags_to_cache +from rag.flow.pipeline import Pipeline from rag.prompts import keyword_extraction, question_proposal, content_tagging import logging @@ -223,7 +225,14 @@ async def collect(): logging.warning(f"collect task {msg['id']} {state}") redis_msg.ack() return None, None - task["task_type"] = msg.get("task_type", "") + + task_type = msg.get("task_type", "") + task["task_type"] = task_type + if task_type == "dataflow": + task["tenant_id"]=msg.get("tenant_id", "") + task["dsl"] = msg.get("dsl", "") + task["dataflow_id"] = msg.get("dataflow_id", get_uuid()) + task["kb_id"] = msg.get("kb_id", "") return redis_msg, task @@ -473,6 +482,15 @@ def batch_encode(txts): return tk_count, vector_size +async def run_dataflow(dsl:str, tenant_id:str, doc_id:str, task_id:str, flow_id:str, callback=None): + _ = callback + + pipeline = Pipeline(dsl=dsl, tenant_id=tenant_id, doc_id=doc_id, task_id=task_id, flow_id=flow_id) + pipeline.reset() + + await pipeline.run() + + @timeout(3600) async def run_raptor(row, chat_mdl, embd_mdl, vector_size, callback=None): chunks = [] @@ -558,15 +576,20 @@ async def do_handle_task(task): init_kb(task, vector_size) - # Either using RAPTOR or Standard chunking methods - if task.get("task_type", "") == "raptor": + task_type = task.get("task_type", "") + if task_type == "dataflow": + task_dataflow_dsl = task["dsl"] + task_dataflow_id = task["dataflow_id"] + await run_dataflow(dsl=task_dataflow_dsl, tenant_id=task_tenant_id, doc_id=task_doc_id, task_id=task_id, flow_id=task_dataflow_id, callback=None) + return + elif task_type == "raptor": # bind LLM for raptor chat_model = LLMBundle(task_tenant_id, LLMType.CHAT, llm_name=task_llm_id, lang=task_language) # run RAPTOR async with kg_limiter: chunks, token_count = await run_raptor(task, chat_model, embedding_model, vector_size, progress_callback) # Either using graphrag or Standard chunking methods - elif task.get("task_type", "") == "graphrag": + elif task_type == "graphrag": if not task_parser_config.get("graphrag", {}).get("use_graphrag", False): progress_callback(prog=-1.0, msg="Internal configuration error.") return From 91d6fb8061dc838d9c1d3b91103e171e7cbbec8d Mon Sep 17 00:00:00 2001 From: Yuhao Bi Date: Fri, 5 Sep 2025 19:17:21 +0800 Subject: [PATCH 0632/1187] Fix miscalculated token count (#9776) ### What problem does this PR solve? The total token was incorrectly accumulated when using the OpenAI-API-Compatible api. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/llm/chat_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 98d71141c85..adc4a3c5af8 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -374,7 +374,7 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict = if not tol: total_tokens += num_tokens_from_string(resp.choices[0].delta.content) else: - total_tokens += tol + total_tokens = tol finish_reason = resp.choices[0].finish_reason if hasattr(resp.choices[0], "finish_reason") else "" if finish_reason == "length": @@ -410,7 +410,7 @@ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict = if not tol: total_tokens += num_tokens_from_string(resp.choices[0].delta.content) else: - total_tokens += tol + total_tokens = tol answer += resp.choices[0].delta.content yield resp.choices[0].delta.content From 63781bde3f705416626607f9532244692cca4884 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Fri, 5 Sep 2025 19:26:15 +0800 Subject: [PATCH 0633/1187] Refa: import issue. (#9958) ### What problem does this PR solve? ### Type of change - [x] Refactoring --- api/apps/sdk/dify_retrieval.py | 17 +---------------- api/apps/sdk/doc.py | 3 +-- api/db/services/dialog_service.py | 19 +++++++++++++++++-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/api/apps/sdk/dify_retrieval.py b/api/apps/sdk/dify_retrieval.py index 0e80b4587cf..446d4d74a52 100644 --- a/api/apps/sdk/dify_retrieval.py +++ b/api/apps/sdk/dify_retrieval.py @@ -24,7 +24,7 @@ from api import settings from api.utils.api_utils import validate_request, build_error_result, apikey_required from rag.app.tag import label_question -from api.db.services.dialog_service import meta_filter +from api.db.services.dialog_service import meta_filter, convert_conditions @manager.route('/dify/retrieval', methods=['POST']) # noqa: F821 @@ -101,19 +101,4 @@ def retrieval(tenant_id): logging.exception(e) return build_error_result(message=str(e), code=settings.RetCode.SERVER_ERROR) -def convert_conditions(metadata_condition): - if metadata_condition is None: - metadata_condition = {} - op_mapping = { - "is": "=", - "not is": "≠" - } - return [ - { - "op": op_mapping.get(cond["comparison_operator"], cond["comparison_operator"]), - "key": cond["name"], - "value": cond["value"] - } - for cond in metadata_condition.get("conditions", []) -] diff --git a/api/apps/sdk/doc.py b/api/apps/sdk/doc.py index 50e6f7bba6b..5009b6fee2b 100644 --- a/api/apps/sdk/doc.py +++ b/api/apps/sdk/doc.py @@ -35,8 +35,7 @@ from api.db.services.llm_service import LLMBundle from api.db.services.tenant_llm_service import TenantLLMService from api.db.services.task_service import TaskService, queue_tasks -from api.db.services.dialog_service import meta_filter -from api.apps.sdk.dify_retrieval import convert_conditions +from api.db.services.dialog_service import meta_filter, convert_conditions from api.utils.api_utils import check_duplicate_ids, construct_json_result, get_error_data_result, get_parser_config, get_result, server_error_response, token_required from rag.app.qa import beAdoc, rmPrefix from rag.app.tag import label_question diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index b56459a568d..3855c1ded60 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -21,11 +21,9 @@ from datetime import datetime from functools import partial from timeit import default_timer as timer - import trio from langfuse import Langfuse from peewee import fn - from agentic_reasoning import DeepResearcher from api import settings from api.db import LLMType, ParserType, StatusEnum @@ -255,6 +253,23 @@ def replacement(match): return answer, idx +def convert_conditions(metadata_condition): + if metadata_condition is None: + metadata_condition = {} + op_mapping = { + "is": "=", + "not is": "≠" + } + return [ + { + "op": op_mapping.get(cond["comparison_operator"], cond["comparison_operator"]), + "key": cond["name"], + "value": cond["value"] + } + for cond in metadata_condition.get("conditions", []) +] + + def meta_filter(metas: dict, filters: list[dict]): doc_ids = set([]) From 994517495f5d776dacb6536c147daf8378cbb5ec Mon Sep 17 00:00:00 2001 From: TeslaZY Date: Mon, 8 Sep 2025 10:39:23 +0800 Subject: [PATCH 0634/1187] add model: qwen3-max-preview (#9959) ### What problem does this PR solve? add qwen3-max-preview model, ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- conf/llm_factories.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 71f48609a84..20d817df401 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -337,6 +337,13 @@ "model_type": "chat", "is_tools": true }, + { + "llm_name": "qwen3-max-preview", + "tags": "LLM,CHAT,256k", + "max_tokens": 256000, + "model_type": "chat", + "is_tools": true + }, { "llm_name": "qwen3-coder-480b-a35b-instruct", "tags": "LLM,CHAT,256k", From b524cf0ec8a690c1b5c10c1c860fd2d36bc2f4b2 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 8 Sep 2025 11:42:46 +0800 Subject: [PATCH 0635/1187] Feat: Delete unused code in the data pipeline #9869 (#9971) ### What problem does this PR solve? Feat: Delete unused code in the data pipeline #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/chat/box.tsx | 4 +- .../agent/chat/use-send-agent-message.ts | 28 ++- .../agent/hooks/use-send-shared-message.ts | 7 +- web/src/pages/data-flow/canvas/index.tsx | 13 +- web/src/pages/data-flow/constant.tsx | 3 + .../data-flow/form-sheet/form-config-map.tsx | 80 ------- .../data-flow/form/akshare-form/index.tsx | 22 -- .../pages/data-flow/form/arxiv-form/index.tsx | 96 -------- .../data-flow/form/baidu-fanyi-form/index.tsx | 71 ------ .../pages/data-flow/form/baidu-form/index.tsx | 22 -- .../pages/data-flow/form/bing-form/index.tsx | 131 ---------- .../pages/data-flow/form/deepl-form/index.tsx | 36 --- .../data-flow/form/duckduckgo-form/index.tsx | 91 ------- .../data-flow/form/github-form/index.tsx | 52 ---- .../form/google-scholar-form/index.tsx | 166 ------------- .../pages/data-flow/form/jin10-form/index.tsx | 145 ------------ .../{google-form => parser-form}/index.tsx | 5 +- .../data-flow/form/pubmed-form/index.tsx | 90 ------- .../data-flow/form/qweather-form/index.tsx | 157 ------------ .../data-flow/form/searxng-form/index.tsx | 73 ------ .../form/tavily-extract-form/index.tsx | 121 ---------- .../form/tavily-form/dynamic-domain.tsx | 60 ----- .../data-flow/form/tavily-form/index.tsx | 214 ----------------- .../data-flow/form/tavily-form/use-values.ts | 23 -- .../form/tavily-form/use-watch-change.ts | 23 -- .../data-flow/form/tushare-form/index.tsx | 83 ------- .../data-flow/form/wencai-form/index.tsx | 97 -------- .../data-flow/form/wikipedia-form/index.tsx | 88 ------- .../form/yahoo-finance-form/index.tsx | 125 ---------- web/src/pages/data-flow/hooks.tsx | 2 - web/src/pages/data-flow/hooks/use-add-node.ts | 2 - .../hooks/use-send-shared-message.ts | 79 ------- web/src/pages/data-flow/share/index.tsx | 223 ------------------ .../data-flow/share/parameter-dialog.tsx | 30 --- 34 files changed, 35 insertions(+), 2427 deletions(-) delete mode 100644 web/src/pages/data-flow/form/akshare-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/arxiv-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/baidu-fanyi-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/baidu-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/bing-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/deepl-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/duckduckgo-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/github-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/google-scholar-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/jin10-form/index.tsx rename web/src/pages/data-flow/form/{google-form => parser-form}/index.tsx (97%) delete mode 100644 web/src/pages/data-flow/form/pubmed-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/qweather-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/searxng-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/tavily-extract-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/tavily-form/dynamic-domain.tsx delete mode 100644 web/src/pages/data-flow/form/tavily-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/tavily-form/use-values.ts delete mode 100644 web/src/pages/data-flow/form/tavily-form/use-watch-change.ts delete mode 100644 web/src/pages/data-flow/form/tushare-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/wencai-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/wikipedia-form/index.tsx delete mode 100644 web/src/pages/data-flow/form/yahoo-finance-form/index.tsx delete mode 100644 web/src/pages/data-flow/hooks/use-send-shared-message.ts delete mode 100644 web/src/pages/data-flow/share/index.tsx delete mode 100644 web/src/pages/data-flow/share/parameter-dialog.tsx diff --git a/web/src/pages/agent/chat/box.tsx b/web/src/pages/agent/chat/box.tsx index 14cebe96710..8404ee55688 100644 --- a/web/src/pages/agent/chat/box.tsx +++ b/web/src/pages/agent/chat/box.tsx @@ -21,6 +21,7 @@ import { useAwaitCompentData } from '../hooks/use-chat-logic'; import { useIsTaskMode } from '../hooks/use-get-begin-query'; function AgentChatBox() { + const { data: canvasInfo, refetch } = useFetchAgent(); const { value, scrollRef, @@ -33,13 +34,12 @@ function AgentChatBox() { sendFormMessage, findReferenceByMessageId, appendUploadResponseList, - } = useSendAgentMessage(); + } = useSendAgentMessage({ refetch }); const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = useClickDrawer(); useGetFileIcon(); const { data: userInfo } = useFetchUserInfo(); - const { data: canvasInfo } = useFetchAgent(); const { id: canvasId } = useParams(); const { uploadCanvasFile, loading } = useUploadCanvasFileWithProgress(); diff --git a/web/src/pages/agent/chat/use-send-agent-message.ts b/web/src/pages/agent/chat/use-send-agent-message.ts index 952e67e2847..7d16a4715d9 100644 --- a/web/src/pages/agent/chat/use-send-agent-message.ts +++ b/web/src/pages/agent/chat/use-send-agent-message.ts @@ -194,12 +194,19 @@ export const buildRequestBody = (value: string = '') => { return msgBody; }; -export const useSendAgentMessage = ( - url?: string, - addEventList?: (data: IEventList, messageId: string) => void, - beginParams?: any[], - isShared?: boolean, -) => { +export const useSendAgentMessage = ({ + url, + addEventList, + beginParams, + isShared, + refetch, +}: { + url?: string; + addEventList?: (data: IEventList, messageId: string) => void; + beginParams?: any[]; + isShared?: boolean; + refetch?: () => void; +}) => { const { id: agentId } = useParams(); const { handleInputChange, value, setValue } = useHandleMessageInputChange(); const inputs = useSelectBeginNodeDataInputs(); @@ -212,8 +219,6 @@ export const useSendAgentMessage = ( const isTaskMode = useIsTaskMode(); - // const { refetch } = useFetchAgent(); // This will cause the shared page to also send a request - const { findReferenceByMessageId } = useFindMessageReference(answerList); const prologue = useGetBeginNodePrologue(); const { @@ -277,7 +282,7 @@ export const useSendAgentMessage = ( setValue(message.content); removeLatestMessage(); } else { - // refetch(); // pull the message list after sending the message successfully + refetch?.(); // pull the message list after sending the message successfully } } catch (error) { console.log('🚀 ~ useSendAgentMessage ~ error:', error); @@ -293,6 +298,7 @@ export const useSendAgentMessage = ( clearUploadResponseList, setValue, removeLatestMessage, + refetch, ], ); @@ -305,9 +311,9 @@ export const useSendAgentMessage = ( role: MessageType.User, }); await send({ ...body, session_id: sessionId }); - // refetch(); + refetch?.(); }, - [addNewestOneQuestion, send, sessionId], + [addNewestOneQuestion, refetch, send, sessionId], ); // reset session diff --git a/web/src/pages/agent/hooks/use-send-shared-message.ts b/web/src/pages/agent/hooks/use-send-shared-message.ts index d8e2d61de31..61570edc041 100644 --- a/web/src/pages/agent/hooks/use-send-shared-message.ts +++ b/web/src/pages/agent/hooks/use-send-shared-message.ts @@ -48,7 +48,12 @@ export const useSendNextSharedMessage = ( showModal: showParameterDialog, } = useSetModalState(); - const ret = useSendAgentMessage(url, addEventList, params, true); + const ret = useSendAgentMessage({ + url, + addEventList, + beginParams: params, + isShared: true, + }); const ok = useCallback( (params: any[]) => { diff --git a/web/src/pages/data-flow/canvas/index.tsx b/web/src/pages/data-flow/canvas/index.tsx index eb91ee58454..c34d94f2edc 100644 --- a/web/src/pages/data-flow/canvas/index.tsx +++ b/web/src/pages/data-flow/canvas/index.tsx @@ -14,6 +14,7 @@ import { NodeTypes, Position, ReactFlow, + ReactFlowInstance, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import { NotebookPen } from 'lucide-react'; @@ -23,11 +24,7 @@ import { AgentBackground } from '../components/background'; import { AgentInstanceContext, HandleContext } from '../context'; import FormSheet from '../form-sheet/next'; -import { - useHandleDrop, - useSelectCanvasData, - useValidateConnection, -} from '../hooks'; +import { useSelectCanvasData, useValidateConnection } from '../hooks'; import { useAddNode } from '../hooks/use-add-node'; import { useBeforeDelete } from '../hooks/use-before-delete'; import { useMoveNote } from '../hooks/use-move-note'; @@ -104,8 +101,8 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { } = useSelectCanvasData(); const isValidConnection = useValidateConnection(); - const { onDrop, onDragOver, setReactFlowInstance, reactFlowInstance } = - useHandleDrop(); + const [reactFlowInstance, setReactFlowInstance] = + useState>(); const { onNodeClick, @@ -237,10 +234,8 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { onConnect={onConnect} nodeTypes={nodeTypes} edgeTypes={edgeTypes} - onDrop={onDrop} onConnectStart={OnConnectStart} onConnectEnd={OnConnectEnd} - onDragOver={onDragOver} onNodeClick={onNodeClick} onPaneClick={onPaneClick} onInit={setReactFlowInstance} diff --git a/web/src/pages/data-flow/constant.tsx b/web/src/pages/data-flow/constant.tsx index 7e57c4abcbe..bef95fb8de9 100644 --- a/web/src/pages/data-flow/constant.tsx +++ b/web/src/pages/data-flow/constant.tsx @@ -89,6 +89,9 @@ export enum Operator { UserFillUp = 'UserFillUp', StringTransform = 'StringTransform', SearXNG = 'SearXNG', + Parser = 'Parser', + Chunker = 'Chunker', + Tokenizer = 'Tokenizer', } export const SwitchLogicOperatorOptions = ['and', 'or']; diff --git a/web/src/pages/data-flow/form-sheet/form-config-map.tsx b/web/src/pages/data-flow/form-sheet/form-config-map.tsx index 0fc1b9e2aee..44dc2ca2eec 100644 --- a/web/src/pages/data-flow/form-sheet/form-config-map.tsx +++ b/web/src/pages/data-flow/form-sheet/form-config-map.tsx @@ -1,42 +1,22 @@ import { Operator } from '../constant'; import AgentForm from '../form/agent-form'; -import AkShareForm from '../form/akshare-form'; -import ArXivForm from '../form/arxiv-form'; -import BaiduFanyiForm from '../form/baidu-fanyi-form'; -import BaiduForm from '../form/baidu-form'; import BeginForm from '../form/begin-form'; -import BingForm from '../form/bing-form'; import CategorizeForm from '../form/categorize-form'; import CodeForm from '../form/code-form'; import CrawlerForm from '../form/crawler-form'; -import DeepLForm from '../form/deepl-form'; -import DuckDuckGoForm from '../form/duckduckgo-form'; import EmailForm from '../form/email-form'; import ExeSQLForm from '../form/exesql-form'; -import GithubForm from '../form/github-form'; -import GoogleForm from '../form/google-form'; -import GoogleScholarForm from '../form/google-scholar-form'; import InvokeForm from '../form/invoke-form'; import IterationForm from '../form/iteration-form'; import IterationStartForm from '../form/iteration-start-from'; -import Jin10Form from '../form/jin10-form'; import KeywordExtractForm from '../form/keyword-extract-form'; import MessageForm from '../form/message-form'; -import PubMedForm from '../form/pubmed-form'; -import QWeatherForm from '../form/qweather-form'; import RelevantForm from '../form/relevant-form'; import RetrievalForm from '../form/retrieval-form/next'; import RewriteQuestionForm from '../form/rewrite-question-form'; -import SearXNGForm from '../form/searxng-form'; import StringTransformForm from '../form/string-transform-form'; import SwitchForm from '../form/switch-form'; -import TavilyExtractForm from '../form/tavily-extract-form'; -import TavilyForm from '../form/tavily-form'; -import TuShareForm from '../form/tushare-form'; import UserFillUpForm from '../form/user-fill-up-form'; -import WenCaiForm from '../form/wencai-form'; -import WikipediaForm from '../form/wikipedia-form'; -import YahooFinanceForm from '../form/yahoo-finance-form'; export const FormConfigMap = { [Operator.Begin]: { @@ -66,75 +46,21 @@ export const FormConfigMap = { [Operator.Agent]: { component: AgentForm, }, - [Operator.Baidu]: { - component: BaiduForm, - }, - [Operator.DuckDuckGo]: { - component: DuckDuckGoForm, - }, [Operator.KeywordExtract]: { component: KeywordExtractForm, }, - [Operator.Wikipedia]: { - component: WikipediaForm, - }, - [Operator.PubMed]: { - component: PubMedForm, - }, - [Operator.ArXiv]: { - component: ArXivForm, - }, - [Operator.Google]: { - component: GoogleForm, - }, - [Operator.Bing]: { - component: BingForm, - }, - [Operator.GoogleScholar]: { - component: GoogleScholarForm, - }, - [Operator.DeepL]: { - component: DeepLForm, - }, - [Operator.GitHub]: { - component: GithubForm, - }, - [Operator.BaiduFanyi]: { - component: BaiduFanyiForm, - }, - [Operator.QWeather]: { - component: QWeatherForm, - }, [Operator.ExeSQL]: { component: ExeSQLForm, }, [Operator.Switch]: { component: SwitchForm, }, - [Operator.WenCai]: { - component: WenCaiForm, - }, - [Operator.AkShare]: { - component: AkShareForm, - }, - [Operator.YahooFinance]: { - component: YahooFinanceForm, - }, - [Operator.Jin10]: { - component: Jin10Form, - }, - [Operator.TuShare]: { - component: TuShareForm, - }, [Operator.Crawler]: { component: CrawlerForm, }, [Operator.Invoke]: { component: InvokeForm, }, - [Operator.SearXNG]: { - component: SearXNGForm, - }, [Operator.Concentrator]: { component: () => <>, }, @@ -150,16 +76,10 @@ export const FormConfigMap = { [Operator.IterationStart]: { component: IterationStartForm, }, - [Operator.TavilySearch]: { - component: TavilyForm, - }, [Operator.UserFillUp]: { component: UserFillUpForm, }, [Operator.StringTransform]: { component: StringTransformForm, }, - [Operator.TavilyExtract]: { - component: TavilyExtractForm, - }, }; diff --git a/web/src/pages/data-flow/form/akshare-form/index.tsx b/web/src/pages/data-flow/form/akshare-form/index.tsx deleted file mode 100644 index 1cfd554b17b..00000000000 --- a/web/src/pages/data-flow/form/akshare-form/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { TopNFormField } from '@/components/top-n-item'; -import { Form } from '@/components/ui/form'; -import { INextOperatorForm } from '../../interface'; -import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; - -const AkShareForm = ({ form, node }: INextOperatorForm) => { - return ( - - { - e.preventDefault(); - }} - > - - - - - ); -}; - -export default AkShareForm; diff --git a/web/src/pages/data-flow/form/arxiv-form/index.tsx b/web/src/pages/data-flow/form/arxiv-form/index.tsx deleted file mode 100644 index a6e1b7c4570..00000000000 --- a/web/src/pages/data-flow/form/arxiv-form/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useMemo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialArXivValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -export const ArXivFormPartialSchema = { - top_n: z.number(), - sort_by: z.string(), -}; - -export const FormSchema = z.object({ - ...ArXivFormPartialSchema, - query: z.string(), -}); - -export function ArXivFormWidgets() { - const form = useFormContext(); - const { t } = useTranslate('flow'); - - const options = useMemo(() => { - return ['submittedDate', 'lastUpdatedDate', 'relevance'].map((x) => ({ - value: x, - label: t(x), - })); - }, [t]); - - return ( - <> - - ( - - {t('sortBy')} - - - - - - )} - /> - - ); -} - -const outputList = buildOutputList(initialArXivValues.outputs); - -function ArXivForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialArXivValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - -
    - -
    -
    - ); -} - -export default memo(ArXivForm); diff --git a/web/src/pages/data-flow/form/baidu-fanyi-form/index.tsx b/web/src/pages/data-flow/form/baidu-fanyi-form/index.tsx deleted file mode 100644 index c02b3dd857b..00000000000 --- a/web/src/pages/data-flow/form/baidu-fanyi-form/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useTranslate } from '@/hooks/common-hooks'; -import { Form, Input, Select } from 'antd'; -import { useMemo } from 'react'; -import { IOperatorForm } from '../../interface'; -import { - BaiduFanyiDomainOptions, - BaiduFanyiSourceLangOptions, -} from '../../options'; -import DynamicInputVariable from '../components/dynamic-input-variable'; - -const BaiduFanyiForm = ({ onValuesChange, form, node }: IOperatorForm) => { - const { t } = useTranslate('flow'); - const options = useMemo(() => { - return ['translate', 'fieldtranslate'].map((x) => ({ - value: x, - label: t(`baiduSecretKeyOptions.${x}`), - })); - }, [t]); - - const baiduFanyiOptions = useMemo(() => { - return BaiduFanyiDomainOptions.map((x) => ({ - value: x, - label: t(`baiduDomainOptions.${x}`), - })); - }, [t]); - - const baiduFanyiSourceLangOptions = useMemo(() => { - return BaiduFanyiSourceLangOptions.map((x) => ({ - value: x, - label: t(`baiduSourceLangOptions.${x}`), - })); - }, [t]); - - return ( -
    - - - - - - - - - - - - {({ getFieldValue }) => - getFieldValue('trans_type') === 'fieldtranslate' && ( - - - - ) - } - - - - - - - -
    - ); -}; - -export default BaiduFanyiForm; diff --git a/web/src/pages/data-flow/form/baidu-form/index.tsx b/web/src/pages/data-flow/form/baidu-form/index.tsx deleted file mode 100644 index 0861ef829df..00000000000 --- a/web/src/pages/data-flow/form/baidu-form/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { TopNFormField } from '@/components/top-n-item'; -import { Form } from '@/components/ui/form'; -import { INextOperatorForm } from '../../interface'; -import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; - -const BaiduForm = ({ form, node }: INextOperatorForm) => { - return ( -
    - { - e.preventDefault(); - }} - > - - -
    - - ); -}; - -export default BaiduForm; diff --git a/web/src/pages/data-flow/form/bing-form/index.tsx b/web/src/pages/data-flow/form/bing-form/index.tsx deleted file mode 100644 index fae75aa125d..00000000000 --- a/web/src/pages/data-flow/form/bing-form/index.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useMemo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialBingValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { BingCountryOptions, BingLanguageOptions } from '../../options'; -import { FormWrapper } from '../components/form-wrapper'; -import { QueryVariable } from '../components/query-variable'; - -export const BingFormSchema = { - channel: z.string(), - api_key: z.string(), - country: z.string(), - language: z.string(), - top_n: z.number(), -}; - -export const FormSchema = z.object({ - query: z.string().optional(), - ...BingFormSchema, -}); - -export function BingFormWidgets() { - const form = useFormContext(); - const { t } = useTranslate('flow'); - - const options = useMemo(() => { - return ['Webpages', 'News'].map((x) => ({ label: x, value: x })); - }, []); - - return ( - <> - - ( - - {t('channel')} - - - - - - )} - /> - ( - - {t('apiKey')} - - - - - - )} - /> - ( - - {t('country')} - - - - - - )} - /> - ( - - {t('language')} - - - - - - )} - /> - - ); -} - -function BingForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialBingValues, node); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues, - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - -
    - ); -} - -export default memo(BingForm); diff --git a/web/src/pages/data-flow/form/deepl-form/index.tsx b/web/src/pages/data-flow/form/deepl-form/index.tsx deleted file mode 100644 index 55240b3c120..00000000000 --- a/web/src/pages/data-flow/form/deepl-form/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import TopNItem from '@/components/top-n-item'; -import { useTranslate } from '@/hooks/common-hooks'; -import { Form, Select } from 'antd'; -import { useBuildSortOptions } from '../../form-hooks'; -import { IOperatorForm } from '../../interface'; -import { DeepLSourceLangOptions, DeepLTargetLangOptions } from '../../options'; -import DynamicInputVariable from '../components/dynamic-input-variable'; - -const DeepLForm = ({ onValuesChange, form, node }: IOperatorForm) => { - const { t } = useTranslate('flow'); - const options = useBuildSortOptions(); - - return ( -
    - - - - - - - - - - - -
    - ); -}; - -export default DeepLForm; diff --git a/web/src/pages/data-flow/form/duckduckgo-form/index.tsx b/web/src/pages/data-flow/form/duckduckgo-form/index.tsx deleted file mode 100644 index 776635d3cd3..00000000000 --- a/web/src/pages/data-flow/form/duckduckgo-form/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useMemo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { Channel, initialDuckValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -export const DuckDuckGoFormPartialSchema = { - top_n: z.string(), - channel: z.string(), -}; - -const FormSchema = z.object({ - query: z.string(), - ...DuckDuckGoFormPartialSchema, -}); - -export function DuckDuckGoWidgets() { - const { t } = useTranslate('flow'); - const form = useFormContext(); - - const options = useMemo(() => { - return Object.values(Channel).map((x) => ({ value: x, label: t(x) })); - }, [t]); - - return ( - <> - - ( - - {t('channel')} - - - - - - )} - /> - - ); -} - -const outputList = buildOutputList(initialDuckValues.outputs); - -function DuckDuckGoForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialDuckValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - -
    - -
    -
    - ); -} - -export default memo(DuckDuckGoForm); diff --git a/web/src/pages/data-flow/form/github-form/index.tsx b/web/src/pages/data-flow/form/github-form/index.tsx deleted file mode 100644 index 2e514a3e774..00000000000 --- a/web/src/pages/data-flow/form/github-form/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { TopNFormField } from '@/components/top-n-item'; -import { Form } from '@/components/ui/form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { initialGithubValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -export const FormSchema = z.object({ - query: z.string(), - top_n: z.number(), -}); - -const outputList = buildOutputList(initialGithubValues.outputs); - -function GithubForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialGithubValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - mode: 'onChange', - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - -
    - -
    -
    - ); -} - -export default memo(GithubForm); diff --git a/web/src/pages/data-flow/form/google-scholar-form/index.tsx b/web/src/pages/data-flow/form/google-scholar-form/index.tsx deleted file mode 100644 index 00f54056a1d..00000000000 --- a/web/src/pages/data-flow/form/google-scholar-form/index.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Switch } from '@/components/ui/switch'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { DatePicker, DatePickerProps } from 'antd'; -import dayjs from 'dayjs'; -import { memo, useCallback, useMemo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialGoogleScholarValues } from '../../constant'; -import { useBuildSortOptions } from '../../form-hooks'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -// TODO: To be replaced -const YearPicker = ({ - onChange, - value, -}: { - onChange?: (val: number | undefined) => void; - value?: number | undefined; -}) => { - const handleChange: DatePickerProps['onChange'] = useCallback( - (val: any) => { - const nextVal = val?.format('YYYY'); - onChange?.(nextVal ? Number(nextVal) : undefined); - }, - [onChange], - ); - // The year needs to be converted into a number and saved to the backend - const nextValue = useMemo(() => { - if (value) { - return dayjs(value.toString()); - } - return undefined; - }, [value]); - - return ; -}; - -export function GoogleScholarFormWidgets() { - const form = useFormContext(); - const { t } = useTranslate('flow'); - - const options = useBuildSortOptions(); - - return ( - <> - - ( - - {t('sortBy')} - - - - - - )} - /> - ( - - {t('yearLow')} - - - - - - )} - /> - ( - - {t('yearHigh')} - - - - - - )} - /> - ( - - {t('patents')} - - - - - - )} - /> - - ); -} - -export const GoogleScholarFormPartialSchema = { - top_n: z.number(), - sort_by: z.string(), - year_low: z.number(), - year_high: z.number(), - patents: z.boolean(), -}; - -export const FormSchema = z.object({ - ...GoogleScholarFormPartialSchema, - query: z.string(), -}); - -const outputList = buildOutputList(initialGoogleScholarValues.outputs); - -function GoogleScholarForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialGoogleScholarValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - -
    - -
    -
    - ); -} - -export default memo(GoogleScholarForm); diff --git a/web/src/pages/data-flow/form/jin10-form/index.tsx b/web/src/pages/data-flow/form/jin10-form/index.tsx deleted file mode 100644 index 2bc6d774a38..00000000000 --- a/web/src/pages/data-flow/form/jin10-form/index.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { useTranslate } from '@/hooks/common-hooks'; -import { Form, Input, Select } from 'antd'; -import { useMemo } from 'react'; -import { IOperatorForm } from '../../interface'; -import { - Jin10CalendarDatashapeOptions, - Jin10CalendarTypeOptions, - Jin10FlashTypeOptions, - Jin10SymbolsDatatypeOptions, - Jin10SymbolsTypeOptions, - Jin10TypeOptions, -} from '../../options'; -import DynamicInputVariable from '../components/dynamic-input-variable'; - -const Jin10Form = ({ onValuesChange, form, node }: IOperatorForm) => { - const { t } = useTranslate('flow'); - - const jin10TypeOptions = useMemo(() => { - return Jin10TypeOptions.map((x) => ({ - value: x, - label: t(`jin10TypeOptions.${x}`), - })); - }, [t]); - - const jin10FlashTypeOptions = useMemo(() => { - return Jin10FlashTypeOptions.map((x) => ({ - value: x, - label: t(`jin10FlashTypeOptions.${x}`), - })); - }, [t]); - - const jin10CalendarTypeOptions = useMemo(() => { - return Jin10CalendarTypeOptions.map((x) => ({ - value: x, - label: t(`jin10CalendarTypeOptions.${x}`), - })); - }, [t]); - - const jin10CalendarDatashapeOptions = useMemo(() => { - return Jin10CalendarDatashapeOptions.map((x) => ({ - value: x, - label: t(`jin10CalendarDatashapeOptions.${x}`), - })); - }, [t]); - - const jin10SymbolsTypeOptions = useMemo(() => { - return Jin10SymbolsTypeOptions.map((x) => ({ - value: x, - label: t(`jin10SymbolsTypeOptions.${x}`), - })); - }, [t]); - - const jin10SymbolsDatatypeOptions = useMemo(() => { - return Jin10SymbolsDatatypeOptions.map((x) => ({ - value: x, - label: t(`jin10SymbolsDatatypeOptions.${x}`), - })); - }, [t]); - - return ( -
    - - - - - - - - - {({ getFieldValue }) => { - const type = getFieldValue('type'); - switch (type) { - case 'flash': - return ( - <> - - - - - - - - - - - ); - - case 'calendar': - return ( - <> - - - - - - - - ); - - case 'symbols': - return ( - <> - - - - - - - - ); - - case 'news': - return ( - <> - - - - - - - - ); - - default: - return <>; - } - }} - -
    - ); -}; - -export default Jin10Form; diff --git a/web/src/pages/data-flow/form/google-form/index.tsx b/web/src/pages/data-flow/form/parser-form/index.tsx similarity index 97% rename from web/src/pages/data-flow/form/google-form/index.tsx rename to web/src/pages/data-flow/form/parser-form/index.tsx index cec5ae4c732..3418d83a923 100644 --- a/web/src/pages/data-flow/form/google-form/index.tsx +++ b/web/src/pages/data-flow/form/parser-form/index.tsx @@ -11,6 +11,7 @@ import { } from '@/components/ui/form'; import { useTranslate } from '@/hooks/common-hooks'; import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; import { useForm, useFormContext } from 'react-hook-form'; import { z } from 'zod'; import { initialGoogleValues } from '../../constant'; @@ -81,7 +82,7 @@ export function GoogleFormWidgets() { ); } -const GoogleForm = ({ node }: INextOperatorForm) => { +const ParserForm = ({ node }: INextOperatorForm) => { const { t } = useTranslate('flow'); const defaultValues = useFormValues(initialGoogleValues, node); @@ -136,4 +137,4 @@ const GoogleForm = ({ node }: INextOperatorForm) => { ); }; -export default GoogleForm; +export default memo(ParserForm); diff --git a/web/src/pages/data-flow/form/pubmed-form/index.tsx b/web/src/pages/data-flow/form/pubmed-form/index.tsx deleted file mode 100644 index a2c35d55c6c..00000000000 --- a/web/src/pages/data-flow/form/pubmed-form/index.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialPubMedValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -export const PubMedFormPartialSchema = { - top_n: z.number(), - email: z.string().email(), -}; - -export const FormSchema = z.object({ - ...PubMedFormPartialSchema, - query: z.string(), -}); - -export function PubMedFormWidgets() { - const form = useFormContext(); - const { t } = useTranslate('flow'); - - return ( - <> - - ( - - {t('email')} - - - - - - )} - /> - - ); -} - -const outputList = buildOutputList(initialPubMedValues.outputs); - -function PubMedForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialPubMedValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - mode: 'onChange', - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - -
    - -
    -
    - ); -} - -export default memo(PubMedForm); diff --git a/web/src/pages/data-flow/form/qweather-form/index.tsx b/web/src/pages/data-flow/form/qweather-form/index.tsx deleted file mode 100644 index eee088762ad..00000000000 --- a/web/src/pages/data-flow/form/qweather-form/index.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { INextOperatorForm } from '../../interface'; -import { - QWeatherLangOptions, - QWeatherTimePeriodOptions, - QWeatherTypeOptions, - QWeatherUserTypeOptions, -} from '../../options'; -import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; - -enum FormFieldName { - Type = 'type', - UserType = 'user_type', -} - -const QWeatherForm = ({ form, node }: INextOperatorForm) => { - const { t } = useTranslation(); - const typeValue = form.watch(FormFieldName.Type); - - const qWeatherLangOptions = useMemo(() => { - return QWeatherLangOptions.map((x) => ({ - value: x, - label: t(`flow.qWeatherLangOptions.${x}`), - })); - }, [t]); - - const qWeatherTypeOptions = useMemo(() => { - return QWeatherTypeOptions.map((x) => ({ - value: x, - label: t(`flow.qWeatherTypeOptions.${x}`), - })); - }, [t]); - - const qWeatherUserTypeOptions = useMemo(() => { - return QWeatherUserTypeOptions.map((x) => ({ - value: x, - label: t(`flow.qWeatherUserTypeOptions.${x}`), - })); - }, [t]); - - const getQWeatherTimePeriodOptions = useCallback(() => { - let options = QWeatherTimePeriodOptions; - const userType = form.getValues(FormFieldName.UserType); - if (userType === 'free') { - options = options.slice(0, 3); - } - return options.map((x) => ({ - value: x, - label: t(`flow.qWeatherTimePeriodOptions.${x}`), - })); - }, [form, t]); - - return ( -
    - { - e.preventDefault(); - }} - > - - ( - - {t('flow.webApiKey')} - - - - - - )} - /> - ( - - {t('flow.lang')} - - - - - - )} - /> - ( - - {t('flow.type')} - - - - - - )} - /> - ( - - {t('flow.userType')} - - - - - - )} - /> - {typeValue === 'weather' && ( - ( - - {t('flow.timePeriod')} - - - - - - )} - /> - )} - - - ); -}; - -export default QWeatherForm; diff --git a/web/src/pages/data-flow/form/searxng-form/index.tsx b/web/src/pages/data-flow/form/searxng-form/index.tsx deleted file mode 100644 index 193e5d84e79..00000000000 --- a/web/src/pages/data-flow/form/searxng-form/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { initialSearXNGValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -const FormSchema = z.object({ - query: z.string(), - searxng_url: z.string().min(1), - top_n: z.string(), -}); - -const outputList = buildOutputList(initialSearXNGValues.outputs); - -function SearXNGForm({ node }: INextOperatorForm) { - const { t } = useTranslate('flow'); - const defaultValues = useFormValues(initialSearXNGValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - ( - - SearXNG URL - - - - - - )} - /> - - -
    - -
    -
    - ); -} - -export default memo(SearXNGForm); diff --git a/web/src/pages/data-flow/form/tavily-extract-form/index.tsx b/web/src/pages/data-flow/form/tavily-extract-form/index.tsx deleted file mode 100644 index 45a9e0fe718..00000000000 --- a/web/src/pages/data-flow/form/tavily-extract-form/index.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { buildOptions } from '@/utils/form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { t } from 'i18next'; -import { memo } from 'react'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { - TavilyExtractDepth, - TavilyExtractFormat, - initialTavilyExtractValues, -} from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { ApiKeyField } from '../components/api-key-field'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { PromptEditor } from '../components/prompt-editor'; -import { TavilyFormSchema } from '../tavily-form'; - -const outputList = buildOutputList(initialTavilyExtractValues.outputs); - -function TavilyExtractForm({ node }: INextOperatorForm) { - const values = useFormValues(initialTavilyExtractValues, node); - - const FormSchema = z.object({ - ...TavilyFormSchema, - urls: z.string(), - extract_depth: z.enum([ - TavilyExtractDepth.Advanced, - TavilyExtractDepth.Basic, - ]), - format: z.enum([TavilyExtractFormat.Text, TavilyExtractFormat.Markdown]), - }); - - const form = useForm>({ - defaultValues: values, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - ( - - URL - - - - - - )} - /> - ( - - {t('flow.extractDepth')} - - - - - - )} - /> - ( - - {t('flow.format')} - - - - - - )} - /> - - -
    - -
    -
    - ); -} - -export default memo(TavilyExtractForm); diff --git a/web/src/pages/data-flow/form/tavily-form/dynamic-domain.tsx b/web/src/pages/data-flow/form/tavily-form/dynamic-domain.tsx deleted file mode 100644 index 4c34dc946e5..00000000000 --- a/web/src/pages/data-flow/form/tavily-form/dynamic-domain.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { BlockButton, Button } from '@/components/ui/button'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { t } from 'i18next'; -import { X } from 'lucide-react'; -import { ReactNode } from 'react'; -import { useFieldArray, useFormContext } from 'react-hook-form'; - -type DynamicDomainProps = { name: string; label: ReactNode }; - -export const DynamicDomain = ({ name, label }: DynamicDomainProps) => { - const form = useFormContext(); - - const { fields, append, remove } = useFieldArray({ - name: name, - control: form.control, - }); - - return ( - - {label} -
    - {fields.map((field, index) => ( -
    -
    - ( - - - - - - )} - /> -
    - -
    - ))} -
    - - append({ value: '' })}> - {t('common.add')} - -
    - ); -}; diff --git a/web/src/pages/data-flow/form/tavily-form/index.tsx b/web/src/pages/data-flow/form/tavily-form/index.tsx deleted file mode 100644 index afa8d859b42..00000000000 --- a/web/src/pages/data-flow/form/tavily-form/index.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { Switch } from '@/components/ui/switch'; -import { buildOptions } from '@/utils/form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { t } from 'i18next'; -import { memo } from 'react'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { - TavilySearchDepth, - TavilyTopic, - initialTavilyValues, -} from '../../constant'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { ApiKeyField } from '../components/api-key-field'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; -import { DynamicDomain } from './dynamic-domain'; -import { useValues } from './use-values'; -import { useWatchFormChange } from './use-watch-change'; - -export const TavilyFormSchema = { - api_key: z.string(), -}; - -const outputList = buildOutputList(initialTavilyValues.outputs); - -function TavilyForm({ node }: INextOperatorForm) { - const values = useValues(node); - - const FormSchema = z.object({ - ...TavilyFormSchema, - query: z.string(), - search_depth: z.enum([TavilySearchDepth.Advanced, TavilySearchDepth.Basic]), - topic: z.enum([TavilyTopic.News, TavilyTopic.General]), - max_results: z.coerce.number(), - days: z.coerce.number(), - include_answer: z.boolean(), - include_raw_content: z.boolean(), - include_images: z.boolean(), - include_image_descriptions: z.boolean(), - include_domains: z.array(z.object({ value: z.any() })), // TODO: z.string should be used, but an error will be reported - exclude_domains: z.array(z.object({ value: z.any() })), - }); - - const form = useForm>({ - defaultValues: values, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - ( - - {t('flow.searchDepth')} - - - - - - )} - /> - ( - - {t('flow.tavilyTopic')} - - - - - - )} - /> - ( - - {t('flow.maxResults')} - - - - - - )} - /> - ( - - {t('flow.days')} - - - - - - )} - /> - ( - - {t('flow.includeAnswer')} - - - - - - )} - /> - ( - - {t('flow.includeRawContent')} - - - - - - )} - /> - ( - - {t('flow.includeImages')} - - - - - - )} - /> - ( - - {t('flow.includeImageDescriptions')} - - - - - - )} - /> - - - - -
    - -
    -
    - ); -} - -export default memo(TavilyForm); diff --git a/web/src/pages/data-flow/form/tavily-form/use-values.ts b/web/src/pages/data-flow/form/tavily-form/use-values.ts deleted file mode 100644 index 011e04005d2..00000000000 --- a/web/src/pages/data-flow/form/tavily-form/use-values.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { RAGFlowNodeType } from '@/interfaces/database/agent'; -import { isEmpty } from 'lodash'; -import { useMemo } from 'react'; -import { initialTavilyValues } from '../../constant'; -import { convertToObjectArray } from '../../utils'; - -export function useValues(node?: RAGFlowNodeType) { - const values = useMemo(() => { - const formData = node?.data?.form; - - if (isEmpty(formData)) { - return initialTavilyValues; - } - - return { - ...formData, - include_domains: convertToObjectArray(formData.include_domains), - exclude_domains: convertToObjectArray(formData.exclude_domains), - }; - }, [node?.data?.form]); - - return values; -} diff --git a/web/src/pages/data-flow/form/tavily-form/use-watch-change.ts b/web/src/pages/data-flow/form/tavily-form/use-watch-change.ts deleted file mode 100644 index cb24dce688d..00000000000 --- a/web/src/pages/data-flow/form/tavily-form/use-watch-change.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import useGraphStore from '../../store'; -import { convertToStringArray } from '../../utils'; - -export function useWatchFormChange(id?: string, form?: UseFormReturn) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - // Manually triggered form updates are synchronized to the canvas - if (id) { - values = form?.getValues(); - let nextValues: any = { - ...values, - include_domains: convertToStringArray(values.include_domains), - exclude_domains: convertToStringArray(values.exclude_domains), - }; - - updateNodeForm(id, nextValues); - } - }, [form?.formState.isDirty, id, updateNodeForm, values]); -} diff --git a/web/src/pages/data-flow/form/tushare-form/index.tsx b/web/src/pages/data-flow/form/tushare-form/index.tsx deleted file mode 100644 index a64bf25bf86..00000000000 --- a/web/src/pages/data-flow/form/tushare-form/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useTranslate } from '@/hooks/common-hooks'; -import { DatePicker, DatePickerProps, Form, Input, Select } from 'antd'; -import dayjs from 'dayjs'; -import { useCallback, useMemo } from 'react'; -import { IOperatorForm } from '../../interface'; -import { TuShareSrcOptions } from '../../options'; -import DynamicInputVariable from '../components/dynamic-input-variable'; - -const DateTimePicker = ({ - onChange, - value, -}: { - onChange?: (val: number | undefined) => void; - value?: number | undefined; -}) => { - const handleChange: DatePickerProps['onChange'] = useCallback( - (val: any) => { - const nextVal = val?.format('YYYY-MM-DD HH:mm:ss'); - onChange?.(nextVal ? nextVal : undefined); - }, - [onChange], - ); - // The value needs to be converted into a string and saved to the backend - const nextValue = useMemo(() => { - if (value) { - return dayjs(value); - } - return undefined; - }, [value]); - - return ( - - ); -}; - -const TuShareForm = ({ onValuesChange, form, node }: IOperatorForm) => { - const { t } = useTranslate('flow'); - - const tuShareSrcOptions = useMemo(() => { - return TuShareSrcOptions.map((x) => ({ - value: x, - label: t(`tuShareSrcOptions.${x}`), - })); - }, [t]); - - return ( -
    - - - - - - - - - - - - - - - - -
    - ); -}; - -export default TuShareForm; diff --git a/web/src/pages/data-flow/form/wencai-form/index.tsx b/web/src/pages/data-flow/form/wencai-form/index.tsx deleted file mode 100644 index a4e668ed97c..00000000000 --- a/web/src/pages/data-flow/form/wencai-form/index.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useMemo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; -import { initialWenCaiValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { WenCaiQueryTypeOptions } from '../../options'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -export const WenCaiPartialSchema = { - top_n: z.number(), - query_type: z.string(), -}; - -export const FormSchema = z.object({ - ...WenCaiPartialSchema, - query: z.string(), -}); - -export function WenCaiFormWidgets() { - const { t } = useTranslation(); - const form = useFormContext(); - - const wenCaiQueryTypeOptions = useMemo(() => { - return WenCaiQueryTypeOptions.map((x) => ({ - value: x, - label: t(`flow.wenCaiQueryTypeOptions.${x}`), - })); - }, [t]); - - return ( - <> - - ( - - {t('flow.queryType')} - - - - - - )} - /> - - ); -} - -const outputList = buildOutputList(initialWenCaiValues.outputs); - -function WenCaiForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialWenCaiValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - -
    - -
    -
    - ); -} - -export default memo(WenCaiForm); diff --git a/web/src/pages/data-flow/form/wikipedia-form/index.tsx b/web/src/pages/data-flow/form/wikipedia-form/index.tsx deleted file mode 100644 index 1603d802e35..00000000000 --- a/web/src/pages/data-flow/form/wikipedia-form/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialWikipediaValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { LanguageOptions } from '../../options'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -export const WikipediaFormPartialSchema = { - top_n: z.string(), - language: z.string(), -}; - -const FormSchema = z.object({ - query: z.string(), - ...WikipediaFormPartialSchema, -}); - -export function WikipediaFormWidgets() { - const { t } = useTranslate('common'); - const form = useFormContext(); - - return ( - <> - - ( - - {t('language')} - - - - - - )} - /> - - ); -} - -const outputList = buildOutputList(initialWikipediaValues.outputs); - -function WikipediaForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialWikipediaValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - -
    - -
    -
    - ); -} - -export default memo(WikipediaForm); diff --git a/web/src/pages/data-flow/form/yahoo-finance-form/index.tsx b/web/src/pages/data-flow/form/yahoo-finance-form/index.tsx deleted file mode 100644 index 68a34836b1b..00000000000 --- a/web/src/pages/data-flow/form/yahoo-finance-form/index.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Switch } from '@/components/ui/switch'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { ReactNode } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialYahooFinanceValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -export const YahooFinanceFormPartialSchema = { - info: z.boolean(), - history: z.boolean(), - financials: z.boolean(), - balance_sheet: z.boolean(), - cash_flow_statement: z.boolean(), - news: z.boolean(), -}; - -const FormSchema = z.object({ - stock_code: z.string(), - ...YahooFinanceFormPartialSchema, -}); - -interface SwitchFormFieldProps { - name: string; - label: ReactNode; -} -function SwitchFormField({ name, label }: SwitchFormFieldProps) { - const form = useFormContext(); - - return ( - ( - - {label} - - - - - - )} - /> - ); -} - -export function YahooFinanceFormWidgets() { - const { t } = useTranslate('flow'); - return ( - <> - - - - - - - - - - ); -} - -const outputList = buildOutputList(initialYahooFinanceValues.outputs); - -const YahooFinanceForm = ({ node }: INextOperatorForm) => { - const { t } = useTranslate('flow'); - - const defaultValues = useFormValues(initialYahooFinanceValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - -
    - -
    -
    - ); -}; - -export default YahooFinanceForm; diff --git a/web/src/pages/data-flow/hooks.tsx b/web/src/pages/data-flow/hooks.tsx index 5d153762307..779ba092862 100644 --- a/web/src/pages/data-flow/hooks.tsx +++ b/web/src/pages/data-flow/hooks.tsx @@ -18,7 +18,6 @@ import { initialAkShareValues, initialArXivValues, initialBaiduFanyiValues, - initialBaiduValues, initialBeginValues, initialBingValues, initialCategorizeValues, @@ -103,7 +102,6 @@ export const useInitializeOperatorParams = () => { llm_id: llmId, }, [Operator.DuckDuckGo]: initialDuckValues, - [Operator.Baidu]: initialBaiduValues, [Operator.Wikipedia]: initialWikipediaValues, [Operator.PubMed]: initialPubMedValues, [Operator.ArXiv]: initialArXivValues, diff --git a/web/src/pages/data-flow/hooks/use-add-node.ts b/web/src/pages/data-flow/hooks/use-add-node.ts index 625eeb5cded..e3dad598e0c 100644 --- a/web/src/pages/data-flow/hooks/use-add-node.ts +++ b/web/src/pages/data-flow/hooks/use-add-node.ts @@ -13,7 +13,6 @@ import { initialAkShareValues, initialArXivValues, initialBaiduFanyiValues, - initialBaiduValues, initialBeginValues, initialBingValues, initialCategorizeValues, @@ -82,7 +81,6 @@ export const useInitializeOperatorParams = () => { llm_id: llmId, }, [Operator.DuckDuckGo]: initialDuckValues, - [Operator.Baidu]: initialBaiduValues, [Operator.Wikipedia]: initialWikipediaValues, [Operator.PubMed]: initialPubMedValues, [Operator.ArXiv]: initialArXivValues, diff --git a/web/src/pages/data-flow/hooks/use-send-shared-message.ts b/web/src/pages/data-flow/hooks/use-send-shared-message.ts deleted file mode 100644 index d8e2d61de31..00000000000 --- a/web/src/pages/data-flow/hooks/use-send-shared-message.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { SharedFrom } from '@/constants/chat'; -import { useSetModalState } from '@/hooks/common-hooks'; -import { IEventList } from '@/hooks/use-send-message'; -import { - buildRequestBody, - useSendAgentMessage, -} from '@/pages/agent/chat/use-send-agent-message'; -import trim from 'lodash/trim'; -import { useCallback, useState } from 'react'; -import { useSearchParams } from 'umi'; - -export const useSendButtonDisabled = (value: string) => { - return trim(value) === ''; -}; - -export const useGetSharedChatSearchParams = () => { - const [searchParams] = useSearchParams(); - const data_prefix = 'data_'; - const data = Object.fromEntries( - searchParams - .entries() - .filter(([key]) => key.startsWith(data_prefix)) - .map(([key, value]) => [key.replace(data_prefix, ''), value]), - ); - return { - from: searchParams.get('from') as SharedFrom, - sharedId: searchParams.get('shared_id'), - locale: searchParams.get('locale'), - data: data, - visibleAvatar: searchParams.get('visible_avatar') - ? searchParams.get('visible_avatar') !== '1' - : true, - }; -}; - -export const useSendNextSharedMessage = ( - addEventList: (data: IEventList, messageId: string) => void, - isTaskMode: boolean, -) => { - const { from, sharedId: conversationId } = useGetSharedChatSearchParams(); - const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`; - - const [params, setParams] = useState([]); - - const { - visible: parameterDialogVisible, - hideModal: hideParameterDialog, - showModal: showParameterDialog, - } = useSetModalState(); - - const ret = useSendAgentMessage(url, addEventList, params, true); - - const ok = useCallback( - (params: any[]) => { - if (isTaskMode) { - const msgBody = buildRequestBody(''); - - ret.sendMessage({ - message: msgBody, - beginInputs: params, - }); - } else { - setParams(params); - } - - hideParameterDialog(); - }, - [hideParameterDialog, isTaskMode, ret], - ); - - return { - ...ret, - hasError: false, - parameterDialogVisible, - hideParameterDialog, - showParameterDialog, - ok, - }; -}; diff --git a/web/src/pages/data-flow/share/index.tsx b/web/src/pages/data-flow/share/index.tsx deleted file mode 100644 index 26cdb02486e..00000000000 --- a/web/src/pages/data-flow/share/index.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { EmbedContainer } from '@/components/embed-container'; -import { FileUploadProps } from '@/components/file-upload'; -import { NextMessageInput } from '@/components/message-input/next'; -import MessageItem from '@/components/next-message-item'; -import PdfDrawer from '@/components/pdf-drawer'; -import { useClickDrawer } from '@/components/pdf-drawer/hooks'; -import { MessageType } from '@/constants/chat'; -import { - useFetchExternalAgentInputs, - useUploadCanvasFileWithProgress, -} from '@/hooks/use-agent-request'; -import { cn } from '@/lib/utils'; -import i18n from '@/locales/config'; -import DebugContent from '@/pages/agent/debug-content'; -import { useCacheChatLog } from '@/pages/agent/hooks/use-cache-chat-log'; -import { useAwaitCompentData } from '@/pages/agent/hooks/use-chat-logic'; -import { useSendButtonDisabled } from '@/pages/chat/hooks'; -import { buildMessageUuidWithRole } from '@/utils/chat'; -import { isEmpty } from 'lodash'; -import React, { forwardRef, useCallback } from 'react'; -import { AgentDialogueMode } from '../constant'; -import { - useGetSharedChatSearchParams, - useSendNextSharedMessage, -} from '../hooks/use-send-shared-message'; -import { ParameterDialog } from './parameter-dialog'; - -const ChatContainer = () => { - const { - sharedId: conversationId, - locale, - visibleAvatar, - } = useGetSharedChatSearchParams(); - const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = - useClickDrawer(); - - const { uploadCanvasFile, loading } = - useUploadCanvasFileWithProgress(conversationId); - const { - addEventList, - setCurrentMessageId, - currentEventListWithoutMessageById, - clearEventList, - } = useCacheChatLog(); - - const { data: inputsData } = useFetchExternalAgentInputs(); - const isTaskMode = inputsData.mode === AgentDialogueMode.Task; - - const { - handlePressEnter, - handleInputChange, - value, - sendLoading, - scrollRef, - messageContainerRef, - derivedMessages, - hasError, - stopOutputMessage, - findReferenceByMessageId, - appendUploadResponseList, - parameterDialogVisible, - showParameterDialog, - sendFormMessage, - addNewestOneAnswer, - ok, - resetSession, - } = useSendNextSharedMessage(addEventList, isTaskMode); - const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({ - derivedMessages, - sendFormMessage, - canvasId: conversationId as string, - }); - const sendDisabled = useSendButtonDisabled(value); - - const handleUploadFile: NonNullable = - useCallback( - async (files, options) => { - const ret = await uploadCanvasFile({ files, options }); - appendUploadResponseList(ret.data, files); - }, - [appendUploadResponseList, uploadCanvasFile], - ); - - React.useEffect(() => { - if (locale && i18n.language !== locale) { - i18n.changeLanguage(locale); - } - }, [locale, visibleAvatar]); - - React.useEffect(() => { - if (!isTaskMode && inputsData.prologue) { - addNewestOneAnswer({ - answer: inputsData.prologue, - }); - } - }, [inputsData.prologue, addNewestOneAnswer, isTaskMode]); - - React.useEffect(() => { - if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) { - showParameterDialog(); - } - }, [inputsData, showParameterDialog]); - - const handleInputsModalOk = (params: any[]) => { - ok(params); - }; - const handleReset = () => { - resetSession(); - clearEventList(); - }; - if (!conversationId) { - return
    empty
    ; - } - return ( - <> - -
    -
    -
    - {derivedMessages?.map((message, i) => { - return ( - - {message.role === MessageType.Assistant && - derivedMessages.length - 1 === i && ( - - )} - {message.role === MessageType.Assistant && - derivedMessages.length - 1 !== i && ( -
    -
    {message?.data?.tips}
    - -
    - {buildInputList(message)?.map((item) => item.value)} -
    -
    - )} -
    - ); - })} -
    -
    -
    - {isTaskMode || ( -
    -
    - -
    -
    - )} -
    - - {visible && ( - - )} - {parameterDialogVisible && ( - - )} - - ); -}; - -export default forwardRef(ChatContainer); diff --git a/web/src/pages/data-flow/share/parameter-dialog.tsx b/web/src/pages/data-flow/share/parameter-dialog.tsx deleted file mode 100644 index 7b363836e52..00000000000 --- a/web/src/pages/data-flow/share/parameter-dialog.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Modal } from '@/components/ui/modal/modal'; -import { IModalProps } from '@/interfaces/common'; -import DebugContent from '@/pages/agent/debug-content'; -import { buildBeginInputListFromObject } from '@/pages/agent/form/begin-form/utils'; -import { BeginQuery } from '@/pages/agent/interface'; - -interface IProps extends IModalProps { - ok(parameters: any[]): void; - data: Record>; -} -export function ParameterDialog({ ok, data }: IProps) { - return ( - -
    - -
    -
    - ); -} From f48aed6d4a03816cc29a36fb1922c719b735d293 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 8 Sep 2025 12:48:58 +0800 Subject: [PATCH 0636/1187] Fix: The files in the knowledge base folder on the file management page should not be deleted #9975 (#9976) ### What problem does this PR solve? Fix: The files in the knowledge base folder on the file management page should not be deleted #9975 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/pages/files/action-cell.tsx | 86 +++++++++++++++++------------ web/src/pages/files/files-table.tsx | 7 ++- web/src/pages/files/util.ts | 4 ++ 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/web/src/pages/files/action-cell.tsx b/web/src/pages/files/action-cell.tsx index b50140eddd1..e477ed19817 100644 --- a/web/src/pages/files/action-cell.tsx +++ b/web/src/pages/files/action-cell.tsx @@ -21,8 +21,9 @@ import { UseHandleConnectToKnowledgeReturnType, UseRenameCurrentFileReturnType, } from './hooks'; +import { useHandleDeleteFile } from './use-delete-file'; import { UseMoveDocumentShowType } from './use-move-file'; -import { isFolderType } from './util'; +import { isFolderType, isKnowledgeBaseType } from './util'; type IProps = Pick, 'row'> & Pick & @@ -40,6 +41,7 @@ export function ActionCell({ const { downloadFile } = useDownloadFile(); const isFolder = isFolderType(record.type); const extension = getExtension(record.name); + const isKnowledgeBase = isKnowledgeBaseType(record.source_type); const handleShowConnectToKnowledgeModal = useCallback(() => { showConnectToKnowledgeModal(record); @@ -60,34 +62,44 @@ export function ActionCell({ showMoveFileModal([record.id]); }, [record, showMoveFileModal]); - return ( -
    - - + const { handleRemoveFile } = useHandleDeleteFile(); - + const onRemoveFile = useCallback(() => { + handleRemoveFile([documentId]); + }, [handleRemoveFile, documentId]); + return ( +
    + {isKnowledgeBase || ( + + )} + {isKnowledgeBase || ( + + )} + {isKnowledgeBase || ( + + )} {isFolder || ( - + {isKnowledgeBase || ( + + + + )}
    ); } diff --git a/web/src/pages/files/files-table.tsx b/web/src/pages/files/files-table.tsx index 5ea00dab9cd..e40d87fed61 100644 --- a/web/src/pages/files/files-table.tsx +++ b/web/src/pages/files/files-table.tsx @@ -48,7 +48,7 @@ import { KnowledgeCell } from './knowledge-cell'; import { LinkToDatasetDialog } from './link-to-dataset-dialog'; import { UseMoveDocumentShowType } from './use-move-file'; import { useNavigateToOtherFolder } from './use-navigate-to-folder'; -import { isFolderType } from './util'; +import { isFolderType, isKnowledgeBaseType } from './util'; type FilesTableProps = Pick< ReturnType, @@ -112,6 +112,7 @@ export function FilesTable({ checked={row.getIsSelected()} onCheckedChange={(value) => row.toggleSelected(!!value)} aria-label="Select row" + disabled={!row.getCanSelect()} /> ), enableSorting: false, @@ -247,7 +248,9 @@ export function FilesTable({ onRowSelectionChange: setRowSelection, manualPagination: true, //we're doing manual "server-side" pagination - + enableRowSelection(row) { + return !isKnowledgeBaseType(row.original.source_type); + }, state: { sorting, columnFilters, diff --git a/web/src/pages/files/util.ts b/web/src/pages/files/util.ts index 3ae478a1167..2bd9575ee41 100644 --- a/web/src/pages/files/util.ts +++ b/web/src/pages/files/util.ts @@ -1,3 +1,7 @@ export function isFolderType(type: string) { return type === 'folder'; } + +export function isKnowledgeBaseType(sourceType: string) { + return sourceType === 'knowledgebase'; +} From cf182317130438109e84004a7dd62fbd8bff5189 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Mon, 8 Sep 2025 12:49:12 +0800 Subject: [PATCH 0637/1187] Fix: Optimized the test results page layout and internationalization #3221 (#9974) ### What problem does this PR solve? Fix: Optimized the test results page layout and internationalization - Added an empty data component for when test results are empty - Optimized internationalization support for the paging component - Updated the layout and style of the test results page - Added a tooltip for when test results are empty ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/empty/empty.tsx | 77 +++++++++++++++++++ web/src/components/ui/ragflow-pagination.tsx | 9 ++- web/src/hooks/use-knowledge-request.ts | 4 +- web/src/interfaces/database/knowledge.ts | 1 + web/src/locales/en.ts | 9 +++ web/src/locales/zh.ts | 7 ++ .../dataset/dataset/parsing-status-cell.tsx | 2 +- web/src/pages/dataset/testing/index.tsx | 5 +- .../pages/dataset/testing/testing-result.tsx | 53 +++++++++---- web/src/pages/next-search/search-view.tsx | 2 +- 10 files changed, 149 insertions(+), 20 deletions(-) create mode 100644 web/src/components/empty/empty.tsx diff --git a/web/src/components/empty/empty.tsx b/web/src/components/empty/empty.tsx new file mode 100644 index 00000000000..b32d25c5c2a --- /dev/null +++ b/web/src/components/empty/empty.tsx @@ -0,0 +1,77 @@ +import { cn } from '@/lib/utils'; +import { t } from 'i18next'; + +type EmptyProps = { + className?: string; + children?: React.ReactNode; +}; + +const EmptyIcon = () => ( + + {t('common.noData')} + + + + + + + + + + + + + + + +); + +const Empty = (props: EmptyProps) => { + const { className, children } = props; + return ( +
    + + {!children && ( +
    + {t('common.noData')} +
    + )} + {children} +
    + ); +}; + +export default Empty; diff --git a/web/src/components/ui/ragflow-pagination.tsx b/web/src/components/ui/ragflow-pagination.tsx index 3ef7749bece..0eb01355614 100644 --- a/web/src/components/ui/ragflow-pagination.tsx +++ b/web/src/components/ui/ragflow-pagination.tsx @@ -9,6 +9,7 @@ import { } from '@/components/ui/pagination'; import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select'; import { cn } from '@/lib/utils'; +import { t } from 'i18next'; import { useCallback, useEffect, useMemo, useState } from 'react'; export type RAGFlowPaginationType = { @@ -32,7 +33,11 @@ export function RAGFlowPagination({ const sizeChangerOptions: RAGFlowSelectOptionType[] = useMemo(() => { return [10, 20, 50, 100].map((x) => ({ - label: {x} / page, + label: ( + + {x} / {t('pagination.page')} + + ), value: x.toString(), })); }, []); @@ -134,7 +139,7 @@ export function RAGFlowPagination({ return (
    - Total {total} + {t('pagination.total', { total: total })} diff --git a/web/src/hooks/use-knowledge-request.ts b/web/src/hooks/use-knowledge-request.ts index cda325ccb4e..3251afe79f8 100644 --- a/web/src/hooks/use-knowledge-request.ts +++ b/web/src/hooks/use-knowledge-request.ts @@ -72,12 +72,14 @@ export const useTestRetrieval = () => { chunks: [], doc_aggs: [], total: 0, + isRuned: false, }, enabled: false, gcTime: 0, queryFn: async () => { const { data } = await kbService.retrieval_test(queryParams); - return data?.data ?? {}; + const result = data?.data ?? {}; + return { ...result, isRuned: true }; }, }); diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index 011f8a9eb71..0d8fc025eec 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -150,6 +150,7 @@ export interface INextTestingResult { doc_aggs: ITestingDocument[]; total: number; labels?: Record; + isRuned?: boolean; } export type IRenameTag = { fromTag: string; toTag: string }; diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index b94d1c9c56d..b22a9112779 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -46,6 +46,7 @@ export default { remove: 'Remove', search: 'Search', noDataFound: 'No data found.', + noData: 'No data', promptPlaceholder: `Please input or use / to quickly insert variables.`, mcp: { namePlaceholder: 'My MCP Server', @@ -138,6 +139,10 @@ export default { processBeginAt: 'Begin at', processDuration: 'Duration', progressMsg: 'Progress', + noTestResultsForRuned: + 'No relevant results found. Try adjusting your query or parameters.', + noTestResultsForNotRuned: + 'No test has been run yet. Results will appear here.', testingDescription: 'Conduct a retrieval test to check if RAGFlow can recover the intended content for the LLM. If you have adjusted the default settings, such as keyword similarity weight or similarity threshold, to achieve the optimal results, be aware that these changes will not be automatically saved. You must apply them to your chat assistant settings or the Retrieval agent component settings.', similarityThreshold: 'Similarity threshold', @@ -1579,5 +1584,9 @@ This delimiter is used to split the input text into several text pieces echo of korean: 'Korean', vietnamese: 'Vietnamese', }, + pagination: { + total: 'Total {{total}}', + page: 'Page', + }, }, }; diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index bfae19a6b68..b8084fda78a 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -45,6 +45,7 @@ export default { remove: '移除', search: '搜索', noDataFound: '没有找到数据。', + noData: '暂无数据', promptPlaceholder: '请输入或使用 / 快速插入变量。', }, login: { @@ -129,6 +130,8 @@ export default { processBeginAt: '开始于', processDuration: '持续时间', progressMsg: '进度', + noTestResultsForRuned: '未找到相关结果,请尝试调整查询语句或参数', + noTestResultsForNotRuned: '尚未运行测试,结果会显示在这里', testingDescription: '请完成召回测试:确保你的配置可以从数据库召回正确的文本块。如果你调整了这里的默认设置,比如关键词相似度权重,请注意这里的改动不会被自动保存。请务必在聊天助手设置或者召回算子设置处同步更新相关设置。', similarityThreshold: '相似度阈值', @@ -1493,5 +1496,9 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 korean: '韩语', vietnamese: '越南语', }, + pagination: { + total: '总共 {{total}} 条', + page: '页', + }, }, }; diff --git a/web/src/pages/dataset/dataset/parsing-status-cell.tsx b/web/src/pages/dataset/dataset/parsing-status-cell.tsx index 2b3abd66eaa..8ada0b09d0c 100644 --- a/web/src/pages/dataset/dataset/parsing-status-cell.tsx +++ b/web/src/pages/dataset/dataset/parsing-status-cell.tsx @@ -67,7 +67,7 @@ export function ParsingStatusCell({ return (
    -
    +
    */}
    @@ -51,6 +51,7 @@ export default function RetrievalTesting() { ; export function TestingResult({ @@ -45,6 +47,7 @@ export function TestingResult({ handleFilterSubmit, page, pageSize, + loading, onPaginationChange, data, }: TestingResultProps) { @@ -77,20 +80,42 @@ export function TestingResult({
    -
    - {data.chunks?.map((x) => ( - - -

    {x.content_with_weight}

    -
    - ))} -
    - + {data.chunks?.length > 0 && !loading && ( + <> +
    + {data.chunks?.map((x) => ( + + +

    {x.content_with_weight}

    +
    + ))} +
    + + + )} + {!data.chunks?.length && !loading && ( +
    +
    + + {data.isRuned && ( +
    + {t('knowledgeDetails.noTestResultsForRuned')} +
    + )} + {!data.isRuned && ( +
    + {t('knowledgeDetails.noTestResultsForNotRuned')} +
    + )} +
    +
    +
    + )}
    ); } diff --git a/web/src/pages/next-search/search-view.tsx b/web/src/pages/next-search/search-view.tsx index 6c69dace3e5..fe1bb0d7f36 100644 --- a/web/src/pages/next-search/search-view.tsx +++ b/web/src/pages/next-search/search-view.tsx @@ -262,7 +262,7 @@ export default function SearchingView({
    {total > 0 && ( -
    +
    Date: Mon, 8 Sep 2025 14:05:01 +0800 Subject: [PATCH 0638/1187] Feat: user defined prompt. (#9972) ### What problem does this PR solve? ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- agent/canvas.py | 5 ++- agent/component/agent_with_tools.py | 24 ++++++------ agent/component/llm.py | 23 ++++++++--- api/apps/canvas_app.py | 13 +++++++ api/apps/conversation_app.py | 2 + rag/nlp/__init__.py | 14 ++++++- rag/prompts/analyze_task_system.md | 52 ++++++++++++++++++++++--- rag/prompts/analyze_task_user.md | 32 +++++----------- rag/prompts/next_step.md | 35 +++++++++++++++-- rag/prompts/prompts.py | 12 +++--- rag/prompts/reflect.md | 59 ++++++++++++++++++++++++----- 11 files changed, 204 insertions(+), 67 deletions(-) diff --git a/agent/canvas.py b/agent/canvas.py index 6c99b4d8e1c..ffa67c73d14 100644 --- a/agent/canvas.py +++ b/agent/canvas.py @@ -16,6 +16,7 @@ import base64 import json import logging +import re import time from concurrent.futures import ThreadPoolExecutor from copy import deepcopy @@ -300,9 +301,11 @@ def _node_finished(cpn_obj): yield decorate("message", {"content": m}) _m += m cpn_obj.set_output("content", _m) + cite = re.search(r"\[ID:[ 0-9]+\]", _m) else: yield decorate("message", {"content": cpn_obj.output("content")}) - yield decorate("message_end", {"reference": self.get_reference()}) + cite = re.search(r"\[ID:[ 0-9]+\]", cpn_obj.output("content")) + yield decorate("message_end", {"reference": self.get_reference() if cite else None}) while partials: _cpn_obj = self.get_component_obj(partials[0]) diff --git a/agent/component/agent_with_tools.py b/agent/component/agent_with_tools.py index 52a6900ec6a..4019c89ae85 100644 --- a/agent/component/agent_with_tools.py +++ b/agent/component/agent_with_tools.py @@ -155,12 +155,12 @@ def _invoke(self, **kwargs): if not self.tools: return LLM._invoke(self, **kwargs) - prompt, msg = self._prepare_prompt_variables() + prompt, msg, user_defined_prompt = self._prepare_prompt_variables() 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 self._param.output_structure and not (ex and ex["goto"]): - self.set_output("content", partial(self.stream_output_with_tools, prompt, msg)) + 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)) @@ -182,11 +182,11 @@ def _invoke(self, **kwargs): self.set_output("use_tools", use_tools) return ans - def stream_output_with_tools(self, prompt, msg): + 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): + for delta_ans,_ in self._react_with_tools_streamly(prompt, msg, use_tools, user_defined_prompt): if delta_ans.find("**ERROR**") >= 0: if self.get_exception_default_value(): self.set_output("content", self.get_exception_default_value()) @@ -209,7 +209,7 @@ def _gen_citations(self, text): ]): yield delta_ans - def _react_with_tools_streamly(self, prompt, history: list[dict], use_tools): + def _react_with_tools_streamly(self, prompt, history: list[dict], use_tools, user_defined_prompt={}): token_count = 0 tool_metas = self.tool_meta hist = deepcopy(history) @@ -230,7 +230,7 @@ def use_tool(name, args): # 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"]))) + # 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 tool_response = self.toolcall_session.tool_call(name, args) use_tools.append({ @@ -239,7 +239,7 @@ def use_tool(name, args): "results": tool_response }) # self.callback("add_memory", {}, "...") - #self.add_memory(hist[-2]["content"], hist[-1]["content"], name, args, str(tool_response)) + #self.add_memory(hist[-2]["content"], hist[-1]["content"], name, args, str(tool_response), user_defined_prompt) return name, tool_response @@ -279,10 +279,10 @@ def append_user_content(hist, content): hist.append({"role": "user", "content": content}) st = timer() - task_desc = analyze_task(self.chat_mdl, prompt, user_request, tool_metas) + task_desc = analyze_task(self.chat_mdl, prompt, user_request, tool_metas, user_defined_prompt) self.callback("analyze_task", {}, task_desc, elapsed_time=timer()-st) for _ in range(self._param.max_rounds + 1): - response, tk = next_step(self.chat_mdl, hist, tool_metas, task_desc) + response, tk = next_step(self.chat_mdl, hist, tool_metas, task_desc, user_defined_prompt) # self.callback("next_step", {}, str(response)[:256]+"...") token_count += tk hist.append({"role": "assistant", "content": response}) @@ -307,7 +307,7 @@ def append_user_content(hist, content): thr.append(executor.submit(use_tool, name, args)) st = timer() - reflection = reflect(self.chat_mdl, hist, [th.result() for th in thr]) + reflection = reflect(self.chat_mdl, hist, [th.result() for th in thr], user_defined_prompt) append_user_content(hist, reflection) self.callback("reflection", {}, str(reflection), elapsed_time=timer()-st) @@ -334,10 +334,10 @@ def append_user_content(hist, content): for txt, tkcnt in complete(): yield txt, tkcnt - def get_useful_memory(self, goal: str, sub_goal:str, topn=3) -> str: + def get_useful_memory(self, goal: str, sub_goal:str, topn=3, user_defined_prompt:dict={}) -> str: # self.callback("get_useful_memory", {"topn": 3}, "...") mems = self._canvas.get_memory() - rank = rank_memories(self.chat_mdl, goal, sub_goal, [summ for (user, assist, summ) in mems]) + rank = rank_memories(self.chat_mdl, goal, sub_goal, [summ for (user, assist, summ) in mems], user_defined_prompt) try: rank = json_repair.loads(re.sub(r"```.*", "", rank))[:topn] mems = [mems[r] for r in rank] diff --git a/agent/component/llm.py b/agent/component/llm.py index f02d957a425..7f1f2eb361e 100644 --- a/agent/component/llm.py +++ b/agent/component/llm.py @@ -145,12 +145,23 @@ def _prepare_prompt_variables(self): msg.append(deepcopy(p)) sys_prompt = self.string_format(sys_prompt, args) + user_defined_prompt, sys_prompt = self._extract_prompts(sys_prompt) for m in msg: m["content"] = self.string_format(m["content"], args) if self._param.cite and self._canvas.get_reference()["chunks"]: - sys_prompt += citation_prompt() + sys_prompt += citation_prompt(user_defined_prompt) - return sys_prompt, msg + return sys_prompt, msg, user_defined_prompt + + def _extract_prompts(self, sys_prompt): + pts = {} + for tag in ["TASK_ANALYSIS", "PLAN_GENERATION", "REFLECTION", "CONTEXT_SUMMARY", "CONTEXT_RANKING", "CITATION_GUIDELINES"]: + r = re.search(rf"<{tag}>(.*?)", sys_prompt, flags=re.DOTALL|re.IGNORECASE) + if not r: + continue + pts[tag.lower()] = r.group(1) + sys_prompt = re.sub(rf"<{tag}>(.*?)", sys_prompt, flags=re.DOTALL|re.IGNORECASE) + return pts, sys_prompt def _generate(self, msg:list[dict], **kwargs) -> str: if not self.imgs: @@ -198,7 +209,7 @@ def clean_formated_answer(ans: str) -> str: ans = re.sub(r"^.*```json", "", ans, flags=re.DOTALL) return re.sub(r"```\n*$", "", ans, flags=re.DOTALL) - prompt, msg = self._prepare_prompt_variables() + prompt, msg, _ = self._prepare_prompt_variables() error = "" if self._param.output_structure: @@ -262,11 +273,11 @@ def _stream_output(self, prompt, msg): answer += ans self.set_output("content", answer) - def add_memory(self, user:str, assist:str, func_name: str, params: dict, results: str): - summ = tool_call_summary(self.chat_mdl, func_name, params, results) + 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) logging.info(f"[MEMORY]: {summ}") self._canvas.add_memory(user, assist, summ) def thoughts(self) -> str: - _, msg = self._prepare_prompt_variables() + _, msg,_ = self._prepare_prompt_variables() return "⌛Give me a moment—starting from: \n\n" + re.sub(r"(User's query:|[\\]+)", '', msg[-1]['content'], flags=re.DOTALL) + "\n\nI’ll figure out our best next move." \ No newline at end of file diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py index a220af096e0..61a76211c98 100644 --- a/api/apps/canvas_app.py +++ b/api/apps/canvas_app.py @@ -470,3 +470,16 @@ def sessions(canvas_id): except Exception as e: return server_error_response(e) + +@manager.route('/prompts', methods=['GET']) # noqa: F821 +@login_required +def prompts(): + from rag.prompts.prompts import ANALYZE_TASK_SYSTEM, ANALYZE_TASK_USER, NEXT_STEP, REFLECT, SUMMARY4MEMORY, RANK_MEMORY, CITATION_PROMPT_TEMPLATE + return get_json_result(data={ + "task_analysis": ANALYZE_TASK_SYSTEM + ANALYZE_TASK_USER, + "plan_generation": NEXT_STEP, + "reflection": REFLECT, + "context_summary": SUMMARY4MEMORY, + "context_ranking": RANK_MEMORY, + "citation_guidelines": CITATION_PROMPT_TEMPLATE + }) diff --git a/api/apps/conversation_app.py b/api/apps/conversation_app.py index 790172f779d..9ef4d645352 100644 --- a/api/apps/conversation_app.py +++ b/api/apps/conversation_app.py @@ -400,6 +400,8 @@ def related_questions(): chat_mdl = LLMBundle(current_user.id, LLMType.CHAT, chat_id) gen_conf = search_config.get("llm_setting", {"temperature": 0.9}) + if "parameter" in gen_conf: + del gen_conf["parameter"] prompt = load_prompt("related_question") ans = chat_mdl.chat( prompt, diff --git a/rag/nlp/__init__.py b/rag/nlp/__init__.py index 2b283168497..fc548ee61c2 100644 --- a/rag/nlp/__init__.py +++ b/rag/nlp/__init__.py @@ -687,8 +687,20 @@ def add_chunk(t, image, pos=""): tk_nums[-1] += tnum dels = get_delimiters(delimiter) + line = "" for sec, image in sections: - split_sec = re.split(r"(%s)" % dels, sec) + if not image: + line += sec + "\n" + continue + split_sec = re.split(r"(%s)" % dels, line + sec) + for sub_sec in split_sec: + if re.match(f"^{dels}$", sub_sec): + continue + add_chunk(sub_sec, image,"") + line = "" + + if line: + split_sec = re.split(r"(%s)" % dels, line) for sub_sec in split_sec: if re.match(f"^{dels}$", sub_sec): continue diff --git a/rag/prompts/analyze_task_system.md b/rag/prompts/analyze_task_system.md index c086195b8c7..148e4113fbe 100644 --- a/rag/prompts/analyze_task_system.md +++ b/rag/prompts/analyze_task_system.md @@ -1,8 +1,48 @@ -Your responsibility is to execute assigned tasks to a high standard. Please: -1. Carefully analyze the task requirements. -2. Develop a reasonable execution plan. -3. Execute step-by-step and document the reasoning process. -4. Provide clear and accurate results. +You are an intelligent task analyzer that adapts analysis depth to task complexity. -If difficulties are encountered, clearly state the problem and explore alternative approaches. +**Analysis Framework** +**Step 1: Task Transmission Assessment** +**Note**: This section is not subject to word count limitations when transmission is needed, as it serves critical handoff functions. + +**Evaluate if task transmission information is needed:** +- **Is this an initial step?** If yes, skip this section +- **Are there upstream agents/steps?** If no, provide minimal transmission +- **Is there critical state/context to preserve?** If yes, include full transmission + +### If Task Transmission is Needed: +- **Current State Summary**: [1-2 sentences on where we are] +- **Key Data/Results**: [Critical findings that must carry forward] +- **Context Dependencies**: [Essential context for next agent/step] +- **Unresolved Items**: [Issues requiring continuation] +- **Status for User**: [Clear status update in user terms] +- **Technical State**: [System state for technical handoffs] + +**Step 2: Complexity Classification** +Classify as LOW / MEDIUM / HIGH: +- **LOW**: Single-step tasks, direct queries, small talk +- **MEDIUM**: Multi-step tasks within one domain +- **HIGH**: Multi-domain coordination or complex reasoning + +**Step 3: Adaptive Analysis** +Scale depth to match complexity. Always stop once success criteria are met. + +**For LOW (max 50 words for analysis only):** +- Detect small talk; if true, output exactly: `Small talk — no further analysis needed` +- One-sentence objective +- Direct execution approach (1–2 steps) + +**For MEDIUM (80–150 words for analysis only):** +- Objective; Intent & Scope +- 3–5 step minimal Plan (may mark parallel steps) +- **Uncertainty & Probes** (at least one probe with a clear stop condition) +- Success Criteria + basic Failure detection & fallback +- **Source Plan** (how evidence will be obtained/verified) + +**For HIGH (150–250 words for analysis only):** +- Comprehensive objective analysis; Intent & Scope +- 5–8 step Plan with dependencies/parallelism +- **Uncertainty & Probes** (key unknowns → probe → stop condition) +- Measurable Success Criteria; Failure detectors & fallbacks +- **Source Plan** (evidence acquisition & validation) +- **Reflection Hooks** (escalation/de-escalation triggers) diff --git a/rag/prompts/analyze_task_user.md b/rag/prompts/analyze_task_user.md index 499cad57529..81dc9f2bd9d 100644 --- a/rag/prompts/analyze_task_user.md +++ b/rag/prompts/analyze_task_user.md @@ -1,23 +1,9 @@ -Please analyze the following task: - -Task: {{ task }} - -Context: {{ context }} - -**Agent Prompt** -{{ agent_prompt }} - -**Analysis Requirements:** -1. Is it just a small talk? (If yes, no further plan or analysis is needed) -2. What is the core objective of the task? -3. What is the complexity level of the task? -4. What types of specialized skills are required? -5. Does the task need to be decomposed into subtasks? (If yes, propose the subtask structure) -6. How to know the task or the subtasks are impossible to lead to the success after a few rounds of interaction? -7. What are the expected success criteria? - -**Available Sub-Agents and Their Specializations:** - -{{ tools_desc }} - -Provide a detailed analysis of the task based on the above requirements. \ No newline at end of file +**Input Variables** +- **{{ task }}** — the task/request to analyze +- **{{ context }}** — background, history, situational context +- **{{ agent_prompt }}** — special instructions/role hints +- **{{ tools_desc }}** — available sub-agents and capabilities + +**Final Output Rule** +Return the Task Transmission section (if needed) followed by the concrete analysis and planning steps according to LOW / MEDIUM / HIGH complexity. +Do not restate the framework, definitions, or rules. Output only the final structured result. diff --git a/rag/prompts/next_step.md b/rag/prompts/next_step.md index a57f9000521..493c190512e 100644 --- a/rag/prompts/next_step.md +++ b/rag/prompts/next_step.md @@ -7,7 +7,6 @@ Your job is: # ========== TASK ANALYSIS ============= {{ task_analisys }} - # ========== TOOLS (JSON-Schema) ========== You may invoke only the tools listed below. Return a JSON array of objects in which item is with exactly two top-level keys: @@ -16,8 +15,24 @@ Return a JSON array of objects in which item is with exactly two top-level keys: {{ desc }} + +# ========== MULTI-STEP EXECUTION ========== +When tasks require multiple independent steps, you can execute them in parallel by returning multiple tool calls in a single JSON array. + +• **Data Collection**: Gathering information from multiple sources simultaneously +• **Validation**: Cross-checking facts using different tools +• **Comprehensive Analysis**: Analyzing different aspects of the same problem +• **Efficiency**: Reducing total execution time when steps don't depend on each other + +**Example Scenarios:** +- Searching multiple databases for the same query +- Checking weather in multiple cities +- Validating information through different APIs +- Performing calculations on different datasets +- Gathering user preferences from multiple sources + # ========== RESPONSE FORMAT ========== -✦ **When you need a tool** +**When you need a tool** Return ONLY the Json (no additional keys, no commentary, end with `<|stop|>`), such as following: [{ "name": "", @@ -27,7 +42,20 @@ Return ONLY the Json (no additional keys, no commentary, end with `<|stop|>`), s "arguments": { /* tool arguments matching its schema */ } }...]<|stop|> -✦ **When you are certain the task is solved OR no further information can be obtained** +**When you need multiple tools:** +Return ONLY: +[{ + "name": "", + "arguments": { /* tool arguments matching its schema */ } +},{ + "name": "", + "arguments": { /* tool arguments matching its schema */ } +},{ + "name": "", + "arguments": { /* tool arguments matching its schema */ } +}...]<|stop|> + +**When you are certain the task is solved OR no further information can be obtained** Return ONLY: [{ "name": "complete_task", @@ -61,3 +89,4 @@ Internal guideline: 2. **Act**: Emit the JSON object to call the tool. Today is {{ today }}. Remember that success in answering questions accurately is paramount - take all necessary steps to ensure your answer is correct. + diff --git a/rag/prompts/prompts.py b/rag/prompts/prompts.py index 40750a1efcc..5077d51f45a 100644 --- a/rag/prompts/prompts.py +++ b/rag/prompts/prompts.py @@ -157,7 +157,7 @@ def draw_node(k, line): PROMPT_JINJA_ENV = jinja2.Environment(autoescape=False, trim_blocks=True, lstrip_blocks=True) -def citation_prompt() -> str: +def citation_prompt(user_defined_prompts: dict={}) -> str: template = PROMPT_JINJA_ENV.from_string(CITATION_PROMPT_TEMPLATE) return template.render() @@ -339,7 +339,7 @@ def form_history(history, limit=-6): return context -def analyze_task(chat_mdl, prompt, task_name, tools_description: list[dict]): +def analyze_task(chat_mdl, prompt, task_name, tools_description: list[dict], user_defined_prompts: dict={}): tools_desc = tool_schema(tools_description) context = "" @@ -354,7 +354,7 @@ def analyze_task(chat_mdl, prompt, task_name, tools_description: list[dict]): return kwd -def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc): +def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc, user_defined_prompts: dict={}): if not tools_description: return "" desc = tool_schema(tools_description) @@ -372,7 +372,7 @@ def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc): return json_str, tk_cnt -def reflect(chat_mdl, history: list[dict], tool_call_res: list[Tuple]): +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] goal = history[1]["content"] template = PROMPT_JINJA_ENV.from_string(REFLECT) @@ -398,7 +398,7 @@ def form_message(system_prompt, user_prompt): return [{"role": "system", "content": system_prompt},{"role": "user", "content": user_prompt}] -def tool_call_summary(chat_mdl, name: str, params: dict, result: str) -> str: +def tool_call_summary(chat_mdl, name: str, params: dict, result: str, user_defined_prompts: dict={}) -> str: template = PROMPT_JINJA_ENV.from_string(SUMMARY4MEMORY) system_prompt = template.render(name=name, params=json.dumps(params, ensure_ascii=False, indent=2), @@ -409,7 +409,7 @@ def tool_call_summary(chat_mdl, name: str, params: dict, result: str) -> str: return re.sub(r"^.*", "", ans, flags=re.DOTALL) -def rank_memories(chat_mdl, goal:str, sub_goal:str, tool_call_summaries: list[str]): +def rank_memories(chat_mdl, goal:str, sub_goal:str, tool_call_summaries: list[str], user_defined_prompts: dict={}): 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)]) user_prompt = " → rank: " diff --git a/rag/prompts/reflect.md b/rag/prompts/reflect.md index a9c0e4f1f68..a3e5e9cf114 100644 --- a/rag/prompts/reflect.md +++ b/rag/prompts/reflect.md @@ -6,29 +6,70 @@ Tool call: `{{ call.name }}` Results: {{ call.result }} {% endfor %} +## Task Complexity Analysis & Reflection Scope -**Reflection Instructions:** +**First, analyze the task complexity using these dimensions:** -Analyze the current state of the overall task ({{ goal }}), then provide structured responses to the following: +### Complexity Assessment Matrix +- **Scope Breadth**: Single-step (1) | Multi-step (2) | Multi-domain (3) +- **Data Dependency**: Self-contained (1) | External inputs (2) | Multiple sources (3) +- **Decision Points**: Linear (1) | Few branches (2) | Complex logic (3) +- **Risk Level**: Low (1) | Medium (2) | High (3) -## 1. Goal Achievement Status +**Complexity Score**: Sum all dimensions (4-12 points) + +--- + +## Task Transmission Assessment +**Note**: This section is not subject to word count limitations when transmission is needed, as it serves critical handoff functions. +**Evaluate if task transmission information is needed:** +- **Is this an initial step?** If yes, skip this section +- **Are there downstream agents/steps?** If no, provide minimal transmission +- **Is there critical state/context to preserve?** If yes, include full transmission + +### If Task Transmission is Needed: +- **Current State Summary**: [1-2 sentences on where we are] +- **Key Data/Results**: [Critical findings that must carry forward] +- **Context Dependencies**: [Essential context for next agent/step] +- **Unresolved Items**: [Issues requiring continuation] +- **Status for User**: [Clear status update in user terms] +- **Technical State**: [System state for technical handoffs] + +--- + +## Situational Reflection (Adjust Length Based on Complexity Score) + +### Reflection Guidelines: +- **Simple Tasks (4-5 points)**: ~50-100 words, focus on completion status and immediate next step +- **Moderate Tasks (6-8 points)**: ~100-200 words, include core details and main risks +- **Complex Tasks (9-12 points)**: ~200-300 words, provide full analysis and alternatives + +### 1. Goal Achievement Status - Does the current outcome align with the original purpose of this task phase? - If not, what critical gaps exist? -## 2. Step Completion Check +### 2. Step Completion Check - Which planned steps were completed? (List verified items) - - Which steps are pending/incomplete? (Specify exactly what’s missing) + - Which steps are pending/incomplete? (Specify exactly what's missing) -## 3. Information Adequacy +### 3. Information Adequacy - Is the collected data sufficient to proceed? - What key information is still needed? (e.g., metrics, user input, external data) -## 4. Critical Observations +### 4. Critical Observations - Unexpected outcomes: [Flag anomalies/errors] - Risks/blockers: [Identify immediate obstacles] - Accuracy concerns: [Highlight unreliable results] -## 5. Next-Step Recommendations +### 5. Next-Step Recommendations - Proposed immediate action: [Concrete next step] - Alternative strategies if blocked: [Workaround solution] - - Tools/inputs required for next phase: [Specify resources] \ No newline at end of file + - Tools/inputs required for next phase: [Specify resources] + +--- + +**Output Instructions:** +1. First determine your complexity score +2. Assess if task transmission section is needed using the evaluation questions +3. Provide situational reflection with length appropriate to complexity +4. Use clear headers for easy parsing by downstream systems From f514482c0a8dae40d4441a8c925156eea87e8deb Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 8 Sep 2025 17:14:11 +0800 Subject: [PATCH 0639/1187] Feat: Add ConfirmDeleteDialog storybook #9914 (#9977) ### What problem does this PR solve? Feat: Add ConfirmDeleteDialog storybook #9914 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../stories/confirm-delete-dialog.stories.tsx | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 web/src/stories/confirm-delete-dialog.stories.tsx diff --git a/web/src/stories/confirm-delete-dialog.stories.tsx b/web/src/stories/confirm-delete-dialog.stories.tsx new file mode 100644 index 00000000000..234392cc047 --- /dev/null +++ b/web/src/stories/confirm-delete-dialog.stories.tsx @@ -0,0 +1,75 @@ +import type { Meta, StoryObj } from '@storybook/react-webpack5'; + +import { fn } from 'storybook/test'; + +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import { Button } from '@/components/ui/button'; + +// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export +const meta = { + title: 'Example/ConfirmDeleteDialog', + component: ConfirmDeleteDialog, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout + layout: 'centered', + docs: { + description: { + component: ` +## Component Description + +ConfirmDeleteDialog is a dialog component for confirming delete operations with customizable title and callback functions. `, + }, + }, + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs + // More on argTypes: https://storybook.js.org/docs/api/argtypes + argTypes: { + title: { control: 'text' }, + hidden: { control: 'boolean' }, + onOk: { action: 'onOk' }, + onCancel: { action: 'onCancel' }, + }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args + args: { onOk: fn(), onCancel: fn() }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Default: Story = { + args: { + title: 'Confirm Delete', + children: , + }, + parameters: { + docs: { + description: { + story: ` +### Usage Examples + +\`\`\`tsx +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import { Button } from '@/components/ui/button'; + + console.log('Confirmed')} + onCancel={() => console.log('Cancelled')} +> + + +\`\`\` + `, + }, + }, + }, +}; + +export const WithCustomTitle: Story = { + args: { + title: 'Are you sure you want to delete this file?', + children: , + }, +}; From e8018fde83874bad6da0cdcfd002bb5f9a294b17 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Mon, 8 Sep 2025 17:14:23 +0800 Subject: [PATCH 0640/1187] Fix: Update the pagination prompt text in zh.ts, changing "page" to "item/page" #3221 (#9978) ### What problem does this PR solve? Fix: Update the pagination prompt text in zh.ts, changing "page" to "item/page" ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/ui/ragflow-pagination.tsx | 6 +----- web/src/locales/en.ts | 2 +- web/src/locales/zh.ts | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/web/src/components/ui/ragflow-pagination.tsx b/web/src/components/ui/ragflow-pagination.tsx index 0eb01355614..1426425d3c4 100644 --- a/web/src/components/ui/ragflow-pagination.tsx +++ b/web/src/components/ui/ragflow-pagination.tsx @@ -33,11 +33,7 @@ export function RAGFlowPagination({ const sizeChangerOptions: RAGFlowSelectOptionType[] = useMemo(() => { return [10, 20, 50, 100].map((x) => ({ - label: ( - - {x} / {t('pagination.page')} - - ), + label: {t('pagination.page', { page: x })}, value: x.toString(), })); }, []); diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index b22a9112779..fa62ce3c090 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1586,7 +1586,7 @@ This delimiter is used to split the input text into several text pieces echo of }, pagination: { total: 'Total {{total}}', - page: 'Page', + page: '{{page}} /Page', }, }, }; diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index b8084fda78a..b63805c7c3d 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1498,7 +1498,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 }, pagination: { total: '总共 {{total}} 条', - page: '页', + page: '{{page}}条/页', }, }, }; From 2616f651c96e46cfed843fffa92b6a2cc6bdbcd9 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 8 Sep 2025 18:59:51 +0800 Subject: [PATCH 0641/1187] Feat: The agent's external page should be able to fill in the begin parameter after being reset in task mode #9745 (#9982) ### What problem does this PR solve? Feat: The agent's external page should be able to fill in the begin parameter after being reset in task mode #9745 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/locales/en.ts | 2 +- web/src/locales/zh.ts | 2 +- .../agent/hooks/use-send-shared-message.ts | 27 +- web/src/pages/agent/share/index.tsx | 30 +- web/src/pages/data-flow/canvas/index.tsx | 12 +- .../data-flow/canvas/node/chunker-node.tsx | 49 ++ .../data-flow/canvas/node/parser-node.tsx | 49 ++ .../data-flow/canvas/node/tokenizer-node.tsx | 49 ++ web/src/pages/data-flow/constant.tsx | 461 +----------------- web/src/pages/data-flow/hooks.tsx | 278 +---------- web/src/pages/data-flow/hooks/use-add-node.ts | 44 +- .../hooks/use-agent-tool-initial-values.ts | 70 --- web/src/pages/data-flow/index.tsx | 6 +- web/src/pages/data-flow/operator-icon.tsx | 35 +- 14 files changed, 234 insertions(+), 880 deletions(-) create mode 100644 web/src/pages/data-flow/canvas/node/chunker-node.tsx create mode 100644 web/src/pages/data-flow/canvas/node/parser-node.tsx create mode 100644 web/src/pages/data-flow/canvas/node/tokenizer-node.tsx delete mode 100644 web/src/pages/data-flow/hooks/use-agent-tool-initial-values.ts diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index fa62ce3c090..43869812211 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1523,7 +1523,7 @@ This delimiter is used to split the input text into several text pieces echo of sqlStatement: 'SQL Statement', sqlStatementTip: 'Write your SQL query here. You can use variables, raw SQL, or mix both using variable syntax.', - frameworkPrompts: 'Framework Prompts', + frameworkPrompts: 'Framework', }, llmTools: { bad_calculator: { diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index b63805c7c3d..17fbedacff1 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1436,7 +1436,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 sqlStatement: 'SQL 语句', sqlStatementTip: '在此处编写您的 SQL 查询。您可以使用变量、原始 SQL,或使用变量语法混合使用两者。', - frameworkPrompts: '框架提示词', + frameworkPrompts: '框架', }, footer: { profile: 'All rights reserved @ React', diff --git a/web/src/pages/agent/hooks/use-send-shared-message.ts b/web/src/pages/agent/hooks/use-send-shared-message.ts index 61570edc041..e379215fc99 100644 --- a/web/src/pages/agent/hooks/use-send-shared-message.ts +++ b/web/src/pages/agent/hooks/use-send-shared-message.ts @@ -1,13 +1,16 @@ import { SharedFrom } from '@/constants/chat'; import { useSetModalState } from '@/hooks/common-hooks'; +import { useFetchExternalAgentInputs } from '@/hooks/use-agent-request'; import { IEventList } from '@/hooks/use-send-message'; import { buildRequestBody, useSendAgentMessage, } from '@/pages/agent/chat/use-send-agent-message'; +import { isEmpty } from 'lodash'; import trim from 'lodash/trim'; -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useSearchParams } from 'umi'; +import { AgentDialogueMode } from '../constant'; export const useSendButtonDisabled = (value: string) => { return trim(value) === ''; @@ -35,12 +38,15 @@ export const useGetSharedChatSearchParams = () => { export const useSendNextSharedMessage = ( addEventList: (data: IEventList, messageId: string) => void, - isTaskMode: boolean, ) => { const { from, sharedId: conversationId } = useGetSharedChatSearchParams(); const url = `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`; + const { data: inputsData } = useFetchExternalAgentInputs(); const [params, setParams] = useState([]); + const sendedTaskMessage = useRef(false); + + const isTaskMode = inputsData.mode === AgentDialogueMode.Task; const { visible: parameterDialogVisible, @@ -73,10 +79,27 @@ export const useSendNextSharedMessage = ( [hideParameterDialog, isTaskMode, ret], ); + const runTask = useCallback(() => { + if ( + isTaskMode && + isEmpty(inputsData?.inputs) && + !sendedTaskMessage.current + ) { + ok([]); + sendedTaskMessage.current = true; + } + }, [inputsData?.inputs, isTaskMode, ok]); + + useEffect(() => { + runTask(); + }, [runTask]); + return { ...ret, hasError: false, parameterDialogVisible, + inputsData, + isTaskMode, hideParameterDialog, showParameterDialog, ok, diff --git a/web/src/pages/agent/share/index.tsx b/web/src/pages/agent/share/index.tsx index 26cdb02486e..9246d9bcf37 100644 --- a/web/src/pages/agent/share/index.tsx +++ b/web/src/pages/agent/share/index.tsx @@ -5,10 +5,7 @@ import MessageItem from '@/components/next-message-item'; import PdfDrawer from '@/components/pdf-drawer'; import { useClickDrawer } from '@/components/pdf-drawer/hooks'; import { MessageType } from '@/constants/chat'; -import { - useFetchExternalAgentInputs, - useUploadCanvasFileWithProgress, -} from '@/hooks/use-agent-request'; +import { useUploadCanvasFileWithProgress } from '@/hooks/use-agent-request'; import { cn } from '@/lib/utils'; import i18n from '@/locales/config'; import DebugContent from '@/pages/agent/debug-content'; @@ -18,7 +15,6 @@ import { useSendButtonDisabled } from '@/pages/chat/hooks'; import { buildMessageUuidWithRole } from '@/utils/chat'; import { isEmpty } from 'lodash'; import React, { forwardRef, useCallback } from 'react'; -import { AgentDialogueMode } from '../constant'; import { useGetSharedChatSearchParams, useSendNextSharedMessage, @@ -43,9 +39,6 @@ const ChatContainer = () => { clearEventList, } = useCacheChatLog(); - const { data: inputsData } = useFetchExternalAgentInputs(); - const isTaskMode = inputsData.mode === AgentDialogueMode.Task; - const { handlePressEnter, handleInputChange, @@ -55,6 +48,8 @@ const ChatContainer = () => { messageContainerRef, derivedMessages, hasError, + inputsData, + isTaskMode, stopOutputMessage, findReferenceByMessageId, appendUploadResponseList, @@ -64,7 +59,8 @@ const ChatContainer = () => { addNewestOneAnswer, ok, resetSession, - } = useSendNextSharedMessage(addEventList, isTaskMode); + } = useSendNextSharedMessage(addEventList); + const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({ derivedMessages, sendFormMessage, @@ -72,6 +68,12 @@ const ChatContainer = () => { }); const sendDisabled = useSendButtonDisabled(value); + const showBeginParameterDialog = useCallback(() => { + if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) { + showParameterDialog(); + } + }, [inputsData, showParameterDialog]); + const handleUploadFile: NonNullable = useCallback( async (files, options) => { @@ -96,10 +98,8 @@ const ChatContainer = () => { }, [inputsData.prologue, addNewestOneAnswer, isTaskMode]); React.useEffect(() => { - if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) { - showParameterDialog(); - } - }, [inputsData, showParameterDialog]); + showBeginParameterDialog(); + }, [showBeginParameterDialog]); const handleInputsModalOk = (params: any[]) => { ok(params); @@ -107,10 +107,14 @@ const ChatContainer = () => { const handleReset = () => { resetSession(); clearEventList(); + if (isTaskMode) { + showBeginParameterDialog(); + } }; if (!conversationId) { return
    empty
    ; } + return ( <> ) { + return ( + + + + + + + + ); +} + +export default memo(ChunkerNode); diff --git a/web/src/pages/data-flow/canvas/node/parser-node.tsx b/web/src/pages/data-flow/canvas/node/parser-node.tsx new file mode 100644 index 00000000000..4dceabce97f --- /dev/null +++ b/web/src/pages/data-flow/canvas/node/parser-node.tsx @@ -0,0 +1,49 @@ +import { IRagNode } from '@/interfaces/database/flow'; +import { NodeProps, Position } from '@xyflow/react'; +import { memo } from 'react'; +import { NodeHandleId } from '../../constant'; +import { needsSingleStepDebugging } from '../../utils'; +import { CommonHandle } from './handle'; +import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; + +function ParserNode({ + id, + data, + isConnectable = true, + selected, +}: NodeProps) { + return ( + + + + + + + + ); +} + +export default memo(ParserNode); diff --git a/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx b/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx new file mode 100644 index 00000000000..141399b148c --- /dev/null +++ b/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx @@ -0,0 +1,49 @@ +import { IRagNode } from '@/interfaces/database/flow'; +import { NodeProps, Position } from '@xyflow/react'; +import { memo } from 'react'; +import { NodeHandleId } from '../../constant'; +import { needsSingleStepDebugging } from '../../utils'; +import { CommonHandle } from './handle'; +import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; + +function TokenizerNode({ + id, + data, + isConnectable = true, + selected, +}: NodeProps) { + return ( + + + + + + + + ); +} + +export default memo(TokenizerNode); diff --git a/web/src/pages/data-flow/constant.tsx b/web/src/pages/data-flow/constant.tsx index bef95fb8de9..ea158722f6f 100644 --- a/web/src/pages/data-flow/constant.tsx +++ b/web/src/pages/data-flow/constant.tsx @@ -8,11 +8,6 @@ import { ProgrammingLanguage, } from '@/constants/agent'; -export enum AgentDialogueMode { - Conversational = 'conversational', - Task = 'task', -} - import { ChatVariableEnabledField, variableEnabledFieldMap, @@ -22,17 +17,6 @@ import i18n from '@/locales/config'; import { setInitialChatVariableEnabledFieldValue } from '@/utils/chat'; import { t } from 'i18next'; -// DuckDuckGo's channel options -export enum Channel { - Text = 'text', - News = 'news', -} - -export enum PromptRole { - User = 'user', - Assistant = 'assistant', -} - import { Circle, CircleSlash2, @@ -44,6 +28,16 @@ import { WrapText, } from 'lucide-react'; +export enum PromptRole { + User = 'user', + Assistant = 'assistant', +} + +export enum AgentDialogueMode { + Conversational = 'conversational', + Task = 'task', +} + export const BeginId = 'begin'; export enum Operator { @@ -54,26 +48,9 @@ export enum Operator { Relevant = 'Relevant', RewriteQuestion = 'RewriteQuestion', KeywordExtract = 'KeywordExtract', - Baidu = 'Baidu', - DuckDuckGo = 'DuckDuckGo', - Wikipedia = 'Wikipedia', - PubMed = 'PubMed', - ArXiv = 'ArXiv', - Google = 'Google', - Bing = 'Bing', - GoogleScholar = 'GoogleScholar', - DeepL = 'DeepL', - GitHub = 'GitHub', - BaiduFanyi = 'BaiduFanyi', - QWeather = 'QWeather', ExeSQL = 'ExeSQL', Switch = 'Switch', - WenCai = 'WenCai', - AkShare = 'AkShare', - YahooFinance = 'YahooFinance', - Jin10 = 'Jin10', Concentrator = 'Concentrator', - TuShare = 'TuShare', Note = 'Note', Crawler = 'Crawler', Invoke = 'Invoke', @@ -84,11 +61,8 @@ export enum Operator { WaitingDialogue = 'WaitingDialogue', Agent = 'Agent', Tool = 'Tool', - TavilySearch = 'TavilySearch', - TavilyExtract = 'TavilyExtract', UserFillUp = 'UserFillUp', StringTransform = 'StringTransform', - SearXNG = 'SearXNG', Parser = 'Parser', Chunker = 'Chunker', Tokenizer = 'Tokenizer', @@ -114,112 +88,6 @@ export const AgentOperatorList = [ Operator.Agent, ]; -export const componentMenuList = [ - { - name: Operator.Retrieval, - }, - { - name: Operator.Categorize, - }, - { - name: Operator.Message, - }, - - { - name: Operator.RewriteQuestion, - }, - { - name: Operator.KeywordExtract, - }, - { - name: Operator.Switch, - }, - { - name: Operator.Concentrator, - }, - { - name: Operator.Iteration, - }, - { - name: Operator.Code, - }, - { - name: Operator.WaitingDialogue, - }, - { - name: Operator.Agent, - }, - { - name: Operator.Note, - }, - { - name: Operator.DuckDuckGo, - }, - { - name: Operator.Baidu, - }, - { - name: Operator.Wikipedia, - }, - { - name: Operator.PubMed, - }, - { - name: Operator.ArXiv, - }, - { - name: Operator.Google, - }, - { - name: Operator.Bing, - }, - { - name: Operator.GoogleScholar, - }, - { - name: Operator.DeepL, - }, - { - name: Operator.GitHub, - }, - { - name: Operator.BaiduFanyi, - }, - { - name: Operator.QWeather, - }, - { - name: Operator.ExeSQL, - }, - { - name: Operator.WenCai, - }, - { - name: Operator.AkShare, - }, - { - name: Operator.YahooFinance, - }, - { - name: Operator.Jin10, - }, - { - name: Operator.TuShare, - }, - { - name: Operator.Crawler, - }, - { - name: Operator.Invoke, - }, - { - name: Operator.Email, - }, - { - name: Operator.SearXNG, - }, -]; - export const SwitchOperatorOptions = [ { value: '=', label: 'equal', icon: 'equal' }, { value: '≠', label: 'notEqual', icon: 'not-equals' }, @@ -331,161 +199,6 @@ export const initialKeywordExtractValues = { top_n: 3, ...initialQueryBaseValues, }; -export const initialDuckValues = { - top_n: 10, - channel: Channel.Text, - query: AgentGlobals.SysQuery, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: [], - type: 'Array', - }, - }, -}; - -export const initialSearXNGValues = { - top_n: '10', - searxng_url: '', - query: AgentGlobals.SysQuery, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: [], - type: 'Array', - }, - }, -}; - -export const initialBaiduValues = { - top_n: 10, - ...initialQueryBaseValues, -}; - -export const initialWikipediaValues = { - top_n: 10, - language: 'en', - query: AgentGlobals.SysQuery, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - }, -}; - -export const initialPubMedValues = { - top_n: 12, - email: '', - query: AgentGlobals.SysQuery, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - }, -}; - -export const initialArXivValues = { - top_n: 12, - sort_by: 'relevance', - query: AgentGlobals.SysQuery, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - }, -}; - -export const initialGoogleValues = { - q: AgentGlobals.SysQuery, - start: 0, - num: 12, - api_key: '', - country: 'us', - language: 'en', - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: [], - type: 'Array', - }, - }, -}; - -export const initialBingValues = { - top_n: 10, - channel: 'Webpages', - api_key: - 'YOUR_API_KEY (obtained from https://www.microsoft.com/en-us/bing/apis/bing-web-search-api)', - country: 'CH', - language: 'en', - query: '', -}; - -export const initialGoogleScholarValues = { - top_n: 12, - sort_by: 'relevance', - patents: true, - query: AgentGlobals.SysQuery, - year_low: undefined, - year_high: undefined, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: [], - type: 'Array', - }, - }, -}; - -export const initialDeepLValues = { - top_n: 5, - auth_key: 'relevance', -}; - -export const initialGithubValues = { - top_n: 5, - query: AgentGlobals.SysQuery, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: [], - type: 'Array', - }, - }, -}; - -export const initialBaiduFanyiValues = { - appid: 'xxx', - secret_key: 'xxx', - trans_type: 'translate', - ...initialQueryBaseValues, -}; - -export const initialQWeatherValues = { - web_apikey: 'xxx', - type: 'weather', - user_type: 'free', - time_period: 'now', - ...initialQueryBaseValues, -}; export const initialExeSqlValues = { sql: '', @@ -523,54 +236,8 @@ export const initialSwitchValues = { [SwitchElseTo]: [], }; -export const initialWenCaiValues = { - top_n: 20, - query_type: 'stock', - query: AgentGlobals.SysQuery, - outputs: { - report: { - value: '', - type: 'string', - }, - }, -}; - -export const initialAkShareValues = { top_n: 10, ...initialQueryBaseValues }; - -export const initialYahooFinanceValues = { - stock_code: '', - info: true, - history: false, - financials: false, - balance_sheet: false, - cash_flow_statement: false, - news: true, - outputs: { - report: { - value: '', - type: 'string', - }, - }, -}; - -export const initialJin10Values = { - type: 'flash', - secret_key: 'xxx', - flash_type: '1', - contain: '', - filter: '', - ...initialQueryBaseValues, -}; - export const initialConcentratorValues = {}; -export const initialTuShareValues = { - token: 'xxx', - src: 'eastmoney', - start_date: '2024-01-01 09:00:00', - ...initialQueryBaseValues, -}; - export const initialNoteValues = { text: '', }; @@ -650,6 +317,10 @@ export const initialCodeValues = { export const initialWaitingDialogueValues = {}; +export const initialChunkerValues = {}; + +export const initialTokenizerValues = {}; + export const initialAgentValues = { ...initialLlmBaseValues, description: '', @@ -717,66 +388,7 @@ export const initialStringTransformValues = { }, }; -export enum TavilySearchDepth { - Basic = 'basic', - Advanced = 'advanced', -} - -export enum TavilyTopic { - News = 'news', - General = 'general', -} - -export const initialTavilyValues = { - api_key: '', - query: AgentGlobals.SysQuery, - search_depth: TavilySearchDepth.Basic, - topic: TavilyTopic.General, - max_results: 5, - days: 7, - include_answer: false, - include_raw_content: true, - include_images: false, - include_image_descriptions: false, - include_domains: [], - exclude_domains: [], - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: [], - type: 'Array', - }, - }, -}; - -export enum TavilyExtractDepth { - Basic = 'basic', - Advanced = 'advanced', -} - -export enum TavilyExtractFormat { - Text = 'text', - Markdown = 'markdown', -} - -export const initialTavilyExtractValues = { - urls: '', - extract_depth: TavilyExtractDepth.Basic, - format: TavilyExtractFormat.Markdown, - outputs: { - formalized_content: { - value: '', - type: 'string', - }, - json: { - value: [], - type: 'Array', - }, - }, -}; +export const initialParserValues = {}; export const CategorizeAnchorPointPositions = [ { top: 1, right: 34 }, @@ -818,27 +430,9 @@ export const RestrictedUpstreamMap = { Operator.Message, Operator.Relevant, ], - [Operator.Baidu]: [Operator.Begin, Operator.Retrieval], - [Operator.DuckDuckGo]: [Operator.Begin, Operator.Retrieval], - [Operator.Wikipedia]: [Operator.Begin, Operator.Retrieval], - [Operator.PubMed]: [Operator.Begin, Operator.Retrieval], - [Operator.ArXiv]: [Operator.Begin, Operator.Retrieval], - [Operator.Google]: [Operator.Begin, Operator.Retrieval], - [Operator.Bing]: [Operator.Begin, Operator.Retrieval], - [Operator.GoogleScholar]: [Operator.Begin, Operator.Retrieval], - [Operator.DeepL]: [Operator.Begin, Operator.Retrieval], - [Operator.GitHub]: [Operator.Begin, Operator.Retrieval], - [Operator.BaiduFanyi]: [Operator.Begin, Operator.Retrieval], - [Operator.QWeather]: [Operator.Begin, Operator.Retrieval], - [Operator.SearXNG]: [Operator.Begin, Operator.Retrieval], [Operator.ExeSQL]: [Operator.Begin], [Operator.Switch]: [Operator.Begin], - [Operator.WenCai]: [Operator.Begin], - [Operator.AkShare]: [Operator.Begin], - [Operator.YahooFinance]: [Operator.Begin], - [Operator.Jin10]: [Operator.Begin], [Operator.Concentrator]: [Operator.Begin], - [Operator.TuShare]: [Operator.Begin], [Operator.Crawler]: [Operator.Begin], [Operator.Note]: [], [Operator.Invoke]: [Operator.Begin], @@ -848,8 +442,6 @@ export const RestrictedUpstreamMap = { [Operator.Code]: [Operator.Begin], [Operator.WaitingDialogue]: [Operator.Begin], [Operator.Agent]: [Operator.Begin], - [Operator.TavilySearch]: [Operator.Begin], - [Operator.TavilyExtract]: [Operator.Begin], [Operator.StringTransform]: [Operator.Begin], [Operator.UserFillUp]: [Operator.Begin], [Operator.Tool]: [Operator.Begin], @@ -863,27 +455,9 @@ export const NodeMap = { [Operator.Relevant]: 'relevantNode', [Operator.RewriteQuestion]: 'rewriteNode', [Operator.KeywordExtract]: 'keywordNode', - [Operator.DuckDuckGo]: 'ragNode', - [Operator.Baidu]: 'ragNode', - [Operator.Wikipedia]: 'ragNode', - [Operator.PubMed]: 'ragNode', - [Operator.ArXiv]: 'ragNode', - [Operator.Google]: 'ragNode', - [Operator.Bing]: 'ragNode', - [Operator.GoogleScholar]: 'ragNode', - [Operator.DeepL]: 'ragNode', - [Operator.GitHub]: 'ragNode', - [Operator.BaiduFanyi]: 'ragNode', - [Operator.QWeather]: 'ragNode', - [Operator.SearXNG]: 'ragNode', [Operator.ExeSQL]: 'ragNode', [Operator.Switch]: 'switchNode', [Operator.Concentrator]: 'logicNode', - [Operator.WenCai]: 'ragNode', - [Operator.AkShare]: 'ragNode', - [Operator.YahooFinance]: 'ragNode', - [Operator.Jin10]: 'ragNode', - [Operator.TuShare]: 'ragNode', [Operator.Note]: 'noteNode', [Operator.Crawler]: 'ragNode', [Operator.Invoke]: 'ragNode', @@ -894,10 +468,11 @@ export const NodeMap = { [Operator.WaitingDialogue]: 'ragNode', [Operator.Agent]: 'agentNode', [Operator.Tool]: 'toolNode', - [Operator.TavilySearch]: 'ragNode', [Operator.UserFillUp]: 'ragNode', [Operator.StringTransform]: 'ragNode', - [Operator.TavilyExtract]: 'ragNode', + [Operator.Parser]: 'parserNode', + [Operator.Chunker]: 'chunkerNode', + [Operator.Tokenizer]: 'tokenizerNode', }; export enum BeginQueryType { diff --git a/web/src/pages/data-flow/hooks.tsx b/web/src/pages/data-flow/hooks.tsx index 779ba092862..42687313f85 100644 --- a/web/src/pages/data-flow/hooks.tsx +++ b/web/src/pages/data-flow/hooks.tsx @@ -1,69 +1,12 @@ -import { - Connection, - Edge, - getOutgoers, - Node, - Position, - ReactFlowInstance, -} from '@xyflow/react'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { Connection, Edge, getOutgoers } from '@xyflow/react'; +import { useCallback } from 'react'; // import { shallow } from 'zustand/shallow'; -import { useFetchModelId } from '@/hooks/logic-hooks'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { humanId } from 'human-id'; -import { get, lowerFirst } from 'lodash'; +import { lowerFirst } from 'lodash'; import { useTranslation } from 'react-i18next'; -import { - initialAgentValues, - initialAkShareValues, - initialArXivValues, - initialBaiduFanyiValues, - initialBeginValues, - initialBingValues, - initialCategorizeValues, - initialCodeValues, - initialConcentratorValues, - initialCrawlerValues, - initialDeepLValues, - initialDuckValues, - initialEmailValues, - initialExeSqlValues, - initialGithubValues, - initialGoogleScholarValues, - initialGoogleValues, - initialInvokeValues, - initialIterationValues, - initialJin10Values, - initialKeywordExtractValues, - initialMessageValues, - initialNoteValues, - initialPubMedValues, - initialQWeatherValues, - initialRelevantValues, - initialRetrievalValues, - initialRewriteQuestionValues, - initialSearXNGValues, - initialStringTransformValues, - initialSwitchValues, - initialTavilyExtractValues, - initialTavilyValues, - initialTuShareValues, - initialUserFillUpValues, - initialWaitingDialogueValues, - initialWenCaiValues, - initialWikipediaValues, - initialYahooFinanceValues, - NodeMap, - Operator, - RestrictedUpstreamMap, -} from './constant'; +import { Operator, RestrictedUpstreamMap } from './constant'; import useGraphStore, { RFState } from './store'; -import { - generateNodeNamesWithIncreasingIndex, - getNodeDragHandle, - getRelativePositionToIterationNode, - replaceIdWithText, -} from './utils'; +import { replaceIdWithText } from './utils'; const selector = (state: RFState) => ({ nodes: state.nodes, @@ -83,83 +26,6 @@ export const useSelectCanvasData = () => { return useGraphStore(selector); }; -export const useInitializeOperatorParams = () => { - const llmId = useFetchModelId(); - - const initialFormValuesMap = useMemo(() => { - return { - [Operator.Begin]: initialBeginValues, - [Operator.Retrieval]: initialRetrievalValues, - [Operator.Categorize]: { ...initialCategorizeValues, llm_id: llmId }, - [Operator.Relevant]: { ...initialRelevantValues, llm_id: llmId }, - [Operator.RewriteQuestion]: { - ...initialRewriteQuestionValues, - llm_id: llmId, - }, - [Operator.Message]: initialMessageValues, - [Operator.KeywordExtract]: { - ...initialKeywordExtractValues, - llm_id: llmId, - }, - [Operator.DuckDuckGo]: initialDuckValues, - [Operator.Wikipedia]: initialWikipediaValues, - [Operator.PubMed]: initialPubMedValues, - [Operator.ArXiv]: initialArXivValues, - [Operator.Google]: initialGoogleValues, - [Operator.Bing]: initialBingValues, - [Operator.GoogleScholar]: initialGoogleScholarValues, - [Operator.DeepL]: initialDeepLValues, - [Operator.SearXNG]: initialSearXNGValues, - [Operator.GitHub]: initialGithubValues, - [Operator.BaiduFanyi]: initialBaiduFanyiValues, - [Operator.QWeather]: initialQWeatherValues, - [Operator.ExeSQL]: { ...initialExeSqlValues, llm_id: llmId }, - [Operator.Switch]: initialSwitchValues, - [Operator.WenCai]: initialWenCaiValues, - [Operator.AkShare]: initialAkShareValues, - [Operator.YahooFinance]: initialYahooFinanceValues, - [Operator.Jin10]: initialJin10Values, - [Operator.Concentrator]: initialConcentratorValues, - [Operator.TuShare]: initialTuShareValues, - [Operator.Note]: initialNoteValues, - [Operator.Crawler]: initialCrawlerValues, - [Operator.Invoke]: initialInvokeValues, - [Operator.Email]: initialEmailValues, - [Operator.Iteration]: initialIterationValues, - [Operator.IterationStart]: initialIterationValues, - [Operator.Code]: initialCodeValues, - [Operator.WaitingDialogue]: initialWaitingDialogueValues, - [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, - [Operator.TavilySearch]: initialTavilyValues, - [Operator.TavilyExtract]: initialTavilyExtractValues, - [Operator.Tool]: {}, - [Operator.UserFillUp]: initialUserFillUpValues, - [Operator.StringTransform]: initialStringTransformValues, - }; - }, [llmId]); - - const initializeOperatorParams = useCallback( - (operatorName: Operator) => { - return initialFormValuesMap[operatorName]; - }, - [initialFormValuesMap], - ); - - return initializeOperatorParams; -}; - -export const useHandleDrag = () => { - const handleDragStart = useCallback( - (operatorId: string) => (ev: React.DragEvent) => { - ev.dataTransfer.setData('application/@xyflow/react', operatorId); - ev.dataTransfer.effectAllowed = 'move'; - }, - [], - ); - - return { handleDragStart }; -}; - export const useGetNodeName = () => { const { t } = useTranslation(); @@ -169,91 +35,6 @@ export const useGetNodeName = () => { }; }; -export const useHandleDrop = () => { - const addNode = useGraphStore((state) => state.addNode); - const nodes = useGraphStore((state) => state.nodes); - const [reactFlowInstance, setReactFlowInstance] = - useState>(); - const initializeOperatorParams = useInitializeOperatorParams(); - const getNodeName = useGetNodeName(); - - const onDragOver = useCallback((event: React.DragEvent) => { - event.preventDefault(); - event.dataTransfer.dropEffect = 'move'; - }, []); - - const onDrop = useCallback( - (event: React.DragEvent) => { - event.preventDefault(); - - const type = event.dataTransfer.getData('application/@xyflow/react'); - - // check if the dropped element is valid - if (typeof type === 'undefined' || !type) { - return; - } - - // reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition - // and you don't need to subtract the reactFlowBounds.left/top anymore - // details: https://@xyflow/react.dev/whats-new/2023-11-10 - const position = reactFlowInstance?.screenToFlowPosition({ - x: event.clientX, - y: event.clientY, - }); - const newNode: Node = { - id: `${type}:${humanId()}`, - type: NodeMap[type as Operator] || 'ragNode', - position: position || { - x: 0, - y: 0, - }, - data: { - label: `${type}`, - name: generateNodeNamesWithIncreasingIndex(getNodeName(type), nodes), - form: initializeOperatorParams(type as Operator), - }, - sourcePosition: Position.Right, - targetPosition: Position.Left, - dragHandle: getNodeDragHandle(type), - }; - - if (type === Operator.Iteration) { - newNode.width = 500; - newNode.height = 250; - const iterationStartNode: Node = { - id: `${Operator.IterationStart}:${humanId()}`, - type: 'iterationStartNode', - position: { x: 50, y: 100 }, - // draggable: false, - data: { - label: Operator.IterationStart, - name: Operator.IterationStart, - form: {}, - }, - parentId: newNode.id, - extent: 'parent', - }; - addNode(newNode); - addNode(iterationStartNode); - } else { - const subNodeOfIteration = getRelativePositionToIterationNode( - nodes, - position, - ); - if (subNodeOfIteration) { - newNode.parentId = subNodeOfIteration.parentId; - newNode.position = subNodeOfIteration.position; - newNode.extent = 'parent'; - } - addNode(newNode); - } - }, - [reactFlowInstance, getNodeName, nodes, initializeOperatorParams, addNode], - ); - - return { onDrop, onDragOver, setReactFlowInstance, reactFlowInstance }; -}; - export const useValidateConnection = () => { const { getOperatorTypeFromId, getParentIdById, edges, nodes } = useGraphStore((state) => state); @@ -352,52 +133,3 @@ export const useDuplicateNode = () => { return duplicateNode; }; - -export const useCopyPaste = () => { - const nodes = useGraphStore((state) => state.nodes); - const duplicateNode = useDuplicateNode(); - - const onCopyCapture = useCallback( - (event: ClipboardEvent) => { - if (get(event, 'srcElement.tagName') !== 'BODY') return; - - event.preventDefault(); - const nodesStr = JSON.stringify( - nodes.filter((n) => n.selected && n.data.label !== Operator.Begin), - ); - - event.clipboardData?.setData('agent:nodes', nodesStr); - }, - [nodes], - ); - - const onPasteCapture = useCallback( - (event: ClipboardEvent) => { - const nodes = JSON.parse( - event.clipboardData?.getData('agent:nodes') || '[]', - ) as RAGFlowNodeType[] | undefined; - - if (Array.isArray(nodes) && nodes.length) { - event.preventDefault(); - nodes.forEach((n) => { - duplicateNode(n.id, n.data.label); - }); - } - }, - [duplicateNode], - ); - - useEffect(() => { - window.addEventListener('copy', onCopyCapture); - return () => { - window.removeEventListener('copy', onCopyCapture); - }; - }, [onCopyCapture]); - - useEffect(() => { - window.addEventListener('paste', onPasteCapture); - return () => { - window.removeEventListener('paste', onPasteCapture); - }; - }, [onPasteCapture]); -}; diff --git a/web/src/pages/data-flow/hooks/use-add-node.ts b/web/src/pages/data-flow/hooks/use-add-node.ts index e3dad598e0c..8f1ebf77f3f 100644 --- a/web/src/pages/data-flow/hooks/use-add-node.ts +++ b/web/src/pages/data-flow/hooks/use-add-node.ts @@ -10,45 +10,29 @@ import { NodeMap, Operator, initialAgentValues, - initialAkShareValues, - initialArXivValues, - initialBaiduFanyiValues, initialBeginValues, - initialBingValues, initialCategorizeValues, + initialChunkerValues, initialCodeValues, initialConcentratorValues, initialCrawlerValues, - initialDeepLValues, - initialDuckValues, initialEmailValues, initialExeSqlValues, - initialGithubValues, - initialGoogleScholarValues, - initialGoogleValues, initialInvokeValues, initialIterationStartValues, initialIterationValues, - initialJin10Values, initialKeywordExtractValues, initialMessageValues, initialNoteValues, - initialPubMedValues, - initialQWeatherValues, + initialParserValues, initialRelevantValues, initialRetrievalValues, initialRewriteQuestionValues, - initialSearXNGValues, initialStringTransformValues, initialSwitchValues, - initialTavilyExtractValues, - initialTavilyValues, - initialTuShareValues, + initialTokenizerValues, initialUserFillUpValues, initialWaitingDialogueValues, - initialWenCaiValues, - initialWikipediaValues, - initialYahooFinanceValues, } from '../constant'; import useGraphStore from '../store'; import { @@ -80,26 +64,9 @@ export const useInitializeOperatorParams = () => { ...initialKeywordExtractValues, llm_id: llmId, }, - [Operator.DuckDuckGo]: initialDuckValues, - [Operator.Wikipedia]: initialWikipediaValues, - [Operator.PubMed]: initialPubMedValues, - [Operator.ArXiv]: initialArXivValues, - [Operator.Google]: initialGoogleValues, - [Operator.Bing]: initialBingValues, - [Operator.GoogleScholar]: initialGoogleScholarValues, - [Operator.DeepL]: initialDeepLValues, - [Operator.SearXNG]: initialSearXNGValues, - [Operator.GitHub]: initialGithubValues, - [Operator.BaiduFanyi]: initialBaiduFanyiValues, - [Operator.QWeather]: initialQWeatherValues, [Operator.ExeSQL]: initialExeSqlValues, [Operator.Switch]: initialSwitchValues, - [Operator.WenCai]: initialWenCaiValues, - [Operator.AkShare]: initialAkShareValues, - [Operator.YahooFinance]: initialYahooFinanceValues, - [Operator.Jin10]: initialJin10Values, [Operator.Concentrator]: initialConcentratorValues, - [Operator.TuShare]: initialTuShareValues, [Operator.Note]: initialNoteValues, [Operator.Crawler]: initialCrawlerValues, [Operator.Invoke]: initialInvokeValues, @@ -110,10 +77,11 @@ export const useInitializeOperatorParams = () => { [Operator.WaitingDialogue]: initialWaitingDialogueValues, [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, [Operator.Tool]: {}, - [Operator.TavilySearch]: initialTavilyValues, [Operator.UserFillUp]: initialUserFillUpValues, [Operator.StringTransform]: initialStringTransformValues, - [Operator.TavilyExtract]: initialTavilyExtractValues, + [Operator.Parser]: initialParserValues, + [Operator.Chunker]: initialChunkerValues, + [Operator.Tokenizer]: initialTokenizerValues, }; }, [llmId]); diff --git a/web/src/pages/data-flow/hooks/use-agent-tool-initial-values.ts b/web/src/pages/data-flow/hooks/use-agent-tool-initial-values.ts deleted file mode 100644 index 05864184d99..00000000000 --- a/web/src/pages/data-flow/hooks/use-agent-tool-initial-values.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { omit, pick } from 'lodash'; -import { useCallback } from 'react'; -import { Operator } from '../constant'; -import { useInitializeOperatorParams } from './use-add-node'; - -export function useAgentToolInitialValues() { - const { initialFormValuesMap } = useInitializeOperatorParams(); - - const initializeAgentToolValues = useCallback( - (operatorName: Operator) => { - const initialValues = initialFormValuesMap[operatorName]; - - switch (operatorName) { - case Operator.Retrieval: - return { - ...omit(initialValues, 'query'), - description: '', - }; - case (Operator.TavilySearch, Operator.TavilyExtract): - return { - api_key: '', - }; - case Operator.ExeSQL: - return omit(initialValues, 'sql'); - case Operator.Bing: - return omit(initialValues, 'query'); - case Operator.YahooFinance: - return omit(initialValues, 'stock_code'); - - case Operator.Email: - return pick( - initialValues, - 'smtp_server', - 'smtp_port', - 'email', - 'password', - 'sender_name', - ); - - case Operator.DuckDuckGo: - return pick(initialValues, 'top_n', 'channel'); - - case Operator.Wikipedia: - return pick(initialValues, 'top_n', 'language'); - case Operator.Google: - return pick(initialValues, 'api_key', 'country', 'language'); - case Operator.GoogleScholar: - return omit(initialValues, 'query', 'outputs'); - case Operator.ArXiv: - return pick(initialValues, 'top_n', 'sort_by'); - case Operator.PubMed: - return pick(initialValues, 'top_n', 'email'); - case Operator.GitHub: - return pick(initialValues, 'top_n'); - case Operator.WenCai: - return pick(initialValues, 'top_n', 'query_type'); - case Operator.Code: - return {}; - case Operator.SearXNG: - return pick(initialValues, 'searxng_url', 'top_n'); - - default: - return initialValues; - } - }, - [initialFormValuesMap], - ); - - return { initializeAgentToolValues }; -} diff --git a/web/src/pages/data-flow/index.tsx b/web/src/pages/data-flow/index.tsx index 4a1fc81aee4..10798b5e862 100644 --- a/web/src/pages/data-flow/index.tsx +++ b/web/src/pages/data-flow/index.tsx @@ -29,7 +29,7 @@ import { } from 'lucide-react'; import { ComponentPropsWithoutRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import AgentCanvas from './canvas'; +import DataFlowCanvas from './canvas'; import { DropdownProvider } from './canvas/context'; import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; import { useFetchDataOnMount } from './hooks/use-fetch-data'; @@ -161,10 +161,10 @@ export default function DataFlow() { - + > {fileUploadVisible && ( diff --git a/web/src/pages/data-flow/operator-icon.tsx b/web/src/pages/data-flow/operator-icon.tsx index b8ebc34ba2b..a11145ace75 100644 --- a/web/src/pages/data-flow/operator-icon.tsx +++ b/web/src/pages/data-flow/operator-icon.tsx @@ -1,17 +1,3 @@ -import { ReactComponent as ArxivIcon } from '@/assets/svg/arxiv.svg'; -import { ReactComponent as BingIcon } from '@/assets/svg/bing.svg'; -import { ReactComponent as CrawlerIcon } from '@/assets/svg/crawler.svg'; -import { ReactComponent as DuckIcon } from '@/assets/svg/duck.svg'; -import { ReactComponent as GithubIcon } from '@/assets/svg/github.svg'; -import { ReactComponent as GoogleScholarIcon } from '@/assets/svg/google-scholar.svg'; -import { ReactComponent as GoogleIcon } from '@/assets/svg/google.svg'; -import { ReactComponent as PubMedIcon } from '@/assets/svg/pubmed.svg'; -import { ReactComponent as SearXNGIcon } from '@/assets/svg/searxng.svg'; -import { ReactComponent as TavilyIcon } from '@/assets/svg/tavily.svg'; -import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg'; -import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg'; -import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg'; - import { IconFont } from '@/components/icon-font'; import { cn } from '@/lib/utils'; import { HousePlus } from 'lucide-react'; @@ -39,30 +25,13 @@ export const OperatorIconMap = { [Operator.Email]: 'sendemail-0', }; -export const SVGIconMap = { - [Operator.ArXiv]: ArxivIcon, - [Operator.GitHub]: GithubIcon, - [Operator.Bing]: BingIcon, - [Operator.DuckDuckGo]: DuckIcon, - [Operator.Google]: GoogleIcon, - [Operator.GoogleScholar]: GoogleScholarIcon, - [Operator.PubMed]: PubMedIcon, - [Operator.SearXNG]: SearXNGIcon, - [Operator.TavilyExtract]: TavilyIcon, - [Operator.TavilySearch]: TavilyIcon, - [Operator.Wikipedia]: WikipediaIcon, - [Operator.YahooFinance]: YahooFinanceIcon, - [Operator.WenCai]: WenCaiIcon, - [Operator.Crawler]: CrawlerIcon, -}; - const Empty = () => { return
    ; }; const OperatorIcon = ({ name, className }: IProps) => { const Icon = OperatorIconMap[name as keyof typeof OperatorIconMap] || Empty; - const SvgIcon = SVGIconMap[name as keyof typeof SVGIconMap] || Empty; + const SvgIcon = Empty; if (name === Operator.Begin) { return ( @@ -80,7 +49,7 @@ const OperatorIcon = ({ name, className }: IProps) => { return typeof Icon === 'string' ? ( ) : ( - + ); }; From 936f27e9e5fea0731c9d96c589ed226b715bd9a2 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Mon, 8 Sep 2025 19:00:52 +0800 Subject: [PATCH 0642/1187] Feat: add LongCat-Flash-Chat (#9973) ### What problem does this PR solve? Add LongCat-Flash-Chat from Meituan, deepseek v3.1 from SiliconFlow, kimi-k2-09-05-preview and kimi-k2-turbo-preview from Moonshot. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- conf/llm_factories.json | 43 ++++++++++++++++++++++++++++++ rag/llm/chat_model.py | 14 +++++++++- web/src/assets/svg/llm/longcat.svg | 7 +++++ web/src/constants/llm.ts | 2 ++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 web/src/assets/svg/llm/longcat.svg diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 20d817df401..476307206a0 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -755,6 +755,20 @@ "model_type": "chat", "is_tools": true }, + { + "llm_name": "kimi-k2-0905-preview", + "tags": "LLM,CHAT,256k", + "max_tokens": 262144, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "kimi-k2-turbo-preview", + "tags": "LLM,CHAT,256k", + "max_tokens": 262144, + "model_type": "chat", + "is_tools": true + }, { "llm_name": "kimi-latest", "tags": "LLM,CHAT,8k,32k,128k", @@ -2794,6 +2808,20 @@ "model_type": "chat", "is_tools": true }, + { + "llm_name": "Pro/deepseek-ai/DeepSeek-V3.1", + "tags": "LLM,CHAT,160k", + "max_tokens": 160000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "deepseek-ai/DeepSeek-V3.1", + "tags": "LLM,CHAT,160", + "max_tokens": 160000, + "model_type": "chat", + "is_tools": true + }, { "llm_name": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", "tags": "LLM,CHAT,32k", @@ -4448,6 +4476,21 @@ "is_tools": false } ] + }, + { + "name": "Meituan", + "logo": "", + "tags": "LLM", + "status": "1", + "llm": [ + { + "llm_name": "LongCat-Flash-Chat", + "tags": "LLM,CHAT,8000", + "max_tokens": 8000, + "model_type": "chat", + "is_tools": true + } + ] } ] } diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index adc4a3c5af8..bb04b520c30 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -155,7 +155,10 @@ def _chat(self, history, gen_conf, **kwargs): def _chat_streamly(self, history, gen_conf, **kwargs): logging.info("[HISTORY STREAMLY]" + json.dumps(history, ensure_ascii=False, indent=4)) reasoning_start = False - response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf, stop=kwargs.get("stop")) + if kwargs.get("stop") or "stop" in gen_conf: + response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf, stop=kwargs.get("stop")) + else: + response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf) for resp in response: if not resp.choices: continue @@ -1353,6 +1356,15 @@ def __init__(self, key, model_name, base_url="https://api.302.ai/v1", **kwargs): super().__init__(key, model_name, base_url, **kwargs) +class MeituanChat(Base): + _FACTORY_NAME = "Meituan" + + def __init__(self, key, model_name, base_url="https://api.longcat.chat/openai", **kwargs): + if not base_url: + base_url = "https://api.longcat.chat/openai" + super().__init__(key, model_name, base_url, **kwargs) + + class LiteLLMBase(ABC): _FACTORY_NAME = ["Tongyi-Qianwen", "Bedrock", "Moonshot", "xAI", "DeepInfra", "Groq", "Cohere", "Gemini", "DeepSeek", "NVIDIA", "TogetherAI", "Anthropic", "Ollama"] diff --git a/web/src/assets/svg/llm/longcat.svg b/web/src/assets/svg/llm/longcat.svg new file mode 100644 index 00000000000..f123daf3a38 --- /dev/null +++ b/web/src/assets/svg/llm/longcat.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/web/src/constants/llm.ts b/web/src/constants/llm.ts index 172c136e5e2..2fde6acedb8 100644 --- a/web/src/constants/llm.ts +++ b/web/src/constants/llm.ts @@ -54,6 +54,7 @@ export enum LLMFactory { DeepInfra = 'DeepInfra', Grok = 'Grok', XAI = 'xAI', + Meituan = 'Meituan', } // Please lowercase the file name @@ -113,4 +114,5 @@ export const IconMap = { [LLMFactory.DeepInfra]: 'deepinfra', [LLMFactory.Grok]: 'grok', [LLMFactory.XAI]: 'xai', + [LLMFactory.Meituan]: 'longcat', }; From a255c78b5987c290332d91039e80472677ca3785 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 9 Sep 2025 09:50:46 +0800 Subject: [PATCH 0643/1187] Feat: Add ParserForm to the data pipeline #9869 (#9986) ### What problem does this PR solve? Feat: Add ParserForm to the data pipeline #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../node/dropdown/next-step-dropdown.tsx | 32 ++-- web/src/pages/data-flow/constant.tsx | 4 +- .../data-flow/form-sheet/form-config-map.tsx | 12 ++ .../data-flow/form/chunker-form/index.tsx | 140 ++++++++++++++++++ .../data-flow/form/parser-form/index.tsx | 6 +- .../data-flow/form/tokenizer-form/index.tsx | 140 ++++++++++++++++++ web/src/pages/data-flow/operator-icon.tsx | 17 ++- 7 files changed, 322 insertions(+), 29 deletions(-) create mode 100644 web/src/pages/data-flow/form/chunker-form/index.tsx create mode 100644 web/src/pages/data-flow/form/tokenizer-form/index.tsx diff --git a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx index 6d9f3245370..88c35dae6a0 100644 --- a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx @@ -17,9 +17,6 @@ import { TooltipTrigger, } from '@/components/ui/tooltip'; import { IModalProps } from '@/interfaces/common'; -import { Operator } from '@/pages/agent/constant'; -import { AgentInstanceContext, HandleContext } from '@/pages/agent/context'; -import OperatorIcon from '@/pages/agent/operator-icon'; import { Position } from '@xyflow/react'; import { t } from 'i18next'; import { lowerFirst } from 'lodash'; @@ -32,6 +29,9 @@ import { useRef, } from 'react'; import { useTranslation } from 'react-i18next'; +import { Operator } from '../../../constant'; +import { AgentInstanceContext, HandleContext } from '../../../context'; +import OperatorIcon from '../../../operator-icon'; type OperatorItemProps = { operators: Operator[]; @@ -134,7 +134,13 @@ function AccordionOperators({ @@ -186,23 +192,7 @@ function AccordionOperators({ diff --git a/web/src/pages/data-flow/constant.tsx b/web/src/pages/data-flow/constant.tsx index ea158722f6f..d1d124f488b 100644 --- a/web/src/pages/data-flow/constant.tsx +++ b/web/src/pages/data-flow/constant.tsx @@ -317,7 +317,7 @@ export const initialCodeValues = { export const initialWaitingDialogueValues = {}; -export const initialChunkerValues = {}; +export const initialChunkerValues = { outputs: {} }; export const initialTokenizerValues = {}; @@ -388,7 +388,7 @@ export const initialStringTransformValues = { }, }; -export const initialParserValues = {}; +export const initialParserValues = { outputs: {} }; export const CategorizeAnchorPointPositions = [ { top: 1, right: 34 }, diff --git a/web/src/pages/data-flow/form-sheet/form-config-map.tsx b/web/src/pages/data-flow/form-sheet/form-config-map.tsx index 44dc2ca2eec..f00d93009c4 100644 --- a/web/src/pages/data-flow/form-sheet/form-config-map.tsx +++ b/web/src/pages/data-flow/form-sheet/form-config-map.tsx @@ -2,6 +2,7 @@ import { Operator } from '../constant'; import AgentForm from '../form/agent-form'; import BeginForm from '../form/begin-form'; import CategorizeForm from '../form/categorize-form'; +import ChunkerForm from '../form/chunker-form'; import CodeForm from '../form/code-form'; import CrawlerForm from '../form/crawler-form'; import EmailForm from '../form/email-form'; @@ -11,11 +12,13 @@ import IterationForm from '../form/iteration-form'; import IterationStartForm from '../form/iteration-start-from'; import KeywordExtractForm from '../form/keyword-extract-form'; import MessageForm from '../form/message-form'; +import ParserForm from '../form/parser-form'; import RelevantForm from '../form/relevant-form'; import RetrievalForm from '../form/retrieval-form/next'; import RewriteQuestionForm from '../form/rewrite-question-form'; import StringTransformForm from '../form/string-transform-form'; import SwitchForm from '../form/switch-form'; +import TokenizerForm from '../form/tokenizer-form'; import UserFillUpForm from '../form/user-fill-up-form'; export const FormConfigMap = { @@ -82,4 +85,13 @@ export const FormConfigMap = { [Operator.StringTransform]: { component: StringTransformForm, }, + [Operator.Parser]: { + component: ParserForm, + }, + [Operator.Chunker]: { + component: ChunkerForm, + }, + [Operator.Tokenizer]: { + component: TokenizerForm, + }, }; diff --git a/web/src/pages/data-flow/form/chunker-form/index.tsx b/web/src/pages/data-flow/form/chunker-form/index.tsx new file mode 100644 index 00000000000..cfc32e82f56 --- /dev/null +++ b/web/src/pages/data-flow/form/chunker-form/index.tsx @@ -0,0 +1,140 @@ +import { FormContainer } from '@/components/form-container'; +import NumberInput from '@/components/originui/number-input'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialChunkerValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; +import { buildOutputList } from '../../utils/build-output-list'; +import { ApiKeyField } from '../components/api-key-field'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +const outputList = buildOutputList(initialChunkerValues.outputs); + +export const GoogleFormPartialSchema = { + api_key: z.string(), + country: z.string(), + language: z.string(), +}; + +export const FormSchema = z.object({ + ...GoogleFormPartialSchema, + q: z.string(), + start: z.number(), + num: z.number(), +}); + +export function GoogleFormWidgets() { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + return ( + <> + ( + + {t('country')} + + + + + + )} + /> + ( + + {t('language')} + + + + + + )} + /> + + ); +} + +const ChunkerForm = ({ node }: INextOperatorForm) => { + const { t } = useTranslate('flow'); + const defaultValues = useFormValues(initialChunkerValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + ( + + {t('flowStart')} + + + + + + )} + /> + ( + + {t('flowNum')} + + + + + + )} + /> + + + +
    + +
    +
    + ); +}; + +export default memo(ChunkerForm); diff --git a/web/src/pages/data-flow/form/parser-form/index.tsx b/web/src/pages/data-flow/form/parser-form/index.tsx index 3418d83a923..1a84c304aa3 100644 --- a/web/src/pages/data-flow/form/parser-form/index.tsx +++ b/web/src/pages/data-flow/form/parser-form/index.tsx @@ -14,7 +14,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { memo } from 'react'; import { useForm, useFormContext } from 'react-hook-form'; import { z } from 'zod'; -import { initialGoogleValues } from '../../constant'; +import { initialParserValues } from '../../constant'; import { useFormValues } from '../../hooks/use-form-values'; import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; @@ -25,7 +25,7 @@ import { FormWrapper } from '../components/form-wrapper'; import { Output } from '../components/output'; import { QueryVariable } from '../components/query-variable'; -const outputList = buildOutputList(initialGoogleValues.outputs); +const outputList = buildOutputList(initialParserValues.outputs); export const GoogleFormPartialSchema = { api_key: z.string(), @@ -84,7 +84,7 @@ export function GoogleFormWidgets() { const ParserForm = ({ node }: INextOperatorForm) => { const { t } = useTranslate('flow'); - const defaultValues = useFormValues(initialGoogleValues, node); + const defaultValues = useFormValues(initialParserValues, node); const form = useForm>({ defaultValues, diff --git a/web/src/pages/data-flow/form/tokenizer-form/index.tsx b/web/src/pages/data-flow/form/tokenizer-form/index.tsx new file mode 100644 index 00000000000..b48581390b2 --- /dev/null +++ b/web/src/pages/data-flow/form/tokenizer-form/index.tsx @@ -0,0 +1,140 @@ +import { FormContainer } from '@/components/form-container'; +import NumberInput from '@/components/originui/number-input'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { useTranslate } from '@/hooks/common-hooks'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm, useFormContext } from 'react-hook-form'; +import { z } from 'zod'; +import { initialChunkerValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; +import { buildOutputList } from '../../utils/build-output-list'; +import { ApiKeyField } from '../components/api-key-field'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { QueryVariable } from '../components/query-variable'; + +const outputList = buildOutputList(initialChunkerValues.outputs); + +export const GoogleFormPartialSchema = { + api_key: z.string(), + country: z.string(), + language: z.string(), +}; + +export const FormSchema = z.object({ + ...GoogleFormPartialSchema, + q: z.string(), + start: z.number(), + num: z.number(), +}); + +export function GoogleFormWidgets() { + const form = useFormContext(); + const { t } = useTranslate('flow'); + + return ( + <> + ( + + {t('country')} + + + + + + )} + /> + ( + + {t('language')} + + + + + + )} + /> + + ); +} + +const TokenizerForm = ({ node }: INextOperatorForm) => { + const { t } = useTranslate('flow'); + const defaultValues = useFormValues(initialChunkerValues, node); + + const form = useForm>({ + defaultValues, + resolver: zodResolver(FormSchema), + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + + + ( + + {t('flowStart')} + + + + + + )} + /> + ( + + {t('flowNum')} + + + + + + )} + /> + + + +
    + +
    +
    + ); +}; + +export default memo(TokenizerForm); diff --git a/web/src/pages/data-flow/operator-icon.tsx b/web/src/pages/data-flow/operator-icon.tsx index a11145ace75..2184569d6de 100644 --- a/web/src/pages/data-flow/operator-icon.tsx +++ b/web/src/pages/data-flow/operator-icon.tsx @@ -1,6 +1,11 @@ import { IconFont } from '@/components/icon-font'; import { cn } from '@/lib/utils'; -import { HousePlus } from 'lucide-react'; +import { + FileChartColumnIncreasing, + Grid3x3, + HousePlus, + ListMinus, +} from 'lucide-react'; import { Operator } from './constant'; interface IProps { @@ -25,13 +30,19 @@ export const OperatorIconMap = { [Operator.Email]: 'sendemail-0', }; +export const SVGIconMap = { + [Operator.Parser]: FileChartColumnIncreasing, + [Operator.Chunker]: Grid3x3, + [Operator.Tokenizer]: ListMinus, +}; + const Empty = () => { return
    ; }; const OperatorIcon = ({ name, className }: IProps) => { const Icon = OperatorIconMap[name as keyof typeof OperatorIconMap] || Empty; - const SvgIcon = Empty; + const SvgIcon = SVGIconMap[name as keyof typeof SVGIconMap] || Empty; if (name === Operator.Begin) { return ( @@ -49,7 +60,7 @@ const OperatorIcon = ({ name, className }: IProps) => { return typeof Icon === 'string' ? ( ) : ( - + ); }; From c4f43a395d4e1cdb89160439f80efe32c55a1502 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Tue, 9 Sep 2025 10:52:18 +0800 Subject: [PATCH 0644/1187] Fix: re sub error. (#9985) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- agent/component/llm.py | 2 +- api/utils/api_utils.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/agent/component/llm.py b/agent/component/llm.py index 7f1f2eb361e..b13c4a87e29 100644 --- a/agent/component/llm.py +++ b/agent/component/llm.py @@ -160,7 +160,7 @@ def _extract_prompts(self, sys_prompt): if not r: continue pts[tag.lower()] = r.group(1) - sys_prompt = re.sub(rf"<{tag}>(.*?)", sys_prompt, flags=re.DOTALL|re.IGNORECASE) + sys_prompt = re.sub(rf"<{tag}>(.*?)", "", sys_prompt, flags=re.DOTALL|re.IGNORECASE) return pts, sys_prompt def _generate(self, msg:list[dict], **kwargs) -> str: diff --git a/api/utils/api_utils.py b/api/utils/api_utils.py index 30cf12aae5c..c66347d3e13 100644 --- a/api/utils/api_utils.py +++ b/api/utils/api_utils.py @@ -320,6 +320,8 @@ def construct_error_response(e): def token_required(func): @wraps(func) def decorated_function(*args, **kwargs): + if os.environ.get("DISABLE_SDK"): + return get_json_result(data=False, message="`Authorization` can't be empty") authorization_str = flask_request.headers.get("Authorization") if not authorization_str: return get_json_result(data=False, message="`Authorization` can't be empty") From e8dcdfb9f0af0f44393d84d52bf423d16762bfd3 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 9 Sep 2025 12:32:22 +0800 Subject: [PATCH 0645/1187] Fix: Issue of ineffective weight adjustment for retrieval_test API-related functions #9854 (#9989) ### What problem does this PR solve? Fix: Issue of ineffective weight adjustment for retrieval_test API-related functions #9854 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/interfaces/request/knowledge.ts | 2 +- web/src/pages/dataset/testing/testing-form.tsx | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/web/src/interfaces/request/knowledge.ts b/web/src/interfaces/request/knowledge.ts index c43e5b2d193..de1b00b438c 100644 --- a/web/src/interfaces/request/knowledge.ts +++ b/web/src/interfaces/request/knowledge.ts @@ -1,7 +1,7 @@ export interface ITestRetrievalRequestBody { question: string; similarity_threshold: number; - keywords_similarity_weight: number; + vector_similarity_weight: number; rerank_id?: string; top_k?: number; use_kg?: boolean; diff --git a/web/src/pages/dataset/testing/testing-form.tsx b/web/src/pages/dataset/testing/testing-form.tsx index ba614ed6781..fb9f3189be1 100644 --- a/web/src/pages/dataset/testing/testing-form.tsx +++ b/web/src/pages/dataset/testing/testing-form.tsx @@ -12,11 +12,11 @@ import { topKSchema, } from '@/components/rerank'; import { - initialKeywordsSimilarityWeightValue, initialSimilarityThresholdValue, - keywordsSimilarityWeightSchema, + initialVectorSimilarityWeightValue, SimilaritySliderFormField, similarityThresholdSchema, + vectorSimilarityWeightSchema, } from '@/components/similarity-slider'; import { ButtonLoading } from '@/components/ui/button'; import { @@ -52,16 +52,18 @@ export default function TestingForm({ message: t('knowledgeDetails.testTextPlaceholder'), }), ...similarityThresholdSchema, - ...keywordsSimilarityWeightSchema, + ...vectorSimilarityWeightSchema, ...topKSchema, + use_kg: z.boolean().optional(), }); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { ...initialSimilarityThresholdValue, - ...initialKeywordsSimilarityWeightValue, + ...initialVectorSimilarityWeightValue, ...initialTopKValue, + use_kg: false, }, }); @@ -82,7 +84,6 @@ export default function TestingForm({
    From 79076ffb5f9f8c3f50eab89dc6b822e63d41221e Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Tue, 9 Sep 2025 14:45:43 +0800 Subject: [PATCH 0646/1187] Fix: remove 2 prompts. (#9990) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/canvas_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py index 61a76211c98..691d91c3977 100644 --- a/api/apps/canvas_app.py +++ b/api/apps/canvas_app.py @@ -474,12 +474,12 @@ def sessions(canvas_id): @manager.route('/prompts', methods=['GET']) # noqa: F821 @login_required def prompts(): - from rag.prompts.prompts import ANALYZE_TASK_SYSTEM, ANALYZE_TASK_USER, NEXT_STEP, REFLECT, SUMMARY4MEMORY, RANK_MEMORY, CITATION_PROMPT_TEMPLATE + from rag.prompts.prompts import ANALYZE_TASK_SYSTEM, ANALYZE_TASK_USER, NEXT_STEP, REFLECT, CITATION_PROMPT_TEMPLATE return get_json_result(data={ "task_analysis": ANALYZE_TASK_SYSTEM + ANALYZE_TASK_USER, "plan_generation": NEXT_STEP, "reflection": REFLECT, - "context_summary": SUMMARY4MEMORY, - "context_ranking": RANK_MEMORY, + #"context_summary": SUMMARY4MEMORY, + #"context_ranking": RANK_MEMORY, "citation_guidelines": CITATION_PROMPT_TEMPLATE }) From fcdde26a7f4a2d7485e7591ec9d83544d824931d Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 9 Sep 2025 17:04:37 +0800 Subject: [PATCH 0647/1187] Fix: Highlight the edges after running #9538 (#9994) ### What problem does this PR solve? Fix: Highlight the edges after running #9538 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/llm-setting-items/next.tsx | 5 +++ web/src/interfaces/database/agent.ts | 2 +- web/src/pages/agent/canvas/edge/index.tsx | 36 +++++++------------ web/src/pages/agent/share/index.tsx | 4 +-- .../search-setting-aisummery-config.tsx | 4 +++ 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/web/src/components/llm-setting-items/next.tsx b/web/src/components/llm-setting-items/next.tsx index c3fb3e3bbaa..14e6e532c93 100644 --- a/web/src/components/llm-setting-items/next.tsx +++ b/web/src/components/llm-setting-items/next.tsx @@ -134,6 +134,7 @@ export function LlmSettingFieldItems({ label="temperature" max={1} step={0.01} + min={0} > ); diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index 39a44225f1f..4fd36de2f00 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -38,7 +38,7 @@ export type DSLComponents = Record; export interface DSL { components: DSLComponents; history: any[]; - path?: string[][]; + path?: string[]; answer?: any[]; graph?: IGraph; messages: Message[]; diff --git a/web/src/pages/agent/canvas/edge/index.tsx b/web/src/pages/agent/canvas/edge/index.tsx index 3e1b57e8560..0fa23b3b96b 100644 --- a/web/src/pages/agent/canvas/edge/index.tsx +++ b/web/src/pages/agent/canvas/edge/index.tsx @@ -39,7 +39,7 @@ function InnerButtonEdge({ targetPosition, }); const selectedStyle = useMemo(() => { - return selected ? { strokeWidth: 1, stroke: 'rgba(76, 164, 231, 1)' } : {}; + return selected ? { strokeWidth: 1, stroke: 'var(--accent-primary)' } : {}; }, [selected]); const onEdgeClick = () => { @@ -49,31 +49,21 @@ function InnerButtonEdge({ // highlight the nodes that the workflow passes through const { data: flowDetail } = useFetchAgent(); - const graphPath = useMemo(() => { - // TODO: this will be called multiple times + const showHighlight = useMemo(() => { const path = flowDetail?.dsl?.path ?? []; - // The second to last - const previousGraphPath: string[] = path.at(-2) ?? []; - let graphPath: string[] = path.at(-1) ?? []; - // The last of the second to last article - const previousLatestElement = previousGraphPath.at(-1); - if (previousGraphPath.length > 0 && previousLatestElement) { - graphPath = [previousLatestElement, ...graphPath]; - } - return Array.isArray(graphPath) ? graphPath : []; - }, [flowDetail.dsl?.path]); - - const highlightStyle = useMemo(() => { - const idx = graphPath.findIndex((x) => x === source); + const idx = path.findIndex((x) => x === target); if (idx !== -1) { - // The set of elements following source - const slicedGraphPath = graphPath.slice(idx + 1); - if (slicedGraphPath.some((x) => x === target)) { - return { strokeWidth: 1, stroke: 'red' }; + let index = idx - 1; + while (index >= 0) { + if (path[index] === source) { + return { strokeWidth: 1, stroke: 'var(--accent-primary)' }; + } + index--; } + return {}; } return {}; - }, [source, target, graphPath]); + }, [flowDetail?.dsl?.path, source, target]); const visible = useMemo(() => { return ( @@ -89,8 +79,8 @@ function InnerButtonEdge({ diff --git a/web/src/pages/agent/share/index.tsx b/web/src/pages/agent/share/index.tsx index 9246d9bcf37..b33c1b3cecf 100644 --- a/web/src/pages/agent/share/index.tsx +++ b/web/src/pages/agent/share/index.tsx @@ -107,9 +107,7 @@ const ChatContainer = () => { const handleReset = () => { resetSession(); clearEventList(); - if (isTaskMode) { - showBeginParameterDialog(); - } + showBeginParameterDialog(); }; if (!conversationId) { return
    empty
    ; diff --git a/web/src/pages/next-search/search-setting-aisummery-config.tsx b/web/src/pages/next-search/search-setting-aisummery-config.tsx index 6f30de26a32..8764c3014a6 100644 --- a/web/src/pages/next-search/search-setting-aisummery-config.tsx +++ b/web/src/pages/next-search/search-setting-aisummery-config.tsx @@ -186,6 +186,7 @@ export function LlmSettingFieldItems({ checkName={getFieldWithPrefix('temperatureEnabled')} label="temperature" max={1} + min={0} step={0.01} onChange={() => { checkParameterIsEquel(); @@ -197,6 +198,7 @@ export function LlmSettingFieldItems({ label="topP" max={1} step={0.01} + min={0} onChange={() => { checkParameterIsEquel(); }} @@ -207,6 +209,7 @@ export function LlmSettingFieldItems({ label="presencePenalty" max={1} step={0.01} + min={0} onChange={() => { checkParameterIsEquel(); }} @@ -217,6 +220,7 @@ export function LlmSettingFieldItems({ label="frequencyPenalty" max={1} step={0.01} + min={0} onChange={() => { checkParameterIsEquel(); }} From 776ea078a6714613dee6f99b175466b485975469 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Tue, 9 Sep 2025 18:50:43 +0800 Subject: [PATCH 0648/1187] Fix: Optimized the table of contents style and homepage card layout #3221 (#9993) ### What problem does this PR solve? Fix: Optimized the table of contents style and homepage card layout #3221 - Added background color, text color, and shadow styles to the Markdown table of contents - Optimized the date display style in the HomeCard component to prevent overflow - Standardized the translation of "dataset" to "knowledge base" to improve terminology consistency ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../api-service/chat-overview-modal/markdown-toc.tsx | 3 +-- web/src/components/home-card.tsx | 2 +- web/src/locales/zh.ts | 6 +++--- web/src/pages/home/datasets.tsx | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx b/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx index 7694a0cb906..498026b09b7 100644 --- a/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx +++ b/web/src/components/api-service/chat-overview-modal/markdown-toc.tsx @@ -53,14 +53,13 @@ const MarkdownToc: React.FC = ({ content }) => { return (
    -

    +

    {formatDate(data.update_time)}

    {sharedBadge} diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 17fbedacff1..952a9d3c8ed 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -81,7 +81,7 @@ export default { flow: '智能体', search: '搜索', welcome: '欢迎来到', - dataset: '数据集', + dataset: '知识库', }, knowledgeList: { welcome: '欢迎回来', @@ -104,7 +104,7 @@ export default { retrievalTestingDescription: '进行检索测试,检查 RAGFlow 是否能够为大语言模型(LLM)恢复预期的内容。', Parse: '解析', - dataset: '数据集', + dataset: '知识库', testing: '检索测试', configuration: '配置', knowledgeGraph: '知识图谱', @@ -1474,7 +1474,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 name: '姓名', avatar: '头像', description: '描述', - datasets: '数据集', + datasets: '知识库', rerankModel: 'rerank 模型', AISummary: 'AI 总结', enableWebSearch: '启用网页搜索', diff --git a/web/src/pages/home/datasets.tsx b/web/src/pages/home/datasets.tsx index f8e27e66c01..a397d68e6ad 100644 --- a/web/src/pages/home/datasets.tsx +++ b/web/src/pages/home/datasets.tsx @@ -30,7 +30,7 @@ export function Datasets() {
    ) : ( -
    +
    {kbs ?.slice(0, 6) .map((dataset) => ( From 906969fe4e92def8f9b0f7f9ff156f8493fc6f1a Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Tue, 9 Sep 2025 19:45:10 +0800 Subject: [PATCH 0649/1187] Fix: exesql issue. (#9995) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- agent/component/agent_with_tools.py | 2 +- agent/tools/exesql.py | 15 ++++++++++++++- api/apps/chunk_app.py | 4 ++++ api/apps/sdk/session.py | 3 +++ rag/prompts/prompts.py | 13 ++++++++----- 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/agent/component/agent_with_tools.py b/agent/component/agent_with_tools.py index 4019c89ae85..6b57fa1202c 100644 --- a/agent/component/agent_with_tools.py +++ b/agent/component/agent_with_tools.py @@ -166,7 +166,7 @@ def _invoke(self, **kwargs): _, 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): + for delta_ans, tk in self._react_with_tools_streamly(prompt, msg, use_tools, user_defined_prompt): ans += delta_ans if ans.find("**ERROR**") >= 0: diff --git a/agent/tools/exesql.py b/agent/tools/exesql.py index 48f9e3b7418..317941713e7 100644 --- a/agent/tools/exesql.py +++ b/agent/tools/exesql.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json import os import re from abc import ABC @@ -93,8 +94,20 @@ def convert_decimals(obj): sql = kwargs.get("sql") if not sql: raise Exception("SQL for `ExeSQL` MUST not be empty.") - sqls = sql.split(";") + vars = self.get_input_elements_from_text(sql) + args = {} + for k, o in vars.items(): + args[k] = o["value"] + if not isinstance(args[k], str): + try: + args[k] = json.dumps(args[k], ensure_ascii=False) + except Exception: + args[k] = str(args[k]) + self.set_input_value(k, args[k]) + sql = self.string_format(sql, args) + + sqls = sql.split(";") if self._param.db_type in ["mysql", "mariadb"]: db = pymysql.connect(db=self._param.database, user=self._param.username, host=self._param.host, port=self._param.port, password=self._param.password) diff --git a/api/apps/chunk_app.py b/api/apps/chunk_app.py index e790b4e2de7..9b4c341b6d3 100644 --- a/api/apps/chunk_app.py +++ b/api/apps/chunk_app.py @@ -291,6 +291,10 @@ def retrieval_test(): kb_ids = req["kb_id"] if isinstance(kb_ids, str): kb_ids = [kb_ids] + if not kb_ids: + return get_json_result(data=False, message='Please specify dataset firstly.', + code=settings.RetCode.DATA_ERROR) + doc_ids = req.get("doc_ids", []) use_kg = req.get("use_kg", False) top = int(req.get("top_k", 1024)) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 2a2f534e7ea..80e45a778b4 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -941,6 +941,9 @@ def retrieval_test_embedded(): kb_ids = req["kb_id"] if isinstance(kb_ids, str): kb_ids = [kb_ids] + if not kb_ids: + return get_json_result(data=False, message='Please specify dataset firstly.', + code=settings.RetCode.DATA_ERROR) doc_ids = req.get("doc_ids", []) similarity_threshold = float(req.get("similarity_threshold", 0.0)) vector_similarity_weight = float(req.get("vector_similarity_weight", 0.3)) diff --git a/rag/prompts/prompts.py b/rag/prompts/prompts.py index 5077d51f45a..b15019b6ff3 100644 --- a/rag/prompts/prompts.py +++ b/rag/prompts/prompts.py @@ -158,7 +158,7 @@ def draw_node(k, line): def citation_prompt(user_defined_prompts: dict={}) -> str: - template = PROMPT_JINJA_ENV.from_string(CITATION_PROMPT_TEMPLATE) + template = PROMPT_JINJA_ENV.from_string(user_defined_prompts.get("citation_guidelines", CITATION_PROMPT_TEMPLATE)) return template.render() @@ -343,9 +343,12 @@ def analyze_task(chat_mdl, prompt, task_name, tools_description: list[dict], use tools_desc = tool_schema(tools_description) context = "" - template = PROMPT_JINJA_ENV.from_string(ANALYZE_TASK_USER) + if user_defined_prompts.get("task_analysis"): + template = PROMPT_JINJA_ENV.from_string(user_defined_prompts["task_analysis"]) + else: + 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) - kwd = chat_mdl.chat(ANALYZE_TASK_SYSTEM,[{"role": "user", "content": context}], {}) + kwd = chat_mdl.chat(context, [{"role": "user", "content": "Please analyze it."}]) if isinstance(kwd, tuple): kwd = kwd[0] kwd = re.sub(r"^.*", "", kwd, flags=re.DOTALL) @@ -358,7 +361,7 @@ def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc, if not tools_description: return "" desc = tool_schema(tools_description) - template = PROMPT_JINJA_ENV.from_string(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`." hist = deepcopy(history) if hist[-1]["role"] == "user": @@ -375,7 +378,7 @@ def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc, 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] goal = history[1]["content"] - template = PROMPT_JINJA_ENV.from_string(REFLECT) + template = PROMPT_JINJA_ENV.from_string(user_defined_prompts.get("reflection", REFLECT)) user_prompt = template.render(goal=goal, tool_calls=tool_calls) hist = deepcopy(history) if hist[-1]["role"] == "user": From 1a904edd9498081beb8a13419122217566527812 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Tue, 9 Sep 2025 21:18:06 +0800 Subject: [PATCH 0650/1187] Fix: Optimize search functionality #3221 (#10002) ### What problem does this PR solve? Fix: Optimize search functionality - Fixed search limitations when no dataset is selected ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/locales/en.ts | 1 + web/src/locales/zh.ts | 1 + web/src/pages/next-search/hooks.ts | 2 +- web/src/pages/next-search/index.tsx | 1 + web/src/pages/next-search/search-home.tsx | 15 +++++++++++++++ web/src/pages/next-search/share/index.tsx | 9 +++++---- 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 43869812211..e89a4ff1dee 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1573,6 +1573,7 @@ This delimiter is used to split the input text into several text pieces echo of descriptionValue: 'You are an intelligent assistant.', okText: 'Save', cancelText: 'Cancel', + chooseDataset: 'Please select a dataset first', }, language: { english: 'English', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 952a9d3c8ed..55dfe80d12f 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1485,6 +1485,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 descriptionValue: '你是一位智能助手。', okText: '保存', cancelText: '返回', + chooseDataset: '请先选择知识库', }, language: { english: '英语', diff --git a/web/src/pages/next-search/hooks.ts b/web/src/pages/next-search/hooks.ts index b889fd6e80c..56870558277 100644 --- a/web/src/pages/next-search/hooks.ts +++ b/web/src/pages/next-search/hooks.ts @@ -536,6 +536,6 @@ export const useCheckSettings = (data: ISearchAppDetailProps) => { const { search_config, name } = data; const { kb_ids } = search_config; return { - openSetting: kb_ids && kb_ids.length && name ? false : true, + openSetting: kb_ids && kb_ids.length > 0 && name ? false : true, }; }; diff --git a/web/src/pages/next-search/index.tsx b/web/src/pages/next-search/index.tsx index 0a8ef62ad0a..b869e8fc947 100644 --- a/web/src/pages/next-search/index.tsx +++ b/web/src/pages/next-search/index.tsx @@ -82,6 +82,7 @@ export default function SearchPage() { searchText={searchText} setSearchText={setSearchText} userInfo={userInfo} + canSearch={!checkOpenSetting} />
    )} diff --git a/web/src/pages/next-search/search-home.tsx b/web/src/pages/next-search/search-home.tsx index 168e9124a9c..0fa7d157bd7 100644 --- a/web/src/pages/next-search/search-home.tsx +++ b/web/src/pages/next-search/search-home.tsx @@ -1,4 +1,5 @@ import { Input } from '@/components/originui/input'; +import message from '@/components/ui/message'; import { IUserInfo } from '@/interfaces/database/user-setting'; import { cn } from '@/lib/utils'; import { Search } from 'lucide-react'; @@ -13,12 +14,14 @@ export default function SearchPage({ searchText, setSearchText, userInfo, + canSearch, }: { isSearching: boolean; setIsSearching: Dispatch>; searchText: string; setSearchText: Dispatch>; userInfo?: IUserInfo; + canSearch?: boolean; }) { // const { data: userInfo } = useFetchUserInfo(); const { t } = useTranslation(); @@ -56,10 +59,18 @@ export default function SearchPage({ value={searchText} onKeyUp={(e) => { if (e.key === 'Enter') { + if (canSearch === false) { + message.warning(t('search.chooseDataset')); + return; + } setIsSearching(!isSearching); } }} onChange={(e) => { + if (canSearch === false) { + message.warning(t('search.chooseDataset')); + return; + } setSearchText(e.target.value || ''); }} /> @@ -67,6 +78,10 @@ export default function SearchPage({ type="button" className="absolute right-2 top-1/2 -translate-y-1/2 transform rounded-full bg-text-primary p-2 text-bg-base shadow w-12" onClick={() => { + if (canSearch === false) { + message.warning(t('search.chooseDataset')); + return; + } setIsSearching(!isSearching); }} > diff --git a/web/src/pages/next-search/share/index.tsx b/web/src/pages/next-search/share/index.tsx index c269fe479ae..8b663adb0e7 100644 --- a/web/src/pages/next-search/share/index.tsx +++ b/web/src/pages/next-search/share/index.tsx @@ -5,7 +5,7 @@ import { ISearchAppDetailProps, useFetchSearchDetail, } from '../../next-searches/hooks'; -import { useGetSharedSearchParams, useSearching } from '../hooks'; +import { useCheckSettings, useGetSharedSearchParams } from '../hooks'; import '../index.less'; import SearchHome from '../search-home'; import SearchingPage from '../searching'; @@ -18,9 +18,9 @@ export default function ShareSeachPage() { } = useFetchSearchDetail(tenantId as string); const [isSearching, setIsSearching] = useState(false); const [searchText, setSearchText] = useState(''); - const searchingParam = useSearching({ - data: searchData, - }); + const { openSetting: canSearch } = useCheckSettings( + searchData as ISearchAppDetailProps, + ); useEffect(() => { if (locale && i18n.language !== locale) { @@ -47,6 +47,7 @@ export default function ShareSeachPage() { isSearching={isSearching} searchText={searchText} setSearchText={setSearchText} + canSearch={!canSearch} />
    )} From 07a83f93d5335a99afcc5256303620b5fca4c7c7 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 9 Sep 2025 21:18:24 +0800 Subject: [PATCH 0651/1187] Feat: The prompt words "plan" are displayed only when the agent operator has sub-agent operators or sub-tool operators. #10000 (#10001) ### What problem does this PR solve? Feat: The prompt words "plan" are displayed only when the agent operator has sub-agent operators or sub-tool operators. . #10000 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/form/agent-form/index.tsx | 2 +- .../agent-form/use-build-prompt-options.ts | 25 ++++++++++++++----- web/src/pages/agent/utils.ts | 10 ++++++++ web/src/pages/agents/agent-card.tsx | 2 ++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 8692ab96eb8..f761e6d3cad 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -86,7 +86,7 @@ function AgentForm({ node }: INextOperatorForm) { const defaultValues = useValues(node); - const { extraOptions } = useBuildPromptExtraPromptOptions(); + const { extraOptions } = useBuildPromptExtraPromptOptions(edges, node?.id); const ExceptionMethodOptions = Object.values(AgentExceptionMethod).map( (x) => ({ diff --git a/web/src/pages/agent/form/agent-form/use-build-prompt-options.ts b/web/src/pages/agent/form/agent-form/use-build-prompt-options.ts index 9c34ce77c06..5a0f04abb7f 100644 --- a/web/src/pages/agent/form/agent-form/use-build-prompt-options.ts +++ b/web/src/pages/agent/form/agent-form/use-build-prompt-options.ts @@ -1,6 +1,8 @@ import { useFetchPrompt } from '@/hooks/use-agent-request'; +import { Edge } from '@xyflow/react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import { hasSubAgentOrTool } from '../../utils'; export const PromptIdentity = 'RAGFlow-Prompt'; @@ -11,16 +13,27 @@ function wrapPromptWithTag(text: string, tag: string) { `; } -export function useBuildPromptExtraPromptOptions() { +export function useBuildPromptExtraPromptOptions( + edges: Edge[], + nodeId?: string, +) { const { data: prompts } = useFetchPrompt(); const { t } = useTranslation(); + const has = hasSubAgentOrTool(edges, nodeId); const options = useMemo(() => { - return Object.entries(prompts || {}).map(([key, value]) => ({ - label: key, - value: wrapPromptWithTag(value, key), - })); - }, [prompts]); + return Object.entries(prompts || {}) + .map(([key, value]) => ({ + label: key, + value: wrapPromptWithTag(value, key), + })) + .filter((x) => { + if (!has) { + return x.label === 'citation_guidelines'; + } + return true; + }); + }, [has, prompts]); const extraOptions = [ { label: PromptIdentity, title: t('flow.frameworkPrompts'), options }, diff --git a/web/src/pages/agent/utils.ts b/web/src/pages/agent/utils.ts index c7d60dfd2c5..c36b1b85538 100644 --- a/web/src/pages/agent/utils.ts +++ b/web/src/pages/agent/utils.ts @@ -152,6 +152,16 @@ export function isBottomSubAgent(edges: Edge[], nodeId?: string) { return !!edge; } +export function hasSubAgentOrTool(edges: Edge[], nodeId?: string) { + const edge = edges.find( + (x) => + x.source === nodeId && + (x.sourceHandle === NodeHandleId.Tool || + x.sourceHandle === NodeHandleId.AgentBottom), + ); + return !!edge; +} + // construct a dsl based on the node information of the graph export const buildDslComponentsByGraph = ( nodes: RAGFlowNodeType[], diff --git a/web/src/pages/agents/agent-card.tsx b/web/src/pages/agents/agent-card.tsx index 831575c390f..e1aa20ac43b 100644 --- a/web/src/pages/agents/agent-card.tsx +++ b/web/src/pages/agents/agent-card.tsx @@ -1,5 +1,6 @@ import { HomeCard } from '@/components/home-card'; import { MoreButton } from '@/components/more-button'; +import { SharedBadge } from '@/components/shared-badge'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { IFlow } from '@/interfaces/database/agent'; import { AgentDropdown } from './agent-dropdown'; @@ -20,6 +21,7 @@ export function AgentCard({ data, showAgentRenameModal }: DatasetCardProps) { } + sharedBadge={{data.nickname}} onClick={navigateToAgent(data?.id)} /> ); From 81fede00411e2b67a485938e3250624faa43fbb1 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Tue, 9 Sep 2025 22:01:44 +0800 Subject: [PATCH 0652/1187] Fix: refactor prompts (#10005) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/canvas_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py index 691d91c3977..4ced90a3d88 100644 --- a/api/apps/canvas_app.py +++ b/api/apps/canvas_app.py @@ -476,7 +476,7 @@ def sessions(canvas_id): def prompts(): from rag.prompts.prompts import ANALYZE_TASK_SYSTEM, ANALYZE_TASK_USER, NEXT_STEP, REFLECT, CITATION_PROMPT_TEMPLATE return get_json_result(data={ - "task_analysis": ANALYZE_TASK_SYSTEM + ANALYZE_TASK_USER, + "task_analysis": ANALYZE_TASK_SYSTEM +"\n\n"+ ANALYZE_TASK_USER, "plan_generation": NEXT_STEP, "reflection": REFLECT, #"context_summary": SUMMARY4MEMORY, From 5cf2c97908f9e196b5c9a1e53f4934ecc10f510e Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:46:22 +0800 Subject: [PATCH 0653/1187] Docs: v0.20.5 - Added Framework prompt block documentation for the Agent component (#10006) ### What problem does this PR solve? ### Type of change - [x] Documentation Update --- .../agent/agent_component_reference/agent.mdx | 67 +++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/docs/guides/agent/agent_component_reference/agent.mdx b/docs/guides/agent/agent_component_reference/agent.mdx index 97c131e34bb..dec9d30b411 100644 --- a/docs/guides/agent/agent_component_reference/agent.mdx +++ b/docs/guides/agent/agent_component_reference/agent.mdx @@ -18,6 +18,14 @@ An **Agent** component fine-tunes the LLM and sets its prompt. From v0.20.4 onwa An **Agent** component is essential when you need the LLM to assist with summarizing, translating, or controlling various tasks. +## Prerequisites + +1. Ensure you have a chat model properly configured: + +![Set default models](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/set_default_models.jpg) + +2. If your Agent involves dataset retrieval, ensure you [have properly configured your target knowledge base(s)](../../dataset/configure_knowledge_base.md). + ## Configurations ### Model @@ -57,13 +65,44 @@ Click the dropdown menu of **Model** to show the model configuration window. Typically, you use the system prompt to describe the task for the LLM, specify how it should respond, and outline other miscellaneous requirements. We do not plan to elaborate on this topic, as it can be as extensive as prompt engineering. However, please be aware that the system prompt is often used in conjunction with keys (variables), which serve as various data inputs for the LLM. -:::danger IMPORTANT An **Agent** component relies on keys (variables) to specify its data inputs. Its immediate upstream component is *not* necessarily its data input, and the arrows in the workflow indicate *only* the processing sequence. Keys in a **Agent** component are used in conjunction with the system prompt to specify data inputs for the LLM. Use a forward slash `/` or the **(x)** button to show the keys to use. -::: + +#### Advanced usage + +From v0.20.5 onwards, four framework-level prompt blocks are available in the **System prompt** field. Type `/` or click **(x)** to view them; they appear under the **Framework** entry in the dropdown menu. + +- `task_analysis` prompt block + - This block is responsible for analyzing tasks — either a user task or a task assigned by the lead Agent when the **Agent** component is acting as a Sub-Agent. + - Reference design: [analyze_task_system.md](https://github.com/infiniflow/ragflow/blob/main/rag/prompts/analyze_task_system.md) and [analyze_task_user.md](https://github.com/infiniflow/ragflow/blob/main/rag/prompts/analyze_task_user.md) + - Available *only* when this **Agent** component is acting as a planner, with either tools or sub-Agents under it. + - Input variables: + - `agent_prompt`: The system prompt. + - `task`: The user prompt for either a lead Agent or a sub-Agent. The lead Agent's user prompt is defined by the user, while a sub-Agent's user prompt is defined by the lead Agent when delegating tasks. + - `tool_desc`: A description of the tools and sub_Agents that can be called. + - `context`: The operational context, which stores interactions between the Agent, tools, and sub-agents; initially empty. +- `plan_generation` prompt block + - This block creates a plan for the **Agent** component to execute next, based on the task analysis results. + - Reference design: [next_step.md](https://github.com/infiniflow/ragflow/blob/main/rag/prompts/next_step.md) + - Available *only* when this **Agent** component is acting as a planner, with either tools or sub-Agents under it. + - Input variables: + - `task_analysis`: The analysis result of the current task. + - `desc`: A description of the tools or sub-Agents currently being called. + - `today`: The date of today. +- `reflection` prompt block + - This block enables the **Agent** component to reflect, improving task accuracy and efficiency. + - Reference design: [reflect.md](https://github.com/infiniflow/ragflow/blob/main/rag/prompts/reflect.md) + - Available *only* when this **Agent** component is acting as a planner, with either tools or sub-Agents under it. + - Input variables: + - `goal`: The goal of the current task. It is the user prompt for either a lead Agent or a sub-Agent. The lead Agent's user prompt is defined by the user, while a sub-Agent's user prompt is defined by the lead Agent. + - `tool_calls`: The history of tool calling + - `call.name`:The name of the tool called. + - `call.result`:The result of tool calling +- `citation_guidelines` prompt block + - Reference design: [citation_prompt.md](https://github.com/infiniflow/ragflow/blob/main/rag/prompts/citation_prompt.md) ### User prompt -The user-defined prompt. Defaults to `sys.query`, the user query. +The user-defined prompt. Defaults to `sys.query`, the user query. As a general rule, when using the **Agent** component as a standalone module (not as a planner), you usually need to specify the corresponding **Retrieval** component’s output variable (`formalized_content`) here as part of the input to the LLM. ### Tools @@ -100,4 +139,24 @@ Increasing this value will significantly extend your agent's response time. ### Output -The global variable name for the output of the **Agent** component, which can be referenced by other components in the workflow. \ No newline at end of file +The global variable name for the output of the **Agent** component, which can be referenced by other components in the workflow. + +## Frequently asked questions + +### Why does it take so long for my Agent to respond? + +An Agent’s response time generally depends on two key factors: the LLM’s capabilities and the prompt, the latter reflecting task complexity. When using an Agent, you should always balance task demands with the LLM’s ability. See [How to balance task complexity with an Agent's performance and speed?](#how-to-balance-task-complexity-with-an-agents-performance-and-speed) for details. + +## Best practices + +### How to balance task complexity with an Agent’s performance and speed? + +- For simple tasks, such as retrieval, rewriting, formatting, or structured data extraction, use concise prompts, remove planning or reasoning instructions, enforce output length limits, and select smaller or Turbo-class models. This significantly reduces latency and cost with minimal impact on quality. + +- For complex tasks, like multi-step reasoning, cross-document synthesis, or tool-based workflows, maintain or enhance prompts that include planning, reflection, and verification steps. + +- In multi-Agent orchestration systems, delegate simple subtasks to sub-Agents using smaller, faster models, and reserve more powerful models for the lead Agent to handle complexity and uncertainty. + +:::tip KEY INSIGHT +Focus on minimizing output tokens — through summarization, bullet points, or explicit length limits — as this has far greater impact on reducing latency than optimizing input size. +::: \ No newline at end of file From a9cc992d13153af1c2f0027a7d3c978615d04214 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 10 Sep 2025 10:56:34 +0800 Subject: [PATCH 0654/1187] Feat: Translate the maxRounds field of the chat settings #3221 (#10010) ### What problem does this PR solve? Feat: Translate the maxRounds field of the chat settings #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/locales/en.ts | 2 +- web/src/locales/zh.ts | 2 +- web/src/pages/agent/form/agent-form/index.tsx | 28 ++++++++++--------- .../chat/app-settings/dynamic-variable.tsx | 4 +-- .../pages/next-chats/hooks/use-rename-chat.ts | 3 +- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index e89a4ff1dee..a6916f3e5ad 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -932,7 +932,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s builtIn: 'Built-in', ExceptionDefaultValue: 'Exception default value', exceptionMethod: 'Exception method', - maxRounds: 'Max rounds', + maxRounds: 'Max reflection rounds', delayEfterError: 'Delay after error', maxRetries: 'Max retries', advancedSettings: 'Advanced Settings', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 55dfe80d12f..fb19ed8ff1c 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -890,7 +890,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 comment: '默认值', ExceptionDefaultValue: '异常处理默认值', exceptionMethod: '异常处理方法', - maxRounds: '最大轮数', + maxRounds: '最大反思轮数', delayEfterError: '错误后延迟', maxRetries: '最大重试次数', advancedSettings: '高级设置', diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index f761e6d3cad..5aba3582668 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -31,7 +31,7 @@ import { } from '../../constant'; import { INextOperatorForm } from '../../interface'; import useGraphStore from '../../store'; -import { isBottomSubAgent } from '../../utils'; +import { hasSubAgentOrTool, isBottomSubAgent } from '../../utils'; import { buildOutputList } from '../../utils/build-output-list'; import { DescriptionField } from '../components/description-field'; import { FormWrapper } from '../components/form-wrapper'; @@ -231,18 +231,20 @@ function AgentForm({ node }: INextOperatorForm) { )} /> - ( - - {t('flow.maxRounds')} - - - - - )} - /> + {hasSubAgentOrTool(edges, node?.id) && ( + ( + + {t('flow.maxRounds')} + + + + + )} + /> + )}
    - Key + {t('chat.key')} - Optional + {t('chat.optional')}
    {fields.map((field, index) => { diff --git a/web/src/pages/next-chats/hooks/use-rename-chat.ts b/web/src/pages/next-chats/hooks/use-rename-chat.ts index fd2b975eb7a..0f240169a32 100644 --- a/web/src/pages/next-chats/hooks/use-rename-chat.ts +++ b/web/src/pages/next-chats/hooks/use-rename-chat.ts @@ -22,6 +22,7 @@ export const useRenameChat = () => { name: '', icon: '', language: 'English', + description: '', prompt_config: { empty_response: '', prologue: t('chat.setAnOpenerInitial'), @@ -37,7 +38,7 @@ export const useRenameChat = () => { llm_id: tenantInfo.data.llm_id, llm_setting: {}, similarity_threshold: 0.2, - vector_similarity_weight: 0.30000000000000004, + vector_similarity_weight: 0.3, top_n: 8, }), [t, tenantInfo.data.llm_id], From 38ff2ffc01351702e1fcb45ba2392eb58c4d6a27 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Wed, 10 Sep 2025 11:07:03 +0800 Subject: [PATCH 0655/1187] Fix: typo. (#10011) ### What problem does this PR solve? ### Type of change - [x] Refactoring --- rag/prompts/next_step.md | 2 +- rag/prompts/prompts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rag/prompts/next_step.md b/rag/prompts/next_step.md index 493c190512e..3e6b608fce7 100644 --- a/rag/prompts/next_step.md +++ b/rag/prompts/next_step.md @@ -5,7 +5,7 @@ Your job is: 3. Use `complete_task` if no further step you need to take from tools. (All necessary steps done or little hope to be done) # ========== TASK ANALYSIS ============= -{{ task_analisys }} +{{ task_analysis }} # ========== TOOLS (JSON-Schema) ========== You may invoke only the tools listed below. diff --git a/rag/prompts/prompts.py b/rag/prompts/prompts.py index b15019b6ff3..13ea801b05f 100644 --- a/rag/prompts/prompts.py +++ b/rag/prompts/prompts.py @@ -368,7 +368,7 @@ def next_step(chat_mdl, history:list, tools_description: list[dict], task_desc, hist[-1]["content"] += user_prompt else: hist.append({"role": "user", "content": user_prompt}) - json_str = chat_mdl.chat(template.render(task_analisys=task_desc, desc=desc, today=datetime.datetime.now().strftime("%Y-%m-%d")), + json_str = chat_mdl.chat(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) json_str = re.sub(r"^.*", "", json_str, flags=re.DOTALL) From 067b4fc012e8e78c2a3fe7d0a048c32a24f3a333 Mon Sep 17 00:00:00 2001 From: Liu An Date: Wed, 10 Sep 2025 11:20:43 +0800 Subject: [PATCH 0656/1187] Docs: Update version references to v0.20.5 in READMEs and docs (#10015) ### What problem does this PR solve? - Update version tags in README files (including translations) from v0.20.4 to v0.20.5 - Modify Docker image references and documentation to reflect new version - Update version badges and image descriptions - Maintain consistency across all language variants of README files ### Type of change - [x] Documentation Update --- README.md | 8 ++++---- README_id.md | 8 ++++---- README_ja.md | 8 ++++---- README_ko.md | 8 ++++---- README_pt_br.md | 8 ++++---- README_tzh.md | 8 ++++---- README_zh.md | 8 ++++---- docker/.env | 8 ++++---- docker/README.md | 4 ++-- docs/configurations.md | 4 ++-- docs/develop/build_docker_image.mdx | 2 +- docs/faq.mdx | 10 +++++----- .../guides/agent/agent_component_reference/agent.mdx | 2 +- docs/guides/chat/start_chat.md | 2 +- docs/guides/dataset/configure_knowledge_base.md | 2 +- docs/guides/manage_files.md | 2 +- docs/guides/tracing.mdx | 2 +- docs/guides/upgrade_ragflow.mdx | 12 ++++++------ docs/quickstart.mdx | 12 ++++++------ docs/references/glossary.mdx | 2 +- docs/release_notes.md | 4 ++-- helm/values.yaml | 2 +- pyproject.toml | 2 +- sdk/python/pyproject.toml | 2 +- sdk/python/uv.lock | 2 +- uv.lock | 2 +- 26 files changed, 67 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index c6f77a0cc3d..0a64d5c26d6 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Static Badge - docker pull infiniflow/ragflow:v0.20.4 + docker pull infiniflow/ragflow:v0.20.5 Latest Release @@ -187,7 +187,7 @@ releases! 🌟 > All Docker images are built for x86 platforms. We don't currently offer Docker images for ARM64. > If you are on an ARM64 platform, follow [this guide](https://ragflow.io/docs/dev/build_docker_image) to build a Docker image compatible with your system. - > The command below downloads the `v0.20.4-slim` edition of the RAGFlow Docker image. See the following table for descriptions of different RAGFlow editions. To download a RAGFlow edition different from `v0.20.4-slim`, update the `RAGFLOW_IMAGE` variable accordingly in **docker/.env** before using `docker compose` to start the server. For example: set `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4` for the full edition `v0.20.4`. + > The command below downloads the `v0.20.5-slim` edition of the RAGFlow Docker image. See the following table for descriptions of different RAGFlow editions. To download a RAGFlow edition different from `v0.20.5-slim`, update the `RAGFLOW_IMAGE` variable accordingly in **docker/.env** before using `docker compose` to start the server. For example: set `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5` for the full edition `v0.20.5`. ```bash $ cd ragflow/docker @@ -200,8 +200,8 @@ releases! 🌟 | RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? | |-------------------|-----------------|-----------------------|--------------------------| - | v0.20.4 | ≈9 | :heavy_check_mark: | Stable release | - | v0.20.4-slim | ≈2 | ❌ | Stable release | + | v0.20.5 | ≈9 | :heavy_check_mark: | Stable release | + | v0.20.5-slim | ≈2 | ❌ | Stable release | | nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build | | nightly-slim | ≈2 | ❌ | _Unstable_ nightly build | diff --git a/README_id.md b/README_id.md index 44b615bec46..e1f4cf2e755 100644 --- a/README_id.md +++ b/README_id.md @@ -22,7 +22,7 @@ Lencana Daring - docker pull infiniflow/ragflow:v0.20.4 + docker pull infiniflow/ragflow:v0.20.5 Rilis Terbaru @@ -181,7 +181,7 @@ Coba demo kami di [https://demo.ragflow.io](https://demo.ragflow.io). > Semua gambar Docker dibangun untuk platform x86. Saat ini, kami tidak menawarkan gambar Docker untuk ARM64. > Jika Anda menggunakan platform ARM64, [silakan gunakan panduan ini untuk membangun gambar Docker yang kompatibel dengan sistem Anda](https://ragflow.io/docs/dev/build_docker_image). -> Perintah di bawah ini mengunduh edisi v0.20.4-slim dari gambar Docker RAGFlow. Silakan merujuk ke tabel berikut untuk deskripsi berbagai edisi RAGFlow. Untuk mengunduh edisi RAGFlow yang berbeda dari v0.20.4-slim, perbarui variabel RAGFLOW_IMAGE di docker/.env sebelum menggunakan docker compose untuk memulai server. Misalnya, atur RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4 untuk edisi lengkap v0.20.4. +> Perintah di bawah ini mengunduh edisi v0.20.5-slim dari gambar Docker RAGFlow. Silakan merujuk ke tabel berikut untuk deskripsi berbagai edisi RAGFlow. Untuk mengunduh edisi RAGFlow yang berbeda dari v0.20.5-slim, perbarui variabel RAGFLOW_IMAGE di docker/.env sebelum menggunakan docker compose untuk memulai server. Misalnya, atur RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5 untuk edisi lengkap v0.20.5. ```bash $ cd ragflow/docker @@ -194,8 +194,8 @@ $ docker compose -f docker-compose.yml up -d | RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? | | ----------------- | --------------- | --------------------- | ------------------------ | -| v0.20.4 | ≈9 | :heavy_check_mark: | Stable release | -| v0.20.4-slim | ≈2 | ❌ | Stable release | +| v0.20.5 | ≈9 | :heavy_check_mark: | Stable release | +| v0.20.5-slim | ≈2 | ❌ | Stable release | | nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build | | nightly-slim | ≈2 | ❌ | _Unstable_ nightly build | diff --git a/README_ja.md b/README_ja.md index 720902094ec..6f5432ce549 100644 --- a/README_ja.md +++ b/README_ja.md @@ -22,7 +22,7 @@ Static Badge - docker pull infiniflow/ragflow:v0.20.4 + docker pull infiniflow/ragflow:v0.20.5 Latest Release @@ -160,7 +160,7 @@ > 現在、公式に提供されているすべての Docker イメージは x86 アーキテクチャ向けにビルドされており、ARM64 用の Docker イメージは提供されていません。 > ARM64 アーキテクチャのオペレーティングシステムを使用している場合は、[このドキュメント](https://ragflow.io/docs/dev/build_docker_image)を参照して Docker イメージを自分でビルドしてください。 - > 以下のコマンドは、RAGFlow Docker イメージの v0.20.4-slim エディションをダウンロードします。異なる RAGFlow エディションの説明については、以下の表を参照してください。v0.20.4-slim とは異なるエディションをダウンロードするには、docker/.env ファイルの RAGFLOW_IMAGE 変数を適宜更新し、docker compose を使用してサーバーを起動してください。例えば、完全版 v0.20.4 をダウンロードするには、RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4 と設定します。 + > 以下のコマンドは、RAGFlow Docker イメージの v0.20.5-slim エディションをダウンロードします。異なる RAGFlow エディションの説明については、以下の表を参照してください。v0.20.5-slim とは異なるエディションをダウンロードするには、docker/.env ファイルの RAGFLOW_IMAGE 変数を適宜更新し、docker compose を使用してサーバーを起動してください。例えば、完全版 v0.20.5 をダウンロードするには、RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5 と設定します。 ```bash $ cd ragflow/docker @@ -173,8 +173,8 @@ | RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? | | ----------------- | --------------- | --------------------- | ------------------------ | - | v0.20.4 | ≈9 | :heavy_check_mark: | Stable release | - | v0.20.4-slim | ≈2 | ❌ | Stable release | + | v0.20.5 | ≈9 | :heavy_check_mark: | Stable release | + | v0.20.5-slim | ≈2 | ❌ | Stable release | | nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build | | nightly-slim | ≈2 | ❌ | _Unstable_ nightly build | diff --git a/README_ko.md b/README_ko.md index 68fd01bb3c7..c579162e7cb 100644 --- a/README_ko.md +++ b/README_ko.md @@ -22,7 +22,7 @@ Static Badge - docker pull infiniflow/ragflow:v0.20.4 + docker pull infiniflow/ragflow:v0.20.5 Latest Release @@ -160,7 +160,7 @@ > 모든 Docker 이미지는 x86 플랫폼을 위해 빌드되었습니다. 우리는 현재 ARM64 플랫폼을 위한 Docker 이미지를 제공하지 않습니다. > ARM64 플랫폼을 사용 중이라면, [시스템과 호환되는 Docker 이미지를 빌드하려면 이 가이드를 사용해 주세요](https://ragflow.io/docs/dev/build_docker_image). - > 아래 명령어는 RAGFlow Docker 이미지의 v0.20.4-slim 버전을 다운로드합니다. 다양한 RAGFlow 버전에 대한 설명은 다음 표를 참조하십시오. v0.20.4-slim과 다른 RAGFlow 버전을 다운로드하려면, docker/.env 파일에서 RAGFLOW_IMAGE 변수를 적절히 업데이트한 후 docker compose를 사용하여 서버를 시작하십시오. 예를 들어, 전체 버전인 v0.20.4을 다운로드하려면 RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4로 설정합니다. + > 아래 명령어는 RAGFlow Docker 이미지의 v0.20.5-slim 버전을 다운로드합니다. 다양한 RAGFlow 버전에 대한 설명은 다음 표를 참조하십시오. v0.20.5-slim과 다른 RAGFlow 버전을 다운로드하려면, docker/.env 파일에서 RAGFLOW_IMAGE 변수를 적절히 업데이트한 후 docker compose를 사용하여 서버를 시작하십시오. 예를 들어, 전체 버전인 v0.20.5을 다운로드하려면 RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5로 설정합니다. ```bash $ cd ragflow/docker @@ -173,8 +173,8 @@ | RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? | | ----------------- | --------------- | --------------------- | ------------------------ | - | v0.20.4 | ≈9 | :heavy_check_mark: | Stable release | - | v0.20.4-slim | ≈2 | ❌ | Stable release | + | v0.20.5 | ≈9 | :heavy_check_mark: | Stable release | + | v0.20.5-slim | ≈2 | ❌ | Stable release | | nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build | | nightly-slim | ≈2 | ❌ | _Unstable_ nightly build | diff --git a/README_pt_br.md b/README_pt_br.md index dd89f0a34b5..88c48d21268 100644 --- a/README_pt_br.md +++ b/README_pt_br.md @@ -22,7 +22,7 @@ Badge Estático - docker pull infiniflow/ragflow:v0.20.4 + docker pull infiniflow/ragflow:v0.20.5 Última Versão @@ -180,7 +180,7 @@ Experimente nossa demo em [https://demo.ragflow.io](https://demo.ragflow.io). > Todas as imagens Docker são construídas para plataformas x86. Atualmente, não oferecemos imagens Docker para ARM64. > Se você estiver usando uma plataforma ARM64, por favor, utilize [este guia](https://ragflow.io/docs/dev/build_docker_image) para construir uma imagem Docker compatível com o seu sistema. - > O comando abaixo baixa a edição `v0.20.4-slim` da imagem Docker do RAGFlow. Consulte a tabela a seguir para descrições de diferentes edições do RAGFlow. Para baixar uma edição do RAGFlow diferente da `v0.20.4-slim`, atualize a variável `RAGFLOW_IMAGE` conforme necessário no **docker/.env** antes de usar `docker compose` para iniciar o servidor. Por exemplo: defina `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4` para a edição completa `v0.20.4`. + > O comando abaixo baixa a edição `v0.20.5-slim` da imagem Docker do RAGFlow. Consulte a tabela a seguir para descrições de diferentes edições do RAGFlow. Para baixar uma edição do RAGFlow diferente da `v0.20.5-slim`, atualize a variável `RAGFLOW_IMAGE` conforme necessário no **docker/.env** antes de usar `docker compose` para iniciar o servidor. Por exemplo: defina `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5` para a edição completa `v0.20.5`. ```bash $ cd ragflow/docker @@ -193,8 +193,8 @@ Experimente nossa demo em [https://demo.ragflow.io](https://demo.ragflow.io). | Tag da imagem RAGFlow | Tamanho da imagem (GB) | Possui modelos de incorporação? | Estável? | | --------------------- | ---------------------- | ------------------------------- | ------------------------ | - | v0.20.4 | ~9 | :heavy_check_mark: | Lançamento estável | - | v0.20.4-slim | ~2 | ❌ | Lançamento estável | + | v0.20.5 | ~9 | :heavy_check_mark: | Lançamento estável | + | v0.20.5-slim | ~2 | ❌ | Lançamento estável | | nightly | ~9 | :heavy_check_mark: | _Instável_ build noturno | | nightly-slim | ~2 | ❌ | _Instável_ build noturno | diff --git a/README_tzh.md b/README_tzh.md index 2ee08ab859e..4a95be1e764 100644 --- a/README_tzh.md +++ b/README_tzh.md @@ -22,7 +22,7 @@ Static Badge - docker pull infiniflow/ragflow:v0.20.4 + docker pull infiniflow/ragflow:v0.20.5 Latest Release @@ -183,7 +183,7 @@ > 所有 Docker 映像檔都是為 x86 平台建置的。目前,我們不提供 ARM64 平台的 Docker 映像檔。 > 如果您使用的是 ARM64 平台,請使用 [這份指南](https://ragflow.io/docs/dev/build_docker_image) 來建置適合您系統的 Docker 映像檔。 - > 執行以下指令會自動下載 RAGFlow slim Docker 映像 `v0.20.4-slim`。請參考下表查看不同 Docker 發行版的說明。如需下載不同於 `v0.20.4-slim` 的 Docker 映像,請在執行 `docker compose` 啟動服務之前先更新 **docker/.env** 檔案內的 `RAGFLOW_IMAGE` 變數。例如,你可以透過設定 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4` 來下載 RAGFlow 鏡像的 `v0.20.4` 完整發行版。 + > 執行以下指令會自動下載 RAGFlow slim Docker 映像 `v0.20.5-slim`。請參考下表查看不同 Docker 發行版的說明。如需下載不同於 `v0.20.5-slim` 的 Docker 映像,請在執行 `docker compose` 啟動服務之前先更新 **docker/.env** 檔案內的 `RAGFLOW_IMAGE` 變數。例如,你可以透過設定 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5` 來下載 RAGFlow 鏡像的 `v0.20.5` 完整發行版。 ```bash $ cd ragflow/docker @@ -196,8 +196,8 @@ | RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? | | ----------------- | --------------- | --------------------- | ------------------------ | - | v0.20.4 | ≈9 | :heavy_check_mark: | Stable release | - | v0.20.4-slim | ≈2 | ❌ | Stable release | + | v0.20.5 | ≈9 | :heavy_check_mark: | Stable release | + | v0.20.5-slim | ≈2 | ❌ | Stable release | | nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build | | nightly-slim | ≈2 | ❌ | _Unstable_ nightly build | diff --git a/README_zh.md b/README_zh.md index bd394f34fb4..ad1a001dc84 100644 --- a/README_zh.md +++ b/README_zh.md @@ -22,7 +22,7 @@ Static Badge - docker pull infiniflow/ragflow:v0.20.4 + docker pull infiniflow/ragflow:v0.20.5 Latest Release @@ -183,7 +183,7 @@ > 请注意,目前官方提供的所有 Docker 镜像均基于 x86 架构构建,并不提供基于 ARM64 的 Docker 镜像。 > 如果你的操作系统是 ARM64 架构,请参考[这篇文档](https://ragflow.io/docs/dev/build_docker_image)自行构建 Docker 镜像。 - > 运行以下命令会自动下载 RAGFlow slim Docker 镜像 `v0.20.4-slim`。请参考下表查看不同 Docker 发行版的描述。如需下载不同于 `v0.20.4-slim` 的 Docker 镜像,请在运行 `docker compose` 启动服务之前先更新 **docker/.env** 文件内的 `RAGFLOW_IMAGE` 变量。比如,你可以通过设置 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4` 来下载 RAGFlow 镜像的 `v0.20.4` 完整发行版。 + > 运行以下命令会自动下载 RAGFlow slim Docker 镜像 `v0.20.5-slim`。请参考下表查看不同 Docker 发行版的描述。如需下载不同于 `v0.20.5-slim` 的 Docker 镜像,请在运行 `docker compose` 启动服务之前先更新 **docker/.env** 文件内的 `RAGFLOW_IMAGE` 变量。比如,你可以通过设置 `RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5` 来下载 RAGFlow 镜像的 `v0.20.5` 完整发行版。 ```bash $ cd ragflow/docker @@ -196,8 +196,8 @@ | RAGFlow image tag | Image size (GB) | Has embedding models? | Stable? | | ----------------- | --------------- | --------------------- | ------------------------ | - | v0.20.4 | ≈9 | :heavy_check_mark: | Stable release | - | v0.20.4-slim | ≈2 | ❌ | Stable release | + | v0.20.5 | ≈9 | :heavy_check_mark: | Stable release | + | v0.20.5-slim | ≈2 | ❌ | Stable release | | nightly | ≈9 | :heavy_check_mark: | _Unstable_ nightly build | | nightly-slim | ≈2 | ❌ | _Unstable_ nightly build | diff --git a/docker/.env b/docker/.env index d5acaad666e..1375f5564eb 100644 --- a/docker/.env +++ b/docker/.env @@ -93,13 +93,13 @@ REDIS_PASSWORD=infini_rag_flow SVR_HTTP_PORT=9380 # The RAGFlow Docker image to download. -# Defaults to the v0.20.4-slim edition, which is the RAGFlow Docker image without embedding models. -RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4-slim +# Defaults to the v0.20.5-slim edition, which is the RAGFlow Docker image without embedding models. +RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5-slim # # To download the RAGFlow Docker image with embedding models, uncomment the following line instead: -# RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4 +# RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5 # -# The Docker image of the v0.20.4 edition includes built-in embedding models: +# The Docker image of the v0.20.5 edition includes built-in embedding models: # - BAAI/bge-large-zh-v1.5 # - maidalun1020/bce-embedding-base_v1 # diff --git a/docker/README.md b/docker/README.md index fa3cff7bde6..d3c072ded85 100644 --- a/docker/README.md +++ b/docker/README.md @@ -79,8 +79,8 @@ The [.env](./.env) file contains important environment variables for Docker. - `RAGFLOW-IMAGE` The Docker image edition. Available editions: - - `infiniflow/ragflow:v0.20.4-slim` (default): The RAGFlow Docker image without embedding models. - - `infiniflow/ragflow:v0.20.4`: The RAGFlow Docker image with embedding models including: + - `infiniflow/ragflow:v0.20.5-slim` (default): The RAGFlow Docker image without embedding models. + - `infiniflow/ragflow:v0.20.5`: The RAGFlow Docker image with embedding models including: - Built-in embedding models: - `BAAI/bge-large-zh-v1.5` - `maidalun1020/bce-embedding-base_v1` diff --git a/docs/configurations.md b/docs/configurations.md index 3669312af45..0c1bf3b6c26 100644 --- a/docs/configurations.md +++ b/docs/configurations.md @@ -99,8 +99,8 @@ RAGFlow utilizes MinIO as its object storage solution, leveraging its scalabilit - `RAGFLOW-IMAGE` The Docker image edition. Available editions: - - `infiniflow/ragflow:v0.20.4-slim` (default): The RAGFlow Docker image without embedding models. - - `infiniflow/ragflow:v0.20.4`: The RAGFlow Docker image with embedding models including: + - `infiniflow/ragflow:v0.20.5-slim` (default): The RAGFlow Docker image without embedding models. + - `infiniflow/ragflow:v0.20.5`: The RAGFlow Docker image with embedding models including: - Built-in embedding models: - `BAAI/bge-large-zh-v1.5` - `maidalun1020/bce-embedding-base_v1` diff --git a/docs/develop/build_docker_image.mdx b/docs/develop/build_docker_image.mdx index 2fa411e79ef..4c20b8004dd 100644 --- a/docs/develop/build_docker_image.mdx +++ b/docs/develop/build_docker_image.mdx @@ -77,7 +77,7 @@ After building the infiniflow/ragflow:nightly-slim image, you are ready to launc 1. Edit Docker Compose Configuration -Open the `docker/.env` file. Find the `RAGFLOW_IMAGE` setting and change the image reference from `infiniflow/ragflow:v0.20.4-slim` to `infiniflow/ragflow:nightly-slim` to use the pre-built image. +Open the `docker/.env` file. Find the `RAGFLOW_IMAGE` setting and change the image reference from `infiniflow/ragflow:v0.20.5-slim` to `infiniflow/ragflow:nightly-slim` to use the pre-built image. 2. Launch the Service diff --git a/docs/faq.mdx b/docs/faq.mdx index 1e26d4f94d5..0d7cb46ce39 100644 --- a/docs/faq.mdx +++ b/docs/faq.mdx @@ -30,17 +30,17 @@ The "garbage in garbage out" status quo remains unchanged despite the fact that Each RAGFlow release is available in two editions: -- **Slim edition**: excludes built-in embedding models and is identified by a **-slim** suffix added to the version name. Example: `infiniflow/ragflow:v0.20.4-slim` -- **Full edition**: includes built-in embedding models and has no suffix added to the version name. Example: `infiniflow/ragflow:v0.20.4` +- **Slim edition**: excludes built-in embedding models and is identified by a **-slim** suffix added to the version name. Example: `infiniflow/ragflow:v0.20.5-slim` +- **Full edition**: includes built-in embedding models and has no suffix added to the version name. Example: `infiniflow/ragflow:v0.20.5` --- ### Which embedding models can be deployed locally? -RAGFlow offers two Docker image editions, `v0.20.4-slim` and `v0.20.4`: +RAGFlow offers two Docker image editions, `v0.20.5-slim` and `v0.20.5`: -- `infiniflow/ragflow:v0.20.4-slim` (default): The RAGFlow Docker image without embedding models. -- `infiniflow/ragflow:v0.20.4`: The RAGFlow Docker image with embedding models including: +- `infiniflow/ragflow:v0.20.5-slim` (default): The RAGFlow Docker image without embedding models. +- `infiniflow/ragflow:v0.20.5`: The RAGFlow Docker image with embedding models including: - Built-in embedding models: - `BAAI/bge-large-zh-v1.5` - `maidalun1020/bce-embedding-base_v1` diff --git a/docs/guides/agent/agent_component_reference/agent.mdx b/docs/guides/agent/agent_component_reference/agent.mdx index dec9d30b411..f31cb3c6c99 100644 --- a/docs/guides/agent/agent_component_reference/agent.mdx +++ b/docs/guides/agent/agent_component_reference/agent.mdx @@ -9,7 +9,7 @@ The component equipped with reasoning, tool usage, and multi-agent collaboration --- -An **Agent** component fine-tunes the LLM and sets its prompt. From v0.20.4 onwards, an **Agent** component is able to work independently and with the following capabilities: +An **Agent** component fine-tunes the LLM and sets its prompt. From v0.20.5 onwards, an **Agent** component is able to work independently and with the following capabilities: - Autonomous reasoning with reflection and adjustment based on environmental feedback. - Use of tools or subagents to complete tasks. diff --git a/docs/guides/chat/start_chat.md b/docs/guides/chat/start_chat.md index a22c6fe0f62..c56b0ff6bf9 100644 --- a/docs/guides/chat/start_chat.md +++ b/docs/guides/chat/start_chat.md @@ -48,7 +48,7 @@ You start an AI conversation by creating an assistant. - If no target language is selected, the system will search only in the language of your query, which may cause relevant information in other languages to be missed. - **Variable** refers to the variables (keys) to be used in the system prompt. `{knowledge}` is a reserved variable. Click **Add** to add more variables for the system prompt. - If you are uncertain about the logic behind **Variable**, leave it *as-is*. - - As of v0.20.4, if you add custom variables here, the only way you can pass in their values is to call: + - As of v0.20.5, if you add custom variables here, the only way you can pass in their values is to call: - HTTP method [Converse with chat assistant](../../references/http_api_reference.md#converse-with-chat-assistant), or - Python method [Converse with chat assistant](../../references/python_api_reference.md#converse-with-chat-assistant). diff --git a/docs/guides/dataset/configure_knowledge_base.md b/docs/guides/dataset/configure_knowledge_base.md index 626f3bf7523..432ecce1f4a 100644 --- a/docs/guides/dataset/configure_knowledge_base.md +++ b/docs/guides/dataset/configure_knowledge_base.md @@ -124,7 +124,7 @@ See [Run retrieval test](./run_retrieval_test.md) for details. ## Search for knowledge base -As of RAGFlow v0.20.4, the search feature is still in a rudimentary form, supporting only knowledge base search by name. +As of RAGFlow v0.20.5, the search feature is still in a rudimentary form, supporting only knowledge base search by name. ![search knowledge base](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/search_datasets.jpg) diff --git a/docs/guides/manage_files.md b/docs/guides/manage_files.md index 7366dcb598c..7f633dd0a49 100644 --- a/docs/guides/manage_files.md +++ b/docs/guides/manage_files.md @@ -87,4 +87,4 @@ RAGFlow's file management allows you to download an uploaded file: ![download_file](https://github.com/infiniflow/ragflow/assets/93570324/cf3b297f-7d9b-4522-bf5f-4f45743e4ed5) -> As of RAGFlow v0.20.4, bulk download is not supported, nor can you download an entire folder. +> As of RAGFlow v0.20.5, bulk download is not supported, nor can you download an entire folder. diff --git a/docs/guides/tracing.mdx b/docs/guides/tracing.mdx index 78da8c48ad1..eed2c8cf7dd 100644 --- a/docs/guides/tracing.mdx +++ b/docs/guides/tracing.mdx @@ -18,7 +18,7 @@ RAGFlow ships with a built-in [Langfuse](https://langfuse.com) integration so th Langfuse stores traces, spans and prompt payloads in a purpose-built observability backend and offers filtering and visualisations on top. :::info NOTE -• RAGFlow **≥ 0.20.4** (contains the Langfuse connector) +• RAGFlow **≥ 0.20.5** (contains the Langfuse connector) • A Langfuse workspace (cloud or self-hosted) with a _Project Public Key_ and _Secret Key_ ::: diff --git a/docs/guides/upgrade_ragflow.mdx b/docs/guides/upgrade_ragflow.mdx index 6fa4621a728..57a9bd7d8a3 100644 --- a/docs/guides/upgrade_ragflow.mdx +++ b/docs/guides/upgrade_ragflow.mdx @@ -66,10 +66,10 @@ To upgrade RAGFlow, you must upgrade **both** your code **and** your Docker imag git clone https://github.com/infiniflow/ragflow.git ``` -2. Switch to the latest, officially published release, e.g., `v0.20.4`: +2. Switch to the latest, officially published release, e.g., `v0.20.5`: ```bash - git checkout -f v0.20.4 + git checkout -f v0.20.5 ``` 3. Update **ragflow/docker/.env**: @@ -83,14 +83,14 @@ To upgrade RAGFlow, you must upgrade **both** your code **and** your Docker imag ```bash -RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4-slim +RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5-slim ``` ```bash -RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.4 +RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5 ``` @@ -114,10 +114,10 @@ No, you do not need to. Upgrading RAGFlow in itself will *not* remove your uploa 1. From an environment with Internet access, pull the required Docker image. 2. Save the Docker image to a **.tar** file. ```bash - docker save -o ragflow.v0.20.4.tar infiniflow/ragflow:v0.20.4 + docker save -o ragflow.v0.20.5.tar infiniflow/ragflow:v0.20.5 ``` 3. Copy the **.tar** file to the target server. 4. Load the **.tar** file into Docker: ```bash - docker load -i ragflow.v0.20.4.tar + docker load -i ragflow.v0.20.5.tar ``` diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index 2056ae22264..8be0a8b351a 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -44,7 +44,7 @@ This section provides instructions on setting up the RAGFlow server on Linux. If `vm.max_map_count`. This value sets the maximum number of memory map areas a process may have. Its default value is 65530. While most applications require fewer than a thousand maps, reducing this value can result in abnormal behaviors, and the system will throw out-of-memory errors when a process reaches the limitation. - RAGFlow v0.20.4 uses Elasticsearch or [Infinity](https://github.com/infiniflow/infinity) for multiple recall. Setting the value of `vm.max_map_count` correctly is crucial to the proper functioning of the Elasticsearch component. + RAGFlow v0.20.5 uses Elasticsearch or [Infinity](https://github.com/infiniflow/infinity) for multiple recall. Setting the value of `vm.max_map_count` correctly is crucial to the proper functioning of the Elasticsearch component. Date: Wed, 10 Sep 2025 11:21:25 +0800 Subject: [PATCH 0657/1187] Docs: Added v0.20.5 release notes. (#10014) ### What problem does this PR solve? _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change - [x] Documentation Update --- docs/release_notes.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/release_notes.md b/docs/release_notes.md index 25173305e4b..550bae5dd68 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -22,6 +22,31 @@ The embedding models included in a full edition are: These two embedding models are optimized specifically for English and Chinese, so performance may be compromised if you use them to embed documents in other languages. ::: +## v0.20.5 + +Released on September 10, 2025. + +### Improvements + +- Agent Performance Optimized: Improved planning and reflection speed for simple tasks; optimized concurrent tool calls for parallelizable scenarios, significantly reducing overall response time. +- Agent Prompt Framework exposed: Developers can now customize and override framework-level prompts in the system prompt section, enhancing flexibility and control. +- Execute SQL Component Enhanced: Replaced the original variable reference component with a text input field, allowing free-form SQL writing with variable support. +- Chat: Re-enabled Reasoning and Cross-language search. +- Retrieval API Enhanced: Added metadata filtering support to the [Retrieve chunks](https://ragflow.io/docs/dev/http_api_reference#retrieve-chunks) method. + +### Added models + +- Meituan LongCat +- Kimi: kimi-k2-turbo-preview and kimi-k2-0905-preview +- Qwen: qwen3-max-preview +- SiliconFlow: DeepSeek V3.1 + +### Fixed issues + +- Dataset: Deleted files remained searchable. +- Chat: Unable to chat with an Ollama model. +- Agent: Resolved issues including cite toggle failure, task mode requiring dialogue triggers, repeated answers in multi-turn dialogues, and duplicate summarization of parallel execution results. + ## v0.20.4 Released on August 27, 2025. From 0d9c1f1c3c861ecfb9adcef3de94c266dc9cf9d0 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Wed, 10 Sep 2025 13:02:53 +0800 Subject: [PATCH 0658/1187] Feat: dataflow supports Spreadsheet and Word processor document (#9996) ### What problem does this PR solve? Dataflow supports Spreadsheet and Word processor document ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- deepdoc/parser/excel_parser.py | 24 +++-- rag/flow/chunker/chunker.py | 7 +- rag/flow/chunker/schema.py | 2 +- rag/flow/parser/parser.py | 101 ++++++++++++++---- .../tests/dsl_examples/general_pdf_all.json | 13 ++- rag/flow/tokenizer/schema.py | 2 +- rag/flow/tokenizer/tokenizer.py | 6 +- rag/llm/embedding_model.py | 6 +- rag/nlp/__init__.py | 8 +- 9 files changed, 126 insertions(+), 43 deletions(-) diff --git a/deepdoc/parser/excel_parser.py b/deepdoc/parser/excel_parser.py index 29bf4e2e69e..315df7df316 100644 --- a/deepdoc/parser/excel_parser.py +++ b/deepdoc/parser/excel_parser.py @@ -22,10 +22,10 @@ from rag.nlp import find_codec # copied from `/openpyxl/cell/cell.py` -ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]') +ILLEGAL_CHARACTERS_RE = re.compile(r"[\000-\010]|[\013-\014]|[\016-\037]") -class RAGFlowExcelParser: +class RAGFlowExcelParser: @staticmethod def _load_excel_to_workbook(file_like_object): if isinstance(file_like_object, bytes): @@ -36,7 +36,7 @@ def _load_excel_to_workbook(file_like_object): file_head = file_like_object.read(4) file_like_object.seek(0) - if not (file_head.startswith(b'PK\x03\x04') or file_head.startswith(b'\xD0\xCF\x11\xE0')): + if not (file_head.startswith(b"PK\x03\x04") or file_head.startswith(b"\xd0\xcf\x11\xe0")): logging.info("Not an Excel file, converting CSV to Excel Workbook") try: @@ -48,7 +48,7 @@ def _load_excel_to_workbook(file_like_object): raise Exception(f"Failed to parse CSV and convert to Excel Workbook: {e_csv}") try: - return load_workbook(file_like_object,data_only= True) + return load_workbook(file_like_object, data_only=True) except Exception as e: logging.info(f"openpyxl load error: {e}, try pandas instead") try: @@ -59,7 +59,7 @@ def _load_excel_to_workbook(file_like_object): except Exception as ex: logging.info(f"pandas with default engine load error: {ex}, try calamine instead") file_like_object.seek(0) - df = pd.read_excel(file_like_object, engine='calamine') + df = pd.read_excel(file_like_object, engine="calamine") return RAGFlowExcelParser._dataframe_to_workbook(df) except Exception as e_pandas: raise Exception(f"pandas.read_excel error: {e_pandas}, original openpyxl error: {e}") @@ -116,9 +116,7 @@ def _fmt(v): tb = "" tb += f"" tb += tb_rows_0 - for r in list( - rows[1 + chunk_i * chunk_rows: min(1 + (chunk_i + 1) * chunk_rows, len(rows))] - ): + for r in list(rows[1 + chunk_i * chunk_rows : min(1 + (chunk_i + 1) * chunk_rows, len(rows))]): tb += "" for i, c in enumerate(r): if c.value is None: @@ -133,8 +131,16 @@ def _fmt(v): def markdown(self, fnm): import pandas as pd + file_like_object = BytesIO(fnm) if not isinstance(fnm, str) else fnm - df = pd.read_excel(file_like_object) + try: + file_like_object.seek(0) + df = pd.read_excel(file_like_object) + except Exception as e: + logging.warning(f"Parse spreadsheet error: {e}, trying to interpret as CSV file") + file_like_object.seek(0) + df = pd.read_csv(file_like_object) + df = df.replace(r"^\s*$", "", regex=True) return df.to_markdown(index=False) def __call__(self, fnm): diff --git a/rag/flow/chunker/chunker.py b/rag/flow/chunker/chunker.py index f853fc9e77e..a8281c30657 100644 --- a/rag/flow/chunker/chunker.py +++ b/rag/flow/chunker/chunker.py @@ -73,11 +73,13 @@ class Chunker(ProcessBase): def _general(self, from_upstream: ChunkerFromUpstream): self.callback(random.randint(1, 5) / 100.0, "Start to chunk via `General`.") - if from_upstream.output_format in ["markdown", "text"]: + if from_upstream.output_format in ["markdown", "text", "html"]: if from_upstream.output_format == "markdown": payload = from_upstream.markdown_result - else: # == "text" + elif from_upstream.output_format == "text": payload = from_upstream.text_result + else: # == "html" + payload = from_upstream.html_result if not payload: payload = "" @@ -90,6 +92,7 @@ def _general(self, from_upstream: ChunkerFromUpstream): ) return [{"text": c} for c in cks] + # json sections, section_images = [], [] for o in from_upstream.json_result or []: sections.append((o.get("text", ""), o.get("position_tag", ""))) diff --git a/rag/flow/chunker/schema.py b/rag/flow/chunker/schema.py index 0f0e3042cde..bfeff447d03 100644 --- a/rag/flow/chunker/schema.py +++ b/rag/flow/chunker/schema.py @@ -29,7 +29,7 @@ class ChunkerFromUpstream(BaseModel): json_result: list[dict[str, Any]] | None = Field(default=None, alias="json") markdown_result: str | None = Field(default=None, alias="markdown") text_result: str | None = Field(default=None, alias="text") - html_result: str | None = Field(default=None, alias="html") + html_result: list[str] | None = Field(default=None, alias="html") model_config = ConfigDict(populate_by_name=True, extra="forbid") diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py index fd65665fa31..f70c4d95819 100644 --- a/rag/flow/parser/parser.py +++ b/rag/flow/parser/parser.py @@ -12,6 +12,7 @@ # 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 random import trio @@ -29,8 +30,18 @@ class ParserParam(ProcessParamBase): def __init__(self): super().__init__() self.allowed_output_format = { - "pdf": ["json", "markdown"], - "excel": ["json", "markdown", "html"], + "pdf": [ + "json", + "markdown", + ], + "spreadsheet": [ + "json", + "markdown", + "html", + ], + "word": [ + "json", + ], "ppt": [], "image": [], "email": [], @@ -44,12 +55,25 @@ def __init__(self): "parse_method": "deepdoc", # deepdoc/plain_text/vlm "vlm_name": "", "lang": "Chinese", - "suffix": ["pdf"], + "suffix": [ + "pdf", + ], "output_format": "json", }, - "excel": { + "spreadsheet": { "output_format": "html", - "suffix": ["xls", "xlsx", "csv"], + "suffix": [ + "xls", + "xlsx", + "csv", + ], + }, + "word": { + "suffix": [ + "doc", + "docx", + ], + "output_format": "json", }, "ppt": {}, "image": { @@ -76,10 +100,15 @@ def check(self): pdf_output_format = pdf_config.get("output_format", "") self.check_valid_value(pdf_output_format, "PDF output format abnormal.", self.allowed_output_format["pdf"]) - excel_config = self.setups.get("excel", "") - if excel_config: - excel_output_format = excel_config.get("output_format", "") - self.check_valid_value(excel_output_format, "Excel output format abnormal.", self.allowed_output_format["excel"]) + spreadsheet_config = self.setups.get("spreadsheet", "") + if spreadsheet_config: + spreadsheet_output_format = spreadsheet_config.get("output_format", "") + self.check_valid_value(spreadsheet_output_format, "Spreadsheet output format abnormal.", self.allowed_output_format["spreadsheet"]) + + doc_config = self.setups.get("doc", "") + if doc_config: + doc_output_format = doc_config.get("output_format", "") + self.check_valid_value(doc_output_format, "Word processer document output format abnormal.", self.allowed_output_format["doc"]) image_config = self.setups.get("image", "") if image_config: @@ -93,10 +122,13 @@ def get_input_form(self) -> dict[str, dict]: class Parser(ProcessBase): component_name = "Parser" - def _pdf(self, blob): + def _pdf(self, from_upstream: ParserFromUpstream): self.callback(random.randint(1, 5) / 100.0, "Start to work on a PDF.") + + blob = from_upstream.blob conf = self._param.setups["pdf"] self.set_output("output_format", conf["output_format"]) + if conf.get("parse_method") == "deepdoc": bboxes = RAGFlowPdfParser().parse_into_bboxes(blob, callback=self.callback) elif conf.get("parse_method") == "plain_text": @@ -110,6 +142,7 @@ def _pdf(self, blob): for t, poss in lines: pn, x0, x1, top, bott = poss.split(" ") bboxes.append({"page_number": int(pn), "x0": float(x0), "x1": float(x1), "top": float(top), "bottom": float(bott), "text": t}) + if conf.get("output_format") == "json": self.set_output("json", bboxes) if conf.get("output_format") == "markdown": @@ -123,23 +156,53 @@ def _pdf(self, blob): mkdn += b.get("text", "") + "\n" self.set_output("markdown", mkdn) - def _excel(self, blob): - self.callback(random.randint(1, 5) / 100.0, "Start to work on a Excel.") - conf = self._param.setups["excel"] + def _spreadsheet(self, from_upstream: ParserFromUpstream): + self.callback(random.randint(1, 5) / 100.0, "Start to work on a Spreadsheet.") + + blob = from_upstream.blob + conf = self._param.setups["spreadsheet"] self.set_output("output_format", conf["output_format"]) - excel_parser = ExcelParser() + + print("spreadsheet {conf=}", flush=True) + spreadsheet_parser = ExcelParser() if conf.get("output_format") == "html": - html = excel_parser.html(blob, 1000000000) + html = spreadsheet_parser.html(blob, 1000000000) self.set_output("html", html) elif conf.get("output_format") == "json": - self.set_output("json", [{"text": txt} for txt in excel_parser(blob) if txt]) + self.set_output("json", [{"text": txt} for txt in spreadsheet_parser(blob) if txt]) elif conf.get("output_format") == "markdown": - self.set_output("markdown", excel_parser.markdown(blob)) + self.set_output("markdown", spreadsheet_parser.markdown(blob)) + + def _word(self, from_upstream: ParserFromUpstream): + from tika import parser as word_parser + + self.callback(random.randint(1, 5) / 100.0, "Start to work on a Word Processor Document") + + blob = from_upstream.blob + name = from_upstream.name + conf = self._param.setups["word"] + self.set_output("output_format", conf["output_format"]) + + print("word {conf=}", flush=True) + doc_parsed = word_parser.from_buffer(blob) + + sections = [] + if doc_parsed.get("content"): + sections = doc_parsed["content"].split("\n") + sections = [{"text": section} for section in sections if section] + else: + logging.warning(f"tika.parser got empty content from {name}.") + + # json + assert conf.get("output_format") == "json", "have to be json for doc" + if conf.get("output_format") == "json": + self.set_output("json", sections) async def _invoke(self, **kwargs): function_map = { "pdf": self._pdf, - "excel": self._excel, + "spreadsheet": self._spreadsheet, + "word": self._word, } try: from_upstream = ParserFromUpstream.model_validate(kwargs) @@ -150,5 +213,5 @@ async def _invoke(self, **kwargs): for p_type, conf in self._param.setups.items(): if from_upstream.name.split(".")[-1].lower() not in conf.get("suffix", []): continue - await trio.to_thread.run_sync(function_map[p_type], from_upstream.blob) + await trio.to_thread.run_sync(function_map[p_type], from_upstream) break diff --git a/rag/flow/tests/dsl_examples/general_pdf_all.json b/rag/flow/tests/dsl_examples/general_pdf_all.json index 7142e55478a..df713bb6de5 100644 --- a/rag/flow/tests/dsl_examples/general_pdf_all.json +++ b/rag/flow/tests/dsl_examples/general_pdf_all.json @@ -23,13 +23,20 @@ ], "output_format": "json" }, - "excel": { - "output_format": "html", + "spreadsheet": { "suffix": [ "xls", "xlsx", "csv" - ] + ], + "output_format": "html" + }, + "word": { + "suffix": [ + "doc", + "docx" + ], + "output_format": "json" } } } diff --git a/rag/flow/tokenizer/schema.py b/rag/flow/tokenizer/schema.py index 508fa002c61..d58725171a0 100644 --- a/rag/flow/tokenizer/schema.py +++ b/rag/flow/tokenizer/schema.py @@ -31,7 +31,7 @@ class TokenizerFromUpstream(BaseModel): json_result: list[dict[str, Any]] | None = Field(default=None, alias="json") markdown_result: str | None = Field(default=None, alias="markdown") text_result: str | None = Field(default=None, alias="text") - html_result: str | None = Field(default=None, alias="html") + html_result: list[str] | None = Field(default=None, alias="html") model_config = ConfigDict(populate_by_name=True, extra="forbid") diff --git a/rag/flow/tokenizer/tokenizer.py b/rag/flow/tokenizer/tokenizer.py index 5ad20977600..5b43a9d82b1 100644 --- a/rag/flow/tokenizer/tokenizer.py +++ b/rag/flow/tokenizer/tokenizer.py @@ -117,11 +117,13 @@ async def _invoke(self, **kwargs): ck["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(ck["content_ltks"]) if i % 100 == 99: self.callback(i * 1.0 / len(chunks) / parts) - elif from_upstream.output_format in ["markdown", "text"]: + elif from_upstream.output_format in ["markdown", "text", "html"]: if from_upstream.output_format == "markdown": payload = from_upstream.markdown_result - else: # == "text" + elif from_upstream.output_format == "text": payload = from_upstream.text_result + else: # == "html" + payload = from_upstream.html_result if not payload: return "" diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 4a9f375a67b..d39e0f0cc8d 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -751,6 +751,8 @@ def encode(self, texts: list): token_count = 0 for i in range(0, len(texts), batch_size): texts_batch = texts[i : i + batch_size] + texts_batch = [" " if not text.strip() else text for text in texts_batch] + payload = { "model": self.model_name, "input": texts_batch, @@ -935,7 +937,7 @@ def __init__(self, key, model_name, base_url="https://ai.gitee.com/v1/embeddings if not base_url: base_url = "https://ai.gitee.com/v1/embeddings" super().__init__(key, model_name, base_url) - + class DeepInfraEmbed(OpenAIEmbed): _FACTORY_NAME = "DeepInfra" @@ -951,4 +953,4 @@ class Ai302Embed(Base): def __init__(self, key, model_name, base_url="https://api.302.ai/v1/embeddings"): if not base_url: base_url = "https://api.302.ai/v1/embeddings" - super().__init__(key, model_name, base_url) \ No newline at end of file + super().__init__(key, model_name, base_url) diff --git a/rag/nlp/__init__.py b/rag/nlp/__init__.py index fc548ee61c2..2424ba03355 100644 --- a/rag/nlp/__init__.py +++ b/rag/nlp/__init__.py @@ -518,7 +518,7 @@ def binary_search(arr, target): return res -def naive_merge(sections, chunk_token_num=128, delimiter="\n。;!?", overlapped_percent=0): +def naive_merge(sections: str | list, chunk_token_num=128, delimiter="\n。;!?", overlapped_percent=0): from deepdoc.parser.pdf_parser import RAGFlowPdfParser if not sections: return [] @@ -534,7 +534,7 @@ def add_chunk(t, pos): pos = "" if tnum < 8: pos = "" - # Ensure that the length of the merged chunk does not exceed chunk_token_num + # Ensure that the length of the merged chunk does not exceed chunk_token_num if cks[-1] == "" or tk_nums[-1] > chunk_token_num * (100 - overlapped_percent)/100.: if cks: overlapped = RAGFlowPdfParser.remove_tag(cks[-1]) @@ -638,10 +638,10 @@ def concat_img(img1, img2): return img2 if not img1 and not img2: return None - + if img1 is img2: return img1 - + if isinstance(img1, Image.Image) and isinstance(img2, Image.Image): pixel_data1 = img1.tobytes() pixel_data2 = img2.tobytes() From 41cdba19bab229d48e29d7346cfefe0abc00e491 Mon Sep 17 00:00:00 2001 From: Lynn Date: Wed, 10 Sep 2025 13:31:02 +0800 Subject: [PATCH 0659/1187] Feat: dataflow supports markdown (#10003) ### What problem does this PR solve? Dataflow supports markdown. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Co-authored-by: Kevin Hu --- rag/flow/parser/parser.py | 46 ++++++++++++++++++- .../tests/dsl_examples/general_pdf_all.json | 8 ++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py index f70c4d95819..26f5021f10f 100644 --- a/rag/flow/parser/parser.py +++ b/rag/flow/parser/parser.py @@ -75,6 +75,10 @@ def __init__(self): ], "output_format": "json", }, + "markdown": { + "suffix": ["md", "markdown"], + "output_format": "json", + }, "ppt": {}, "image": { "parse_method": "ocr", @@ -198,11 +202,51 @@ def _word(self, from_upstream: ParserFromUpstream): if conf.get("output_format") == "json": self.set_output("json", sections) + def _markdown(self, from_upstream: ParserFromUpstream): + from functools import reduce + + from rag.app.naive import Markdown as naive_markdown_parser + from rag.nlp import concat_img + + self.callback(random.randint(1, 5) / 100.0, "Start to work on a Word Processor Document") + + blob = from_upstream.blob + name = from_upstream.name + conf = self._param.setups["markdown"] + self.set_output("output_format", conf["output_format"]) + + print("markdown {conf=}", flush=True) + + markdown_parser = naive_markdown_parser() + sections, tables = markdown_parser(name, blob, separate_tables=False) + + # json + assert conf.get("output_format") == "json", "have to be json for doc" + if conf.get("output_format") == "json": + json_results = [] + + for section_text, _ in sections: + json_result = { + "text": section_text, + } + + images = markdown_parser.get_pictures(section_text) if section_text else None + if images: + # If multiple images found, combine them using concat_img + combined_image = reduce(concat_img, images) if len(images) > 1 else images[0] + json_result["image"] = combined_image + + json_results.append(json_result) + + self.set_output("json", json_results) + + async def _invoke(self, **kwargs): function_map = { "pdf": self._pdf, + "markdown": self._markdown, "spreadsheet": self._spreadsheet, - "word": self._word, + "word": self._word } try: from_upstream = ParserFromUpstream.model_validate(kwargs) diff --git a/rag/flow/tests/dsl_examples/general_pdf_all.json b/rag/flow/tests/dsl_examples/general_pdf_all.json index df713bb6de5..42eae3f61b7 100644 --- a/rag/flow/tests/dsl_examples/general_pdf_all.json +++ b/rag/flow/tests/dsl_examples/general_pdf_all.json @@ -37,9 +37,17 @@ "docx" ], "output_format": "json" + }, + "markdown": { + "suffix": [ + "md", + "markdown" + ], + "output_format": "json" } } } + } }, "downstream": ["Chunker:0"], "upstream": ["Begin"] From 127af4e45c3a93eced8d3504f0620f3b5de98c18 Mon Sep 17 00:00:00 2001 From: Stephen Hu Date: Wed, 10 Sep 2025 15:55:33 +0800 Subject: [PATCH 0660/1187] Refactor:Improve BytesIO usage for image2base64 (#9997) ### What problem does this PR solve? Improve BytesIO usage for image2base64 ### Type of change - [x] Refactoring --- rag/llm/cv_model.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index 4ca4ee190db..83c79bbfdef 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -124,17 +124,19 @@ def image2base64(image): mime = "image/jpeg" b64 = base64.b64encode(data).decode("utf-8") return f"data:{mime};base64,{b64}" - buffered = BytesIO() - fmt = "JPEG" - try: - image.save(buffered, format="JPEG") - except Exception: - buffered = BytesIO() # reset buffer before saving PNG - image.save(buffered, format="PNG") - fmt = "PNG" - data = buffered.getvalue() - b64 = base64.b64encode(data).decode("utf-8") - mime = f"image/{fmt.lower()}" + with BytesIO() as buffered: + fmt = "JPEG" + try: + image.save(buffered, format="JPEG") + except Exception: + # reset buffer before saving PNG + buffered.seek(0) + buffered.truncate() + image.save(buffered, format="PNG") + fmt = "PNG" + data = buffered.getvalue() + b64 = base64.b64encode(data).decode("utf-8") + mime = f"image/{fmt.lower()}" return f"data:{mime};base64,{b64}" def prompt(self, b64): From bbe6ed3b90657eda7ce187455da44c0422d6919d Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 10 Sep 2025 15:55:59 +0800 Subject: [PATCH 0661/1187] Fix: Fixed the issue where newly added tool operators would disappear after editing the form #10013 (#10016) ### What problem does this PR solve? Fix: Fixed the issue where newly added tool operators would disappear after editing the form #10013 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/pages/agent/form/agent-form/index.tsx | 7 ------- web/src/pages/agent/form/agent-form/use-values.ts | 12 +++++++++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 5aba3582668..65fb364b7c9 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -57,13 +57,6 @@ const FormSchema = z.object({ // ) // .optional(), message_history_window_size: z.coerce.number(), - tools: z - .array( - z.object({ - component_name: z.string(), - }), - ) - .optional(), ...LlmSettingSchema, max_retries: z.coerce.number(), delay_after_error: z.coerce.number().optional(), diff --git a/web/src/pages/agent/form/agent-form/use-values.ts b/web/src/pages/agent/form/agent-form/use-values.ts index b2d61dc9f25..f8747e4b4d4 100644 --- a/web/src/pages/agent/form/agent-form/use-values.ts +++ b/web/src/pages/agent/form/agent-form/use-values.ts @@ -1,15 +1,21 @@ import { useFetchModelId } from '@/hooks/logic-hooks'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { get, isEmpty } from 'lodash'; +import { get, isEmpty, omit } from 'lodash'; import { useMemo } from 'react'; import { initialAgentValues } from '../../constant'; +// You need to exclude the mcp and tools fields that are not in the form, +// otherwise the form data update will reset the tools or mcp data to an array +function omitToolsAndMcp(values: Record) { + return omit(values, ['mcp', 'tools']); +} + export function useValues(node?: RAGFlowNodeType) { const llmId = useFetchModelId(); const defaultValues = useMemo( () => ({ - ...initialAgentValues, + ...omitToolsAndMcp(initialAgentValues), llm_id: llmId, prompts: '', }), @@ -24,7 +30,7 @@ export function useValues(node?: RAGFlowNodeType) { } return { - ...formData, + ...omitToolsAndMcp(formData), prompts: get(formData, 'prompts.0.content', ''), }; }, [defaultValues, node?.data?.form]); From 7d14455fbec880012b42cd63d54c25465878c0f8 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 10 Sep 2025 15:56:10 +0800 Subject: [PATCH 0662/1187] Feat: Add type card to create agent dialog #9869 (#10025) ### What problem does this PR solve? Feat: Add type card to create agent dialog #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/ragflow-form.tsx | 8 +- web/src/components/ui/dialog.tsx | 2 +- web/src/locales/en.ts | 2 +- web/src/locales/zh.ts | 2 +- web/src/pages/agents/agent-templates.tsx | 2 + web/src/pages/agents/create-agent-dialog.tsx | 14 ++- web/src/pages/agents/create-agent-form.tsx | 114 +++++++++++++------ web/src/pages/agents/index.tsx | 57 +++++++++- 8 files changed, 151 insertions(+), 50 deletions(-) diff --git a/web/src/components/ragflow-form.tsx b/web/src/components/ragflow-form.tsx index 0afe9a670f3..4151e6fef79 100644 --- a/web/src/components/ragflow-form.tsx +++ b/web/src/components/ragflow-form.tsx @@ -15,6 +15,7 @@ type RAGFlowFormItemProps = { tooltip?: ReactNode; children: ReactNode | ((field: ControllerRenderProps) => ReactNode); horizontal?: boolean; + required?: boolean; }; export function RAGFlowFormItem({ @@ -23,6 +24,7 @@ export function RAGFlowFormItem({ tooltip, children, horizontal = false, + required = false, }: RAGFlowFormItemProps) { const form = useFormContext(); return ( @@ -35,7 +37,11 @@ export function RAGFlowFormItem({ 'flex items-center': horizontal, })} > - + {label} diff --git a/web/src/components/ui/dialog.tsx b/web/src/components/ui/dialog.tsx index f65644ee026..d091c1f031b 100644 --- a/web/src/components/ui/dialog.tsx +++ b/web/src/components/ui/dialog.tsx @@ -38,7 +38,7 @@ const DialogContent = React.forwardRef< ( MenuItemKey.Recommended, ); + useEffect(() => { setTemplateList(list); }, [list]); + const { visible: creatingVisible, hideModal: hideCreatingModal, diff --git a/web/src/pages/agents/create-agent-dialog.tsx b/web/src/pages/agents/create-agent-dialog.tsx index 00815d0bc27..c4b7fd46765 100644 --- a/web/src/pages/agents/create-agent-dialog.tsx +++ b/web/src/pages/agents/create-agent-dialog.tsx @@ -6,16 +6,18 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; -import { IModalProps } from '@/interfaces/common'; import { TagRenameId } from '@/pages/add-knowledge/constant'; import { useTranslation } from 'react-i18next'; -import { CreateAgentForm } from './create-agent-form'; +import { CreateAgentForm, CreateAgentFormProps } from './create-agent-form'; + +type CreateAgentDialogProps = CreateAgentFormProps; export function CreateAgentDialog({ hideModal, onOk, loading, -}: IModalProps) { + shouldChooseAgent, +}: CreateAgentDialogProps) { const { t } = useTranslation(); return ( @@ -24,7 +26,11 @@ export function CreateAgentDialog({ {t('flow.createGraph')} - + {t('common.save')} diff --git a/web/src/pages/agents/create-agent-form.tsx b/web/src/pages/agents/create-agent-form.tsx index 43883b9abfd..fac0cbb1e28 100644 --- a/web/src/pages/agents/create-agent-form.tsx +++ b/web/src/pages/agents/create-agent-form.tsx @@ -4,6 +4,8 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Card, CardContent } from '@/components/ui/card'; import { Form, FormControl, @@ -14,10 +16,76 @@ import { } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { IModalProps } from '@/interfaces/common'; +import { cn } from '@/lib/utils'; import { TagRenameId } from '@/pages/add-knowledge/constant'; +import { BrainCircuit, Check, Route } from 'lucide-react'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -export function CreateAgentForm({ hideModal, onOk }: IModalProps) { +export type CreateAgentFormProps = IModalProps & { + shouldChooseAgent?: boolean; +}; + +enum FlowType { + Agent = 'agent', + Flow = 'flow', +} + +type FlowTypeCardProps = { + value?: FlowType; + onChange?: (value: FlowType) => void; +}; +function FlowTypeCards({ value, onChange }: FlowTypeCardProps) { + const handleChange = useCallback( + (value: FlowType) => () => { + onChange?.(value); + }, + [onChange], + ); + + return ( +
    + {Object.values(FlowType).map((val) => { + const isActive = value === val; + return ( + + +
    + {val === FlowType.Agent ? ( + + ) : ( + + )} +

    {val}

    +
    + {isActive && } +
    +
    + ); + })} +
    + ); +} + +export function CreateAgentForm({ + hideModal, + onOk, + shouldChooseAgent = false, +}: CreateAgentFormProps) { const { t } = useTranslation(); const FormSchema = z.object({ name: z @@ -28,11 +96,12 @@ export function CreateAgentForm({ hideModal, onOk }: IModalProps) { .trim(), tag: z.string().trim().optional(), description: z.string().trim().optional(), + type: z.nativeEnum(FlowType).optional(), }); const form = useForm>({ resolver: zodResolver(FormSchema), - defaultValues: { name: '' }, + defaultValues: { name: '', type: FlowType.Agent }, }); async function onSubmit(data: z.infer) { @@ -49,12 +118,17 @@ export function CreateAgentForm({ hideModal, onOk }: IModalProps) { className="space-y-6" id={TagRenameId} > + {shouldChooseAgent && ( + + + + )} ( - {t('common.name')} + {t('common.name')} ) { )} /> - {/* ( - - {t('flow.tag')} - - - - - - )} - /> - ( - - {t('flow.description')} - - - - - - )} - /> */} ); diff --git a/web/src/pages/agents/index.tsx b/web/src/pages/agents/index.tsx index d4d5c913ac0..95c6f180871 100644 --- a/web/src/pages/agents/index.tsx +++ b/web/src/pages/agents/index.tsx @@ -1,14 +1,22 @@ import ListFilterBar from '@/components/list-filter-bar'; import { RenameDialog } from '@/components/rename-dialog'; import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; import { RAGFlowPagination } from '@/components/ui/ragflow-pagination'; +import { useSetModalState } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useFetchAgentListByPage } from '@/hooks/use-agent-request'; import { t } from 'i18next'; import { pick } from 'lodash'; -import { Plus } from 'lucide-react'; +import { Clipboard, ClipboardPlus, FileInput, Plus } from 'lucide-react'; import { useCallback } from 'react'; import { AgentCard } from './agent-card'; +import { CreateAgentDialog } from './create-agent-dialog'; import { useRenameAgent } from './use-rename-agent'; export default function Agents() { @@ -25,6 +33,12 @@ export default function Agents() { showAgentRenameModal, } = useRenameAgent(); + const { + visible: creatingVisible, + hideModal: hideCreatingModal, + showModal: showCreatingModal, + } = useSetModalState(); + const handlePageChange = useCallback( (page: number, pageSize?: number) => { setPagination({ page, pageSize }); @@ -41,10 +55,34 @@ export default function Agents() { onSearchChange={handleInputChange} icon="agent" > - + + + + + + + + Create from Blank + + + + Create from Template + + + + Import json file + + +
    @@ -75,6 +113,15 @@ export default function Agents() { loading={agentRenameLoading} > )} + {creatingVisible && ( + {}} + > + )} ); } From fc95d113c3a8f8436baafcfe2a8f4e9634226f00 Mon Sep 17 00:00:00 2001 From: Liu An Date: Wed, 10 Sep 2025 16:39:26 +0800 Subject: [PATCH 0663/1187] Feat(config): Update service config template new defaults (#10029) ### What problem does this PR solve? - Update default LLM configuration with BAAI and model details #9404 - Add SMTP configuration section #9479 - Add OpenDAL storage configuration option #8232 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- docker/service_conf.yaml.template | 37 ++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/docker/service_conf.yaml.template b/docker/service_conf.yaml.template index c36e921c9c7..5db35b9c7f2 100644 --- a/docker/service_conf.yaml.template +++ b/docker/service_conf.yaml.template @@ -29,7 +29,6 @@ redis: db: 1 password: '${REDIS_PASSWORD:-infini_rag_flow}' host: '${REDIS_HOST:-redis}:6379' - # postgres: # name: '${POSTGRES_DBNAME:-rag_flow}' # user: '${POSTGRES_USER:-rag_flow}' @@ -65,15 +64,26 @@ redis: # secret: 'secret' # tenant_id: 'tenant_id' # container_name: 'container_name' +# The OSS object storage uses the MySQL configuration above by default. If you need to switch to another object storage service, please uncomment and configure the following parameters. +# opendal: +# scheme: 'mysql' # Storage type, such as s3, oss, azure, etc. +# config: +# oss_table: 'opendal_storage' # user_default_llm: -# factory: 'Tongyi-Qianwen' -# api_key: 'sk-xxxxxxxxxxxxx' -# base_url: '' +# factory: 'BAAI' +# api_key: 'backup' +# base_url: 'backup_base_url' # default_models: -# chat_model: 'qwen-plus' -# embedding_model: 'BAAI/bge-large-zh-v1.5@BAAI' -# rerank_model: '' -# asr_model: '' +# chat_model: +# name: 'qwen2.5-7b-instruct' +# factory: 'xxxx' +# api_key: 'xxxx' +# base_url: 'https://api.xx.com' +# embedding_model: +# name: 'bge-m3' +# rerank_model: 'bge-reranker-v2' +# asr_model: +# model: 'whisper-large-v3' # alias of name # image2text_model: '' # oauth: # oauth2: @@ -109,3 +119,14 @@ redis: # switch: false # component: false # dataset: false +# smtp: +# mail_server: "" +# mail_port: 465 +# mail_use_ssl: true +# mail_use_tls: false +# mail_username: "" +# mail_password: "" +# mail_default_sender: +# - "RAGFlow" # display name +# - "" # sender email address +# mail_frontend_url: "https://your-frontend.example.com" From df8d31451b6a164dc87ef177114b9e312d105958 Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 10 Sep 2025 18:22:16 +0800 Subject: [PATCH 0664/1187] Feat: Import dsl from agent list page #9869 (#10033) ### What problem does this PR solve? Feat: Import dsl from agent list page #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/hooks/use-agent-request.ts | 14 +---- web/src/interfaces/database/agent.ts | 4 +- web/src/pages/agent/hooks/use-export-json.ts | 56 +---------------- web/src/pages/agent/index.tsx | 24 +------- web/src/pages/agents/agent-templates.tsx | 3 +- web/src/pages/agents/constant.ts | 4 ++ web/src/pages/agents/create-agent-form.tsx | 61 +++++-------------- .../pages/agents/hooks/use-create-agent.ts | 42 +++++++++++++ web/src/pages/agents/index.tsx | 36 ++++++++--- web/src/pages/agents/name-form-field.tsx | 28 +++++++++ web/src/pages/agents/template-card.tsx | 52 ++++++---------- .../upload-agent-dialog/index.tsx | 6 +- .../upload-agent-dialog/upload-agent-form.tsx | 42 ++++--------- web/src/pages/agents/use-import-json.ts | 56 +++++++++++++++++ 14 files changed, 213 insertions(+), 215 deletions(-) create mode 100644 web/src/pages/agents/constant.ts create mode 100644 web/src/pages/agents/hooks/use-create-agent.ts create mode 100644 web/src/pages/agents/name-form-field.tsx rename web/src/pages/{agent => agents}/upload-agent-dialog/index.tsx (85%) rename web/src/pages/{agent => agents}/upload-agent-dialog/upload-agent-form.tsx (56%) create mode 100644 web/src/pages/agents/use-import-json.ts diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index 6a3f53eca8e..17d5424badc 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -24,9 +24,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useDebounce } from 'ahooks'; import { get, set } from 'lodash'; import { useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; import { useParams, useSearchParams } from 'umi'; -import { v4 as uuid } from 'uuid'; import { useGetPaginationWithRouter, useHandleSearchChange, @@ -80,7 +78,7 @@ export const EmptyDsl = { component_name: 'Begin', params: {}, }, - downstream: ['Answer:China'], // other edge target is downstream, edge source is current node id + downstream: [], // other edge target is downstream, edge source is current node id upstream: [], // edge source is upstream, edge target is current node id }, }, @@ -96,21 +94,11 @@ export const EmptyDsl = { }; export const useFetchAgentTemplates = () => { - const { t } = useTranslation(); - const { data } = useQuery({ queryKey: [AgentApiAction.FetchAgentTemplates], initialData: [], queryFn: async () => { const { data } = await agentService.listTemplates(); - if (Array.isArray(data?.data)) { - data.data.unshift({ - id: uuid(), - title: t('flow.blank'), - description: t('flow.createFromNothing'), - dsl: EmptyDsl, - }); - } return data.data; }, diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index 4fd36de2f00..7994426ea05 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -41,8 +41,8 @@ export interface DSL { path?: string[]; answer?: any[]; graph?: IGraph; - messages: Message[]; - reference: IReference[]; + messages?: Message[]; + reference?: IReference[]; globals: Record; retrieval: IReference[]; } diff --git a/web/src/pages/agent/hooks/use-export-json.ts b/web/src/pages/agent/hooks/use-export-json.ts index 1efe6bb50dd..2f2e9242e8a 100644 --- a/web/src/pages/agent/hooks/use-export-json.ts +++ b/web/src/pages/agent/hooks/use-export-json.ts @@ -1,71 +1,17 @@ -import { useToast } from '@/components/hooks/use-toast'; -import { FileMimeType, Platform } from '@/constants/common'; -import { useSetModalState } from '@/hooks/common-hooks'; import { useFetchAgent } from '@/hooks/use-agent-request'; -import { IGraph } from '@/interfaces/database/flow'; import { downloadJsonFile } from '@/utils/file-util'; -import { message } from 'antd'; -import isEmpty from 'lodash/isEmpty'; import { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; import { useBuildDslData } from './use-build-dsl'; -import { useSetGraphInfo } from './use-set-graph'; -export const useHandleExportOrImportJsonFile = () => { +export const useHandleExportJsonFile = () => { const { buildDslData } = useBuildDslData(); - const { - visible: fileUploadVisible, - hideModal: hideFileUploadModal, - showModal: showFileUploadModal, - } = useSetModalState(); - const setGraphInfo = useSetGraphInfo(); const { data } = useFetchAgent(); - const { t } = useTranslation(); - const { toast } = useToast(); - - const onFileUploadOk = useCallback( - async ({ - fileList, - platform, - }: { - fileList: File[]; - platform: Platform; - }) => { - console.log('🚀 ~ useHandleExportOrImportJsonFile ~ platform:', platform); - if (fileList.length > 0) { - const file = fileList[0]; - if (file.type !== FileMimeType.Json) { - toast({ title: t('flow.jsonUploadTypeErrorMessage') }); - return; - } - - const graphStr = await file.text(); - const errorMessage = t('flow.jsonUploadContentErrorMessage'); - try { - const graph = JSON.parse(graphStr); - if (graphStr && !isEmpty(graph) && Array.isArray(graph?.nodes)) { - setGraphInfo(graph ?? ({} as IGraph)); - hideFileUploadModal(); - } else { - message.error(errorMessage); - } - } catch (error) { - message.error(errorMessage); - } - } - }, - [hideFileUploadModal, setGraphInfo, t, toast], - ); const handleExportJson = useCallback(() => { downloadJsonFile(buildDslData().graph, `${data.title}.json`); }, [buildDslData, data.title]); return { - fileUploadVisible, handleExportJson, - handleImportJson: showFileUploadModal, - hideFileUploadModal, - onFileUploadOk, }; }; diff --git a/web/src/pages/agent/index.tsx b/web/src/pages/agent/index.tsx index b356a93195a..4c49c6daa01 100644 --- a/web/src/pages/agent/index.tsx +++ b/web/src/pages/agent/index.tsx @@ -24,7 +24,6 @@ import { ReactFlowProvider } from '@xyflow/react'; import { ChevronDown, CirclePlay, - Download, History, LaptopMinimalCheck, Logs, @@ -37,7 +36,7 @@ import { useTranslation } from 'react-i18next'; import { useParams } from 'umi'; import AgentCanvas from './canvas'; import { DropdownProvider } from './canvas/context'; -import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; +import { useHandleExportJsonFile } from './hooks/use-export-json'; import { useFetchDataOnMount } from './hooks/use-fetch-data'; import { useGetBeginNodeDataInputs } from './hooks/use-get-begin-query'; import { @@ -46,7 +45,6 @@ import { useWatchAgentChange, } from './hooks/use-save-graph'; import { SettingDialog } from './setting-dialog'; -import { UploadAgentDialog } from './upload-agent-dialog'; import { useAgentHistoryManager } from './use-agent-history-manager'; import { VersionDialog } from './version-dialog'; @@ -71,13 +69,8 @@ export default function Agent() { } = useSetModalState(); const { t } = useTranslation(); useAgentHistoryManager(); - const { - handleExportJson, - handleImportJson, - fileUploadVisible, - onFileUploadOk, - hideFileUploadModal, - } = useHandleExportOrImportJsonFile(); + + const { handleExportJson } = useHandleExportJsonFile(); const { saveGraph, loading } = useSaveGraph(); const { flowDetail: agentDetail } = useFetchDataOnMount(); const inputs = useGetBeginNodeDataInputs(); @@ -158,11 +151,6 @@ export default function Agent() { - - - {t('flow.import')} - - {t('flow.export')} @@ -193,12 +181,6 @@ export default function Agent() { > - {fileUploadVisible && ( - - )} {embedVisible && (
    - {tempListFilter?.map((x, index) => { + {tempListFilter?.map((x) => { return ( & { shouldChooseAgent?: boolean; }; -enum FlowType { - Agent = 'agent', - Flow = 'flow', -} - type FlowTypeCardProps = { value?: FlowType; onChange?: (value: FlowType) => void; @@ -51,7 +40,7 @@ function FlowTypeCards({ value, onChange }: FlowTypeCardProps) { @@ -81,30 +70,28 @@ function FlowTypeCards({ value, onChange }: FlowTypeCardProps) { ); } +export const FormSchema = z.object({ + ...NameFormSchema, + tag: z.string().trim().optional(), + description: z.string().trim().optional(), + type: z.nativeEnum(FlowType).optional(), +}); + +export type FormSchemaType = z.infer; + export function CreateAgentForm({ hideModal, onOk, shouldChooseAgent = false, }: CreateAgentFormProps) { const { t } = useTranslation(); - const FormSchema = z.object({ - name: z - .string() - .min(1, { - message: t('common.namePlaceholder'), - }) - .trim(), - tag: z.string().trim().optional(), - description: z.string().trim().optional(), - type: z.nativeEnum(FlowType).optional(), - }); - const form = useForm>({ + const form = useForm({ resolver: zodResolver(FormSchema), defaultValues: { name: '', type: FlowType.Agent }, }); - async function onSubmit(data: z.infer) { + async function onSubmit(data: FormSchemaType) { const ret = await onOk?.(data); if (ret) { hideModal?.(); @@ -123,23 +110,7 @@ export function CreateAgentForm({ )} - ( - - {t('common.name')} - - - - - - )} - /> + ); diff --git a/web/src/pages/agents/hooks/use-create-agent.ts b/web/src/pages/agents/hooks/use-create-agent.ts new file mode 100644 index 00000000000..3693ce4ec52 --- /dev/null +++ b/web/src/pages/agents/hooks/use-create-agent.ts @@ -0,0 +1,42 @@ +import { useSetModalState } from '@/hooks/common-hooks'; +import { EmptyDsl, useSetAgent } from '@/hooks/use-agent-request'; +import { DSL } from '@/interfaces/database/agent'; +import { useCallback } from 'react'; +import { FlowType } from '../constant'; +import { FormSchemaType } from '../create-agent-form'; + +export function useCreateAgentOrPipeline() { + const { loading, setAgent } = useSetAgent(); + const { + visible: creatingVisible, + hideModal: hideCreatingModal, + showModal: showCreatingModal, + } = useSetModalState(); + + const createAgent = useCallback( + async (name: string) => { + return setAgent({ title: name, dsl: EmptyDsl as DSL }); + }, + [setAgent], + ); + + const handleCreateAgentOrPipeline = useCallback( + async (data: FormSchemaType) => { + if (data.type === FlowType.Agent) { + const ret = await createAgent(data.name); + if (ret.code === 0) { + hideCreatingModal(); + } + } + }, + [createAgent, hideCreatingModal], + ); + + return { + loading, + creatingVisible, + hideCreatingModal, + showCreatingModal, + handleCreateAgentOrPipeline, + }; +} diff --git a/web/src/pages/agents/index.tsx b/web/src/pages/agents/index.tsx index 95c6f180871..5889753ddf4 100644 --- a/web/src/pages/agents/index.tsx +++ b/web/src/pages/agents/index.tsx @@ -8,7 +8,6 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { RAGFlowPagination } from '@/components/ui/ragflow-pagination'; -import { useSetModalState } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useFetchAgentListByPage } from '@/hooks/use-agent-request'; import { t } from 'i18next'; @@ -17,6 +16,9 @@ import { Clipboard, ClipboardPlus, FileInput, Plus } from 'lucide-react'; import { useCallback } from 'react'; import { AgentCard } from './agent-card'; import { CreateAgentDialog } from './create-agent-dialog'; +import { useCreateAgentOrPipeline } from './hooks/use-create-agent'; +import { UploadAgentDialog } from './upload-agent-dialog'; +import { useHandleImportJsonFile } from './use-import-json'; import { useRenameAgent } from './use-rename-agent'; export default function Agents() { @@ -34,10 +36,19 @@ export default function Agents() { } = useRenameAgent(); const { - visible: creatingVisible, - hideModal: hideCreatingModal, - showModal: showCreatingModal, - } = useSetModalState(); + creatingVisible, + hideCreatingModal, + showCreatingModal, + loading, + handleCreateAgentOrPipeline, + } = useCreateAgentOrPipeline(); + + const { + handleImportJson, + fileUploadVisible, + onFileUploadOk, + hideFileUploadModal, + } = useHandleImportJsonFile(); const handlePageChange = useCallback( (page: number, pageSize?: number) => { @@ -77,7 +88,10 @@ export default function Agents() { Create from Template - + Import json file @@ -115,13 +129,19 @@ export default function Agents() { )} {creatingVisible && ( {}} + onOk={handleCreateAgentOrPipeline} > )} + {fileUploadVisible && ( + + )} ); } diff --git a/web/src/pages/agents/name-form-field.tsx b/web/src/pages/agents/name-form-field.tsx new file mode 100644 index 00000000000..f18e46924a9 --- /dev/null +++ b/web/src/pages/agents/name-form-field.tsx @@ -0,0 +1,28 @@ +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Input } from '@/components/ui/input'; +import i18n from '@/locales/config'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +export const NameFormSchema = { + name: z + .string() + .min(1, { + message: i18n.t('common.namePlaceholder'), + }) + .trim(), +}; + +export function NameFormField() { + const { t } = useTranslation(); + return ( + + + + ); +} diff --git a/web/src/pages/agents/template-card.tsx b/web/src/pages/agents/template-card.tsx index 3a59e40c8a7..76b02ee3c5e 100644 --- a/web/src/pages/agents/template-card.tsx +++ b/web/src/pages/agents/template-card.tsx @@ -3,7 +3,6 @@ import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { IFlowTemplate } from '@/interfaces/database/flow'; import i18n from '@/locales/config'; -import { Plus } from 'lucide-react'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; interface IProps { @@ -12,7 +11,7 @@ interface IProps { showModal(record: IFlowTemplate): void; } -export function TemplateCard({ data, showModal, isCreate = false }: IProps) { +export function TemplateCard({ data, showModal }: IProps) { const { t } = useTranslation(); const handleClick = useCallback(() => { @@ -26,41 +25,24 @@ export function TemplateCard({ data, showModal, isCreate = false }: IProps) { return ( - {isCreate && ( -
    + +
    {data?.title[language]}
    +
    +

    {data?.description[language]}

    +
    +
    - )} - {!isCreate && ( - <> -
    - -
    - {data?.title[language]} -
    -
    -

    {data?.description[language]}

    -
    - -
    - - )} + {t('flow.useTemplate')} + +
    ); diff --git a/web/src/pages/agent/upload-agent-dialog/index.tsx b/web/src/pages/agents/upload-agent-dialog/index.tsx similarity index 85% rename from web/src/pages/agent/upload-agent-dialog/index.tsx rename to web/src/pages/agents/upload-agent-dialog/index.tsx index 45f9ec88222..b2084bc0f9c 100644 --- a/web/src/pages/agent/upload-agent-dialog/index.tsx +++ b/web/src/pages/agents/upload-agent-dialog/index.tsx @@ -1,3 +1,4 @@ +import { ButtonLoading } from '@/components/ui/button'; import { Dialog, DialogContent, @@ -5,7 +6,6 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; -import { LoadingButton } from '@/components/ui/loading-button'; import { IModalProps } from '@/interfaces/common'; import { TagRenameId } from '@/pages/add-knowledge/constant'; import { useTranslation } from 'react-i18next'; @@ -26,9 +26,9 @@ export function UploadAgentDialog({ - + {t('common.save')} - + diff --git a/web/src/pages/agent/upload-agent-dialog/upload-agent-form.tsx b/web/src/pages/agents/upload-agent-dialog/upload-agent-form.tsx similarity index 56% rename from web/src/pages/agent/upload-agent-dialog/upload-agent-form.tsx rename to web/src/pages/agents/upload-agent-dialog/upload-agent-form.tsx index 39a4ac01250..df2d21c1520 100644 --- a/web/src/pages/agent/upload-agent-dialog/upload-agent-form.tsx +++ b/web/src/pages/agents/upload-agent-dialog/upload-agent-form.tsx @@ -13,32 +13,24 @@ import { FormLabel, FormMessage, } from '@/components/ui/form'; -import { FileMimeType, Platform } from '@/constants/common'; +import { FileMimeType } from '@/constants/common'; import { IModalProps } from '@/interfaces/common'; import { TagRenameId } from '@/pages/add-knowledge/constant'; -import { useTranslation } from 'react-i18next'; +import { NameFormField, NameFormSchema } from '../name-form-field'; -// const options = Object.values(Platform).map((x) => ({ label: x, value: x })); +export const FormSchema = z.object({ + fileList: z.array(z.instanceof(File)), + ...NameFormSchema, +}); +export type FormSchemaType = z.infer; export function UploadAgentForm({ hideModal, onOk }: IModalProps) { - const { t } = useTranslation(); - const FormSchema = z.object({ - platform: z - .string() - .min(1, { - message: t('common.namePlaceholder'), - }) - .trim(), - fileList: z.array(z.instanceof(File)), - }); - const form = useForm>({ resolver: zodResolver(FormSchema), - defaultValues: { platform: Platform.RAGFlow }, + defaultValues: { name: '' }, }); - async function onSubmit(data: z.infer) { - console.log('🚀 ~ onSubmit ~ data:', data); + async function onSubmit(data: FormSchemaType) { const ret = await onOk?.(data); if (ret) { hideModal?.(); @@ -52,12 +44,13 @@ export function UploadAgentForm({ hideModal, onOk }: IModalProps) { className="space-y-6" id={TagRenameId} > + ( - {t('common.name')} + DSL ) { )} /> - {/* ( - - {t('common.name')} - - - - - - )} - /> */} ); diff --git a/web/src/pages/agents/use-import-json.ts b/web/src/pages/agents/use-import-json.ts new file mode 100644 index 00000000000..54c0f89a39f --- /dev/null +++ b/web/src/pages/agents/use-import-json.ts @@ -0,0 +1,56 @@ +import { useToast } from '@/components/hooks/use-toast'; +import { FileMimeType } from '@/constants/common'; +import { useSetModalState } from '@/hooks/common-hooks'; +import { EmptyDsl, useSetAgent } from '@/hooks/use-agent-request'; +import { message } from 'antd'; +import isEmpty from 'lodash/isEmpty'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FormSchemaType } from './upload-agent-dialog/upload-agent-form'; + +export const useHandleImportJsonFile = () => { + const { + visible: fileUploadVisible, + hideModal: hideFileUploadModal, + showModal: showFileUploadModal, + } = useSetModalState(); + const { t } = useTranslation(); + const { toast } = useToast(); + const { loading, setAgent } = useSetAgent(); + + const onFileUploadOk = useCallback( + async ({ fileList, name }: FormSchemaType) => { + if (fileList.length > 0) { + const file = fileList[0]; + if (file.type !== FileMimeType.Json) { + toast({ title: t('flow.jsonUploadTypeErrorMessage') }); + return; + } + + const graphStr = await file.text(); + const errorMessage = t('flow.jsonUploadContentErrorMessage'); + try { + const graph = JSON.parse(graphStr); + if (graphStr && !isEmpty(graph) && Array.isArray(graph?.nodes)) { + const dsl = { ...EmptyDsl, graph }; + setAgent({ title: name, dsl }); + hideFileUploadModal(); + } else { + message.error(errorMessage); + } + } catch (error) { + message.error(errorMessage); + } + } + }, + [hideFileUploadModal, setAgent, t, toast], + ); + + return { + fileUploadVisible, + handleImportJson: showFileUploadModal, + hideFileUploadModal, + onFileUploadOk, + loading, + }; +}; From 8a09f07186a61026637df26dbf357c84ee6f0dcf Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Thu, 11 Sep 2025 09:51:18 +0800 Subject: [PATCH 0665/1187] feat: Added UI functions related to data-flow knowledge base #3221 (#10038) ### What problem does this PR solve? feat: Added UI functions related to data-flow knowledge base #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/package-lock.json | 8 +- web/package.json | 2 +- .../assets/svg/data-flow/knowledgegraph.svg | 1 + web/src/assets/svg/data-flow/raptor.svg | 1 + web/src/assets/svg/rerun.svg | 1 + web/src/components/file-status-badge.tsx | 57 +++ web/src/components/originui/timeline.tsx | 203 +++++++++ .../next-search => components}/spotlight.tsx | 20 +- web/src/hooks/logic-hooks/navigate-hooks.ts | 11 + web/src/locales/en.ts | 39 ++ web/src/locales/zh.ts | 35 ++ web/src/pages/agent/canvas/index.tsx | 2 + web/src/pages/dataflow-result/chunker.tsx | 234 +++++++++++ .../components/chunk-card/index.less | 36 ++ .../components/chunk-card/index.tsx | 127 ++++++ .../components/chunk-creating-modal/index.tsx | 206 ++++++++++ .../chunk-creating-modal/tag-feature-item.tsx | 136 +++++++ .../chunk-result-bar/checkbox-sets.tsx | 85 ++++ .../components/chunk-result-bar/index.tsx | 108 +++++ .../components/chunk-toolbar/index.tsx | 221 ++++++++++ .../document-preview/csv-preview.tsx | 114 ++++++ .../document-preview/doc-preview.tsx | 70 ++++ .../document-preview/document-header.tsx | 21 + .../document-preview/excel-preview.tsx | 25 ++ .../components/document-preview/hooks.ts | 55 +++ .../document-preview/image-preview.tsx | 73 ++++ .../components/document-preview/index.less | 13 + .../components/document-preview/index.tsx | 68 ++++ .../document-preview/pdf-preview.tsx | 127 ++++++ .../document-preview/ppt-preview.tsx | 70 ++++ .../document-preview/txt-preview.tsx | 56 +++ .../components/parse-editer/index.tsx | 48 +++ .../components/rerun-button/index.tsx | 29 ++ .../components/time-line/index.tsx | 82 ++++ web/src/pages/dataflow-result/constant.ts | 4 + web/src/pages/dataflow-result/hooks.ts | 185 +++++++++ web/src/pages/dataflow-result/index.less | 96 +++++ web/src/pages/dataflow-result/index.tsx | 121 ++++++ web/src/pages/dataflow-result/parser.tsx | 58 +++ web/src/pages/dataflow-result/utils.ts | 24 ++ .../dataset-overview/dataset-common.ts | 9 + .../dataset-overview/dataset-filter.tsx | 91 +++++ .../pages/dataset/dataset-overview/index.tsx | 156 +++++++ .../dataset-overview/overview-table.tsx | 384 ++++++++++++++++++ .../dataset-setting/chunk-method-form.tsx | 31 ++ .../dataset-setting/components/tag-item.tsx | 155 +++++++ .../configuration-form-container.tsx | 14 + .../configuration/common-item.tsx | 328 +++++++++++++++ .../dataset/dataset-setting/form-schema.ts | 73 ++++ .../dataset/dataset-setting/general-form.tsx | 92 +++++ .../pages/dataset/dataset-setting/hooks.ts | 87 ++++ .../pages/dataset/dataset-setting/index.tsx | 88 ++++ .../pages/dataset/dataset-setting/naive.tsx | 33 ++ .../dataset-setting/permission-form-field.tsx | 29 ++ .../dataset/dataset-setting/saving-button.tsx | 82 ++++ web/src/pages/dataset/dataset/generate.tsx | 68 ++++ web/src/pages/dataset/dataset/index.tsx | 132 +++--- .../setting/configuration/common-item.tsx | 197 ++++++++- web/src/pages/dataset/sidebar/index.tsx | 13 +- .../datasets/dataset-creating-dialog.tsx | 30 +- web/src/pages/datasets/process-log-modal.tsx | 116 ++++++ web/src/pages/next-search/search-home.tsx | 2 +- web/src/routes.ts | 16 + web/src/utils/common-util.ts | 62 +++ 64 files changed, 5079 insertions(+), 81 deletions(-) create mode 100644 web/src/assets/svg/data-flow/knowledgegraph.svg create mode 100644 web/src/assets/svg/data-flow/raptor.svg create mode 100644 web/src/assets/svg/rerun.svg create mode 100644 web/src/components/file-status-badge.tsx rename web/src/{pages/next-search => components}/spotlight.tsx (56%) create mode 100644 web/src/pages/dataflow-result/chunker.tsx create mode 100644 web/src/pages/dataflow-result/components/chunk-card/index.less create mode 100644 web/src/pages/dataflow-result/components/chunk-card/index.tsx create mode 100644 web/src/pages/dataflow-result/components/chunk-creating-modal/index.tsx create mode 100644 web/src/pages/dataflow-result/components/chunk-creating-modal/tag-feature-item.tsx create mode 100644 web/src/pages/dataflow-result/components/chunk-result-bar/checkbox-sets.tsx create mode 100644 web/src/pages/dataflow-result/components/chunk-result-bar/index.tsx create mode 100644 web/src/pages/dataflow-result/components/chunk-toolbar/index.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/csv-preview.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/doc-preview.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/document-header.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/excel-preview.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/hooks.ts create mode 100644 web/src/pages/dataflow-result/components/document-preview/image-preview.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/index.less create mode 100644 web/src/pages/dataflow-result/components/document-preview/index.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/pdf-preview.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/ppt-preview.tsx create mode 100644 web/src/pages/dataflow-result/components/document-preview/txt-preview.tsx create mode 100644 web/src/pages/dataflow-result/components/parse-editer/index.tsx create mode 100644 web/src/pages/dataflow-result/components/rerun-button/index.tsx create mode 100644 web/src/pages/dataflow-result/components/time-line/index.tsx create mode 100644 web/src/pages/dataflow-result/constant.ts create mode 100644 web/src/pages/dataflow-result/hooks.ts create mode 100644 web/src/pages/dataflow-result/index.less create mode 100644 web/src/pages/dataflow-result/index.tsx create mode 100644 web/src/pages/dataflow-result/parser.tsx create mode 100644 web/src/pages/dataflow-result/utils.ts create mode 100644 web/src/pages/dataset/dataset-overview/dataset-common.ts create mode 100644 web/src/pages/dataset/dataset-overview/dataset-filter.tsx create mode 100644 web/src/pages/dataset/dataset-overview/index.tsx create mode 100644 web/src/pages/dataset/dataset-overview/overview-table.tsx create mode 100644 web/src/pages/dataset/dataset-setting/chunk-method-form.tsx create mode 100644 web/src/pages/dataset/dataset-setting/components/tag-item.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration-form-container.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/common-item.tsx create mode 100644 web/src/pages/dataset/dataset-setting/form-schema.ts create mode 100644 web/src/pages/dataset/dataset-setting/general-form.tsx create mode 100644 web/src/pages/dataset/dataset-setting/hooks.ts create mode 100644 web/src/pages/dataset/dataset-setting/index.tsx create mode 100644 web/src/pages/dataset/dataset-setting/naive.tsx create mode 100644 web/src/pages/dataset/dataset-setting/permission-form-field.tsx create mode 100644 web/src/pages/dataset/dataset-setting/saving-button.tsx create mode 100644 web/src/pages/dataset/dataset/generate.tsx create mode 100644 web/src/pages/datasets/process-log-modal.tsx diff --git a/web/package-lock.json b/web/package-lock.json index 1579cf24016..e1b4f458144 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -66,7 +66,7 @@ "jsencrypt": "^3.3.2", "lexical": "^0.23.1", "lodash": "^4.17.21", - "lucide-react": "^0.508.0", + "lucide-react": "^0.542.0", "mammoth": "^1.7.2", "next-themes": "^0.4.6", "openai-speech-stream-player": "^1.0.8", @@ -25113,9 +25113,9 @@ } }, "node_modules/lucide-react": { - "version": "0.508.0", - "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.508.0.tgz", - "integrity": "sha512-gcP16PnexqtOFrTtv98kVsGzTfnbPekzZiQfByi2S89xfk7E/4uKE1USZqccIp58v42LqkO7MuwpCqshwSrJCg==", + "version": "0.542.0", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.542.0.tgz", + "integrity": "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" diff --git a/web/package.json b/web/package.json index 2e7a9c0f715..783ffff66d8 100644 --- a/web/package.json +++ b/web/package.json @@ -79,7 +79,7 @@ "jsencrypt": "^3.3.2", "lexical": "^0.23.1", "lodash": "^4.17.21", - "lucide-react": "^0.508.0", + "lucide-react": "^0.542.0", "mammoth": "^1.7.2", "next-themes": "^0.4.6", "openai-speech-stream-player": "^1.0.8", diff --git a/web/src/assets/svg/data-flow/knowledgegraph.svg b/web/src/assets/svg/data-flow/knowledgegraph.svg new file mode 100644 index 00000000000..b0feec92493 --- /dev/null +++ b/web/src/assets/svg/data-flow/knowledgegraph.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/svg/data-flow/raptor.svg b/web/src/assets/svg/data-flow/raptor.svg new file mode 100644 index 00000000000..8f83bffc24a --- /dev/null +++ b/web/src/assets/svg/data-flow/raptor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/svg/rerun.svg b/web/src/assets/svg/rerun.svg new file mode 100644 index 00000000000..cd972f4e6cd --- /dev/null +++ b/web/src/assets/svg/rerun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/components/file-status-badge.tsx b/web/src/components/file-status-badge.tsx new file mode 100644 index 00000000000..ad681eb34e0 --- /dev/null +++ b/web/src/components/file-status-badge.tsx @@ -0,0 +1,57 @@ +// src/pages/dataset/file-logs/file-status-badge.tsx +import { FC } from 'react'; + +interface StatusBadgeProps { + status: 'Success' | 'Failed' | 'Running' | 'Pending'; +} + +const FileStatusBadge: FC = ({ status }) => { + const getStatusColor = () => { + // #3ba05c → rgb(59, 160, 92) // state-success + // #d8494b → rgb(216, 73, 75) // state-error + // #00beb4 → rgb(0, 190, 180) // accent-primary + // #faad14 → rgb(250, 173, 20) // state-warning + switch (status) { + case 'Success': + return `bg-[rgba(59,160,92,0.1)] text-state-success`; + case 'Failed': + return `bg-[rgba(216,73,75,0.1)] text-state-error`; + case 'Running': + return `bg-[rgba(0,190,180,0.1)] text-accent-primary`; + case 'Pending': + return `bg-[rgba(250,173,20,0.1)] text-state-warning`; + default: + return 'bg-gray-500/10 text-white'; + } + }; + + const getBgStatusColor = () => { + // #3ba05c → rgb(59, 160, 92) // state-success + // #d8494b → rgb(216, 73, 75) // state-error + // #00beb4 → rgb(0, 190, 180) // accent-primary + // #faad14 → rgb(250, 173, 20) // state-warning + switch (status) { + case 'Success': + return `bg-[rgba(59,160,92,1)] text-state-success`; + case 'Failed': + return `bg-[rgba(216,73,75,1)] text-state-error`; + case 'Running': + return `bg-[rgba(0,190,180,1)] text-accent-primary`; + case 'Pending': + return `bg-[rgba(250,173,20,1)] text-state-warning`; + default: + return 'bg-gray-500/10 text-white'; + } + }; + + return ( + +
    + {status} +
    + ); +}; + +export default FileStatusBadge; diff --git a/web/src/components/originui/timeline.tsx b/web/src/components/originui/timeline.tsx index befe5272ec9..57694ee3b94 100644 --- a/web/src/components/originui/timeline.tsx +++ b/web/src/components/originui/timeline.tsx @@ -1,6 +1,7 @@ 'use client'; import { cn } from '@/lib/utils'; +import { parseColorToRGBA } from '@/utils/common-util'; import { Slot } from '@radix-ui/react-slot'; import * as React from 'react'; @@ -197,7 +198,208 @@ function TimelineTitle({ ); } +interface TimelineIndicatorNodeProps { + nodeSize?: string | number; + iconColor?: string; + lineColor?: string; + textColor?: string; + indicatorBgColor?: string; + indicatorBorderColor?: string; +} +interface TimelineNode + extends Omit< + React.HTMLAttributes, + 'id' | 'title' | 'content' + >, + TimelineIndicatorNodeProps { + id: string | number; + title?: React.ReactNode; + content?: React.ReactNode; + date?: React.ReactNode; + icon?: React.ReactNode; + completed?: boolean; + clickable?: boolean; + activeStyle?: TimelineIndicatorNodeProps; +} + +interface CustomTimelineProps extends React.HTMLAttributes { + nodes: TimelineNode[]; + activeStep?: number; + nodeSize?: string | number; + onStepChange?: (step: number, id: string | number) => void; + orientation?: 'horizontal' | 'vertical'; + lineStyle?: 'solid' | 'dashed'; + lineColor?: string; + indicatorColor?: string; + defaultValue?: number; + activeStyle?: TimelineIndicatorNodeProps; +} + +const CustomTimeline = ({ + nodes, + activeStep, + nodeSize = 12, + onStepChange, + orientation = 'horizontal', + lineStyle = 'solid', + lineColor = 'var(--text-secondary)', + indicatorColor = 'var(--accent-primary)', + defaultValue = 1, + className, + activeStyle, + ...props +}: CustomTimelineProps) => { + const [internalActiveStep, setInternalActiveStep] = + React.useState(defaultValue); + const _lineColor = `rgb(${parseColorToRGBA(lineColor)})`; + console.log(lineColor, _lineColor); + const currentActiveStep = activeStep ?? internalActiveStep; + + const handleStepChange = (step: number, id: string | number) => { + if (activeStep === undefined) { + setInternalActiveStep(step); + } + onStepChange?.(step, id); + }; + const [r, g, b] = parseColorToRGBA(indicatorColor); + return ( + handleStepChange(step, nodes[step - 1]?.id)} + orientation={orientation} + className={className} + {...props} + > + {nodes.map((node, index) => { + const step = index + 1; + const isCompleted = node.completed ?? step <= currentActiveStep; + const isActive = step === currentActiveStep; + const isClickable = node.clickable ?? true; + const _activeStyle = node.activeStyle ?? (activeStyle || {}); + const _nodeSizeTemp = + isActive && _activeStyle?.nodeSize + ? _activeStyle?.nodeSize + : node.nodeSize ?? nodeSize; + const _nodeSize = + typeof _nodeSizeTemp === 'number' + ? `${_nodeSizeTemp}px` + : _nodeSizeTemp; + console.log('icon-size', nodeSize, node.nodeSize, _nodeSize); + // const activeStyle = _activeStyle || {}; + + return ( + isClickable && handleStepChange(step, node.id)} + > + + + + {node.icon && ( +
    + {node.icon} +
    + )} +
    + + + {node.date && {node.date}} + + {node.title} + + + {node.content && {node.content}} +
    + ); + })} +
    + ); +}; + +CustomTimeline.displayName = 'CustomTimeline'; + export { + CustomTimeline, Timeline, TimelineContent, TimelineDate, @@ -206,4 +408,5 @@ export { TimelineItem, TimelineSeparator, TimelineTitle, + type TimelineNode, }; diff --git a/web/src/pages/next-search/spotlight.tsx b/web/src/components/spotlight.tsx similarity index 56% rename from web/src/pages/next-search/spotlight.tsx rename to web/src/components/spotlight.tsx index 3d9b31f4ae6..f5eacbceec6 100644 --- a/web/src/pages/next-search/spotlight.tsx +++ b/web/src/components/spotlight.tsx @@ -3,10 +3,22 @@ import React from 'react'; interface SpotlightProps { className?: string; + opcity?: number; + coverage?: number; } - -const Spotlight: React.FC = ({ className }) => { +/** + * + * @param opcity 0~1 default 0.5 + * @param coverage 0~100 default 60 + * @returns + */ +const Spotlight: React.FC = ({ + className, + opcity = 0.5, + coverage = 60, +}) => { const isDark = useIsDarkTheme(); + const rgb = isDark ? '255, 255, 255' : '194, 221, 243'; return (
    = ({ className }) => {
    diff --git a/web/src/hooks/logic-hooks/navigate-hooks.ts b/web/src/hooks/logic-hooks/navigate-hooks.ts index b31e9fb5f4e..b403b37ecba 100644 --- a/web/src/hooks/logic-hooks/navigate-hooks.ts +++ b/web/src/hooks/logic-hooks/navigate-hooks.ts @@ -125,6 +125,16 @@ export const useNavigatePage = () => { [navigate], ); + const navigateToDataflowResult = useCallback( + (id: string, knowledgeId?: string) => () => { + navigate( + // `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`, + `${Routes.DataflowResult}/${id}`, + ); + }, + [navigate], + ); + return { navigateToDatasetList, navigateToDataset, @@ -144,5 +154,6 @@ export const useNavigatePage = () => { navigateToFiles, navigateToAgentList, navigateToOldProfile, + navigateToDataflowResult, }; }; diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index c42e2b693d9..997eb1b762e 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -102,6 +102,28 @@ export default { noMoreData: `That's all. Nothing more.`, }, knowledgeDetails: { + generateKnowledgeGraph: + 'This will extract entities and relationships from all your documents in this dataset. The process may take a while to complete.', + generateRaptor: + 'This will extract entities and relationships from all your documents in this dataset. The process may take a while to complete.', + generate: 'Generate', + raptor: 'Raptor', + knowledgeGraph: 'Knowledge Graph', + processingType: 'Processing Type', + dataPipeline: 'Data Pipeline', + operations: 'Operations', + status: 'Status', + task: 'Task', + startDate: 'Start Date', + source: 'Source', + fileName: 'File Name', + datasetLogs: 'Dataset Logs', + fileLogs: 'File Logs', + overview: 'Overview', + success: 'Success', + failed: 'Failed', + completed: 'Completed', + processLog: 'Process Log', created: 'Created', learnMore: 'Learn More', general: 'General', @@ -195,6 +217,7 @@ export default { chunk: 'Chunk', bulk: 'Bulk', cancel: 'Cancel', + close: 'Close', rerankModel: 'Rerank model', rerankPlaceholder: 'Please select', rerankTip: `Optional. If left empty, RAGFlow will use a combination of weighted keyword similarity and weighted vector cosine similarity; if a rerank model is selected, a weighted reranking score will replace the weighted vector cosine similarity. Please be aware that using a rerank model will significantly increase the system's response time. If you wish to use a rerank model, ensure you use a SaaS reranker; if you prefer a locally deployed rerank model, ensure you start RAGFlow with docker-compose-gpu.yml.`, @@ -238,6 +261,16 @@ export default { reRankModelWaring: 'Re-rank model is very time consuming.', }, knowledgeConfiguration: { + enableAutoGenerate: 'Enable Auto Generate', + teamPlaceholder: 'Please select a team.', + dataFlowPlaceholder: 'Please select a data flow.', + buildItFromScratch: 'Build it from scratch', + useRAPTORToEnhanceRetrieval: 'Use RAPTOR to Enhance Retrieval', + extractKnowledgeGraph: 'Extract Knowledge Graph', + dataFlow: 'Data Flow', + parseType: 'Parse Type', + manualSetup: 'Manual Setup', + builtIn: 'Built-in', titleDescription: 'Update your knowledge base configuration here, particularly the chunking method.', name: 'Knowledge base name', @@ -1589,5 +1622,11 @@ This delimiter is used to split the input text into several text pieces echo of total: 'Total {{total}}', page: '{{page}} /Page', }, + dataflowParser: { + parseSummary: 'Parse Summary', + parseSummaryTip: 'Parser:deepdoc', + rerunFromCurrentStep: 'Rerun From Current Step', + rerunFromCurrentStepTip: 'Changes detected. Click to re-run.', + }, }, }; diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 1f8d9d9ee5e..403ff6b456f 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -94,6 +94,24 @@ export default { noMoreData: '没有更多数据了', }, knowledgeDetails: { + generate: '生成', + raptor: 'Raptor', + knowledgeGraph: '知识图谱', + processingType: '处理类型', + dataPipeline: '数据管道', + operations: '操作', + status: '状态', + task: '任务', + startDate: '开始时间', + source: '来源', + fileName: '文件名', + datasetLogs: '数据集日志', + fileLogs: '文件日志', + overview: '概览', + success: '成功', + failed: '失败', + completed: '已完成', + processLog: '处理进度日志', created: '创建于', learnMore: '了解更多', general: '通用', @@ -183,6 +201,7 @@ export default { chunk: '解析块', bulk: '批量', cancel: '取消', + close: '关闭', rerankModel: 'Rerank模型', rerankPlaceholder: '请选择', rerankTip: `非必选项:若不选择 rerank 模型,系统将默认采用关键词相似度与向量余弦相似度相结合的混合查询方式;如果设置了 rerank 模型,则混合查询中的向量相似度部分将被 rerank 打分替代。请注意:采用 rerank 模型会非常耗时。如需选用 rerank 模型,建议使用 SaaS 的 rerank 模型服务;如果你倾向使用本地部署的 rerank 模型,请务必确保你使用 docker-compose-gpu.yml 启动 RAGFlow。`, @@ -227,6 +246,16 @@ export default { theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除', }, knowledgeConfiguration: { + enableAutoGenerate: '是否启用自动生成', + teamPlaceholder: '请选择团队', + dataFlowPlaceholder: '请选择数据流', + buildItFromScratch: '去Scratch构建', + useRAPTORToEnhanceRetrieval: '使用 RAPTOR 提升检索效果', + extractKnowledgeGraph: '知识图谱提取', + dataFlow: '数据流', + parseType: '切片方法', + manualSetup: '手动设置', + builtIn: '内置', titleDescription: '在这里更新您的知识库详细信息,尤其是切片方法。', name: '知识库名称', photo: '知识库图片', @@ -1501,5 +1530,11 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 total: '总共 {{total}} 条', page: '{{page}}条/页', }, + dataflowParser: { + parseSummary: '解析摘要', + parseSummaryTip: '解析器: deepdoc', + rerunFromCurrentStep: '从当前步骤重新运行', + rerunFromCurrentStepTip: '已修改,点击重新运行。', + }, }, }; diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index b7c929a52d6..74e6d146a7b 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -40,6 +40,7 @@ import { useCacheChatLog } from '../hooks/use-cache-chat-log'; import { useMoveNote } from '../hooks/use-move-note'; import { useDropdownManager } from './context'; +import Spotlight from '@/components/spotlight'; import { useHideFormSheetOnNodeDeletion, useShowDrawer, @@ -309,6 +310,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { onBeforeDelete={handleBeforeDelete} > + diff --git a/web/src/pages/dataflow-result/chunker.tsx b/web/src/pages/dataflow-result/chunker.tsx new file mode 100644 index 00000000000..ff869809de6 --- /dev/null +++ b/web/src/pages/dataflow-result/chunker.tsx @@ -0,0 +1,234 @@ +import message from '@/components/ui/message'; +import { + RAGFlowPagination, + RAGFlowPaginationType, +} from '@/components/ui/ragflow-pagination'; +import { Spin } from '@/components/ui/spin'; +import { + useFetchNextChunkList, + useSwitchChunk, +} from '@/hooks/use-chunk-request'; +import classNames from 'classnames'; +import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import ChunkCard from './components/chunk-card'; +import CreatingModal from './components/chunk-creating-modal'; +import ChunkResultBar from './components/chunk-result-bar'; +import CheckboxSets from './components/chunk-result-bar/checkbox-sets'; +import RerunButton from './components/rerun-button'; +import { + useChangeChunkTextMode, + useDeleteChunkByIds, + useHandleChunkCardClick, + useUpdateChunk, +} from './hooks'; +import styles from './index.less'; +const ChunkerContainer = () => { + const [selectedChunkIds, setSelectedChunkIds] = useState([]); + const [isChange, setIsChange] = useState(false); + const { t } = useTranslation(); + const { + data: { documentInfo, data = [], total }, + pagination, + loading, + searchString, + handleInputChange, + available, + handleSetAvailable, + } = useFetchNextChunkList(); + const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick(); + const isPdf = documentInfo?.type === 'pdf'; + const { + chunkUpdatingLoading, + onChunkUpdatingOk, + showChunkUpdatingModal, + hideChunkUpdatingModal, + chunkId, + chunkUpdatingVisible, + documentId, + } = useUpdateChunk(); + const { removeChunk } = useDeleteChunkByIds(); + const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); + const selectAllChunk = useCallback( + (checked: boolean) => { + setSelectedChunkIds(checked ? data.map((x) => x.chunk_id) : []); + }, + [data], + ); + const showSelectedChunkWarning = useCallback(() => { + message.warning(t('message.pleaseSelectChunk')); + }, [t]); + const { switchChunk } = useSwitchChunk(); + + const [chunkList, setChunkList] = useState(data); + useEffect(() => { + setChunkList(data); + }, [data]); + const onPaginationChange: RAGFlowPaginationType['onChange'] = ( + page, + size, + ) => { + setSelectedChunkIds([]); + pagination.onChange?.(page, size); + }; + + const handleSwitchChunk = useCallback( + async (available?: number, chunkIds?: string[]) => { + let ids = chunkIds; + if (!chunkIds) { + ids = selectedChunkIds; + if (selectedChunkIds.length === 0) { + showSelectedChunkWarning(); + return; + } + } + + const resCode: number = await switchChunk({ + chunk_ids: ids, + available_int: available, + doc_id: documentId, + }); + if (ids?.length && resCode === 0) { + chunkList.forEach((x: any) => { + if (ids.indexOf(x['chunk_id']) > -1) { + x['available_int'] = available; + } + }); + setChunkList(chunkList); + } + }, + [ + switchChunk, + documentId, + selectedChunkIds, + showSelectedChunkWarning, + chunkList, + ], + ); + const handleSingleCheckboxClick = useCallback( + (chunkId: string, checked: boolean) => { + setSelectedChunkIds((previousIds) => { + const idx = previousIds.findIndex((x) => x === chunkId); + const nextIds = [...previousIds]; + if (checked && idx === -1) { + nextIds.push(chunkId); + } else if (!checked && idx !== -1) { + nextIds.splice(idx, 1); + } + return nextIds; + }); + }, + [], + ); + const handleRemoveChunk = useCallback(async () => { + if (selectedChunkIds.length > 0) { + const resCode: number = await removeChunk(selectedChunkIds, documentId); + if (resCode === 0) { + setSelectedChunkIds([]); + } + } else { + showSelectedChunkWarning(); + } + }, [selectedChunkIds, documentId, removeChunk, showSelectedChunkWarning]); + + const handleChunkEditSave = (e: any) => { + setIsChange(true); + onChunkUpdatingOk(e); + }; + return ( + <> + {isChange && ( +
    + +
    + )} +
    + +
    +
    +

    {t('chunk.chunkResult')}

    +
    + {t('chunk.chunkResultTip')} +
    +
    + +
    +
    +
    + +
    +
    +
    + {chunkList.map((item) => ( + x === item.chunk_id)} + handleCheckboxClick={handleSingleCheckboxClick} + switchChunk={handleSwitchChunk} + clickChunkCard={handleChunkCardClick} + selected={item.chunk_id === selectedChunkId} + textMode={textMode} + > + ))} +
    +
    +
    + { + onPaginationChange(page, pageSize); + }} + > +
    +
    +
    +
    + {chunkUpdatingVisible && ( + { + handleChunkEditSave(e); + }} + parserId={documentInfo.parser_id} + /> + )} + + ); +}; + +export { ChunkerContainer }; diff --git a/web/src/pages/dataflow-result/components/chunk-card/index.less b/web/src/pages/dataflow-result/components/chunk-card/index.less new file mode 100644 index 00000000000..aac7724af4f --- /dev/null +++ b/web/src/pages/dataflow-result/components/chunk-card/index.less @@ -0,0 +1,36 @@ +.image { + width: 100px !important; + object-fit: contain; +} + +.imagePreview { + max-width: 50vw; + max-height: 50vh; + object-fit: contain; +} + +.content { + flex: 1; + .chunkText; +} + +.contentEllipsis { + .multipleLineEllipsis(3); +} + +.contentText { + word-break: break-all !important; +} + +.chunkCard { + width: 100%; + padding: 18px 10px; +} + +.cardSelected { + background-color: @selectedBackgroundColor; +} + +.cardSelectedDark { + background-color: #ffffff2f; +} diff --git a/web/src/pages/dataflow-result/components/chunk-card/index.tsx b/web/src/pages/dataflow-result/components/chunk-card/index.tsx new file mode 100644 index 00000000000..198746aefe3 --- /dev/null +++ b/web/src/pages/dataflow-result/components/chunk-card/index.tsx @@ -0,0 +1,127 @@ +import Image from '@/components/image'; +import { useTheme } from '@/components/theme-provider'; +import { Card } from '@/components/ui/card'; +import { Checkbox } from '@/components/ui/checkbox'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Switch } from '@/components/ui/switch'; +import { IChunk } from '@/interfaces/database/knowledge'; +import { CheckedState } from '@radix-ui/react-checkbox'; +import classNames from 'classnames'; +import DOMPurify from 'dompurify'; +import { useEffect, useState } from 'react'; +import { ChunkTextMode } from '../../constant'; +import styles from './index.less'; + +interface IProps { + item: IChunk; + checked: boolean; + switchChunk: (available?: number, chunkIds?: string[]) => void; + editChunk: (chunkId: string) => void; + handleCheckboxClick: (chunkId: string, checked: boolean) => void; + selected: boolean; + clickChunkCard: (chunkId: string) => void; + textMode: ChunkTextMode; +} + +const ChunkCard = ({ + item, + checked, + handleCheckboxClick, + editChunk, + switchChunk, + selected, + clickChunkCard, + textMode, +}: IProps) => { + const available = Number(item.available_int); + const [enabled, setEnabled] = useState(false); + const { theme } = useTheme(); + + const onChange = (checked: boolean) => { + setEnabled(checked); + switchChunk(available === 0 ? 1 : 0, [item.chunk_id]); + }; + + const handleCheck = (e: CheckedState) => { + handleCheckboxClick(item.chunk_id, e === 'indeterminate' ? false : e); + }; + + const handleContentDoubleClick = () => { + editChunk(item.chunk_id); + }; + + const handleContentClick = () => { + clickChunkCard(item.chunk_id); + }; + + useEffect(() => { + setEnabled(available === 1); + }, [available]); + const [open, setOpen] = useState(false); + return ( + +
    + + {item.image_id && ( + + setOpen(true)} + onMouseLeave={() => setOpen(false)} + > +
    + +
    +
    + +
    + +
    +
    +
    + )} +
    +
    +
    +
    + +
    +
    +
    + ); +}; + +export default ChunkCard; diff --git a/web/src/pages/dataflow-result/components/chunk-creating-modal/index.tsx b/web/src/pages/dataflow-result/components/chunk-creating-modal/index.tsx new file mode 100644 index 00000000000..66cf4d619c5 --- /dev/null +++ b/web/src/pages/dataflow-result/components/chunk-creating-modal/index.tsx @@ -0,0 +1,206 @@ +import EditTag from '@/components/edit-tag'; +import Divider from '@/components/ui/divider'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from '@/components/ui/hover-card'; +import { Modal } from '@/components/ui/modal/modal'; +import Space from '@/components/ui/space'; +import { Switch } from '@/components/ui/switch'; +import { Textarea } from '@/components/ui/textarea'; +import { useFetchChunk } from '@/hooks/chunk-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { Trash2 } from 'lucide-react'; +import React, { useCallback, useEffect, useState } from 'react'; +import { FieldValues, FormProvider, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useDeleteChunkByIds } from '../../hooks'; +import { + transformTagFeaturesArrayToObject, + transformTagFeaturesObjectToArray, +} from '../../utils'; +import { TagFeatureItem } from './tag-feature-item'; + +interface kFProps { + doc_id: string; + chunkId: string | undefined; + parserId: string; +} + +const ChunkCreatingModal: React.FC & kFProps> = ({ + doc_id, + chunkId, + hideModal, + onOk, + loading, + parserId, +}) => { + // const [form] = Form.useForm(); + // const form = useFormContext(); + const form = useForm({ + defaultValues: { + content_with_weight: '', + tag_kwd: [], + question_kwd: [], + important_kwd: [], + tag_feas: [], + }, + }); + const [checked, setChecked] = useState(false); + const { removeChunk } = useDeleteChunkByIds(); + const { data } = useFetchChunk(chunkId); + const { t } = useTranslation(); + + const isTagParser = parserId === 'tag'; + const onSubmit = useCallback( + (values: FieldValues) => { + onOk?.({ + ...values, + tag_feas: transformTagFeaturesArrayToObject(values.tag_feas), + available_int: checked ? 1 : 0, + }); + }, + [checked, onOk], + ); + + const handleOk = form.handleSubmit(onSubmit); + + const handleRemove = useCallback(() => { + if (chunkId) { + return removeChunk([chunkId], doc_id); + } + }, [chunkId, doc_id, removeChunk]); + + const handleCheck = useCallback(() => { + setChecked(!checked); + }, [checked]); + + useEffect(() => { + if (data?.code === 0) { + const { available_int, tag_feas } = data.data; + form.reset({ + ...data.data, + tag_feas: transformTagFeaturesObjectToArray(tag_feas), + }); + + setChecked(available_int !== 0); + } + }, [data, form, chunkId]); + + return ( + +
    +
    + ( + + {t('chunk.chunk')} + + - - - - )} - /> - )} - {/* Create a hidden field to make Form instance record this */} -
    } - /> - - {t('flow.input')} - -
    - } - rightContent={ - - } - > - - - {visible && ( - - )} - - - ); -} - -export default memo(BeginForm); diff --git a/web/src/pages/data-flow/form/begin-form/parameter-dialog.tsx b/web/src/pages/data-flow/form/begin-form/parameter-dialog.tsx deleted file mode 100644 index 3b907043795..00000000000 --- a/web/src/pages/data-flow/form/begin-form/parameter-dialog.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select'; -import { Switch } from '@/components/ui/switch'; -import { useTranslate } from '@/hooks/common-hooks'; -import { IModalProps } from '@/interfaces/common'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { isEmpty } from 'lodash'; -import { ChangeEvent, useEffect, useMemo } from 'react'; -import { useForm, useWatch } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; -import { BeginQueryType, BeginQueryTypeIconMap } from '../../constant'; -import { BeginQuery } from '../../interface'; -import { BeginDynamicOptions } from './begin-dynamic-options'; - -type ModalFormProps = { - initialValue: BeginQuery; - otherThanCurrentQuery: BeginQuery[]; - submit(values: any): void; -}; - -const FormId = 'BeginParameterForm'; - -function ParameterForm({ - initialValue, - otherThanCurrentQuery, - submit, -}: ModalFormProps) { - const { t } = useTranslate('flow'); - const FormSchema = z.object({ - type: z.string(), - key: z - .string() - .trim() - .min(1) - .refine( - (value) => - !value || !otherThanCurrentQuery.some((x) => x.key === value), - { message: 'The key cannot be repeated!' }, - ), - optional: z.boolean(), - name: z.string().trim().min(1), - options: z - .array(z.object({ value: z.string().or(z.boolean()).or(z.number()) })) - .optional(), - }); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - mode: 'onChange', - defaultValues: { - type: BeginQueryType.Line, - optional: false, - key: '', - name: '', - options: [], - }, - }); - - const options = useMemo(() => { - return Object.values(BeginQueryType).reduce( - (pre, cur) => { - const Icon = BeginQueryTypeIconMap[cur]; - - return [ - ...pre, - { - label: ( -
    - - {t(cur.toLowerCase())} -
    - ), - value: cur, - }, - ]; - }, - [], - ); - }, []); - - const type = useWatch({ - control: form.control, - name: 'type', - }); - - useEffect(() => { - if (!isEmpty(initialValue)) { - form.reset({ - ...initialValue, - options: initialValue.options?.map((x) => ({ value: x })), - }); - } - }, [form, initialValue]); - - function onSubmit(data: z.infer) { - const values = { ...data, options: data.options?.map((x) => x.value) }; - console.log('🚀 ~ onSubmit ~ values:', values); - - submit(values); - } - - const handleKeyChange = (e: ChangeEvent) => { - const name = form.getValues().name || ''; - form.setValue('key', e.target.value.trim()); - if (!name) { - form.setValue('name', e.target.value.trim()); - } - }; - return ( -
    - - ( - - {t('type')} - - - - - - )} - /> - ( - - {t('key')} - - - - - - )} - /> - ( - - {t('name')} - - - - - - )} - /> - ( - - {t('optional')} - - - - - - )} - /> - {type === BeginQueryType.Options && ( - - )} - - - ); -} - -export function ParameterDialog({ - initialValue, - hideModal, - otherThanCurrentQuery, - submit, -}: ModalFormProps & IModalProps) { - const { t } = useTranslation(); - - return ( - - - - {t('flow.variableSettings')} - - - - - - - - ); -} diff --git a/web/src/pages/data-flow/form/begin-form/query-table.tsx b/web/src/pages/data-flow/form/begin-form/query-table.tsx deleted file mode 100644 index 5701c49b1f8..00000000000 --- a/web/src/pages/data-flow/form/begin-form/query-table.tsx +++ /dev/null @@ -1,199 +0,0 @@ -'use client'; - -import { - ColumnDef, - ColumnFiltersState, - SortingState, - VisibilityState, - flexRender, - getCoreRowModel, - getFilteredRowModel, - getPaginationRowModel, - getSortedRowModel, - useReactTable, -} from '@tanstack/react-table'; -import { Pencil, Trash2 } from 'lucide-react'; -import * as React from 'react'; - -import { TableEmpty } from '@/components/table-skeleton'; -import { Button } from '@/components/ui/button'; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table'; -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/components/ui/tooltip'; -import { cn } from '@/lib/utils'; -import { useTranslation } from 'react-i18next'; -import { BeginQuery } from '../../interface'; - -interface IProps { - data: BeginQuery[]; - deleteRecord(index: number): void; - showModal(index: number, record: BeginQuery): void; -} - -export function QueryTable({ data = [], deleteRecord, showModal }: IProps) { - const { t } = useTranslation(); - - const [sorting, setSorting] = React.useState([]); - const [columnFilters, setColumnFilters] = React.useState( - [], - ); - const [columnVisibility, setColumnVisibility] = - React.useState({}); - - const columns: ColumnDef[] = [ - { - accessorKey: 'key', - header: t('flow.key'), - meta: { cellClassName: 'max-w-30' }, - cell: ({ row }) => { - const key: string = row.getValue('key'); - return ( - - -
    {key}
    -
    - -

    {key}

    -
    -
    - ); - }, - }, - { - accessorKey: 'name', - header: t('flow.name'), - meta: { cellClassName: 'max-w-30' }, - cell: ({ row }) => { - const name: string = row.getValue('name'); - return ( - - -
    {name}
    -
    - -

    {name}

    -
    -
    - ); - }, - }, - { - accessorKey: 'type', - header: t('flow.type'), - cell: ({ row }) => ( -
    - {t(`flow.${(row.getValue('type')?.toString() || '').toLowerCase()}`)} -
    - ), - }, - { - accessorKey: 'optional', - header: t('flow.optional'), - cell: ({ row }) =>
    {row.getValue('optional') ? 'Yes' : 'No'}
    , - }, - { - id: 'actions', - enableHiding: false, - header: t('common.action'), - cell: ({ row }) => { - const record = row.original; - const idx = row.index; - - return ( -
    - - -
    - ); - }, - }, - ]; - - const table = useReactTable({ - data, - columns, - onSortingChange: setSorting, - onColumnFiltersChange: setColumnFilters, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), - onColumnVisibilityChange: setColumnVisibility, - state: { - sorting, - columnFilters, - columnVisibility, - }, - }); - - return ( -
    -
    -
    {sheetname}
    - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - )) - ) : ( - - )} - -
    -
    - - ); -} diff --git a/web/src/pages/data-flow/form/begin-form/use-edit-query.ts b/web/src/pages/data-flow/form/begin-form/use-edit-query.ts deleted file mode 100644 index 6942ba88bef..00000000000 --- a/web/src/pages/data-flow/form/begin-form/use-edit-query.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { useSetModalState } from '@/hooks/common-hooks'; -import { useSetSelectedRecord } from '@/hooks/logic-hooks'; -import { useCallback, useMemo, useState } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import { BeginQuery, INextOperatorForm } from '../../interface'; - -export const useEditQueryRecord = ({ - form, -}: INextOperatorForm & { form: UseFormReturn }) => { - const { setRecord, currentRecord } = useSetSelectedRecord(); - const { visible, hideModal, showModal } = useSetModalState(); - const [index, setIndex] = useState(-1); - const inputs: BeginQuery[] = useWatch({ - control: form.control, - name: 'inputs', - }); - - const otherThanCurrentQuery = useMemo(() => { - return inputs.filter((item, idx) => idx !== index); - }, [index, inputs]); - - const handleEditRecord = useCallback( - (record: BeginQuery) => { - const inputs: BeginQuery[] = form?.getValues('inputs') || []; - - const nextQuery: BeginQuery[] = - index > -1 ? inputs.toSpliced(index, 1, record) : [...inputs, record]; - - form.setValue('inputs', nextQuery); - - hideModal(); - }, - [form, hideModal, index], - ); - - const handleShowModal = useCallback( - (idx?: number, record?: BeginQuery) => { - setIndex(idx ?? -1); - setRecord(record ?? ({} as BeginQuery)); - showModal(); - }, - [setRecord, showModal], - ); - - const handleDeleteRecord = useCallback( - (idx: number) => { - const inputs = form?.getValues('inputs') || []; - const nextInputs = inputs.filter( - (item: BeginQuery, index: number) => index !== idx, - ); - - form.setValue('inputs', nextInputs); - }, - [form], - ); - - return { - ok: handleEditRecord, - currentRecord, - setRecord, - visible, - hideModal, - showModal: handleShowModal, - otherThanCurrentQuery, - handleDeleteRecord, - }; -}; diff --git a/web/src/pages/data-flow/form/begin-form/use-values.ts b/web/src/pages/data-flow/form/begin-form/use-values.ts deleted file mode 100644 index 10326bae83c..00000000000 --- a/web/src/pages/data-flow/form/begin-form/use-values.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { isEmpty } from 'lodash'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { AgentDialogueMode } from '../../constant'; -import { buildBeginInputListFromObject } from './utils'; - -export function useValues(node?: RAGFlowNodeType) { - const { t } = useTranslation(); - - const defaultValues = useMemo( - () => ({ - enablePrologue: true, - prologue: t('chat.setAnOpenerInitial'), - mode: AgentDialogueMode.Conversational, - inputs: [], - }), - [t], - ); - - const values = useMemo(() => { - const formData = node?.data?.form; - - if (isEmpty(formData)) { - return defaultValues; - } - - const inputs = buildBeginInputListFromObject(formData?.inputs); - - return { ...(formData || {}), inputs }; - }, [defaultValues, node?.data?.form]); - - return values; -} diff --git a/web/src/pages/data-flow/form/begin-form/use-watch-change.ts b/web/src/pages/data-flow/form/begin-form/use-watch-change.ts deleted file mode 100644 index f0da58068a1..00000000000 --- a/web/src/pages/data-flow/form/begin-form/use-watch-change.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { omit } from 'lodash'; -import { useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import { BeginQuery } from '../../interface'; -import useGraphStore from '../../store'; - -export function transferInputsArrayToObject(inputs: BeginQuery[] = []) { - return inputs.reduce>>((pre, cur) => { - pre[cur.key] = omit(cur, 'key'); - - return pre; - }, {}); -} - -export function useWatchFormChange(id?: string, form?: UseFormReturn) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - if (id) { - values = form?.getValues() || {}; - - const nextValues = { - ...values, - inputs: transferInputsArrayToObject(values.inputs), - }; - - updateNodeForm(id, nextValues); - } - }, [form?.formState.isDirty, id, updateNodeForm, values]); -} diff --git a/web/src/pages/data-flow/form/begin-form/utils.ts b/web/src/pages/data-flow/form/begin-form/utils.ts deleted file mode 100644 index 36038c4f6d2..00000000000 --- a/web/src/pages/data-flow/form/begin-form/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BeginQuery } from '../../interface'; - -export function buildBeginInputListFromObject( - inputs: Record>, -) { - return Object.entries(inputs || {}).reduce( - (pre, [key, value]) => { - pre.push({ ...(value || {}), key }); - - return pre; - }, - [], - ); -} diff --git a/web/src/pages/data-flow/form/categorize-form/dynamic-categorize.tsx b/web/src/pages/data-flow/form/categorize-form/dynamic-categorize.tsx deleted file mode 100644 index 0807f7bfa6b..00000000000 --- a/web/src/pages/data-flow/form/categorize-form/dynamic-categorize.tsx +++ /dev/null @@ -1,249 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '@/components/ui/collapsible'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { BlurTextarea } from '@/components/ui/textarea'; -import { useTranslate } from '@/hooks/common-hooks'; -import { PlusOutlined } from '@ant-design/icons'; -import { useUpdateNodeInternals } from '@xyflow/react'; -import humanId from 'human-id'; -import trim from 'lodash/trim'; -import { ChevronsUpDown, X } from 'lucide-react'; -import { - ChangeEventHandler, - FocusEventHandler, - memo, - useCallback, - useEffect, - useState, -} from 'react'; -import { UseFormReturn, useFieldArray, useFormContext } from 'react-hook-form'; -import { v4 as uuid } from 'uuid'; -import { z } from 'zod'; -import useGraphStore from '../../store'; -import DynamicExample from './dynamic-example'; -import { useCreateCategorizeFormSchema } from './use-form-schema'; - -interface IProps { - nodeId?: string; -} - -interface INameInputProps { - value?: string; - onChange?: (value: string) => void; - otherNames?: string[]; - validate(error?: string): void; -} - -const getOtherFieldValues = ( - form: UseFormReturn, - formListName: string = 'items', - index: number, - latestField: string, -) => - (form.getValues(formListName) ?? []) - .map((x: any) => x[latestField]) - .filter( - (x: string) => - x !== form.getValues(`${formListName}.${index}.${latestField}`), - ); - -const InnerNameInput = ({ - value, - onChange, - otherNames, - validate, -}: INameInputProps) => { - const [name, setName] = useState(); - const { t } = useTranslate('flow'); - - const handleNameChange: ChangeEventHandler = useCallback( - (e) => { - const val = e.target.value; - setName(val); - const trimmedVal = trim(val); - // trigger validation - if (otherNames?.some((x) => x === trimmedVal)) { - validate(t('nameRepeatedMsg')); - } else if (trimmedVal === '') { - validate(t('nameRequiredMsg')); - } else { - validate(''); - } - }, - [otherNames, validate, t], - ); - - const handleNameBlur: FocusEventHandler = useCallback( - (e) => { - const val = e.target.value; - if (otherNames?.every((x) => x !== val) && trim(val) !== '') { - onChange?.(val); - } - }, - [onChange, otherNames], - ); - - useEffect(() => { - setName(value); - }, [value]); - - return ( - - ); -}; - -const NameInput = memo(InnerNameInput); - -const InnerFormSet = ({ index }: IProps & { index: number }) => { - const form = useFormContext(); - const { t } = useTranslate('flow'); - - const buildFieldName = useCallback( - (name: string) => { - return `items.${index}.${name}`; - }, - [index], - ); - - return ( -
    - ( - - {t('categoryName')} - - { - const fieldName = buildFieldName('name'); - if (error) { - form.setError(fieldName, { message: error }); - } else { - form.clearErrors(fieldName); - } - }} - > - - - - )} - /> - ( - - {t('description')} - - - - - - )} - /> - {/* Create a hidden field to make Form instance record this */} -
    } - /> - -
    - ); -}; - -const FormSet = memo(InnerFormSet); - -const DynamicCategorize = ({ nodeId }: IProps) => { - const updateNodeInternals = useUpdateNodeInternals(); - const FormSchema = useCreateCategorizeFormSchema(); - - const deleteCategorizeCaseEdges = useGraphStore( - (state) => state.deleteEdgesBySourceAndSourceHandle, - ); - const form = useFormContext>(); - const { t } = useTranslate('flow'); - const { fields, remove, append } = useFieldArray({ - name: 'items', - control: form.control, - }); - - const handleAdd = useCallback(() => { - append({ - name: humanId(), - description: '', - uuid: uuid(), - examples: [{ value: '' }], - }); - if (nodeId) updateNodeInternals(nodeId); - }, [append, nodeId, updateNodeInternals]); - - const handleRemove = useCallback( - (index: number) => () => { - remove(index); - if (nodeId) { - const uuid = fields[index].uuid; - deleteCategorizeCaseEdges(nodeId, uuid); - } - }, - [deleteCategorizeCaseEdges, fields, nodeId, remove], - ); - - return ( -
    - {fields.map((field, index) => ( - -
    -

    - {form.getValues(`items.${index}.name`)} -

    - -
    - - -
    -
    -
    - - - -
    - ))} - - -
    - ); -}; - -export default memo(DynamicCategorize); diff --git a/web/src/pages/data-flow/form/categorize-form/dynamic-example.tsx b/web/src/pages/data-flow/form/categorize-form/dynamic-example.tsx deleted file mode 100644 index 35d95cbc6c8..00000000000 --- a/web/src/pages/data-flow/form/categorize-form/dynamic-example.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Textarea } from '@/components/ui/textarea'; -import { Plus, X } from 'lucide-react'; -import { memo } from 'react'; -import { useFieldArray, useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; - -type DynamicExampleProps = { name: string }; - -const DynamicExample = ({ name }: DynamicExampleProps) => { - const { t } = useTranslation(); - const form = useFormContext(); - - const { fields, append, remove } = useFieldArray({ - name: name, - control: form.control, - }); - - return ( - - {t('flow.examples')} -
    - {fields.map((field, index) => ( -
    - ( - - - - - - )} - /> - {index === 0 ? ( - - ) : ( - - )} -
    - ))} -
    - -
    - ); -}; - -export default memo(DynamicExample); diff --git a/web/src/pages/data-flow/form/categorize-form/index.tsx b/web/src/pages/data-flow/form/categorize-form/index.tsx deleted file mode 100644 index c36ff452946..00000000000 --- a/web/src/pages/data-flow/form/categorize-form/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { LargeModelFormField } from '@/components/large-model-form-field'; -import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; -import { Form } from '@/components/ui/form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm } from 'react-hook-form'; -import { initialCategorizeValues } from '../../constant'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; -import DynamicCategorize from './dynamic-categorize'; -import { useCreateCategorizeFormSchema } from './use-form-schema'; -import { useValues } from './use-values'; -import { useWatchFormChange } from './use-watch-change'; - -const outputList = buildOutputList(initialCategorizeValues.outputs); - -function CategorizeForm({ node }: INextOperatorForm) { - const values = useValues(node); - - const FormSchema = useCreateCategorizeFormSchema(); - - const form = useForm({ - defaultValues: values, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - - -
    - ); -} - -export default memo(CategorizeForm); diff --git a/web/src/pages/data-flow/form/categorize-form/use-form-schema.ts b/web/src/pages/data-flow/form/categorize-form/use-form-schema.ts deleted file mode 100644 index 9e56bb18b21..00000000000 --- a/web/src/pages/data-flow/form/categorize-form/use-form-schema.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { LlmSettingSchema } from '@/components/llm-setting-items/next'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; - -export function useCreateCategorizeFormSchema() { - const { t } = useTranslation(); - - const FormSchema = z.object({ - query: z.string().optional(), - parameter: z.string().optional(), - ...LlmSettingSchema, - message_history_window_size: z.coerce.number(), - items: z.array( - z - .object({ - name: z.string().min(1, t('flow.nameMessage')).trim(), - description: z.string().optional(), - uuid: z.string(), - examples: z - .array( - z.object({ - value: z.string(), - }), - ) - .optional(), - }) - .optional(), - ), - }); - - return FormSchema; -} diff --git a/web/src/pages/data-flow/form/categorize-form/use-values.ts b/web/src/pages/data-flow/form/categorize-form/use-values.ts deleted file mode 100644 index a920ec4cce4..00000000000 --- a/web/src/pages/data-flow/form/categorize-form/use-values.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ModelVariableType } from '@/constants/knowledge'; -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { isEmpty, isPlainObject } from 'lodash'; -import { useMemo } from 'react'; - -const defaultValues = { - parameter: ModelVariableType.Precise, - message_history_window_size: 1, - temperatureEnabled: true, - topPEnabled: true, - presencePenaltyEnabled: true, - frequencyPenaltyEnabled: true, - maxTokensEnabled: true, - items: [], -}; - -export function useValues(node?: RAGFlowNodeType) { - const values = useMemo(() => { - const formData = node?.data?.form; - if (isEmpty(formData)) { - return defaultValues; - } - if (isPlainObject(formData)) { - // const nextValues = { - // ...omit(formData, 'category_description'), - // items, - // }; - - return formData; - } - }, [node]); - - return values; -} diff --git a/web/src/pages/data-flow/form/categorize-form/use-watch-change.ts b/web/src/pages/data-flow/form/categorize-form/use-watch-change.ts deleted file mode 100644 index a97b80a77f2..00000000000 --- a/web/src/pages/data-flow/form/categorize-form/use-watch-change.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import useGraphStore from '../../store'; - -export function useWatchFormChange(id?: string, form?: UseFormReturn) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - // Manually triggered form updates are synchronized to the canvas - if (id) { - values = form?.getValues(); - - updateNodeForm(id, { ...values, items: values.items?.slice() || [] }); - } - }, [id, updateNodeForm, values]); -} diff --git a/web/src/pages/data-flow/form/chunker-form/index.tsx b/web/src/pages/data-flow/form/chunker-form/index.tsx deleted file mode 100644 index cfc32e82f56..00000000000 --- a/web/src/pages/data-flow/form/chunker-form/index.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import NumberInput from '@/components/originui/number-input'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialChunkerValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; -import { buildOutputList } from '../../utils/build-output-list'; -import { ApiKeyField } from '../components/api-key-field'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; - -const outputList = buildOutputList(initialChunkerValues.outputs); - -export const GoogleFormPartialSchema = { - api_key: z.string(), - country: z.string(), - language: z.string(), -}; - -export const FormSchema = z.object({ - ...GoogleFormPartialSchema, - q: z.string(), - start: z.number(), - num: z.number(), -}); - -export function GoogleFormWidgets() { - const form = useFormContext(); - const { t } = useTranslate('flow'); - - return ( - <> - ( - - {t('country')} - - - - - - )} - /> - ( - - {t('language')} - - - - - - )} - /> - - ); -} - -const ChunkerForm = ({ node }: INextOperatorForm) => { - const { t } = useTranslate('flow'); - const defaultValues = useFormValues(initialChunkerValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - ( - - {t('flowStart')} - - - - - - )} - /> - ( - - {t('flowNum')} - - - - - - )} - /> - - - -
    - -
    -
    - ); -}; - -export default memo(ChunkerForm); diff --git a/web/src/pages/data-flow/form/code-form/index.tsx b/web/src/pages/data-flow/form/code-form/index.tsx deleted file mode 100644 index 2883fdf467b..00000000000 --- a/web/src/pages/data-flow/form/code-form/index.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import Editor, { loader } from '@monaco-editor/react'; -import { INextOperatorForm } from '../../interface'; - -import { FormContainer } from '@/components/form-container'; -import { useIsDarkTheme } from '@/components/theme-provider'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { ProgrammingLanguage } from '@/constants/agent'; -import { ICodeForm } from '@/interfaces/database/agent'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { - DynamicInputVariable, - TypeOptions, - VariableTitle, -} from './next-variable'; -import { FormSchema, FormSchemaType } from './schema'; -import { useValues } from './use-values'; -import { - useHandleLanguageChange, - useWatchFormChange, -} from './use-watch-change'; - -loader.config({ paths: { vs: '/vs' } }); - -const options = [ - ProgrammingLanguage.Python, - ProgrammingLanguage.Javascript, -].map((x) => ({ value: x, label: x })); - -const DynamicFieldName = 'outputs'; - -function CodeForm({ node }: INextOperatorForm) { - const formData = node?.data.form as ICodeForm; - const { t } = useTranslation(); - const values = useValues(node); - const isDarkTheme = useIsDarkTheme(); - - const form = useForm({ - defaultValues: values, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - const handleLanguageChange = useHandleLanguageChange(node?.id, form); - - return ( -
    - - - ( - - - Code - ( - - - { - field.onChange(val); - handleLanguageChange(val); - }} - options={options} - /> - - - - )} - /> - - - - - - - )} - /> - - {formData.lang === ProgrammingLanguage.Python ? ( - - ) : ( -
    - - - ( - - Name - - - - - - )} - /> - ( - - Type - - - - - - )} - /> - -
    - )} -
    -
    - -
    -
    - ); -} - -export default memo(CodeForm); diff --git a/web/src/pages/data-flow/form/code-form/next-variable.tsx b/web/src/pages/data-flow/form/code-form/next-variable.tsx deleted file mode 100644 index 39a2dd4a48d..00000000000 --- a/web/src/pages/data-flow/form/code-form/next-variable.tsx +++ /dev/null @@ -1,128 +0,0 @@ -'use client'; - -import { FormContainer } from '@/components/form-container'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { BlockButton, Button } from '@/components/ui/button'; -import { - FormControl, - FormField, - FormItem, - FormMessage, -} from '@/components/ui/form'; -import { BlurInput } from '@/components/ui/input'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { Separator } from '@/components/ui/separator'; -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { X } from 'lucide-react'; -import { ReactNode } from 'react'; -import { useFieldArray, useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; - -interface IProps { - node?: RAGFlowNodeType; - name?: string; - isOutputs: boolean; -} - -export const TypeOptions = [ - 'String', - 'Number', - 'Boolean', - 'Array', - 'Array', - 'Object', -].map((x) => ({ label: x, value: x })); - -export function DynamicVariableForm({ name = 'arguments', isOutputs }: IProps) { - const { t } = useTranslation(); - const form = useFormContext(); - - const { fields, remove, append } = useFieldArray({ - name: name, - control: form.control, - }); - - const nextOptions = useBuildQueryVariableOptions(); - - return ( -
    - {fields.map((field, index) => { - const typeField = `${name}.${index}.name`; - return ( -
    - ( - - - - - - - )} - /> - - ( - - - {isOutputs ? ( - - ) : ( - - )} - - - - )} - /> - -
    - ); - })} - append({ name: '', type: undefined })}> - {t('flow.addVariable')} - -
    - ); -} - -export function VariableTitle({ title }: { title: ReactNode }) { - return
    {title}
    ; -} - -export function DynamicInputVariable({ - node, - name, - title, - isOutputs = false, -}: IProps & { title: ReactNode }) { - return ( -
    - - - - -
    - ); -} diff --git a/web/src/pages/data-flow/form/code-form/schema.ts b/web/src/pages/data-flow/form/code-form/schema.ts deleted file mode 100644 index fe694444e20..00000000000 --- a/web/src/pages/data-flow/form/code-form/schema.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ProgrammingLanguage } from '@/constants/agent'; -import { z } from 'zod'; - -export const FormSchema = z.object({ - lang: z.enum([ProgrammingLanguage.Python, ProgrammingLanguage.Javascript]), - script: z.string(), - arguments: z.array(z.object({ name: z.string(), type: z.string() })), - outputs: z.union([ - z.array(z.object({ name: z.string(), type: z.string() })).optional(), - z.object({ name: z.string(), type: z.string() }), - ]), -}); - -export type FormSchemaType = z.infer; diff --git a/web/src/pages/data-flow/form/code-form/use-values.ts b/web/src/pages/data-flow/form/code-form/use-values.ts deleted file mode 100644 index ea6f2d67cf8..00000000000 --- a/web/src/pages/data-flow/form/code-form/use-values.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ProgrammingLanguage } from '@/constants/agent'; -import { ICodeForm } from '@/interfaces/database/agent'; -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { isEmpty } from 'lodash'; -import { useMemo } from 'react'; -import { initialCodeValues } from '../../constant'; - -function convertToArray(args: Record) { - return Object.entries(args).map(([key, value]) => ({ - name: key, - type: value, - })); -} - -type OutputsFormType = { name: string; type: string }; - -function convertOutputsToArray({ lang, outputs = {} }: ICodeForm) { - if (lang === ProgrammingLanguage.Python) { - return Object.entries(outputs).map(([key, val]) => ({ - name: key, - type: val.type, - })); - } - return Object.entries(outputs).reduce((pre, [key, val]) => { - pre.name = key; - pre.type = val.type; - return pre; - }, {} as OutputsFormType); -} - -export function useValues(node?: RAGFlowNodeType) { - const values = useMemo(() => { - const formData = node?.data?.form; - - if (isEmpty(formData)) { - return initialCodeValues; - } - - return { - ...formData, - arguments: convertToArray(formData.arguments), - outputs: convertOutputsToArray(formData), - }; - }, [node?.data?.form]); - - return values; -} diff --git a/web/src/pages/data-flow/form/code-form/use-watch-change.ts b/web/src/pages/data-flow/form/code-form/use-watch-change.ts deleted file mode 100644 index 80e0c8b15d7..00000000000 --- a/web/src/pages/data-flow/form/code-form/use-watch-change.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; -import { ICodeForm } from '@/interfaces/database/agent'; -import { isEmpty } from 'lodash'; -import { useCallback, useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import useGraphStore from '../../store'; -import { FormSchemaType } from './schema'; - -function convertToObject(list: FormSchemaType['arguments'] = []) { - return list.reduce>((pre, cur) => { - pre[cur.name] = cur.type; - - return pre; - }, {}); -} - -type ArrayOutputs = Extract>; - -type ObjectOutputs = Exclude>; - -function convertOutputsToObject({ lang, outputs }: FormSchemaType) { - if (lang === ProgrammingLanguage.Python) { - return (outputs as ArrayOutputs).reduce( - (pre, cur) => { - pre[cur.name] = { - value: '', - type: cur.type, - }; - - return pre; - }, - {}, - ); - } - const outputsObject = outputs as ObjectOutputs; - if (isEmpty(outputsObject)) { - return {}; - } - return { - [outputsObject.name]: { - value: '', - type: outputsObject.type, - }, - }; -} - -export function useWatchFormChange( - id?: string, - form?: UseFormReturn, -) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - // Manually triggered form updates are synchronized to the canvas - if (id) { - values = form?.getValues() || {}; - let nextValues: any = { - ...values, - arguments: convertToObject( - values?.arguments as FormSchemaType['arguments'], - ), - outputs: convertOutputsToObject(values as FormSchemaType), - }; - - updateNodeForm(id, nextValues); - } - }, [form?.formState.isDirty, id, updateNodeForm, values]); -} - -export function useHandleLanguageChange( - id?: string, - form?: UseFormReturn, -) { - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - const handleLanguageChange = useCallback( - (lang: string) => { - if (id) { - const script = CodeTemplateStrMap[lang as ProgrammingLanguage]; - form?.setValue('script', script); - form?.setValue( - 'outputs', - (lang === ProgrammingLanguage.Python - ? [] - : {}) as FormSchemaType['outputs'], - ); - updateNodeForm(id, script, ['script']); - } - }, - [form, id, updateNodeForm], - ); - - return handleLanguageChange; -} diff --git a/web/src/pages/data-flow/form/components/api-key-field.tsx b/web/src/pages/data-flow/form/components/api-key-field.tsx deleted file mode 100644 index f9debfc4f3c..00000000000 --- a/web/src/pages/data-flow/form/components/api-key-field.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { t } from 'i18next'; -import { useFormContext } from 'react-hook-form'; - -interface IApiKeyFieldProps { - placeholder?: string; -} -export function ApiKeyField({ placeholder }: IApiKeyFieldProps) { - const form = useFormContext(); - return ( - ( - - {t('flow.apiKey')} - - - - - - )} - /> - ); -} diff --git a/web/src/pages/data-flow/form/components/description-field.tsx b/web/src/pages/data-flow/form/components/description-field.tsx deleted file mode 100644 index 8fa2eef6402..00000000000 --- a/web/src/pages/data-flow/form/components/description-field.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { - FormControl, - FormField, - FormItem, - FormLabel, -} from '@/components/ui/form'; -import { Textarea } from '@/components/ui/textarea'; -import { t } from 'i18next'; -import { useFormContext } from 'react-hook-form'; - -export function DescriptionField() { - const form = useFormContext(); - return ( - ( - - {t('flow.description')} - - - - - )} - /> - ); -} diff --git a/web/src/pages/data-flow/form/components/dynamic-input-variable.tsx b/web/src/pages/data-flow/form/components/dynamic-input-variable.tsx deleted file mode 100644 index a5781fd16f9..00000000000 --- a/web/src/pages/data-flow/form/components/dynamic-input-variable.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; -import { Button, Collapse, Flex, Form, Input, Select } from 'antd'; -import { PropsWithChildren, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useBuildVariableOptions } from '../../hooks/use-get-begin-query'; - -import styles from './index.less'; - -interface IProps { - node?: RAGFlowNodeType; -} - -enum VariableType { - Reference = 'reference', - Input = 'input', -} - -const getVariableName = (type: string) => - type === VariableType.Reference ? 'component_id' : 'value'; - -const DynamicVariableForm = ({ node }: IProps) => { - const { t } = useTranslation(); - const valueOptions = useBuildVariableOptions(node?.id, node?.parentId); - const form = Form.useFormInstance(); - - const options = [ - { value: VariableType.Reference, label: t('flow.reference') }, - { value: VariableType.Input, label: t('flow.text') }, - ]; - - const handleTypeChange = useCallback( - (name: number) => () => { - setTimeout(() => { - form.setFieldValue(['query', name, 'component_id'], undefined); - form.setFieldValue(['query', name, 'value'], undefined); - }, 0); - }, - [form], - ); - - return ( - - {(fields, { add, remove }) => ( - <> - {fields.map(({ key, name, ...restField }) => ( - - - - - - {({ getFieldValue }) => { - const type = getFieldValue(['query', name, 'type']); - return ( - - {type === VariableType.Reference ? ( - - ) : ( - - )} - - ); - }} - - remove(name)} /> - - ))} - - - - - )} - - ); -}; - -export function FormCollapse({ - children, - title, -}: PropsWithChildren<{ title: string }>) { - return ( - {title}, - children, - }, - ]} - /> - ); -} - -const DynamicInputVariable = ({ node }: IProps) => { - const { t } = useTranslation(); - return ( - - - - ); -}; - -export default DynamicInputVariable; diff --git a/web/src/pages/data-flow/form/components/next-dynamic-input-variable.tsx b/web/src/pages/data-flow/form/components/next-dynamic-input-variable.tsx deleted file mode 100644 index 8b4cbd8a95b..00000000000 --- a/web/src/pages/data-flow/form/components/next-dynamic-input-variable.tsx +++ /dev/null @@ -1,135 +0,0 @@ -'use client'; - -import { SideDown } from '@/assets/icon/next-icon'; -import { Button } from '@/components/ui/button'; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '@/components/ui/collapsible'; -import { - FormControl, - FormDescription, - FormField, - FormItem, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { Plus, Trash2 } from 'lucide-react'; -import { useFieldArray, useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useBuildVariableOptions } from '../../hooks/use-get-begin-query'; - -interface IProps { - node?: RAGFlowNodeType; -} - -enum VariableType { - Reference = 'reference', - Input = 'input', -} - -const getVariableName = (type: string) => - type === VariableType.Reference ? 'component_id' : 'value'; - -export function DynamicVariableForm({ node }: IProps) { - const { t } = useTranslation(); - const form = useFormContext(); - const { fields, remove, append } = useFieldArray({ - name: 'query', - control: form.control, - }); - - const valueOptions = useBuildVariableOptions(node?.id, node?.parentId); - - const options = [ - { value: VariableType.Reference, label: t('flow.reference') }, - { value: VariableType.Input, label: t('flow.text') }, - ]; - - return ( -
    - {fields.map((field, index) => { - const typeField = `query.${index}.type`; - const typeValue = form.watch(typeField); - return ( -
    - ( - - - - { - field.onChange(val); - form.resetField(`query.${index}.value`); - form.resetField(`query.${index}.component_id`); - }} - > - - - - )} - /> - ( - - - - {typeValue === VariableType.Reference ? ( - - ) : ( - - )} - - - - )} - /> - remove(index)} - /> -
    - ); - })} - -
    - ); -} - -export function DynamicInputVariable({ node }: IProps) { - const { t } = useTranslation(); - - return ( - - - - {t('flow.input')} - - - - - - - - ); -} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/constant.ts b/web/src/pages/data-flow/form/components/prompt-editor/constant.ts deleted file mode 100644 index b6cf30ed9cd..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/constant.ts +++ /dev/null @@ -1 +0,0 @@ -export const ProgrammaticTag = 'programmatic'; diff --git a/web/src/pages/data-flow/form/components/prompt-editor/index.css b/web/src/pages/data-flow/form/components/prompt-editor/index.css deleted file mode 100644 index 8f305064721..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/index.css +++ /dev/null @@ -1,76 +0,0 @@ -.typeahead-popover { - background: #fff; - box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3); - border-radius: 8px; - position: fixed; - z-index: 1000; -} - -.typeahead-popover ul { - list-style: none; - margin: 0; - max-height: 200px; - overflow-y: scroll; -} - -.typeahead-popover ul::-webkit-scrollbar { - display: none; -} - -.typeahead-popover ul { - -ms-overflow-style: none; - scrollbar-width: none; -} - -.typeahead-popover ul li { - margin: 0; - min-width: 180px; - font-size: 14px; - outline: none; - cursor: pointer; - border-radius: 8px; -} - -.typeahead-popover ul li.selected { - background: #eee; -} - -.typeahead-popover li { - margin: 0 8px 0 8px; - color: #050505; - cursor: pointer; - line-height: 16px; - font-size: 15px; - display: flex; - align-content: center; - flex-direction: row; - flex-shrink: 0; - background-color: #fff; - border: 0; -} - -.typeahead-popover li.active { - display: flex; - width: 20px; - height: 20px; - background-size: contain; -} - -.typeahead-popover li .text { - display: flex; - line-height: 20px; - flex-grow: 1; - min-width: 150px; -} - -.typeahead-popover li .icon { - display: flex; - width: 20px; - height: 20px; - user-select: none; - margin-right: 8px; - line-height: 16px; - background-size: contain; - background-repeat: no-repeat; - background-position: center; -} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/index.tsx b/web/src/pages/data-flow/form/components/prompt-editor/index.tsx deleted file mode 100644 index caf6914b45c..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/index.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { CodeHighlightNode, CodeNode } from '@lexical/code'; -import { - InitialConfigType, - LexicalComposer, -} from '@lexical/react/LexicalComposer'; -import { ContentEditable } from '@lexical/react/LexicalContentEditable'; -import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'; -import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; -import { HeadingNode, QuoteNode } from '@lexical/rich-text'; -import { - $getRoot, - $getSelection, - EditorState, - Klass, - LexicalNode, -} from 'lexical'; - -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/components/ui/tooltip'; -import { cn } from '@/lib/utils'; -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { Variable } from 'lucide-react'; -import { ReactNode, useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PasteHandlerPlugin } from './paste-handler-plugin'; -import theme from './theme'; -import { VariableNode } from './variable-node'; -import { VariableOnChangePlugin } from './variable-on-change-plugin'; -import VariablePickerMenuPlugin from './variable-picker-plugin'; - -// Catch any errors that occur during Lexical updates and log them -// or throw them as needed. If you don't throw them, Lexical will -// try to recover gracefully without losing user data. -function onError(error: Error) { - console.error(error); -} - -const Nodes: Array> = [ - HeadingNode, - QuoteNode, - CodeHighlightNode, - CodeNode, - VariableNode, -]; - -type PromptContentProps = { showToolbar?: boolean; multiLine?: boolean }; - -type IProps = { - value?: string; - onChange?: (value?: string) => void; - placeholder?: ReactNode; -} & PromptContentProps; - -function PromptContent({ - showToolbar = true, - multiLine = true, -}: PromptContentProps) { - const [editor] = useLexicalComposerContext(); - const [isBlur, setIsBlur] = useState(false); - const { t } = useTranslation(); - - const insertTextAtCursor = useCallback(() => { - editor.update(() => { - const selection = $getSelection(); - - if (selection !== null) { - selection.insertText(' /'); - } - }); - }, [editor]); - - const handleVariableIconClick = useCallback(() => { - insertTextAtCursor(); - }, [insertTextAtCursor]); - - const handleBlur = useCallback(() => { - setIsBlur(true); - }, []); - - const handleFocus = useCallback(() => { - setIsBlur(false); - }, []); - - return ( -
    - {showToolbar && ( -
    - - - - - - - -

    {t('flow.insertVariableTip')}

    -
    -
    -
    - )} - -
    - ); -} - -export function PromptEditor({ - value, - onChange, - placeholder, - showToolbar, - multiLine = true, -}: IProps) { - const { t } = useTranslation(); - const initialConfig: InitialConfigType = { - namespace: 'PromptEditor', - theme, - onError, - nodes: Nodes, - }; - - const onValueChange = useCallback( - (editorState: EditorState) => { - editorState?.read(() => { - // const listNodes = $nodesOfType(VariableNode); // to be removed - // const allNodes = $dfs(); - - const text = $getRoot().getTextContent(); - - onChange?.(text); - }); - }, - [onChange], - ); - - return ( -
    - - - } - placeholder={ -
    - {placeholder || t('common.promptPlaceholder')} -
    - } - ErrorBoundary={LexicalErrorBoundary} - /> - - - -
    -
    - ); -} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/paste-handler-plugin.tsx b/web/src/pages/data-flow/form/components/prompt-editor/paste-handler-plugin.tsx deleted file mode 100644 index a45a5e5fbfa..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/paste-handler-plugin.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { - $createParagraphNode, - $createTextNode, - $getSelection, - $isRangeSelection, - PASTE_COMMAND, -} from 'lexical'; -import { useEffect } from 'react'; - -function PasteHandlerPlugin() { - const [editor] = useLexicalComposerContext(); - useEffect(() => { - const removeListener = editor.registerCommand( - PASTE_COMMAND, - (clipboardEvent: ClipboardEvent) => { - const clipboardData = clipboardEvent.clipboardData; - if (!clipboardData) { - return false; - } - - const text = clipboardData.getData('text/plain'); - if (!text) { - return false; - } - - // Check if text contains line breaks - if (text.includes('\n')) { - editor.update(() => { - const selection = $getSelection(); - if (selection && $isRangeSelection(selection)) { - // Normalize line breaks, merge multiple consecutive line breaks into a single line break - const normalizedText = text.replace(/\n{2,}/g, '\n'); - - // Clear current selection - selection.removeText(); - - // Create a paragraph node to contain all content - const paragraph = $createParagraphNode(); - - // Split text by line breaks - const lines = normalizedText.split('\n'); - - // Process each line - lines.forEach((lineText, index) => { - // Add line text (if any) - if (lineText) { - const textNode = $createTextNode(lineText); - paragraph.append(textNode); - } - - // If not the last line, add a line break - if (index < lines.length - 1) { - const lineBreak = $createTextNode('\n'); - paragraph.append(lineBreak); - } - }); - - // Insert paragraph - selection.insertNodes([paragraph]); - } - }); - - // Prevent default paste behavior - clipboardEvent.preventDefault(); - return true; - } - - // If no line breaks, use default behavior - return false; - }, - 4, - ); - - return () => { - removeListener(); - }; - }, [editor]); - - return null; -} - -export { PasteHandlerPlugin }; diff --git a/web/src/pages/data-flow/form/components/prompt-editor/theme.ts b/web/src/pages/data-flow/form/components/prompt-editor/theme.ts deleted file mode 100644 index 1cc2bc15528..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/theme.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -export default { - code: 'editor-code', - heading: { - h1: 'editor-heading-h1', - h2: 'editor-heading-h2', - h3: 'editor-heading-h3', - h4: 'editor-heading-h4', - h5: 'editor-heading-h5', - }, - image: 'editor-image', - link: 'editor-link', - list: { - listitem: 'editor-listitem', - nested: { - listitem: 'editor-nested-listitem', - }, - ol: 'editor-list-ol', - ul: 'editor-list-ul', - }, - ltr: 'ltr', - paragraph: 'editor-paragraph', - placeholder: 'editor-placeholder', - quote: 'editor-quote', - rtl: 'rtl', - text: { - bold: 'editor-text-bold', - code: 'editor-text-code', - hashtag: 'editor-text-hashtag', - italic: 'editor-text-italic', - overflowed: 'editor-text-overflowed', - strikethrough: 'editor-text-strikethrough', - underline: 'editor-text-underline', - underlineStrikethrough: 'editor-text-underlineStrikethrough', - }, -}; diff --git a/web/src/pages/data-flow/form/components/prompt-editor/variable-node.tsx b/web/src/pages/data-flow/form/components/prompt-editor/variable-node.tsx deleted file mode 100644 index 177c370c9c6..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/variable-node.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { BeginId } from '@/pages/flow/constant'; -import { DecoratorNode, LexicalNode, NodeKey } from 'lexical'; -import { ReactNode } from 'react'; -const prefix = BeginId + '@'; - -export class VariableNode extends DecoratorNode { - __value: string; - __label: string; - key?: NodeKey; - __parentLabel?: string | ReactNode; - __icon?: ReactNode; - - static getType(): string { - return 'variable'; - } - - static clone(node: VariableNode): VariableNode { - return new VariableNode( - node.__value, - node.__label, - node.__key, - node.__parentLabel, - node.__icon, - ); - } - - constructor( - value: string, - label: string, - key?: NodeKey, - parent?: string | ReactNode, - icon?: ReactNode, - ) { - super(key); - this.__value = value; - this.__label = label; - this.__parentLabel = parent; - this.__icon = icon; - } - - createDOM(): HTMLElement { - const dom = document.createElement('span'); - dom.className = 'mr-1'; - - return dom; - } - - updateDOM(): false { - return false; - } - - decorate(): ReactNode { - let content: ReactNode = ( -
    {this.__label}
    - ); - if (this.__parentLabel) { - content = ( -
    -
    {this.__icon}
    -
    {this.__parentLabel}
    -
    /
    - {content} -
    - ); - } - return ( -
    - {content} -
    - ); - } - - getTextContent(): string { - return `{${this.__value}}`; - } -} - -export function $createVariableNode( - value: string, - label: string, - parentLabel: string | ReactNode, - icon?: ReactNode, -): VariableNode { - return new VariableNode(value, label, undefined, parentLabel, icon); -} - -export function $isVariableNode( - node: LexicalNode | null | undefined, -): node is VariableNode { - return node instanceof VariableNode; -} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/variable-on-change-plugin.tsx b/web/src/pages/data-flow/form/components/prompt-editor/variable-on-change-plugin.tsx deleted file mode 100644 index 86fa66db4f8..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/variable-on-change-plugin.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { EditorState, LexicalEditor } from 'lexical'; -import { useEffect } from 'react'; -import { ProgrammaticTag } from './constant'; - -interface IProps { - onChange: ( - editorState: EditorState, - editor?: LexicalEditor, - tags?: Set, - ) => void; -} - -export function VariableOnChangePlugin({ onChange }: IProps) { - // Access the editor through the LexicalComposerContext - const [editor] = useLexicalComposerContext(); - // Wrap our listener in useEffect to handle the teardown and avoid stale references. - useEffect(() => { - // most listeners return a teardown function that can be called to clean them up. - return editor.registerUpdateListener( - ({ editorState, tags, dirtyElements }) => { - // Check if there is a "programmatic" tag - const isProgrammaticUpdate = tags.has(ProgrammaticTag); - - // The onchange event is only triggered when the data is manually updated - // Otherwise, the content will be displayed incorrectly. - if (dirtyElements.size > 0 && !isProgrammaticUpdate) { - onChange(editorState); - } - }, - ); - }, [editor, onChange]); - - return null; -} diff --git a/web/src/pages/data-flow/form/components/prompt-editor/variable-picker-plugin.tsx b/web/src/pages/data-flow/form/components/prompt-editor/variable-picker-plugin.tsx deleted file mode 100644 index f429981c780..00000000000 --- a/web/src/pages/data-flow/form/components/prompt-editor/variable-picker-plugin.tsx +++ /dev/null @@ -1,297 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { - LexicalTypeaheadMenuPlugin, - MenuOption, - useBasicTypeaheadTriggerMatch, -} from '@lexical/react/LexicalTypeaheadMenuPlugin'; -import { - $createParagraphNode, - $createTextNode, - $getRoot, - $getSelection, - $isRangeSelection, - TextNode, -} from 'lexical'; -import React, { - ReactElement, - ReactNode, - useCallback, - useEffect, - useRef, -} from 'react'; -import * as ReactDOM from 'react-dom'; - -import { $createVariableNode } from './variable-node'; - -import { useBuildQueryVariableOptions } from '@/pages/agent/hooks/use-get-begin-query'; -import { ProgrammaticTag } from './constant'; -import './index.css'; -class VariableInnerOption extends MenuOption { - label: string; - value: string; - parentLabel: string | JSX.Element; - icon?: ReactNode; - - constructor( - label: string, - value: string, - parentLabel: string | JSX.Element, - icon?: ReactNode, - ) { - super(value); - this.label = label; - this.value = value; - this.parentLabel = parentLabel; - this.icon = icon; - } -} - -class VariableOption extends MenuOption { - label: ReactElement | string; - title: string; - options: VariableInnerOption[]; - - constructor( - label: ReactElement | string, - title: string, - options: VariableInnerOption[], - ) { - super(title); - this.label = label; - this.title = title; - this.options = options; - } -} - -function VariablePickerMenuItem({ - index, - option, - selectOptionAndCleanUp, -}: { - index: number; - option: VariableOption; - selectOptionAndCleanUp: ( - option: VariableOption | VariableInnerOption, - ) => void; -}) { - return ( -
  • -
    - {option.title} -
      - {option.options.map((x) => ( -
    • selectOptionAndCleanUp(x)} - className="hover:bg-slate-300 p-1" - > - {x.label} -
    • - ))} -
    -
    -
  • - ); -} - -export default function VariablePickerMenuPlugin({ - value, -}: { - value?: string; -}): JSX.Element { - const [editor] = useLexicalComposerContext(); - const isFirstRender = useRef(true); - - const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', { - minLength: 0, - }); - - const [queryString, setQueryString] = React.useState(''); - - const options = useBuildQueryVariableOptions(); - - const buildNextOptions = useCallback(() => { - let filteredOptions = options; - if (queryString) { - const lowerQuery = queryString.toLowerCase(); - filteredOptions = options - .map((x) => ({ - ...x, - options: x.options.filter( - (y) => - y.label.toLowerCase().includes(lowerQuery) || - y.value.toLowerCase().includes(lowerQuery), - ), - })) - .filter((x) => x.options.length > 0); - } - - const nextOptions: VariableOption[] = filteredOptions.map( - (x) => - new VariableOption( - x.label, - x.title, - x.options.map((y) => { - return new VariableInnerOption(y.label, y.value, x.label, y.icon); - }), - ), - ); - return nextOptions; - }, [options, queryString]); - - const findItemByValue = useCallback( - (value: string) => { - const children = options.reduce< - Array<{ - label: string; - value: string; - parentLabel?: string | ReactNode; - icon?: ReactNode; - }> - >((pre, cur) => { - return pre.concat(cur.options); - }, []); - - return children.find((x) => x.value === value); - }, - [options], - ); - - const onSelectOption = useCallback( - ( - selectedOption: VariableOption | VariableInnerOption, - nodeToRemove: TextNode | null, - closeMenu: () => void, - ) => { - editor.update(() => { - const selection = $getSelection(); - - if (!$isRangeSelection(selection) || selectedOption === null) { - return; - } - - if (nodeToRemove) { - nodeToRemove.remove(); - } - const variableNode = $createVariableNode( - (selectedOption as VariableInnerOption).value, - selectedOption.label as string, - selectedOption.parentLabel as string | ReactNode, - selectedOption.icon as ReactNode, - ); - selection.insertNodes([variableNode]); - - closeMenu(); - }); - }, - [editor], - ); - - const parseTextToVariableNodes = useCallback( - (text: string) => { - const paragraph = $createParagraphNode(); - - // Regular expression to match content within {} - const regex = /{([^}]*)}/g; - let match; - let lastIndex = 0; - while ((match = regex.exec(text)) !== null) { - const { 1: content, index, 0: template } = match; - - // Add the previous text part (if any) - if (index > lastIndex) { - const textNode = $createTextNode(text.slice(lastIndex, index)); - - paragraph.append(textNode); - } - - // Add variable node or text node - const nodeItem = findItemByValue(content); - - if (nodeItem) { - paragraph.append( - $createVariableNode( - content, - nodeItem.label, - nodeItem.parentLabel, - nodeItem.icon, - ), - ); - } else { - paragraph.append($createTextNode(template)); - } - - // Update index - lastIndex = regex.lastIndex; - } - - // Add the last part of text (if any) - if (lastIndex < text.length) { - const textNode = $createTextNode(text.slice(lastIndex)); - paragraph.append(textNode); - } - - $getRoot().clear().append(paragraph); - - if ($isRangeSelection($getSelection())) { - $getRoot().selectEnd(); - } - }, - [findItemByValue], - ); - - useEffect(() => { - if (editor && value && isFirstRender.current) { - isFirstRender.current = false; - editor.update( - () => { - parseTextToVariableNodes(value); - }, - { tag: ProgrammaticTag }, - ); - } - }, [parseTextToVariableNodes, editor, value]); - - return ( - - onQueryChange={setQueryString} - onSelectOption={onSelectOption} - triggerFn={checkForTriggerMatch} - options={buildNextOptions()} - menuRenderFn={(anchorElementRef, { selectOptionAndCleanUp }) => { - const nextOptions = buildNextOptions(); - return anchorElementRef.current && nextOptions.length - ? ReactDOM.createPortal( -
    -
      - {nextOptions.map((option, i: number) => ( - - ))} -
    -
    , - anchorElementRef.current, - ) - : null; - }} - /> - ); -} diff --git a/web/src/pages/data-flow/form/components/query-variable.tsx b/web/src/pages/data-flow/form/components/query-variable.tsx deleted file mode 100644 index dafcb4cde79..00000000000 --- a/web/src/pages/data-flow/form/components/query-variable.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { toLower } from 'lodash'; -import { ReactNode, useMemo } from 'react'; -import { useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { VariableType } from '../../constant'; -import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; - -type QueryVariableProps = { - name?: string; - type?: VariableType; - label?: ReactNode; -}; - -export function QueryVariable({ - name = 'query', - type, - label, -}: QueryVariableProps) { - const { t } = useTranslation(); - const form = useFormContext(); - - const nextOptions = useBuildQueryVariableOptions(); - - const finalOptions = useMemo(() => { - return type - ? nextOptions.map((x) => { - return { - ...x, - options: x.options.filter((y) => toLower(y.type).includes(type)), - }; - }) - : nextOptions; - }, [nextOptions, type]); - - return ( - ( - - {label || ( - - {t('flow.query')} - - )} - - - - - - )} - /> - ); -} diff --git a/web/src/pages/data-flow/form/crawler-form/index.tsx b/web/src/pages/data-flow/form/crawler-form/index.tsx deleted file mode 100644 index 8c8da6b086b..00000000000 --- a/web/src/pages/data-flow/form/crawler-form/index.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useMemo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialCrawlerValues } from '../../constant'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { CrawlerResultOptions } from '../../options'; -import { QueryVariable } from '../components/query-variable'; - -export function CrawlerProxyFormField() { - const { t } = useTranslate('flow'); - const form = useFormContext(); - - return ( - ( - - {t('proxy')} - - - - - - )} - /> - ); -} - -export function CrawlerExtractTypeFormField() { - const { t } = useTranslate('flow'); - const form = useFormContext(); - const crawlerResultOptions = useMemo(() => { - return CrawlerResultOptions.map((x) => ({ - value: x, - label: t(`crawlerResultOptions.${x}`), - })); - }, [t]); - - return ( - ( - - {t('extractType')} - - - - - - )} - /> - ); -} - -export const CrawlerFormSchema = { - proxy: z.string().url(), - extract_type: z.string(), -}; - -const FormSchema = z.object({ - query: z.string().optional(), - ...CrawlerFormSchema, -}); - -function CrawlerForm({ node }: INextOperatorForm) { - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: initialCrawlerValues, - mode: 'onChange', - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - { - e.preventDefault(); - }} - > - - - -
    - - ); -} - -export default memo(CrawlerForm); diff --git a/web/src/pages/data-flow/form/email-form/index.tsx b/web/src/pages/data-flow/form/email-form/index.tsx deleted file mode 100644 index b142dae7651..00000000000 --- a/web/src/pages/data-flow/form/email-form/index.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { ReactNode } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialEmailValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { PromptEditor } from '../components/prompt-editor'; - -interface InputFormFieldProps { - name: string; - label: ReactNode; - type?: string; -} - -function InputFormField({ name, label, type }: InputFormFieldProps) { - const form = useFormContext(); - - return ( - ( - - {label} - - - - - - )} - /> - ); -} - -function PromptFormField({ name, label }: InputFormFieldProps) { - const form = useFormContext(); - - return ( - ( - - {label} - - - - - - )} - /> - ); -} -export function EmailFormWidgets() { - const { t } = useTranslate('flow'); - - return ( - <> - - - - - - - ); -} - -export const EmailFormPartialSchema = { - smtp_server: z.string(), - smtp_port: z.number(), - email: z.string(), - password: z.string(), - sender_name: z.string(), -}; - -const FormSchema = z.object({ - to_email: z.string(), - cc_email: z.string(), - content: z.string(), - subject: z.string(), - ...EmailFormPartialSchema, -}); - -const outputList = buildOutputList(initialEmailValues.outputs); - -const EmailForm = ({ node }: INextOperatorForm) => { - const { t } = useTranslate('flow'); - const defaultValues = useFormValues(initialEmailValues, node); - - const form = useForm>({ - defaultValues, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - - - -
    - -
    -
    - ); -}; - -export default EmailForm; diff --git a/web/src/pages/data-flow/form/exesql-form/index.tsx b/web/src/pages/data-flow/form/exesql-form/index.tsx deleted file mode 100644 index 5a6cb5ba693..00000000000 --- a/web/src/pages/data-flow/form/exesql-form/index.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import NumberInput from '@/components/originui/number-input'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { ButtonLoading } from '@/components/ui/button'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useTranslate } from '@/hooks/common-hooks'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo } from 'react'; -import { useForm, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; -import { initialExeSqlValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { ExeSQLOptions } from '../../options'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; -import { FormSchema, useSubmitForm } from './use-submit-form'; - -const outputList = buildOutputList(initialExeSqlValues.outputs); - -export function ExeSQLFormWidgets({ loading }: { loading: boolean }) { - const form = useFormContext(); - const { t } = useTranslate('flow'); - - return ( - <> - ( - - {t('dbType')} - - - - - - )} - /> - ( - - {t('database')} - - - - - - )} - /> - ( - - {t('username')} - - - - - - )} - /> - ( - - {t('host')} - - - - - - )} - /> - ( - - {t('port')} - - - - - - )} - /> - ( - - {t('password')} - - - - - - )} - /> - - ( - - {t('maxRecords')} - - - - - - )} - /> - -
    - - {t('test')} - -
    - - ); -} - -function ExeSQLForm({ node }: INextOperatorForm) { - const defaultValues = useFormValues(initialExeSqlValues, node); - - const { onSubmit, loading } = useSubmitForm(); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues, - }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - -
    - -
    -
    - ); -} - -export default memo(ExeSQLForm); diff --git a/web/src/pages/data-flow/form/exesql-form/use-submit-form.ts b/web/src/pages/data-flow/form/exesql-form/use-submit-form.ts deleted file mode 100644 index 8be69c7b067..00000000000 --- a/web/src/pages/data-flow/form/exesql-form/use-submit-form.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useTestDbConnect } from '@/hooks/use-agent-request'; -import { useCallback } from 'react'; -import { z } from 'zod'; - -export const ExeSQLFormSchema = { - db_type: z.string().min(1), - database: z.string().min(1), - username: z.string().min(1), - host: z.string().min(1), - port: z.number(), - password: z.string().min(1), - max_records: z.number(), -}; - -export const FormSchema = z.object({ - sql: z.string().optional(), - ...ExeSQLFormSchema, -}); - -export function useSubmitForm() { - const { testDbConnect, loading } = useTestDbConnect(); - - const onSubmit = useCallback( - async (data: z.infer) => { - testDbConnect(data); - }, - [testDbConnect], - ); - - return { loading, onSubmit }; -} diff --git a/web/src/pages/data-flow/form/extractor-form/index.tsx b/web/src/pages/data-flow/form/extractor-form/index.tsx new file mode 100644 index 00000000000..cb0abc877cb --- /dev/null +++ b/web/src/pages/data-flow/form/extractor-form/index.tsx @@ -0,0 +1,102 @@ +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import { LargeModelFormField } from '@/components/large-model-form-field'; +import { LlmSettingSchema } from '@/components/llm-setting-items/next'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Form } from '@/components/ui/form'; +import { PromptEditor } from '@/pages/agent/form/components/prompt-editor'; +import { buildOptions } from '@/utils/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { + ContextGeneratorFieldName, + initialExtractorValues, +} from '../../constant'; +import { useBuildNodeOutputOptions } from '../../hooks/use-build-options'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { FormWrapper } from '../components/form-wrapper'; +import { useSwitchPrompt } from './use-switch-prompt'; + +export const FormSchema = z.object({ + field_name: z.string(), + sys_prompt: z.string(), + prompts: z.string().optional(), + ...LlmSettingSchema, +}); + +export type ExtractorFormSchemaType = z.infer; + +const ExtractorForm = ({ node }: INextOperatorForm) => { + const defaultValues = useFormValues(initialExtractorValues, node); + const { t } = useTranslation(); + + const form = useForm({ + defaultValues, + resolver: zodResolver(FormSchema), + // mode: 'onChange', + }); + + const promptOptions = useBuildNodeOutputOptions(node?.id); + + const options = buildOptions(ContextGeneratorFieldName, t, 'dataflow'); + + const { + handleFieldNameChange, + confirmSwitch, + hideModal, + visible, + cancelSwitch, + } = useSwitchPrompt(form); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + {(field) => ( + { + field.onChange(value); + handleFieldNameChange(value); + }} + value={field.value} + placeholder={t('dataFlowPlaceholder')} + options={options} + > + )} + + + + + + + + + {visible && ( + + )} +
    + ); +}; + +export default memo(ExtractorForm); diff --git a/web/src/pages/data-flow/form/extractor-form/use-switch-prompt.ts b/web/src/pages/data-flow/form/extractor-form/use-switch-prompt.ts new file mode 100644 index 00000000000..4efb2c4727e --- /dev/null +++ b/web/src/pages/data-flow/form/extractor-form/use-switch-prompt.ts @@ -0,0 +1,69 @@ +import { LlmSettingSchema } from '@/components/llm-setting-items/next'; +import { useSetModalState } from '@/hooks/common-hooks'; +import { useCallback, useRef } from 'react'; +import { UseFormReturn } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +export const FormSchema = z.object({ + field_name: z.string(), + sys_prompt: z.string(), + prompts: z.string().optional(), + ...LlmSettingSchema, +}); + +export type ExtractorFormSchemaType = z.infer; + +export function useSwitchPrompt(form: UseFormReturn) { + const { visible, showModal, hideModal } = useSetModalState(); + const { t } = useTranslation(); + const previousFieldNames = useRef([form.getValues('field_name')]); + + const setPromptValue = useCallback( + (field: keyof ExtractorFormSchemaType, key: string, value: string) => { + form.setValue(field, t(`dataflow.prompts.${key}.${value}`), { + shouldDirty: true, + shouldValidate: true, + }); + }, + [form, t], + ); + + const handleFieldNameChange = useCallback( + (value: string) => { + if (value) { + const names = previousFieldNames.current; + if (names.length > 1) { + names.shift(); + } + names.push(value); + showModal(); + } + }, + [showModal], + ); + + const confirmSwitch = useCallback(() => { + const value = form.getValues('field_name'); + setPromptValue('sys_prompt', 'system', value); + setPromptValue('prompts', 'user', value); + }, [form, setPromptValue]); + + const cancelSwitch = useCallback(() => { + const previousValue = previousFieldNames.current.at(-2); + if (previousValue) { + form.setValue('field_name', previousValue, { + shouldDirty: true, + shouldValidate: true, + }); + } + }, [form]); + + return { + handleFieldNameChange, + confirmSwitch, + hideModal, + visible, + cancelSwitch, + }; +} diff --git a/web/src/pages/data-flow/form/hierarchical-merger-form/index.tsx b/web/src/pages/data-flow/form/hierarchical-merger-form/index.tsx new file mode 100644 index 00000000000..6ce32fd6b04 --- /dev/null +++ b/web/src/pages/data-flow/form/hierarchical-merger-form/index.tsx @@ -0,0 +1,188 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { BlockButton, Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader } from '@/components/ui/card'; +import { Form, FormLabel } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Plus, Trash2 } from 'lucide-react'; +import { memo } from 'react'; +import { useFieldArray, useForm, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { Hierarchy, initialHierarchicalMergerValues } from '../../constant'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; + +const outputList = buildOutputList(initialHierarchicalMergerValues.outputs); + +const HierarchyOptions = [ + { label: 'H1', value: Hierarchy.H1 }, + { label: 'H2', value: Hierarchy.H2 }, + { label: 'H3', value: Hierarchy.H3 }, + { label: 'H4', value: Hierarchy.H4 }, + { label: 'H5', value: Hierarchy.H5 }, +]; + +export const FormSchema = z.object({ + hierarchy: z.string(), + levels: z.array( + z.object({ + expressions: z.array( + z.object({ + expression: z.string().refine( + (val) => { + try { + // Try converting the string to a RegExp + new RegExp(val); + return true; + } catch { + return false; + } + }, + { + message: 'Must be a valid regular expression string', + }, + ), + }), + ), + }), + ), +}); + +export type HierarchicalMergerFormSchemaType = z.infer; + +type RegularExpressionsProps = { + index: number; + parentName: string; + removeParent: (index: number) => void; + isLatest: boolean; +}; + +export function RegularExpressions({ + index, + parentName, + isLatest, + removeParent, +}: RegularExpressionsProps) { + const { t } = useTranslation(); + const form = useFormContext(); + + const name = `${parentName}.${index}.expressions`; + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( + + + H{index + 1} + {isLatest && ( + + )} + + + + {t('dataflow.regularExpressions')} + +
    + {fields.map((field, index) => ( +
    +
    + + + +
    + {index === 0 ? ( + + ) : ( + + )} +
    + ))} +
    +
    +
    + ); +} + +const HierarchicalMergerForm = ({ node }: INextOperatorForm) => { + const { t } = useTranslation(); + const defaultValues = useFormValues(initialHierarchicalMergerValues, node); + + const form = useForm({ + defaultValues, + resolver: zodResolver(FormSchema), + mode: 'onChange', + }); + + const name = 'levels'; + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + useWatchFormChange(node?.id, form); + + return ( +
    + + + + + {fields.map((field, index) => ( +
    +
    + +
    +
    + ))} + {fields.length < 5 && ( + append({ expressions: [{ expression: '' }] })} + > + {t('common.add')} + + )} +
    +
    + +
    +
    + ); +}; + +export default memo(HierarchicalMergerForm); diff --git a/web/src/pages/data-flow/form/invoke-form/hooks.ts b/web/src/pages/data-flow/form/invoke-form/hooks.ts deleted file mode 100644 index 951cd42ae72..00000000000 --- a/web/src/pages/data-flow/form/invoke-form/hooks.ts +++ /dev/null @@ -1,97 +0,0 @@ -import get from 'lodash/get'; -import { - ChangeEventHandler, - MouseEventHandler, - useCallback, - useMemo, -} from 'react'; -import { v4 as uuid } from 'uuid'; -import { IGenerateParameter, IInvokeVariable } from '../../interface'; -import useGraphStore from '../../store'; - -export const useHandleOperateParameters = (nodeId: string) => { - const { getNode, updateNodeForm } = useGraphStore((state) => state); - const node = getNode(nodeId); - const dataSource: IGenerateParameter[] = useMemo( - () => get(node, 'data.form.variables', []) as IGenerateParameter[], - [node], - ); - - const changeValue = useCallback( - (row: IInvokeVariable, field: string, value: string) => { - const newData = [...dataSource]; - const index = newData.findIndex((item) => row.id === item.id); - const item = newData[index]; - newData.splice(index, 1, { - ...item, - [field]: value, - }); - - updateNodeForm(nodeId, { variables: newData }); - }, - [dataSource, nodeId, updateNodeForm], - ); - - const handleComponentIdChange = useCallback( - (row: IInvokeVariable) => (value: string) => { - changeValue(row, 'component_id', value); - }, - [changeValue], - ); - - const handleValueChange = useCallback( - (row: IInvokeVariable): ChangeEventHandler => - (e) => { - changeValue(row, 'value', e.target.value); - }, - [changeValue], - ); - - const handleRemove = useCallback( - (id?: string) => () => { - const newData = dataSource.filter((item) => item.id !== id); - updateNodeForm(nodeId, { variables: newData }); - }, - [updateNodeForm, nodeId, dataSource], - ); - - const handleAdd: MouseEventHandler = useCallback( - (e) => { - e.preventDefault(); - e.stopPropagation(); - updateNodeForm(nodeId, { - variables: [ - ...dataSource, - { - id: uuid(), - key: '', - component_id: undefined, - value: '', - }, - ], - }); - }, - [dataSource, nodeId, updateNodeForm], - ); - - const handleSave = (row: IGenerateParameter) => { - const newData = [...dataSource]; - const index = newData.findIndex((item) => row.id === item.id); - const item = newData[index]; - newData.splice(index, 1, { - ...item, - ...row, - }); - - updateNodeForm(nodeId, { variables: newData }); - }; - - return { - handleAdd, - handleRemove, - handleComponentIdChange, - handleValueChange, - handleSave, - dataSource, - }; -}; diff --git a/web/src/pages/data-flow/form/invoke-form/index.tsx b/web/src/pages/data-flow/form/invoke-form/index.tsx deleted file mode 100644 index 3d67ec03046..00000000000 --- a/web/src/pages/data-flow/form/invoke-form/index.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { Collapse } from '@/components/collapse'; -import { FormContainer } from '@/components/form-container'; -import NumberInput from '@/components/originui/number-input'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { Button } from '@/components/ui/button'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { Switch } from '@/components/ui/switch'; -import { zodResolver } from '@hookform/resolvers/zod'; -import Editor, { loader } from '@monaco-editor/react'; -import { Plus } from 'lucide-react'; -import { memo } from 'react'; -import { useForm, useWatch } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { initialInvokeValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; -import { INextOperatorForm } from '../../interface'; -import { buildOutputList } from '../../utils/build-output-list'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { FormSchema, FormSchemaType } from './schema'; -import { useEditVariableRecord } from './use-edit-variable'; -import { VariableDialog } from './variable-dialog'; -import { VariableTable } from './variable-table'; - -loader.config({ paths: { vs: '/vs' } }); - -enum Method { - GET = 'GET', - POST = 'POST', - PUT = 'PUT', -} - -const MethodOptions = [Method.GET, Method.POST, Method.PUT].map((x) => ({ - label: x, - value: x, -})); - -interface TimeoutInputProps { - value?: number; - onChange?: (value: number | null) => void; -} - -const TimeoutInput = ({ value, onChange }: TimeoutInputProps) => { - const { t } = useTranslation(); - return ( -
    - {t('flow.seconds')} -
    - ); -}; - -const outputList = buildOutputList(initialInvokeValues.outputs); - -function InvokeForm({ node }: INextOperatorForm) { - const { t } = useTranslation(); - const defaultValues = useFormValues(initialInvokeValues, node); - - const form = useForm({ - defaultValues, - resolver: zodResolver(FormSchema), - mode: 'onChange', - }); - - const { - visible, - hideModal, - showModal, - ok, - currentRecord, - otherThanCurrentQuery, - handleDeleteRecord, - } = useEditVariableRecord({ - form, - node, - }); - - const variables = useWatch({ control: form.control, name: 'variables' }); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - ( - - {t('flow.url')} - - - - - - )} - /> - ( - - {t('flow.method')} - - - - - - )} - /> - ( - - {t('flow.timeout')} - - - - - - )} - /> - ( - - {t('flow.headers')} - - - - - - )} - /> - ( - - {t('flow.proxy')} - - - - - - )} - /> - ( - - - {t('flow.cleanHtml')} - - - - - - - )} - /> - {/* Create a hidden field to make Form instance record this */} -
    } - /> -
    - {t('flow.parameter')}} - rightContent={ - - } - > - - - {visible && ( - - )} -
    -
    - -
    -
    - ); -} - -export default memo(InvokeForm); diff --git a/web/src/pages/data-flow/form/invoke-form/schema.ts b/web/src/pages/data-flow/form/invoke-form/schema.ts deleted file mode 100644 index a3b11aff268..00000000000 --- a/web/src/pages/data-flow/form/invoke-form/schema.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { z } from 'zod'; - -export const VariableFormSchema = z.object({ - key: z.string(), - ref: z.string(), - value: z.string(), -}); - -export const FormSchema = z.object({ - url: z.string().url(), - method: z.string(), - timeout: z.number(), - headers: z.string(), - proxy: z.string().url(), - clean_html: z.boolean(), - variables: z.array(VariableFormSchema), -}); - -export type FormSchemaType = z.infer; - -export type VariableFormSchemaType = z.infer; diff --git a/web/src/pages/data-flow/form/invoke-form/use-edit-variable.ts b/web/src/pages/data-flow/form/invoke-form/use-edit-variable.ts deleted file mode 100644 index 40b371894ff..00000000000 --- a/web/src/pages/data-flow/form/invoke-form/use-edit-variable.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { useSetModalState } from '@/hooks/common-hooks'; -import { useSetSelectedRecord } from '@/hooks/logic-hooks'; -import { useCallback, useMemo, useState } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import { INextOperatorForm } from '../../interface'; -import { FormSchemaType, VariableFormSchemaType } from './schema'; - -export const useEditVariableRecord = ({ - form, -}: INextOperatorForm & { form: UseFormReturn }) => { - const { setRecord, currentRecord } = - useSetSelectedRecord(); - - const { visible, hideModal, showModal } = useSetModalState(); - const [index, setIndex] = useState(-1); - const variables = useWatch({ - control: form.control, - name: 'variables', - }); - - const otherThanCurrentQuery = useMemo(() => { - return variables.filter((item, idx) => idx !== index); - }, [index, variables]); - - const handleEditRecord = useCallback( - (record: VariableFormSchemaType) => { - const variables = form?.getValues('variables') || []; - - const nextVaribales = - index > -1 - ? variables.toSpliced(index, 1, record) - : [...variables, record]; - - form.setValue('variables', nextVaribales); - - hideModal(); - }, - [form, hideModal, index], - ); - - const handleShowModal = useCallback( - (idx?: number, record?: VariableFormSchemaType) => { - setIndex(idx ?? -1); - setRecord(record ?? ({} as VariableFormSchemaType)); - showModal(); - }, - [setRecord, showModal], - ); - - const handleDeleteRecord = useCallback( - (idx: number) => { - const variables = form?.getValues('variables') || []; - const nextVariables = variables.filter((item, index) => index !== idx); - - form.setValue('variables', nextVariables); - }, - [form], - ); - - return { - ok: handleEditRecord, - currentRecord, - setRecord, - visible, - hideModal, - showModal: handleShowModal, - otherThanCurrentQuery, - handleDeleteRecord, - }; -}; diff --git a/web/src/pages/data-flow/form/invoke-form/variable-dialog.tsx b/web/src/pages/data-flow/form/invoke-form/variable-dialog.tsx deleted file mode 100644 index 03c4d83b037..00000000000 --- a/web/src/pages/data-flow/form/invoke-form/variable-dialog.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { IModalProps } from '@/interfaces/common'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { isEmpty } from 'lodash'; -import { useEffect } from 'react'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; -import { QueryVariable } from '../components/query-variable'; -import { VariableFormSchemaType } from './schema'; - -type ModalFormProps = { - initialValue: VariableFormSchemaType; - otherThanCurrentQuery: VariableFormSchemaType[]; - submit(values: any): void; -}; - -const FormId = 'BeginParameterForm'; - -function VariableForm({ - initialValue, - otherThanCurrentQuery, - submit, -}: ModalFormProps) { - const { t } = useTranslation(); - const FormSchema = z.object({ - key: z - .string() - .trim() - .min(1) - .refine( - (value) => - !value || !otherThanCurrentQuery.some((x) => x.key === value), - { message: 'The key cannot be repeated!' }, - ), - ref: z.string(), - value: z.string(), - }); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - mode: 'onChange', - defaultValues: { - key: '', - value: '', - ref: '', - }, - }); - - useEffect(() => { - if (!isEmpty(initialValue)) { - form.reset(initialValue); - } - }, [form, initialValue]); - - function onSubmit(data: z.infer) { - submit(data); - } - - return ( -
    - - ( - - {t('flow.key')} - - - - - - )} - /> - - ( - - {t('flow.value')} - - - - - - )} - /> - - - ); -} - -export function VariableDialog({ - initialValue, - hideModal, - otherThanCurrentQuery, - submit, -}: ModalFormProps & IModalProps) { - const { t } = useTranslation(); - - return ( - - - - {t('flow.variableSettings')} - - - - - - - - ); -} diff --git a/web/src/pages/data-flow/form/invoke-form/variable-table.tsx b/web/src/pages/data-flow/form/invoke-form/variable-table.tsx deleted file mode 100644 index 33a74767002..00000000000 --- a/web/src/pages/data-flow/form/invoke-form/variable-table.tsx +++ /dev/null @@ -1,199 +0,0 @@ -'use client'; - -import { - ColumnDef, - ColumnFiltersState, - SortingState, - VisibilityState, - flexRender, - getCoreRowModel, - getFilteredRowModel, - getPaginationRowModel, - getSortedRowModel, - useReactTable, -} from '@tanstack/react-table'; -import { Pencil, Trash2 } from 'lucide-react'; -import * as React from 'react'; - -import { TableEmpty } from '@/components/table-skeleton'; -import { Button } from '@/components/ui/button'; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table'; -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/components/ui/tooltip'; -import { cn } from '@/lib/utils'; -import { useTranslation } from 'react-i18next'; -import { useGetVariableLabelByValue } from '../../hooks/use-get-begin-query'; -import { VariableFormSchemaType } from './schema'; - -interface IProps { - data: VariableFormSchemaType[]; - deleteRecord(index: number): void; - showModal(index: number, record: VariableFormSchemaType): void; - nodeId?: string; -} - -export function VariableTable({ - data = [], - deleteRecord, - showModal, - nodeId, -}: IProps) { - const { t } = useTranslation(); - const getLabel = useGetVariableLabelByValue(nodeId!); - - const [sorting, setSorting] = React.useState([]); - const [columnFilters, setColumnFilters] = React.useState( - [], - ); - const [columnVisibility, setColumnVisibility] = - React.useState({}); - - const columns: ColumnDef[] = [ - { - accessorKey: 'key', - header: t('flow.key'), - meta: { cellClassName: 'max-w-30' }, - cell: ({ row }) => { - const key: string = row.getValue('key'); - return ( - - -
    {key}
    -
    - -

    {key}

    -
    -
    - ); - }, - }, - { - accessorKey: 'ref', - header: t('flow.ref'), - meta: { cellClassName: 'max-w-30' }, - cell: ({ row }) => { - const ref: string = row.getValue('ref'); - const label = getLabel(ref); - return ( - - -
    {label}
    -
    - -

    {label}

    -
    -
    - ); - }, - }, - { - accessorKey: 'value', - header: t('flow.value'), - cell: ({ row }) =>
    {row.getValue('value')}
    , - }, - { - id: 'actions', - enableHiding: false, - header: t('common.action'), - cell: ({ row }) => { - const record = row.original; - const idx = row.index; - - return ( -
    - - -
    - ); - }, - }, - ]; - - const table = useReactTable({ - data, - columns, - onSortingChange: setSorting, - onColumnFiltersChange: setColumnFilters, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), - onColumnVisibilityChange: setColumnVisibility, - state: { - sorting, - columnFilters, - columnVisibility, - }, - }); - - return ( -
    -
    - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - )) - ) : ( - - )} - -
    -
    -
    - ); -} diff --git a/web/src/pages/data-flow/form/iteration-form/dynamic-output.tsx b/web/src/pages/data-flow/form/iteration-form/dynamic-output.tsx deleted file mode 100644 index c31be8fd062..00000000000 --- a/web/src/pages/data-flow/form/iteration-form/dynamic-output.tsx +++ /dev/null @@ -1,128 +0,0 @@ -'use client'; - -import { FormContainer } from '@/components/form-container'; -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { BlockButton, Button } from '@/components/ui/button'; -import { - FormControl, - FormField, - FormItem, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { Separator } from '@/components/ui/separator'; -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { t } from 'i18next'; -import { X } from 'lucide-react'; -import { ReactNode, useCallback, useMemo } from 'react'; -import { useFieldArray, useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useBuildSubNodeOutputOptions } from './use-build-options'; - -interface IProps { - node?: RAGFlowNodeType; -} - -export function DynamicOutputForm({ node }: IProps) { - const { t } = useTranslation(); - const form = useFormContext(); - const options = useBuildSubNodeOutputOptions(node?.id); - const name = 'outputs'; - - const flatOptions = useMemo(() => { - return options.reduce<{ label: string; value: string; type: string }[]>( - (pre, cur) => { - pre.push(...cur.options); - return pre; - }, - [], - ); - }, [options]); - - const findType = useCallback( - (val: string) => { - const type = flatOptions.find((x) => x.value === val)?.type; - if (type) { - return `Array<${type}>`; - } - }, - [flatOptions], - ); - - const { fields, remove, append } = useFieldArray({ - name: name, - control: form.control, - }); - - return ( -
    - {fields.map((field, index) => { - const nameField = `${name}.${index}.name`; - const typeField = `${name}.${index}.type`; - return ( -
    - ( - - - - - - - )} - /> - - ( - - - { - form.setValue(typeField, findType(val)); - field.onChange(val); - }} - > - - - - )} - /> -
    } - /> - -
    - ); - })} - append({ name: '', ref: undefined })}> - {t('common.add')} - -
    - ); -} - -export function VariableTitle({ title }: { title: ReactNode }) { - return
    {title}
    ; -} - -export function DynamicOutput({ node }: IProps) { - return ( - - - - - ); -} diff --git a/web/src/pages/data-flow/form/iteration-form/index.tsx b/web/src/pages/data-flow/form/iteration-form/index.tsx deleted file mode 100644 index c70b764fb00..00000000000 --- a/web/src/pages/data-flow/form/iteration-form/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { Form } from '@/components/ui/form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useMemo } from 'react'; -import { useForm, useWatch } from 'react-hook-form'; -import { z } from 'zod'; -import { VariableType } from '../../constant'; -import { INextOperatorForm } from '../../interface'; -import { FormWrapper } from '../components/form-wrapper'; -import { Output } from '../components/output'; -import { QueryVariable } from '../components/query-variable'; -import { DynamicOutput } from './dynamic-output'; -import { OutputArray } from './interface'; -import { useValues } from './use-values'; -import { useWatchFormChange } from './use-watch-form-change'; - -const FormSchema = z.object({ - query: z.string().optional(), - outputs: z.array(z.object({ name: z.string(), value: z.any() })).optional(), -}); - -function IterationForm({ node }: INextOperatorForm) { - const defaultValues = useValues(node); - - const form = useForm({ - defaultValues: defaultValues, - resolver: zodResolver(FormSchema), - }); - - const outputs: OutputArray = useWatch({ - control: form?.control, - name: 'outputs', - }); - - const outputList = useMemo(() => { - return outputs.map((x) => ({ title: x.name, type: x?.type })); - }, [outputs]); - - useWatchFormChange(node?.id, form); - - return ( -
    - - - - - - - -
    - ); -} - -export default memo(IterationForm); diff --git a/web/src/pages/data-flow/form/iteration-form/interface.ts b/web/src/pages/data-flow/form/iteration-form/interface.ts deleted file mode 100644 index 25f22aab047..00000000000 --- a/web/src/pages/data-flow/form/iteration-form/interface.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type OutputArray = Array<{ name: string; ref: string; type?: string }>; -export type OutputObject = Record; diff --git a/web/src/pages/data-flow/form/iteration-form/use-build-options.ts b/web/src/pages/data-flow/form/iteration-form/use-build-options.ts deleted file mode 100644 index 3439000d4e8..00000000000 --- a/web/src/pages/data-flow/form/iteration-form/use-build-options.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { isEmpty } from 'lodash'; -import { useMemo } from 'react'; -import { Operator } from '../../constant'; -import { buildOutputOptions } from '../../hooks/use-get-begin-query'; -import useGraphStore from '../../store'; - -export function useBuildSubNodeOutputOptions(nodeId?: string) { - const { nodes } = useGraphStore((state) => state); - - const nodeOutputOptions = useMemo(() => { - if (!nodeId) { - return []; - } - - const subNodeWithOutputList = nodes.filter( - (x) => - x.parentId === nodeId && - x.data.label !== Operator.IterationStart && - !isEmpty(x.data?.form?.outputs), - ); - - return subNodeWithOutputList.map((x) => ({ - label: x.data.name, - value: x.id, - title: x.data.name, - options: buildOutputOptions(x.data.form.outputs, x.id), - })); - }, [nodeId, nodes]); - - return nodeOutputOptions; -} diff --git a/web/src/pages/data-flow/form/iteration-form/use-values.ts b/web/src/pages/data-flow/form/iteration-form/use-values.ts deleted file mode 100644 index 29cd0632416..00000000000 --- a/web/src/pages/data-flow/form/iteration-form/use-values.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { isEmpty } from 'lodash'; -import { useMemo } from 'react'; -import { initialIterationValues } from '../../constant'; -import { OutputObject } from './interface'; - -function convertToArray(outputObject: OutputObject) { - return Object.entries(outputObject).map(([key, value]) => ({ - name: key, - ref: value.ref, - type: value.type, - })); -} - -export function useValues(node?: RAGFlowNodeType) { - const values = useMemo(() => { - const formData = node?.data?.form; - - if (isEmpty(formData)) { - return { ...initialIterationValues, outputs: [] }; - } - - return { ...formData, outputs: convertToArray(formData.outputs) }; - }, [node?.data?.form]); - - return values; -} diff --git a/web/src/pages/data-flow/form/iteration-form/use-watch-form-change.ts b/web/src/pages/data-flow/form/iteration-form/use-watch-form-change.ts deleted file mode 100644 index 4a780667ede..00000000000 --- a/web/src/pages/data-flow/form/iteration-form/use-watch-form-change.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import useGraphStore from '../../store'; -import { OutputArray, OutputObject } from './interface'; - -export function transferToObject(list: OutputArray) { - return list.reduce((pre, cur) => { - pre[cur.name] = { ref: cur.ref, type: cur.type }; - return pre; - }, {}); -} - -export function useWatchFormChange(id?: string, form?: UseFormReturn) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - // Manually triggered form updates are synchronized to the canvas - if (id && form?.formState.isDirty) { - values = form?.getValues(); - console.log('🚀 ~ useEffect ~ values:', values); - let nextValues: any = { - ...values, - outputs: transferToObject(values.outputs), - }; - - updateNodeForm(id, nextValues); - } - }, [form?.formState.isDirty, id, updateNodeForm, values]); -} diff --git a/web/src/pages/data-flow/form/iteration-start-from/index.tsx b/web/src/pages/data-flow/form/iteration-start-from/index.tsx deleted file mode 100644 index ba58b92d1eb..00000000000 --- a/web/src/pages/data-flow/form/iteration-start-from/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Output, OutputType } from '@/pages/agent/form/components/output'; -import { memo } from 'react'; -import { initialIterationStartValues } from '../../constant'; - -const outputs = initialIterationStartValues.outputs; - -const outputList = Object.entries(outputs).reduce( - (pre, [key, value]) => { - pre.push({ title: key, type: value.type }); - - return pre; - }, - [], -); -function IterationStartForm() { - return ( -
    - -
    - ); -} - -export default memo(IterationStartForm); diff --git a/web/src/pages/data-flow/form/keyword-extract-form/index.tsx b/web/src/pages/data-flow/form/keyword-extract-form/index.tsx deleted file mode 100644 index bda5d44f510..00000000000 --- a/web/src/pages/data-flow/form/keyword-extract-form/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { NextLLMSelect } from '@/components/llm-select/next'; -import { TopNFormField } from '@/components/top-n-item'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { useTranslation } from 'react-i18next'; -import { INextOperatorForm } from '../../interface'; -import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; - -const KeywordExtractForm = ({ form, node }: INextOperatorForm) => { - const { t } = useTranslation(); - - return ( -
    - { - e.preventDefault(); - }} - > - - ( - - - {t('chat.model')} - - - - - - - )} - /> - - - - ); -}; - -export default KeywordExtractForm; diff --git a/web/src/pages/data-flow/form/message-form/index.tsx b/web/src/pages/data-flow/form/message-form/index.tsx deleted file mode 100644 index 05c831a84aa..00000000000 --- a/web/src/pages/data-flow/form/message-form/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { FormContainer } from '@/components/form-container'; -import { BlockButton, Button } from '@/components/ui/button'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { X } from 'lucide-react'; -import { memo } from 'react'; -import { useFieldArray, useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; -import { INextOperatorForm } from '../../interface'; -import { FormWrapper } from '../components/form-wrapper'; -import { PromptEditor } from '../components/prompt-editor'; -import { useValues } from './use-values'; -import { useWatchFormChange } from './use-watch-change'; - -function MessageForm({ node }: INextOperatorForm) { - const { t } = useTranslation(); - - const values = useValues(node); - - const FormSchema = z.object({ - content: z - .array( - z.object({ - value: z.string(), - }), - ) - .optional(), - }); - - const form = useForm({ - defaultValues: values, - resolver: zodResolver(FormSchema), - }); - - useWatchFormChange(node?.id, form); - - const { fields, append, remove } = useFieldArray({ - name: 'content', - control: form.control, - }); - - return ( -
    - - - - {t('flow.msg')} -
    - {fields.map((field, index) => ( -
    - ( - - - {/* */} - - - - )} - /> - {fields.length > 1 && ( - - )} -
    - ))} - - append({ value: '' })} // "" will cause the inability to add, refer to: https://github.com/orgs/react-hook-form/discussions/8485#discussioncomment-2961861 - > - {t('flow.addMessage')} - -
    - -
    -
    -
    -
    - ); -} - -export default memo(MessageForm); diff --git a/web/src/pages/data-flow/form/message-form/use-values.ts b/web/src/pages/data-flow/form/message-form/use-values.ts deleted file mode 100644 index 6a90881becf..00000000000 --- a/web/src/pages/data-flow/form/message-form/use-values.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { isEmpty } from 'lodash'; -import { useMemo } from 'react'; -import { initialMessageValues } from '../../constant'; -import { convertToObjectArray } from '../../utils'; - -export function useValues(node?: RAGFlowNodeType) { - const values = useMemo(() => { - const formData = node?.data?.form; - - if (isEmpty(formData)) { - return initialMessageValues; - } - - return { - ...formData, - content: convertToObjectArray(formData.content), - }; - }, [node]); - - return values; -} diff --git a/web/src/pages/data-flow/form/message-form/use-watch-change.ts b/web/src/pages/data-flow/form/message-form/use-watch-change.ts deleted file mode 100644 index 10c35c653c1..00000000000 --- a/web/src/pages/data-flow/form/message-form/use-watch-change.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import useGraphStore from '../../store'; -import { convertToStringArray } from '../../utils'; - -export function useWatchFormChange(id?: string, form?: UseFormReturn) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - // Manually triggered form updates are synchronized to the canvas - if (id && form?.formState.isDirty) { - values = form?.getValues(); - let nextValues: any = values; - - nextValues = { - ...values, - content: convertToStringArray(values.content), - }; - - updateNodeForm(id, nextValues); - } - }, [form?.formState.isDirty, id, updateNodeForm, values]); -} diff --git a/web/src/pages/data-flow/form/parser-form/common-form-fields.tsx b/web/src/pages/data-flow/form/parser-form/common-form-fields.tsx new file mode 100644 index 00000000000..46809ad2c0f --- /dev/null +++ b/web/src/pages/data-flow/form/parser-form/common-form-fields.tsx @@ -0,0 +1,85 @@ +import { crossLanguageOptions } from '@/components/cross-language-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { LLMFormField } from '@/components/llm-setting-items/llm-form-field'; +import { + SelectWithSearch, + SelectWithSearchFlagOptionType, +} from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { buildOptions } from '@/utils/form'; +import { useTranslation } from 'react-i18next'; +import { FileType, OutputFormatMap } from '../../constant'; +import { CommonProps } from './interface'; +import { buildFieldNameWithPrefix } from './utils'; + +function buildOutputOptionsFormatMap() { + return Object.entries(OutputFormatMap).reduce< + Record + >((pre, [key, value]) => { + pre[key] = buildOptions(value); + return pre; + }, {}); +} + +export type OutputFormatFormFieldProps = CommonProps & { + fileType: FileType; +}; + +export function OutputFormatFormField({ + prefix, + fileType, +}: OutputFormatFormFieldProps) { + const { t } = useTranslation(); + return ( + + + + ); +} + +export function ParserMethodFormField({ + prefix, + optionsWithoutLLM, +}: CommonProps & { optionsWithoutLLM?: { value: string; label: string }[] }) { + const { t } = useTranslation(); + return ( + + ); +} + +export function LargeModelFormField({ prefix }: CommonProps) { + return ( + + ); +} + +export function LanguageFormField({ prefix }: CommonProps) { + const { t } = useTranslation(); + + return ( + + {(field) => ( + + )} + + ); +} diff --git a/web/src/pages/data-flow/form/parser-form/email-form-fields.tsx b/web/src/pages/data-flow/form/parser-form/email-form-fields.tsx new file mode 100644 index 00000000000..640c271f9a2 --- /dev/null +++ b/web/src/pages/data-flow/form/parser-form/email-form-fields.tsx @@ -0,0 +1,30 @@ +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { MultiSelect } from '@/components/ui/multi-select'; +import { buildOptions } from '@/utils/form'; +import { useTranslation } from 'react-i18next'; +import { ParserFields } from '../../constant'; +import { CommonProps } from './interface'; +import { buildFieldNameWithPrefix } from './utils'; + +const options = buildOptions(ParserFields); + +export function EmailFormFields({ prefix }: CommonProps) { + const { t } = useTranslation(); + return ( + <> + + {(field) => ( + + )} + + + ); +} diff --git a/web/src/pages/data-flow/form/parser-form/image-form-fields.tsx b/web/src/pages/data-flow/form/parser-form/image-form-fields.tsx new file mode 100644 index 00000000000..4cff99ea770 --- /dev/null +++ b/web/src/pages/data-flow/form/parser-form/image-form-fields.tsx @@ -0,0 +1,57 @@ +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Textarea } from '@/components/ui/textarea'; +import { buildOptions } from '@/utils/form'; +import { isEmpty } from 'lodash'; +import { useEffect, useMemo } from 'react'; +import { useFormContext, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { ImageParseMethod } from '../../constant'; +import { LanguageFormField, ParserMethodFormField } from './common-form-fields'; +import { CommonProps } from './interface'; +import { useSetInitialLanguage } from './use-set-initial-language'; +import { buildFieldNameWithPrefix } from './utils'; + +const options = buildOptions(ImageParseMethod); + +export function ImageFormFields({ prefix }: CommonProps) { + const { t } = useTranslation(); + const form = useFormContext(); + const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix); + + const parseMethod = useWatch({ + name: parseMethodName, + }); + + const languageShown = useMemo(() => { + return !isEmpty(parseMethod) && parseMethod !== ImageParseMethod.OCR; + }, [parseMethod]); + + useEffect(() => { + if (isEmpty(form.getValues(parseMethodName))) { + form.setValue(parseMethodName, ImageParseMethod.OCR, { + shouldValidate: true, + shouldDirty: true, + }); + } + }, [form, parseMethodName]); + + useSetInitialLanguage({ prefix, languageShown }); + + return ( + <> + + {languageShown && } + {languageShown && ( + + - - - - )} - /> - - {/* Create a hidden field to make Form instance record this */} -
    } - /> - - {t('flow.input')} - - - } - rightContent={ - - } - > - - - - {visible && ( - - )} - - - - ); -} - -export default memo(UserFillUpForm); diff --git a/web/src/pages/data-flow/form/user-fill-up-form/use-values.ts b/web/src/pages/data-flow/form/user-fill-up-form/use-values.ts deleted file mode 100644 index 0af1c78c35b..00000000000 --- a/web/src/pages/data-flow/form/user-fill-up-form/use-values.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { isEmpty } from 'lodash'; -import { useMemo } from 'react'; -import { initialUserFillUpValues } from '../../constant'; -import { buildBeginInputListFromObject } from '../begin-form/utils'; - -export function useValues(node?: RAGFlowNodeType) { - const values = useMemo(() => { - const formData = node?.data?.form; - - if (isEmpty(formData)) { - return initialUserFillUpValues; - } - - const inputs = buildBeginInputListFromObject(formData?.inputs); - - return { ...(formData || {}), inputs }; - }, [node?.data?.form]); - - return values; -} diff --git a/web/src/pages/data-flow/form/user-fill-up-form/use-watch-change.ts b/web/src/pages/data-flow/form/user-fill-up-form/use-watch-change.ts deleted file mode 100644 index bdf4b9a9195..00000000000 --- a/web/src/pages/data-flow/form/user-fill-up-form/use-watch-change.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { omit } from 'lodash'; -import { useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import { BeginQuery } from '../../interface'; -import useGraphStore from '../../store'; - -function transferInputsArrayToObject(inputs: BeginQuery[] = []) { - return inputs.reduce>>((pre, cur) => { - pre[cur.key] = omit(cur, 'key'); - - return pre; - }, {}); -} - -export function useWatchFormChange(id?: string, form?: UseFormReturn) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - // TODO: This should only be executed when the form changes - if (id) { - values = form?.getValues() || {}; - - const inputs = transferInputsArrayToObject(values.inputs); - - const nextValues = { - ...values, - inputs, - outputs: inputs, - }; - - updateNodeForm(id, nextValues); - } - }, [form?.formState.isDirty, id, updateNodeForm, values]); -} diff --git a/web/src/pages/data-flow/hooks/use-add-node.ts b/web/src/pages/data-flow/hooks/use-add-node.ts index 7e0ddf5848f..2b3e23c9d6f 100644 --- a/web/src/pages/data-flow/hooks/use-add-node.ts +++ b/web/src/pages/data-flow/hooks/use-add-node.ts @@ -1,7 +1,6 @@ import { useFetchModelId } from '@/hooks/logic-hooks'; import { Connection, Node, Position, ReactFlowInstance } from '@xyflow/react'; import humanId from 'human-id'; -import { t } from 'i18next'; import { lowerFirst } from 'lodash'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -9,30 +8,13 @@ import { NodeHandleId, NodeMap, Operator, - initialAgentValues, initialBeginValues, - initialCategorizeValues, - initialChunkerValues, - initialCodeValues, - initialConcentratorValues, - initialCrawlerValues, - initialEmailValues, - initialExeSqlValues, - initialInvokeValues, - initialIterationStartValues, - initialIterationValues, - initialKeywordExtractValues, - initialMessageValues, + initialExtractorValues, + initialHierarchicalMergerValues, initialNoteValues, initialParserValues, - initialRelevantValues, - initialRetrievalValues, - initialRewriteQuestionValues, - initialStringTransformValues, - initialSwitchValues, + initialSplitterValues, initialTokenizerValues, - initialUserFillUpValues, - initialWaitingDialogueValues, } from '../constant'; import useGraphStore from '../store'; import { @@ -40,61 +22,30 @@ import { getNodeDragHandle, } from '../utils'; -function isBottomSubAgent(type: string, position: Position) { - return ( - (type === Operator.Agent && position === Position.Bottom) || - type === Operator.Tool - ); -} export const useInitializeOperatorParams = () => { const llmId = useFetchModelId(); + const { t } = useTranslation(); const initialFormValuesMap = useMemo(() => { return { [Operator.Begin]: initialBeginValues, - [Operator.Retrieval]: initialRetrievalValues, - [Operator.Categorize]: { ...initialCategorizeValues, llm_id: llmId }, - [Operator.Relevant]: { ...initialRelevantValues, llm_id: llmId }, - [Operator.RewriteQuestion]: { - ...initialRewriteQuestionValues, - llm_id: llmId, - }, - [Operator.Message]: initialMessageValues, - [Operator.KeywordExtract]: { - ...initialKeywordExtractValues, - llm_id: llmId, - }, - [Operator.ExeSQL]: initialExeSqlValues, - [Operator.Switch]: initialSwitchValues, - [Operator.Concentrator]: initialConcentratorValues, [Operator.Note]: initialNoteValues, - [Operator.Crawler]: initialCrawlerValues, - [Operator.Invoke]: initialInvokeValues, - [Operator.Email]: initialEmailValues, - [Operator.Iteration]: initialIterationValues, - [Operator.IterationStart]: initialIterationStartValues, - [Operator.Code]: initialCodeValues, - [Operator.WaitingDialogue]: initialWaitingDialogueValues, - [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, - [Operator.Tool]: {}, - [Operator.UserFillUp]: initialUserFillUpValues, - [Operator.StringTransform]: initialStringTransformValues, [Operator.Parser]: initialParserValues, - [Operator.Chunker]: initialChunkerValues, [Operator.Tokenizer]: initialTokenizerValues, + [Operator.Splitter]: initialSplitterValues, + [Operator.HierarchicalMerger]: initialHierarchicalMergerValues, + [Operator.Extractor]: { + ...initialExtractorValues, + llm_id: llmId, + sys_prompt: t('dataflow.prompts.system.summary'), + prompts: t('dataflow.prompts.user.summary'), + }, }; - }, [llmId]); + }, [llmId, t]); const initializeOperatorParams = useCallback( - (operatorName: Operator, position: Position) => { + (operatorName: Operator) => { const initialValues = initialFormValuesMap[operatorName]; - if (isBottomSubAgent(operatorName, position)) { - return { - ...initialValues, - description: t('flow.descriptionMessage'), - user_prompt: t('flow.userPromptDefaultValue'), - }; - } return initialValues; }, @@ -171,95 +122,17 @@ function useAddChildEdge() { return { addChildEdge }; } -function useAddToolNode() { - const { nodes, edges, addEdge, getNode, addNode } = useGraphStore( - (state) => state, - ); - - const addToolNode = useCallback( - (newNode: Node, nodeId?: string): boolean => { - const agentNode = getNode(nodeId); - - if (agentNode) { - const childToolNodeIds = edges - .filter( - (x) => x.source === nodeId && x.sourceHandle === NodeHandleId.Tool, - ) - .map((x) => x.target); - - if ( - childToolNodeIds.length > 0 && - nodes.some((x) => x.id === childToolNodeIds[0]) - ) { - return false; - } - - newNode.position = { - x: agentNode.position.x - 82, - y: agentNode.position.y + 140, - }; - - addNode(newNode); - if (nodeId) { - addEdge({ - source: nodeId, - target: newNode.id, - sourceHandle: NodeHandleId.Tool, - targetHandle: NodeHandleId.End, - }); - } - return true; - } - return false; - }, - [addEdge, addNode, edges, getNode, nodes], - ); - - return { addToolNode }; -} - -function useResizeIterationNode() { - const { getNode, nodes, updateNode } = useGraphStore((state) => state); - - const resizeIterationNode = useCallback( - (type: string, position: Position, parentId?: string) => { - const parentNode = getNode(parentId); - if (parentNode && !isBottomSubAgent(type, position)) { - const MoveRightDistance = 310; - const childNodeList = nodes.filter((x) => x.parentId === parentId); - const maxX = Math.max(...childNodeList.map((x) => x.position.x)); - if (maxX + MoveRightDistance > parentNode.position.x) { - updateNode({ - ...parentNode, - width: (parentNode.width || 0) + MoveRightDistance, - position: { - x: parentNode.position.x + MoveRightDistance / 2, - y: parentNode.position.y, - }, - }); - } - } - }, - [getNode, nodes, updateNode], - ); - - return { resizeIterationNode }; -} type CanvasMouseEvent = Pick< React.MouseEvent, 'clientX' | 'clientY' >; export function useAddNode(reactFlowInstance?: ReactFlowInstance) { - const { edges, nodes, addEdge, addNode, getNode } = useGraphStore( - (state) => state, - ); + const { nodes, addNode } = useGraphStore((state) => state); const getNodeName = useGetNodeName(); const { initializeOperatorParams } = useInitializeOperatorParams(); const { calculateNewlyBackChildPosition } = useCalculateNewlyChildPosition(); const { addChildEdge } = useAddChildEdge(); - const { addToolNode } = useAddToolNode(); - const { resizeIterationNode } = useResizeIterationNode(); // const [reactFlowInstance, setReactFlowInstance] = // useState>(); @@ -277,7 +150,6 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { ) => (event?: CanvasMouseEvent): string | undefined => { const nodeId = params.nodeId; - const node = getNode(nodeId); // reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition // and you don't need to subtract the reactFlowBounds.left/top anymore @@ -308,112 +180,30 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance) { getNodeName(type), nodes, ), - form: initializeOperatorParams(type as Operator, params.position), + form: initializeOperatorParams(type as Operator), }, sourcePosition: Position.Right, targetPosition: Position.Left, dragHandle: getNodeDragHandle(type), }; - if (node && node.parentId) { - newNode.parentId = node.parentId; - newNode.extent = 'parent'; - const parentNode = getNode(node.parentId); - if (parentNode && !isBottomSubAgent(type, params.position)) { - resizeIterationNode(type, params.position, node.parentId); - } - } - - if (type === Operator.Iteration) { - newNode.width = 500; - newNode.height = 250; - const iterationStartNode: Node = { - id: `${Operator.IterationStart}:${humanId()}`, - type: 'iterationStartNode', - position: { x: 50, y: 100 }, - // draggable: false, - data: { - label: Operator.IterationStart, - name: Operator.IterationStart, - form: initialIterationStartValues, - }, - parentId: newNode.id, - extent: 'parent', - }; - addNode(newNode); - addNode(iterationStartNode); - if (nodeId) { - addEdge({ - source: nodeId, - target: newNode.id, - sourceHandle: NodeHandleId.Start, - targetHandle: NodeHandleId.End, - }); - } - return newNode.id; - } else if ( - type === Operator.Agent && - params.position === Position.Bottom - ) { - const agentNode = getNode(nodeId); - if (agentNode) { - // Calculate the coordinates of child nodes to prevent newly added child nodes from covering other child nodes - const allChildAgentNodeIds = edges - .filter( - (x) => - x.source === nodeId && - x.sourceHandle === NodeHandleId.AgentBottom, - ) - .map((x) => x.target); - - const xAxises = nodes - .filter((x) => allChildAgentNodeIds.some((y) => y === x.id)) - .map((x) => x.position.x); - - const maxX = Math.max(...xAxises); - - newNode.position = { - x: xAxises.length > 0 ? maxX + 262 : agentNode.position.x + 82, - y: agentNode.position.y + 140, - }; - } - addNode(newNode); - if (nodeId) { - addEdge({ - source: nodeId, - target: newNode.id, - sourceHandle: NodeHandleId.AgentBottom, - targetHandle: NodeHandleId.AgentTop, - }); - } - return newNode.id; - } else if (type === Operator.Tool) { - const toolNodeAdded = addToolNode(newNode, params.nodeId); - return toolNodeAdded ? newNode.id : undefined; - } else { - addNode(newNode); - addChildEdge(params.position, { - source: params.nodeId, - target: newNode.id, - sourceHandle: params.id, - }); - } + addNode(newNode); + addChildEdge(params.position, { + source: params.nodeId, + target: newNode.id, + sourceHandle: params.id, + }); return newNode.id; }, [ addChildEdge, - addEdge, addNode, - addToolNode, calculateNewlyBackChildPosition, - edges, - getNode, getNodeName, initializeOperatorParams, nodes, reactFlowInstance, - resizeIterationNode, ], ); diff --git a/web/src/pages/data-flow/hooks/use-before-delete.tsx b/web/src/pages/data-flow/hooks/use-before-delete.tsx index d08333c8610..bbeeaae108e 100644 --- a/web/src/pages/data-flow/hooks/use-before-delete.tsx +++ b/web/src/pages/data-flow/hooks/use-before-delete.tsx @@ -1,17 +1,12 @@ import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { Node, OnBeforeDelete } from '@xyflow/react'; +import { OnBeforeDelete } from '@xyflow/react'; import { Operator } from '../constant'; import useGraphStore from '../store'; -import { deleteAllDownstreamAgentsAndTool } from '../utils/delete-node'; -const UndeletableNodes = [Operator.Begin, Operator.IterationStart]; +const UndeletableNodes = [Operator.Begin]; export function useBeforeDelete() { - const { getOperatorTypeFromId, getNode } = useGraphStore((state) => state); - - const agentPredicate = (node: Node) => { - return getOperatorTypeFromId(node.id) === Operator.Agent; - }; + const { getOperatorTypeFromId } = useGraphStore((state) => state); const handleBeforeDelete: OnBeforeDelete = async ({ nodes, // Nodes to be deleted @@ -23,13 +18,6 @@ export function useBeforeDelete() { return false; } - if ( - operatorType === Operator.IterationStart && - !nodes.some((x) => x.id === node.parentId) - ) { - return false; - } - return true; }); @@ -51,27 +39,6 @@ export function useBeforeDelete() { return true; }); - // Delete the agent and tool nodes downstream of the agent node - if (nodes.some(agentPredicate)) { - nodes.filter(agentPredicate).forEach((node) => { - const { downstreamAgentAndToolEdges, downstreamAgentAndToolNodeIds } = - deleteAllDownstreamAgentsAndTool(node.id, edges); - - downstreamAgentAndToolNodeIds.forEach((nodeId) => { - const currentNode = getNode(nodeId); - if (toBeDeletedNodes.every((x) => x.id !== nodeId) && currentNode) { - toBeDeletedNodes.push(currentNode); - } - }); - - downstreamAgentAndToolEdges.forEach((edge) => { - if (toBeDeletedEdges.every((x) => x.id !== edge.id)) { - toBeDeletedEdges.push(edge); - } - }); - }, []); - } - return { nodes: toBeDeletedNodes, edges: toBeDeletedEdges, diff --git a/web/src/pages/data-flow/hooks/use-build-options.tsx b/web/src/pages/data-flow/hooks/use-build-options.tsx new file mode 100644 index 00000000000..d1214f7299c --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-build-options.tsx @@ -0,0 +1,19 @@ +import { buildNodeOutputOptions } from '@/utils/canvas-util'; +import { useMemo } from 'react'; +import { Operator } from '../constant'; +import OperatorIcon from '../operator-icon'; +import useGraphStore from '../store'; + +export function useBuildNodeOutputOptions(nodeId?: string) { + const nodes = useGraphStore((state) => state.nodes); + const edges = useGraphStore((state) => state.edges); + + return useMemo(() => { + return buildNodeOutputOptions({ + nodes, + edges, + nodeId, + Icon: ({ name }) => , + }); + }, [edges, nodeId, nodes]); +} diff --git a/web/src/pages/data-flow/hooks/use-cancel-dataflow.ts b/web/src/pages/data-flow/hooks/use-cancel-dataflow.ts new file mode 100644 index 00000000000..96705a0f8fd --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-cancel-dataflow.ts @@ -0,0 +1,21 @@ +import { useCancelDataflow } from '@/hooks/use-agent-request'; +import { useCallback } from 'react'; + +export function useCancelCurrentDataflow({ + messageId, + stopFetchTrace, +}: { + messageId: string; + stopFetchTrace(): void; +}) { + const { cancelDataflow } = useCancelDataflow(); + + const handleCancel = useCallback(async () => { + const code = await cancelDataflow(messageId); + if (code === 0) { + stopFetchTrace(); + } + }, [cancelDataflow, messageId, stopFetchTrace]); + + return { handleCancel }; +} diff --git a/web/src/pages/data-flow/hooks/use-change-node-name.ts b/web/src/pages/data-flow/hooks/use-change-node-name.ts index 61a5653d732..da7316a8460 100644 --- a/web/src/pages/data-flow/hooks/use-change-node-name.ts +++ b/web/src/pages/data-flow/hooks/use-change-node-name.ts @@ -9,7 +9,6 @@ import { useMemo, useState, } from 'react'; -import { Operator } from '../constant'; import useGraphStore from '../store'; import { getAgentNodeTools } from '../utils'; @@ -77,13 +76,10 @@ export const useHandleNodeNameChange = ({ data: any; }) => { const [name, setName] = useState(''); - const { updateNodeName, nodes, getOperatorTypeFromId } = useGraphStore( - (state) => state, - ); + const { updateNodeName, nodes } = useGraphStore((state) => state); const previousName = data?.name; - const isToolNode = getOperatorTypeFromId(id) === Operator.Tool; - const { handleToolNameBlur, previousToolName } = useHandleTooNodeNameChange({ + const { previousToolName } = useHandleTooNodeNameChange({ id, name, setName, @@ -109,12 +105,12 @@ export const useHandleNodeNameChange = ({ }, []); useEffect(() => { - setName(isToolNode ? previousToolName : previousName); - }, [isToolNode, previousName, previousToolName]); + setName(previousName); + }, [previousName, previousToolName]); return { name, - handleNameBlur: isToolNode ? handleToolNameBlur : handleNameBlur, + handleNameBlur: handleNameBlur, handleNameChange, }; }; diff --git a/web/src/pages/data-flow/hooks/use-chat-logic.ts b/web/src/pages/data-flow/hooks/use-chat-logic.ts deleted file mode 100644 index 42b0533cb76..00000000000 --- a/web/src/pages/data-flow/hooks/use-chat-logic.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MessageType } from '@/constants/chat'; -import { Message } from '@/interfaces/database/chat'; -import { IMessage } from '@/pages/chat/interface'; -import { get } from 'lodash'; -import { useCallback, useMemo } from 'react'; -import { BeginQuery } from '../interface'; -import { buildBeginQueryWithObject } from '../utils'; -type IAwaitCompentData = { - derivedMessages: IMessage[]; - sendFormMessage: (params: { - inputs: Record; - id: string; - }) => void; - canvasId: string; -}; -const useAwaitCompentData = (props: IAwaitCompentData) => { - const { derivedMessages, sendFormMessage, canvasId } = props; - - const getInputs = useCallback((message: Message) => { - return get(message, 'data.inputs', {}) as Record; - }, []); - - const buildInputList = useCallback( - (message: Message) => { - return Object.entries(getInputs(message)).map(([key, val]) => { - return { - ...val, - key, - }; - }); - }, - [getInputs], - ); - - const handleOk = useCallback( - (message: Message) => (values: BeginQuery[]) => { - const inputs = getInputs(message); - const nextInputs = buildBeginQueryWithObject(inputs, values); - sendFormMessage({ - inputs: nextInputs, - id: canvasId, - }); - }, - [getInputs, sendFormMessage, canvasId], - ); - - const isWaitting = useMemo(() => { - const temp = derivedMessages?.some((message, i) => { - const flag = - message.role === MessageType.Assistant && - derivedMessages.length - 1 === i && - message.data; - return flag; - }); - return temp; - }, [derivedMessages]); - return { getInputs, buildInputList, handleOk, isWaitting }; -}; - -export { useAwaitCompentData }; diff --git a/web/src/pages/data-flow/hooks/use-download-output.ts b/web/src/pages/data-flow/hooks/use-download-output.ts new file mode 100644 index 00000000000..8e02105e874 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-download-output.ts @@ -0,0 +1,38 @@ +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { ITraceData } from '@/interfaces/database/agent'; +import { downloadJsonFile } from '@/utils/file-util'; +import { get, isEmpty } from 'lodash'; +import { useCallback } from 'react'; + +export function findEndOutput(list?: ITraceData[]) { + if (Array.isArray(list)) { + const trace = list.find((x) => x.component_id === 'END')?.trace; + + const str = get(trace, '0.message'); + + try { + if (!isEmpty(str)) { + const json = JSON.parse(str); + return json; + } + } catch (error) {} + } +} + +export function isEndOutputEmpty(list?: ITraceData[]) { + return isEmpty(findEndOutput(list)); +} +export function useDownloadOutput(data?: ITraceData[]) { + const { data: agent } = useFetchAgent(); + + const handleDownloadJson = useCallback(() => { + const output = findEndOutput(data); + if (!isEndOutputEmpty(data)) { + downloadJsonFile(output, `${agent.title}.json`); + } + }, [agent.title, data]); + + return { + handleDownloadJson, + }; +} diff --git a/web/src/pages/data-flow/hooks/use-export-json.ts b/web/src/pages/data-flow/hooks/use-export-json.ts index 1efe6bb50dd..87a811cf513 100644 --- a/web/src/pages/data-flow/hooks/use-export-json.ts +++ b/web/src/pages/data-flow/hooks/use-export-json.ts @@ -1,71 +1,17 @@ -import { useToast } from '@/components/hooks/use-toast'; -import { FileMimeType, Platform } from '@/constants/common'; -import { useSetModalState } from '@/hooks/common-hooks'; import { useFetchAgent } from '@/hooks/use-agent-request'; -import { IGraph } from '@/interfaces/database/flow'; import { downloadJsonFile } from '@/utils/file-util'; -import { message } from 'antd'; -import isEmpty from 'lodash/isEmpty'; import { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; import { useBuildDslData } from './use-build-dsl'; -import { useSetGraphInfo } from './use-set-graph'; export const useHandleExportOrImportJsonFile = () => { const { buildDslData } = useBuildDslData(); - const { - visible: fileUploadVisible, - hideModal: hideFileUploadModal, - showModal: showFileUploadModal, - } = useSetModalState(); - const setGraphInfo = useSetGraphInfo(); const { data } = useFetchAgent(); - const { t } = useTranslation(); - const { toast } = useToast(); - - const onFileUploadOk = useCallback( - async ({ - fileList, - platform, - }: { - fileList: File[]; - platform: Platform; - }) => { - console.log('🚀 ~ useHandleExportOrImportJsonFile ~ platform:', platform); - if (fileList.length > 0) { - const file = fileList[0]; - if (file.type !== FileMimeType.Json) { - toast({ title: t('flow.jsonUploadTypeErrorMessage') }); - return; - } - - const graphStr = await file.text(); - const errorMessage = t('flow.jsonUploadContentErrorMessage'); - try { - const graph = JSON.parse(graphStr); - if (graphStr && !isEmpty(graph) && Array.isArray(graph?.nodes)) { - setGraphInfo(graph ?? ({} as IGraph)); - hideFileUploadModal(); - } else { - message.error(errorMessage); - } - } catch (error) { - message.error(errorMessage); - } - } - }, - [hideFileUploadModal, setGraphInfo, t, toast], - ); const handleExportJson = useCallback(() => { downloadJsonFile(buildDslData().graph, `${data.title}.json`); }, [buildDslData, data.title]); return { - fileUploadVisible, handleExportJson, - handleImportJson: showFileUploadModal, - hideFileUploadModal, - onFileUploadOk, }; }; diff --git a/web/src/pages/data-flow/hooks/use-fetch-log.ts b/web/src/pages/data-flow/hooks/use-fetch-log.ts new file mode 100644 index 00000000000..e8918bdade2 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-fetch-log.ts @@ -0,0 +1,56 @@ +import { useFetchMessageTrace } from '@/hooks/use-agent-request'; +import { isEmpty } from 'lodash'; +import { useCallback, useEffect, useMemo } from 'react'; + +export function useFetchLog(logSheetVisible: boolean) { + const { + setMessageId, + data, + loading, + messageId, + setISStopFetchTrace, + isStopFetchTrace, + } = useFetchMessageTrace(); + + const isCompleted = useMemo(() => { + if (Array.isArray(data)) { + const latest = data?.at(-1); + return ( + latest?.component_id === 'END' && !isEmpty(latest?.trace[0].message) + ); + } + return false; + }, [data]); + + const isLogEmpty = !data || !data.length; + + const stopFetchTrace = useCallback(() => { + setISStopFetchTrace(true); + }, [setISStopFetchTrace]); + + // cancel request + useEffect(() => { + if (isCompleted) { + stopFetchTrace(); + } + }, [isCompleted, stopFetchTrace]); + + useEffect(() => { + if (logSheetVisible) { + setISStopFetchTrace(false); + } + }, [logSheetVisible, setISStopFetchTrace]); + + return { + logs: data, + isLogEmpty, + isCompleted, + loading, + isParsing: !isLogEmpty && !isCompleted && !isStopFetchTrace, + messageId, + setMessageId, + stopFetchTrace, + }; +} + +export type UseFetchLogReturnType = ReturnType; diff --git a/web/src/pages/data-flow/hooks/use-find-mcp-by-id.ts b/web/src/pages/data-flow/hooks/use-find-mcp-by-id.ts deleted file mode 100644 index e4c5aed5a33..00000000000 --- a/web/src/pages/data-flow/hooks/use-find-mcp-by-id.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useListMcpServer } from '@/hooks/use-mcp-request'; - -export function useFindMcpById() { - const { data } = useListMcpServer(); - - const findMcpById = (id: string) => - data.mcp_servers.find((item) => item.id === id); - - return { - findMcpById, - }; -} diff --git a/web/src/pages/data-flow/hooks/use-get-begin-query.tsx b/web/src/pages/data-flow/hooks/use-get-begin-query.tsx deleted file mode 100644 index 83cda207803..00000000000 --- a/web/src/pages/data-flow/hooks/use-get-begin-query.tsx +++ /dev/null @@ -1,317 +0,0 @@ -import { AgentGlobals } from '@/constants/agent'; -import { useFetchAgent } from '@/hooks/use-agent-request'; -import { RAGFlowNodeType } from '@/interfaces/database/flow'; -import { Edge } from '@xyflow/react'; -import { DefaultOptionType } from 'antd/es/select'; -import { t } from 'i18next'; -import { isEmpty } from 'lodash'; -import get from 'lodash/get'; -import { - ReactNode, - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import { - AgentDialogueMode, - BeginId, - BeginQueryType, - Operator, - VariableType, -} from '../constant'; -import { AgentFormContext } from '../context'; -import { buildBeginInputListFromObject } from '../form/begin-form/utils'; -import { BeginQuery } from '../interface'; -import OperatorIcon from '../operator-icon'; -import useGraphStore from '../store'; - -export function useSelectBeginNodeDataInputs() { - const getNode = useGraphStore((state) => state.getNode); - - return buildBeginInputListFromObject( - getNode(BeginId)?.data?.form?.inputs ?? {}, - ); -} - -export function useIsTaskMode() { - const getNode = useGraphStore((state) => state.getNode); - - return useMemo(() => { - const node = getNode(BeginId); - return node?.data?.form?.mode === AgentDialogueMode.Task; - }, [getNode]); -} - -export const useGetBeginNodeDataQuery = () => { - const getNode = useGraphStore((state) => state.getNode); - - const getBeginNodeDataQuery = useCallback(() => { - return buildBeginInputListFromObject( - get(getNode(BeginId), 'data.form.inputs', {}), - ); - }, [getNode]); - - return getBeginNodeDataQuery; -}; - -export const useGetBeginNodeDataInputs = () => { - const getNode = useGraphStore((state) => state.getNode); - - const inputs = get(getNode(BeginId), 'data.form.inputs', {}); - - const beginNodeDataInputs = useMemo(() => { - return buildBeginInputListFromObject(inputs); - }, [inputs]); - - return beginNodeDataInputs; -}; - -export const useGetBeginNodeDataQueryIsSafe = () => { - const [isBeginNodeDataQuerySafe, setIsBeginNodeDataQuerySafe] = - useState(false); - const inputs = useSelectBeginNodeDataInputs(); - const nodes = useGraphStore((state) => state.nodes); - - useEffect(() => { - const query: BeginQuery[] = inputs; - const isSafe = !query.some((q) => !q.optional && q.type === 'file'); - setIsBeginNodeDataQuerySafe(isSafe); - }, [inputs, nodes]); - - return isBeginNodeDataQuerySafe; -}; - -function filterAllUpstreamNodeIds(edges: Edge[], nodeIds: string[]) { - return nodeIds.reduce((pre, nodeId) => { - const currentEdges = edges.filter((x) => x.target === nodeId); - - const upstreamNodeIds: string[] = currentEdges.map((x) => x.source); - - const ids = upstreamNodeIds.concat( - filterAllUpstreamNodeIds(edges, upstreamNodeIds), - ); - - ids.forEach((x) => { - if (pre.every((y) => y !== x)) { - pre.push(x); - } - }); - - return pre; - }, []); -} - -export function buildOutputOptions( - outputs: Record = {}, - nodeId?: string, - parentLabel?: string | ReactNode, - icon?: ReactNode, -) { - return Object.keys(outputs).map((x) => ({ - label: x, - value: `${nodeId}@${x}`, - parentLabel, - icon, - type: outputs[x]?.type, - })); -} - -export function useBuildNodeOutputOptions(nodeId?: string) { - const nodes = useGraphStore((state) => state.nodes); - const edges = useGraphStore((state) => state.edges); - - const nodeOutputOptions = useMemo(() => { - if (!nodeId) { - return []; - } - const upstreamIds = filterAllUpstreamNodeIds(edges, [nodeId]); - - const nodeWithOutputList = nodes.filter( - (x) => - upstreamIds.some((y) => y === x.id) && !isEmpty(x.data?.form?.outputs), - ); - - return nodeWithOutputList - .filter((x) => x.id !== nodeId) - .map((x) => ({ - label: x.data.name, - value: x.id, - title: x.data.name, - options: buildOutputOptions( - x.data.form.outputs, - x.id, - x.data.name, - , - ), - })); - }, [edges, nodeId, nodes]); - - return nodeOutputOptions; -} - -// exclude nodes with branches -const ExcludedNodes = [ - Operator.Categorize, - Operator.Relevant, - Operator.Begin, - Operator.Note, -]; - -const StringList = [ - BeginQueryType.Line, - BeginQueryType.Paragraph, - BeginQueryType.Options, -]; - -function transferToVariableType(type: string) { - if (StringList.some((x) => x === type)) { - return VariableType.String; - } - return type; -} - -export function useBuildBeginVariableOptions() { - const inputs = useSelectBeginNodeDataInputs(); - - const options = useMemo(() => { - return [ - { - label: {t('flow.beginInput')}, - title: t('flow.beginInput'), - options: inputs.map((x) => ({ - label: x.name, - parentLabel: {t('flow.beginInput')}, - icon: , - value: `begin@${x.key}`, - type: transferToVariableType(x.type), - })), - }, - ]; - }, [inputs]); - - return options; -} - -export const useBuildVariableOptions = (nodeId?: string, parentId?: string) => { - const nodeOutputOptions = useBuildNodeOutputOptions(nodeId); - const parentNodeOutputOptions = useBuildNodeOutputOptions(parentId); - const beginOptions = useBuildBeginVariableOptions(); - - const options = useMemo(() => { - return [...beginOptions, ...nodeOutputOptions, ...parentNodeOutputOptions]; - }, [beginOptions, nodeOutputOptions, parentNodeOutputOptions]); - - return options; -}; - -export function useBuildQueryVariableOptions(n?: RAGFlowNodeType) { - const { data } = useFetchAgent(); - const node = useContext(AgentFormContext) || n; - const options = useBuildVariableOptions(node?.id, node?.parentId); - const nextOptions = useMemo(() => { - const globals = data?.dsl?.globals ?? {}; - const globalOptions = Object.entries(globals).map(([key, value]) => ({ - label: key, - value: key, - icon: , - parentLabel: {t('flow.beginInput')}, - type: Array.isArray(value) - ? `${VariableType.Array}${key === AgentGlobals.SysFiles ? '' : ''}` - : typeof value, - })); - return [ - { ...options[0], options: [...options[0]?.options, ...globalOptions] }, - ...options.slice(1), - ]; - }, [data.dsl?.globals, options]); - - return nextOptions; -} - -export function useBuildComponentIdOptions(nodeId?: string, parentId?: string) { - const nodes = useGraphStore((state) => state.nodes); - - // Limit the nodes inside iteration to only reference peer nodes with the same parentId and other external nodes other than their parent nodes - const filterChildNodesToSameParentOrExternal = useCallback( - (node: RAGFlowNodeType) => { - // Node inside iteration - if (parentId) { - return ( - (node.parentId === parentId || node.parentId === undefined) && - node.id !== parentId - ); - } - - return node.parentId === undefined; // The outermost node - }, - [parentId], - ); - - const componentIdOptions = useMemo(() => { - return nodes - .filter( - (x) => - x.id !== nodeId && - !ExcludedNodes.some((y) => y === x.data.label) && - filterChildNodesToSameParentOrExternal(x), - ) - .map((x) => ({ label: x.data.name, value: x.id })); - }, [nodes, nodeId, filterChildNodesToSameParentOrExternal]); - - return [ - { - label: Component Output, - title: 'Component Output', - options: componentIdOptions, - }, - ]; -} - -export function useBuildComponentIdAndBeginOptions( - nodeId?: string, - parentId?: string, -) { - const componentIdOptions = useBuildComponentIdOptions(nodeId, parentId); - const beginOptions = useBuildBeginVariableOptions(); - - return [...beginOptions, ...componentIdOptions]; -} - -export const useGetComponentLabelByValue = (nodeId: string) => { - const options = useBuildComponentIdAndBeginOptions(nodeId); - - const flattenOptions = useMemo(() => { - return options.reduce((pre, cur) => { - return [...pre, ...cur.options]; - }, []); - }, [options]); - - const getLabel = useCallback( - (val?: string) => { - return flattenOptions.find((x) => x.value === val)?.label; - }, - [flattenOptions], - ); - return getLabel; -}; - -export function useGetVariableLabelByValue(nodeId: string) { - const { getNode } = useGraphStore((state) => state); - const nextOptions = useBuildQueryVariableOptions(getNode(nodeId)); - - const flattenOptions = useMemo(() => { - return nextOptions.reduce((pre, cur) => { - return [...pre, ...cur.options]; - }, []); - }, [nextOptions]); - - const getLabel = useCallback( - (val?: string) => { - return flattenOptions.find((x) => x.value === val)?.label; - }, - [flattenOptions], - ); - return getLabel; -} diff --git a/web/src/pages/data-flow/hooks/use-iteration.ts b/web/src/pages/data-flow/hooks/use-iteration.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/web/src/pages/data-flow/hooks/use-run-dataflow.ts b/web/src/pages/data-flow/hooks/use-run-dataflow.ts new file mode 100644 index 00000000000..e49f1ac5687 --- /dev/null +++ b/web/src/pages/data-flow/hooks/use-run-dataflow.ts @@ -0,0 +1,59 @@ +import message from '@/components/ui/message'; +import { useSendMessageBySSE } from '@/hooks/use-send-message'; +import api from '@/utils/api'; +import { get } from 'lodash'; +import { useCallback, useContext } from 'react'; +import { useParams } from 'umi'; +import { LogContext } from '../context'; +import { useSaveGraphBeforeOpeningDebugDrawer } from './use-save-graph'; + +export function useRunDataflow( + showLogSheet: () => void, + hideRunOrChatDrawer: () => void, +) { + const { send } = useSendMessageBySSE(api.runCanvas); + const { id } = useParams(); + const { setMessageId, setUploadedFileData } = useContext(LogContext); + + const { handleRun: saveGraph, loading } = + useSaveGraphBeforeOpeningDebugDrawer(showLogSheet!); + + const run = useCallback( + async (fileResponseData: Record) => { + const success = await saveGraph(); + if (!success) return; + const res = await send({ + id, + query: '', + session_id: null, + files: [fileResponseData.file], + }); + + if (res && res?.response.status === 200 && get(res, 'data.code') === 0) { + // fetch canvas + hideRunOrChatDrawer(); + setUploadedFileData(fileResponseData.file); + const msgId = get(res, 'data.data.message_id'); + if (msgId) { + setMessageId(msgId); + } + + return msgId; + } else { + message.error(get(res, 'data.message', '')); + } + }, + [ + hideRunOrChatDrawer, + id, + saveGraph, + send, + setMessageId, + setUploadedFileData, + ], + ); + + return { run, loading: loading }; +} + +export type RunDataflowType = ReturnType; diff --git a/web/src/pages/data-flow/hooks/use-save-graph.ts b/web/src/pages/data-flow/hooks/use-save-graph.ts index 8ce4dc15e06..322e8b80e23 100644 --- a/web/src/pages/data-flow/hooks/use-save-graph.ts +++ b/web/src/pages/data-flow/hooks/use-save-graph.ts @@ -1,8 +1,4 @@ -import { - useFetchAgent, - useResetAgent, - useSetAgent, -} from '@/hooks/use-agent-request'; +import { useFetchAgent, useSetAgent } from '@/hooks/use-agent-request'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { formatDate } from '@/utils/date'; import { useDebounceEffect } from 'ahooks'; @@ -22,6 +18,7 @@ export const useSaveGraph = (showMessage: boolean = true) => { return setAgent({ id, title: data.title, + canvas_category: data.canvas_category, dsl: buildDslData(currentNodes), }); }, @@ -33,21 +30,22 @@ export const useSaveGraph = (showMessage: boolean = true) => { export const useSaveGraphBeforeOpeningDebugDrawer = (show: () => void) => { const { saveGraph, loading } = useSaveGraph(); - const { resetAgent } = useResetAgent(); + // const { resetAgent } = useResetAgent(); const handleRun = useCallback( async (nextNodes?: RAGFlowNodeType[]) => { const saveRet = await saveGraph(nextNodes); if (saveRet?.code === 0) { // Call the reset api before opening the run drawer each time - const resetRet = await resetAgent(); + // const resetRet = await resetAgent(); // After resetting, all previous messages will be cleared. - if (resetRet?.code === 0) { - show(); - } + // if (resetRet?.code === 0) { + show(); + // } } + return saveRet?.code === 0; }, - [saveGraph, resetAgent, show], + [saveGraph, show], ); return { handleRun, loading }; diff --git a/web/src/pages/data-flow/hooks/use-show-drawer.tsx b/web/src/pages/data-flow/hooks/use-show-drawer.tsx index 6789e86ba6f..82a9bec8538 100644 --- a/web/src/pages/data-flow/hooks/use-show-drawer.tsx +++ b/web/src/pages/data-flow/hooks/use-show-drawer.tsx @@ -2,10 +2,9 @@ import { useSetModalState } from '@/hooks/common-hooks'; import { NodeMouseHandler } from '@xyflow/react'; import get from 'lodash/get'; import React, { useCallback, useEffect } from 'react'; -import { Operator } from '../constant'; +import { BeginId, Operator } from '../constant'; import useGraphStore from '../store'; import { useCacheChatLog } from './use-cache-chat-log'; -import { useGetBeginNodeDataInputs } from './use-get-begin-query'; import { useSaveGraph } from './use-save-graph'; export const useShowFormDrawer = () => { @@ -13,7 +12,6 @@ export const useShowFormDrawer = () => { clickedNodeId: clickNodeId, setClickedNodeId, getNode, - setClickedToolId, } = useGraphStore((state) => state); const { visible: formDrawerVisible, @@ -23,16 +21,14 @@ export const useShowFormDrawer = () => { const handleShow = useCallback( (e: React.MouseEvent, nodeId: string) => { - const tool = get(e.target, 'dataset.tool'); // TODO: Operator type judgment should be used - if (nodeId.startsWith(Operator.Tool) && !tool) { + if (nodeId === BeginId) { return; } setClickedNodeId(nodeId); - setClickedToolId(tool); showFormDrawer(); }, - [setClickedNodeId, setClickedToolId, showFormDrawer], + [setClickedNodeId, showFormDrawer], ); return { @@ -87,26 +83,12 @@ export function useShowDrawer({ } = useShowSingleDebugDrawer(); const { formDrawerVisible, hideFormDrawer, showFormDrawer, clickedNode } = useShowFormDrawer(); - const inputs = useGetBeginNodeDataInputs(); useEffect(() => { if (drawerVisible) { - if (inputs.length > 0) { - showRunModal(); - hideChatModal(); - } else { - showChatModal(); - hideRunModal(); - } + showRunModal(); } - }, [ - hideChatModal, - hideRunModal, - showChatModal, - showRunModal, - drawerVisible, - inputs, - ]); + }, [hideChatModal, hideRunModal, showChatModal, showRunModal, drawerVisible]); const hideRunOrChatDrawer = useCallback(() => { hideChatModal(); diff --git a/web/src/pages/data-flow/hooks/use-watch-form-change.ts b/web/src/pages/data-flow/hooks/use-watch-form-change.ts index 534c2e2a9c3..1f85b107f22 100644 --- a/web/src/pages/data-flow/hooks/use-watch-form-change.ts +++ b/web/src/pages/data-flow/hooks/use-watch-form-change.ts @@ -4,15 +4,12 @@ import useGraphStore from '../store'; export function useWatchFormChange(id?: string, form?: UseFormReturn) { let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); useEffect(() => { - // Manually triggered form updates are synchronized to the canvas if (id) { - values = form?.getValues() || {}; - let nextValues: any = values; - - updateNodeForm(id, nextValues); + updateNodeForm(id, values); } - }, [form?.formState.isDirty, id, updateNodeForm, values]); + }, [id, updateNodeForm, values]); } diff --git a/web/src/pages/data-flow/index.tsx b/web/src/pages/data-flow/index.tsx index 10798b5e862..cdca3ebae7a 100644 --- a/web/src/pages/data-flow/index.tsx +++ b/web/src/pages/data-flow/index.tsx @@ -21,26 +21,27 @@ import { ReactFlowProvider } from '@xyflow/react'; import { ChevronDown, CirclePlay, - Download, History, LaptopMinimalCheck, Settings, Upload, } from 'lucide-react'; -import { ComponentPropsWithoutRef, useCallback } from 'react'; +import { ComponentPropsWithoutRef, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import DataFlowCanvas from './canvas'; import { DropdownProvider } from './canvas/context'; +import { LogContext } from './context'; +import { useCancelCurrentDataflow } from './hooks/use-cancel-dataflow'; import { useHandleExportOrImportJsonFile } from './hooks/use-export-json'; import { useFetchDataOnMount } from './hooks/use-fetch-data'; -import { useGetBeginNodeDataInputs } from './hooks/use-get-begin-query'; +import { useFetchLog } from './hooks/use-fetch-log'; import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer, useWatchAgentChange, } from './hooks/use-save-graph'; +import { LogSheet } from './log-sheet'; import { SettingDialog } from './setting-dialog'; -import { UploadAgentDialog } from './upload-agent-dialog'; import { useAgentHistoryManager } from './use-agent-history-manager'; import { VersionDialog } from './version-dialog'; @@ -64,24 +65,12 @@ export default function DataFlow() { } = useSetModalState(); const { t } = useTranslation(); useAgentHistoryManager(); - const { - handleExportJson, - handleImportJson, - fileUploadVisible, - onFileUploadOk, - hideFileUploadModal, - } = useHandleExportOrImportJsonFile(); + const { handleExportJson } = useHandleExportOrImportJsonFile(); const { saveGraph, loading } = useSaveGraph(); const { flowDetail: agentDetail } = useFetchDataOnMount(); - const inputs = useGetBeginNodeDataInputs(); - const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); - const handleRunAgent = useCallback(() => { - if (inputs.length > 0) { - showChatDrawer(); - } else { - handleRun(); - } - }, [handleRun, inputs, showChatDrawer]); + const { handleRun, loading: running } = + useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); + const { visible: versionDialogVisible, hideModal: hideVersionDialog, @@ -94,6 +83,40 @@ export default function DataFlow() { showModal: showSettingDialog, } = useSetModalState(); + const { + visible: logSheetVisible, + showModal: showLogSheet, + hideModal: hideLogSheet, + } = useSetModalState(); + + const { + isParsing, + logs, + messageId, + setMessageId, + isCompleted, + stopFetchTrace, + isLogEmpty, + } = useFetchLog(logSheetVisible); + + const [uploadedFileData, setUploadedFileData] = + useState>(); + + const handleRunAgent = useCallback(() => { + if (isParsing) { + // show log sheet + showLogSheet(); + } else { + hideLogSheet(); + handleRun(); + } + }, [handleRun, hideLogSheet, isParsing, showLogSheet]); + + const { handleCancel } = useCancelCurrentDataflow({ + messageId, + stopFetchTrace, + }); + const time = useWatchAgentChange(chatDrawerVisible); return ( @@ -125,15 +148,25 @@ export default function DataFlow() { > {t('flow.save')} - + + {running || ( + + )} + + {isParsing || running ? t('dataflow.running') : t('flow.run')} + - + {/* */} - - - {t('flow.import')} - - {t('flow.export')} @@ -159,21 +187,19 @@ export default function DataFlow() { - - - - - - {fileUploadVisible && ( - - )} - + + + + + + + {versionDialogVisible && ( @@ -182,6 +208,18 @@ export default function DataFlow() { {settingDialogVisible && ( )} + {logSheetVisible && ( + + )} ); } diff --git a/web/src/pages/data-flow/log-sheet/dataflow-timeline.tsx b/web/src/pages/data-flow/log-sheet/dataflow-timeline.tsx new file mode 100644 index 00000000000..25787a2e4d7 --- /dev/null +++ b/web/src/pages/data-flow/log-sheet/dataflow-timeline.tsx @@ -0,0 +1,137 @@ +import { + Timeline, + TimelineContent, + TimelineHeader, + TimelineIndicator, + TimelineItem, + TimelineSeparator, + TimelineTitle, +} from '@/components/originui/timeline'; +import { Progress } from '@/components/ui/progress'; +import { ITraceData } from '@/interfaces/database/agent'; +import { cn } from '@/lib/utils'; +import { isEmpty } from 'lodash'; +import { File } from 'lucide-react'; +import { useCallback } from 'react'; +import { Operator } from '../constant'; +import OperatorIcon from '../operator-icon'; +import useGraphStore from '../store'; + +export type DataflowTimelineProps = { + traceList?: ITraceData[]; +}; + +const END = 'END'; + +interface DataflowTrace { + datetime: string; + elapsed_time: number; + message: string; + progress: number; + timestamp: number; +} +export function DataflowTimeline({ traceList }: DataflowTimelineProps) { + const getNode = useGraphStore((state) => state.getNode); + + const getNodeData = useCallback( + (componentId: string) => { + return getNode(componentId)?.data; + }, + [getNode], + ); + + const getNodeLabel = useCallback( + (componentId: string) => { + return getNodeData(componentId)?.label as Operator; + }, + [getNodeData], + ); + + return ( + + {Array.isArray(traceList) && + traceList?.map((item, index) => { + const traces = item.trace as DataflowTrace[]; + const nodeLabel = getNodeLabel(item.component_id); + + const latest = traces[traces.length - 1]; + const progress = latest.progress * 100; + + return ( + + + + + +
    + + {getNodeData(item.component_id)?.name || END} + +
    + + + {progress}% + +
    +
    +
    + {traces + .filter((x) => !isEmpty(x.message)) + .map((x, idx) => ( +
    + {x.datetime} + {item.component_id !== 'END' && ( + + {x.message} + + )} + + {x.elapsed_time.toString().slice(0, 6)}s + +
    + ))} +
    +
    +
    + + {item.component_id === END ? ( + + ) : nodeLabel === Operator.Begin ? ( + + ) : ( + + )} + +
    +
    + ); + })} +
    + ); +} diff --git a/web/src/pages/data-flow/log-sheet/index.tsx b/web/src/pages/data-flow/log-sheet/index.tsx new file mode 100644 index 00000000000..f66bada1bdf --- /dev/null +++ b/web/src/pages/data-flow/log-sheet/index.tsx @@ -0,0 +1,111 @@ +import { SkeletonCard } from '@/components/skeleton-card'; +import { Button } from '@/components/ui/button'; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, +} from '@/components/ui/sheet'; +import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; +import { useFetchAgent } from '@/hooks/use-agent-request'; +import { IModalProps } from '@/interfaces/common'; +import { cn } from '@/lib/utils'; +import { PipelineResultSearchParams } from '@/pages/dataflow-result/constant'; +import { + ArrowUpRight, + CirclePause, + Logs, + SquareArrowOutUpRight, +} from 'lucide-react'; +import { useTranslation } from 'react-i18next'; +import 'react18-json-view/src/style.css'; +import { useParams } from 'umi'; +import { + isEndOutputEmpty, + useDownloadOutput, +} from '../hooks/use-download-output'; +import { UseFetchLogReturnType } from '../hooks/use-fetch-log'; +import { DataflowTimeline } from './dataflow-timeline'; + +type LogSheetProps = IModalProps & { + handleCancel(): void; + uploadedFileData?: Record; +} & Pick< + UseFetchLogReturnType, + 'isCompleted' | 'isLogEmpty' | 'isParsing' | 'logs' | 'messageId' + >; + +export function LogSheet({ + hideModal, + isParsing, + logs, + handleCancel, + isCompleted, + isLogEmpty, + messageId, + uploadedFileData, +}: LogSheetProps) { + const { t } = useTranslation(); + const { id } = useParams(); + const { data: agent } = useFetchAgent(); + + const { handleDownloadJson } = useDownloadOutput(logs); + const { navigateToDataflowResult } = useNavigatePage(); + + return ( + + e.preventDefault()} + > + + + {t('flow.log')} + + + +
    + {isLogEmpty ? ( + + ) : ( + + )} +
    + {isParsing ? ( + + ) : ( + + )} +
    +
    + ); +} diff --git a/web/src/pages/data-flow/operator-icon.tsx b/web/src/pages/data-flow/operator-icon.tsx index 2184569d6de..187c84cf574 100644 --- a/web/src/pages/data-flow/operator-icon.tsx +++ b/web/src/pages/data-flow/operator-icon.tsx @@ -1,9 +1,11 @@ import { IconFont } from '@/components/icon-font'; import { cn } from '@/lib/utils'; import { + Blocks, + File, FileChartColumnIncreasing, - Grid3x3, - HousePlus, + FileStack, + Heading, ListMinus, } from 'lucide-react'; import { Operator } from './constant'; @@ -14,26 +16,16 @@ interface IProps { } export const OperatorIconMap = { - [Operator.Retrieval]: 'KR', - [Operator.Begin]: 'house-plus', - [Operator.Categorize]: 'a-QuestionClassification', - [Operator.Message]: 'reply', - [Operator.Iteration]: 'loop', - [Operator.Switch]: 'condition', - [Operator.Code]: 'code-set', - [Operator.Agent]: 'agent-ai', - [Operator.UserFillUp]: 'await', - [Operator.StringTransform]: 'a-textprocessing', [Operator.Note]: 'notebook-pen', - [Operator.ExeSQL]: 'executesql-0', - [Operator.Invoke]: 'httprequest-0', - [Operator.Email]: 'sendemail-0', }; export const SVGIconMap = { + [Operator.Begin]: File, [Operator.Parser]: FileChartColumnIncreasing, - [Operator.Chunker]: Grid3x3, [Operator.Tokenizer]: ListMinus, + [Operator.Splitter]: Blocks, + [Operator.HierarchicalMerger]: Heading, + [Operator.Extractor]: FileStack, }; const Empty = () => { @@ -52,7 +44,7 @@ const OperatorIcon = ({ name, className }: IProps) => { className, )} > - + ); } @@ -60,7 +52,7 @@ const OperatorIcon = ({ name, className }: IProps) => { return typeof Icon === 'string' ? ( ) : ( - + ); }; diff --git a/web/src/pages/data-flow/options.ts b/web/src/pages/data-flow/options.ts index f231ec450b2..e69de29bb2d 100644 --- a/web/src/pages/data-flow/options.ts +++ b/web/src/pages/data-flow/options.ts @@ -1,2178 +0,0 @@ -import { upperFirst } from 'lodash'; - -export const LanguageOptions = [ - { - value: 'af', - label: 'Afrikaans', - }, - { - value: 'pl', - label: 'Polski', - }, - { - value: 'ar', - label: 'العربية', - }, - { - value: 'ast', - label: 'Asturianu', - }, - { - value: 'az', - label: 'Azərbaycanca', - }, - { - value: 'bg', - label: 'Български', - }, - { - value: 'nan', - label: '閩南語 / Bân-lâm-gú', - }, - { - value: 'bn', - label: 'বাংলা', - }, - { - value: 'be', - label: 'Беларуская', - }, - { - value: 'ca', - label: 'Català', - }, - { - value: 'cs', - label: 'Čeština', - }, - { - value: 'cy', - label: 'Cymraeg', - }, - { - value: 'da', - label: 'Dansk', - }, - { - value: 'de', - label: 'Deutsch', - }, - { - value: 'fr', - label: 'Français', - }, - { - value: 'et', - label: 'Eesti', - }, - { - value: 'el', - label: 'Ελληνικά', - }, - { - value: 'en', - label: 'English', - }, - { - value: 'es', - label: 'Español', - }, - { - value: 'eo', - label: 'Esperanto', - }, - { - value: 'eu', - label: 'Euskara', - }, - { - value: 'fa', - label: 'فارسی', - }, - { - value: 'fr', - label: 'Français', - }, - { - value: 'gl', - label: 'Galego', - }, - { - value: 'ko', - label: '한국어', - }, - { - value: 'hy', - label: 'Հայերեն', - }, - { - value: 'hi', - label: 'हिन्दी', - }, - { - value: 'hr', - label: 'Hrvatski', - }, - { - value: 'id', - label: 'Bahasa Indonesia', - }, - { - value: 'it', - label: 'Italiano', - }, - { - value: 'he', - label: 'עברית', - }, - { - value: 'ka', - label: 'ქართული', - }, - { - value: 'lld', - label: 'Ladin', - }, - { - value: 'la', - label: 'Latina', - }, - { - value: 'lv', - label: 'Latviešu', - }, - { - value: 'lt', - label: 'Lietuvių', - }, - { - value: 'hu', - label: 'Magyar', - }, - { - value: 'mk', - label: 'Македонски', - }, - { - value: 'arz', - label: 'مصرى', - }, - { - value: 'ms', - label: 'Bahasa Melayu', - }, - { - value: 'min', - label: 'Bahaso Minangkabau', - }, - { - value: 'my', - label: 'မြန်မာဘာသာ', - }, - { - value: 'nl', - label: 'Nederlands', - }, - { - value: 'ja', - label: '日本語', - }, - { - value: 'no', - label: 'Norsk (bokmål)', - }, - { - value: 'nn', - label: 'Norsk (nynorsk)', - }, - { - value: 'ce', - label: 'Нохчийн', - }, - { - value: 'uz', - label: 'Oʻzbekcha / Ўзбекча', - }, - { - value: 'pt', - label: 'Português', - }, - { - value: 'kk', - label: 'Қазақша / Qazaqşa / قازاقشا', - }, - { - value: 'ro', - label: 'Română', - }, - { - value: 'ru', - label: 'Русский', - }, - { - value: 'ceb', - label: 'Sinugboanong Binisaya', - }, - { - value: 'sk', - label: 'Slovenčina', - }, - { - value: 'sl', - label: 'Slovenščina', - }, - { - value: 'sr', - label: 'Српски / Srpski', - }, - { - value: 'sh', - label: 'Srpskohrvatski / Српскохрватски', - }, - { - value: 'fi', - label: 'Suomi', - }, - { - value: 'sv', - label: 'Svenska', - }, - { - value: 'ta', - label: 'தமிழ்', - }, - { - value: 'tt', - label: 'Татарча / Tatarça', - }, - { - value: 'th', - label: 'ภาษาไทย', - }, - { - value: 'tg', - label: 'Тоҷикӣ', - }, - { - value: 'azb', - label: 'تۆرکجه', - }, - { - value: 'tr', - label: 'Türkçe', - }, - { - value: 'uk', - label: 'Українська', - }, - { - value: 'ur', - label: 'اردو', - }, - { - value: 'vi', - label: 'Tiếng Việt', - }, - { - value: 'war', - label: 'Winaray', - }, - { - value: 'zh', - label: '中文', - }, - { - value: 'yue', - label: '粵語', - }, -]; - -export const GoogleLanguageOptions = [ - { - language_code: 'af', - language_name: 'Afrikaans', - }, - { - language_code: 'ak', - language_name: 'Akan', - }, - { - language_code: 'sq', - language_name: 'Albanian', - }, - { - language_code: 'ws', - language_name: 'Samoa', - }, - { - language_code: 'am', - language_name: 'Amharic', - }, - { - language_code: 'ar', - language_name: 'Arabic', - }, - { - language_code: 'hy', - language_name: 'Armenian', - }, - { - language_code: 'az', - language_name: 'Azerbaijani', - }, - { - language_code: 'eu', - language_name: 'Basque', - }, - { - language_code: 'be', - language_name: 'Belarusian', - }, - { - language_code: 'bem', - language_name: 'Bemba', - }, - { - language_code: 'bn', - language_name: 'Bengali', - }, - { - language_code: 'bh', - language_name: 'Bihari', - }, - { - language_code: 'xx-bork', - language_name: 'Bork, bork, bork!', - }, - { - language_code: 'bs', - language_name: 'Bosnian', - }, - { - language_code: 'br', - language_name: 'Breton', - }, - { - language_code: 'bg', - language_name: 'Bulgarian', - }, - { - language_code: 'bt', - language_name: 'Bhutanese', - }, - { - language_code: 'km', - language_name: 'Cambodian', - }, - { - language_code: 'ca', - language_name: 'Catalan', - }, - { - language_code: 'chr', - language_name: 'Cherokee', - }, - { - language_code: 'ny', - language_name: 'Chichewa', - }, - { - language_code: 'zh-cn', - language_name: 'Chinese (Simplified)', - }, - { - language_code: 'zh-tw', - language_name: 'Chinese (Traditional)', - }, - { - language_code: 'co', - language_name: 'Corsican', - }, - { - language_code: 'hr', - language_name: 'Croatian', - }, - { - language_code: 'cs', - language_name: 'Czech', - }, - { - language_code: 'da', - language_name: 'Danish', - }, - { - language_code: 'nl', - language_name: 'Dutch', - }, - { - language_code: 'xx-elmer', - language_name: 'Elmer Fudd', - }, - { - language_code: 'en', - language_name: 'English', - }, - { - language_code: 'eo', - language_name: 'Esperanto', - }, - { - language_code: 'et', - language_name: 'Estonian', - }, - { - language_code: 'ee', - language_name: 'Ewe', - }, - { - language_code: 'fo', - language_name: 'Faroese', - }, - { - language_code: 'tl', - language_name: 'Filipino', - }, - { - language_code: 'fi', - language_name: 'Finnish', - }, - { - language_code: 'fr', - language_name: 'French', - }, - { - language_code: 'fy', - language_name: 'Frisian', - }, - { - language_code: 'gaa', - language_name: 'Ga', - }, - { - language_code: 'gl', - language_name: 'Galician', - }, - { - language_code: 'ka', - language_name: 'Georgian', - }, - { - language_code: 'de', - language_name: 'German', - }, - { - language_code: 'el', - language_name: 'Greek', - }, - { - language_code: 'kl', - language_name: 'Greenlandic', - }, - { - language_code: 'gn', - language_name: 'Guarani', - }, - { - language_code: 'gu', - language_name: 'Gujarati', - }, - { - language_code: 'xx-hacker', - language_name: 'Hacker', - }, - { - language_code: 'ht', - language_name: 'Haitian Creole', - }, - { - language_code: 'ha', - language_name: 'Hausa', - }, - { - language_code: 'haw', - language_name: 'Hawaiian', - }, - { - language_code: 'iw', - language_name: 'Hebrew', - }, - { - language_code: 'hi', - language_name: 'Hindi', - }, - { - language_code: 'hu', - language_name: 'Hungarian', - }, - { - language_code: 'is', - language_name: 'Icelandic', - }, - { - language_code: 'ig', - language_name: 'Igbo', - }, - { - language_code: 'id', - language_name: 'Indonesian', - }, - { - language_code: 'ia', - language_name: 'Interlingua', - }, - { - language_code: 'ga', - language_name: 'Irish', - }, - { - language_code: 'it', - language_name: 'Italian', - }, - { - language_code: 'ja', - language_name: 'Japanese', - }, - { - language_code: 'jw', - language_name: 'Javanese', - }, - { - language_code: 'kn', - language_name: 'Kannada', - }, - { - language_code: 'kk', - language_name: 'Kazakh', - }, - { - language_code: 'rw', - language_name: 'Kinyarwanda', - }, - { - language_code: 'rn', - language_name: 'Kirundi', - }, - { - language_code: 'xx-klingon', - language_name: 'Klingon', - }, - { - language_code: 'kg', - language_name: 'Kongo', - }, - { - language_code: 'ko', - language_name: 'Korean', - }, - { - language_code: 'kri', - language_name: 'Krio (Sierra Leone)', - }, - { - language_code: 'ku', - language_name: 'Kurdish', - }, - { - language_code: 'ckb', - language_name: 'Kurdish (Soranî)', - }, - { - language_code: 'ky', - language_name: 'Kyrgyz', - }, - { - language_code: 'lo', - language_name: 'Laothian', - }, - { - language_code: 'la', - language_name: 'Latin', - }, - { - language_code: 'lv', - language_name: 'Latvian', - }, - { - language_code: 'ln', - language_name: 'Lingala', - }, - { - language_code: 'lt', - language_name: 'Lithuanian', - }, - { - language_code: 'loz', - language_name: 'Lozi', - }, - { - language_code: 'lg', - language_name: 'Luganda', - }, - { - language_code: 'ach', - language_name: 'Luo', - }, - { - language_code: 'mk', - language_name: 'Macedonian', - }, - { - language_code: 'mg', - language_name: 'Malagasy', - }, - { - language_code: 'ms', - language_name: 'Malay', - }, - { - language_code: 'ml', - language_name: 'Malayalam', - }, - { - language_code: 'mt', - language_name: 'Maltese', - }, - { - language_code: 'mv', - language_name: 'Maldives', - }, - { - language_code: 'mi', - language_name: 'Maori', - }, - { - language_code: 'mr', - language_name: 'Marathi', - }, - { - language_code: 'mfe', - language_name: 'Mauritian Creole', - }, - { - language_code: 'mo', - language_name: 'Moldavian', - }, - { - language_code: 'mn', - language_name: 'Mongolian', - }, - { - language_code: 'sr-me', - language_name: 'Montenegrin', - }, - { - language_code: 'my', - language_name: 'Myanmar', - }, - { - language_code: 'ne', - language_name: 'Nepali', - }, - { - language_code: 'pcm', - language_name: 'Nigerian Pidgin', - }, - { - language_code: 'nso', - language_name: 'Northern Sotho', - }, - { - language_code: 'no', - language_name: 'Norwegian', - }, - { - language_code: 'nn', - language_name: 'Norwegian (Nynorsk)', - }, - { - language_code: 'oc', - language_name: 'Occitan', - }, - { - language_code: 'or', - language_name: 'Oriya', - }, - { - language_code: 'om', - language_name: 'Oromo', - }, - { - language_code: 'ps', - language_name: 'Pashto', - }, - { - language_code: 'fa', - language_name: 'Persian', - }, - { - language_code: 'xx-pirate', - language_name: 'Pirate', - }, - { - language_code: 'pl', - language_name: 'Polish', - }, - { - language_code: 'pt', - language_name: 'Portuguese', - }, - { - language_code: 'pt-br', - language_name: 'Portuguese (Brazil)', - }, - { - language_code: 'pt-pt', - language_name: 'Portuguese (Portugal)', - }, - { - language_code: 'pa', - language_name: 'Punjabi', - }, - { - language_code: 'qu', - language_name: 'Quechua', - }, - { - language_code: 'ro', - language_name: 'Romanian', - }, - { - language_code: 'rm', - language_name: 'Romansh', - }, - { - language_code: 'nyn', - language_name: 'Runyakitara', - }, - { - language_code: 'ru', - language_name: 'Russian', - }, - { - language_code: 'gd', - language_name: 'Scots Gaelic', - }, - { - language_code: 'sr', - language_name: 'Serbian', - }, - { - language_code: 'sh', - language_name: 'Serbo-Croatian', - }, - { - language_code: 'st', - language_name: 'Sesotho', - }, - { - language_code: 'tn', - language_name: 'Setswana', - }, - { - language_code: 'crs', - language_name: 'Seychellois Creole', - }, - { - language_code: 'sn', - language_name: 'Shona', - }, - { - language_code: 'sd', - language_name: 'Sindhi', - }, - { - language_code: 'si', - language_name: 'Sinhalese', - }, - { - language_code: 'sk', - language_name: 'Slovak', - }, - { - language_code: 'sl', - language_name: 'Slovenian', - }, - { - language_code: 'so', - language_name: 'Somali', - }, - { - language_code: 'es', - language_name: 'Spanish', - }, - { - language_code: 'es-419', - language_name: 'Spanish (Latin American)', - }, - { - language_code: 'su', - language_name: 'Sundanese', - }, - { - language_code: 'sw', - language_name: 'Swahili', - }, - { - language_code: 'sv', - language_name: 'Swedish', - }, - { - language_code: 'tg', - language_name: 'Tajik', - }, - { - language_code: 'ta', - language_name: 'Tamil', - }, - { - language_code: 'tt', - language_name: 'Tatar', - }, - { - language_code: 'te', - language_name: 'Telugu', - }, - { - language_code: 'th', - language_name: 'Thai', - }, - { - language_code: 'ti', - language_name: 'Tigrinya', - }, - { - language_code: 'to', - language_name: 'Tonga', - }, - { - language_code: 'lua', - language_name: 'Tshiluba', - }, - { - language_code: 'tum', - language_name: 'Tumbuka', - }, - { - language_code: 'tr', - language_name: 'Turkish', - }, - { - language_code: 'tk', - language_name: 'Turkmen', - }, - { - language_code: 'tw', - language_name: 'Twi', - }, - { - language_code: 'ug', - language_name: 'Uighur', - }, - { - language_code: 'uk', - language_name: 'Ukrainian', - }, - { - language_code: 'ur', - language_name: 'Urdu', - }, - { - language_code: 'uz', - language_name: 'Uzbek', - }, - { - language_code: 'vu', - language_name: 'Vanuatu', - }, - { - language_code: 'vi', - language_name: 'Vietnamese', - }, - { - language_code: 'cy', - language_name: 'Welsh', - }, - { - language_code: 'wo', - language_name: 'Wolof', - }, - { - language_code: 'xh', - language_name: 'Xhosa', - }, - { - language_code: 'yi', - language_name: 'Yiddish', - }, - { - language_code: 'yo', - language_name: 'Yoruba', - }, - { - language_code: 'zu', - language_name: 'Zulu', - }, -].map((x) => ({ label: x.language_name, value: x.language_code })); - -export const GoogleCountryOptions = [ - { - country_code: 'af', - country_name: 'Afghanistan', - }, - { - country_code: 'al', - country_name: 'Albania', - }, - { - country_code: 'dz', - country_name: 'Algeria', - }, - { - country_code: 'as', - country_name: 'American Samoa', - }, - { - country_code: 'ad', - country_name: 'Andorra', - }, - { - country_code: 'ao', - country_name: 'Angola', - }, - { - country_code: 'ai', - country_name: 'Anguilla', - }, - { - country_code: 'aq', - country_name: 'Antarctica', - }, - { - country_code: 'ag', - country_name: 'Antigua and Barbuda', - }, - { - country_code: 'ar', - country_name: 'Argentina', - }, - { - country_code: 'am', - country_name: 'Armenia', - }, - { - country_code: 'aw', - country_name: 'Aruba', - }, - { - country_code: 'au', - country_name: 'Australia', - }, - { - country_code: 'at', - country_name: 'Austria', - }, - { - country_code: 'az', - country_name: 'Azerbaijan', - }, - { - country_code: 'bs', - country_name: 'Bahamas', - }, - { - country_code: 'bh', - country_name: 'Bahrain', - }, - { - country_code: 'bd', - country_name: 'Bangladesh', - }, - { - country_code: 'bb', - country_name: 'Barbados', - }, - { - country_code: 'by', - country_name: 'Belarus', - }, - { - country_code: 'be', - country_name: 'Belgium', - }, - { - country_code: 'bz', - country_name: 'Belize', - }, - { - country_code: 'bj', - country_name: 'Benin', - }, - { - country_code: 'bm', - country_name: 'Bermuda', - }, - { - country_code: 'bt', - country_name: 'Bhutan', - }, - { - country_code: 'bo', - country_name: 'Bolivia', - }, - { - country_code: 'ba', - country_name: 'Bosnia and Herzegovina', - }, - { - country_code: 'bw', - country_name: 'Botswana', - }, - { - country_code: 'bv', - country_name: 'Bouvet Island', - }, - { - country_code: 'br', - country_name: 'Brazil', - }, - { - country_code: 'io', - country_name: 'British Indian Ocean Territory', - }, - { - country_code: 'bn', - country_name: 'Brunei Darussalam', - }, - { - country_code: 'bg', - country_name: 'Bulgaria', - }, - { - country_code: 'bf', - country_name: 'Burkina Faso', - }, - { - country_code: 'bi', - country_name: 'Burundi', - }, - { - country_code: 'kh', - country_name: 'Cambodia', - }, - { - country_code: 'cm', - country_name: 'Cameroon', - }, - { - country_code: 'ca', - country_name: 'Canada', - }, - { - country_code: 'cv', - country_name: 'Cape Verde', - }, - { - country_code: 'ky', - country_name: 'Cayman Islands', - }, - { - country_code: 'cf', - country_name: 'Central African Republic', - }, - { - country_code: 'td', - country_name: 'Chad', - }, - { - country_code: 'cl', - country_name: 'Chile', - }, - { - country_code: 'cn', - country_name: 'China', - }, - { - country_code: 'cx', - country_name: 'Christmas Island', - }, - { - country_code: 'cc', - country_name: 'Cocos (Keeling) Islands', - }, - { - country_code: 'co', - country_name: 'Colombia', - }, - { - country_code: 'km', - country_name: 'Comoros', - }, - { - country_code: 'cg', - country_name: 'Congo', - }, - { - country_code: 'cd', - country_name: 'Congo, the Democratic Republic of the', - }, - { - country_code: 'ck', - country_name: 'Cook Islands', - }, - { - country_code: 'cr', - country_name: 'Costa Rica', - }, - { - country_code: 'ci', - country_name: "Cote D'ivoire", - }, - { - country_code: 'hr', - country_name: 'Croatia', - }, - { - country_code: 'cu', - country_name: 'Cuba', - }, - { - country_code: 'cy', - country_name: 'Cyprus', - }, - { - country_code: 'cz', - country_name: 'Czech Republic', - }, - { - country_code: 'dk', - country_name: 'Denmark', - }, - { - country_code: 'dj', - country_name: 'Djibouti', - }, - { - country_code: 'dm', - country_name: 'Dominica', - }, - { - country_code: 'do', - country_name: 'Dominican Republic', - }, - { - country_code: 'ec', - country_name: 'Ecuador', - }, - { - country_code: 'eg', - country_name: 'Egypt', - }, - { - country_code: 'sv', - country_name: 'El Salvador', - }, - { - country_code: 'gq', - country_name: 'Equatorial Guinea', - }, - { - country_code: 'er', - country_name: 'Eritrea', - }, - { - country_code: 'ee', - country_name: 'Estonia', - }, - { - country_code: 'et', - country_name: 'Ethiopia', - }, - { - country_code: 'fk', - country_name: 'Falkland Islands (Malvinas)', - }, - { - country_code: 'fo', - country_name: 'Faroe Islands', - }, - { - country_code: 'fj', - country_name: 'Fiji', - }, - { - country_code: 'fi', - country_name: 'Finland', - }, - { - country_code: 'fr', - country_name: 'France', - }, - { - country_code: 'gf', - country_name: 'French Guiana', - }, - { - country_code: 'pf', - country_name: 'French Polynesia', - }, - { - country_code: 'tf', - country_name: 'French Southern Territories', - }, - { - country_code: 'ga', - country_name: 'Gabon', - }, - { - country_code: 'gm', - country_name: 'Gambia', - }, - { - country_code: 'ge', - country_name: 'Georgia', - }, - { - country_code: 'de', - country_name: 'Germany', - }, - { - country_code: 'gh', - country_name: 'Ghana', - }, - { - country_code: 'gi', - country_name: 'Gibraltar', - }, - { - country_code: 'gr', - country_name: 'Greece', - }, - { - country_code: 'gl', - country_name: 'Greenland', - }, - { - country_code: 'gd', - country_name: 'Grenada', - }, - { - country_code: 'gp', - country_name: 'Guadeloupe', - }, - { - country_code: 'gu', - country_name: 'Guam', - }, - { - country_code: 'gt', - country_name: 'Guatemala', - }, - { - country_code: 'gn', - country_name: 'Guinea', - }, - { - country_code: 'gw', - country_name: 'Guinea-Bissau', - }, - { - country_code: 'gy', - country_name: 'Guyana', - }, - { - country_code: 'ht', - country_name: 'Haiti', - }, - { - country_code: 'hm', - country_name: 'Heard Island and Mcdonald Islands', - }, - { - country_code: 'va', - country_name: 'Holy See (Vatican City State)', - }, - { - country_code: 'hn', - country_name: 'Honduras', - }, - { - country_code: 'hk', - country_name: 'Hong Kong', - }, - { - country_code: 'hu', - country_name: 'Hungary', - }, - { - country_code: 'is', - country_name: 'Iceland', - }, - { - country_code: 'in', - country_name: 'India', - }, - { - country_code: 'id', - country_name: 'Indonesia', - }, - { - country_code: 'ir', - country_name: 'Iran, Islamic Republic of', - }, - { - country_code: 'iq', - country_name: 'Iraq', - }, - { - country_code: 'ie', - country_name: 'Ireland', - }, - { - country_code: 'il', - country_name: 'Israel', - }, - { - country_code: 'it', - country_name: 'Italy', - }, - { - country_code: 'jm', - country_name: 'Jamaica', - }, - { - country_code: 'jp', - country_name: 'Japan', - }, - { - country_code: 'jo', - country_name: 'Jordan', - }, - { - country_code: 'kz', - country_name: 'Kazakhstan', - }, - { - country_code: 'ke', - country_name: 'Kenya', - }, - { - country_code: 'ki', - country_name: 'Kiribati', - }, - { - country_code: 'kp', - country_name: "Korea, Democratic People's Republic of", - }, - { - country_code: 'kr', - country_name: 'Korea, Republic of', - }, - { - country_code: 'kw', - country_name: 'Kuwait', - }, - { - country_code: 'kg', - country_name: 'Kyrgyzstan', - }, - { - country_code: 'la', - country_name: "Lao People's Democratic Republic", - }, - { - country_code: 'lv', - country_name: 'Latvia', - }, - { - country_code: 'lb', - country_name: 'Lebanon', - }, - { - country_code: 'ls', - country_name: 'Lesotho', - }, - { - country_code: 'lr', - country_name: 'Liberia', - }, - { - country_code: 'ly', - country_name: 'Libyan Arab Jamahiriya', - }, - { - country_code: 'li', - country_name: 'Liechtenstein', - }, - { - country_code: 'lt', - country_name: 'Lithuania', - }, - { - country_code: 'lu', - country_name: 'Luxembourg', - }, - { - country_code: 'mo', - country_name: 'Macao', - }, - { - country_code: 'mk', - country_name: 'Macedonia, the Former Yugosalv Republic of', - }, - { - country_code: 'mg', - country_name: 'Madagascar', - }, - { - country_code: 'mw', - country_name: 'Malawi', - }, - { - country_code: 'my', - country_name: 'Malaysia', - }, - { - country_code: 'mv', - country_name: 'Maldives', - }, - { - country_code: 'ml', - country_name: 'Mali', - }, - { - country_code: 'mt', - country_name: 'Malta', - }, - { - country_code: 'mh', - country_name: 'Marshall Islands', - }, - { - country_code: 'mq', - country_name: 'Martinique', - }, - { - country_code: 'mr', - country_name: 'Mauritania', - }, - { - country_code: 'mu', - country_name: 'Mauritius', - }, - { - country_code: 'yt', - country_name: 'Mayotte', - }, - { - country_code: 'mx', - country_name: 'Mexico', - }, - { - country_code: 'fm', - country_name: 'Micronesia, Federated States of', - }, - { - country_code: 'md', - country_name: 'Moldova, Republic of', - }, - { - country_code: 'mc', - country_name: 'Monaco', - }, - { - country_code: 'mn', - country_name: 'Mongolia', - }, - { - country_code: 'ms', - country_name: 'Montserrat', - }, - { - country_code: 'ma', - country_name: 'Morocco', - }, - { - country_code: 'mz', - country_name: 'Mozambique', - }, - { - country_code: 'mm', - country_name: 'Myanmar', - }, - { - country_code: 'na', - country_name: 'Namibia', - }, - { - country_code: 'nr', - country_name: 'Nauru', - }, - { - country_code: 'np', - country_name: 'Nepal', - }, - { - country_code: 'nl', - country_name: 'Netherlands', - }, - { - country_code: 'an', - country_name: 'Netherlands Antilles', - }, - { - country_code: 'nc', - country_name: 'New Caledonia', - }, - { - country_code: 'nz', - country_name: 'New Zealand', - }, - { - country_code: 'ni', - country_name: 'Nicaragua', - }, - { - country_code: 'ne', - country_name: 'Niger', - }, - { - country_code: 'ng', - country_name: 'Nigeria', - }, - { - country_code: 'nu', - country_name: 'Niue', - }, - { - country_code: 'nf', - country_name: 'Norfolk Island', - }, - { - country_code: 'mp', - country_name: 'Northern Mariana Islands', - }, - { - country_code: 'no', - country_name: 'Norway', - }, - { - country_code: 'om', - country_name: 'Oman', - }, - { - country_code: 'pk', - country_name: 'Pakistan', - }, - { - country_code: 'pw', - country_name: 'Palau', - }, - { - country_code: 'ps', - country_name: 'Palestinian Territory, Occupied', - }, - { - country_code: 'pa', - country_name: 'Panama', - }, - { - country_code: 'pg', - country_name: 'Papua New Guinea', - }, - { - country_code: 'py', - country_name: 'Paraguay', - }, - { - country_code: 'pe', - country_name: 'Peru', - }, - { - country_code: 'ph', - country_name: 'Philippines', - }, - { - country_code: 'pn', - country_name: 'Pitcairn', - }, - { - country_code: 'pl', - country_name: 'Poland', - }, - { - country_code: 'pt', - country_name: 'Portugal', - }, - { - country_code: 'pr', - country_name: 'Puerto Rico', - }, - { - country_code: 'qa', - country_name: 'Qatar', - }, - { - country_code: 're', - country_name: 'Reunion', - }, - { - country_code: 'ro', - country_name: 'Romania', - }, - { - country_code: 'ru', - country_name: 'Russian Federation', - }, - { - country_code: 'rw', - country_name: 'Rwanda', - }, - { - country_code: 'sh', - country_name: 'Saint Helena', - }, - { - country_code: 'kn', - country_name: 'Saint Kitts and Nevis', - }, - { - country_code: 'lc', - country_name: 'Saint Lucia', - }, - { - country_code: 'pm', - country_name: 'Saint Pierre and Miquelon', - }, - { - country_code: 'vc', - country_name: 'Saint Vincent and the Grenadines', - }, - { - country_code: 'ws', - country_name: 'Samoa', - }, - { - country_code: 'sm', - country_name: 'San Marino', - }, - { - country_code: 'st', - country_name: 'Sao Tome and Principe', - }, - { - country_code: 'sa', - country_name: 'Saudi Arabia', - }, - { - country_code: 'sn', - country_name: 'Senegal', - }, - { - country_code: 'rs', - country_name: 'Serbia and Montenegro', - }, - { - country_code: 'sc', - country_name: 'Seychelles', - }, - { - country_code: 'sl', - country_name: 'Sierra Leone', - }, - { - country_code: 'sg', - country_name: 'Singapore', - }, - { - country_code: 'sk', - country_name: 'Slovakia', - }, - { - country_code: 'si', - country_name: 'Slovenia', - }, - { - country_code: 'sb', - country_name: 'Solomon Islands', - }, - { - country_code: 'so', - country_name: 'Somalia', - }, - { - country_code: 'za', - country_name: 'South Africa', - }, - { - country_code: 'gs', - country_name: 'South Georgia and the South Sandwich Islands', - }, - { - country_code: 'es', - country_name: 'Spain', - }, - { - country_code: 'lk', - country_name: 'Sri Lanka', - }, - { - country_code: 'sd', - country_name: 'Sudan', - }, - { - country_code: 'sr', - country_name: 'Suriname', - }, - { - country_code: 'sj', - country_name: 'Svalbard and Jan Mayen', - }, - { - country_code: 'sz', - country_name: 'Swaziland', - }, - { - country_code: 'se', - country_name: 'Sweden', - }, - { - country_code: 'ch', - country_name: 'Switzerland', - }, - { - country_code: 'sy', - country_name: 'Syrian Arab Republic', - }, - { - country_code: 'tw', - country_name: 'Taiwan, Province of China', - }, - { - country_code: 'tj', - country_name: 'Tajikistan', - }, - { - country_code: 'tz', - country_name: 'Tanzania, United Republic of', - }, - { - country_code: 'th', - country_name: 'Thailand', - }, - { - country_code: 'tl', - country_name: 'Timor-Leste', - }, - { - country_code: 'tg', - country_name: 'Togo', - }, - { - country_code: 'tk', - country_name: 'Tokelau', - }, - { - country_code: 'to', - country_name: 'Tonga', - }, - { - country_code: 'tt', - country_name: 'Trinidad and Tobago', - }, - { - country_code: 'tn', - country_name: 'Tunisia', - }, - { - country_code: 'tr', - country_name: 'Turkiye', - }, - { - country_code: 'tm', - country_name: 'Turkmenistan', - }, - { - country_code: 'tc', - country_name: 'Turks and Caicos Islands', - }, - { - country_code: 'tv', - country_name: 'Tuvalu', - }, - { - country_code: 'ug', - country_name: 'Uganda', - }, - { - country_code: 'ua', - country_name: 'Ukraine', - }, - { - country_code: 'ae', - country_name: 'United Arab Emirates', - }, - { - country_code: 'uk', - country_name: 'United Kingdom', - }, - { - country_code: 'gb', - country_name: 'United Kingdom', - }, - { - country_code: 'us', - country_name: 'United States', - }, - { - country_code: 'um', - country_name: 'United States Minor Outlying Islands', - }, - { - country_code: 'uy', - country_name: 'Uruguay', - }, - { - country_code: 'uz', - country_name: 'Uzbekistan', - }, - { - country_code: 'vu', - country_name: 'Vanuatu', - }, - { - country_code: 've', - country_name: 'Venezuela', - }, - { - country_code: 'vn', - country_name: 'Viet Nam', - }, - { - country_code: 'vg', - country_name: 'Virgin Islands, British', - }, - { - country_code: 'vi', - country_name: 'Virgin Islands, U.S.', - }, - { - country_code: 'wf', - country_name: 'Wallis and Futuna', - }, - { - country_code: 'eh', - country_name: 'Western Sahara', - }, - { - country_code: 'ye', - country_name: 'Yemen', - }, - { - country_code: 'zm', - country_name: 'Zambia', - }, - { - country_code: 'zw', - country_name: 'Zimbabwe', - }, -].map((x) => ({ label: x.country_name, value: x.country_code })); - -export const BingCountryOptions = [ - { label: 'Argentina AR', value: 'AR' }, - { label: 'Australia AU', value: 'AU' }, - { label: 'Austria AT', value: 'AT' }, - { label: 'Belgium BE', value: 'BE' }, - { label: 'Brazil BR', value: 'BR' }, - { label: 'Canada CA', value: 'CA' }, - { label: 'Chile CL', value: 'CL' }, - { label: 'Denmark DK', value: 'DK' }, - { label: 'Finland FI', value: 'FI' }, - { label: 'France FR', value: 'FR' }, - { label: 'Germany DE', value: 'DE' }, - { label: 'Hong Kong SAR HK', value: 'HK' }, - { label: 'India IN', value: 'IN' }, - { label: 'Indonesia ID', value: 'ID' }, - { label: 'Italy IT', value: 'IT' }, - { label: 'Japan JP', value: 'JP' }, - { label: 'Korea KR', value: 'KR' }, - { label: 'Malaysia MY', value: 'MY' }, - { label: 'Mexico MX', value: 'MX' }, - { label: 'Netherlands NL', value: 'NL' }, - { label: 'New Zealand NZ', value: 'NZ' }, - { label: 'Norway NO', value: 'NO' }, - { label: "People's Republic of China CN", value: 'CN' }, - { label: 'Poland PL', value: 'PL' }, - { label: 'Portugal PT', value: 'PT' }, - { label: 'Republic of the Philippines PH', value: 'PH' }, - { label: 'Russia RU', value: 'RU' }, - { label: 'Saudi Arabia SA', value: 'SA' }, - { label: 'South Africa ZA', value: 'ZA' }, - { label: 'Spain ES', value: 'ES' }, - { label: 'Sweden SE', value: 'SE' }, - { label: 'Switzerland CH', value: 'CH' }, - { label: 'Taiwan TW', value: 'TW' }, - { label: 'Türkiye TR', value: 'TR' }, - { label: 'United Kingdom GB', value: 'GB' }, - { label: 'United States US', value: 'US' }, -]; - -export const BingLanguageOptions = [ - { label: 'Arabic ar', value: 'ar' }, - { label: 'Basque eu', value: 'eu' }, - { label: 'Bengali bn', value: 'bn' }, - { label: 'Bulgarian bg', value: 'bg' }, - { label: 'Catalan ca', value: 'ca' }, - { label: 'Chinese (Simplified) zh-hans', value: 'ns' }, - { label: 'Chinese (Traditional) zh-hant', value: 'nt' }, - { label: 'Croatian hr', value: 'hr' }, - { label: 'Czech cs', value: 'cs' }, - { label: 'Danish da', value: 'da' }, - { label: 'Dutch nl', value: 'nl' }, - { label: 'English en', value: 'en' }, - { label: 'English-United Kingdom en-gb', value: 'gb' }, - { label: 'Estonian et', value: 'et' }, - { label: 'Finnish fi', value: 'fi' }, - { label: 'French fr', value: 'fr' }, - { label: 'Galician gl', value: 'gl' }, - { label: 'German de', value: 'de' }, - { label: 'Gujarati gu', value: 'gu' }, - { label: 'Hebrew he', value: 'he' }, - { label: 'Hindi hi', value: 'hi' }, - { label: 'Hungarian hu', value: 'hu' }, - { label: 'Icelandic is', value: 'is' }, - { label: 'Italian it', value: 'it' }, - { label: 'Japanese jp', value: 'jp' }, - { label: 'Kannada kn', value: 'kn' }, - { label: 'Korean ko', value: 'ko' }, - { label: 'Latvian lv', value: 'lv' }, - { label: 'Lithuanian lt', value: 'lt' }, - { label: 'Malay ms', value: 'ms' }, - { label: 'Malayalam ml', value: 'ml' }, - { label: 'Marathi mr', value: 'mr' }, - { label: 'Norwegian (Bokmål) nb', value: 'nb' }, - { label: 'Polish pl', value: 'pl' }, - { label: 'Portuguese (Brazil) pt-br', value: 'br' }, - { label: 'Portuguese (Portugal) pt-pt', value: 'pt' }, - { label: 'Punjabi pa', value: 'pa' }, - { label: 'Romanian ro', value: 'ro' }, - { label: 'Russian ru', value: 'ru' }, - { label: 'Serbian (Cyrylic) sr', value: 'sr' }, - { label: 'Slovak sk', value: 'sk' }, - { label: 'Slovenian sl', value: 'sl' }, - { label: 'Spanish es', value: 'es' }, - { label: 'Swedish sv', value: 'sv' }, - { label: 'Tamil ta', value: 'ta' }, - { label: 'Telugu te', value: 'te' }, - { label: 'Thai th', value: 'th' }, - { label: 'Turkish tr', value: 'tr' }, - { label: 'Ukrainian uk', value: 'uk' }, - { label: 'Vietnamese vi', value: 'vi' }, -]; - -export const DeepLSourceLangOptions = [ - { label: 'Arabic [1]', value: 'AR' }, - { label: 'Bulgarian', value: 'BG' }, - { label: 'Czech', value: 'CS' }, - { label: 'Danish', value: 'DA' }, - { label: 'German', value: 'DE' }, - { label: 'Greek', value: 'EL' }, - { label: 'English', value: 'EN' }, - { label: 'Spanish', value: 'ES' }, - { label: 'Estonian', value: 'ET' }, - { label: 'Finnish', value: 'FI' }, - { label: 'French', value: 'FR' }, - { label: 'Hungarian', value: 'HU' }, - { label: 'Indonesian', value: 'ID' }, - { label: 'Italian', value: 'IT' }, - { label: 'Japanese', value: 'JA' }, - { label: 'Korean', value: 'KO' }, - { label: 'Lithuanian', value: 'LT' }, - { label: 'Latvian', value: 'LV' }, - { label: 'Norwegian Bokmål', value: 'NB' }, - { label: 'Dutch', value: 'NL' }, - { label: 'Polish', value: 'PL' }, - { label: 'Portuguese (all Portuguese varieties mixed)', value: 'PT' }, - { label: 'Romanian', value: 'RO' }, - { label: 'Russian', value: 'RU' }, - { label: 'Slovak', value: 'SK' }, - { label: 'Slovenian', value: 'SL' }, - { label: 'Swedish', value: 'SV' }, - { label: 'Turkish', value: 'TR' }, - { label: 'Ukrainian', value: 'UK' }, - { label: 'Chinese', value: 'ZH' }, -]; -export const DeepLTargetLangOptions = [ - { label: 'Arabic [1]', value: 'AR' }, - { label: 'Bulgarian', value: 'BG' }, - { label: 'Czech', value: 'CS' }, - { label: 'Danish', value: 'DA' }, - { label: 'German', value: 'DE' }, - { label: 'Greek', value: 'EL' }, - { label: 'English (British)', value: 'EN-GB' }, - { label: 'English (American)', value: 'EN-US' }, - { label: 'Spanish', value: 'ES' }, - { label: 'Estonian', value: 'ET' }, - { label: 'Finnish', value: 'FI' }, - { label: 'French', value: 'FR' }, - { label: 'Hungarian', value: 'HU' }, - { label: 'Indonesian', value: 'ID' }, - { label: 'Italian', value: 'IT' }, - { label: 'Japanese', value: 'JA' }, - { label: 'Korean', value: 'KO' }, - { label: 'Lithuanian', value: 'LT' }, - { label: 'Latvian', value: 'LV' }, - { label: 'Norwegian Bokmål', value: 'NB' }, - { label: 'Dutch', value: 'NL' }, - { label: 'Polish', value: 'PL' }, - { label: 'Portuguese (Brazilian)', value: 'PT-BR' }, - { - label: - 'Portuguese (all Portuguese varieties excluding Brazilian Portuguese)', - value: 'PT-PT', - }, - { label: 'Romanian', value: 'RO' }, - { label: 'Russian', value: 'RU' }, - { label: 'Slovak', value: 'SK' }, - { label: 'Slovenian', value: 'SL' }, - { label: 'Swedish', value: 'SV' }, - { label: 'Turkish', value: 'TR' }, - { label: 'Ukrainian', value: 'UK' }, - { label: 'Chinese (simplified)', value: 'ZH' }, -]; - -export const BaiduFanyiDomainOptions = [ - 'it', - 'finance', - 'machinery', - 'senimed', - 'novel', - 'academic', - 'aerospace', - 'wiki', - 'news', - 'law', - 'contract', -]; - -export const BaiduFanyiSourceLangOptions = [ - 'auto', - 'zh', - 'en', - 'yue', - 'wyw', - 'jp', - 'kor', - 'fra', - 'spa', - 'th', - 'ara', - 'ru', - 'pt', - 'de', - 'it', - 'el', - 'nl', - 'pl', - 'bul', - 'est', - 'dan', - 'fin', - 'cs', - 'rom', - 'slo', - 'swe', - 'hu', - 'cht', - 'vie', -]; - -export const QWeatherLangOptions = [ - 'zh', - 'zh-hant', - 'en', - 'de', - 'es', - 'fr', - 'it', - 'ja', - 'ko', - 'ru', - 'hi', - 'th', - 'ar', - 'pt', - 'bn', - 'ms', - 'nl', - 'el', - 'la', - 'sv', - 'id', - 'pl', - 'tr', - 'cs', - 'et', - 'vi', - 'fil', - 'fi', - 'he', - 'is', - 'nb', -]; - -export const QWeatherTypeOptions = ['weather', 'indices', 'airquality']; - -export const QWeatherUserTypeOptions = ['free', 'paid']; - -export const QWeatherTimePeriodOptions = [ - 'now', - '3d', - '7d', - '10d', - '15d', - '30d', -]; - -export const ExeSQLOptions = [ - 'mysql', - 'postgres', - 'mariadb', - 'mssql', - 'IBM DB2', -].map((x) => ({ - label: upperFirst(x), - value: x, -})); - -export const WenCaiQueryTypeOptions = [ - 'stock', - 'zhishu', - 'fund', - 'hkstock', - 'usstock', - 'threeboard', - 'conbond', - 'insurance', - 'futures', - 'lccp', - 'foreign_exchange', -]; - -export const Jin10TypeOptions = ['flash', 'calendar', 'symbols', 'news']; -export const Jin10FlashTypeOptions = new Array(5) - .fill(1) - .map((x, idx) => (idx + 1).toString()); -export const Jin10CalendarTypeOptions = ['cj', 'qh', 'hk', 'us']; -export const Jin10CalendarDatashapeOptions = ['data', 'event', 'holiday']; -export const Jin10SymbolsTypeOptions = ['GOODS', 'FOREX', 'FUTURE', 'CRYPTO']; -export const Jin10SymbolsDatatypeOptions = ['symbols', 'quotes']; -export const TuShareSrcOptions = [ - 'sina', - 'wallstreetcn', - '10jqka', - 'eastmoney', - 'yuncaijing', - 'fenghuang', - 'jinrongjie', -]; -export const CrawlerResultOptions = ['markdown', 'html', 'content']; diff --git a/web/src/pages/data-flow/run-sheet/index.tsx b/web/src/pages/data-flow/run-sheet/index.tsx index cac62d008ca..7ee9adc35bb 100644 --- a/web/src/pages/data-flow/run-sheet/index.tsx +++ b/web/src/pages/data-flow/run-sheet/index.tsx @@ -6,60 +6,22 @@ import { } from '@/components/ui/sheet'; import { IModalProps } from '@/interfaces/common'; import { cn } from '@/lib/utils'; -import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { BeginId } from '../constant'; -import DebugContent from '../debug-content'; -import { useGetBeginNodeDataInputs } from '../hooks/use-get-begin-query'; -import { useSaveGraphBeforeOpeningDebugDrawer } from '../hooks/use-save-graph'; -import { BeginQuery } from '../interface'; -import useGraphStore from '../store'; -import { buildBeginQueryWithObject } from '../utils'; +import { RunDataflowType } from '../hooks/use-run-dataflow'; +import { UploaderForm } from './uploader'; -const RunSheet = ({ - hideModal, - showModal: showChatModal, -}: IModalProps) => { - const { t } = useTranslation(); - const { updateNodeForm, getNode } = useGraphStore((state) => state); - - const inputs = useGetBeginNodeDataInputs(); - - const { handleRun, loading } = useSaveGraphBeforeOpeningDebugDrawer( - showChatModal!, - ); - - const handleRunAgent = useCallback( - (nextValues: BeginQuery[]) => { - const beginNode = getNode(BeginId); - const inputs: Record = beginNode?.data.form.inputs; +type RunSheetProps = IModalProps & + Pick; - const nextInputs = buildBeginQueryWithObject(inputs, nextValues); - - const currentNodes = updateNodeForm(BeginId, nextInputs, ['inputs']); - handleRun(currentNodes); - hideModal?.(); - }, - [getNode, handleRun, hideModal, updateNodeForm], - ); - - const onOk = useCallback( - async (nextValues: any[]) => { - handleRunAgent(nextValues); - }, - [handleRunAgent], - ); +const RunSheet = ({ hideModal, run, loading }: RunSheetProps) => { + const { t } = useTranslation(); return ( - + {t('flow.testRun')} - + diff --git a/web/src/pages/data-flow/run-sheet/uploader.tsx b/web/src/pages/data-flow/run-sheet/uploader.tsx new file mode 100644 index 00000000000..db4a977dd93 --- /dev/null +++ b/web/src/pages/data-flow/run-sheet/uploader.tsx @@ -0,0 +1,57 @@ +'use client'; + +import { z } from 'zod'; + +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { ButtonLoading } from '@/components/ui/button'; +import { Form } from '@/components/ui/form'; +import { FileUploadDirectUpload } from '@/pages/agent/debug-content/uploader'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +const formSchema = z.object({ + file: z.record(z.any()), +}); + +export type FormSchemaType = z.infer; + +type UploaderFormProps = { + ok: (values: FormSchemaType) => void; + loading: boolean; +}; + +export function UploaderForm({ ok, loading }: UploaderFormProps) { + const { t } = useTranslation(); + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: {}, + }); + + return ( +
    + + + {(field) => { + return ( + + ); + }} + + +
    + + {t('flow.run')} + +
    +
    + + ); +} diff --git a/web/src/pages/data-flow/store.ts b/web/src/pages/data-flow/store.ts index e163ff8cb9a..64fda97a408 100644 --- a/web/src/pages/data-flow/store.ts +++ b/web/src/pages/data-flow/store.ts @@ -14,7 +14,6 @@ import { applyEdgeChanges, applyNodeChanges, } from '@xyflow/react'; -import { omit } from 'lodash'; import differenceWith from 'lodash/differenceWith'; import intersectionWith from 'lodash/intersectionWith'; import lodashSet from 'lodash/set'; @@ -59,7 +58,6 @@ export type RFState = { updateNode: (node: RAGFlowNodeType) => void; addEdge: (connection: Connection) => void; getEdge: (id: string) => Edge | undefined; - updateFormDataOnConnect: (connection: Connection) => void; updateSwitchFormData: ( source: string, sourceHandle?: string | null, @@ -67,7 +65,6 @@ export type RFState = { isConnecting?: boolean, ) => void; duplicateNode: (id: string, name: string) => void; - duplicateIterationNode: (id: string, name: string) => void; deleteEdge: () => void; deleteEdgeById: (id: string) => void; deleteNodeById: (id: string) => void; @@ -89,6 +86,7 @@ export type RFState = { ) => void; // Deleting a condition of a classification operator will delete the related edge findAgentToolNodeById: (id: string | null) => string | undefined; selectNodeIds: (nodeIds: string[]) => void; + hasChildNode: (nodeId: string) => boolean; }; // this is our useStore hook that we can use in our components to get parts of the store and call actions @@ -126,11 +124,9 @@ const useGraphStore = create()( setEdges(mapEdgeMouseEvent(edges, edgeId, false)); }, onConnect: (connection: Connection) => { - const { updateFormDataOnConnect } = get(); set({ edges: addEdge(connection, get().edges), }); - updateFormDataOnConnect(connection); }, onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => { set({ @@ -217,37 +213,14 @@ const useGraphStore = create()( set({ edges: addEdge(connection, get().edges), }); - // TODO: This may not be reasonable. You need to choose between listening to changes in the form. - get().updateFormDataOnConnect(connection); }, getEdge: (id: string) => { return get().edges.find((x) => x.id === id); }, - updateFormDataOnConnect: (connection: Connection) => { - const { getOperatorTypeFromId, updateSwitchFormData } = get(); - const { source, target, sourceHandle } = connection; - const operatorType = getOperatorTypeFromId(source); - if (source) { - switch (operatorType) { - case Operator.Switch: { - updateSwitchFormData(source, sourceHandle, target, true); - break; - } - default: - break; - } - } - }, duplicateNode: (id: string, name: string) => { - const { getNode, addNode, generateNodeName, duplicateIterationNode } = - get(); + const { getNode, addNode, generateNodeName } = get(); const node = getNode(id); - if (node?.data.label === Operator.Iteration) { - duplicateIterationNode(id, name); - return; - } - addNode({ ...(node || {}), data: { @@ -257,35 +230,6 @@ const useGraphStore = create()( ...generateDuplicateNode(node?.position, node?.data?.label), }); }, - duplicateIterationNode: (id: string, name: string) => { - const { getNode, generateNodeName, nodes } = get(); - const node = getNode(id); - - const iterationNode: RAGFlowNodeType = { - ...(node || {}), - data: { - ...(node?.data || { label: Operator.Iteration, form: {} }), - name: generateNodeName(name), - }, - ...generateDuplicateNode(node?.position, node?.data?.label), - }; - - const children = nodes - .filter((x) => x.parentId === node?.id) - .map((x) => ({ - ...(x || {}), - data: { - ...duplicateNodeForm(x?.data), - name: generateNodeName(x.data.name), - }, - ...omit(generateDuplicateNode(x?.position, x?.data?.label), [ - 'position', - ]), - parentId: iterationNode.id, - })); - - set({ nodes: nodes.concat(iterationNode, ...children) }); - }, deleteEdge: () => { const { edges, selectedEdgeIds } = get(); set({ @@ -295,55 +239,15 @@ const useGraphStore = create()( }); }, deleteEdgeById: (id: string) => { - const { - edges, - updateNodeForm, - getOperatorTypeFromId, - updateSwitchFormData, - } = get(); - const currentEdge = edges.find((x) => x.id === id); + const { edges } = get(); - if (currentEdge) { - const { source, sourceHandle, target } = currentEdge; - const operatorType = getOperatorTypeFromId(source); - // After deleting the edge, set the corresponding field in the node's form field to undefined - switch (operatorType) { - case Operator.Relevant: - updateNodeForm(source, { - [sourceHandle as string]: undefined, - }); - break; - // case Operator.Categorize: - // if (sourceHandle) - // updateNodeForm(source, undefined, [ - // 'category_description', - // sourceHandle, - // 'to', - // ]); - // break; - case Operator.Switch: { - updateSwitchFormData(source, sourceHandle, target, false); - break; - } - default: - break; - } - } set({ edges: edges.filter((edge) => edge.id !== id), }); }, deleteNodeById: (id: string) => { - const { - nodes, - edges, - getOperatorTypeFromId, - deleteAgentDownstreamNodesById, - } = get(); - if (getOperatorTypeFromId(id) === Operator.Agent) { - deleteAgentDownstreamNodesById(id); - return; - } + const { nodes, edges } = get(); + set({ nodes: nodes.filter((node) => node.id !== id), edges: edges @@ -526,6 +430,10 @@ const useGraphStore = create()( })), ); }, + hasChildNode: (nodeId) => { + const { edges } = get(); + return edges.some((edge) => edge.source === nodeId); + }, })), { name: 'graph', trace: true }, ), diff --git a/web/src/pages/data-flow/upload-agent-dialog/index.tsx b/web/src/pages/data-flow/upload-agent-dialog/index.tsx deleted file mode 100644 index 45f9ec88222..00000000000 --- a/web/src/pages/data-flow/upload-agent-dialog/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { LoadingButton } from '@/components/ui/loading-button'; -import { IModalProps } from '@/interfaces/common'; -import { TagRenameId } from '@/pages/add-knowledge/constant'; -import { useTranslation } from 'react-i18next'; -import { UploadAgentForm } from './upload-agent-form'; - -export function UploadAgentDialog({ - hideModal, - onOk, - loading, -}: IModalProps) { - const { t } = useTranslation(); - - return ( - - - - {t('fileManager.uploadFile')} - - - - - {t('common.save')} - - - - - ); -} diff --git a/web/src/pages/data-flow/upload-agent-dialog/upload-agent-form.tsx b/web/src/pages/data-flow/upload-agent-dialog/upload-agent-form.tsx deleted file mode 100644 index 39a4ac01250..00000000000 --- a/web/src/pages/data-flow/upload-agent-dialog/upload-agent-form.tsx +++ /dev/null @@ -1,89 +0,0 @@ -'use client'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { FileUploader } from '@/components/file-uploader'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { FileMimeType, Platform } from '@/constants/common'; -import { IModalProps } from '@/interfaces/common'; -import { TagRenameId } from '@/pages/add-knowledge/constant'; -import { useTranslation } from 'react-i18next'; - -// const options = Object.values(Platform).map((x) => ({ label: x, value: x })); - -export function UploadAgentForm({ hideModal, onOk }: IModalProps) { - const { t } = useTranslation(); - const FormSchema = z.object({ - platform: z - .string() - .min(1, { - message: t('common.namePlaceholder'), - }) - .trim(), - fileList: z.array(z.instanceof(File)), - }); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { platform: Platform.RAGFlow }, - }); - - async function onSubmit(data: z.infer) { - console.log('🚀 ~ onSubmit ~ data:', data); - const ret = await onOk?.(data); - if (ret) { - hideModal?.(); - } - } - - return ( -
    - - ( - - {t('common.name')} - - - - - - )} - /> - {/* ( - - {t('common.name')} - - - - - - )} - /> */} - - - ); -} diff --git a/web/src/pages/data-flow/utils.ts b/web/src/pages/data-flow/utils.ts index c7d60dfd2c5..e766e085117 100644 --- a/web/src/pages/data-flow/utils.ts +++ b/web/src/pages/data-flow/utils.ts @@ -1,56 +1,36 @@ -import { - IAgentForm, - ICategorizeForm, - ICategorizeItem, - ICategorizeItemResult, -} from '@/interfaces/database/agent'; +import { IAgentForm } from '@/interfaces/database/agent'; import { DSLComponents, RAGFlowNodeType } from '@/interfaces/database/flow'; -import { removeUselessFieldsFromValues } from '@/utils/form'; -import { Edge, Node, XYPosition } from '@xyflow/react'; +import { Edge, XYPosition } from '@xyflow/react'; import { FormInstance, FormListFieldData } from 'antd'; import { humanId } from 'human-id'; -import { curry, get, intersectionWith, isEqual, omit, sample } from 'lodash'; +import { curry, get, intersectionWith, isEmpty, isEqual, sample } from 'lodash'; import pipe from 'lodash/fp/pipe'; import isObject from 'lodash/isObject'; import { CategorizeAnchorPointPositions, + FileType, + FileTypeSuffixMap, NoDebugOperatorsList, NodeHandleId, Operator, } from './constant'; -import { BeginQuery, IPosition } from './interface'; - -function buildAgentExceptionGoto(edges: Edge[], nodeId: string) { - const exceptionEdges = edges.filter( - (x) => - x.source === nodeId && x.sourceHandle === NodeHandleId.AgentException, - ); - - return exceptionEdges.map((x) => x.target); -} +import { ExtractorFormSchemaType } from './form/extractor-form'; +import { HierarchicalMergerFormSchemaType } from './form/hierarchical-merger-form'; +import { ParserFormSchemaType } from './form/parser-form'; +import { SplitterFormSchemaType } from './form/splitter-form'; +import { IPosition } from './interface'; const buildComponentDownstreamOrUpstream = ( edges: Edge[], nodeId: string, isBuildDownstream = true, - nodes: Node[], ) => { return edges .filter((y) => { - const node = nodes.find((x) => x.id === nodeId); let isNotUpstreamTool = true; let isNotUpstreamAgent = true; let isNotExceptionGoto = true; - if (isBuildDownstream && node?.data.label === Operator.Agent) { - isNotExceptionGoto = y.sourceHandle !== NodeHandleId.AgentException; - // Exclude the tool operator downstream of the agent operator - isNotUpstreamTool = !y.target.startsWith(Operator.Tool); - // Exclude the agent operator downstream of the agent operator - isNotUpstreamAgent = !( - y.target.startsWith(Operator.Agent) && - y.targetHandle === NodeHandleId.AgentTop - ); - } + return ( y[isBuildDownstream ? 'source' : 'target'] === nodeId && isNotUpstreamTool && @@ -63,79 +43,12 @@ const buildComponentDownstreamOrUpstream = ( const removeUselessDataInTheOperator = curry( (operatorName: string, params: Record) => { - if ( - operatorName === Operator.Generate || - operatorName === Operator.Categorize - ) { - return removeUselessFieldsFromValues(params, ''); - } + // if (operatorName === Operator.Categorize) { + // return removeUselessFieldsFromValues(params, ''); + // } return params; }, ); -// initialize data for operators without parameters -// const initializeOperatorParams = curry((operatorName: string, values: any) => { -// if (isEmpty(values)) { -// return initialFormValuesMap[operatorName as Operator]; -// } -// return values; -// }); - -function buildAgentTools(edges: Edge[], nodes: Node[], nodeId: string) { - const node = nodes.find((x) => x.id === nodeId); - const params = { ...(node?.data.form ?? {}) }; - if (node && node.data.label === Operator.Agent) { - const bottomSubAgentEdges = edges.filter( - (x) => x.source === nodeId && x.sourceHandle === NodeHandleId.AgentBottom, - ); - - (params as IAgentForm).tools = (params as IAgentForm).tools.concat( - bottomSubAgentEdges.map((x) => { - const { - params: formData, - id, - name, - } = buildAgentTools(edges, nodes, x.target); - - return { - component_name: Operator.Agent, - id, - name: name as string, // Cast name to string and provide fallback - params: { ...formData }, - }; - }), - ); - } - return { params, name: node?.data.name, id: node?.id }; -} - -function filterTargetsBySourceHandleId(edges: Edge[], handleId: string) { - return edges.filter((x) => x.sourceHandle === handleId).map((x) => x.target); -} - -function buildCategorize(edges: Edge[], nodes: Node[], nodeId: string) { - const node = nodes.find((x) => x.id === nodeId); - const params = { ...(node?.data.form ?? {}) } as ICategorizeForm; - if (node && node.data.label === Operator.Categorize) { - const subEdges = edges.filter((x) => x.source === nodeId); - - const items = params.items || []; - - const nextCategoryDescription = items.reduce< - ICategorizeForm['category_description'] - >((pre, val) => { - const key = val.name; - pre[key] = { - ...omit(val, 'name', 'uuid'), - examples: val.examples?.map((x) => x.value) || [], - to: filterTargetsBySourceHandleId(subEdges, val.uuid), - }; - return pre; - }, {}); - - params.category_description = nextCategoryDescription; - } - return omit(params, 'items'); -} const buildOperatorParams = (operatorName: string) => pipe( @@ -143,7 +56,7 @@ const buildOperatorParams = (operatorName: string) => // initializeOperatorParams(operatorName), // Final processing, for guarantee ); -const ExcludeOperators = [Operator.Note, Operator.Tool]; +const ExcludeOperators = [Operator.Note]; export function isBottomSubAgent(edges: Edge[], nodeId?: string) { const edge = edges.find( @@ -151,6 +64,90 @@ export function isBottomSubAgent(edges: Edge[], nodeId?: string) { ); return !!edge; } +// Because the array of react-hook-form must be object data, +// it needs to be converted into a simple data type array required by the backend +function transformObjectArrayToPureArray( + list: Array>, + field: string, +) { + return Array.isArray(list) + ? list.filter((x) => !isEmpty(x[field])).map((y) => y[field]) + : []; +} + +function transformParserParams(params: ParserFormSchemaType) { + const setups = params.setups.reduce< + Record + >((pre, cur) => { + if (cur.fileFormat) { + let filteredSetup: Partial< + ParserFormSchemaType['setups'][0] & { suffix: string[] } + > = { + output_format: cur.output_format, + suffix: FileTypeSuffixMap[cur.fileFormat as FileType], + }; + + switch (cur.fileFormat) { + case FileType.PDF: + filteredSetup = { + ...filteredSetup, + parse_method: cur.parse_method, + lang: cur.lang, + }; + break; + case FileType.Image: + filteredSetup = { + ...filteredSetup, + parse_method: cur.parse_method, + lang: cur.lang, + system_prompt: cur.system_prompt, + }; + break; + case FileType.Email: + filteredSetup = { + ...filteredSetup, + fields: cur.fields, + }; + break; + case FileType.Video: + case FileType.Audio: + filteredSetup = { + ...filteredSetup, + llm_id: cur.llm_id, + }; + break; + default: + break; + } + + pre[cur.fileFormat] = filteredSetup; + } + return pre; + }, {}); + + return { ...params, setups }; +} + +function transformSplitterParams(params: SplitterFormSchemaType) { + return { + ...params, + delimiters: transformObjectArrayToPureArray(params.delimiters, 'value'), + }; +} + +function transformHierarchicalMergerParams( + params: HierarchicalMergerFormSchemaType, +) { + const levels = params.levels.map((x) => + transformObjectArrayToPureArray(x.expressions, 'expression'), + ); + + return { ...params, hierarchy: Number(params.hierarchy), levels }; +} + +function transformExtractorParams(params: ExtractorFormSchemaType) { + return { ...params, prompts: [{ content: params.prompts, role: 'user' }] }; +} // construct a dsl based on the node information of the graph export const buildDslComponentsByGraph = ( @@ -172,16 +169,19 @@ export const buildDslComponentsByGraph = ( let params = x?.data.form ?? {}; switch (operatorName) { - case Operator.Agent: { - const { params: formData } = buildAgentTools(edges, nodes, id); - params = { - ...formData, - exception_goto: buildAgentExceptionGoto(edges, id), - }; + case Operator.Parser: + params = transformParserParams(params); break; - } - case Operator.Categorize: - params = buildCategorize(edges, nodes, id); + + case Operator.Splitter: + params = transformSplitterParams(params); + break; + + case Operator.HierarchicalMerger: + params = transformHierarchicalMergerParams(params); + break; + case Operator.Extractor: + params = transformExtractorParams(params); break; default: @@ -194,8 +194,8 @@ export const buildDslComponentsByGraph = ( component_name: operatorName, params: buildOperatorParams(operatorName)(params) ?? {}, }, - downstream: buildComponentDownstreamOrUpstream(edges, id, true, nodes), - upstream: buildComponentDownstreamOrUpstream(edges, id, false, nodes), + downstream: buildComponentDownstreamOrUpstream(edges, id, true), + upstream: buildComponentDownstreamOrUpstream(edges, id, false), parent_id: x?.parentId, }; }); @@ -294,10 +294,6 @@ export const getOtherFieldValues = ( x !== form.getFieldValue([formListName, field.name, latestField]), ); -export const generateSwitchHandleText = (idx: number) => { - return `Case ${idx + 1}`; -}; - export const getNodeDragHandle = (nodeType?: string) => { return nodeType === Operator.Note ? '.note-drag-handle' : undefined; }; @@ -353,25 +349,6 @@ export const generateNodeNamesWithIncreasingIndex = ( export const duplicateNodeForm = (nodeData?: RAGFlowNodeType['data']) => { const form: Record = { ...(nodeData?.form ?? {}) }; - // Delete the downstream node corresponding to the to field of the Categorize operator - if (nodeData?.label === Operator.Categorize) { - form.category_description = Object.keys(form.category_description).reduce< - Record> - >((pre, cur) => { - pre[cur] = { - ...form.category_description[cur], - to: undefined, - }; - return pre; - }, {}); - } - - // Delete the downstream nodes corresponding to the yes and no fields of the Relevant operator - if (nodeData?.label === Operator.Relevant) { - form.yes = undefined; - form.no = undefined; - } - return { ...(nodeData ?? { label: '' }), form, @@ -386,40 +363,6 @@ export const needsSingleStepDebugging = (label: string) => { return !NoDebugOperatorsList.some((x) => (label as Operator) === x); }; -// Get the coordinates of the node relative to the Iteration node -export function getRelativePositionToIterationNode( - nodes: RAGFlowNodeType[], - position?: XYPosition, // relative position -) { - if (!position) { - return; - } - - const iterationNodes = nodes.filter( - (node) => node.data.label === Operator.Iteration, - ); - - for (const iterationNode of iterationNodes) { - const { - position: { x, y }, - width, - height, - } = iterationNode; - const halfWidth = (width || 0) / 2; - if ( - position.x >= x - halfWidth && - position.x <= x + halfWidth && - position.y >= y && - position.y <= y + (height || 0) - ) { - return { - parentId: iterationNode.id, - position: { x: position.x - x + halfWidth, y: position.y - y }, - }; - } - } -} - export const generateDuplicateNode = ( position?: XYPosition, label?: string, @@ -454,71 +397,11 @@ export function convertToObjectArray(list: Array) { return list.map((x) => ({ value: x })); } -/** - * convert the following object into a list - * - * { - "product_related": { - "description": "The question is about product usage, appearance and how it works.", - "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?", - "to": "generate:0" - } - } -*/ -export const buildCategorizeListFromObject = ( - categorizeItem: ICategorizeItemResult, -) => { - // Categorize's to field has two data sources, with edges as the data source. - // Changes in the edge or to field need to be synchronized to the form field. - return Object.keys(categorizeItem) - .reduce>((pre, cur) => { - // synchronize edge data to the to field - - pre.push({ - name: cur, - ...categorizeItem[cur], - examples: convertToObjectArray(categorizeItem[cur].examples), - }); - return pre; - }, []) - .sort((a, b) => a.index - b.index); -}; - -/** - * Convert the list in the following form into an object - * { - "items": [ - { - "name": "Categorize 1", - "description": "111", - "examples": ["ddd"], - "to": "Retrieval:LazyEelsStick" - } - ] - } -*/ -export const buildCategorizeObjectFromList = (list: Array) => { - return list.reduce((pre, cur) => { - if (cur?.name) { - pre[cur.name] = { - ...omit(cur, 'name', 'examples'), - examples: convertToStringArray(cur.examples) as string[], - }; - } - return pre; - }, {}); -}; - export function getAgentNodeTools(agentNode?: RAGFlowNodeType) { const tools: IAgentForm['tools'] = get(agentNode, 'data.form.tools', []); return tools; } -export function getAgentNodeMCP(agentNode?: RAGFlowNodeType) { - const tools: IAgentForm['mcp'] = get(agentNode, 'data.form.mcp', []); - return tools; -} - export function mapEdgeMouseEvent( edges: Edge[], edgeId: string, @@ -538,21 +421,3 @@ export function mapEdgeMouseEvent( return nextEdges; } - -export function buildBeginQueryWithObject( - inputs: Record, - values: BeginQuery[], -) { - const nextInputs = Object.keys(inputs).reduce>( - (pre, key) => { - const item = values.find((x) => x.key === key); - if (item) { - pre[key] = { ...item }; - } - return pre; - }, - {}, - ); - - return nextInputs; -} diff --git a/web/src/pages/data-flow/utils/chat.ts b/web/src/pages/data-flow/utils/chat.ts deleted file mode 100644 index 15c8ff85028..00000000000 --- a/web/src/pages/data-flow/utils/chat.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MessageType } from '@/constants/chat'; -import { IReference } from '@/interfaces/database/chat'; -import { IMessage } from '@/pages/chat/interface'; -import { isEmpty } from 'lodash'; - -export const buildAgentMessageItemReference = ( - conversation: { message: IMessage[]; reference: IReference[] }, - message: IMessage, -) => { - const assistantMessages = conversation.message?.filter( - (x) => x.role === MessageType.Assistant, - ); - const referenceIndex = assistantMessages.findIndex( - (x) => x.id === message.id, - ); - const reference = !isEmpty(message?.reference) - ? message?.reference - : (conversation?.reference ?? [])[referenceIndex]; - - return reference ?? { doc_aggs: [], chunks: [], total: 0 }; -}; diff --git a/web/src/pages/dataflow-result/chunker.tsx b/web/src/pages/dataflow-result/chunker.tsx index ff869809de6..1c194f76a7e 100644 --- a/web/src/pages/dataflow-result/chunker.tsx +++ b/web/src/pages/dataflow-result/chunker.tsx @@ -1,3 +1,4 @@ +import { TimelineNode } from '@/components/originui/timeline'; import message from '@/components/ui/message'; import { RAGFlowPagination, @@ -23,9 +24,16 @@ import { useUpdateChunk, } from './hooks'; import styles from './index.less'; -const ChunkerContainer = () => { + +interface IProps { + isChange: boolean; + setIsChange: (isChange: boolean) => void; + step?: TimelineNode; +} +const ChunkerContainer = (props: IProps) => { + const { isChange, setIsChange, step } = props; const [selectedChunkIds, setSelectedChunkIds] = useState([]); - const [isChange, setIsChange] = useState(false); + const { t } = useTranslation(); const { data: { documentInfo, data = [], total }, @@ -135,19 +143,18 @@ const ChunkerContainer = () => { setIsChange(true); onChunkUpdatingOk(e); }; + + const handleReRunFunc = () => { + setIsChange(false); + }; return ( - <> +
    {isChange && (
    - +
    )} -
    +
    @@ -176,7 +183,7 @@ const ChunkerContainer = () => { selectedChunkIds={selectedChunkIds} />
    -
    +
    { parserId={documentInfo.parser_id} /> )} - +
    ); }; diff --git a/web/src/pages/dataflow-result/components/chunk-result-bar/checkbox-sets.tsx b/web/src/pages/dataflow-result/components/chunk-result-bar/checkbox-sets.tsx index 68bafeeb758..bf98299a23e 100644 --- a/web/src/pages/dataflow-result/components/chunk-result-bar/checkbox-sets.tsx +++ b/web/src/pages/dataflow-result/components/chunk-result-bar/checkbox-sets.tsx @@ -1,24 +1,17 @@ import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; -import { Ban, CircleCheck, Trash2 } from 'lucide-react'; +import { Trash2 } from 'lucide-react'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; type ICheckboxSetProps = { selectAllChunk: (e: any) => void; removeChunk: (e?: any) => void; - switchChunk: (available: number) => void; checked: boolean; selectedChunkIds: string[]; }; export default (props: ICheckboxSetProps) => { - const { - selectAllChunk, - removeChunk, - switchChunk, - checked, - selectedChunkIds, - } = props; + const { selectAllChunk, removeChunk, checked, selectedChunkIds } = props; const { t } = useTranslation(); const handleSelectAllCheck = useCallback( (e: any) => { @@ -32,14 +25,6 @@ export default (props: ICheckboxSetProps) => { removeChunk(); }, [removeChunk]); - const handleEnabledClick = useCallback(() => { - switchChunk(1); - }, [switchChunk]); - - const handleDisabledClick = useCallback(() => { - switchChunk(0); - }, [switchChunk]); - const isSelected = useMemo(() => { return selectedChunkIds?.length > 0; }, [selectedChunkIds]); @@ -57,20 +42,6 @@ export default (props: ICheckboxSetProps) => {
    {isSelected && ( <> -
    - - {t('chunk.enable')} -
    -
    - - {t('chunk.disable')} -
    >; - available: number | undefined; - selectAllChunk: (value: boolean) => void; - handleSetAvailable: (value: number | undefined) => void; - createChunk: () => void; - handleInputChange: (e: React.ChangeEvent) => void; - searchString: string; + createChunk: (text: string) => void; + isReadonly: boolean; } export default ({ changeChunkTextMode, - available, - selectAllChunk, - handleSetAvailable, createChunk, - handleInputChange, - searchString, + isReadonly, }: ChunkResultBarProps) => { const { t } = useTranslate('chunk'); const [textSelectValue, setTextSelectValue] = useState( ChunkTextMode.Full, ); - const handleFilterChange = (e: string | number) => { - const value = e === -1 ? undefined : (e as number); - selectAllChunk(false); - handleSetAvailable(value); - }; - const filterContent = ( -
    - -
    - {t('all')} - {t('enabled')} - {t('disabled')} -
    -
    -
    - ); const textSelectOptions = [ { label: t(ChunkTextMode.Full), value: ChunkTextMode.Full }, { label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse }, @@ -78,31 +46,15 @@ export default ({
    ))}
    - } - onChange={handleInputChange} - value={searchString} - /> - - - - - - {filterContent} - - - + {!isReadonly && ( + + )}
    ); }; diff --git a/web/src/pages/dataflow-result/components/document-preview/hooks.ts b/web/src/pages/dataflow-result/components/document-preview/hooks.ts index fcf6a01babd..30e5887d848 100644 --- a/web/src/pages/dataflow-result/components/document-preview/hooks.ts +++ b/web/src/pages/dataflow-result/components/document-preview/hooks.ts @@ -1,8 +1,9 @@ import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; -import { api_host } from '@/utils/api'; +import api, { api_host } from '@/utils/api'; import { useSize } from 'ahooks'; import { CustomTextRenderer } from 'node_modules/react-pdf/dist/esm/shared/types'; import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useGetPipelineResultSearchParams } from '../../hooks'; export const useDocumentResizeObserver = () => { const [containerWidth, setContainerWidth] = useState(); @@ -44,12 +45,16 @@ export const useHighlightText = (searchText: string = '') => { return textRenderer; }; -export const useGetDocumentUrl = () => { +export const useGetDocumentUrl = (isAgent: boolean) => { const { documentId } = useGetKnowledgeSearchParams(); + const { createdBy, documentId: id } = useGetPipelineResultSearchParams(); const url = useMemo(() => { + if (isAgent) { + return api.downloadFile + `?id=${id}&created_by=${createdBy}`; + } return `${api_host}/document/get/${documentId}`; - }, [documentId]); + }, [createdBy, documentId, id, isAgent]); return url; }; diff --git a/web/src/pages/dataflow-result/components/parse-editer/hook.ts b/web/src/pages/dataflow-result/components/parse-editer/hook.ts new file mode 100644 index 00000000000..6ec503acd94 --- /dev/null +++ b/web/src/pages/dataflow-result/components/parse-editer/hook.ts @@ -0,0 +1,30 @@ +import { useEffect, useRef, useState } from 'react'; +import { IJsonContainerProps, IObjContainerProps } from './interface'; + +export const useParserInit = ({ + initialValue, +}: { + initialValue: + | IJsonContainerProps['initialValue'] + | IObjContainerProps['initialValue']; +}) => { + const [content, setContent] = useState(initialValue); + + useEffect(() => { + setContent(initialValue); + console.log('initialValue json parse', initialValue); + }, [initialValue]); + + const [activeEditIndex, setActiveEditIndex] = useState( + undefined, + ); + const editDivRef = useRef(null); + + return { + content, + setContent, + activeEditIndex, + setActiveEditIndex, + editDivRef, + }; +}; diff --git a/web/src/pages/dataflow-result/components/parse-editer/index.tsx b/web/src/pages/dataflow-result/components/parse-editer/index.tsx index 2a748e910ae..799cb11c9ff 100644 --- a/web/src/pages/dataflow-result/components/parse-editer/index.tsx +++ b/web/src/pages/dataflow-result/components/parse-editer/index.tsx @@ -1,45 +1,63 @@ -import { Textarea } from '@/components/ui/textarea'; -import { cn } from '@/lib/utils'; -import { useState } from 'react'; +import { CheckedState } from '@radix-ui/react-checkbox'; +import { FormatPreserveEditorProps } from './interface'; +import { ArrayContainer } from './json-parser'; +import { ObjectContainer } from './object-parser'; -interface FormatPreserveEditorProps { - initialValue: string; - onSave: (value: string) => void; - className?: string; -} const FormatPreserveEditor = ({ initialValue, onSave, className, + isChunck, + handleCheckboxClick, + selectedChunkIds, + textMode, + clickChunk, + isReadonly, }: FormatPreserveEditorProps) => { - const [content, setContent] = useState(initialValue); - const [isEditing, setIsEditing] = useState(false); + console.log('initialValue', initialValue); - const handleEdit = () => setIsEditing(true); - - const handleSave = () => { - onSave(content); - setIsEditing(false); + const escapeNewlines = (text: string) => { + return text.replace(/\n/g, '\\n'); + }; + const unescapeNewlines = (text: string) => { + return text.replace(/\\n/g, '\n'); + }; + const handleCheck = (e: CheckedState, id: string | number) => { + handleCheckboxClick?.(id, e === 'indeterminate' ? false : e); }; return (
    - {isEditing ? ( - - - - This is your public display name. - - - - )} - /> - - - - ); -} diff --git a/web/src/pages/dataset/setting/basic-setting-form.tsx b/web/src/pages/dataset/setting/basic-setting-form.tsx deleted file mode 100644 index f4b2466e7d7..00000000000 --- a/web/src/pages/dataset/setting/basic-setting-form.tsx +++ /dev/null @@ -1,146 +0,0 @@ -'use client'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { MultiSelect } from '@/components/ui/multi-select'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import { useTranslate } from '@/hooks/common-hooks'; -import { Cat, Dog, Fish, Rabbit, Turtle } from 'lucide-react'; -import { useState } from 'react'; - -const frameworksList = [ - { value: 'react', label: 'React', icon: Turtle }, - { value: 'angular', label: 'Angular', icon: Cat }, - { value: 'vue', label: 'Vue', icon: Dog }, - { value: 'svelte', label: 'Svelte', icon: Rabbit }, - { value: 'ember', label: 'Ember', icon: Fish }, -]; - -export default function BasicSettingForm() { - const { t } = useTranslate('knowledgeConfiguration'); - - const formSchema = z.object({ - name: z.string().min(1), - a: z.number().min(2, { - message: 'Username must be at least 2 characters.', - }), - language: z.string().min(1, { - message: 'Username must be at least 2 characters.', - }), - c: z.number().min(2, { - message: 'Username must be at least 2 characters.', - }), - d: z.string().min(2, { - message: 'Username must be at least 2 characters.', - }), - }); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - name: '', - language: 'English', - }, - }); - const [selectedFrameworks, setSelectedFrameworks] = useState([ - 'react', - 'angular', - ]); - - function onSubmit(values: z.infer) { - console.log(values); - } - - return ( -
    - - ( - - {t('name')} - - - - - - )} - /> - ( - - Username - - - - - - )} - /> - ( - - {t('language')} - - - - )} - /> - ( - - Username - - - - - - )} - /> - - - ); -} diff --git a/web/src/pages/dataset/setting/category-panel.tsx b/web/src/pages/dataset/setting/category-panel.tsx deleted file mode 100644 index 4e96fefcc6d..00000000000 --- a/web/src/pages/dataset/setting/category-panel.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import SvgIcon from '@/components/svg-icon'; -import { useTranslate } from '@/hooks/common-hooks'; -import { useSelectParserList } from '@/hooks/user-setting-hooks'; -import { Col, Divider, Empty, Row, Typography } from 'antd'; -import DOMPurify from 'dompurify'; -import camelCase from 'lodash/camelCase'; -import { useMemo } from 'react'; -import styles from './index.less'; -import { TagTabs } from './tag-tabs'; -import { ImageMap } from './utils'; - -const { Text } = Typography; - -const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => { - const parserList = useSelectParserList(); - const { t } = useTranslate('knowledgeConfiguration'); - - const item = useMemo(() => { - const item = parserList.find((x) => x.value === chunkMethod); - if (item) { - return { - title: item.label, - description: t(camelCase(item.value)), - }; - } - return { title: '', description: '' }; - }, [parserList, chunkMethod, t]); - - const imageList = useMemo(() => { - if (chunkMethod in ImageMap) { - return ImageMap[chunkMethod as keyof typeof ImageMap]; - } - return []; - }, [chunkMethod]); - - return ( -
    - {imageList.length > 0 ? ( - <> -
    - {`"${item.title}" ${t('methodTitle')}`} -
    -

    -
    {`"${item.title}" ${t('methodExamples')}`}
    - {t('methodExamplesDescription')} - - {imageList.map((x) => ( - - - - ))} - -
    - {item.title} {t('dialogueExamplesTitle')} -
    - - - ) : ( - -

    {t('methodEmpty')}

    - -
    - )} - {chunkMethod === 'tag' && } -
    - ); -}; - -export default CategoryPanel; diff --git a/web/src/pages/dataset/setting/chunk-method-card.tsx b/web/src/pages/dataset/setting/chunk-method-card.tsx deleted file mode 100644 index 68114cf2828..00000000000 --- a/web/src/pages/dataset/setting/chunk-method-card.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import SvgIcon from '@/components/svg-icon'; -import { Card } from '@/components/ui/card'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import { useTranslate } from '@/hooks/common-hooks'; -import { useSelectParserList } from '@/hooks/user-setting-hooks'; -import { Col, Divider, Empty, Row, Typography } from 'antd'; -import DOMPurify from 'dompurify'; -import camelCase from 'lodash/camelCase'; -import { useMemo } from 'react'; -import { useFormContext } from 'react-hook-form'; -import styles from './index.less'; -import { ImageMap } from './utils'; - -const { Title, Text } = Typography; - -const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => { - const parserList = useSelectParserList(); - const { t } = useTranslate('knowledgeConfiguration'); - - const item = useMemo(() => { - const item = parserList.find((x) => x.value === chunkMethod); - if (item) { - return { - title: item.label, - description: t(camelCase(item.value)), - }; - } - return { title: '', description: '' }; - }, [parserList, chunkMethod, t]); - - const imageList = useMemo(() => { - if (chunkMethod in ImageMap) { - return ImageMap[chunkMethod as keyof typeof ImageMap]; - } - return []; - }, [chunkMethod]); - - return ( -
    - {imageList.length > 0 ? ( - <> - - {`"${item.title}" ${t('methodTitle')}`} - -

    - {`"${item.title}" ${t('methodExamples')}`} - {t('methodExamplesDescription')} - - {imageList.map((x) => ( - - - - ))} - - - {item.title} {t('dialogueExamplesTitle')} - - - - ) : ( - -

    {t('methodEmpty')}

    - -
    - )} -
    - ); -}; - -export default function ChunkMethodCard() { - const { t } = useTranslate('knowledgeConfiguration'); - const form = useFormContext(); - - return ( - -
    - ( - - {t('chunkMethod')} - - - - )} - /> -
    - -
    - ); -} diff --git a/web/src/pages/dataset/setting/chunk-method-form.tsx b/web/src/pages/dataset/setting/chunk-method-form.tsx deleted file mode 100644 index 26f3e090ff1..00000000000 --- a/web/src/pages/dataset/setting/chunk-method-form.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { useFormContext, useWatch } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; - -import { DocumentParserType } from '@/constants/knowledge'; -import { useMemo } from 'react'; -import { AudioConfiguration } from './configuration/audio'; -import { BookConfiguration } from './configuration/book'; -import { EmailConfiguration } from './configuration/email'; -import { KnowledgeGraphConfiguration } from './configuration/knowledge-graph'; -import { LawsConfiguration } from './configuration/laws'; -import { ManualConfiguration } from './configuration/manual'; -import { NaiveConfiguration } from './configuration/naive'; -import { OneConfiguration } from './configuration/one'; -import { PaperConfiguration } from './configuration/paper'; -import { PictureConfiguration } from './configuration/picture'; -import { PresentationConfiguration } from './configuration/presentation'; -import { QAConfiguration } from './configuration/qa'; -import { ResumeConfiguration } from './configuration/resume'; -import { TableConfiguration } from './configuration/table'; -import { TagConfiguration } from './configuration/tag'; -import { SavingButton } from './saving-button'; - -const ConfigurationComponentMap = { - [DocumentParserType.Naive]: NaiveConfiguration, - [DocumentParserType.Qa]: QAConfiguration, - [DocumentParserType.Resume]: ResumeConfiguration, - [DocumentParserType.Manual]: ManualConfiguration, - [DocumentParserType.Table]: TableConfiguration, - [DocumentParserType.Paper]: PaperConfiguration, - [DocumentParserType.Book]: BookConfiguration, - [DocumentParserType.Laws]: LawsConfiguration, - [DocumentParserType.Presentation]: PresentationConfiguration, - [DocumentParserType.Picture]: PictureConfiguration, - [DocumentParserType.One]: OneConfiguration, - [DocumentParserType.Audio]: AudioConfiguration, - [DocumentParserType.Email]: EmailConfiguration, - [DocumentParserType.Tag]: TagConfiguration, - [DocumentParserType.KnowledgeGraph]: KnowledgeGraphConfiguration, -}; - -function EmptyComponent() { - return
    ; -} - -export function ChunkMethodForm() { - const form = useFormContext(); - const { t } = useTranslation(); - - const finalParserId: DocumentParserType = useWatch({ - control: form.control, - name: 'parser_id', - }); - - const ConfigurationComponent = useMemo(() => { - return finalParserId - ? ConfigurationComponentMap[finalParserId] - : EmptyComponent; - }, [finalParserId]); - - return ( -
    -
    - -
    -
    - - -
    -
    - ); -} diff --git a/web/src/pages/dataset/setting/chunk-method-learn-more.tsx b/web/src/pages/dataset/setting/chunk-method-learn-more.tsx deleted file mode 100644 index 850f90db519..00000000000 --- a/web/src/pages/dataset/setting/chunk-method-learn-more.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { cn } from '@/lib/utils'; -import { t } from 'i18next'; -import { X } from 'lucide-react'; -import { useState } from 'react'; -import CategoryPanel from './category-panel'; - -export default ({ - tab = 'generalForm', - parserId, -}: { - tab: 'generalForm' | 'chunkMethodForm'; - parserId: string; -}) => { - const [visible, setVisible] = useState(false); - - return ( -
    -
    - -
    -
    - -
    { - setVisible(false); - }} - > - -
    -
    -
    - ); -}; diff --git a/web/src/pages/dataset/setting/configuration-form-container.tsx b/web/src/pages/dataset/setting/configuration-form-container.tsx deleted file mode 100644 index 215f032dd6e..00000000000 --- a/web/src/pages/dataset/setting/configuration-form-container.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { FormContainer, FormContainerProps } from '@/components/form-container'; -import { cn } from '@/lib/utils'; -import { PropsWithChildren } from 'react'; - -export function ConfigurationFormContainer({ - children, - className, -}: FormContainerProps) { - return ( - {children} - ); -} - -export function MainContainer({ children }: PropsWithChildren) { - return
    {children}
    ; -} diff --git a/web/src/pages/dataset/setting/configuration/audio.tsx b/web/src/pages/dataset/setting/configuration/audio.tsx deleted file mode 100644 index f7f372f7a65..00000000000 --- a/web/src/pages/dataset/setting/configuration/audio.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function AudioConfiguration() { - return ( - - - - - - - <> - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/book.tsx b/web/src/pages/dataset/setting/configuration/book.tsx deleted file mode 100644 index 39f58f03fa2..00000000000 --- a/web/src/pages/dataset/setting/configuration/book.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { - ConfigurationFormContainer, - MainContainer, -} from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function BookConfiguration() { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/common-item.tsx b/web/src/pages/dataset/setting/configuration/common-item.tsx deleted file mode 100644 index 1ee6a8ad6c1..00000000000 --- a/web/src/pages/dataset/setting/configuration/common-item.tsx +++ /dev/null @@ -1,287 +0,0 @@ -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Radio } from '@/components/ui/radio'; -import { RAGFlowSelect } from '@/components/ui/select'; -import { Switch } from '@/components/ui/switch'; -import { useTranslate } from '@/hooks/common-hooks'; -import { ArrowUpRight } from 'lucide-react'; -import { useFormContext } from 'react-hook-form'; -import { - useHasParsedDocument, - useSelectChunkMethodList, - useSelectEmbeddingModelOptions, -} from '../hooks'; - -export function ChunkMethodItem() { - const { t } = useTranslate('knowledgeConfiguration'); - const form = useFormContext(); - // const handleChunkMethodSelectChange = useHandleChunkMethodSelectChange(form); - const parserList = useSelectChunkMethodList(); - - return ( - ( - -
    - - {t('chunkMethod')} - -
    - - - -
    -
    -
    -
    - -
    -
    - )} - /> - ); -} - -export function EmbeddingModelItem() { - const { t } = useTranslate('knowledgeConfiguration'); - const form = useFormContext(); - const embeddingModelOptions = useSelectEmbeddingModelOptions(); - const disabled = useHasParsedDocument(); - - return ( - ( - -
    - - {t('embeddingModel')} - -
    - - - -
    -
    -
    -
    - -
    -
    - )} - /> - ); -} - -export function ParseTypeItem() { - const { t } = useTranslate('knowledgeConfiguration'); - const form = useFormContext(); - - return ( - ( - -
    - - {t('parseType')} - -
    - - -
    - {t('builtIn')} - {t('manualSetup')} -
    -
    -
    -
    -
    -
    -
    - -
    -
    - )} - /> - ); -} - -export function DataFlowItem() { - const { t } = useTranslate('knowledgeConfiguration'); - const form = useFormContext(); - - return ( - ( - -
    -
    - - {t('dataFlow')} - -
    - {t('buildItFromScratch')} - -
    -
    - -
    - - - -
    -
    -
    -
    - -
    -
    - )} - /> - ); -} - -export function DataExtractKnowledgeItem() { - const { t } = useTranslate('knowledgeConfiguration'); - const form = useFormContext(); - - return ( - <> - {' '} - ( - -
    - - {t('extractKnowledgeGraph')} - -
    - - - -
    -
    -
    -
    - -
    -
    - )} - />{' '} - ( - -
    - - {t('useRAPTORToEnhanceRetrieval')} - -
    - - - -
    -
    -
    -
    - -
    -
    - )} - /> - - ); -} - -export function TeamItem() { - const { t } = useTranslate('knowledgeConfiguration'); - const form = useFormContext(); - - return ( - ( - -
    - - * - {t('team')} - -
    - - - -
    -
    -
    -
    - -
    -
    - )} - /> - ); -} diff --git a/web/src/pages/dataset/setting/configuration/email.tsx b/web/src/pages/dataset/setting/configuration/email.tsx deleted file mode 100644 index 03f4cfcad44..00000000000 --- a/web/src/pages/dataset/setting/configuration/email.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function EmailConfiguration() { - return ( - - - - - - - <> - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/knowledge-graph.tsx b/web/src/pages/dataset/setting/configuration/knowledge-graph.tsx deleted file mode 100644 index 5e2170e975e..00000000000 --- a/web/src/pages/dataset/setting/configuration/knowledge-graph.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { DelimiterFormField } from '@/components/delimiter-form-field'; -import { EntityTypesFormField } from '@/components/entity-types-form-field'; -import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function KnowledgeGraphConfiguration() { - return ( - <> - - - - - - <> - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/laws.tsx b/web/src/pages/dataset/setting/configuration/laws.tsx deleted file mode 100644 index 76e819bef37..00000000000 --- a/web/src/pages/dataset/setting/configuration/laws.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { - ConfigurationFormContainer, - MainContainer, -} from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function LawsConfiguration() { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/manual.tsx b/web/src/pages/dataset/setting/configuration/manual.tsx deleted file mode 100644 index da5e24c5a8d..00000000000 --- a/web/src/pages/dataset/setting/configuration/manual.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { - ConfigurationFormContainer, - MainContainer, -} from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function ManualConfiguration() { - return ( - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/naive.tsx b/web/src/pages/dataset/setting/configuration/naive.tsx deleted file mode 100644 index 29b57ef08c3..00000000000 --- a/web/src/pages/dataset/setting/configuration/naive.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import { DelimiterFormField } from '@/components/delimiter-form-field'; -import { ExcelToHtmlFormField } from '@/components/excel-to-html-form-field'; -import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { - ConfigurationFormContainer, - MainContainer, -} from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function NaiveConfiguration() { - return ( - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/one.tsx b/web/src/pages/dataset/setting/configuration/one.tsx deleted file mode 100644 index bd9c166def2..00000000000 --- a/web/src/pages/dataset/setting/configuration/one.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function OneConfiguration() { - return ( - - - - - - - - <> - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/paper.tsx b/web/src/pages/dataset/setting/configuration/paper.tsx deleted file mode 100644 index 3157bb14196..00000000000 --- a/web/src/pages/dataset/setting/configuration/paper.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { - ConfigurationFormContainer, - MainContainer, -} from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function PaperConfiguration() { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/picture.tsx b/web/src/pages/dataset/setting/configuration/picture.tsx deleted file mode 100644 index bb908896fca..00000000000 --- a/web/src/pages/dataset/setting/configuration/picture.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function PictureConfiguration() { - return ( - - - - - - - <> - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/presentation.tsx b/web/src/pages/dataset/setting/configuration/presentation.tsx deleted file mode 100644 index f2a38c2c376..00000000000 --- a/web/src/pages/dataset/setting/configuration/presentation.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { - AutoKeywordsFormField, - AutoQuestionsFormField, -} from '@/components/auto-keywords-form-field'; -import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import PageRankFormField from '@/components/page-rank-form-field'; -import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; -import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; -import { - ConfigurationFormContainer, - MainContainer, -} from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function PresentationConfiguration() { - return ( - - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/qa.tsx b/web/src/pages/dataset/setting/configuration/qa.tsx deleted file mode 100644 index 0f5d76f3909..00000000000 --- a/web/src/pages/dataset/setting/configuration/qa.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import PageRankFormField from '@/components/page-rank-form-field'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function QAConfiguration() { - return ( - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/resume.tsx b/web/src/pages/dataset/setting/configuration/resume.tsx deleted file mode 100644 index a5b3be198a5..00000000000 --- a/web/src/pages/dataset/setting/configuration/resume.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import PageRankFormField from '@/components/page-rank-form-field'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { TagItems } from '../tag-item'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function ResumeConfiguration() { - return ( - - - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/table.tsx b/web/src/pages/dataset/setting/configuration/table.tsx deleted file mode 100644 index 947a633a956..00000000000 --- a/web/src/pages/dataset/setting/configuration/table.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import PageRankFormField from '@/components/page-rank-form-field'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function TableConfiguration() { - return ( - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/configuration/tag.tsx b/web/src/pages/dataset/setting/configuration/tag.tsx deleted file mode 100644 index 5e6b3a2b9e1..00000000000 --- a/web/src/pages/dataset/setting/configuration/tag.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import PageRankFormField from '@/components/page-rank-form-field'; -import { ConfigurationFormContainer } from '../configuration-form-container'; -import { ChunkMethodItem, EmbeddingModelItem } from './common-item'; - -export function TagConfiguration() { - return ( - - - - - - - ); -} diff --git a/web/src/pages/dataset/setting/form-schema.ts b/web/src/pages/dataset/setting/form-schema.ts deleted file mode 100644 index 1594ba3626b..00000000000 --- a/web/src/pages/dataset/setting/form-schema.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { z } from 'zod'; - -export const formSchema = z.object({ - name: z.string().min(1, { - message: 'Username must be at least 2 characters.', - }), - description: z.string().min(2, { - message: 'Username must be at least 2 characters.', - }), - // avatar: z.instanceof(File), - avatar: z.any().nullish(), - permission: z.string().optional(), - parser_id: z.string(), - embd_id: z.string(), - parser_config: z - .object({ - layout_recognize: z.string(), - chunk_token_num: z.number(), - delimiter: z.string(), - auto_keywords: z.number().optional(), - auto_questions: z.number().optional(), - html4excel: z.boolean(), - tag_kb_ids: z.array(z.string()).nullish(), - topn_tags: z.number().optional(), - raptor: z - .object({ - use_raptor: z.boolean().optional(), - prompt: z.string().optional(), - max_token: z.number().optional(), - threshold: z.number().optional(), - max_cluster: z.number().optional(), - random_seed: z.number().optional(), - }) - .refine( - (data) => { - if (data.use_raptor && !data.prompt) { - return false; - } - return true; - }, - { - message: 'Prompt is required', - path: ['prompt'], - }, - ), - graphrag: z - .object({ - use_graphrag: z.boolean().optional(), - entity_types: z.array(z.string()).optional(), - method: z.string().optional(), - resolution: z.boolean().optional(), - community: z.boolean().optional(), - }) - .refine( - (data) => { - if ( - data.use_graphrag && - (!data.entity_types || data.entity_types.length === 0) - ) { - return false; - } - return true; - }, - { - message: 'Please enter Entity types', - path: ['entity_types'], - }, - ), - }) - .optional(), - pagerank: z.number(), - // icon: z.array(z.instanceof(File)), -}); diff --git a/web/src/pages/dataset/setting/general-form.tsx b/web/src/pages/dataset/setting/general-form.tsx deleted file mode 100644 index 34be01db796..00000000000 --- a/web/src/pages/dataset/setting/general-form.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { AvatarUpload } from '@/components/avatar-upload'; -import { FormContainer } from '@/components/form-container'; -import { Button } from '@/components/ui/button'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { PermissionFormField } from './permission-form-field'; -import { GeneralSavingButton } from './saving-button'; - -export function GeneralForm() { - const form = useFormContext(); - const { t } = useTranslation(); - - return ( - <> - - ( - -
    - - * - {t('common.name')} - - - - -
    -
    -
    - -
    -
    - )} - /> - ( - -
    - - {t('setting.avatar')} - - - - -
    -
    -
    - -
    -
    - )} - /> - { - // null initialize empty string - if (typeof field.value === 'object' && !field.value) { - form.setValue('description', ' '); - } - return ( - -
    - - {t('flow.description')} - - - - -
    -
    -
    - -
    -
    - ); - }} - /> - -
    -
    - - -
    - - ); -} diff --git a/web/src/pages/dataset/setting/hooks.ts b/web/src/pages/dataset/setting/hooks.ts deleted file mode 100644 index 3332ca3f57a..00000000000 --- a/web/src/pages/dataset/setting/hooks.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { LlmModelType } from '@/constants/knowledge'; -import { useSetModalState } from '@/hooks/common-hooks'; - -import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks'; -import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request'; -import { useSelectParserList } from '@/hooks/user-setting-hooks'; -import { useIsFetching } from '@tanstack/react-query'; -import { pick } from 'lodash'; -import { useCallback, useEffect, useState } from 'react'; -import { UseFormReturn } from 'react-hook-form'; -import { z } from 'zod'; -import { formSchema } from './form-schema'; - -// The value that does not need to be displayed in the analysis method Select -const HiddenFields = ['email', 'picture', 'audio']; - -export function useSelectChunkMethodList() { - const parserList = useSelectParserList(); - - return parserList.filter((x) => !HiddenFields.some((y) => y === x.value)); -} - -export function useSelectEmbeddingModelOptions() { - const allOptions = useSelectLlmOptionsByModelType(); - return allOptions[LlmModelType.Embedding]; -} - -export function useHasParsedDocument() { - const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration(); - return knowledgeDetails.chunk_num > 0; -} - -export const useFetchKnowledgeConfigurationOnMount = ( - form: UseFormReturn, any, undefined>, -) => { - const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration(); - - useEffect(() => { - const parser_config = { - ...form.formState?.defaultValues?.parser_config, - ...knowledgeDetails.parser_config, - }; - const formValues = { - ...pick({ ...knowledgeDetails, parser_config: parser_config }, [ - 'description', - 'name', - 'permission', - 'embd_id', - 'parser_id', - 'language', - 'parser_config', - 'pagerank', - 'avatar', - ]), - }; - form.reset(formValues); - }, [form, knowledgeDetails]); - - return knowledgeDetails; -}; - -export const useSelectKnowledgeDetailsLoading = () => - useIsFetching({ queryKey: ['fetchKnowledgeDetail'] }) > 0; - -export const useRenameKnowledgeTag = () => { - const [tag, setTag] = useState(''); - const { - visible: tagRenameVisible, - hideModal: hideTagRenameModal, - showModal: showFileRenameModal, - } = useSetModalState(); - - const handleShowTagRenameModal = useCallback( - (record: string) => { - setTag(record); - showFileRenameModal(); - }, - [showFileRenameModal], - ); - - return { - initialName: tag, - tagRenameVisible, - hideTagRenameModal, - showTagRenameModal: handleShowTagRenameModal, - }; -}; diff --git a/web/src/pages/dataset/setting/index.less b/web/src/pages/dataset/setting/index.less deleted file mode 100644 index 4889e37762d..00000000000 --- a/web/src/pages/dataset/setting/index.less +++ /dev/null @@ -1,45 +0,0 @@ -.tags { - margin-bottom: 24px; -} - -.preset { - display: flex; - height: 80px; - background-color: rgba(0, 0, 0, 0.1); - border-radius: 5px; - padding: 5px; - margin-bottom: 24px; - - .left { - flex: 1; - } - - .right { - width: 100px; - border-left: 1px solid rgba(0, 0, 0, 0.4); - margin: 10px 0px; - padding: 5px; - } -} - -.configurationWrapper { - padding: 0 52px; - .buttonWrapper { - text-align: right; - } - .variableSlider { - width: 100%; - } -} - -.categoryPanelWrapper { - .topTitle { - margin-top: 0; - } - .imageRow { - margin-top: 16px; - } - .image { - width: 100%; - } -} diff --git a/web/src/pages/dataset/setting/index.tsx b/web/src/pages/dataset/setting/index.tsx deleted file mode 100644 index 9115339b985..00000000000 --- a/web/src/pages/dataset/setting/index.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { Form } from '@/components/ui/form'; -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, -} from '@/components/ui/tabs-underlined'; -import { DocumentParserType } from '@/constants/knowledge'; -import { PermissionRole } from '@/constants/permission'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { useState } from 'react'; -import { useForm, useWatch } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; -import { TopTitle } from '../dataset-title'; -import { ChunkMethodForm } from './chunk-method-form'; -import ChunkMethodLearnMore from './chunk-method-learn-more'; -import { formSchema } from './form-schema'; -import { GeneralForm } from './general-form'; -import { useFetchKnowledgeConfigurationOnMount } from './hooks'; - -const enum DocumentType { - DeepDOC = 'DeepDOC', - PlainText = 'Plain Text', -} - -const initialEntityTypes = [ - 'organization', - 'person', - 'geo', - 'event', - 'category', -]; - -const enum MethodValue { - General = 'general', - Light = 'light', -} - -export default function DatasetSettings() { - const { t } = useTranslation(); - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - name: '', - parser_id: DocumentParserType.Naive, - permission: PermissionRole.Me, - parser_config: { - layout_recognize: DocumentType.DeepDOC, - chunk_token_num: 512, - delimiter: `\n`, - auto_keywords: 0, - auto_questions: 0, - html4excel: false, - topn_tags: 3, - raptor: { - use_raptor: false, - }, - graphrag: { - use_graphrag: false, - entity_types: initialEntityTypes, - method: MethodValue.Light, - }, - }, - pagerank: 0, - }, - }); - - useFetchKnowledgeConfigurationOnMount(form); - - const [currentTab, setCurrentTab] = useState< - 'generalForm' | 'chunkMethodForm' - >('generalForm'); // currnet Tab state - - const parserId = useWatch({ - control: form.control, - name: 'parser_id', - }); - - async function onSubmit(data: z.infer) { - console.log('🚀 ~ DatasetSettings ~ data:', data); - } - - return ( -
    - -
    -
    - - { - setCurrentTab(val); - }} - className="h-full flex flex-col" - > - - -
    - - {t('knowledgeDetails.general')} - -
    -
    - -
    - - {t('knowledgeDetails.chunkMethodTab')} - -
    -
    -
    - - - - - - -
    -
    - - -
    -
    - ); -} diff --git a/web/src/pages/dataset/setting/permission-form-field.tsx b/web/src/pages/dataset/setting/permission-form-field.tsx deleted file mode 100644 index ac095a79c43..00000000000 --- a/web/src/pages/dataset/setting/permission-form-field.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { SelectWithSearch } from '@/components/originui/select-with-search'; -import { RAGFlowFormItem } from '@/components/ragflow-form'; -import { PermissionRole } from '@/constants/permission'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; - -export function PermissionFormField() { - const { t } = useTranslation(); - const teamOptions = useMemo(() => { - return Object.values(PermissionRole).map((x) => ({ - label: t('knowledgeConfiguration.' + x), - value: x, - })); - }, [t]); - - return ( - - - - ); -} diff --git a/web/src/pages/dataset/setting/saving-button.tsx b/web/src/pages/dataset/setting/saving-button.tsx deleted file mode 100644 index 1fac81ea7e3..00000000000 --- a/web/src/pages/dataset/setting/saving-button.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { ButtonLoading } from '@/components/ui/button'; -import { useUpdateKnowledge } from '@/hooks/use-knowledge-request'; -import { useMemo } from 'react'; -import { useFormContext } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useParams } from 'umi'; - -export function GeneralSavingButton() { - const form = useFormContext(); - const { saveKnowledgeConfiguration, loading: submitLoading } = - useUpdateKnowledge(); - const { id: kb_id } = useParams(); - const { t } = useTranslation(); - - const defaultValues = useMemo( - () => form.formState.defaultValues ?? {}, - [form.formState.defaultValues], - ); - const parser_id = defaultValues['parser_id']; - - return ( - { - (async () => { - let isValidate = await form.trigger('name'); - const { name, description, permission, avatar } = form.getValues(); - - if (isValidate) { - saveKnowledgeConfiguration({ - kb_id, - parser_id, - name, - description, - avatar, - permission, - }); - } - })(); - }} - > - {t('knowledgeConfiguration.save')} - - ); -} - -export function SavingButton() { - const { saveKnowledgeConfiguration, loading: submitLoading } = - useUpdateKnowledge(); - const form = useFormContext(); - const { id: kb_id } = useParams(); - const { t } = useTranslation(); - - return ( - { - (async () => { - try { - let beValid = await form.formControl.trigger(); - if (beValid) { - form.handleSubmit(async (values) => { - console.log('saveKnowledgeConfiguration: ', values); - delete values['avatar']; - await saveKnowledgeConfiguration({ - kb_id, - ...values, - }); - })(); - } - } catch (e) { - console.log(e); - } finally { - } - })(); - }} - > - {t('knowledgeConfiguration.save')} - - ); -} diff --git a/web/src/pages/dataset/setting/tag-item.tsx b/web/src/pages/dataset/setting/tag-item.tsx deleted file mode 100644 index c5ccecca47d..00000000000 --- a/web/src/pages/dataset/setting/tag-item.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { RAGFlowAvatar } from '@/components/ragflow-avatar'; -import { SliderInputFormField } from '@/components/slider-input-form-field'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { MultiSelect } from '@/components/ui/multi-select'; -import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; -import { Flex, Form, InputNumber, Select, Slider, Space } from 'antd'; -import DOMPurify from 'dompurify'; -import { useFormContext, useWatch } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; - -export const TagSetItem = () => { - const { t } = useTranslation(); - const form = useFormContext(); - - const { list: knowledgeList } = useFetchKnowledgeList(true); - - const knowledgeOptions = knowledgeList - .filter((x) => x.parser_id === 'tag') - .map((x) => ({ - label: x.name, - value: x.id, - icon: () => ( - - - - ), - })); - - return ( - ( - -
    -
    - } - > - {t('knowledgeConfiguration.tagSet')} - -
    - - - -
    -
    -
    -
    - -
    - - )} - /> - ); - - return ( -
    - } - rules={[ - { - message: t('chat.knowledgeBasesMessage'), - type: 'array', - }, - ]} - > - - - ); -}; - -export const TopNTagsItem = () => { - const { t } = useTranslation(); - - return ( - - ); - - return ( - - - - - - - - - - - - - ); -}; - -export function TagItems() { - const form = useFormContext(); - const ids: string[] = useWatch({ - control: form.control, - name: 'parser_config.tag_kb_ids', - }); - - return ( - <> - - {Array.isArray(ids) && ids.length > 0 && } - - ); -} diff --git a/web/src/pages/dataset/setting/tag-table/index.tsx b/web/src/pages/dataset/setting/tag-table/index.tsx deleted file mode 100644 index a2e38018ced..00000000000 --- a/web/src/pages/dataset/setting/tag-table/index.tsx +++ /dev/null @@ -1,305 +0,0 @@ -'use client'; - -import { - ColumnDef, - ColumnFiltersState, - SortingState, - VisibilityState, - flexRender, - getCoreRowModel, - getFilteredRowModel, - getPaginationRowModel, - getSortedRowModel, - useReactTable, -} from '@tanstack/react-table'; -import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react'; -import * as React from 'react'; - -import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; -import { Button } from '@/components/ui/button'; -import { Checkbox } from '@/components/ui/checkbox'; -import { Input } from '@/components/ui/input'; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table'; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from '@/components/ui/tooltip'; -import { useDeleteTag, useFetchTagList } from '@/hooks/knowledge-hooks'; -import { useCallback, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useRenameKnowledgeTag } from '../hooks'; -import { RenameDialog } from './rename-dialog'; - -export type ITag = { - tag: string; - frequency: number; -}; - -export function TagTable() { - const { t } = useTranslation(); - const { list } = useFetchTagList(); - const [tagList, setTagList] = useState([]); - - const [sorting, setSorting] = React.useState([]); - const [columnFilters, setColumnFilters] = React.useState( - [], - ); - const [columnVisibility, setColumnVisibility] = - React.useState({}); - const [rowSelection, setRowSelection] = useState({}); - - const { deleteTag } = useDeleteTag(); - - useEffect(() => { - setTagList(list.map((x) => ({ tag: x[0], frequency: x[1] }))); - }, [list]); - - const handleDeleteTag = useCallback( - (tags: string[]) => () => { - deleteTag(tags); - }, - [deleteTag], - ); - - const { - showTagRenameModal, - hideTagRenameModal, - tagRenameVisible, - initialName, - } = useRenameKnowledgeTag(); - - const columns: ColumnDef[] = [ - { - id: 'select', - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - /> - ), - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: 'tag', - header: ({ column }) => { - return ( - - ); - }, - cell: ({ row }) => { - const value: string = row.getValue('tag'); - return
    {value}
    ; - }, - }, - { - accessorKey: 'frequency', - header: ({ column }) => { - return ( - - ); - }, - cell: ({ row }) => ( -
    {row.getValue('frequency')}
    - ), - }, - { - id: 'actions', - enableHiding: false, - header: t('common.action'), - cell: ({ row }) => { - return ( -
    - - - - - - - -

    {t('common.delete')}

    -
    -
    - - - - - -

    {t('common.rename')}

    -
    -
    -
    - ); - }, - }, - ]; - - const table = useReactTable({ - data: tagList, - columns, - onSortingChange: setSorting, - onColumnFiltersChange: setColumnFilters, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), - onColumnVisibilityChange: setColumnVisibility, - onRowSelectionChange: setRowSelection, - state: { - sorting, - columnFilters, - columnVisibility, - rowSelection, - }, - }); - - const selectedRowLength = table.getFilteredSelectedRowModel().rows.length; - - return ( - -
    -
    - - table.getColumn('tag')?.setFilterValue(event.target.value) - } - className="w-1/2" - /> - {selectedRowLength > 0 && ( - x.original.tag), - )} - > - - - )} -
    - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - )) - ) : ( - - - No results. - - - )} - -
    -
    -
    -
    - {selectedRowLength} of {table.getFilteredRowModel().rows.length}{' '} - row(s) selected. -
    -
    - - -
    -
    - {tagRenameVisible && ( - - )} -
    - ); -} diff --git a/web/src/pages/dataset/setting/tag-table/rename-dialog/index.tsx b/web/src/pages/dataset/setting/tag-table/rename-dialog/index.tsx deleted file mode 100644 index b95907f9243..00000000000 --- a/web/src/pages/dataset/setting/tag-table/rename-dialog/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { LoadingButton } from '@/components/ui/loading-button'; -import { useTagIsRenaming } from '@/hooks/knowledge-hooks'; -import { IModalProps } from '@/interfaces/common'; -import { TagRenameId } from '@/pages/add-knowledge/constant'; -import { useTranslation } from 'react-i18next'; -import { RenameForm } from './rename-form'; - -export function RenameDialog({ - hideModal, - initialName, -}: IModalProps & { initialName: string }) { - const { t } = useTranslation(); - const loading = useTagIsRenaming(); - - return ( - - - - {t('common.rename')} - - - - - {t('common.save')} - - - - - ); -} diff --git a/web/src/pages/dataset/setting/tag-table/rename-dialog/rename-form.tsx b/web/src/pages/dataset/setting/tag-table/rename-dialog/rename-form.tsx deleted file mode 100644 index 9c8f1cf7e4b..00000000000 --- a/web/src/pages/dataset/setting/tag-table/rename-dialog/rename-form.tsx +++ /dev/null @@ -1,83 +0,0 @@ -'use client'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { useRenameTag } from '@/hooks/knowledge-hooks'; -import { IModalProps } from '@/interfaces/common'; -import { TagRenameId } from '@/pages/add-knowledge/constant'; -import { useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; - -export function RenameForm({ - initialName, - hideModal, -}: IModalProps & { initialName: string }) { - const { t } = useTranslation(); - const FormSchema = z.object({ - name: z - .string() - .min(1, { - message: t('common.namePlaceholder'), - }) - .trim(), - }); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { - name: '', - }, - }); - - const { renameTag } = useRenameTag(); - - async function onSubmit(data: z.infer) { - const ret = await renameTag({ fromTag: initialName, toTag: data.name }); - if (ret) { - hideModal?.(); - } - } - - useEffect(() => { - form.setValue('name', initialName); - }, [form, initialName]); - - return ( -
    - - ( - - {t('common.name')} - - - - - - )} - /> - - - ); -} diff --git a/web/src/pages/dataset/setting/tag-tabs.tsx b/web/src/pages/dataset/setting/tag-tabs.tsx deleted file mode 100644 index abcd3f673c0..00000000000 --- a/web/src/pages/dataset/setting/tag-tabs.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Segmented } from 'antd'; -import { SegmentedLabeledOption } from 'antd/es/segmented'; -import { upperFirst } from 'lodash'; -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { TagTable } from './tag-table'; -import { TagWordCloud } from './tag-word-cloud'; - -enum TagType { - Cloud = 'cloud', - Table = 'table', -} - -const TagContentMap = { - [TagType.Cloud]: , - [TagType.Table]: , -}; - -export function TagTabs() { - const [value, setValue] = useState(TagType.Cloud); - const { t } = useTranslation(); - - const options: SegmentedLabeledOption[] = [TagType.Cloud, TagType.Table].map( - (x) => ({ - label: t(`knowledgeConfiguration.tag${upperFirst(x)}`), - value: x, - }), - ); - - return ( -
    - setValue(val as TagType)} - /> - {TagContentMap[value]} -
    - ); -} diff --git a/web/src/pages/dataset/setting/tag-word-cloud.tsx b/web/src/pages/dataset/setting/tag-word-cloud.tsx deleted file mode 100644 index b71ed69afb1..00000000000 --- a/web/src/pages/dataset/setting/tag-word-cloud.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useFetchTagList } from '@/hooks/knowledge-hooks'; -import { Chart } from '@antv/g2'; -import { sumBy } from 'lodash'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; - -export function TagWordCloud() { - const domRef = useRef(null); - let chartRef = useRef(); - const { list } = useFetchTagList(); - - const { list: tagList } = useMemo(() => { - const nextList = list.sort((a, b) => b[1] - a[1]).slice(0, 256); - - return { - list: nextList.map((x) => ({ text: x[0], value: x[1], name: x[0] })), - sumValue: sumBy(nextList, (x: [string, number]) => x[1]), - length: nextList.length, - }; - }, [list]); - - const renderWordCloud = useCallback(() => { - if (domRef.current) { - chartRef.current = new Chart({ container: domRef.current }); - - chartRef.current.options({ - type: 'wordCloud', - autoFit: true, - layout: { - fontSize: [10, 50], - // fontSize: (d: any) => { - // if (d.value) { - // return (d.value / sumValue) * 100 * (length / 10); - // } - // return 0; - // }, - }, - data: { - type: 'inline', - value: tagList, - }, - encode: { color: 'text' }, - legend: false, - tooltip: { - title: 'name', // title - items: ['value'], // data item - }, - }); - - chartRef.current.render(); - } - }, [tagList]); - - useEffect(() => { - renderWordCloud(); - - return () => { - chartRef.current?.destroy(); - }; - }, [renderWordCloud]); - - return
    ; -} diff --git a/web/src/pages/dataset/setting/utils.ts b/web/src/pages/dataset/setting/utils.ts deleted file mode 100644 index 4c5666467cf..00000000000 --- a/web/src/pages/dataset/setting/utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -const getImageName = (prefix: string, length: number) => - new Array(length) - .fill(0) - .map((x, idx) => `chunk-method/${prefix}-0${idx + 1}`); - -export const ImageMap = { - book: getImageName('book', 4), - laws: getImageName('law', 2), - manual: getImageName('manual', 4), - picture: getImageName('media', 2), - naive: getImageName('naive', 2), - paper: getImageName('paper', 2), - presentation: getImageName('presentation', 2), - qa: getImageName('qa', 2), - resume: getImageName('resume', 2), - table: getImageName('table', 2), - one: getImageName('one', 2), - knowledge_graph: getImageName('knowledge-graph', 2), - tag: getImageName('tag', 2), -}; diff --git a/web/src/pages/dataset/sidebar/index.tsx b/web/src/pages/dataset/sidebar/index.tsx index 277eb6b834e..c612f205936 100644 --- a/web/src/pages/dataset/sidebar/index.tsx +++ b/web/src/pages/dataset/sidebar/index.tsx @@ -1,3 +1,4 @@ +import { IconFontFill } from '@/components/icon-font'; import { RAGFlowAvatar } from '@/components/ragflow-avatar'; import { Button } from '@/components/ui/button'; import { useSecondPathName } from '@/hooks/route-hook'; diff --git a/web/src/routes.ts b/web/src/routes.ts index f2f9a47c05b..a1ee7dc75b9 100644 --- a/web/src/routes.ts +++ b/web/src/routes.ts @@ -30,7 +30,6 @@ export enum Routes { ProfilePrompt = `${ProfileSetting}${Prompt}`, ProfileProfile = `${ProfileSetting}${Profile}`, DatasetTesting = '/testing', - DatasetSetting = '/setting', Chunk = '/chunk', ChunkResult = `${Chunk}${Chunk}`, Parsed = '/parsed', @@ -262,10 +261,6 @@ const routes = [ path: `${Routes.Dataset}/:id`, component: `@/pages${Routes.Dataset}`, }, - { - path: `${Routes.DatasetBase}${Routes.DatasetSetting}/:id`, - component: `@/pages${Routes.DatasetBase}${Routes.DatasetSetting}`, - }, { path: `${Routes.DatasetBase}${Routes.DatasetTesting}/:id`, component: `@/pages${Routes.DatasetBase}${Routes.DatasetTesting}`, diff --git a/web/tailwind.css b/web/tailwind.css index fc96e623265..a65a4b609cc 100644 --- a/web/tailwind.css +++ b/web/tailwind.css @@ -119,7 +119,7 @@ /* --state-success: #3ba05c; */ --state-success: 59 160 92; /* --state-warning: #767573; */ - --state-warning: 118 117 115; + --state-warning: 250 173 20; --state-error: 216 73 75; --team-group: #5ab77e; From ee0c38da66457eb1e4c688adc74dc978747fbd79 Mon Sep 17 00:00:00 2001 From: buua436 <66937541+buua436@users.noreply.github.com> Date: Thu, 9 Oct 2025 16:56:23 +0800 Subject: [PATCH 0778/1187] fix:update searxng_url logic (#10440) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? issue: [#10417](https://github.com/infiniflow/ragflow/issues/10417) change: Adjusted the `searxng_url` priority logic to ensure the frontend-provided URL takes precedence over the model’s default configuration. This allows user-specified SearXNG endpoints to be correctly applied during execution, improving flexibility across different environments. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- agent/tools/searxng.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/tools/searxng.py b/agent/tools/searxng.py index f8c30bfd1ac..32e80758518 100644 --- a/agent/tools/searxng.py +++ b/agent/tools/searxng.py @@ -85,7 +85,7 @@ def _invoke(self, **kwargs): self.set_output("formalized_content", "") return "" - searxng_url = (kwargs.get("searxng_url") or getattr(self._param, "searxng_url", "") or "").strip() + searxng_url = (getattr(self._param, "searxng_url", "") or kwargs.get("searxng_url") or "").strip() # In try-run, if no URL configured, just return empty instead of raising if not searxng_url: self.set_output("formalized_content", "") From 1fc2889f989afd34198b32200afb3ec7535c2ceb Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 9 Oct 2025 17:21:38 +0800 Subject: [PATCH 0779/1187] Feat: Replace the collapse icon #9869 (#10442) ### What problem does this PR solve? Feat: Replace the collapse icon #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/.storybook/main.ts | 1 + web/.storybook/preview.ts | 1 + web/public/iconfont.js | 4 + web/src/components/collapse.tsx | 39 +++++-- web/src/stories/collapse.stories.tsx | 153 +++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 web/src/stories/collapse.stories.tsx diff --git a/web/.storybook/main.ts b/web/.storybook/main.ts index e1022053b0c..dcba9b6c051 100644 --- a/web/.storybook/main.ts +++ b/web/.storybook/main.ts @@ -3,6 +3,7 @@ import path from 'path'; const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + staticDirs: ['../public'], addons: [ '@storybook/addon-webpack5-compiler-swc', '@storybook/addon-docs', diff --git a/web/.storybook/preview.ts b/web/.storybook/preview.ts index 07fc69ec79e..58c7d34be96 100644 --- a/web/.storybook/preview.ts +++ b/web/.storybook/preview.ts @@ -1,6 +1,7 @@ import '@/locales/config'; import type { Preview } from '@storybook/react-webpack5'; import { createElement } from 'react'; +import '../public/iconfont.js'; import { TooltipProvider } from '../src/components/ui/tooltip'; import '../tailwind.css'; diff --git a/web/public/iconfont.js b/web/public/iconfont.js index f514c4cfdc2..4d923a373f9 100644 --- a/web/public/iconfont.js +++ b/web/public/iconfont.js @@ -59,6 +59,10 @@ ` ` + + ` + + +` + ''), ((h) => { var a = (l = (l = document.getElementsByTagName('script'))[ diff --git a/web/src/components/collapse.tsx b/web/src/components/collapse.tsx index bbe62412c05..46b1712296e 100644 --- a/web/src/components/collapse.tsx +++ b/web/src/components/collapse.tsx @@ -3,9 +3,16 @@ import { CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; +import { cn } from '@/lib/utils'; import { CollapsibleProps } from '@radix-ui/react-collapsible'; -import { ListCollapse } from 'lucide-react'; -import { PropsWithChildren, ReactNode } from 'react'; +import { + PropsWithChildren, + ReactNode, + useCallback, + useEffect, + useState, +} from 'react'; +import { IconFontFill } from './icon-font'; type CollapseProps = Omit & { title?: ReactNode; @@ -16,22 +23,42 @@ export function Collapse({ title, children, rightContent, - open, + open = true, defaultOpen = false, onOpenChange, disabled, }: CollapseProps) { + const [currentOpen, setCurrentOpen] = useState(open); + + useEffect(() => { + setCurrentOpen(open); + }, [open]); + + const handleOpenChange = useCallback( + (open: boolean) => { + setCurrentOpen(open); + onOpenChange?.(open); + }, + [onOpenChange], + ); + return (
    - {title} + + {title}
    {rightContent}
    diff --git a/web/src/stories/collapse.stories.tsx b/web/src/stories/collapse.stories.tsx new file mode 100644 index 00000000000..d9761e53b40 --- /dev/null +++ b/web/src/stories/collapse.stories.tsx @@ -0,0 +1,153 @@ +import type { Meta, StoryObj } from '@storybook/react-webpack5'; + +import { fn } from 'storybook/test'; + +import { Collapse } from '@/components/collapse'; +import { Button } from '@/components/ui/button'; + +// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export +const meta = { + title: 'Example/Collapse', + component: Collapse, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout + layout: 'centered', + docs: { + description: { + component: ` +## Component Description + +Collapse is a component that allows you to show or hide content with a smooth animation. It can be controlled or uncontrolled and supports custom titles and right-aligned content. + +The component uses a trigger element (typically with an icon) to toggle the visibility of its content. It's built on top of Radix UI's Collapsible primitive. + `, + }, + }, + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs + // More on argTypes: https://storybook.js.org/docs/api/argtypes + argTypes: { + title: { + control: 'text', + description: 'The title text or element to display in the trigger', + }, + open: { + control: 'boolean', + description: 'Controlled open state of the collapse', + }, + defaultOpen: { + control: 'boolean', + description: 'Initial open state of the collapse', + }, + disabled: { + control: 'boolean', + description: 'Whether the collapse is disabled', + }, + rightContent: { + control: 'text', + description: 'Content to display on the right side of the trigger', + }, + onOpenChange: { + action: 'onOpenChange', + description: 'Callback function when the open state changes', + }, + }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args + args: { onOpenChange: fn() }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Default: Story = { + args: { + title: 'Collapse Title', + children: ( +
    +

    This is the collapsible content. It can be any React node.

    +

    You can put any content here, including other components.

    +
    + ), + }, + parameters: { + docs: { + description: { + story: ` +### Usage Examples + +\`\`\`tsx +import { Collapse } from '@/components/collapse'; + + +
    +

    This is the collapsible content.

    +
    +
    +\`\`\` + `, + }, + }, + }, +}; + +export const WithRightContent: Story = { + args: { + title: 'Collapse with Right Content', + rightContent: , + children: ( +
    +

    + This collapse has additional content on the right side of the trigger. +

    +
    + ), + }, + parameters: { + docs: { + description: { + story: ` +### Usage Examples + +\`\`\`tsx +import { Collapse } from '@/components/collapse'; +import { Button } from '@/components/ui/button'; + +Action} +> +
    +

    Content with right-aligned action button.

    +
    +
    +\`\`\` + `, + }, + }, + }, +}; + +export const InitiallyClosed: Story = { + args: { + title: 'Initially Closed Collapse', + defaultOpen: false, + children: ( +
    +

    This collapse is initially closed.

    +
    + ), + }, +}; + +export const Disabled: Story = { + args: { + title: 'Disabled Collapse', + disabled: true, + children: ( +
    +

    This collapse is disabled and cannot be toggled.

    +
    + ), + }, +}; From f04c9e29370e0ff5d8170bff193b2383edbec96a Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Thu, 9 Oct 2025 19:03:12 +0800 Subject: [PATCH 0780/1187] Fix: correctly update parser method & correct vllm pdf parser (#10441) ### What problem does this PR solve? Fix: correctly update parser method ### Type of change - [X] Bug Fix (non-breaking change which fixes an issue) --- api/apps/document_app.py | 4 ++-- deepdoc/parser/pdf_parser.py | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/api/apps/document_app.py b/api/apps/document_app.py index 0b3bfd6ba61..b80d59b099f 100644 --- a/api/apps/document_app.py +++ b/api/apps/document_app.py @@ -557,8 +557,8 @@ def get(doc_id): @login_required @validate_request("doc_id") def change_parser(): - req = request.json + req = request.json if not DocumentService.accessible(req["doc_id"], current_user.id): return get_json_result(data=False, message="No authorization.", code=settings.RetCode.AUTHENTICATION_ERROR) @@ -582,7 +582,7 @@ def reset_doc(): settings.docStoreConn.delete({"doc_id": doc.id}, search.index_name(tenant_id), doc.kb_id) try: - if "pipeline_id" in req: + if "pipeline_id" in req and req["pipeline_id"] != "": if doc.pipeline_id == req["pipeline_id"]: return get_json_result(data=True) DocumentService.update_by_id(doc.id, {"pipeline_id": req["pipeline_id"]}) diff --git a/deepdoc/parser/pdf_parser.py b/deepdoc/parser/pdf_parser.py index ea3a87b14b0..c73b610ade5 100644 --- a/deepdoc/parser/pdf_parser.py +++ b/deepdoc/parser/pdf_parser.py @@ -1274,12 +1274,16 @@ def __call__(self, filename, from_page=0, to_page=100000, **kwargs): prompt=vision_llm_describe_prompt(page=pdf_page_num + 1), callback=callback, ) + if kwargs.get("callback"): kwargs["callback"](idx * 1.0 / len(self.page_images), f"Processed: {idx + 1}/{len(self.page_images)}") if text: width, height = self.page_images[idx].size - all_docs.append((text, f"{pdf_page_num + 1} 0 {width / zoomin} 0 {height / zoomin}")) + all_docs.append(( + text, + f"@@{pdf_page_num + 1}\t{0.0:.1f}\t{width / zoomin:.1f}\t{0.0:.1f}\t{height / zoomin:.1f}##" + )) return all_docs, [] From f4324e89d96fd2249f40a288b3db2a2bd52ad16f Mon Sep 17 00:00:00 2001 From: balibabu Date: Thu, 9 Oct 2025 19:03:29 +0800 Subject: [PATCH 0781/1187] Feat: Importing data flow files from the list page #9869 (#10446) ### What problem does this PR solve? Feat: Importing data flow files from the list page #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../llm-setting-items/llm-form-field.tsx | 2 +- web/src/constants/agent.ts | 10 +++++ web/src/hooks/use-agent-request.ts | 3 +- web/src/pages/agents/use-import-json.ts | 33 +++++++++++++++-- web/src/pages/data-flow/constant.tsx | 16 +++----- .../form/parser-form/common-form-fields.tsx | 11 +++++- .../form/parser-form/video-form-fields.tsx | 13 ++++++- web/src/pages/data-flow/index.tsx | 6 +-- web/src/pages/data-flow/log-sheet/index.tsx | 37 ++++++++++--------- 9 files changed, 90 insertions(+), 41 deletions(-) diff --git a/web/src/components/llm-setting-items/llm-form-field.tsx b/web/src/components/llm-setting-items/llm-form-field.tsx index 52aceb4a32b..79809f3e8f8 100644 --- a/web/src/components/llm-setting-items/llm-form-field.tsx +++ b/web/src/components/llm-setting-items/llm-form-field.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { SelectWithSearch } from '../originui/select-with-search'; import { RAGFlowFormItem } from '../ragflow-form'; -type LLMFormFieldProps = { +export type LLMFormFieldProps = { options?: any[]; name?: string; }; diff --git a/web/src/constants/agent.ts b/web/src/constants/agent.ts index 7e934088590..6e388d7a0d9 100644 --- a/web/src/constants/agent.ts +++ b/web/src/constants/agent.ts @@ -52,3 +52,13 @@ export enum AgentCategory { AgentCanvas = 'agent_canvas', DataflowCanvas = 'dataflow_canvas', } + +export enum DataflowOperator { + Begin = 'File', + Note = 'Note', + Parser = 'Parser', + Tokenizer = 'Tokenizer', + Splitter = 'Splitter', + HierarchicalMerger = 'HierarchicalMerger', + Extractor = 'Extractor', +} diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index ba6ce5d95cc..6b9673b4432 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -3,7 +3,6 @@ import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-f import message from '@/components/ui/message'; import { AgentGlobals } from '@/constants/agent'; import { - DSL, IAgentLogsRequest, IAgentLogsResponse, IFlow, @@ -295,7 +294,7 @@ export const useSetAgent = (showMessage: boolean = true) => { mutationFn: async (params: { id?: string; title?: string; - dsl?: DSL; + dsl?: Record; avatar?: string; canvas_category?: string; }) => { diff --git a/web/src/pages/agents/use-import-json.ts b/web/src/pages/agents/use-import-json.ts index 54c0f89a39f..e402475951f 100644 --- a/web/src/pages/agents/use-import-json.ts +++ b/web/src/pages/agents/use-import-json.ts @@ -1,13 +1,20 @@ import { useToast } from '@/components/hooks/use-toast'; +import message from '@/components/ui/message'; +import { AgentCategory, DataflowOperator } from '@/constants/agent'; import { FileMimeType } from '@/constants/common'; import { useSetModalState } from '@/hooks/common-hooks'; import { EmptyDsl, useSetAgent } from '@/hooks/use-agent-request'; -import { message } from 'antd'; +import { Node } from '@xyflow/react'; import isEmpty from 'lodash/isEmpty'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { DataflowEmptyDsl } from './hooks/use-create-agent'; import { FormSchemaType } from './upload-agent-dialog/upload-agent-form'; +function hasNode(nodes: Node[], operator: DataflowOperator) { + return nodes.some((x) => x.data.label === operator); +} + export const useHandleImportJsonFile = () => { const { visible: fileUploadVisible, @@ -32,8 +39,28 @@ export const useHandleImportJsonFile = () => { try { const graph = JSON.parse(graphStr); if (graphStr && !isEmpty(graph) && Array.isArray(graph?.nodes)) { - const dsl = { ...EmptyDsl, graph }; - setAgent({ title: name, dsl }); + const nodes: Node[] = graph.nodes; + + let isAgent = true; + + if ( + hasNode(nodes, DataflowOperator.Begin) && + hasNode(nodes, DataflowOperator.Parser) + ) { + isAgent = false; + } + + const dsl = isAgent + ? { ...EmptyDsl, graph } + : { ...DataflowEmptyDsl, graph }; + + setAgent({ + title: name, + dsl, + canvas_category: isAgent + ? AgentCategory.AgentCanvas + : AgentCategory.DataflowCanvas, + }); hideFileUploadModal(); } else { message.error(errorMessage); diff --git a/web/src/pages/data-flow/constant.tsx b/web/src/pages/data-flow/constant.tsx index 950227e600c..600febef99e 100644 --- a/web/src/pages/data-flow/constant.tsx +++ b/web/src/pages/data-flow/constant.tsx @@ -1,10 +1,14 @@ import { ParseDocumentType } from '@/components/layout-recognize-form-field'; -import { initialLlmBaseValues } from '@/constants/agent'; +import { + initialLlmBaseValues, + DataflowOperator as Operator, +} from '@/constants/agent'; import { ChatVariableEnabledField, variableEnabledFieldMap, } from '@/constants/chat'; import { setInitialChatVariableEnabledFieldValue } from '@/utils/chat'; +export { DataflowOperator as Operator } from '@/constants/agent'; import { Circle, @@ -112,16 +116,6 @@ export enum AgentDialogueMode { export const BeginId = 'File'; -export enum Operator { - Begin = 'File', - Note = 'Note', - Parser = 'Parser', - Tokenizer = 'Tokenizer', - Splitter = 'Splitter', - HierarchicalMerger = 'HierarchicalMerger', - Extractor = 'Extractor', -} - export const SwitchLogicOperatorOptions = ['and', 'or']; export const CommonOperatorList = Object.values(Operator).filter( diff --git a/web/src/pages/data-flow/form/parser-form/common-form-fields.tsx b/web/src/pages/data-flow/form/parser-form/common-form-fields.tsx index dc7039781ce..cd3fe04975a 100644 --- a/web/src/pages/data-flow/form/parser-form/common-form-fields.tsx +++ b/web/src/pages/data-flow/form/parser-form/common-form-fields.tsx @@ -1,6 +1,9 @@ import { crossLanguageOptions } from '@/components/cross-language-form-field'; import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; -import { LLMFormField } from '@/components/llm-setting-items/llm-form-field'; +import { + LLMFormField, + LLMFormFieldProps, +} from '@/components/llm-setting-items/llm-form-field'; import { SelectWithSearch, SelectWithSearchFlagOptionType, @@ -60,10 +63,14 @@ export function ParserMethodFormField({ ); } -export function LargeModelFormField({ prefix }: CommonProps) { +export function LargeModelFormField({ + prefix, + options, +}: CommonProps & Pick) { return ( ); } diff --git a/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx b/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx index 79f64d6fd4f..d1ca8c68d5f 100644 --- a/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx +++ b/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx @@ -1,13 +1,24 @@ +import { LlmModelType } from '@/constants/knowledge'; +import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; import { LargeModelFormField, OutputFormatFormFieldProps, } from './common-form-fields'; export function VideoFormFields({ prefix }: OutputFormatFormFieldProps) { + const modelOptions = useComposeLlmOptionsByModelTypes([ + LlmModelType.Chat, + LlmModelType.Image2text, + LlmModelType.Speech2text, + ]); + return ( <> {/* Multimodal Model */} - + ); } diff --git a/web/src/pages/data-flow/index.tsx b/web/src/pages/data-flow/index.tsx index f536f8580d0..5291ab834aa 100644 --- a/web/src/pages/data-flow/index.tsx +++ b/web/src/pages/data-flow/index.tsx @@ -162,9 +162,9 @@ export default function DataFlow() { onClick={handleRunAgent} loading={running} > - {running || ( - - )} + {isParsing || running ? t('dataflow.running') : t('flow.run')} diff --git a/web/src/pages/data-flow/log-sheet/index.tsx b/web/src/pages/data-flow/log-sheet/index.tsx index f66bada1bdf..a5bcb2787a9 100644 --- a/web/src/pages/data-flow/log-sheet/index.tsx +++ b/web/src/pages/data-flow/log-sheet/index.tsx @@ -61,24 +61,25 @@ export function LogSheet({ {t('flow.log')} - + {isCompleted && ( + + )}
    From d931c33cedde18497d16a6be55ae2b394d44557f Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Fri, 10 Oct 2025 09:17:36 +0800 Subject: [PATCH 0782/1187] Fix typos: retrievaler -> retriever (#10372) ### What problem does this PR solve? Fix typos ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai --- admin/admin_client.py | 16 ++++++ admin/admin_server.py | 15 ++++++ admin/auth.py | 17 +++++++ admin/config.py | 20 +++++++- admin/models.py | 15 ++++++ admin/responses.py | 23 ++++++++- admin/routes.py | 21 +++++++- admin/services.py | 23 ++++++++- agent/tools/retrieval.py | 6 +-- api/apps/api_app.py | 4 +- api/apps/chunk_app.py | 8 +-- api/apps/kb_app.py | 6 +-- api/apps/plugin_app.py | 18 +++++++ api/apps/sdk/agent.py | 5 +- api/apps/sdk/dataset.py | 37 +++++++++----- api/apps/sdk/dify_retrieval.py | 26 +++++----- api/apps/sdk/doc.py | 6 +-- api/apps/sdk/files.py | 52 ++++++++++++++------ api/apps/sdk/session.py | 60 +++++++++++++++-------- api/apps/system_app.py | 6 ++- api/apps/tenant_app.py | 6 ++- api/common/exceptions.py | 19 +++++++- api/db/services/dialog_service.py | 10 ++-- api/db/services/mcp_server_service.py | 3 +- api/db/services/search_service.py | 3 +- api/db/services/task_service.py | 17 ++++--- api/db/services/tenant_llm_service.py | 41 +++++++++++----- api/db/services/user_canvas_version.py | 26 +++++----- api/settings.py | 10 ++-- api/utils/api_utils.py | 67 +++++++++++++++----------- graphrag/general/index.py | 4 +- graphrag/general/smoke.py | 2 +- graphrag/light/smoke.py | 2 +- graphrag/utils.py | 8 +-- rag/app/tag.py | 4 +- rag/benchmark.py | 2 +- rag/svr/task_executor.py | 6 +-- 37 files changed, 438 insertions(+), 176 deletions(-) diff --git a/admin/admin_client.py b/admin/admin_client.py index 007b73e29df..399d173abce 100644 --- a/admin/admin_client.py +++ b/admin/admin_client.py @@ -1,3 +1,19 @@ +# +# 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 argparse import base64 diff --git a/admin/admin_server.py b/admin/admin_server.py index 27ee0c72aa2..e76b3864298 100644 --- a/admin/admin_server.py +++ b/admin/admin_server.py @@ -1,3 +1,18 @@ +# +# 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 os import signal diff --git a/admin/auth.py b/admin/auth.py index 001ba59400d..5160912a40e 100644 --- a/admin/auth.py +++ b/admin/auth.py @@ -1,3 +1,20 @@ +# +# 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 uuid from functools import wraps diff --git a/admin/config.py b/admin/config.py index 94147de8e65..609c321a755 100644 --- a/admin/config.py +++ b/admin/config.py @@ -1,3 +1,20 @@ +# +# 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 threading from enum import Enum @@ -35,7 +52,8 @@ class BaseConfig(BaseModel): detail_func_name: str def to_dict(self) -> dict[str, Any]: - return {'id': self.id, 'name': self.name, 'host': self.host, 'port': self.port, 'service_type': self.service_type} + return {'id': self.id, 'name': self.name, 'host': self.host, 'port': self.port, + 'service_type': self.service_type} class MetaConfig(BaseConfig): diff --git a/admin/models.py b/admin/models.py index e69de29bb2d..177b91dd051 100644 --- a/admin/models.py +++ b/admin/models.py @@ -0,0 +1,15 @@ +# +# 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. +# diff --git a/admin/responses.py b/admin/responses.py index 00cee703897..54f841a8307 100644 --- a/admin/responses.py +++ b/admin/responses.py @@ -1,15 +1,34 @@ +# +# 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. +# + + from flask import jsonify -def success_response(data=None, message="Success", code = 0): + +def success_response(data=None, message="Success", code=0): return jsonify({ "code": code, "message": message, "data": data }), 200 + def error_response(message="Error", code=-1, data=None): return jsonify({ "code": code, "message": message, "data": data - }), 400 \ No newline at end of file + }), 400 diff --git a/admin/routes.py b/admin/routes.py index a737305de58..afc82bc9d24 100644 --- a/admin/routes.py +++ b/admin/routes.py @@ -1,3 +1,20 @@ +# +# 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. +# + + from flask import Blueprint, request from auth import login_verify @@ -42,7 +59,7 @@ def create_user(): res = UserMgr.create_user(username, password, role) if res["success"]: user_info = res["user_info"] - user_info.pop("password") # do not return password + user_info.pop("password") # do not return password return success_response(user_info, "User created successfully") else: return error_response("create user failed") @@ -102,6 +119,7 @@ def alter_user_activate_status(username): except Exception as e: return error_response(str(e), 500) + @admin_bp.route('/users/', methods=['GET']) @login_verify def get_user_details(username): @@ -114,6 +132,7 @@ def get_user_details(username): except Exception as e: return error_response(str(e), 500) + @admin_bp.route('/users//datasets', methods=['GET']) @login_verify def get_user_datasets(username): diff --git a/admin/services.py b/admin/services.py index 2c8eaaf7cbb..3aa738fd5a0 100644 --- a/admin/services.py +++ b/admin/services.py @@ -1,3 +1,20 @@ +# +# 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 re from werkzeug.security import check_password_hash from api.db import ActiveEnum @@ -12,13 +29,15 @@ from api.common.exceptions import AdminException, UserAlreadyExistsError, UserNotFoundError from config import SERVICE_CONFIGS + class UserMgr: @staticmethod def get_all_users(): users = UserService.get_all_users() result = [] for user in users: - result.append({'email': user.email, 'nickname': user.nickname, 'create_date': user.create_date, 'is_active': user.is_active}) + result.append({'email': user.email, 'nickname': user.nickname, 'create_date': user.create_date, + 'is_active': user.is_active}) return result @staticmethod @@ -112,6 +131,7 @@ def update_user_activate_status(username, activate_status: str): UserService.update_user(usr.id, {"is_active": target_status}) return f"Turn {_activate_status} user activate status successfully!" + class UserServiceMgr: @staticmethod @@ -150,6 +170,7 @@ def get_user_agents(username): 'canvas_category': r['canvas_category'] } for r in res] + class ServiceMgr: @staticmethod diff --git a/agent/tools/retrieval.py b/agent/tools/retrieval.py index 24370f1cab9..07c16d97dbd 100644 --- a/agent/tools/retrieval.py +++ b/agent/tools/retrieval.py @@ -121,7 +121,7 @@ def _invoke(self, **kwargs): if kbs: query = re.sub(r"^user[::\s]*", "", query, flags=re.IGNORECASE) - kbinfos = settings.retrievaler.retrieval( + kbinfos = settings.retriever.retrieval( query, embd_mdl, [kb.tenant_id for kb in kbs], @@ -135,7 +135,7 @@ def _invoke(self, **kwargs): rank_feature=label_question(query, kbs), ) if self._param.use_kg: - ck = settings.kg_retrievaler.retrieval(query, + ck = settings.kg_retriever.retrieval(query, [kb.tenant_id for kb in kbs], kb_ids, embd_mdl, @@ -146,7 +146,7 @@ def _invoke(self, **kwargs): kbinfos = {"chunks": [], "doc_aggs": []} if self._param.use_kg and kbs: - ck = settings.kg_retrievaler.retrieval(query, [kb.tenant_id for kb in kbs], filtered_kb_ids, embd_mdl, LLMBundle(kbs[0].tenant_id, LLMType.CHAT)) + ck = settings.kg_retriever.retrieval(query, [kb.tenant_id for kb in kbs], filtered_kb_ids, embd_mdl, LLMBundle(kbs[0].tenant_id, LLMType.CHAT)) if ck["content_with_weight"]: ck["content"] = ck["content_with_weight"] del ck["content_with_weight"] diff --git a/api/apps/api_app.py b/api/apps/api_app.py index 1bdb7c2f877..4637009d61b 100644 --- a/api/apps/api_app.py +++ b/api/apps/api_app.py @@ -536,7 +536,7 @@ def list_chunks(): ) kb_ids = KnowledgebaseService.get_kb_ids(tenant_id) - res = settings.retrievaler.chunk_list(doc_id, tenant_id, kb_ids) + res = settings.retriever.chunk_list(doc_id, tenant_id, kb_ids) res = [ { "content": res_item["content_with_weight"], @@ -884,7 +884,7 @@ def retrieval(): if req.get("keyword", False): chat_mdl = LLMBundle(kbs[0].tenant_id, LLMType.CHAT) question += keyword_extraction(chat_mdl, question) - ranks = settings.retrievaler.retrieval(question, embd_mdl, kbs[0].tenant_id, kb_ids, page, size, + ranks = settings.retriever.retrieval(question, embd_mdl, kbs[0].tenant_id, kb_ids, page, size, similarity_threshold, vector_similarity_weight, top, doc_ids, rerank_mdl=rerank_mdl, highlight= highlight, rank_feature=label_question(question, kbs)) diff --git a/api/apps/chunk_app.py b/api/apps/chunk_app.py index bfd80ea9fce..5c3c30d65d1 100644 --- a/api/apps/chunk_app.py +++ b/api/apps/chunk_app.py @@ -60,7 +60,7 @@ def list_chunk(): } if "available_int" in req: query["available_int"] = int(req["available_int"]) - sres = settings.retrievaler.search(query, search.index_name(tenant_id), kb_ids, highlight=True) + sres = settings.retriever.search(query, search.index_name(tenant_id), kb_ids, highlight=True) res = {"total": sres.total, "chunks": [], "doc": doc.to_dict()} for id in sres.ids: d = { @@ -346,7 +346,7 @@ def retrieval_test(): question += keyword_extraction(chat_mdl, question) labels = label_question(question, [kb]) - ranks = settings.retrievaler.retrieval(question, embd_mdl, tenant_ids, kb_ids, page, size, + ranks = settings.retriever.retrieval(question, embd_mdl, tenant_ids, kb_ids, page, size, float(req.get("similarity_threshold", 0.0)), float(req.get("vector_similarity_weight", 0.3)), top, @@ -354,7 +354,7 @@ def retrieval_test(): rank_feature=labels ) if use_kg: - ck = settings.kg_retrievaler.retrieval(question, + ck = settings.kg_retriever.retrieval(question, tenant_ids, kb_ids, embd_mdl, @@ -384,7 +384,7 @@ def knowledge_graph(): "doc_ids": [doc_id], "knowledge_graph_kwd": ["graph", "mind_map"] } - sres = settings.retrievaler.search(req, search.index_name(tenant_id), kb_ids) + sres = settings.retriever.search(req, search.index_name(tenant_id), kb_ids) obj = {"graph": {}, "mind_map": {}} for id in sres.ids[:2]: ty = sres.field[id]["knowledge_graph_kwd"] diff --git a/api/apps/kb_app.py b/api/apps/kb_app.py index 0c56b15adac..bca28fb6f30 100644 --- a/api/apps/kb_app.py +++ b/api/apps/kb_app.py @@ -282,7 +282,7 @@ def list_tags(kb_id): tenants = UserTenantService.get_tenants_by_user_id(current_user.id) tags = [] for tenant in tenants: - tags += settings.retrievaler.all_tags(tenant["tenant_id"], [kb_id]) + tags += settings.retriever.all_tags(tenant["tenant_id"], [kb_id]) return get_json_result(data=tags) @@ -301,7 +301,7 @@ def list_tags_from_kbs(): tenants = UserTenantService.get_tenants_by_user_id(current_user.id) tags = [] for tenant in tenants: - tags += settings.retrievaler.all_tags(tenant["tenant_id"], kb_ids) + tags += settings.retriever.all_tags(tenant["tenant_id"], kb_ids) return get_json_result(data=tags) @@ -362,7 +362,7 @@ def knowledge_graph(kb_id): obj = {"graph": {}, "mind_map": {}} if not settings.docStoreConn.indexExist(search.index_name(kb.tenant_id), kb_id): return get_json_result(data=obj) - sres = settings.retrievaler.search(req, search.index_name(kb.tenant_id), [kb_id]) + sres = settings.retriever.search(req, search.index_name(kb.tenant_id), [kb_id]) if not len(sres.ids): return get_json_result(data=obj) diff --git a/api/apps/plugin_app.py b/api/apps/plugin_app.py index dcd209daaac..9ca04416db0 100644 --- a/api/apps/plugin_app.py +++ b/api/apps/plugin_app.py @@ -1,8 +1,26 @@ +# +# 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. +# + + from flask import Response from flask_login import login_required from api.utils.api_utils import get_json_result from plugin import GlobalPluginManager + @manager.route('/llm_tools', methods=['GET']) # noqa: F821 @login_required def llm_tools() -> Response: diff --git a/api/apps/sdk/agent.py b/api/apps/sdk/agent.py index 704a3ffcf32..b4132836516 100644 --- a/api/apps/sdk/agent.py +++ b/api/apps/sdk/agent.py @@ -25,6 +25,7 @@ from api.utils.api_utils import get_result from flask import request + @manager.route('/agents', methods=['GET']) # noqa: F821 @token_required def list_agents(tenant_id): @@ -41,7 +42,7 @@ def list_agents(tenant_id): desc = False else: desc = True - canvas = UserCanvasService.get_list(tenant_id,page_number,items_per_page,orderby,desc,id,title) + canvas = UserCanvasService.get_list(tenant_id, page_number, items_per_page, orderby, desc, id, title) return get_result(data=canvas) @@ -93,7 +94,7 @@ def update_agent(tenant_id: str, agent_id: str): req["dsl"] = json.dumps(req["dsl"], ensure_ascii=False) req["dsl"] = json.loads(req["dsl"]) - + if req.get("title") is not None: req["title"] = req["title"].strip() diff --git a/api/apps/sdk/dataset.py b/api/apps/sdk/dataset.py index 7b25f1d8b8e..ff446055c0f 100644 --- a/api/apps/sdk/dataset.py +++ b/api/apps/sdk/dataset.py @@ -215,7 +215,8 @@ def delete(tenant_id): continue kb_id_instance_pairs.append((kb_id, kb)) if len(error_kb_ids) > 0: - return get_error_permission_result(message=f"""User '{tenant_id}' lacks permission for datasets: '{", ".join(error_kb_ids)}'""") + return get_error_permission_result( + message=f"""User '{tenant_id}' lacks permission for datasets: '{", ".join(error_kb_ids)}'""") errors = [] success_count = 0 @@ -232,7 +233,8 @@ def delete(tenant_id): ] ) File2DocumentService.delete_by_document_id(doc.id) - FileService.filter_delete([File.source_type == FileSource.KNOWLEDGEBASE, File.type == "folder", File.name == kb.name]) + FileService.filter_delete( + [File.source_type == FileSource.KNOWLEDGEBASE, File.type == "folder", File.name == kb.name]) if not KnowledgebaseService.delete_by_id(kb_id): errors.append(f"Delete dataset error for {kb_id}") continue @@ -329,7 +331,8 @@ def update(tenant_id, dataset_id): try: kb = KnowledgebaseService.get_or_none(id=dataset_id, tenant_id=tenant_id) if kb is None: - return get_error_permission_result(message=f"User '{tenant_id}' lacks permission for dataset '{dataset_id}'") + return get_error_permission_result( + message=f"User '{tenant_id}' lacks permission for dataset '{dataset_id}'") if req.get("parser_config"): req["parser_config"] = deep_merge(kb.parser_config, req["parser_config"]) @@ -341,7 +344,8 @@ def update(tenant_id, dataset_id): del req["parser_config"] if "name" in req and req["name"].lower() != kb.name.lower(): - exists = KnowledgebaseService.get_or_none(name=req["name"], tenant_id=tenant_id, status=StatusEnum.VALID.value) + exists = KnowledgebaseService.get_or_none(name=req["name"], tenant_id=tenant_id, + status=StatusEnum.VALID.value) if exists: return get_error_data_result(message=f"Dataset name '{req['name']}' already exists") @@ -349,7 +353,8 @@ def update(tenant_id, dataset_id): if not req["embd_id"]: req["embd_id"] = kb.embd_id if kb.chunk_num != 0 and req["embd_id"] != kb.embd_id: - return get_error_data_result(message=f"When chunk_num ({kb.chunk_num}) > 0, embedding_model must remain {kb.embd_id}") + return get_error_data_result( + message=f"When chunk_num ({kb.chunk_num}) > 0, embedding_model must remain {kb.embd_id}") ok, err = verify_embedding_availability(req["embd_id"], tenant_id) if not ok: return err @@ -359,10 +364,12 @@ def update(tenant_id, dataset_id): return get_error_argument_result(message="'pagerank' can only be set when doc_engine is elasticsearch") if req["pagerank"] > 0: - settings.docStoreConn.update({"kb_id": kb.id}, {PAGERANK_FLD: req["pagerank"]}, search.index_name(kb.tenant_id), kb.id) + settings.docStoreConn.update({"kb_id": kb.id}, {PAGERANK_FLD: req["pagerank"]}, + search.index_name(kb.tenant_id), kb.id) else: # Elasticsearch requires PAGERANK_FLD be non-zero! - settings.docStoreConn.update({"exists": PAGERANK_FLD}, {"remove": PAGERANK_FLD}, search.index_name(kb.tenant_id), kb.id) + settings.docStoreConn.update({"exists": PAGERANK_FLD}, {"remove": PAGERANK_FLD}, + search.index_name(kb.tenant_id), kb.id) if not KnowledgebaseService.update_by_id(kb.id, req): return get_error_data_result(message="Update dataset error.(Database error)") @@ -473,9 +480,10 @@ def list_datasets(tenant_id): logging.exception(e) return get_error_data_result(message="Database operation failed") + @manager.route('/datasets//knowledge_graph', methods=['GET']) # noqa: F821 @token_required -def knowledge_graph(tenant_id,dataset_id): +def knowledge_graph(tenant_id, dataset_id): if not KnowledgebaseService.accessible(dataset_id, tenant_id): return get_result( data=False, @@ -491,7 +499,7 @@ def knowledge_graph(tenant_id,dataset_id): obj = {"graph": {}, "mind_map": {}} if not settings.docStoreConn.indexExist(search.index_name(kb.tenant_id), dataset_id): return get_result(data=obj) - sres = settings.retrievaler.search(req, search.index_name(kb.tenant_id), [dataset_id]) + sres = settings.retriever.search(req, search.index_name(kb.tenant_id), [dataset_id]) if not len(sres.ids): return get_result(data=obj) @@ -507,14 +515,16 @@ def knowledge_graph(tenant_id,dataset_id): if "nodes" in obj["graph"]: obj["graph"]["nodes"] = sorted(obj["graph"]["nodes"], key=lambda x: x.get("pagerank", 0), reverse=True)[:256] if "edges" in obj["graph"]: - node_id_set = { o["id"] for o in obj["graph"]["nodes"] } - filtered_edges = [o for o in obj["graph"]["edges"] if o["source"] != o["target"] and o["source"] in node_id_set and o["target"] in node_id_set] + node_id_set = {o["id"] for o in obj["graph"]["nodes"]} + filtered_edges = [o for o in obj["graph"]["edges"] if + o["source"] != o["target"] and o["source"] in node_id_set and o["target"] in node_id_set] obj["graph"]["edges"] = sorted(filtered_edges, key=lambda x: x.get("weight", 0), reverse=True)[:128] return get_result(data=obj) + @manager.route('/datasets//knowledge_graph', methods=['DELETE']) # noqa: F821 @token_required -def delete_knowledge_graph(tenant_id,dataset_id): +def delete_knowledge_graph(tenant_id, dataset_id): if not KnowledgebaseService.accessible(dataset_id, tenant_id): return get_result( data=False, @@ -522,6 +532,7 @@ def delete_knowledge_graph(tenant_id,dataset_id): code=settings.RetCode.AUTHENTICATION_ERROR ) _, kb = KnowledgebaseService.get_by_id(dataset_id) - settings.docStoreConn.delete({"knowledge_graph_kwd": ["graph", "subgraph", "entity", "relation"]}, search.index_name(kb.tenant_id), dataset_id) + settings.docStoreConn.delete({"knowledge_graph_kwd": ["graph", "subgraph", "entity", "relation"]}, + search.index_name(kb.tenant_id), dataset_id) return get_result(data=True) diff --git a/api/apps/sdk/dify_retrieval.py b/api/apps/sdk/dify_retrieval.py index 446d4d74a52..dc5476f3435 100644 --- a/api/apps/sdk/dify_retrieval.py +++ b/api/apps/sdk/dify_retrieval.py @@ -1,4 +1,4 @@ - # +# # Copyright 2024 The InfiniFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,9 +38,9 @@ def retrieval(tenant_id): retrieval_setting = req.get("retrieval_setting", {}) similarity_threshold = float(retrieval_setting.get("score_threshold", 0.0)) top = int(retrieval_setting.get("top_k", 1024)) - metadata_condition = req.get("metadata_condition",{}) + metadata_condition = req.get("metadata_condition", {}) metas = DocumentService.get_meta_by_kbs([kb_id]) - + doc_ids = [] try: @@ -50,12 +50,12 @@ def retrieval(tenant_id): embd_mdl = LLMBundle(kb.tenant_id, LLMType.EMBEDDING.value, llm_name=kb.embd_id) print(metadata_condition) - print("after",convert_conditions(metadata_condition)) + # print("after", convert_conditions(metadata_condition)) doc_ids.extend(meta_filter(metas, convert_conditions(metadata_condition))) - print("doc_ids",doc_ids) + # print("doc_ids", doc_ids) if not doc_ids and metadata_condition is not None: doc_ids = ['-999'] - ranks = settings.retrievaler.retrieval( + ranks = settings.retriever.retrieval( question, embd_mdl, kb.tenant_id, @@ -70,17 +70,17 @@ def retrieval(tenant_id): ) if use_kg: - ck = settings.kg_retrievaler.retrieval(question, - [tenant_id], - [kb_id], - embd_mdl, - LLMBundle(kb.tenant_id, LLMType.CHAT)) + ck = settings.kg_retriever.retrieval(question, + [tenant_id], + [kb_id], + embd_mdl, + LLMBundle(kb.tenant_id, LLMType.CHAT)) if ck["content_with_weight"]: ranks["chunks"].insert(0, ck) records = [] for c in ranks["chunks"]: - e, doc = DocumentService.get_by_id( c["doc_id"]) + e, doc = DocumentService.get_by_id(c["doc_id"]) c.pop("vector", None) meta = getattr(doc, 'meta_fields', {}) meta["doc_id"] = c["doc_id"] @@ -100,5 +100,3 @@ def retrieval(tenant_id): ) logging.exception(e) return build_error_result(message=str(e), code=settings.RetCode.SERVER_ERROR) - - diff --git a/api/apps/sdk/doc.py b/api/apps/sdk/doc.py index 8d5a413b001..2cc2926df45 100644 --- a/api/apps/sdk/doc.py +++ b/api/apps/sdk/doc.py @@ -982,7 +982,7 @@ def list_chunks(tenant_id, dataset_id, document_id): _ = Chunk(**final_chunk) elif settings.docStoreConn.indexExist(search.index_name(tenant_id), dataset_id): - sres = settings.retrievaler.search(query, search.index_name(tenant_id), [dataset_id], emb_mdl=None, highlight=True) + sres = settings.retriever.search(query, search.index_name(tenant_id), [dataset_id], emb_mdl=None, highlight=True) res["total"] = sres.total for id in sres.ids: d = { @@ -1446,7 +1446,7 @@ def retrieval_test(tenant_id): chat_mdl = LLMBundle(kb.tenant_id, LLMType.CHAT) question += keyword_extraction(chat_mdl, question) - ranks = settings.retrievaler.retrieval( + ranks = settings.retriever.retrieval( question, embd_mdl, tenant_ids, @@ -1462,7 +1462,7 @@ def retrieval_test(tenant_id): rank_feature=label_question(question, kbs), ) if use_kg: - ck = settings.kg_retrievaler.retrieval(question, [k.tenant_id for k in kbs], kb_ids, embd_mdl, LLMBundle(kb.tenant_id, LLMType.CHAT)) + ck = settings.kg_retriever.retrieval(question, [k.tenant_id for k in kbs], kb_ids, embd_mdl, LLMBundle(kb.tenant_id, LLMType.CHAT)) if ck["content_with_weight"]: ranks["chunks"].insert(0, ck) diff --git a/api/apps/sdk/files.py b/api/apps/sdk/files.py index 96efe208d02..17af87dc00f 100644 --- a/api/apps/sdk/files.py +++ b/api/apps/sdk/files.py @@ -1,3 +1,20 @@ +# +# 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 pathlib import re @@ -17,7 +34,8 @@ from api.utils.file_utils import filename_type from rag.utils.storage_factory import STORAGE_IMPL -@manager.route('/file/upload', methods=['POST']) # noqa: F821 + +@manager.route('/file/upload', methods=['POST']) # noqa: F821 @token_required def upload(tenant_id): """ @@ -97,12 +115,14 @@ def upload(tenant_id): e, file = FileService.get_by_id(file_id_list[len_id_list - 1]) if not e: return get_json_result(data=False, message="Folder not found!", code=404) - last_folder = FileService.create_folder(file, file_id_list[len_id_list - 1], file_obj_names, len_id_list) + last_folder = FileService.create_folder(file, file_id_list[len_id_list - 1], file_obj_names, + len_id_list) else: e, file = FileService.get_by_id(file_id_list[len_id_list - 2]) if not e: return get_json_result(data=False, message="Folder not found!", code=404) - last_folder = FileService.create_folder(file, file_id_list[len_id_list - 2], file_obj_names, len_id_list) + last_folder = FileService.create_folder(file, file_id_list[len_id_list - 2], file_obj_names, + len_id_list) filetype = filename_type(file_obj_names[file_len - 1]) location = file_obj_names[file_len - 1] @@ -129,7 +149,7 @@ def upload(tenant_id): return server_error_response(e) -@manager.route('/file/create', methods=['POST']) # noqa: F821 +@manager.route('/file/create', methods=['POST']) # noqa: F821 @token_required def create(tenant_id): """ @@ -207,7 +227,7 @@ def create(tenant_id): return server_error_response(e) -@manager.route('/file/list', methods=['GET']) # noqa: F821 +@manager.route('/file/list', methods=['GET']) # noqa: F821 @token_required def list_files(tenant_id): """ @@ -299,7 +319,7 @@ def list_files(tenant_id): return server_error_response(e) -@manager.route('/file/root_folder', methods=['GET']) # noqa: F821 +@manager.route('/file/root_folder', methods=['GET']) # noqa: F821 @token_required def get_root_folder(tenant_id): """ @@ -335,7 +355,7 @@ def get_root_folder(tenant_id): return server_error_response(e) -@manager.route('/file/parent_folder', methods=['GET']) # noqa: F821 +@manager.route('/file/parent_folder', methods=['GET']) # noqa: F821 @token_required def get_parent_folder(): """ @@ -380,7 +400,7 @@ def get_parent_folder(): return server_error_response(e) -@manager.route('/file/all_parent_folder', methods=['GET']) # noqa: F821 +@manager.route('/file/all_parent_folder', methods=['GET']) # noqa: F821 @token_required def get_all_parent_folders(tenant_id): """ @@ -428,7 +448,7 @@ def get_all_parent_folders(tenant_id): return server_error_response(e) -@manager.route('/file/rm', methods=['POST']) # noqa: F821 +@manager.route('/file/rm', methods=['POST']) # noqa: F821 @token_required def rm(tenant_id): """ @@ -502,7 +522,7 @@ def rm(tenant_id): return server_error_response(e) -@manager.route('/file/rename', methods=['POST']) # noqa: F821 +@manager.route('/file/rename', methods=['POST']) # noqa: F821 @token_required def rename(tenant_id): """ @@ -542,7 +562,8 @@ def rename(tenant_id): if not e: return get_json_result(message="File not found!", code=404) - if file.type != FileType.FOLDER.value and pathlib.Path(req["name"].lower()).suffix != pathlib.Path(file.name.lower()).suffix: + if file.type != FileType.FOLDER.value and pathlib.Path(req["name"].lower()).suffix != pathlib.Path( + file.name.lower()).suffix: return get_json_result(data=False, message="The extension of file can't be changed", code=400) for existing_file in FileService.query(name=req["name"], pf_id=file.parent_id): @@ -562,9 +583,9 @@ def rename(tenant_id): return server_error_response(e) -@manager.route('/file/get/', methods=['GET']) # noqa: F821 +@manager.route('/file/get/', methods=['GET']) # noqa: F821 @token_required -def get(tenant_id,file_id): +def get(tenant_id, file_id): """ Download a file. --- @@ -610,7 +631,7 @@ def get(tenant_id,file_id): return server_error_response(e) -@manager.route('/file/mv', methods=['POST']) # noqa: F821 +@manager.route('/file/mv', methods=['POST']) # noqa: F821 @token_required def move(tenant_id): """ @@ -669,6 +690,7 @@ def move(tenant_id): except Exception as e: return server_error_response(e) + @manager.route('/file/convert', methods=['POST']) # noqa: F821 @token_required def convert(tenant_id): @@ -735,4 +757,4 @@ def convert(tenant_id): file2documents.append(file2document.to_json()) return get_json_result(data=file2documents) except Exception as e: - return server_error_response(e) \ No newline at end of file + return server_error_response(e) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 10b6e975259..684d0092804 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -36,7 +36,8 @@ from api.db.services.search_service import SearchService from api.db.services.user_service import UserTenantService from api.utils import get_uuid -from api.utils.api_utils import check_duplicate_ids, get_data_openai, get_error_data_result, get_json_result, get_result, server_error_response, token_required, validate_request +from api.utils.api_utils import check_duplicate_ids, get_data_openai, get_error_data_result, get_json_result, \ + get_result, server_error_response, token_required, validate_request from rag.app.tag import label_question from rag.prompts.template import load_prompt from rag.prompts.generator import cross_languages, gen_meta_filter, keyword_extraction, chunks_format @@ -88,7 +89,8 @@ def create_agent_session(tenant_id, agent_id): canvas.reset() cvs.dsl = json.loads(str(canvas)) - conv = {"id": session_id, "dialog_id": cvs.id, "user_id": user_id, "message": [{"role": "assistant", "content": canvas.get_prologue()}], "source": "agent", "dsl": cvs.dsl} + conv = {"id": session_id, "dialog_id": cvs.id, "user_id": user_id, + "message": [{"role": "assistant", "content": canvas.get_prologue()}], "source": "agent", "dsl": cvs.dsl} API4ConversationService.save(**conv) conv["agent_id"] = conv.pop("dialog_id") return get_result(data=conv) @@ -279,7 +281,7 @@ def streamed_response_generator(chat_id, dia, msg): reasoning_match = re.search(r"(.*?)", answer, flags=re.DOTALL) if reasoning_match: reasoning_part = reasoning_match.group(1) - content_part = answer[reasoning_match.end() :] + content_part = answer[reasoning_match.end():] else: reasoning_part = "" content_part = answer @@ -324,7 +326,8 @@ def streamed_response_generator(chat_id, dia, msg): response["choices"][0]["delta"]["content"] = None response["choices"][0]["delta"]["reasoning_content"] = None response["choices"][0]["finish_reason"] = "stop" - response["usage"] = {"prompt_tokens": len(prompt), "completion_tokens": token_used, "total_tokens": len(prompt) + token_used} + response["usage"] = {"prompt_tokens": len(prompt), "completion_tokens": token_used, + "total_tokens": len(prompt) + token_used} if need_reference: response["choices"][0]["delta"]["reference"] = chunks_format(last_ans.get("reference", [])) response["choices"][0]["delta"]["final_content"] = last_ans.get("answer", "") @@ -559,7 +562,8 @@ def list_agent_session(tenant_id, agent_id): desc = True # dsl defaults to True in all cases except for False and false include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false" - total, convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id, user_id, include_dsl) + total, convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id, + user_id, include_dsl) if not convs: return get_result(data=[]) for conv in convs: @@ -581,7 +585,8 @@ def list_agent_session(tenant_id, agent_id): if message_num != 0 and messages[message_num]["role"] != "user": chunk_list = [] # Add boundary and type checks to prevent KeyError - if chunk_num < len(conv["reference"]) and conv["reference"][chunk_num] is not None and isinstance(conv["reference"][chunk_num], dict) and "chunks" in conv["reference"][chunk_num]: + if chunk_num < len(conv["reference"]) and conv["reference"][chunk_num] is not None and isinstance( + conv["reference"][chunk_num], dict) and "chunks" in conv["reference"][chunk_num]: chunks = conv["reference"][chunk_num]["chunks"] for chunk in chunks: # Ensure chunk is a dictionary before calling get method @@ -639,13 +644,16 @@ def delete(tenant_id, chat_id): if errors: if success_count > 0: - return get_result(data={"success_count": success_count, "errors": errors}, message=f"Partially deleted {success_count} sessions with {len(errors)} errors") + return get_result(data={"success_count": success_count, "errors": errors}, + message=f"Partially deleted {success_count} sessions with {len(errors)} errors") else: return get_error_data_result(message="; ".join(errors)) if duplicate_messages: if success_count > 0: - return get_result(message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", data={"success_count": success_count, "errors": duplicate_messages}) + return get_result( + message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", + data={"success_count": success_count, "errors": duplicate_messages}) else: return get_error_data_result(message=";".join(duplicate_messages)) @@ -691,13 +699,16 @@ def delete_agent_session(tenant_id, agent_id): if errors: if success_count > 0: - return get_result(data={"success_count": success_count, "errors": errors}, message=f"Partially deleted {success_count} sessions with {len(errors)} errors") + return get_result(data={"success_count": success_count, "errors": errors}, + message=f"Partially deleted {success_count} sessions with {len(errors)} errors") else: return get_error_data_result(message="; ".join(errors)) if duplicate_messages: if success_count > 0: - return get_result(message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", data={"success_count": success_count, "errors": duplicate_messages}) + return get_result( + message=f"Partially deleted {success_count} sessions with {len(duplicate_messages)} errors", + data={"success_count": success_count, "errors": duplicate_messages}) else: return get_error_data_result(message=";".join(duplicate_messages)) @@ -730,7 +741,9 @@ def stream(): for ans in ask(req["question"], req["kb_ids"], uid): yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n" except Exception as e: - yield "data:" + json.dumps({"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, ensure_ascii=False) + "\n\n" + yield "data:" + json.dumps( + {"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, + ensure_ascii=False) + "\n\n" yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n" resp = Response(stream(), mimetype="text/event-stream") @@ -882,7 +895,9 @@ def begin_inputs(agent_id): return get_error_data_result(f"Can't find agent by ID: {agent_id}") canvas = Canvas(json.dumps(cvs.dsl), objs[0].tenant_id) - return get_result(data={"title": cvs.title, "avatar": cvs.avatar, "inputs": canvas.get_component_input_form("begin"), "prologue": canvas.get_prologue(), "mode": canvas.get_mode()}) + return get_result( + data={"title": cvs.title, "avatar": cvs.avatar, "inputs": canvas.get_component_input_form("begin"), + "prologue": canvas.get_prologue(), "mode": canvas.get_mode()}) @manager.route("/searchbots/ask", methods=["POST"]) # noqa: F821 @@ -911,7 +926,9 @@ def stream(): for ans in ask(req["question"], req["kb_ids"], uid, search_config=search_config): yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n" except Exception as e: - yield "data:" + json.dumps({"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, ensure_ascii=False) + "\n\n" + yield "data:" + json.dumps( + {"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, + ensure_ascii=False) + "\n\n" yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n" resp = Response(stream(), mimetype="text/event-stream") @@ -978,7 +995,8 @@ def retrieval_test_embedded(): tenant_ids.append(tenant.tenant_id) break else: - return get_json_result(data=False, message="Only owner of knowledgebase authorized for this operation.", code=settings.RetCode.OPERATING_ERROR) + return get_json_result(data=False, message="Only owner of knowledgebase authorized for this operation.", + code=settings.RetCode.OPERATING_ERROR) e, kb = KnowledgebaseService.get_by_id(kb_ids[0]) if not e: @@ -998,11 +1016,13 @@ def retrieval_test_embedded(): question += keyword_extraction(chat_mdl, question) labels = label_question(question, [kb]) - ranks = settings.retrievaler.retrieval( - question, embd_mdl, tenant_ids, kb_ids, page, size, similarity_threshold, vector_similarity_weight, top, doc_ids, rerank_mdl=rerank_mdl, highlight=req.get("highlight"), rank_feature=labels + ranks = settings.retriever.retrieval( + question, embd_mdl, tenant_ids, kb_ids, page, size, similarity_threshold, vector_similarity_weight, top, + doc_ids, rerank_mdl=rerank_mdl, highlight=req.get("highlight"), rank_feature=labels ) if use_kg: - ck = settings.kg_retrievaler.retrieval(question, tenant_ids, kb_ids, embd_mdl, LLMBundle(kb.tenant_id, LLMType.CHAT)) + ck = settings.kg_retriever.retrieval(question, tenant_ids, kb_ids, embd_mdl, + LLMBundle(kb.tenant_id, LLMType.CHAT)) if ck["content_with_weight"]: ranks["chunks"].insert(0, ck) @@ -1013,7 +1033,8 @@ def retrieval_test_embedded(): return get_json_result(data=ranks) except Exception as e: if str(e).find("not_found") > 0: - return get_json_result(data=False, message="No chunk found! Check the chunk status please!", code=settings.RetCode.DATA_ERROR) + return get_json_result(data=False, message="No chunk found! Check the chunk status please!", + code=settings.RetCode.DATA_ERROR) return server_error_response(e) @@ -1082,7 +1103,8 @@ def detail_share_embedded(): if SearchService.query(tenant_id=tenant.tenant_id, id=search_id): break else: - return get_json_result(data=False, message="Has no permission for this operation.", code=settings.RetCode.OPERATING_ERROR) + return get_json_result(data=False, message="Has no permission for this operation.", + code=settings.RetCode.OPERATING_ERROR) search = SearchService.get_detail(search_id) if not search: diff --git a/api/apps/system_app.py b/api/apps/system_app.py index fa2b5f11675..4302813e855 100644 --- a/api/apps/system_app.py +++ b/api/apps/system_app.py @@ -39,6 +39,7 @@ from flask import jsonify from api.utils.health_utils import run_health_checks + @manager.route("/version", methods=["GET"]) # noqa: F821 @login_required def version(): @@ -161,7 +162,7 @@ def status(): task_executors = REDIS_CONN.smembers("TASKEXE") now = datetime.now().timestamp() for task_executor_id in task_executors: - heartbeats = REDIS_CONN.zrangebyscore(task_executor_id, now - 60*30, now) + heartbeats = REDIS_CONN.zrangebyscore(task_executor_id, now - 60 * 30, now) heartbeats = [json.loads(heartbeat) for heartbeat in heartbeats] task_executor_heartbeats[task_executor_id] = heartbeats except Exception: @@ -273,7 +274,8 @@ def token_list(): objs = [o.to_dict() for o in objs] for o in objs: if not o["beta"]: - o["beta"] = generate_confirmation_token(generate_confirmation_token(tenants[0].tenant_id)).replace("ragflow-", "")[:32] + o["beta"] = generate_confirmation_token(generate_confirmation_token(tenants[0].tenant_id)).replace( + "ragflow-", "")[:32] APITokenService.filter_update([APIToken.tenant_id == tenant_id, APIToken.token == o["token"]], o) return get_json_result(data=objs) except Exception as e: diff --git a/api/apps/tenant_app.py b/api/apps/tenant_app.py index 63c7f74b73b..10668491ebc 100644 --- a/api/apps/tenant_app.py +++ b/api/apps/tenant_app.py @@ -70,7 +70,8 @@ def create(tenant_id): return get_data_error_result(message=f"{invite_user_email} is already in the team.") if user_tenant_role == UserTenantRole.OWNER: return get_data_error_result(message=f"{invite_user_email} is the owner of the team.") - return get_data_error_result(message=f"{invite_user_email} is in the team, but the role: {user_tenant_role} is invalid.") + return get_data_error_result( + message=f"{invite_user_email} is in the team, but the role: {user_tenant_role} is invalid.") UserTenantService.save( id=get_uuid(), @@ -132,7 +133,8 @@ def tenant_list(): @login_required def agree(tenant_id): try: - UserTenantService.filter_update([UserTenant.tenant_id == tenant_id, UserTenant.user_id == current_user.id], {"role": UserTenantRole.NORMAL}) + UserTenantService.filter_update([UserTenant.tenant_id == tenant_id, UserTenant.user_id == current_user.id], + {"role": UserTenantRole.NORMAL}) return get_json_result(data=True) except Exception as e: return server_error_response(e) diff --git a/api/common/exceptions.py b/api/common/exceptions.py index 5ce0e0bc211..0790ff4ccc2 100644 --- a/api/common/exceptions.py +++ b/api/common/exceptions.py @@ -1,3 +1,20 @@ +# +# 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. +# + + class AdminException(Exception): def __init__(self, message, code=400): super().__init__(message) @@ -18,4 +35,4 @@ def __init__(self, username): class CannotDeleteAdminError(AdminException): def __init__(self): - super().__init__("Cannot delete admin account", 403) \ No newline at end of file + super().__init__("Cannot delete admin account", 403) diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index 673000ff9f9..a8ddf178d23 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -370,7 +370,7 @@ def chat(dialog, messages, stream=True, **kwargs): chat_mdl.bind_tools(toolcall_session, tools) bind_models_ts = timer() - retriever = settings.retrievaler + retriever = settings.retriever questions = [m["content"] for m in messages if m["role"] == "user"][-3:] attachments = kwargs["doc_ids"].split(",") if "doc_ids" in kwargs else [] if "doc_ids" in messages[-1]: @@ -472,7 +472,7 @@ def chat(dialog, messages, stream=True, **kwargs): kbinfos["chunks"].extend(tav_res["chunks"]) kbinfos["doc_aggs"].extend(tav_res["doc_aggs"]) if prompt_config.get("use_kg"): - ck = settings.kg_retrievaler.retrieval(" ".join(questions), tenant_ids, dialog.kb_ids, embd_mdl, + ck = settings.kg_retriever.retrieval(" ".join(questions), tenant_ids, dialog.kb_ids, embd_mdl, LLMBundle(dialog.tenant_id, LLMType.CHAT)) if ck["content_with_weight"]: kbinfos["chunks"].insert(0, ck) @@ -658,7 +658,7 @@ def get_table(): logging.debug(f"{question} get SQL(refined): {sql}") tried_times += 1 - return settings.retrievaler.sql_retrieval(sql, format="json"), sql + return settings.retriever.sql_retrieval(sql, format="json"), sql tbl, sql = get_table() if tbl is None: @@ -752,7 +752,7 @@ def ask(question, kb_ids, tenant_id, chat_llm_name=None, search_config={}): embedding_list = list(set([kb.embd_id for kb in kbs])) is_knowledge_graph = all([kb.parser_id == ParserType.KG for kb in kbs]) - retriever = settings.retrievaler if not is_knowledge_graph else settings.kg_retrievaler + retriever = settings.retriever if not is_knowledge_graph else settings.kg_retriever embd_mdl = LLMBundle(tenant_id, LLMType.EMBEDDING, embedding_list[0]) chat_mdl = LLMBundle(tenant_id, LLMType.CHAT, chat_llm_name) @@ -848,7 +848,7 @@ def gen_mindmap(question, kb_ids, tenant_id, search_config={}): if not doc_ids: doc_ids = None - ranks = settings.retrievaler.retrieval( + ranks = settings.retriever.retrieval( question=question, embd_mdl=embd_mdl, tenant_ids=tenant_ids, diff --git a/api/db/services/mcp_server_service.py b/api/db/services/mcp_server_service.py index 101555f4b34..1eae882d6f3 100644 --- a/api/db/services/mcp_server_service.py +++ b/api/db/services/mcp_server_service.py @@ -33,7 +33,8 @@ class MCPServerService(CommonService): @classmethod @DB.connection_context() - def get_servers(cls, tenant_id: str, id_list: list[str] | None, page_number, items_per_page, orderby, desc, keywords): + def get_servers(cls, tenant_id: str, id_list: list[str] | None, page_number, items_per_page, orderby, desc, + keywords): """Retrieve all MCP servers associated with a tenant. This method fetches all MCP servers for a given tenant, ordered by creation time. diff --git a/api/db/services/search_service.py b/api/db/services/search_service.py index acb07da5755..de69f283736 100644 --- a/api/db/services/search_service.py +++ b/api/db/services/search_service.py @@ -94,7 +94,8 @@ def get_by_tenant_ids(cls, joined_tenant_ids, user_id, page_number, items_per_pa query = ( cls.model.select(*fields) .join(User, on=(cls.model.tenant_id == User.id)) - .where(((cls.model.tenant_id.in_(joined_tenant_ids)) | (cls.model.tenant_id == user_id)) & (cls.model.status == StatusEnum.VALID.value)) + .where(((cls.model.tenant_id.in_(joined_tenant_ids)) | (cls.model.tenant_id == user_id)) & ( + cls.model.status == StatusEnum.VALID.value)) ) if keywords: diff --git a/api/db/services/task_service.py b/api/db/services/task_service.py index f31494b0e40..41641733976 100644 --- a/api/db/services/task_service.py +++ b/api/db/services/task_service.py @@ -165,7 +165,7 @@ def get_tasks(cls, doc_id: str): ] tasks = ( cls.model.select(*fields).order_by(cls.model.from_page.asc(), cls.model.create_time.desc()) - .where(cls.model.doc_id == doc_id) + .where(cls.model.doc_id == doc_id) ) tasks = list(tasks.dicts()) if not tasks: @@ -205,18 +205,18 @@ def get_ongoing_doc_name(cls): cls.model.select( *[Document.id, Document.kb_id, Document.location, File.parent_id] ) - .join(Document, on=(cls.model.doc_id == Document.id)) - .join( + .join(Document, on=(cls.model.doc_id == Document.id)) + .join( File2Document, on=(File2Document.document_id == Document.id), join_type=JOIN.LEFT_OUTER, ) - .join( + .join( File, on=(File2Document.file_id == File.id), join_type=JOIN.LEFT_OUTER, ) - .where( + .where( Document.status == StatusEnum.VALID.value, Document.run == TaskStatus.RUNNING.value, ~(Document.type == FileType.VIRTUAL.value), @@ -294,8 +294,8 @@ def update_progress(cls, id, info): cls.model.update(progress=prog).where( (cls.model.id == id) & ( - (cls.model.progress != -1) & - ((prog == -1) | (prog > cls.model.progress)) + (cls.model.progress != -1) & + ((prog == -1) | (prog > cls.model.progress)) ) ).execute() else: @@ -343,6 +343,7 @@ def queue_tasks(doc: dict, bucket: str, name: str, priority: int): - Task digests are calculated for optimization and reuse - Previous task chunks may be reused if available """ + def new_task(): return { "id": get_uuid(), @@ -515,7 +516,7 @@ def queue_dataflow(tenant_id:str, flow_id:str, task_id:str, doc_id:str=CANVAS_DE task["file"] = file if not REDIS_CONN.queue_product( - get_svr_queue_name(priority), message=task + get_svr_queue_name(priority), message=task ): return False, "Can't access Redis. Please check the Redis' status." diff --git a/api/db/services/tenant_llm_service.py b/api/db/services/tenant_llm_service.py index 4eca970ec96..6e826ebae0b 100644 --- a/api/db/services/tenant_llm_service.py +++ b/api/db/services/tenant_llm_service.py @@ -57,8 +57,10 @@ def get_api_key(cls, tenant_id, model_name): @classmethod @DB.connection_context() def get_my_llms(cls, tenant_id): - fields = [cls.model.llm_factory, LLMFactories.logo, LLMFactories.tags, cls.model.model_type, cls.model.llm_name, cls.model.used_tokens] - objs = cls.model.select(*fields).join(LLMFactories, on=(cls.model.llm_factory == LLMFactories.name)).where(cls.model.tenant_id == tenant_id, ~cls.model.api_key.is_null()).dicts() + fields = [cls.model.llm_factory, LLMFactories.logo, LLMFactories.tags, cls.model.model_type, cls.model.llm_name, + cls.model.used_tokens] + objs = cls.model.select(*fields).join(LLMFactories, on=(cls.model.llm_factory == LLMFactories.name)).where( + cls.model.tenant_id == tenant_id, ~cls.model.api_key.is_null()).dicts() return list(objs) @@ -122,7 +124,8 @@ def get_model_config(cls, tenant_id, llm_type, llm_name=None): model_config = {"llm_factory": llm[0].fid, "api_key": "", "llm_name": mdlnm, "api_base": ""} if not model_config: if mdlnm == "flag-embedding": - model_config = {"llm_factory": "Tongyi-Qianwen", "api_key": "", "llm_name": llm_name, "api_base": ""} + model_config = {"llm_factory": "Tongyi-Qianwen", "api_key": "", "llm_name": llm_name, + "api_base": ""} else: if not mdlnm: raise LookupError(f"Type of {llm_type} model is not set.") @@ -137,27 +140,33 @@ def model_instance(cls, tenant_id, llm_type, llm_name=None, lang="Chinese", **kw if llm_type == LLMType.EMBEDDING.value: if model_config["llm_factory"] not in EmbeddingModel: return - return EmbeddingModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], base_url=model_config["api_base"]) + return EmbeddingModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], + base_url=model_config["api_base"]) if llm_type == LLMType.RERANK: if model_config["llm_factory"] not in RerankModel: return - return RerankModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], base_url=model_config["api_base"]) + return RerankModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], + base_url=model_config["api_base"]) if llm_type == LLMType.IMAGE2TEXT.value: if model_config["llm_factory"] not in CvModel: return - return CvModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], lang, base_url=model_config["api_base"], **kwargs) + return CvModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], lang, + base_url=model_config["api_base"], **kwargs) if llm_type == LLMType.CHAT.value: if model_config["llm_factory"] not in ChatModel: return - return ChatModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], base_url=model_config["api_base"], **kwargs) + return ChatModel[model_config["llm_factory"]](model_config["api_key"], model_config["llm_name"], + base_url=model_config["api_base"], **kwargs) if llm_type == LLMType.SPEECH2TEXT: if model_config["llm_factory"] not in Seq2txtModel: return - return Seq2txtModel[model_config["llm_factory"]](key=model_config["api_key"], model_name=model_config["llm_name"], lang=lang, base_url=model_config["api_base"]) + return Seq2txtModel[model_config["llm_factory"]](key=model_config["api_key"], + model_name=model_config["llm_name"], lang=lang, + base_url=model_config["api_base"]) if llm_type == LLMType.TTS: if model_config["llm_factory"] not in TTSModel: return @@ -194,11 +203,14 @@ def increase_usage(cls, tenant_id, llm_type, used_tokens, llm_name=None): try: num = ( cls.model.update(used_tokens=cls.model.used_tokens + used_tokens) - .where(cls.model.tenant_id == tenant_id, cls.model.llm_name == llm_name, cls.model.llm_factory == llm_factory if llm_factory else True) + .where(cls.model.tenant_id == tenant_id, cls.model.llm_name == llm_name, + cls.model.llm_factory == llm_factory if llm_factory else True) .execute() ) except Exception: - logging.exception("TenantLLMService.increase_usage got exception,Failed to update used_tokens for tenant_id=%s, llm_name=%s", tenant_id, llm_name) + logging.exception( + "TenantLLMService.increase_usage got exception,Failed to update used_tokens for tenant_id=%s, llm_name=%s", + tenant_id, llm_name) return 0 return num @@ -206,7 +218,9 @@ def increase_usage(cls, tenant_id, llm_type, used_tokens, llm_name=None): @classmethod @DB.connection_context() def get_openai_models(cls): - objs = cls.model.select().where((cls.model.llm_factory == "OpenAI"), ~(cls.model.llm_name == "text-embedding-3-small"), ~(cls.model.llm_name == "text-embedding-3-large")).dicts() + objs = cls.model.select().where((cls.model.llm_factory == "OpenAI"), + ~(cls.model.llm_name == "text-embedding-3-small"), + ~(cls.model.llm_name == "text-embedding-3-large")).dicts() return list(objs) @classmethod @@ -250,8 +264,9 @@ def __init__(self, tenant_id, llm_type, llm_name=None, lang="Chinese", **kwargs) langfuse_keys = TenantLangfuseService.filter_by_tenant(tenant_id=tenant_id) self.langfuse = None if langfuse_keys: - langfuse = Langfuse(public_key=langfuse_keys.public_key, secret_key=langfuse_keys.secret_key, host=langfuse_keys.host) + langfuse = Langfuse(public_key=langfuse_keys.public_key, secret_key=langfuse_keys.secret_key, + host=langfuse_keys.host) if langfuse.auth_check(): self.langfuse = langfuse trace_id = self.langfuse.create_trace_id() - self.trace_context = {"trace_id": trace_id} \ No newline at end of file + self.trace_context = {"trace_id": trace_id} diff --git a/api/db/services/user_canvas_version.py b/api/db/services/user_canvas_version.py index 9696a78348b..89f73264f0d 100644 --- a/api/db/services/user_canvas_version.py +++ b/api/db/services/user_canvas_version.py @@ -2,22 +2,22 @@ from api.db.services.common_service import CommonService from peewee import DoesNotExist + class UserCanvasVersionService(CommonService): model = UserCanvasVersion - - + @classmethod @DB.connection_context() def list_by_canvas_id(cls, user_canvas_id): try: user_canvas_version = cls.model.select( - *[cls.model.id, - cls.model.create_time, - cls.model.title, - cls.model.create_date, - cls.model.update_date, - cls.model.user_canvas_id, - cls.model.update_time] + *[cls.model.id, + cls.model.create_time, + cls.model.title, + cls.model.create_date, + cls.model.update_date, + cls.model.user_canvas_id, + cls.model.update_time] ).where(cls.model.user_canvas_id == user_canvas_id) return user_canvas_version except DoesNotExist: @@ -46,18 +46,16 @@ def get_all_canvas_version_by_canvas_ids(cls, canvas_ids): @DB.connection_context() def delete_all_versions(cls, user_canvas_id): try: - user_canvas_version = cls.model.select().where(cls.model.user_canvas_id == user_canvas_id).order_by(cls.model.create_time.desc()) + user_canvas_version = cls.model.select().where(cls.model.user_canvas_id == user_canvas_id).order_by( + cls.model.create_time.desc()) if user_canvas_version.count() > 20: delete_ids = [] for i in range(20, user_canvas_version.count()): delete_ids.append(user_canvas_version[i].id) - + cls.delete_by_ids(delete_ids) return True except DoesNotExist: return None except Exception: return None - - - diff --git a/api/settings.py b/api/settings.py index e6763d8a288..9c003d27b6e 100644 --- a/api/settings.py +++ b/api/settings.py @@ -65,8 +65,8 @@ DOC_ENGINE = None docStoreConn = None -retrievaler = None -kg_retrievaler = None +retriever = None +kg_retriever = None # user registration switch REGISTER_ENABLED = 1 @@ -174,7 +174,7 @@ def init_settings(): OAUTH_CONFIG = get_base_config("oauth", {}) - global DOC_ENGINE, docStoreConn, retrievaler, kg_retrievaler + global DOC_ENGINE, docStoreConn, retriever, kg_retriever DOC_ENGINE = os.environ.get("DOC_ENGINE", "elasticsearch") # DOC_ENGINE = os.environ.get('DOC_ENGINE', "opensearch") lower_case_doc_engine = DOC_ENGINE.lower() @@ -187,10 +187,10 @@ def init_settings(): else: raise Exception(f"Not supported doc engine: {DOC_ENGINE}") - retrievaler = search.Dealer(docStoreConn) + retriever = search.Dealer(docStoreConn) from graphrag import search as kg_search - kg_retrievaler = kg_search.KGSearch(docStoreConn) + kg_retriever = kg_search.KGSearch(docStoreConn) if int(os.environ.get("SANDBOX_ENABLED", "0")): global SANDBOX_HOST diff --git a/api/utils/api_utils.py b/api/utils/api_utils.py index 821ec6d31f9..c9e7ae455a7 100644 --- a/api/utils/api_utils.py +++ b/api/utils/api_utils.py @@ -60,6 +60,7 @@ requests.models.complexjson.dumps = functools.partial(json.dumps, cls=CustomJSONEncoder) + def serialize_for_json(obj): """ Recursively serialize objects to make them JSON serializable. @@ -68,8 +69,8 @@ def serialize_for_json(obj): if hasattr(obj, '__dict__'): # For objects with __dict__, try to serialize their attributes try: - return {key: serialize_for_json(value) for key, value in obj.__dict__.items() - if not key.startswith('_')} + return {key: serialize_for_json(value) for key, value in obj.__dict__.items() + if not key.startswith('_')} except (AttributeError, TypeError): return str(obj) elif hasattr(obj, '__name__'): @@ -85,6 +86,7 @@ def serialize_for_json(obj): # Fallback: convert to string representation return str(obj) + def request(**kwargs): sess = requests.Session() stream = kwargs.pop("stream", sess.stream) @@ -105,7 +107,8 @@ def request(**kwargs): settings.HTTP_APP_KEY.encode("ascii"), prepped.path_url.encode("ascii"), prepped.body if kwargs.get("json") else b"", - urlencode(sorted(kwargs["data"].items()), quote_via=quote, safe="-._~").encode("ascii") if kwargs.get("data") and isinstance(kwargs["data"], dict) else b"", + urlencode(sorted(kwargs["data"].items()), quote_via=quote, safe="-._~").encode( + "ascii") if kwargs.get("data") and isinstance(kwargs["data"], dict) else b"", ] ), "sha1", @@ -127,7 +130,7 @@ def request(**kwargs): def get_exponential_backoff_interval(retries, full_jitter=False): """Calculate the exponential backoff wait time.""" # Will be zero if factor equals 0 - countdown = min(REQUEST_MAX_WAIT_SEC, REQUEST_WAIT_SEC * (2**retries)) + countdown = min(REQUEST_MAX_WAIT_SEC, REQUEST_WAIT_SEC * (2 ** retries)) # Full jitter according to # https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ if full_jitter: @@ -158,11 +161,12 @@ def server_error_response(e): if len(e.args) > 1: try: serialized_data = serialize_for_json(e.args[1]) - return get_json_result(code= settings.RetCode.EXCEPTION_ERROR, message=repr(e.args[0]), data=serialized_data) + return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, message=repr(e.args[0]), data=serialized_data) except Exception: return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, message=repr(e.args[0]), data=None) if repr(e).find("index_not_found_exception") >= 0: - return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, message="No chunk found, please upload file and parse it.") + return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, + message="No chunk found, please upload file and parse it.") return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, message=repr(e)) @@ -207,7 +211,8 @@ def decorated_function(*_args, **_kwargs): if no_arguments: error_string += "required argument are missing: {}; ".format(",".join(no_arguments)) if error_arguments: - error_string += "required argument values: {}".format(",".join(["{}={}".format(a[0], a[1]) for a in error_arguments])) + error_string += "required argument values: {}".format( + ",".join(["{}={}".format(a[0], a[1]) for a in error_arguments])) return get_json_result(code=settings.RetCode.ARGUMENT_ERROR, message=error_string) return func(*_args, **_kwargs) @@ -222,7 +227,8 @@ def wrapper(*args, **kwargs): input_arguments = flask_request.json or flask_request.form.to_dict() for param in params: if param in input_arguments: - return get_json_result(code=settings.RetCode.ARGUMENT_ERROR, message=f"Parameter {param} isn't allowed") + return get_json_result(code=settings.RetCode.ARGUMENT_ERROR, + message=f"Parameter {param} isn't allowed") return f(*args, **kwargs) return wrapper @@ -239,6 +245,7 @@ def wrapper(*args, **kwargs): if not usr or not usr.is_active == ActiveEnum.ACTIVE.value: return get_json_result(code=settings.RetCode.FORBIDDEN, message="User isn't active, please activate first.") return f(*args, **kwargs) + return wrapper @@ -259,7 +266,7 @@ def send_file_in_mem(data, filename): return send_file(f, as_attachment=True, attachment_filename=filename) -def get_json_result(code=settings.RetCode.SUCCESS, message="success", data=None): +def get_json_result(code: settings.RetCode = settings.RetCode.SUCCESS, message="success", data=None): response = {"code": code, "message": message, "data": data} return jsonify(response) @@ -314,7 +321,7 @@ def construct_result(code=settings.RetCode.DATA_ERROR, message="data is missing" return jsonify(response) -def construct_json_result(code=settings.RetCode.SUCCESS, message="success", data=None): +def construct_json_result(code: settings.RetCode = settings.RetCode.SUCCESS, message="success", data=None): if data is None: return jsonify({"code": code, "message": message}) else: @@ -347,14 +354,15 @@ def decorated_function(*args, **kwargs): token = authorization_list[1] objs = APIToken.query(token=token) if not objs: - return get_json_result(data=False, message="Authentication error: API key is invalid!", code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message="Authentication error: API key is invalid!", + code=settings.RetCode.AUTHENTICATION_ERROR) kwargs["tenant_id"] = objs[0].tenant_id return func(*args, **kwargs) return decorated_function -def get_result(code=settings.RetCode.SUCCESS, message="", data=None): +def get_result(code: settings.RetCode = settings.RetCode.SUCCESS, message="", data=None): if code == 0: if data is not None: response = {"code": code, "data": data} @@ -366,8 +374,8 @@ def get_result(code=settings.RetCode.SUCCESS, message="", data=None): def get_error_data_result( - message="Sorry! Data missing!", - code=settings.RetCode.DATA_ERROR, + message="Sorry! Data missing!", + code=settings.RetCode.DATA_ERROR, ): result_dict = {"code": code, "message": message} response = {} @@ -402,7 +410,8 @@ def get_parser_config(chunk_method, parser_config): # Define default configurations for each chunking method key_mapping = { - "naive": {"chunk_token_num": 512, "delimiter": r"\n", "html4excel": False, "layout_recognize": "DeepDOC", "raptor": {"use_raptor": False}, "graphrag": {"use_graphrag": False}}, + "naive": {"chunk_token_num": 512, "delimiter": r"\n", "html4excel": False, "layout_recognize": "DeepDOC", + "raptor": {"use_raptor": False}, "graphrag": {"use_graphrag": False}}, "qa": {"raptor": {"use_raptor": False}, "graphrag": {"use_graphrag": False}}, "tag": None, "resume": None, @@ -441,16 +450,16 @@ def get_parser_config(chunk_method, parser_config): def get_data_openai( - id=None, - created=None, - model=None, - prompt_tokens=0, - completion_tokens=0, - content=None, - finish_reason=None, - object="chat.completion", - param=None, - stream=False + id=None, + created=None, + model=None, + prompt_tokens=0, + completion_tokens=0, + content=None, + finish_reason=None, + object="chat.completion", + param=None, + stream=False ): total_tokens = prompt_tokens + completion_tokens @@ -562,7 +571,9 @@ def verify_embedding_availability(embd_id: str, tenant_id: str) -> tuple[bool, R in_llm_service = bool(LLMService.query(llm_name=llm_name, fid=llm_factory, model_type="embedding")) tenant_llms = TenantLLMService.get_my_llms(tenant_id=tenant_id) - is_tenant_model = any(llm["llm_name"] == llm_name and llm["llm_factory"] == llm_factory and llm["model_type"] == "embedding" for llm in tenant_llms) + is_tenant_model = any( + llm["llm_name"] == llm_name and llm["llm_factory"] == llm_factory and llm["model_type"] == "embedding" for + llm in tenant_llms) is_builtin_model = embd_id in settings.BUILTIN_EMBEDDING_MODELS if not (is_builtin_model or is_tenant_model or in_llm_service): @@ -793,7 +804,9 @@ async def _is_strong_enough(): _ = await trio.to_thread.run_sync(lambda: embedding_model.encode(["Are you strong enough!?"])) if chat_model: with trio.fail_after(30): - res = await trio.to_thread.run_sync(lambda: chat_model.chat("Nothing special.", [{"role": "user", "content": "Are you strong enough!?"}], {})) + res = await trio.to_thread.run_sync(lambda: chat_model.chat("Nothing special.", [{"role": "user", + "content": "Are you strong enough!?"}], + {})) if res.find("**ERROR**") >= 0: raise Exception(res) diff --git a/graphrag/general/index.py b/graphrag/general/index.py index 6d0df65bbd8..7cb47de128c 100644 --- a/graphrag/general/index.py +++ b/graphrag/general/index.py @@ -55,7 +55,7 @@ async def run_graphrag( start = trio.current_time() tenant_id, kb_id, doc_id = row["tenant_id"], str(row["kb_id"]), row["doc_id"] chunks = [] - for d in settings.retrievaler.chunk_list(doc_id, tenant_id, [kb_id], fields=["content_with_weight", "doc_id"], sort_by_position=True): + for d in settings.retriever.chunk_list(doc_id, tenant_id, [kb_id], fields=["content_with_weight", "doc_id"], sort_by_position=True): chunks.append(d["content_with_weight"]) with trio.fail_after(max(120, len(chunks) * 60 * 10) if enable_timeout_assertion else 10000000000): @@ -170,7 +170,7 @@ def load_doc_chunks(doc_id: str) -> list[str]: chunks = [] current_chunk = "" - for d in settings.retrievaler.chunk_list( + for d in settings.retriever.chunk_list( doc_id, tenant_id, [kb_id], diff --git a/graphrag/general/smoke.py b/graphrag/general/smoke.py index 3f282fb07c8..5f9fe143712 100644 --- a/graphrag/general/smoke.py +++ b/graphrag/general/smoke.py @@ -62,7 +62,7 @@ async def main(): chunks = [ d["content_with_weight"] - for d in settings.retrievaler.chunk_list( + for d in settings.retriever.chunk_list( args.doc_id, args.tenant_id, [kb_id], diff --git a/graphrag/light/smoke.py b/graphrag/light/smoke.py index 504f09ce71c..f8f505f65d3 100644 --- a/graphrag/light/smoke.py +++ b/graphrag/light/smoke.py @@ -63,7 +63,7 @@ async def main(): chunks = [ d["content_with_weight"] - for d in settings.retrievaler.chunk_list( + for d in settings.retriever.chunk_list( args.doc_id, args.tenant_id, [kb_id], diff --git a/graphrag/utils.py b/graphrag/utils.py index 6abe5f9a968..5e64cdb11f7 100644 --- a/graphrag/utils.py +++ b/graphrag/utils.py @@ -341,7 +341,7 @@ def get_relation(tenant_id, kb_id, from_ent_name, to_ent_name, size=1): ents = list(set(ents)) conds = {"fields": ["content_with_weight"], "size": size, "from_entity_kwd": ents, "to_entity_kwd": ents, "knowledge_graph_kwd": ["relation"]} res = [] - es_res = settings.retrievaler.search(conds, search.index_name(tenant_id), [kb_id] if isinstance(kb_id, str) else kb_id) + es_res = settings.retriever.search(conds, search.index_name(tenant_id), [kb_id] if isinstance(kb_id, str) else kb_id) for id in es_res.ids: try: if size == 1: @@ -398,7 +398,7 @@ async def does_graph_contains(tenant_id, kb_id, doc_id): async def get_graph_doc_ids(tenant_id, kb_id) -> list[str]: conds = {"fields": ["source_id"], "removed_kwd": "N", "size": 1, "knowledge_graph_kwd": ["graph"]} - res = await trio.to_thread.run_sync(lambda: settings.retrievaler.search(conds, search.index_name(tenant_id), [kb_id])) + res = await trio.to_thread.run_sync(lambda: settings.retriever.search(conds, search.index_name(tenant_id), [kb_id])) doc_ids = [] if res.total == 0: return doc_ids @@ -409,7 +409,7 @@ async def get_graph_doc_ids(tenant_id, kb_id) -> list[str]: async def get_graph(tenant_id, kb_id, exclude_rebuild=None): conds = {"fields": ["content_with_weight", "removed_kwd", "source_id"], "size": 1, "knowledge_graph_kwd": ["graph"]} - res = await trio.to_thread.run_sync(settings.retrievaler.search, conds, search.index_name(tenant_id), [kb_id]) + res = await trio.to_thread.run_sync(settings.retriever.search, conds, search.index_name(tenant_id), [kb_id]) if not res.total == 0: for id in res.ids: try: @@ -562,7 +562,7 @@ def merge_tuples(list1, list2): async def get_entity_type2samples(idxnms, kb_ids: list): - es_res = await trio.to_thread.run_sync(lambda: settings.retrievaler.search({"knowledge_graph_kwd": "ty2ents", "kb_id": kb_ids, "size": 10000, "fields": ["content_with_weight"]}, idxnms, kb_ids)) + es_res = await trio.to_thread.run_sync(lambda: settings.retriever.search({"knowledge_graph_kwd": "ty2ents", "kb_id": kb_ids, "size": 10000, "fields": ["content_with_weight"]}, idxnms, kb_ids)) res = defaultdict(list) for id in es_res.ids: diff --git a/rag/app/tag.py b/rag/app/tag.py index de2ce0fa658..e1a67565245 100644 --- a/rag/app/tag.py +++ b/rag/app/tag.py @@ -133,14 +133,14 @@ def label_question(question, kbs): if tag_kb_ids: all_tags = get_tags_from_cache(tag_kb_ids) if not all_tags: - all_tags = settings.retrievaler.all_tags_in_portion(kb.tenant_id, tag_kb_ids) + all_tags = settings.retriever.all_tags_in_portion(kb.tenant_id, tag_kb_ids) set_tags_to_cache(tags=all_tags, kb_ids=tag_kb_ids) else: all_tags = json.loads(all_tags) tag_kbs = KnowledgebaseService.get_by_ids(tag_kb_ids) if not tag_kbs: return tags - tags = settings.retrievaler.tag_query(question, + tags = settings.retriever.tag_query(question, list(set([kb.tenant_id for kb in tag_kbs])), tag_kb_ids, all_tags, diff --git a/rag/benchmark.py b/rag/benchmark.py index 31a6c92ff30..b7383007364 100644 --- a/rag/benchmark.py +++ b/rag/benchmark.py @@ -52,7 +52,7 @@ def _get_retrieval(self, qrels): run = defaultdict(dict) query_list = list(qrels.keys()) for query in query_list: - ranks = settings.retrievaler.retrieval(query, self.embd_mdl, self.tenant_id, [self.kb.id], 1, 30, + ranks = settings.retriever.retrieval(query, self.embd_mdl, self.tenant_id, [self.kb.id], 1, 30, 0.0, self.vector_similarity_weight) if len(ranks["chunks"]) == 0: print(f"deleted query: {query}") diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index eb03a56b80b..ae10214867e 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -380,7 +380,7 @@ async def doc_question_proposal(chat_mdl, d, topn): examples = [] all_tags = get_tags_from_cache(kb_ids) if not all_tags: - all_tags = settings.retrievaler.all_tags_in_portion(tenant_id, kb_ids, S) + all_tags = settings.retriever.all_tags_in_portion(tenant_id, kb_ids, S) set_tags_to_cache(kb_ids, all_tags) else: all_tags = json.loads(all_tags) @@ -393,7 +393,7 @@ async def doc_question_proposal(chat_mdl, d, topn): if task_canceled: progress_callback(-1, msg="Task has been canceled.") return - if settings.retrievaler.tag_content(tenant_id, kb_ids, d, all_tags, topn_tags=topn_tags, S=S) and len(d[TAG_FLD]) > 0: + if settings.retriever.tag_content(tenant_id, kb_ids, d, all_tags, topn_tags=topn_tags, S=S) and len(d[TAG_FLD]) > 0: examples.append({"content": d["content_with_weight"], TAG_FLD: d[TAG_FLD]}) else: docs_to_tag.append(d) @@ -645,7 +645,7 @@ async def run_raptor_for_kb(row, kb_parser_config, chat_mdl, embd_mdl, vector_si chunks = [] vctr_nm = "q_%d_vec"%vector_size for doc_id in doc_ids: - for d in settings.retrievaler.chunk_list(doc_id, row["tenant_id"], [str(row["kb_id"])], + for d in settings.retriever.chunk_list(doc_id, row["tenant_id"], [str(row["kb_id"])], fields=["content_with_weight", vctr_nm], sort_by_position=True): chunks.append((d["content_with_weight"], np.array(d[vctr_nm]))) From 8aabc2807c64597f6ad1e84ed6a0b3d328e74100 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Fri, 10 Oct 2025 09:39:15 +0800 Subject: [PATCH 0783/1187] Feat: Pipeline Docx file supports Markdown output (#10439) ### What problem does this PR solve? Pipeline Docx file supports Markdown output. image image ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- pyproject.toml | 2 + rag/app/naive.py | 45 ++++++++- rag/flow/parser/parser.py | 13 ++- uv.lock | 202 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 06981148c21..24a5299dde6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -133,6 +133,8 @@ dependencies = [ "litellm>=1.74.15.post1", "flask-mail>=0.10.0", "lark>=1.2.2", + "mammoth>=1.11.0", + "markdownify>=1.2.0", ] [project.optional-dependencies] diff --git a/rag/app/naive.py b/rag/app/naive.py index 9265ae7768a..ca253efe15f 100644 --- a/rag/app/naive.py +++ b/rag/app/naive.py @@ -256,6 +256,49 @@ def __call__(self, filename, binary=None, from_page=0, to_page=100000): tbls.append(((None, html), "")) return new_line, tbls + def to_markdown(self, filename=None, binary=None, inline_images: bool = True): + """ + This function uses mammoth, licensed under the BSD 2-Clause License. + """ + + import base64 + import uuid + + import mammoth + from markdownify import markdownify + + docx_file = BytesIO(binary) if binary else open(filename, "rb") + + def _convert_image_to_base64(image): + try: + with image.open() as image_file: + image_bytes = image_file.read() + encoded = base64.b64encode(image_bytes).decode("utf-8") + base64_url = f"data:{image.content_type};base64,{encoded}" + + alt_name = "image" + alt_name = f"img_{uuid.uuid4().hex[:8]}" + + return {"src": base64_url, "alt": alt_name} + except Exception as e: + logging.warning(f"Failed to convert image to base64: {e}") + return {"src": "", "alt": "image"} + + try: + if inline_images: + result = mammoth.convert_to_html(docx_file, convert_image=mammoth.images.img_element(_convert_image_to_base64)) + else: + result = mammoth.convert_to_html(docx_file) + + html = result.value + + markdown_text = markdownify(html) + return markdown_text + + finally: + if not binary: + docx_file.close() + class Pdf(PdfParser): def __init__(self): @@ -512,7 +555,7 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, callback(0.2, "Visual model detected. Attempting to enhance figure extraction...") except Exception: vision_model = None - + if vision_model: # Process images for each section section_images = [] diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py index f4378855db8..424b2d6fa4a 100644 --- a/rag/flow/parser/parser.py +++ b/rag/flow/parser/parser.py @@ -52,6 +52,7 @@ def __init__(self): ], "word": [ "json", + "markdown", ], "slides": [ "json", @@ -247,13 +248,15 @@ def _word(self, name, blob): conf = self._param.setups["word"] self.set_output("output_format", conf["output_format"]) docx_parser = Docx() - sections, tbls = docx_parser(name, binary=blob) - sections = [{"text": section[0], "image": section[1]} for section in sections if section] - sections.extend([{"text": tb, "image": None} for ((_,tb), _) in tbls]) - # json - assert conf.get("output_format") == "json", "have to be json for doc" + if conf.get("output_format") == "json": + sections, tbls = docx_parser(name, binary=blob) + sections = [{"text": section[0], "image": section[1]} for section in sections if section] + sections.extend([{"text": tb, "image": None} for ((_,tb), _) in tbls]) self.set_output("json", sections) + elif conf.get("output_format") == "markdown": + markdown_text = docx_parser.to_markdown(name, binary=blob) + self.set_output("markdown", markdown_text) def _slides(self, name, blob): from deepdoc.parser.ppt_parser import RAGFlowPptParser as ppt_parser diff --git a/uv.lock b/uv.lock index c0d5a04737b..2571f8f4aa8 100644 --- a/uv.lock +++ b/uv.lock @@ -831,6 +831,15 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/1c/3d/3e04a822b8615904269f7126d8b019ae5c3b5c3c78397ec8bab056b02099/cn2an-0.5.22-py3-none-any.whl", hash = "sha256:cba4c8f305b43da01f50696047cca3116c727424ac62338da6a3426e01454f3e" }, ] +[[package]] +name = "cobble" +version = "0.1.4" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/54/7a/a507c709be2c96e1bb6102eb7b7f4026c5e5e223ef7d745a17d239e9d844/cobble-0.1.4.tar.gz", hash = "sha256:de38be1539992c8a06e569630717c485a5f91be2192c461ea2b220607dfa78aa" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/d5/e1/3714a2f371985215c219c2a70953d38e3eed81ef165aed061d21de0e998b/cobble-0.1.4-py3-none-any.whl", hash = "sha256:36c91b1655e599fd428e2b95fdd5f0da1ca2e9f1abb0bc871dec21a0e78a2b44" }, +] + [[package]] name = "cohere" version = "5.6.2" @@ -861,6 +870,15 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, ] +[[package]] +name = "colorclass" +version = "2.2.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d7/1a/31ff00a33569a3b59d65bbdc445c73e12f92ad28195b7ace299f68b9af70/colorclass-2.2.2.tar.gz", hash = "sha256:6d4fe287766166a98ca7bc6f6312daf04a0481b1eda43e7173484051c0ab4366" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/30/b6/daf3e2976932da4ed3579cff7a30a53d22ea9323ee4f0d8e43be60454897/colorclass-2.2.2-py2.py3-none-any.whl", hash = "sha256:6f10c273a0ef7a1150b1120b6095cbdd68e5cf36dfd5d0fc957a2500bbf99a55" }, +] + [[package]] name = "coloredlogs" version = "15.0.1" @@ -873,6 +891,15 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934" }, ] +[[package]] +name = "compressed-rtf" +version = "1.0.7" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b7/0c/929a4e8ef9d7143f54d77dadb5f370cc7b98534b1bd6e1124d0abe8efb24/compressed_rtf-1.0.7.tar.gz", hash = "sha256:7c30859334839f3cdc7d10796af5b434bb326b9df7cb5a65e95a8eacb2951b0e" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/07/1d/62f5bf92e12335eb63517f42671ed78512d48bbc69e02a942dd7b90f03f0/compressed_rtf-1.0.7-py3-none-any.whl", hash = "sha256:b7904921d78c67a0a4b7fff9fb361a00ae2b447b6edca010ce321cd98fa0fcc0" }, +] + [[package]] name = "contourpy" version = "1.3.2" @@ -1322,6 +1349,23 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/fc/da/8376678b4a9ae0f9418d93df9c9cf851dced49c95ceb38daac6651e38f7a/duckduckgo_search-7.5.5-py3-none-any.whl", hash = "sha256:c71a0661aa436f215d9a05d653af424affb58825ab3e79f3b788053cbdee9ebc" }, ] +[[package]] +name = "easygui" +version = "0.98.3" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cc/ad/e35f7a30272d322be09dc98592d2f55d27cc933a7fde8baccbbeb2bd9409/easygui-0.98.3.tar.gz", hash = "sha256:d653ff79ee1f42f63b5a090f2f98ce02335d86ad8963b3ce2661805cafe99a04" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/8e/a7/b276ff776533b423710a285c8168b52551cb2ab0855443131fdc7fd8c16f/easygui-0.98.3-py2.py3-none-any.whl", hash = "sha256:33498710c68b5376b459cd3fc48d1d1f33822139eb3ed01defbc0528326da3ba" }, +] + +[[package]] +name = "ebcdic" +version = "1.1.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/0d/2f/633031205333bee5f9f93761af8268746aa75f38754823aabb8570eb245b/ebcdic-1.1.1-py2.py3-none-any.whl", hash = "sha256:33b4cb729bc2d0bf46cc1847b0e5946897cb8d3f53520c5b9aa5fa98d7e735f1" }, +] + [[package]] name = "editdistance" version = "0.8.1" @@ -1435,6 +1479,26 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10" }, ] +[[package]] +name = "extract-msg" +version = "0.41.5" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "chardet" }, + { name = "compressed-rtf" }, + { name = "ebcdic" }, + { name = "imapclient" }, + { name = "olefile" }, + { name = "red-black-tree-mod" }, + { name = "rtfde" }, + { name = "tzlocal" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ef/fa/67443d9b9f505c32cba96e34745223378b84cd4795c387310788cc8b6d7d/extract_msg-0.41.5.tar.gz", hash = "sha256:99d4fdc0c0912c836370bf9fbb6e77558bb978499c1b5fdd31634684e323885c" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/be/e2/f0ed8df3907ad6e90e762d8e90adb4e25d12fea851a8371611fa14405782/extract_msg-0.41.5-py2.py3-none-any.whl", hash = "sha256:ad70dcdab3701b0fae554168c9642ad4ebef7f2ec283313c55e895a6518911e5" }, +] + [[package]] name = "fake-http-header" version = "0.3.5" @@ -2575,6 +2639,18 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b" }, ] +[[package]] +name = "imapclient" +version = "2.3.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/19/d8/a4a0337d5e39a0569d89793d5053d7535eefd9b8756df4e10dc114caf3c2/IMAPClient-2.3.1.zip", hash = "sha256:26ea995664fae3a88b878ebce2aff7402931697b86658b7882043ddb01b0e6ba" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/13/9c/b2890e73bc9eee53fe63218e3f3cb774a6beefdb7b5c47928a81cc3b3c13/IMAPClient-2.3.1-py2.py3-none-any.whl", hash = "sha256:057f28025d2987c63e065afb0e4370b0b850b539b0e1494cea0427e88130108c" }, +] + [[package]] name = "importlib-metadata" version = "8.7.0" @@ -2902,6 +2978,15 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c" }, ] +[[package]] +name = "lark-parser" +version = "0.12.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5a/ee/fd1192d7724419ddfe15b6f17d1c8742800d4de917c0adac3b6aaf22e921/lark-parser-0.12.0.tar.gz", hash = "sha256:15967db1f1214013dca65b1180745047b9be457d73da224fcda3d9dd4e96a138" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/76/00/90f05db333fe1aa6b6ffea83a35425b7d53ea95c8bba0b1597f226cf1d5f/lark_parser-0.12.0-py2.py3-none-any.whl", hash = "sha256:0eaf30cb5ba787fe404d73a7d6e61df97b21d5a63ac26c5008c78a494373c675" }, +] + [[package]] name = "litellm" version = "1.75.5.post1" @@ -3069,6 +3154,18 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/6c/e1/0686c91738f3e6c2e1a243e0fdd4371667c4d2e5009b0a3605806c2aa020/lz4-4.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:2f4f2965c98ab254feddf6b5072854a6935adab7bc81412ec4fe238f07b85f62" }, ] +[[package]] +name = "mammoth" +version = "1.11.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "cobble" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ed/3c/a58418d2af00f2da60d4a51e18cd0311307b72d48d2fffec36a97b4a5e44/mammoth-1.11.0.tar.gz", hash = "sha256:a0f59e442f34d5b6447f4b0999306cbf3e67aaabfa8cb516f878fb1456744637" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ca/54/2e39566a131b13f6d8d193f974cb6a34e81bb7cc2fa6f7e03de067b36588/mammoth-1.11.0-py2.py3-none-any.whl", hash = "sha256:c077ab0d450bd7c0c6ecd529a23bf7e0fa8190c929e28998308ff4eada3f063b" }, +] + [[package]] name = "markdown" version = "3.6" @@ -3099,6 +3196,19 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/47/2b/dac4143951a16c0c03e8fe217c9fa784838d02a29c52ef0e8b265befea8f/markdown_to_json-2.1.1-py3-none-any.whl", hash = "sha256:c73b8a3ac7fbde65463dbaeba8bb925d1d54377cbb01a064cd65e1f3e394bd62" }, ] +[[package]] +name = "markdownify" +version = "1.2.0" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "six" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/83/1b/6f2697b51eaca81f08852fd2734745af15718fea10222a1d40f8a239c4ea/markdownify-1.2.0.tar.gz", hash = "sha256:f6c367c54eb24ee953921804dfe6d6575c5e5b42c643955e7242034435de634c" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/6a/e2/7af643acb4cae0741dffffaa7f3f7c9e7ab4046724543ba1777c401d821c/markdownify-1.2.0-py3-none-any.whl", hash = "sha256:48e150a1c4993d4d50f282f725c0111bd9eb25645d41fa2f543708fd44161351" }, +] + [[package]] name = "markupsafe" version = "3.0.2" @@ -3386,6 +3496,19 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/b1/ef/27dd35a7049c9a4f4211c6cd6a8c9db0a50647546f003a5867827ec45391/msgspec-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0" }, ] +[[package]] +name = "msoffcrypto-tool" +version = "5.4.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "olefile" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d2/b7/0fd6573157e0ec60c0c470e732ab3322fba4d2834fd24e1088d670522a01/msoffcrypto_tool-5.4.2.tar.gz", hash = "sha256:44b545adba0407564a0cc3d6dde6ca36b7c0fdf352b85bca51618fa1d4817370" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/03/54/7f6d3d9acad083dae8c22d9ab483b657359a1bf56fee1d7af88794677707/msoffcrypto_tool-5.4.2-py3-none-any.whl", hash = "sha256:274fe2181702d1e5a107ec1b68a4c9fea997a44972ae1cc9ae0cb4f6a50fef0e" }, +] + [[package]] name = "multidict" version = "6.6.3" @@ -3735,6 +3858,29 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1" }, ] +[[package]] +name = "olefile" +version = "0.46" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/34/81/e1ac43c6b45b4c5f8d9352396a14144bba52c8fec72a80f425f6a4d653ad/olefile-0.46.zip", hash = "sha256:133b031eaf8fd2c9399b78b8bc5b8fcbe4c31e85295749bb17a87cba8f3c3964" } + +[[package]] +name = "oletools" +version = "0.60.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "colorclass" }, + { name = "easygui" }, + { name = "msoffcrypto-tool", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32') or (sys_platform != 'darwin' and sys_platform != 'win32')" }, + { name = "olefile" }, + { name = "pcodedmp" }, + { name = "pyparsing" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5c/2f/037f40e44706d542b94a2312ccc33ee2701ebfc9a83b46b55263d49ce55a/oletools-0.60.2.zip", hash = "sha256:ad452099f4695ffd8855113f453348200d195ee9fa341a09e197d66ee7e0b2c3" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ac/ff/05257b7183279b80ecec6333744de23f48f0faeeba46c93e6d13ce835515/oletools-0.60.2-py2.py3-none-any.whl", hash = "sha256:72ad8bd748fd0c4e7b5b4733af770d11543ebb2bf2697455f99f975fcd50cc96" }, +] + [[package]] name = "ollama" version = "0.2.1" @@ -4197,6 +4343,19 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/87/2b/b50d3d08ea0fc419c183a84210571eba005328efa62b6b98bc28e9ead32a/patsy-1.0.1-py2.py3-none-any.whl", hash = "sha256:751fb38f9e97e62312e921a1954b81e1bb2bcda4f5eeabaf94db251ee791509c" }, ] +[[package]] +name = "pcodedmp" +version = "1.2.6" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "oletools" }, + { name = "win-unicode-console", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3d/20/6d461e29135f474408d0d7f95b2456a9ba245560768ee51b788af10f7429/pcodedmp-1.2.6.tar.gz", hash = "sha256:025f8c809a126f45a082ffa820893e6a8d990d9d7ddb68694b5a9f0a6dbcd955" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/ba/72/b380fb5c89d89c3afafac8cf02a71a45f4f4a4f35531ca949a34683962d1/pcodedmp-1.2.6-py2.py3-none-any.whl", hash = "sha256:4441f7c0ab4cbda27bd4668db3b14f36261d86e5059ce06c0828602cbe1c4278" }, +] + [[package]] name = "pdfminer-six" version = "20221105" @@ -5309,6 +5468,7 @@ dependencies = [ { name = "elastic-transport" }, { name = "elasticsearch" }, { name = "elasticsearch-dsl" }, + { name = "extract-msg" }, { name = "filelock" }, { name = "flasgger" }, { name = "flask" }, @@ -5331,8 +5491,10 @@ dependencies = [ { name = "langfuse" }, { name = "lark" }, { name = "litellm" }, + { name = "mammoth" }, { name = "markdown" }, { name = "markdown-to-json" }, + { name = "markdownify" }, { name = "mcp" }, { name = "mini-racer" }, { name = "minio" }, @@ -5462,6 +5624,7 @@ requires-dist = [ { name = "elastic-transport", specifier = "==8.12.0" }, { name = "elasticsearch", specifier = "==8.12.1" }, { name = "elasticsearch-dsl", specifier = "==8.12.0" }, + { name = "extract-msg", specifier = ">=0.39.0" }, { name = "fastembed", marker = "(platform_machine != 'x86_64' and extra == 'full') or (sys_platform == 'darwin' and extra == 'full')", specifier = ">=0.3.6,<0.4.0" }, { name = "fastembed-gpu", marker = "platform_machine == 'x86_64' and sys_platform != 'darwin' and extra == 'full'", specifier = ">=0.3.6,<0.4.0" }, { name = "filelock", specifier = "==3.15.4" }, @@ -5487,8 +5650,10 @@ requires-dist = [ { name = "langfuse", specifier = ">=2.60.0" }, { name = "lark", specifier = ">=1.2.2" }, { name = "litellm", specifier = ">=1.74.15.post1" }, + { name = "mammoth", specifier = ">=1.11.0" }, { name = "markdown", specifier = "==3.6" }, { name = "markdown-to-json", specifier = "==2.1.1" }, + { name = "markdownify", specifier = ">=1.2.0" }, { name = "mcp", specifier = ">=1.9.4" }, { name = "mini-racer", specifier = ">=0.12.4,<0.13.0" }, { name = "minio", specifier = "==7.2.4" }, @@ -5641,6 +5806,12 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/c2/5a/2f2e7fc026d5e64b5408aa3fbe0296a6407b8481196cae4daacacb3a3ae0/readerwriterlock-1.0.9-py3-none-any.whl", hash = "sha256:8c4b704e60d15991462081a27ef46762fea49b478aa4426644f2146754759ca7" }, ] +[[package]] +name = "red-black-tree-mod" +version = "1.20" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/34/12/944f61bc67a1e918953741c0b3b75a28f96d8060d08fd3614233309ced3b/red-black-tree-mod-1.20.tar.gz", hash = "sha256:2448e6fc9cbf1be204c753f352c6ee49aa8156dbf1faa57dfc26bd7705077e0a" } + [[package]] name = "referencing" version = "0.36.2" @@ -5894,6 +6065,19 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762" }, ] +[[package]] +name = "rtfde" +version = "0.0.2" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "lark-parser" }, + { name = "oletools" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/81/ea/28f5ab6b46a072887c8c8fd8c8a1f7b54025fc4bb2e09024668ea6686044/RTFDE-0.0.2.tar.gz", hash = "sha256:b86b5d734950fe8745a5b89133f50554252dbd67c6d1b9265e23ee140e7ea8a2" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/5d/3f/39ba5a72620c43656bc80cb1f7afe0d498df4a48947d75ea0ca0752ffbf4/RTFDE-0.0.2-py3-none-any.whl", hash = "sha256:18386e4f060cee12a2a8035b0acf0cc99689f5dff1bf347bab7e92351860a21d" }, +] + [[package]] name = "ruamel-base" version = "1.0.0" @@ -6901,6 +7085,18 @@ wheels = [ { url = "https://mirrors.aliyun.com/pypi/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8" }, ] +[[package]] +name = "tzlocal" +version = "5.3.1" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d" }, +] + [[package]] name = "umap-learn" version = "0.5.6" @@ -7145,6 +7341,12 @@ dependencies = [ ] sdist = { url = "https://mirrors.aliyun.com/pypi/packages/67/35/25e68fbc99e672127cc6fbb14b8ec1ba3dfef035bf1e4c90f78f24a80b7d/wikipedia-1.4.0.tar.gz", hash = "sha256:db0fad1829fdd441b1852306e9856398204dc0786d2996dd2e0c8bb8e26133b2" } +[[package]] +name = "win-unicode-console" +version = "0.5" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/89/8d/7aad74930380c8972ab282304a2ff45f3d4927108bb6693cabcc9fc6a099/win_unicode_console-0.5.zip", hash = "sha256:d4142d4d56d46f449d6f00536a73625a871cba040f0bc1a2e305a04578f07d1e" } + [[package]] name = "win32-setctime" version = "1.2.0" From f631073ac2d087098968c512eebc0c076f5500ad Mon Sep 17 00:00:00 2001 From: XIANG LI <33214035+loks666@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:03:12 +0800 Subject: [PATCH 0784/1187] Fix OCR GPU provider mem limit handling (#10407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? - Running DeepDoc OCR on large PDFs inside the GPU docker-compose setup would intermittently fail with [ONNXRuntimeError] ... p2o.Clip.6 ... Available memory of 0 is smaller than requested bytes ... - Root cause: load_model() in deepdoc/vision/ocr.py treated device_id=None as-is. torch.cuda.device_count() > device_id then raised a TypeError, the helper returned False, and ONNXRuntime quietly fell back to CPUExecutionProvider with the hard-coded 512 MB limit, which then triggered the allocator failure. - Environment where this reproduces: Windows 11, AMD 5900x, 64 GB RAM, RTX 3090 (24 GB), docker-compose-gpu.yml from upstream, default DeepDoc + GraphRAG parser settings, ingesting heavy PDF such as 《内科学》(第10版).pdf (~180 MB). Fixes: - Normalize device_id to 0 when it is None before calling any CUDA APIs, so the GPU path is considered available. - Allow configuring the CUDA provider’s memory cap via OCR_GPU_MEM_LIMIT_MB (default 2048 MB) and expose OCR_ARENA_EXTEND_STRATEGY; the calculated byte limit is logged to confirm the effective settings. After the change, ragflow_server.log shows for example load_model ... uses GPU (device 0, gpu_mem_limit=21474836480, arena_strategy=kNextPowerOfTwo) and the same document finishes OCR without allocator errors. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- deepdoc/vision/ocr.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/deepdoc/vision/ocr.py b/deepdoc/vision/ocr.py index d91de2ab845..9b6e8fdf6ce 100644 --- a/deepdoc/vision/ocr.py +++ b/deepdoc/vision/ocr.py @@ -84,7 +84,8 @@ def load_model(model_dir, nm, device_id: int | None = None): def cuda_is_available(): try: import torch - if torch.cuda.is_available() and torch.cuda.device_count() > device_id: + target_id = 0 if device_id is None else device_id + if torch.cuda.is_available() and torch.cuda.device_count() > target_id: return True except Exception: return False @@ -100,10 +101,13 @@ def cuda_is_available(): # Shrink GPU memory after execution run_options = ort.RunOptions() if cuda_is_available(): + gpu_mem_limit_mb = int(os.environ.get("OCR_GPU_MEM_LIMIT_MB", "2048")) + arena_strategy = os.environ.get("OCR_ARENA_EXTEND_STRATEGY", "kNextPowerOfTwo") + provider_device_id = 0 if device_id is None else device_id cuda_provider_options = { - "device_id": device_id, # Use specific GPU - "gpu_mem_limit": 512 * 1024 * 1024, # Limit gpu memory - "arena_extend_strategy": "kNextPowerOfTwo", # gpu memory allocation strategy + "device_id": provider_device_id, # Use specific GPU + "gpu_mem_limit": max(gpu_mem_limit_mb, 0) * 1024 * 1024, + "arena_extend_strategy": arena_strategy, # gpu memory allocation strategy } sess = ort.InferenceSession( model_file_path, @@ -111,8 +115,8 @@ def cuda_is_available(): providers=['CUDAExecutionProvider'], provider_options=[cuda_provider_options] ) - run_options.add_run_config_entry("memory.enable_memory_arena_shrinkage", "gpu:" + str(device_id)) - logging.info(f"load_model {model_file_path} uses GPU") + run_options.add_run_config_entry("memory.enable_memory_arena_shrinkage", "gpu:" + str(provider_device_id)) + logging.info(f"load_model {model_file_path} uses GPU (device {provider_device_id}, gpu_mem_limit={cuda_provider_options['gpu_mem_limit']}, arena_strategy={arena_strategy})") else: sess = ort.InferenceSession( model_file_path, From 6ab4c1a6e9008f4598e4fcfbbb83593c7d5fda08 Mon Sep 17 00:00:00 2001 From: Stephen Hu <812791840@qq.com> Date: Fri, 10 Oct 2025 11:03:40 +0800 Subject: [PATCH 0785/1187] Refactor: improve how NvidiaCV calculate res total token counts (#10455) ### What problem does this PR solve? improve how NvidiaCV calculate res total token counts ### Type of change - [x] Refactoring --- rag/llm/cv_model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index 7e763641ac7..55f01021ad3 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -614,7 +614,7 @@ def describe(self, image): response = response.json() return ( response["choices"][0]["message"]["content"].strip(), - response["usage"]["total_tokens"], + total_token_count_from_response(response), ) def _request(self, msg, gen_conf={}): @@ -637,7 +637,7 @@ def describe_with_prompt(self, image, prompt=None): response = self._request(vision_prompt) return ( response["choices"][0]["message"]["content"].strip(), - response["usage"]["total_tokens"], + total_token_count_from_response(response) ) def chat(self, system, history, gen_conf, images=[], **kwargs): @@ -645,7 +645,7 @@ def chat(self, system, history, gen_conf, images=[], **kwargs): response = self._request(self._form_history(system, history, images), gen_conf) return ( response["choices"][0]["message"]["content"].strip(), - response["usage"]["total_tokens"], + total_token_count_from_response(response) ) except Exception as e: return "**ERROR**: " + str(e), 0 @@ -656,7 +656,7 @@ def chat_streamly(self, system, history, gen_conf, images=[], **kwargs): response = self._request(self._form_history(system, history, images), gen_conf) cnt = response["choices"][0]["message"]["content"] if "usage" in response and "total_tokens" in response["usage"]: - total_tokens += response["usage"]["total_tokens"] + total_tokens += total_token_count_from_response(response) for resp in cnt: yield resp except Exception as e: From 9b06734cedcae3ba54be3612f6f7cd0d560e28f3 Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Fri, 10 Oct 2025 11:20:55 +0800 Subject: [PATCH 0786/1187] Feat: add total in List dataset API (#10448) ### What problem does this PR solve? Feat: add total in List dataset API, solved #10360 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/apps/sdk/dataset.py | 4 ++-- api/db/services/knowledgebase_service.py | 4 +++- api/utils/api_utils.py | 25 +++++++++++++++++------- docs/references/http_api_reference.md | 5 +++-- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/api/apps/sdk/dataset.py b/api/apps/sdk/dataset.py index ff446055c0f..5a43b6835c2 100644 --- a/api/apps/sdk/dataset.py +++ b/api/apps/sdk/dataset.py @@ -461,7 +461,7 @@ def list_datasets(tenant_id): return get_error_permission_result(message=f"User '{tenant_id}' lacks permission for dataset '{name}'") tenants = TenantService.get_joined_tenants_by_user_id(tenant_id) - kbs = KnowledgebaseService.get_list( + kbs, total = KnowledgebaseService.get_list( [m["tenant_id"] for m in tenants], tenant_id, args["page"], @@ -475,7 +475,7 @@ def list_datasets(tenant_id): response_data_list = [] for kb in kbs: response_data_list.append(remap_dictionary_keys(kb)) - return get_result(data=response_data_list) + return get_result(data=response_data_list, total=total) except OperationalError as e: logging.exception(e) return get_error_data_result(message="Database operation failed") diff --git a/api/db/services/knowledgebase_service.py b/api/db/services/knowledgebase_service.py index 492c24510ef..42d8389fa8b 100644 --- a/api/db/services/knowledgebase_service.py +++ b/api/db/services/knowledgebase_service.py @@ -379,6 +379,7 @@ def get_list(cls, joined_tenant_ids, user_id, # name: Optional name filter # Returns: # List of knowledge bases + # Total count of knowledge bases kbs = cls.model.select() if id: kbs = kbs.where(cls.model.id == id) @@ -390,6 +391,7 @@ def get_list(cls, joined_tenant_ids, user_id, cls.model.tenant_id == user_id)) & (cls.model.status == StatusEnum.VALID.value) ) + if desc: kbs = kbs.order_by(cls.model.getter_by(orderby).desc()) else: @@ -397,7 +399,7 @@ def get_list(cls, joined_tenant_ids, user_id, kbs = kbs.paginate(page_number, items_per_page) - return list(kbs.dicts()) + return list(kbs.dicts()), kbs.count() @classmethod @DB.connection_context() diff --git a/api/utils/api_utils.py b/api/utils/api_utils.py index c9e7ae455a7..8730dff4db4 100644 --- a/api/utils/api_utils.py +++ b/api/utils/api_utils.py @@ -362,16 +362,27 @@ def decorated_function(*args, **kwargs): return decorated_function -def get_result(code: settings.RetCode = settings.RetCode.SUCCESS, message="", data=None): - if code == 0: +def get_result(code=settings.RetCode.SUCCESS, message="", data=None, total=None): + """ + Standard API response format: + { + "code": 0, + "data": [...], # List or object, backward compatible + "total": 47, # Optional field for pagination + "message": "..." # Error or status message + } + """ + response = {"code": code} + + if code == settings.RetCode.SUCCESS: if data is not None: - response = {"code": code, "data": data} - else: - response = {"code": code} + response["data"] = data + if total is not None: + response["total_datasets"] = total else: - response = {"code": code, "message": message} - return jsonify(response) + response["message"] = message or "Error" + return jsonify(response) def get_error_data_result( message="Sorry! Data missing!", diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index 062c942d7c7..12d20b2967e 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -830,7 +830,8 @@ Success: "update_time": 1728533243536, "vector_similarity_weight": 0.3 } - ] + ], + "total": 1 } ``` @@ -1280,7 +1281,7 @@ Success: "update_time": 1728897061948 } ], - "total": 1 + "total_datasets": 1 } } ``` From c802a6ffdd3059c7e475497ab56e62c11daf1c18 Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Fri, 10 Oct 2025 11:44:46 +0800 Subject: [PATCH 0787/1187] Feat: Add prompts for toc relevance check according to #10436 (#10457) ### What problem does this PR solve? Feat: Add prompts for toc relevance check according to #10436 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- rag/prompts/toc_relevance_system.md | 118 ++++++++++++++++++++++++++++ rag/prompts/toc_relevance_user.md | 17 ++++ 2 files changed, 135 insertions(+) create mode 100644 rag/prompts/toc_relevance_system.md create mode 100644 rag/prompts/toc_relevance_user.md diff --git a/rag/prompts/toc_relevance_system.md b/rag/prompts/toc_relevance_system.md new file mode 100644 index 00000000000..287b50284e4 --- /dev/null +++ b/rag/prompts/toc_relevance_system.md @@ -0,0 +1,118 @@ +# System Prompt: TOC Relevance Evaluation + +You are an expert logical reasoning assistant specializing in hierarchical Table of Contents (TOC) relevance evaluation. + +## GOAL +You will receive: +1. A JSON list of TOC items, each with fields: + ```json + { + "level": , // e.g., 1, 2, 3 + "title": // section title + } + ``` +2. A user query (natural language question). + +You must assign a **relevance score** (integer) to every TOC entry, based on how related its `title` is to the `query`. + +--- + +## RULES + +### Scoring System +- 5 → highly relevant (directly answers or matches the query intent) +- 3 → somewhat related (same topic or partially overlaps) +- 1 → weakly related (vague or tangential) +- 0 → no clear relation +- -1 → explicitly irrelevant or contradictory + +### Hierarchy Traversal +- The TOC is hierarchical: smaller `level` = higher layer (e.g., level 1 is top-level, level 2 is a subsection). +- You must traverse in **hierarchical order** — interpret the structure based on levels (1 > 2 > 3). +- If a high-level item (level 1) is strongly related (score 5), its child items (level 2, 3) are likely relevant too. +- If a high-level item is unrelated (-1 or 0), its deeper children are usually less relevant unless the titles clearly match the query. +- Lower (deeper) levels provide more specific content; prefer assigning higher scores if they directly match the query. + +### Output Format +Return a **JSON array**, preserving the input order but adding a new key `"score"`: + +```json +[ + {"level": 1, "title": "Introduction", "score": 0}, + {"level": 2, "title": "Definition of Sustainability", "score": 5} +] +``` + +### Constraints +- Output **only the JSON array** — no explanations or reasoning text. + +### EXAMPLES + +#### Example 1 +Input TOC: +[ + {"level": 1, "title": "Machine Learning Overview"}, + {"level": 2, "title": "Supervised Learning"}, + {"level": 2, "title": "Unsupervised Learning"}, + {"level": 3, "title": "Applications of Deep Learning"} +] + +Query: +"How is deep learning used in image classification?" + +Output: +[ + {"level": 1, "title": "Machine Learning Overview", "score": 3}, + {"level": 2, "title": "Supervised Learning", "score": 3}, + {"level": 2, "title": "Unsupervised Learning", "score": 0}, + {"level": 3, "title": "Applications of Deep Learning", "score": 5} +] + +--- + +#### Example 2 +Input TOC: +[ + {"level": 1, "title": "Marketing Basics"}, + {"level": 2, "title": "Consumer Behavior"}, + {"level": 2, "title": "Digital Marketing"}, + {"level": 3, "title": "Social Media Campaigns"}, + {"level": 3, "title": "SEO Optimization"} +] + +Query: +"What are the best online marketing methods?" + +Output: +[ + {"level": 1, "title": "Marketing Basics", "score": 3}, + {"level": 2, "title": "Consumer Behavior", "score": 1}, + {"level": 2, "title": "Digital Marketing", "score": 5}, + {"level": 3, "title": "Social Media Campaigns", "score": 5}, + {"level": 3, "title": "SEO Optimization", "score": 5} +] + +--- + +#### Example 3 +Input TOC: +[ + {"level": 1, "title": "Physics Overview"}, + {"level": 2, "title": "Classical Mechanics"}, + {"level": 3, "title": "Newton’s Laws"}, + {"level": 2, "title": "Thermodynamics"}, + {"level": 3, "title": "Entropy and Heat Transfer"} +] + +Query: +"What is entropy?" + +Output: +[ + {"level": 1, "title": "Physics Overview", "score": 3}, + {"level": 2, "title": "Classical Mechanics", "score": 0}, + {"level": 3, "title": "Newton’s Laws", "score": -1}, + {"level": 2, "title": "Thermodynamics", "score": 5}, + {"level": 3, "title": "Entropy and Heat Transfer", "score": 5} +] + diff --git a/rag/prompts/toc_relevance_user.md b/rag/prompts/toc_relevance_user.md new file mode 100644 index 00000000000..2a5167ad7b2 --- /dev/null +++ b/rag/prompts/toc_relevance_user.md @@ -0,0 +1,17 @@ +# User Prompt: TOC Relevance Evaluation + +You will now receive: +1. A JSON list of TOC items (each with `level` and `title`) +2. A user query string. + +Traverse the TOC hierarchically based on level numbers and assign scores (5,3,1,0,-1) according to the rules in the system prompt. +Output **only** the JSON array with the added `"score"` field. + +--- + +**Input TOC:** +{{ toc_json }} + +**Query:** +{{ query }} + From 8252b1c5c0b1eb0e9e1eec4dfdbaf5bec3632773 Mon Sep 17 00:00:00 2001 From: Zhichang Yu Date: Fri, 10 Oct 2025 12:41:45 +0800 Subject: [PATCH 0788/1187] bump infinity (#10422) ### What problem does this PR solve? bump infinity to v0.6.0-dev7 Needs https://github.com/infiniflow/infinity/pull/3016 ### Type of change - [x] Other (please describe): Infinity --- docker/docker-compose-base.yml | 2 +- helm/values.yaml | 2 +- pyproject.toml | 4 +- uv.lock | 6959 ++++++++++++++++---------------- 4 files changed, 3505 insertions(+), 3462 deletions(-) diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index 7ded0b10c05..44832fdfc7d 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -77,7 +77,7 @@ services: container_name: ragflow-infinity profiles: - infinity - image: infiniflow/infinity:v0.6.0-dev5 + image: infiniflow/infinity:v0.6.0-dev7 volumes: - infinity_data:/var/infinity - ./infinity_conf.toml:/infinity_conf.toml diff --git a/helm/values.yaml b/helm/values.yaml index ba4c6128073..1456e81e286 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -96,7 +96,7 @@ ragflow: infinity: image: repository: infiniflow/infinity - tag: v0.6.0-dev5 + tag: v0.6.0-dev7 pullPolicy: IfNotPresent pullSecrets: [] storage: diff --git a/pyproject.toml b/pyproject.toml index 24a5299dde6..2133a0590dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "html-text==0.6.2", "httpx[socks]==0.27.2", "huggingface-hub>=0.25.0,<0.26.0", - "infinity-sdk==0.6.0.dev5", + "infinity-sdk==0.6.0.dev7", "infinity-emb>=0.0.66,<0.0.67", "itsdangerous==2.1.2", "json-repair==0.35.0", @@ -161,7 +161,7 @@ test = [ ] [[tool.uv.index]] -url = "https://mirrors.aliyun.com/pypi/simple" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" [tool.setuptools] packages = [ diff --git a/uv.lock b/uv.lock index 2571f8f4aa8..c1e8243f342 100644 --- a/uv.lock +++ b/uv.lock @@ -16,7 +16,7 @@ resolution-markers = [ [[package]] name = "accelerate" version = "1.9.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "huggingface-hub" }, { name = "numpy" }, @@ -26,33 +26,33 @@ dependencies = [ { name = "safetensors" }, { name = "torch" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c4/25/969456a95a90ed38f73f68d0f0915bdf1d76145d05054c59ad587b171150/accelerate-1.9.0.tar.gz", hash = "sha256:0e8c61f81af7bf37195b6175a545ed292617dd90563c88f49020aea5b6a0b47f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/25/969456a95a90ed38f73f68d0f0915bdf1d76145d05054c59ad587b171150/accelerate-1.9.0.tar.gz", hash = "sha256:0e8c61f81af7bf37195b6175a545ed292617dd90563c88f49020aea5b6a0b47f", size = 383234, upload-time = "2025-07-16T16:24:54.526Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9f/1c/a17fb513aeb684fb83bef5f395910f53103ab30308bbdd77fd66d6698c46/accelerate-1.9.0-py3-none-any.whl", hash = "sha256:c24739a97ade1d54af4549a65f8b6b046adc87e2b3e4d6c66516e32c53d5a8f1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/1c/a17fb513aeb684fb83bef5f395910f53103ab30308bbdd77fd66d6698c46/accelerate-1.9.0-py3-none-any.whl", hash = "sha256:c24739a97ade1d54af4549a65f8b6b046adc87e2b3e4d6c66516e32c53d5a8f1", size = 367073, upload-time = "2025-07-16T16:24:52.957Z" }, ] [[package]] name = "aiofiles" version = "24.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247, upload-time = "2024-06-24T11:02:03.584Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" }, ] [[package]] name = "aiohappyeyeballs" version = "2.6.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] [[package]] name = "aiohttp" version = "3.12.15" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiohappyeyeballs" }, { name = "aiosignal" }, @@ -63,108 +63,108 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/47/dc/ef9394bde9080128ad401ac7ede185267ed637df03b51f05d14d1c99ad67/aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/42/63fccfc3a7ed97eb6e1a71722396f409c46b60a0552d8a56d7aad74e0df5/aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9c/a2/7b8a020549f66ea2a68129db6960a762d2393248f1994499f8ba9728bbed/aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/f5/d11e088da9176e2ad8220338ae0000ed5429a15f3c9dfd983f39105399cd/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/6b/b60ce2757e2faed3d70ed45dafee48cee7bfb878785a9423f7e883f0639c/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/de/8c9fde2072a1b72c4fadecf4f7d4be7a85b1d9a4ab333d8245694057b4c6/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/ad/07f863ca3d895a1ad958a54006c6dafb4f9310f8c2fdb5f961b8529029d3/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/43/2bd482ebe2b126533e8755a49b128ec4e58f1a3af56879a3abdb7b42c54f/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/23/40/2fa9f514c4cf4cbae8d7911927f81a1901838baf5e09a8b2c299de1acfe5/aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b8/c3/94dc7357bc421f4fb978ca72a201a6c604ee90148f1181790c129396ceeb/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/3f/1f8911fe1844a07001e26593b5c255a685318943864b27b4e0267e840f95/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4e/46/27bf57a99168c4e145ffee6b63d0458b9c66e58bb70687c23ad3d2f0bd17/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/7e/1d2d9061a574584bb4ad3dbdba0da90a27fdc795bc227def3a46186a8bc1/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/98/bee429b52233c4a391980a5b3b196b060872a13eadd41c3a34be9b1469ed/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/39/b0314c1ea774df3392751b686104a3938c63ece2b7ce0ba1ed7c0b4a934f/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/83/3dacb8d3f8f512c8ca43e3fa8a68b20583bd25636ffa4e56ee841ffd79ae/aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/f9/470b5daba04d558c9673ca2034f28d067f3202a40e17804425f0c331c89f/aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/19/9e86722ec8e835959bd97ce8c1efa78cf361fa4531fca372551abcc9cdd6/aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/f9/0a31fcb1a7d4629ac9d8f01f1cb9242e2f9943f47f5d03215af91c3c1a26/aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/6c/94846f576f1d11df0c2e41d3001000527c0fdf63fce7e69b3927a731325d/aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/6c/f766d0aaafcee0447fad0328da780d344489c042e25cd58fde566bf40aed/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/e5/fb779a05ba6ff44d7bc1e9d24c644e876bfff5abe5454f7b854cace1b9cc/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/4e/a22e799c2035f5d6a4ad2cf8e7c1d1bd0923192871dd6e367dafb158b14c/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/e5/55a33b991f6433569babb56018b2fb8fb9146424f8b3a0c8ecca80556762/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/82/1ddf0ea4f2f3afe79dffed5e8a246737cff6cbe781887a6a170299e33204/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/96/784c785674117b4cb3877522a177ba1b5e4db9ce0fd519430b5de76eec90/aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/12/8a/8b75f203ea7e5c21c0920d84dd24a5c0e971fe1e9b9ebbf29ae7e8e39790/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/0b/a1451543475bb6b86a5cfc27861e52b14085ae232896a2654ff1231c0992/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/fd/793a23a197cc2f0d29188805cfc93aa613407f07e5f9da5cd1366afd9d7c/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ca/bf/23a335a6670b5f5dfc6d268328e55a22651b440fca341a64fccf1eada0c6/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/4f/ed60a591839a9d85d40694aba5cef86dde9ee51ce6cca0bb30d6eb1581e7/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/e0/444747a9455c5de188c0f4a0173ee701e2e325d4b2550e9af84abb20cdba/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/ab/1006278d1ffd13a698e5dd4bfa01e5878f6bddefc296c8b62649753ff249/aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/10/97/ad2b18700708452400278039272032170246a1bf8ec5d832772372c71f1a/aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545" }, - { url = "https://mirrors.aliyun.com/pypi/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716, upload-time = "2025-07-29T05:52:32.215Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/dc/ef9394bde9080128ad401ac7ede185267ed637df03b51f05d14d1c99ad67/aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc", size = 703921, upload-time = "2025-07-29T05:49:43.584Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/42/63fccfc3a7ed97eb6e1a71722396f409c46b60a0552d8a56d7aad74e0df5/aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af", size = 480288, upload-time = "2025-07-29T05:49:47.851Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/a2/7b8a020549f66ea2a68129db6960a762d2393248f1994499f8ba9728bbed/aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421", size = 468063, upload-time = "2025-07-29T05:49:49.789Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/f5/d11e088da9176e2ad8220338ae0000ed5429a15f3c9dfd983f39105399cd/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79", size = 1650122, upload-time = "2025-07-29T05:49:51.874Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/6b/b60ce2757e2faed3d70ed45dafee48cee7bfb878785a9423f7e883f0639c/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77", size = 1624176, upload-time = "2025-07-29T05:49:53.805Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/de/8c9fde2072a1b72c4fadecf4f7d4be7a85b1d9a4ab333d8245694057b4c6/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c", size = 1696583, upload-time = "2025-07-29T05:49:55.338Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/ad/07f863ca3d895a1ad958a54006c6dafb4f9310f8c2fdb5f961b8529029d3/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4", size = 1738896, upload-time = "2025-07-29T05:49:57.045Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/43/2bd482ebe2b126533e8755a49b128ec4e58f1a3af56879a3abdb7b42c54f/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6", size = 1643561, upload-time = "2025-07-29T05:49:58.762Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/40/2fa9f514c4cf4cbae8d7911927f81a1901838baf5e09a8b2c299de1acfe5/aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2", size = 1583685, upload-time = "2025-07-29T05:50:00.375Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/c3/94dc7357bc421f4fb978ca72a201a6c604ee90148f1181790c129396ceeb/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d", size = 1627533, upload-time = "2025-07-29T05:50:02.306Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/3f/1f8911fe1844a07001e26593b5c255a685318943864b27b4e0267e840f95/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb", size = 1638319, upload-time = "2025-07-29T05:50:04.282Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/46/27bf57a99168c4e145ffee6b63d0458b9c66e58bb70687c23ad3d2f0bd17/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5", size = 1613776, upload-time = "2025-07-29T05:50:05.863Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/7e/1d2d9061a574584bb4ad3dbdba0da90a27fdc795bc227def3a46186a8bc1/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b", size = 1693359, upload-time = "2025-07-29T05:50:07.563Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/98/bee429b52233c4a391980a5b3b196b060872a13eadd41c3a34be9b1469ed/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065", size = 1716598, upload-time = "2025-07-29T05:50:09.33Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/39/b0314c1ea774df3392751b686104a3938c63ece2b7ce0ba1ed7c0b4a934f/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1", size = 1644940, upload-time = "2025-07-29T05:50:11.334Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/83/3dacb8d3f8f512c8ca43e3fa8a68b20583bd25636ffa4e56ee841ffd79ae/aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a", size = 429239, upload-time = "2025-07-29T05:50:12.803Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/f9/470b5daba04d558c9673ca2034f28d067f3202a40e17804425f0c331c89f/aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830", size = 452297, upload-time = "2025-07-29T05:50:14.266Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/19/9e86722ec8e835959bd97ce8c1efa78cf361fa4531fca372551abcc9cdd6/aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117", size = 711246, upload-time = "2025-07-29T05:50:15.937Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/f9/0a31fcb1a7d4629ac9d8f01f1cb9242e2f9943f47f5d03215af91c3c1a26/aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe", size = 483515, upload-time = "2025-07-29T05:50:17.442Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/6c/94846f576f1d11df0c2e41d3001000527c0fdf63fce7e69b3927a731325d/aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9", size = 471776, upload-time = "2025-07-29T05:50:19.568Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/6c/f766d0aaafcee0447fad0328da780d344489c042e25cd58fde566bf40aed/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5", size = 1741977, upload-time = "2025-07-29T05:50:21.665Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/e5/fb779a05ba6ff44d7bc1e9d24c644e876bfff5abe5454f7b854cace1b9cc/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728", size = 1690645, upload-time = "2025-07-29T05:50:23.333Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/4e/a22e799c2035f5d6a4ad2cf8e7c1d1bd0923192871dd6e367dafb158b14c/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16", size = 1789437, upload-time = "2025-07-29T05:50:25.007Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/e5/55a33b991f6433569babb56018b2fb8fb9146424f8b3a0c8ecca80556762/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0", size = 1828482, upload-time = "2025-07-29T05:50:26.693Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/82/1ddf0ea4f2f3afe79dffed5e8a246737cff6cbe781887a6a170299e33204/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b", size = 1730944, upload-time = "2025-07-29T05:50:28.382Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/96/784c785674117b4cb3877522a177ba1b5e4db9ce0fd519430b5de76eec90/aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd", size = 1668020, upload-time = "2025-07-29T05:50:30.032Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/8a/8b75f203ea7e5c21c0920d84dd24a5c0e971fe1e9b9ebbf29ae7e8e39790/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8", size = 1716292, upload-time = "2025-07-29T05:50:31.983Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/0b/a1451543475bb6b86a5cfc27861e52b14085ae232896a2654ff1231c0992/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50", size = 1711451, upload-time = "2025-07-29T05:50:33.989Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/fd/793a23a197cc2f0d29188805cfc93aa613407f07e5f9da5cd1366afd9d7c/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676", size = 1691634, upload-time = "2025-07-29T05:50:35.846Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/bf/23a335a6670b5f5dfc6d268328e55a22651b440fca341a64fccf1eada0c6/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7", size = 1785238, upload-time = "2025-07-29T05:50:37.597Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/4f/ed60a591839a9d85d40694aba5cef86dde9ee51ce6cca0bb30d6eb1581e7/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7", size = 1805701, upload-time = "2025-07-29T05:50:39.591Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/e0/444747a9455c5de188c0f4a0173ee701e2e325d4b2550e9af84abb20cdba/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685", size = 1718758, upload-time = "2025-07-29T05:50:41.292Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/ab/1006278d1ffd13a698e5dd4bfa01e5878f6bddefc296c8b62649753ff249/aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b", size = 428868, upload-time = "2025-07-29T05:50:43.063Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/97/ad2b18700708452400278039272032170246a1bf8ec5d832772372c71f1a/aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d", size = 453273, upload-time = "2025-07-29T05:50:44.613Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333, upload-time = "2025-07-29T05:50:46.507Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948, upload-time = "2025-07-29T05:50:48.067Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787, upload-time = "2025-07-29T05:50:49.669Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590, upload-time = "2025-07-29T05:50:51.368Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241, upload-time = "2025-07-29T05:50:53.628Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335, upload-time = "2025-07-29T05:50:55.394Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491, upload-time = "2025-07-29T05:50:57.202Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929, upload-time = "2025-07-29T05:50:59.192Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733, upload-time = "2025-07-29T05:51:01.394Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790, upload-time = "2025-07-29T05:51:03.657Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245, upload-time = "2025-07-29T05:51:05.911Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899, upload-time = "2025-07-29T05:51:07.753Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459, upload-time = "2025-07-29T05:51:09.56Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434, upload-time = "2025-07-29T05:51:11.423Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045, upload-time = "2025-07-29T05:51:13.689Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591, upload-time = "2025-07-29T05:51:15.452Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266, upload-time = "2025-07-29T05:51:17.239Z" }, ] [[package]] name = "aiolimiter" version = "1.2.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f1/23/b52debf471f7a1e42e362d959a3982bdcb4fe13a5d46e63d28868807a79c/aiolimiter-1.2.1.tar.gz", hash = "sha256:e02a37ea1a855d9e832252a105420ad4d15011505512a1a1d814647451b5cca9" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/23/b52debf471f7a1e42e362d959a3982bdcb4fe13a5d46e63d28868807a79c/aiolimiter-1.2.1.tar.gz", hash = "sha256:e02a37ea1a855d9e832252a105420ad4d15011505512a1a1d814647451b5cca9", size = 7185, upload-time = "2024-12-08T15:31:51.496Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f3/ba/df6e8e1045aebc4778d19b8a3a9bc1808adb1619ba94ca354d9ba17d86c3/aiolimiter-1.2.1-py3-none-any.whl", hash = "sha256:d3f249e9059a20badcb56b61601a83556133655c11d1eb3dd3e04ff069e5f3c7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/ba/df6e8e1045aebc4778d19b8a3a9bc1808adb1619ba94ca354d9ba17d86c3/aiolimiter-1.2.1-py3-none-any.whl", hash = "sha256:d3f249e9059a20badcb56b61601a83556133655c11d1eb3dd3e04ff069e5f3c7", size = 6711, upload-time = "2024-12-08T15:31:49.874Z" }, ] [[package]] name = "aiosignal" version = "1.4.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "frozenlist" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] [[package]] name = "aiosqlite" version = "0.20.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0d/3a/22ff5415bf4d296c1e92b07fd746ad42c96781f13295a074d58e77747848/aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/3a/22ff5415bf4d296c1e92b07fd746ad42c96781f13295a074d58e77747848/aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7", size = 21691, upload-time = "2024-02-20T06:12:53.915Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/00/c4/c93eb22025a2de6b83263dfe3d7df2e19138e345bca6f18dba7394120930/aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/c4/c93eb22025a2de6b83263dfe3d7df2e19138e345bca6f18dba7394120930/aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6", size = 15564, upload-time = "2024-02-20T06:12:50.657Z" }, ] [[package]] name = "akracer" version = "0.0.13" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b7/76/424358add4fb060ab24c7a4d6e90c5c05cc1871cf27b6afc3f39ff5774fe/akracer-0.0.13.tar.gz", hash = "sha256:8ab67dabda34f38604d037f2cac67078d253d8c4c316ffe0d80d27ed03cdbb5e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/76/424358add4fb060ab24c7a4d6e90c5c05cc1871cf27b6afc3f39ff5774fe/akracer-0.0.13.tar.gz", hash = "sha256:8ab67dabda34f38604d037f2cac67078d253d8c4c316ffe0d80d27ed03cdbb5e", size = 10047445, upload-time = "2023-10-25T08:02:31.084Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/15/86/6ef05f0b51a36dbec2b260da7a93ed0096dea32e708e127c5051b875af2d/akracer-0.0.13-py3-none-any.whl", hash = "sha256:55bd04c69e35130994d26795f00183e0c33d4e237f7ebfa35074a760c30209d1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/86/6ef05f0b51a36dbec2b260da7a93ed0096dea32e708e127c5051b875af2d/akracer-0.0.13-py3-none-any.whl", hash = "sha256:55bd04c69e35130994d26795f00183e0c33d4e237f7ebfa35074a760c30209d1", size = 10078023, upload-time = "2023-10-25T08:02:27.193Z" }, ] [[package]] name = "akshare" version = "1.17.26" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiohttp" }, { name = "akracer", marker = "sys_platform == 'linux'" }, @@ -184,33 +184,33 @@ dependencies = [ { name = "urllib3" }, { name = "xlrd" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/82/ca/b5eea0a3f2783ce8934495c7364425acbc70a021bdf7e79e8ba02d0ce0a8/akshare-1.17.26.tar.gz", hash = "sha256:3245df116965c46b16bb23da9c0372e61ea802d1f996243d26a64c3e16b513f1" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/ca/b5eea0a3f2783ce8934495c7364425acbc70a021bdf7e79e8ba02d0ce0a8/akshare-1.17.26.tar.gz", hash = "sha256:3245df116965c46b16bb23da9c0372e61ea802d1f996243d26a64c3e16b513f1", size = 839911, upload-time = "2025-07-24T08:40:48.939Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a5/4a/f40c8adb970514eac84f3c0728ebb5e069f2e2ca202d9e94cbc6f50057fe/akshare-1.17.26-py3-none-any.whl", hash = "sha256:b8216f81336349d1069972c2916e7083a5e06eb21b4a9db2d5051e771c9fd4df" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/4a/f40c8adb970514eac84f3c0728ebb5e069f2e2ca202d9e94cbc6f50057fe/akshare-1.17.26-py3-none-any.whl", hash = "sha256:b8216f81336349d1069972c2916e7083a5e06eb21b4a9db2d5051e771c9fd4df", size = 1054174, upload-time = "2025-07-24T08:40:47.67Z" }, ] [[package]] name = "alabaster" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] name = "anthropic" version = "0.34.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -221,153 +221,153 @@ dependencies = [ { name = "tokenizers" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/87/e2/98ff733ff75c1d371c029fb27eb9308f9c8e694749cea70382338a8e7e88/anthropic-0.34.1.tar.gz", hash = "sha256:69e822bd7a31ec11c2edb85f2147e8f0ee0cfd3288fea70b0ca8808b2f9bf91d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/e2/98ff733ff75c1d371c029fb27eb9308f9c8e694749cea70382338a8e7e88/anthropic-0.34.1.tar.gz", hash = "sha256:69e822bd7a31ec11c2edb85f2147e8f0ee0cfd3288fea70b0ca8808b2f9bf91d", size = 901462, upload-time = "2024-08-20T00:44:35.633Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a4/1c/1ce9edec76885badebacb4e31d42acffbdfd30dbaa839d5c378d57ac9aa9/anthropic-0.34.1-py3-none-any.whl", hash = "sha256:2fa26710809d0960d970f26cd0be3686437250a481edb95c33d837aa5fa24158" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/1c/1ce9edec76885badebacb4e31d42acffbdfd30dbaa839d5c378d57ac9aa9/anthropic-0.34.1-py3-none-any.whl", hash = "sha256:2fa26710809d0960d970f26cd0be3686437250a481edb95c33d837aa5fa24158", size = 891537, upload-time = "2024-08-20T00:44:34.033Z" }, ] [[package]] name = "anyio" version = "4.9.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, ] [[package]] name = "anytree" version = "2.13.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bc/a8/eb55fab589c56f9b6be2b3fd6997aa04bb6f3da93b01154ce6fc8e799db2/anytree-2.13.0.tar.gz", hash = "sha256:c9d3aa6825fdd06af7ebb05b4ef291d2db63e62bb1f9b7d9b71354be9d362714" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/a8/eb55fab589c56f9b6be2b3fd6997aa04bb6f3da93b01154ce6fc8e799db2/anytree-2.13.0.tar.gz", hash = "sha256:c9d3aa6825fdd06af7ebb05b4ef291d2db63e62bb1f9b7d9b71354be9d362714", size = 48389, upload-time = "2025-04-08T21:06:30.662Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7b/98/f6aa7fe0783e42be3093d8ef1b0ecdc22c34c0d69640dfb37f56925cb141/anytree-2.13.0-py3-none-any.whl", hash = "sha256:4cbcf10df36b1f1cba131b7e487ff3edafc9d6e932a3c70071b5b768bab901ff" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/98/f6aa7fe0783e42be3093d8ef1b0ecdc22c34c0d69640dfb37f56925cb141/anytree-2.13.0-py3-none-any.whl", hash = "sha256:4cbcf10df36b1f1cba131b7e487ff3edafc9d6e932a3c70071b5b768bab901ff", size = 45077, upload-time = "2025-04-08T21:06:29.494Z" }, ] [[package]] name = "argon2-cffi" version = "25.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "argon2-cffi-bindings" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, ] [[package]] name = "argon2-cffi-bindings" version = "21.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b9/e9/184b8ccce6683b0aa2fbb7ba5683ea4b9c5763f1356347f1312c32e3c66e/argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/e9/184b8ccce6683b0aa2fbb7ba5683ea4b9c5763f1356347f1312c32e3c66e/argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", size = 1779911, upload-time = "2021-12-01T08:52:55.68Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d4/13/838ce2620025e9666aa8f686431f67a29052241692a3dd1ae9d3692a89d3/argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/02/f7f7bb6b6af6031edb11037639c697b912e1dea2db94d436e681aea2f495/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/f6/4a34a37a98311ed73bb80efe422fed95f2ac25a4cacc5ae1d7ae6a144505/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/2b/73d767bfdaab25484f7e7901379d5f8793cccbb86c6e0cbc4c1b96f63896/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4f/fd/37f86deef67ff57c76f137a67181949c2d408077e2e3dd70c6c42912c9bf/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/52/5a60085a3dae8fded8327a4f564223029f5f54b0cb0455a31131b5363a01/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8b/95/143cd64feb24a15fa4b189a3e1e7efbaeeb00f39a51e99b26fc62fbacabd/argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/2c/e34e47c7dee97ba6f01a6203e0383e15b60fb85d78ac9a15cd066f6fe28b/argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/13/838ce2620025e9666aa8f686431f67a29052241692a3dd1ae9d3692a89d3/argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", size = 29658, upload-time = "2021-12-01T09:09:17.016Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/02/f7f7bb6b6af6031edb11037639c697b912e1dea2db94d436e681aea2f495/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", size = 80583, upload-time = "2021-12-01T09:09:19.546Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", size = 86168, upload-time = "2021-12-01T09:09:21.445Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/f6/4a34a37a98311ed73bb80efe422fed95f2ac25a4cacc5ae1d7ae6a144505/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", size = 82709, upload-time = "2021-12-01T09:09:18.182Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/2b/73d767bfdaab25484f7e7901379d5f8793cccbb86c6e0cbc4c1b96f63896/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", size = 83613, upload-time = "2021-12-01T09:09:22.741Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/fd/37f86deef67ff57c76f137a67181949c2d408077e2e3dd70c6c42912c9bf/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", size = 84583, upload-time = "2021-12-01T09:09:24.177Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/52/5a60085a3dae8fded8327a4f564223029f5f54b0cb0455a31131b5363a01/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", size = 88475, upload-time = "2021-12-01T09:09:26.673Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/95/143cd64feb24a15fa4b189a3e1e7efbaeeb00f39a51e99b26fc62fbacabd/argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", size = 27698, upload-time = "2021-12-01T09:09:27.87Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/2c/e34e47c7dee97ba6f01a6203e0383e15b60fb85d78ac9a15cd066f6fe28b/argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", size = 30817, upload-time = "2021-12-01T09:09:30.267Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104, upload-time = "2021-12-01T09:09:31.335Z" }, ] [[package]] name = "arrow" version = "1.3.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "python-dateutil" }, { name = "types-python-dateutil" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80" }, ] [[package]] name = "arxiv" version = "2.1.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "feedparser" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fe/59/fe41f54bdfed776c2e9bcd6289e4c71349eb938241d89b4c97d0f33e8013/arxiv-2.1.3.tar.gz", hash = "sha256:32365221994d2cf05657c1fadf63a26efc8ccdec18590281ee03515bfef8bc4e" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/59/fe41f54bdfed776c2e9bcd6289e4c71349eb938241d89b4c97d0f33e8013/arxiv-2.1.3.tar.gz", hash = "sha256:32365221994d2cf05657c1fadf63a26efc8ccdec18590281ee03515bfef8bc4e", size = 16747, upload-time = "2024-06-25T02:56:20.062Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b7/7b/7bf42178d227b26d3daf94cdd22a72a4ed5bf235548c4f5aea49c51c6458/arxiv-2.1.3-py3-none-any.whl", hash = "sha256:6f43673ab770a9e848d7d4fc1894824df55edeac3c3572ea280c9ba2e3c0f39f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/7b/7bf42178d227b26d3daf94cdd22a72a4ed5bf235548c4f5aea49c51c6458/arxiv-2.1.3-py3-none-any.whl", hash = "sha256:6f43673ab770a9e848d7d4fc1894824df55edeac3c3572ea280c9ba2e3c0f39f", size = 11478, upload-time = "2024-06-25T02:56:17.032Z" }, ] [[package]] name = "aspose-slides" version = "24.12.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6e/db/680408b92f47aa9ff2c70f80b2f5d02155a8ff81ac493c3061099bf56c37/Aspose.Slides-24.12.0-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:ccfaa61a863ed28cd37b221e31a0edf4a83802599d76fb50861c25149ac5e5e3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/ac/29838004784acb72c9d93f0b327a8e5105f35eb925cdaeccd07907464018/Aspose.Slides-24.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b050659129c5ca92e52fbcd7d5091caa244db731adb68fbea1fd0a8b9fd62a5a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/6e/0b9da3757ce46b63f3fbb10ee352009c20260813d369306438bd3552fc18/Aspose.Slides-24.12.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a5eb8407bd93fa7851584c3b143000c09d9f5285f3c1da99677bf1d9c0abefe9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/0b/af65314b471766709627a65096f69e8b70b7840edd98cabaa9b74fda671d/Aspose.Slides-24.12.0-py3-none-win_amd64.whl", hash = "sha256:e816e37a621221e8a73fc631c879ada37cf6a80513a817b687d6f7e189d5a978" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/db/680408b92f47aa9ff2c70f80b2f5d02155a8ff81ac493c3061099bf56c37/Aspose.Slides-24.12.0-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:ccfaa61a863ed28cd37b221e31a0edf4a83802599d76fb50861c25149ac5e5e3", size = 87164865, upload-time = "2024-12-05T00:51:15.328Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/ac/29838004784acb72c9d93f0b327a8e5105f35eb925cdaeccd07907464018/Aspose.Slides-24.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b050659129c5ca92e52fbcd7d5091caa244db731adb68fbea1fd0a8b9fd62a5a", size = 68916630, upload-time = "2024-12-05T00:51:21.587Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/6e/0b9da3757ce46b63f3fbb10ee352009c20260813d369306438bd3552fc18/Aspose.Slides-24.12.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a5eb8407bd93fa7851584c3b143000c09d9f5285f3c1da99677bf1d9c0abefe9", size = 102438903, upload-time = "2024-12-05T00:51:27.926Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/0b/af65314b471766709627a65096f69e8b70b7840edd98cabaa9b74fda671d/Aspose.Slides-24.12.0-py3-none-win_amd64.whl", hash = "sha256:e816e37a621221e8a73fc631c879ada37cf6a80513a817b687d6f7e189d5a978", size = 72093115, upload-time = "2024-12-05T00:51:40.848Z" }, ] [[package]] name = "async-timeout" version = "5.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, ] [[package]] name = "attrs" version = "25.3.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, ] [[package]] name = "autograd" version = "1.8.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/67/1c/3c24ec03c8ba4decc742b1df5a10c52f98c84ca8797757f313e7bdcdf276/autograd-1.8.0.tar.gz", hash = "sha256:107374ded5b09fc8643ac925348c0369e7b0e73bbed9565ffd61b8fd04425683" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/1c/3c24ec03c8ba4decc742b1df5a10c52f98c84ca8797757f313e7bdcdf276/autograd-1.8.0.tar.gz", hash = "sha256:107374ded5b09fc8643ac925348c0369e7b0e73bbed9565ffd61b8fd04425683", size = 2562146, upload-time = "2025-05-05T12:49:02.502Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/84/ea/e16f0c423f7d83cf8b79cae9452040fb7b2e020c7439a167ee7c317de448/autograd-1.8.0-py3-none-any.whl", hash = "sha256:4ab9084294f814cf56c280adbe19612546a35574d67c574b04933c7d2ecb7d78" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/ea/e16f0c423f7d83cf8b79cae9452040fb7b2e020c7439a167ee7c317de448/autograd-1.8.0-py3-none-any.whl", hash = "sha256:4ab9084294f814cf56c280adbe19612546a35574d67c574b04933c7d2ecb7d78", size = 51478, upload-time = "2025-05-05T12:49:00.585Z" }, ] [[package]] name = "azure-core" version = "1.35.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "requests" }, { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ce/89/f53968635b1b2e53e4aad2dd641488929fef4ca9dfb0b97927fa7697ddf3/azure_core-1.35.0.tar.gz", hash = "sha256:c0be528489485e9ede59b6971eb63c1eaacf83ef53001bfe3904e475e972be5c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/89/f53968635b1b2e53e4aad2dd641488929fef4ca9dfb0b97927fa7697ddf3/azure_core-1.35.0.tar.gz", hash = "sha256:c0be528489485e9ede59b6971eb63c1eaacf83ef53001bfe3904e475e972be5c", size = 339689, upload-time = "2025-07-03T00:55:23.496Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1", size = 210708, upload-time = "2025-07-03T00:55:25.238Z" }, ] [[package]] name = "azure-identity" version = "1.17.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "azure-core" }, { name = "cryptography" }, @@ -375,77 +375,77 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/51/c9/f7e3926686a89670ce641b360bd2da9a2d7a12b3e532403462d99f81e9d5/azure-identity-1.17.1.tar.gz", hash = "sha256:32ecc67cc73f4bd0595e4f64b1ca65cd05186f4fe6f98ed2ae9f1aa32646efea" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/c9/f7e3926686a89670ce641b360bd2da9a2d7a12b3e532403462d99f81e9d5/azure-identity-1.17.1.tar.gz", hash = "sha256:32ecc67cc73f4bd0595e4f64b1ca65cd05186f4fe6f98ed2ae9f1aa32646efea", size = 246652, upload-time = "2024-06-22T01:41:45.525Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/49/83/a777861351e7b99e7c84ff3b36bab35e87b6e5d36e50b6905e148c696515/azure_identity-1.17.1-py3-none-any.whl", hash = "sha256:db8d59c183b680e763722bfe8ebc45930e6c57df510620985939f7f3191e0382" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/83/a777861351e7b99e7c84ff3b36bab35e87b6e5d36e50b6905e148c696515/azure_identity-1.17.1-py3-none-any.whl", hash = "sha256:db8d59c183b680e763722bfe8ebc45930e6c57df510620985939f7f3191e0382", size = 173229, upload-time = "2024-06-22T01:41:49.309Z" }, ] [[package]] name = "azure-storage-blob" version = "12.22.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "azure-core" }, { name = "cryptography" }, { name = "isodate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/92/de/9cea85c0d5fc21f99bcf9f060fc2287cb95236b70431fa63cb69890a121e/azure-storage-blob-12.22.0.tar.gz", hash = "sha256:b3804bb4fe8ab1c32771fa464053da772a682c2737b19da438a3f4e5e3b3736e" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/de/9cea85c0d5fc21f99bcf9f060fc2287cb95236b70431fa63cb69890a121e/azure-storage-blob-12.22.0.tar.gz", hash = "sha256:b3804bb4fe8ab1c32771fa464053da772a682c2737b19da438a3f4e5e3b3736e", size = 564873, upload-time = "2024-08-06T20:54:41.054Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a8/52/b578c94048469fbf9f6378e2b2a46a2d0ccba3d59a7845dbed22ebf61601/azure_storage_blob-12.22.0-py3-none-any.whl", hash = "sha256:bb7d2d824ce3f11f14a27ee7d9281289f7e072ac8311c52e3652672455b7d5e8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/52/b578c94048469fbf9f6378e2b2a46a2d0ccba3d59a7845dbed22ebf61601/azure_storage_blob-12.22.0-py3-none-any.whl", hash = "sha256:bb7d2d824ce3f11f14a27ee7d9281289f7e072ac8311c52e3652672455b7d5e8", size = 404892, upload-time = "2024-08-06T20:54:43.612Z" }, ] [[package]] name = "azure-storage-file-datalake" version = "12.16.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "azure-core" }, { name = "azure-storage-blob" }, { name = "isodate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/71/a6/980d2d1405ae5397b618cc9a21b4530fb7e6c9078ccf48b5ce0eec1b25cd/azure-storage-file-datalake-12.16.0.tar.gz", hash = "sha256:3185580e4e438162ef84fb88cb46b2ef248dafbfb07f53297762417bb7000333" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/a6/980d2d1405ae5397b618cc9a21b4530fb7e6c9078ccf48b5ce0eec1b25cd/azure-storage-file-datalake-12.16.0.tar.gz", hash = "sha256:3185580e4e438162ef84fb88cb46b2ef248dafbfb07f53297762417bb7000333", size = 274485, upload-time = "2024-07-18T21:55:41.784Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/42/56/a30d062af3100b3ec3e515fc1f40d38979e8508bb962231530702309aa4b/azure_storage_file_datalake-12.16.0-py3-none-any.whl", hash = "sha256:da57ec6cf5640b92bbd0ba61478f51e67c63b94843fa748b3b6599f1adba5837" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/56/a30d062af3100b3ec3e515fc1f40d38979e8508bb962231530702309aa4b/azure_storage_file_datalake-12.16.0-py3-none-any.whl", hash = "sha256:da57ec6cf5640b92bbd0ba61478f51e67c63b94843fa748b3b6599f1adba5837", size = 255558, upload-time = "2024-07-18T21:55:44.204Z" }, ] [[package]] name = "babel" version = "2.17.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, ] [[package]] name = "backoff" version = "2.2.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, ] [[package]] name = "bce-python-sdk" version = "0.9.42" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "future" }, { name = "pycryptodome" }, { name = "six" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/58/37/bee21dab6b4547538a576982de5b7e4c864bab999c46163a7a4c9f385826/bce_python_sdk-0.9.42.tar.gz", hash = "sha256:41a92b785799b41f25753136ddb6a8bb96afcb69015e2fda6a5a429030ffc6b9" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/37/bee21dab6b4547538a576982de5b7e4c864bab999c46163a7a4c9f385826/bce_python_sdk-0.9.42.tar.gz", hash = "sha256:41a92b785799b41f25753136ddb6a8bb96afcb69015e2fda6a5a429030ffc6b9", size = 252391, upload-time = "2025-07-24T13:38:56.763Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b0/bb/94071e19b45d9922b2d8014ebe553304bfed9ae02a6eaa20a15a6249c49f/bce_python_sdk-0.9.42-py3-none-any.whl", hash = "sha256:b62207e974e9c1a53ef0d71183b3004cca6c8155da3208b51b3dc48b1dee6b86" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/bb/94071e19b45d9922b2d8014ebe553304bfed9ae02a6eaa20a15a6249c49f/bce_python_sdk-0.9.42-py3-none-any.whl", hash = "sha256:b62207e974e9c1a53ef0d71183b3004cca6c8155da3208b51b3dc48b1dee6b86", size = 351625, upload-time = "2025-07-24T13:38:54.45Z" }, ] [[package]] name = "bcembedding" version = "0.1.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "datasets" }, { name = "sentence-transformers" }, @@ -453,43 +453,43 @@ dependencies = [ { name = "transformers" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/52/85/126569029a643837c1e369dc050334d0dff81fc0a9c2a63ce00b466fb9e6/BCEmbedding-0.1.5-py3-none-any.whl", hash = "sha256:7d46c876380dae51ad52c9a8e5e51373691e85d50b6f485e0bcd38ac31f6b144" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/85/126569029a643837c1e369dc050334d0dff81fc0a9c2a63ce00b466fb9e6/BCEmbedding-0.1.5-py3-none-any.whl", hash = "sha256:7d46c876380dae51ad52c9a8e5e51373691e85d50b6f485e0bcd38ac31f6b144", size = 30132, upload-time = "2024-05-13T09:07:26.043Z" }, ] [[package]] name = "beartype" version = "0.18.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/96/15/4e623478a9628ad4cee2391f19aba0b16c1dd6fedcb2a399f0928097b597/beartype-0.18.5.tar.gz", hash = "sha256:264ddc2f1da9ec94ff639141fbe33d22e12a9f75aa863b83b7046ffff1381927" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/15/4e623478a9628ad4cee2391f19aba0b16c1dd6fedcb2a399f0928097b597/beartype-0.18.5.tar.gz", hash = "sha256:264ddc2f1da9ec94ff639141fbe33d22e12a9f75aa863b83b7046ffff1381927", size = 1193506, upload-time = "2024-04-21T07:25:58.64Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/64/43/7a1259741bd989723272ac7d381a43be932422abcff09a1d9f7ba212cb74/beartype-0.18.5-py3-none-any.whl", hash = "sha256:5301a14f2a9a5540fe47ec6d34d758e9cd8331d36c4760fc7a5499ab86310089" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/43/7a1259741bd989723272ac7d381a43be932422abcff09a1d9f7ba212cb74/beartype-0.18.5-py3-none-any.whl", hash = "sha256:5301a14f2a9a5540fe47ec6d34d758e9cd8331d36c4760fc7a5499ab86310089", size = 917762, upload-time = "2024-04-21T07:25:55.758Z" }, ] [[package]] name = "beautifulsoup4" version = "4.12.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "soupsieve" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181, upload-time = "2024-01-17T16:53:17.902Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925, upload-time = "2024-01-17T16:53:12.779Z" }, ] [[package]] name = "bibtexparser" version = "1.4.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/92/8d/e296c7af03757debd8fc80df2898cbed4fb69fc61ed2c9b4a1d42e923a9e/bibtexparser-1.4.3.tar.gz", hash = "sha256:a9c7ded64bc137720e4df0b1b7f12734edc1361185f1c9097048ff7c35af2b8f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/8d/e296c7af03757debd8fc80df2898cbed4fb69fc61ed2c9b4a1d42e923a9e/bibtexparser-1.4.3.tar.gz", hash = "sha256:a9c7ded64bc137720e4df0b1b7f12734edc1361185f1c9097048ff7c35af2b8f", size = 55582, upload-time = "2024-12-19T20:41:57.754Z" } [[package]] name = "bio" version = "1.7.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "biopython" }, { name = "gprofiler-official" }, @@ -499,336 +499,336 @@ dependencies = [ { name = "requests" }, { name = "tqdm" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/58/ba/fdaa4c286ed50f96835a5f81c72d6c76933fb890ee1ff2269b6110ea851e/bio-1.7.1.tar.gz", hash = "sha256:df3252905b0b1e739eca3760c91fd519d5af07b09632df25c2bd4ecd20da2724" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/ba/fdaa4c286ed50f96835a5f81c72d6c76933fb890ee1ff2269b6110ea851e/bio-1.7.1.tar.gz", hash = "sha256:df3252905b0b1e739eca3760c91fd519d5af07b09632df25c2bd4ecd20da2724", size = 241383, upload-time = "2024-05-29T16:32:55.426Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/cb/40/747f3038ac636e520da52f7b9f5721779a50f88fdfc165847b0d8127dae2/bio-1.7.1-py3-none-any.whl", hash = "sha256:851545804b08413a3f27fd5131edefc30acfdee513919eebabb29678d8632218" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/40/747f3038ac636e520da52f7b9f5721779a50f88fdfc165847b0d8127dae2/bio-1.7.1-py3-none-any.whl", hash = "sha256:851545804b08413a3f27fd5131edefc30acfdee513919eebabb29678d8632218", size = 280992, upload-time = "2024-05-29T16:32:56.712Z" }, ] [[package]] name = "biopython" version = "1.85" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/db/ca/1d5fab0fedaf5c2f376d9746d447cdce04241c433602c3861693361ce54c/biopython-1.85.tar.gz", hash = "sha256:5dafab74059de4e78f49f6b5684eddae6e7ce46f09cfa059c1d1339e8b1ea0a6" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/79/de/79824470fdc5c2aedeb1bab637d24eab654a7e9fbb7070f779fd944fd1ca/biopython-1.85-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6308053a61f3bdbb11504ece4cf24e264c6f1d6fad278f7e59e6b84b0d9a7b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/d6/3c90df2ebc4f2a522235f4391392253e528f19f5fb6a52396a2316e17064/biopython-1.85-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:434dd23e972b0c89e128f2ebbd16b38075d609184f4f1fd16368035f923019c2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/d7/34233c4ee906b088b199eb2af9c4107384a547d44b5960257ef2bf2cc3c8/biopython-1.85-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a08d082e85778259a83501063871e00ba57336136403b75050eea14d523c00a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/6b/e7a5ae49ef7e8f81720593db22e884c1c8e3504e0e235da0395758a5c7d0/biopython-1.85-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93464e629950e4df87d810125dc4e904538a4344b924f340908ea5bc95db986" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/2c/0035656dbe51ac0bbe81321172b43c37b38408f8d136abaac443e9f44b62/biopython-1.85-cp310-cp310-win32.whl", hash = "sha256:f2f45ab3f1e43fdaa697fd753148999090298623278097c19c2c3c0ba134e57c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/a4/f20d5830dbd8654c13e763daef429fa32ef0b2b09749ac427d045cc81976/biopython-1.85-cp310-cp310-win_amd64.whl", hash = "sha256:7c8326cd2825ba166abecaf72843b9b15823affd6cec04fde65f0d2526767da4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/73/c3a1323a3fe0d07212b09c04fb903e2cbb98aebfbb58e55e8717473e1bc0/biopython-1.85-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db8822adab0cd75a6e6ae845acf312addd8eab5f9b731c191454b961fc2c2cdc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/cf/299524e896fa49beb7588143e1509cce4848572215ebafb8eea83a861820/biopython-1.85-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e2bbe58cc1a592b239ef6d68396745d3fbfaafc668ce38283871d8ff070dbab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/dd/be3e95b72a35ee6d52c84412e15af828951e5c69175080d4619985fd54ce/biopython-1.85-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5916eb56df7ecd4a3babc07a48d4894c40cfb45dc18ccda1c148d0131017ce04" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/9c/612821b946930b6caa5d795cfe4169ed6a522562eced9776914be7efaf21/biopython-1.85-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0cec8833bf3036049129944ee5382dd576dac9670c3814ff2314b52aa94f199" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/77/316e51dd42fd8225429574a268bdc627ce4f42067a3976c4c8c457a42023/biopython-1.85-cp311-cp311-win32.whl", hash = "sha256:cf88a4c8d8af13138be115949639a5e4a201618185a72ff09adbe175b7946b28" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/11/3c4e8c049b91998bbbd51ddebc6f790b1aa66211babfbf5ff008a72fb1f9/biopython-1.85-cp311-cp311-win_amd64.whl", hash = "sha256:d3c99db65d57ae4fc5034e42ac6cd8ddce069e664903f04c8a4f684d7609d6fa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a3/25/e46f05359df7f0049c3adc5eaeb9aee0f5fbde1d959d05c78eb1de8f4d12/biopython-1.85-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc5b981b9e3060db7c355b6145dfe3ce0b6572e1601b31211f6d742b10543874" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/5b/8b3b029c94c63ab4c1781d141615b4a837e658422381d460c5573d5d8262/biopython-1.85-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6fe47d704c2d3afac99aeb461219ec5f00273120d2d99835dc0a9a617f520141" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/0a/9a8a38eff03c4607b9cec8d0e08c76b346b1cee1f77bc6d00efebfc7ec83/biopython-1.85-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54e495239e623660ad367498c2f7a1a294b1997ba603f2ceafb36fd18f0eba6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/0d/b7a0f10f5100dcf51ae36ba31490169bfa45617323bd82af43e1fb0098fb/biopython-1.85-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d024ad48997ad53d53a77da24b072aaba8a550bd816af8f2e7e606a9918a3b43" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/51/646a4b7bdb4c1153786a70d33588ed09178bfcdda0542dfdc976294f4312/biopython-1.85-cp312-cp312-win32.whl", hash = "sha256:6985e17a2911defcbd30275a12f5ed5de2765e4bc91a60439740d572fdbfdf43" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c1/84/c583fa2ac6e7d392d24ebdc5c99e95e517507de22cf143efb6cf1fc93ff5/biopython-1.85-cp312-cp312-win_amd64.whl", hash = "sha256:d6f8efb2db03f2ec115c5e8c764dbadf635e0c9ecd4c0e91fc8216c1b62f85f5" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/ca/1d5fab0fedaf5c2f376d9746d447cdce04241c433602c3861693361ce54c/biopython-1.85.tar.gz", hash = "sha256:5dafab74059de4e78f49f6b5684eddae6e7ce46f09cfa059c1d1339e8b1ea0a6", size = 19909902, upload-time = "2025-01-15T15:06:51.997Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/de/79824470fdc5c2aedeb1bab637d24eab654a7e9fbb7070f779fd944fd1ca/biopython-1.85-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6308053a61f3bdbb11504ece4cf24e264c6f1d6fad278f7e59e6b84b0d9a7b4", size = 2787511, upload-time = "2025-01-15T15:11:56.656Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/d6/3c90df2ebc4f2a522235f4391392253e528f19f5fb6a52396a2316e17064/biopython-1.85-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:434dd23e972b0c89e128f2ebbd16b38075d609184f4f1fd16368035f923019c2", size = 2765456, upload-time = "2025-01-15T15:12:05.177Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/d7/34233c4ee906b088b199eb2af9c4107384a547d44b5960257ef2bf2cc3c8/biopython-1.85-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a08d082e85778259a83501063871e00ba57336136403b75050eea14d523c00a", size = 3225137, upload-time = "2025-01-15T15:12:15.847Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/6b/e7a5ae49ef7e8f81720593db22e884c1c8e3504e0e235da0395758a5c7d0/biopython-1.85-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93464e629950e4df87d810125dc4e904538a4344b924f340908ea5bc95db986", size = 3245258, upload-time = "2025-01-15T15:12:21.012Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/2c/0035656dbe51ac0bbe81321172b43c37b38408f8d136abaac443e9f44b62/biopython-1.85-cp310-cp310-win32.whl", hash = "sha256:f2f45ab3f1e43fdaa697fd753148999090298623278097c19c2c3c0ba134e57c", size = 2786505, upload-time = "2025-01-15T15:12:26.545Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/a4/f20d5830dbd8654c13e763daef429fa32ef0b2b09749ac427d045cc81976/biopython-1.85-cp310-cp310-win_amd64.whl", hash = "sha256:7c8326cd2825ba166abecaf72843b9b15823affd6cec04fde65f0d2526767da4", size = 2820961, upload-time = "2025-01-15T15:12:32.364Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/73/c3a1323a3fe0d07212b09c04fb903e2cbb98aebfbb58e55e8717473e1bc0/biopython-1.85-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db8822adab0cd75a6e6ae845acf312addd8eab5f9b731c191454b961fc2c2cdc", size = 2787585, upload-time = "2025-01-15T15:12:36.927Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/cf/299524e896fa49beb7588143e1509cce4848572215ebafb8eea83a861820/biopython-1.85-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e2bbe58cc1a592b239ef6d68396745d3fbfaafc668ce38283871d8ff070dbab", size = 2765608, upload-time = "2025-01-15T15:12:43.853Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/dd/be3e95b72a35ee6d52c84412e15af828951e5c69175080d4619985fd54ce/biopython-1.85-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5916eb56df7ecd4a3babc07a48d4894c40cfb45dc18ccda1c148d0131017ce04", size = 3237552, upload-time = "2025-01-15T15:12:49.046Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/9c/612821b946930b6caa5d795cfe4169ed6a522562eced9776914be7efaf21/biopython-1.85-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0cec8833bf3036049129944ee5382dd576dac9670c3814ff2314b52aa94f199", size = 3257287, upload-time = "2025-01-15T15:12:56.168Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/77/316e51dd42fd8225429574a268bdc627ce4f42067a3976c4c8c457a42023/biopython-1.85-cp311-cp311-win32.whl", hash = "sha256:cf88a4c8d8af13138be115949639a5e4a201618185a72ff09adbe175b7946b28", size = 2786411, upload-time = "2025-01-15T15:13:02.754Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/11/3c4e8c049b91998bbbd51ddebc6f790b1aa66211babfbf5ff008a72fb1f9/biopython-1.85-cp311-cp311-win_amd64.whl", hash = "sha256:d3c99db65d57ae4fc5034e42ac6cd8ddce069e664903f04c8a4f684d7609d6fa", size = 2820912, upload-time = "2025-01-15T15:13:10.499Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/25/e46f05359df7f0049c3adc5eaeb9aee0f5fbde1d959d05c78eb1de8f4d12/biopython-1.85-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc5b981b9e3060db7c355b6145dfe3ce0b6572e1601b31211f6d742b10543874", size = 2789327, upload-time = "2025-01-15T15:13:17.086Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/5b/8b3b029c94c63ab4c1781d141615b4a837e658422381d460c5573d5d8262/biopython-1.85-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6fe47d704c2d3afac99aeb461219ec5f00273120d2d99835dc0a9a617f520141", size = 2765805, upload-time = "2025-01-15T15:13:26.92Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/0a/9a8a38eff03c4607b9cec8d0e08c76b346b1cee1f77bc6d00efebfc7ec83/biopython-1.85-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54e495239e623660ad367498c2f7a1a294b1997ba603f2ceafb36fd18f0eba6", size = 3249276, upload-time = "2025-01-15T15:13:36.639Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/0d/b7a0f10f5100dcf51ae36ba31490169bfa45617323bd82af43e1fb0098fb/biopython-1.85-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d024ad48997ad53d53a77da24b072aaba8a550bd816af8f2e7e606a9918a3b43", size = 3268869, upload-time = "2025-01-15T15:13:44.009Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/51/646a4b7bdb4c1153786a70d33588ed09178bfcdda0542dfdc976294f4312/biopython-1.85-cp312-cp312-win32.whl", hash = "sha256:6985e17a2911defcbd30275a12f5ed5de2765e4bc91a60439740d572fdbfdf43", size = 2787011, upload-time = "2025-01-15T15:13:48.958Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/84/c583fa2ac6e7d392d24ebdc5c99e95e517507de22cf143efb6cf1fc93ff5/biopython-1.85-cp312-cp312-win_amd64.whl", hash = "sha256:d6f8efb2db03f2ec115c5e8c764dbadf635e0c9ecd4c0e91fc8216c1b62f85f5", size = 2820972, upload-time = "2025-01-15T15:13:54.475Z" }, ] [[package]] name = "biothings-client" version = "0.4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "httpx" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ef/2f/2ef9115e317f4c2acb690b341f40de751b16d14169fdbb8d6eb86964166c/biothings_client-0.4.1.tar.gz", hash = "sha256:5b34e09c905280b5bd2538f1f34b6fc780c53c8da9b4074e3ff304836046f613" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/2f/2ef9115e317f4c2acb690b341f40de751b16d14169fdbb8d6eb86964166c/biothings_client-0.4.1.tar.gz", hash = "sha256:5b34e09c905280b5bd2538f1f34b6fc780c53c8da9b4074e3ff304836046f613", size = 56096, upload-time = "2025-01-14T22:55:39.926Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/dc/6d/130477cfbd7294949b919c45cc1ed14a642cec95afba06a54400a4419235/biothings_client-0.4.1-py3-none-any.whl", hash = "sha256:9cbc17461b2bf6af6ed200929b886d6670d450af2034b428cd833f725695265a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/6d/130477cfbd7294949b919c45cc1ed14a642cec95afba06a54400a4419235/biothings_client-0.4.1-py3-none-any.whl", hash = "sha256:9cbc17461b2bf6af6ed200929b886d6670d450af2034b428cd833f725695265a", size = 46698, upload-time = "2025-01-14T22:55:37.44Z" }, ] [[package]] name = "blinker" version = "1.7.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134, upload-time = "2023-11-01T22:06:01.588Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068, upload-time = "2023-11-01T22:06:00.162Z" }, ] [[package]] name = "boto3" version = "1.34.140" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3f/79/c9a3651e563c5ac762080b8cef8e85659400a78d9ccffcf83916f68f4b04/boto3-1.34.140.tar.gz", hash = "sha256:578bbd5e356005719b6b610d03edff7ea1b0824d078afe62d3fb8bea72f83a87" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/79/c9a3651e563c5ac762080b8cef8e85659400a78d9ccffcf83916f68f4b04/boto3-1.34.140.tar.gz", hash = "sha256:578bbd5e356005719b6b610d03edff7ea1b0824d078afe62d3fb8bea72f83a87", size = 108704, upload-time = "2024-07-05T19:20:32.085Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7b/26/76ee022975d33c9460ba0b3edefade5569597aa43bfb57c72722b1c5356a/boto3-1.34.140-py3-none-any.whl", hash = "sha256:23ca8d8f7a30c3bbd989808056b5fc5d68ff5121c02c722c6167b6b1bb7f8726" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/26/76ee022975d33c9460ba0b3edefade5569597aa43bfb57c72722b1c5356a/boto3-1.34.140-py3-none-any.whl", hash = "sha256:23ca8d8f7a30c3bbd989808056b5fc5d68ff5121c02c722c6167b6b1bb7f8726", size = 139171, upload-time = "2024-07-05T19:20:11.416Z" }, ] [[package]] name = "botocore" version = "1.34.140" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a5/9a/3ae02c5dcc8f9a188e03d897aac898bd20d7f3eb5b910c9e071caf70f172/botocore-1.34.140.tar.gz", hash = "sha256:86302b2226c743b9eec7915a4c6cfaffd338ae03989cd9ee181078ef39d1ab39" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/9a/3ae02c5dcc8f9a188e03d897aac898bd20d7f3eb5b910c9e071caf70f172/botocore-1.34.140.tar.gz", hash = "sha256:86302b2226c743b9eec7915a4c6cfaffd338ae03989cd9ee181078ef39d1ab39", size = 12565133, upload-time = "2024-07-05T19:19:26.79Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f5/23/91c8b50588470d80317f4afca93d3d542139bdc38ed5ad1b512fba416af3/botocore-1.34.140-py3-none-any.whl", hash = "sha256:43940d3a67d946ba3301631ba4078476a75f1015d4fb0fb0272d0b754b2cf9de" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/23/91c8b50588470d80317f4afca93d3d542139bdc38ed5ad1b512fba416af3/botocore-1.34.140-py3-none-any.whl", hash = "sha256:43940d3a67d946ba3301631ba4078476a75f1015d4fb0fb0272d0b754b2cf9de", size = 12354845, upload-time = "2024-07-05T19:19:10.578Z" }, ] [[package]] name = "brotli" version = "1.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270, upload-time = "2023-09-07T14:05:41.643Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", size = 873045, upload-time = "2023-09-07T14:03:16.894Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", size = 446218, upload-time = "2023-09-07T14:03:18.917Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", size = 2903872, upload-time = "2023-09-07T14:03:20.398Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", size = 2941254, upload-time = "2023-09-07T14:03:21.914Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", size = 2857293, upload-time = "2023-09-07T14:03:24Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", size = 3002385, upload-time = "2023-09-07T14:03:26.248Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", size = 2911104, upload-time = "2023-09-07T14:03:27.849Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", size = 2809981, upload-time = "2023-09-07T14:03:29.92Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", size = 2935297, upload-time = "2023-09-07T14:03:32.035Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", size = 2930735, upload-time = "2023-09-07T14:03:33.801Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c", size = 2933107, upload-time = "2024-10-18T12:32:09.016Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1", size = 2845400, upload-time = "2024-10-18T12:32:11.134Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2", size = 3031985, upload-time = "2024-10-18T12:32:12.813Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec", size = 2927099, upload-time = "2024-10-18T12:32:14.733Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", size = 333172, upload-time = "2023-09-07T14:03:35.212Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", size = 357255, upload-time = "2023-09-07T14:03:36.447Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068, upload-time = "2023-09-07T14:03:37.779Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244, upload-time = "2023-09-07T14:03:39.223Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500, upload-time = "2023-09-07T14:03:40.858Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950, upload-time = "2023-09-07T14:03:42.896Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527, upload-time = "2023-09-07T14:03:44.552Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489, upload-time = "2023-09-07T14:03:46.594Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080, upload-time = "2023-09-07T14:03:48.204Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051, upload-time = "2023-09-07T14:03:50.348Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172, upload-time = "2023-09-07T14:03:52.395Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023, upload-time = "2023-09-07T14:03:53.96Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871, upload-time = "2024-10-18T12:32:16.688Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784, upload-time = "2024-10-18T12:32:18.459Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905, upload-time = "2024-10-18T12:32:20.192Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467, upload-time = "2024-10-18T12:32:21.774Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169, upload-time = "2023-09-07T14:03:55.404Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253, upload-time = "2023-09-07T14:03:56.643Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28", size = 815693, upload-time = "2024-10-18T12:32:23.824Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f", size = 422489, upload-time = "2024-10-18T12:32:25.641Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", size = 873081, upload-time = "2023-09-07T14:03:57.967Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", size = 446244, upload-time = "2023-09-07T14:03:59.319Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", size = 2906505, upload-time = "2023-09-07T14:04:01.327Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", size = 2944152, upload-time = "2023-09-07T14:04:03.033Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", size = 2919252, upload-time = "2023-09-07T14:04:04.675Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", size = 2845955, upload-time = "2023-09-07T14:04:06.585Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", size = 2914304, upload-time = "2023-09-07T14:04:08.668Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", size = 2814452, upload-time = "2023-09-07T14:04:10.736Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", size = 2938751, upload-time = "2023-09-07T14:04:12.875Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", size = 2933757, upload-time = "2023-09-07T14:04:14.551Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9", size = 2936146, upload-time = "2024-10-18T12:32:27.257Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb", size = 2848055, upload-time = "2024-10-18T12:32:29.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111", size = 3035102, upload-time = "2024-10-18T12:32:31.371Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839", size = 2930029, upload-time = "2024-10-18T12:32:33.293Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", size = 333276, upload-time = "2023-09-07T14:04:16.49Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", size = 357255, upload-time = "2023-09-07T14:04:17.83Z" }, ] [[package]] name = "cachelib" version = "0.13.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1d/69/0b5c1259e12fbcf5c2abe5934b5c0c1294ec0f845e2b4b2a51a91d79a4fb/cachelib-0.13.0.tar.gz", hash = "sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/69/0b5c1259e12fbcf5c2abe5934b5c0c1294ec0f845e2b4b2a51a91d79a4fb/cachelib-0.13.0.tar.gz", hash = "sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48", size = 34418, upload-time = "2024-04-13T14:18:27.782Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9b/42/960fc9896ddeb301716fdd554bab7941c35fb90a1dc7260b77df3366f87f/cachelib-0.13.0-py3-none-any.whl", hash = "sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/42/960fc9896ddeb301716fdd554bab7941c35fb90a1dc7260b77df3366f87f/cachelib-0.13.0-py3-none-any.whl", hash = "sha256:8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516", size = 20914, upload-time = "2024-04-13T14:18:26.361Z" }, ] [[package]] name = "cachetools" version = "5.3.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b3/4d/27a3e6dd09011649ad5210bdf963765bc8fa81a0827a4fc01bafd2705c5b/cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/4d/27a3e6dd09011649ad5210bdf963765bc8fa81a0827a4fc01bafd2705c5b/cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105", size = 26522, upload-time = "2024-02-26T20:33:23.386Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fb/2b/a64c2d25a37aeb921fddb929111413049fc5f8b9a4c1aefaffaafe768d54/cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/2b/a64c2d25a37aeb921fddb929111413049fc5f8b9a4c1aefaffaafe768d54/cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945", size = 9325, upload-time = "2024-02-26T20:33:20.308Z" }, ] [[package]] name = "cbor" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9b/99/01c6a987c920500189eb74a291bd3a388e6c7cf85736bb6b066d9833315e/cbor-1.0.0.tar.gz", hash = "sha256:13225a262ddf5615cbd9fd55a76a0d53069d18b07d2e9f19c39e6acb8609bbb6" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/99/01c6a987c920500189eb74a291bd3a388e6c7cf85736bb6b066d9833315e/cbor-1.0.0.tar.gz", hash = "sha256:13225a262ddf5615cbd9fd55a76a0d53069d18b07d2e9f19c39e6acb8609bbb6", size = 20096, upload-time = "2016-02-09T23:11:12.726Z" } [[package]] name = "cbor2" version = "5.6.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e4/aa/ba55b47d51d27911981a18743b4d3cebfabccbb0598c09801b734cec4184/cbor2-5.6.5.tar.gz", hash = "sha256:b682820677ee1dbba45f7da11898d2720f92e06be36acec290867d5ebf3d7e09" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f1/f2/eada1d3fcaeddbd0f9a25381eed6b150f2858cf615d33ef7c14f9f86218a/cbor2-5.6.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e16c4a87fc999b4926f5c8f6c696b0d251b4745bc40f6c5aee51d69b30b15ca2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/d8/33ec188eeff9c5e2e8ee8e214d15a0edf8fd001eefa01730e27834cfd410/cbor2-5.6.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87026fc838370d69f23ed8572939bd71cea2b3f6c8f8bb8283f573374b4d7f33" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fa/88/9b5fc312f21a10e048c348cead933e9f891cc09a295757957d8a5726641f/cbor2-5.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a88f029522aec5425fc2f941b3df90da7688b6756bd3f0472ab886d21208acbd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bb/41/debb6f35d240caa8078a4a27c03e7eed09737575c564d619e1ee0e829616/cbor2-5.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d15b638539b68aa5d5eacc56099b4543a38b2d2c896055dccf7e83d24b7955" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/ba/5c07b001bb26bd91d96e4b3b720676a909e1acdbaf7d250a47c5d7a5a0f7/cbor2-5.6.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:47261f54a024839ec649b950013c4de5b5f521afe592a2688eebbe22430df1dc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/d5/27eced2cee4725bb876875f26d659063e2b7750d0dfb7572f451b569c4d1/cbor2-5.6.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:559dcf0d897260a9e95e7b43556a62253e84550b77147a1ad4d2c389a2a30192" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/55/bd657fdedb0d046762f9bf567b262b233175995d40302cdaa71d9aadcec8/cbor2-5.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:5b856fda4c50c5bc73ed3664e64211fa4f015970ed7a15a4d6361bd48462feaf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bd/b7/ef045245180510305648fd604244d3bb1ecf1b20de68f42ab5bc20198024/cbor2-5.6.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:863e0983989d56d5071270790e7ed8ddbda88c9e5288efdb759aba2efee670bc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/20/5a9d93f86b1e8fd9d9db33aff39c0e3a8459e0803ec24bd837d8b56d4a1d/cbor2-5.6.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5cff06464b8f4ca6eb9abcba67bda8f8334a058abc01005c8e616728c387ad32" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/1e/2010f6d02dd117df88df64baf3eeca6aa6614cc81bdd6bfabf615889cf1f/cbor2-5.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c7dbcdc59ea7f5a745d3e30ee5e6b6ff5ce7ac244aa3de6786391b10027bb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/84/e177d9bef4749d14f31c513b25e341ac84e403e2ffa2bde562eac9e6184b/cbor2-5.6.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34cf5ab0dc310c3d0196caa6ae062dc09f6c242e2544bea01691fe60c0230596" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/75/ebfdbb281104b46419fe7cb65979de9927b75acebcb6afa0af291f728cd2/cbor2-5.6.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6797b824b26a30794f2b169c0575301ca9b74ae99064e71d16e6ba0c9057de51" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/1e/12d887fb1a8227a16181eeec5d43057e251204626d73e1c20a77046ac1b1/cbor2-5.6.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:73b9647eed1493097db6aad61e03d8f1252080ee041a1755de18000dd2c05f37" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/76/478c12193de9517ce691bb8a3f7c00eafdd6a1bc3f7f23282ecdd99d02ec/cbor2-5.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:6e14a1bf6269d25e02ef1d4008e0ce8880aa271d7c6b4c329dba48645764f60e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/af/84ced14c541451696825b7b8ccbb7668f688372ad8ee74aaca4311e79672/cbor2-5.6.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e25c2aebc9db99af7190e2261168cdde8ed3d639ca06868e4f477cf3a228a8e9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/d6/f63a840c68fed4de67d5441947af2dc695152cc488bb0e57312832fb923a/cbor2-5.6.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fde21ac1cf29336a31615a2c469a9cb03cf0add3ae480672d4d38cda467d07fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/77/ac/5fb79db6e882ec29680f4a974d35c098020a1b4709cad077667a8c3f4676/cbor2-5.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8947c102cac79d049eadbd5e2ffb8189952890df7cbc3ee262bbc2f95b011a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cf/cb/70751377d94112001d46c311b5c40b45f34863dfa78a6bc71b71f40c8c7f/cbor2-5.6.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38886c41bebcd7dca57739439455bce759f1e4c551b511f618b8e9c1295b431b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f1/90/08800367e920aef31b93bd7b0cd6fadcb3a3f2243f4ed77a0d1c76f22b99/cbor2-5.6.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae2b49226224e92851c333b91d83292ec62eba53a19c68a79890ce35f1230d70" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/9c/76b11a5ea7548bccb0dfef3e8fb3ede48bfeb39348f0c217519e0c40d33a/cbor2-5.6.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2764804ffb6553283fc4afb10a280715905a4cea4d6dc7c90d3e89c4a93bc8d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/10/18/3866693a87c90cb12f7942e791d0f03a40ba44887dde7b7fc85319647efe/cbor2-5.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:a3ac50485cf67dfaab170a3e7b527630e93cb0a6af8cdaa403054215dff93adf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/ef/1c4698cac96d792005ef0611832f38eaee477c275ab4b02cbfc4daba7ad3/cbor2-5.6.5-py3-none-any.whl", hash = "sha256:3038523b8fc7de312bb9cdcbbbd599987e64307c4db357cd2030c472a6c7d468" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/aa/ba55b47d51d27911981a18743b4d3cebfabccbb0598c09801b734cec4184/cbor2-5.6.5.tar.gz", hash = "sha256:b682820677ee1dbba45f7da11898d2720f92e06be36acec290867d5ebf3d7e09", size = 100886, upload-time = "2024-10-09T12:26:24.106Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/f2/eada1d3fcaeddbd0f9a25381eed6b150f2858cf615d33ef7c14f9f86218a/cbor2-5.6.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e16c4a87fc999b4926f5c8f6c696b0d251b4745bc40f6c5aee51d69b30b15ca2", size = 66473, upload-time = "2024-10-09T12:25:27.157Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/d8/33ec188eeff9c5e2e8ee8e214d15a0edf8fd001eefa01730e27834cfd410/cbor2-5.6.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87026fc838370d69f23ed8572939bd71cea2b3f6c8f8bb8283f573374b4d7f33", size = 67421, upload-time = "2024-10-09T12:25:28.962Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/88/9b5fc312f21a10e048c348cead933e9f891cc09a295757957d8a5726641f/cbor2-5.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a88f029522aec5425fc2f941b3df90da7688b6756bd3f0472ab886d21208acbd", size = 253856, upload-time = "2024-10-09T12:25:29.968Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/41/debb6f35d240caa8078a4a27c03e7eed09737575c564d619e1ee0e829616/cbor2-5.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d15b638539b68aa5d5eacc56099b4543a38b2d2c896055dccf7e83d24b7955", size = 242074, upload-time = "2024-10-09T12:25:31.585Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/ba/5c07b001bb26bd91d96e4b3b720676a909e1acdbaf7d250a47c5d7a5a0f7/cbor2-5.6.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:47261f54a024839ec649b950013c4de5b5f521afe592a2688eebbe22430df1dc", size = 241723, upload-time = "2024-10-09T12:25:33.264Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/d5/27eced2cee4725bb876875f26d659063e2b7750d0dfb7572f451b569c4d1/cbor2-5.6.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:559dcf0d897260a9e95e7b43556a62253e84550b77147a1ad4d2c389a2a30192", size = 240534, upload-time = "2024-10-09T12:25:34.29Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/55/bd657fdedb0d046762f9bf567b262b233175995d40302cdaa71d9aadcec8/cbor2-5.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:5b856fda4c50c5bc73ed3664e64211fa4f015970ed7a15a4d6361bd48462feaf", size = 66291, upload-time = "2024-10-09T12:25:35.763Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/b7/ef045245180510305648fd604244d3bb1ecf1b20de68f42ab5bc20198024/cbor2-5.6.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:863e0983989d56d5071270790e7ed8ddbda88c9e5288efdb759aba2efee670bc", size = 66452, upload-time = "2024-10-09T12:25:36.676Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/20/5a9d93f86b1e8fd9d9db33aff39c0e3a8459e0803ec24bd837d8b56d4a1d/cbor2-5.6.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5cff06464b8f4ca6eb9abcba67bda8f8334a058abc01005c8e616728c387ad32", size = 67421, upload-time = "2024-10-09T12:25:38.114Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/1e/2010f6d02dd117df88df64baf3eeca6aa6614cc81bdd6bfabf615889cf1f/cbor2-5.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c7dbcdc59ea7f5a745d3e30ee5e6b6ff5ce7ac244aa3de6786391b10027bb3", size = 260756, upload-time = "2024-10-09T12:25:39.657Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/84/e177d9bef4749d14f31c513b25e341ac84e403e2ffa2bde562eac9e6184b/cbor2-5.6.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34cf5ab0dc310c3d0196caa6ae062dc09f6c242e2544bea01691fe60c0230596", size = 249210, upload-time = "2024-10-09T12:25:41.316Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/75/ebfdbb281104b46419fe7cb65979de9927b75acebcb6afa0af291f728cd2/cbor2-5.6.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6797b824b26a30794f2b169c0575301ca9b74ae99064e71d16e6ba0c9057de51", size = 249138, upload-time = "2024-10-09T12:25:42.432Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/1e/12d887fb1a8227a16181eeec5d43057e251204626d73e1c20a77046ac1b1/cbor2-5.6.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:73b9647eed1493097db6aad61e03d8f1252080ee041a1755de18000dd2c05f37", size = 247156, upload-time = "2024-10-09T12:25:43.588Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/76/478c12193de9517ce691bb8a3f7c00eafdd6a1bc3f7f23282ecdd99d02ec/cbor2-5.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:6e14a1bf6269d25e02ef1d4008e0ce8880aa271d7c6b4c329dba48645764f60e", size = 66319, upload-time = "2024-10-09T12:25:44.621Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/af/84ced14c541451696825b7b8ccbb7668f688372ad8ee74aaca4311e79672/cbor2-5.6.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e25c2aebc9db99af7190e2261168cdde8ed3d639ca06868e4f477cf3a228a8e9", size = 67553, upload-time = "2024-10-09T12:25:45.767Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/d6/f63a840c68fed4de67d5441947af2dc695152cc488bb0e57312832fb923a/cbor2-5.6.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fde21ac1cf29336a31615a2c469a9cb03cf0add3ae480672d4d38cda467d07fc", size = 67569, upload-time = "2024-10-09T12:25:46.665Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/ac/5fb79db6e882ec29680f4a974d35c098020a1b4709cad077667a8c3f4676/cbor2-5.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8947c102cac79d049eadbd5e2ffb8189952890df7cbc3ee262bbc2f95b011a9", size = 276610, upload-time = "2024-10-09T12:25:48.14Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/cb/70751377d94112001d46c311b5c40b45f34863dfa78a6bc71b71f40c8c7f/cbor2-5.6.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38886c41bebcd7dca57739439455bce759f1e4c551b511f618b8e9c1295b431b", size = 270004, upload-time = "2024-10-09T12:25:49.769Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/90/08800367e920aef31b93bd7b0cd6fadcb3a3f2243f4ed77a0d1c76f22b99/cbor2-5.6.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae2b49226224e92851c333b91d83292ec62eba53a19c68a79890ce35f1230d70", size = 264913, upload-time = "2024-10-09T12:25:50.92Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/9c/76b11a5ea7548bccb0dfef3e8fb3ede48bfeb39348f0c217519e0c40d33a/cbor2-5.6.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2764804ffb6553283fc4afb10a280715905a4cea4d6dc7c90d3e89c4a93bc8d", size = 266751, upload-time = "2024-10-09T12:25:52.777Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/18/3866693a87c90cb12f7942e791d0f03a40ba44887dde7b7fc85319647efe/cbor2-5.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:a3ac50485cf67dfaab170a3e7b527630e93cb0a6af8cdaa403054215dff93adf", size = 66739, upload-time = "2024-10-09T12:25:54.606Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/ef/1c4698cac96d792005ef0611832f38eaee477c275ab4b02cbfc4daba7ad3/cbor2-5.6.5-py3-none-any.whl", hash = "sha256:3038523b8fc7de312bb9cdcbbbd599987e64307c4db357cd2030c472a6c7d468", size = 23752, upload-time = "2024-10-09T12:26:23.167Z" }, ] [[package]] name = "certifi" version = "2025.7.14" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, ] [[package]] name = "cffi" version = "1.17.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pycparser" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67" }, - { url = "https://mirrors.aliyun.com/pypi/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, ] [[package]] name = "chardet" version = "5.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970" }, ] [[package]] name = "charset-normalizer" version = "3.4.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] [[package]] name = "click" version = "8.2.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, ] [[package]] name = "cn2an" version = "0.5.22" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "proces" }, { name = "setuptools" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a1/5c/f565259e568316e5fde4dba292e2ca9cff0619657e4ec9f254f415543f59/cn2an-0.5.22.tar.gz", hash = "sha256:27ae5b56441d7329ed2ececffa026bfa8fc353dcf1fb0d9146b303b9cce3ac37" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/5c/f565259e568316e5fde4dba292e2ca9cff0619657e4ec9f254f415543f59/cn2an-0.5.22.tar.gz", hash = "sha256:27ae5b56441d7329ed2ececffa026bfa8fc353dcf1fb0d9146b303b9cce3ac37", size = 21399, upload-time = "2023-08-21T11:13:16.535Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/1c/3d/3e04a822b8615904269f7126d8b019ae5c3b5c3c78397ec8bab056b02099/cn2an-0.5.22-py3-none-any.whl", hash = "sha256:cba4c8f305b43da01f50696047cca3116c727424ac62338da6a3426e01454f3e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/3d/3e04a822b8615904269f7126d8b019ae5c3b5c3c78397ec8bab056b02099/cn2an-0.5.22-py3-none-any.whl", hash = "sha256:cba4c8f305b43da01f50696047cca3116c727424ac62338da6a3426e01454f3e", size = 224956, upload-time = "2023-08-21T11:13:14.369Z" }, ] [[package]] @@ -843,7 +843,7 @@ wheels = [ [[package]] name = "cohere" version = "5.6.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "boto3" }, { name = "fastavro" }, @@ -856,18 +856,18 @@ dependencies = [ { name = "types-requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2d/e0/d821fe7b3c0b30893b89a22f4e58d431211156499ca00805568b90aafcf6/cohere-5.6.2.tar.gz", hash = "sha256:6bb901afdfb02f62ad8ed2d82f12d8ea87a6869710f5f880cb89190c4e994805" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/e0/d821fe7b3c0b30893b89a22f4e58d431211156499ca00805568b90aafcf6/cohere-5.6.2.tar.gz", hash = "sha256:6bb901afdfb02f62ad8ed2d82f12d8ea87a6869710f5f880cb89190c4e994805", size = 87357, upload-time = "2024-07-22T14:23:50.446Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b2/a7/0572d6ab1d947bd11aa8fc40fe908635fabc5abf254175943c2228c9d108/cohere-5.6.2-py3-none-any.whl", hash = "sha256:cfecf1343bcaa4091266c5a231fbcb3ccbd80cad05ea093ef80024a117aa3a2f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/a7/0572d6ab1d947bd11aa8fc40fe908635fabc5abf254175943c2228c9d108/cohere-5.6.2-py3-none-any.whl", hash = "sha256:cfecf1343bcaa4091266c5a231fbcb3ccbd80cad05ea093ef80024a117aa3a2f", size = 177409, upload-time = "2024-07-22T14:23:47.907Z" }, ] [[package]] name = "colorama" version = "0.4.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -882,13 +882,13 @@ wheels = [ [[package]] name = "coloredlogs" version = "15.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "humanfriendly" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, ] [[package]] @@ -903,7 +903,7 @@ wheels = [ [[package]] name = "contourpy" version = "1.3.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", @@ -912,50 +912,50 @@ resolution-markers = [ dependencies = [ { name = "numpy", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631" }, - { url = "https://mirrors.aliyun.com/pypi/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73" }, - { url = "https://mirrors.aliyun.com/pypi/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, ] [[package]] name = "contourpy" version = "1.3.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } resolution-markers = [ "python_full_version >= '3.12' and sys_platform == 'darwin'", "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", @@ -967,106 +967,106 @@ resolution-markers = [ dependencies = [ { name = "numpy", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381" }, - { url = "https://mirrors.aliyun.com/pypi/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, ] [[package]] name = "cramjam" version = "2.11.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/14/12/34bf6e840a79130dfd0da7badfb6f7810b8fcfd60e75b0539372667b41b6/cramjam-2.11.0.tar.gz", hash = "sha256:5c82500ed91605c2d9781380b378397012e25127e89d64f460fea6aeac4389b4" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/53/d3/20d0402e4e983b66603117ad3dd3b864a05d7997a830206d3ff9cacef9a2/cramjam-2.11.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d0859c65775e8ebf2cbc084bfd51bd0ffda10266da6f9306451123b89f8e5a63" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f5/a8/a6e2744288938ccd320a5c6f6f3653faa790f933f5edd088c6e5782a2354/cramjam-2.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1d77b9b0aca02a3f6eeeff27fcd315ca5972616c0919ee38e522cce257bcd349" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/29/7961e09a849eea7d8302e7baa6f829dd3ef3faf199cb25ed29b318ae799b/cramjam-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66425bc25b5481359b12a6719b6e7c90ffe76d85d0691f1da7df304bfb8ce45c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7a/60/6665e52f01a8919bf37c43dcf0e03b6dd3866f5c4e95440b357d508ee14e/cramjam-2.11.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bd748d3407ec63e049b3aea1595e218814fccab329b7fb10bb51120a30e9fb7e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/80/79bd84dbeb109e2c6efb74e661b7bd4c3ba393208ebcf69e2ae9454ae80c/cramjam-2.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6d9a23a35b3a105c42a8de60fc2e80281ae6e758f05a3baea0b68eb1ddcb679" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/ef/b43280767ebcde022ba31f1e9902137655a956ae30e920d75630fa67e36e/cramjam-2.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:40a75b95e05e38a2a055b2446f09994ce1139151721659315151d4ad6289bbff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/60/1c/79d522757c494dfd9e9b208b0604cc7e97b481483cc477144f5705a06ab7/cramjam-2.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5d042c376d2025300da37d65192d06a457918b63b31140f697f85fd8e310b29" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c8/70/3bf0670380069b3abd4c6b53f61d3148f4e08935569c08efbeaf7550e87d/cramjam-2.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb148b35ab20c75b19a06c27f05732e2a321adbd86fadc93f9466dbd7b1154a7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/db/7e/4f6ca98a4b474348e965a529b359184785d1119ab7c4c9ec1280b8bea50a/cramjam-2.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee47c220f0f5179ddc923ab91fc9e282c27b29fabc60c433dfe06f08084f798" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/6c/b241511c7ffd5f1da29641429bb0e19b5fbcffafde5ba1bbcbf9394ea456/cramjam-2.11.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0cf1b5a81b21ea175c976c3ab09e00494258f4b49b7995efc86060cced3f0b2e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/14/5c/4ef926c8c3c1bf6da96f9c53450ff334cdb6d0fc1efced0aea97e2090803/cramjam-2.11.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:360c00338ecf48921492455007f904be607fc7818de3d681acbcc542aae2fb36" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/fb/eb2aef7fb2730e56c5a2c9000817ee8fb4a95c92f19cc6e441afed42ec29/cramjam-2.11.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f31fcc0d30dc3f3e94ea6b4d8e1a855071757c6abf6a7b1e284050ab7d4c299c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/80/925a5c668dcee1c6f61775067185c5dc9a63c766d5393e5c60d2af4217a7/cramjam-2.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:033be66fdceb3d63b2c99b257a98380c4ec22c9e4dca54a2bfec3718cd24e184" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/ac/b2819640eef0592a6de7ca832c0d23c69bd1620f765ce88b60dbc8da9ba2/cramjam-2.11.0-cp310-cp310-win32.whl", hash = "sha256:1c6cea67f6000b81f6bd27d14c8a6f62d00336ca7252fd03ee16f6b70eb5c0d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5a/f4/06af04727b9556721049e2127656d727306d275c518e3d97f9ed4cffd0d8/cramjam-2.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:98aa4a351b047b0f7f9e971585982065028adc2c162c5c23c5d5734c5ccc1077" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/89/8001f6a9b6b6e9fa69bec5319789083475d6f26d52aaea209d3ebf939284/cramjam-2.11.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:04cfa39118570e70e920a9b75c733299784b6d269733dbc791d9aaed6edd2615" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/f3/001d00070ca92e5fbe6aacc768e455568b0cde46b0eb944561a4ea132300/cramjam-2.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:66a18f68506290349a256375d7aa2f645b9f7993c10fc4cc211db214e4e61d2b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/35/041a3af01bf3f6158f120070f798546d4383b962b63c35cd91dcbf193e17/cramjam-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50e7d65533857736cd56f6509cf2c4866f28ad84dd15b5bdbf2f8a81e77fa28a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/eb/5358b238808abebd0c949c42635c3751204ca7cf82b29b984abe9f5e33c8/cramjam-2.11.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1f71989668458fc327ac15396db28d92df22f8024bb12963929798b2729d2df5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/79/19dba7c03a27408d8d11b5a7a4a7908459cfd4e6f375b73264dc66517bf6/cramjam-2.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee77ac543f1e2b22af1e8be3ae589f729491b6090582340aacd77d1d757d9569" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a4/ad/40e4b3408501d886d082db465c33971655fe82573c535428e52ab905f4d0/cramjam-2.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad52784120e7e4d8a0b5b0517d185b8bf7f74f5e17272857ddc8951a628d9be1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/6e/c1b60ceb6d7ea6ff8b0bf197520aefe23f878bf2bfb0de65f2b0c2f82cd1/cramjam-2.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b86f8e6d9c1b3f9a75b2af870c93ceee0f1b827cd2507387540e053b35d7459" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9c/ad/32a8d5f4b1e3717787945ec6d71bd1c6e6bccba4b7e903fc0d9d4e4b08c3/cramjam-2.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:320d61938950d95da2371b46c406ec433e7955fae9f396c8e1bf148ffc187d11" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/cd/3b5a662736ea62ff7fa4c4a10a85e050bfdaad375cc53dc80427e8afe41c/cramjam-2.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41eafc8c1653a35a5c7e75ad48138f9f60085cc05cd99d592e5298552d944e9f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/26/8e/1dbcfaaa7a702ee82ee683ec3a81656934dd7e04a7bc4ee854033686f98a/cramjam-2.11.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03a7316c6bf763dfa34279335b27702321da44c455a64de58112968c0818ec4a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/62/f11709bfdce74af79a88b410dcb76dedc97612166e759136931bf63cfd7b/cramjam-2.11.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:244c2ed8bd7ccbb294a2abe7ca6498db7e89d7eb5e744691dc511a7dc82e65ca" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/6d/3b98b61841a5376d9a9b8468ae58753a8e6cf22be9534a0fa5af4d8621cc/cramjam-2.11.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:405f8790bad36ce0b4bbdb964ad51507bfc7942c78447f25cb828b870a1d86a0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/72/bd5db5c49dbebc8b002f1c4983101b28d2e7fc9419753db1c31ec22b03ef/cramjam-2.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6b1b751a5411032b08fb3ac556160229ca01c6bbe4757bb3a9a40b951ebaac23" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/32/203c57acdb6eea727e7078b2219984e64ed4ad043c996ed56321301ba167/cramjam-2.11.0-cp311-cp311-win32.whl", hash = "sha256:5251585608778b9ac8effed544933df7ad85b4ba21ee9738b551f17798b215ac" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/bd/102d6deb87a8524ac11cddcd31a7612b8f20bf9b473c3c645045e3b957c7/cramjam-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:dca88bc8b68ce6d35dafd8c4d5d59a238a56c43fa02b74c2ce5f9dfb0d1ccb46" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/0d/7c84c913a5fae85b773a9dcf8874390f9d68ba0fcc6630efa7ff1541b950/cramjam-2.11.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dba5c14b8b4f73ea1e65720f5a3fe4280c1d27761238378be8274135c60bbc6e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/cc/4f6d185d8a744776f53035e72831ff8eefc2354f46ab836f4bd3c4f6c138/cramjam-2.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:11eb40722b3fcf3e6890fba46c711bf60f8dc26360a24876c85e52d76c33b25b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/a8/626c76263085c6d5ded0e71823b411e9522bfc93ba6cc59855a5869296e7/cramjam-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aeb26e2898994b6e8319f19a4d37c481512acdcc6d30e1b5ecc9d8ec57e835cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/52/0851a16a62447532e30ba95a80e638926fdea869a34b4b5b9d0a020083ba/cramjam-2.11.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f8d82081ed7d8fe52c982bd1f06e4c7631a73fe1fb6d4b3b3f2404f87dc40fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/76/122e444f59dbc216451d8e3d8282c9665dc79eaf822f5f1470066be1b695/cramjam-2.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:092a3ec26e0a679305018380e4f652eae1b6dfe3fc3b154ee76aa6b92221a17c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a3/bc/3a0189aef1af2b29632c039c19a7a1b752bc21a4053582a5464183a0ad3d/cramjam-2.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:529d6d667c65fd105d10bd83d1cd3f9869f8fd6c66efac9415c1812281196a92" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/80/8a6343b13778ce52d94bb8d5365a30c3aa951276b1857201fe79d7e2ad25/cramjam-2.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:555eb9c90c450e0f76e27d9ff064e64a8b8c6478ab1a5594c91b7bc5c82fd9f0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/6b/cd1778a207c29eda10791e3dfa018b588001928086e179fc71254793c625/cramjam-2.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5edf4c9e32493035b514cf2ba0c969d81ccb31de63bd05490cc8bfe3b431674e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dc/f0/5c2a5cd5711032f3b191ca50cb786c17689b4a9255f9f768866e6c9f04d9/cramjam-2.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2fe41f48c4d58d923803383b0737f048918b5a0d10390de9628bb6272b107" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/8b/b363a5fb2c3347504fe9a64f8d0f1e276844f0e532aa7162c061cd1ffee4/cramjam-2.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9ca14cf1cabdb0b77d606db1bb9e9ca593b1dbd421fcaf251ec9a5431ec449f3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/78/7b/d83dad46adb6c988a74361f81ad9c5c22642be53ad88616a19baedd06243/cramjam-2.11.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:309e95bf898829476bccf4fd2c358ec00e7ff73a12f95a3cdeeba4bb1d3683d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/be/60d9be4cb33d8740a4aa94c7513f2ef3c4eba4fd13536f086facbafade71/cramjam-2.11.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:86dca35d2f15ef22922411496c220f3c9e315d5512f316fe417461971cc1648d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/b0/4a595f01a243aec8ad272b160b161c44351190c35d98d7787919d962e9e5/cramjam-2.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:193c6488bd2f514cbc0bef5c18fad61a5f9c8d059dd56edf773b3b37f0e85496" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/47/7776659aaa677046b77f527106e53ddd47373416d8fcdb1e1a881ec5dc06/cramjam-2.11.0-cp312-cp312-win32.whl", hash = "sha256:514e2c008a8b4fa823122ca3ecab896eac41d9aa0f5fc881bd6264486c204e32" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/b1/d53002729cfd94c5844ddfaf1233c86d29f2dbfc1b764a6562c41c044199/cramjam-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:53fed080476d5f6ad7505883ec5d1ec28ba36c2273db3b3e92d7224fe5e463db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/8f/82e35ec3c5387f1864f46b3c24bce89a07af8bb3ef242ae47281db2c1848/cramjam-2.11.0-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:37bed927abc4a7ae2d2669baa3675e21904d8a038ed8e4313326ea7b3be62b2b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/4e/0c821918080a32ba1e52c040e12dd02dada67728f07305c5f778b808a807/cramjam-2.11.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:50e4a58635fa8c6897d84847d6e065eb69f92811670fc5e9f2d9e3b6279a02b6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/fd/848d077bf6abc4ce84273d8e3f3a70d61a2240519a339462f699d8acf829/cramjam-2.11.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d1ba626dd5f81f7f09bbf59f70b534e2b75e0d6582b056b7bd31b397f1c13e9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9d/1c/899818999bbdb59c601756b413e87d37fd65875d1315346c10e367bb3505/cramjam-2.11.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c71e140d5eb3145d61d59d0be0bf72f07cc4cf4b32cb136b09f712a3b1040f5f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/26/c2813c5422c43b3dcd8b6645bc359f08870737c44325ee4accc18f24eee0/cramjam-2.11.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a6ed7926a5cca28edebad7d0fedd2ad492710ae3524d25fc59a2b20546d9ce1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/4f/af984f8d7f963f0301812cdd620ddcfd8276461ed7a786c0f89e82b14739/cramjam-2.11.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5eb4ed3cea945b164b0513fd491884993acac2153a27b93a84019c522e8eda82" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/da/b3301962ccd6fce9fefa1ecd8ea479edaeaa38fadb1f34d5391d2587216a/cramjam-2.11.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:52d5db3369f95b27b9f3c14d067acb0b183333613363ed34268c9e04560f997f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b6/c2/410ddb8ad4b9dfb129284666293cb6559479645da560f7077dc19d6bee9e/cramjam-2.11.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4820516366d455b549a44d0e2210ee7c4575882dda677564ce79092588321d54" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/99/f68a443c64f7ce7aff5bed369b0aa5b2fac668fa3dfd441837e316e97a1f/cramjam-2.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d9e5db525dc0a950a825202f84ee68d89a072479e07da98795a3469df942d301" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/02/0ff358ab773def1ee3383587906c453d289953171e9c92db84fdd01bf172/cramjam-2.11.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62ab4971199b2270005359cdc379bc5736071dc7c9a228581c5122d9ffaac50c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/31/3298e15f87c9cf2aabdbdd90b153d8644cf989cb42a45d68a1b71e1f7aaf/cramjam-2.11.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24758375cc5414d3035ca967ebb800e8f24604ececcba3c67d6f0218201ebf2d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/90/20d1747255f1ee69a412e319da51ea594c18cca195e7a4d4c713f045eff5/cramjam-2.11.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6c2eea545fef1065c7dd4eda991666fd9c783fbc1d226592ccca8d8891c02f23" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/12/34bf6e840a79130dfd0da7badfb6f7810b8fcfd60e75b0539372667b41b6/cramjam-2.11.0.tar.gz", hash = "sha256:5c82500ed91605c2d9781380b378397012e25127e89d64f460fea6aeac4389b4", size = 99100, upload-time = "2025-07-27T21:25:07.559Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/d3/20d0402e4e983b66603117ad3dd3b864a05d7997a830206d3ff9cacef9a2/cramjam-2.11.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d0859c65775e8ebf2cbc084bfd51bd0ffda10266da6f9306451123b89f8e5a63", size = 3558999, upload-time = "2025-07-27T21:21:34.105Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/a8/a6e2744288938ccd320a5c6f6f3653faa790f933f5edd088c6e5782a2354/cramjam-2.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1d77b9b0aca02a3f6eeeff27fcd315ca5972616c0919ee38e522cce257bcd349", size = 1861558, upload-time = "2025-07-27T21:21:36.624Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/29/7961e09a849eea7d8302e7baa6f829dd3ef3faf199cb25ed29b318ae799b/cramjam-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66425bc25b5481359b12a6719b6e7c90ffe76d85d0691f1da7df304bfb8ce45c", size = 1699431, upload-time = "2025-07-27T21:21:38.396Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/60/6665e52f01a8919bf37c43dcf0e03b6dd3866f5c4e95440b357d508ee14e/cramjam-2.11.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bd748d3407ec63e049b3aea1595e218814fccab329b7fb10bb51120a30e9fb7e", size = 2025262, upload-time = "2025-07-27T21:21:40.417Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/80/79bd84dbeb109e2c6efb74e661b7bd4c3ba393208ebcf69e2ae9454ae80c/cramjam-2.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6d9a23a35b3a105c42a8de60fc2e80281ae6e758f05a3baea0b68eb1ddcb679", size = 1766177, upload-time = "2025-07-27T21:21:42.224Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/ef/b43280767ebcde022ba31f1e9902137655a956ae30e920d75630fa67e36e/cramjam-2.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:40a75b95e05e38a2a055b2446f09994ce1139151721659315151d4ad6289bbff", size = 1854031, upload-time = "2025-07-27T21:21:43.651Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/1c/79d522757c494dfd9e9b208b0604cc7e97b481483cc477144f5705a06ab7/cramjam-2.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5d042c376d2025300da37d65192d06a457918b63b31140f697f85fd8e310b29", size = 2035812, upload-time = "2025-07-27T21:21:45.473Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/70/3bf0670380069b3abd4c6b53f61d3148f4e08935569c08efbeaf7550e87d/cramjam-2.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb148b35ab20c75b19a06c27f05732e2a321adbd86fadc93f9466dbd7b1154a7", size = 2067661, upload-time = "2025-07-27T21:21:47.901Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/7e/4f6ca98a4b474348e965a529b359184785d1119ab7c4c9ec1280b8bea50a/cramjam-2.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee47c220f0f5179ddc923ab91fc9e282c27b29fabc60c433dfe06f08084f798", size = 1981523, upload-time = "2025-07-27T21:21:49.704Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/6c/b241511c7ffd5f1da29641429bb0e19b5fbcffafde5ba1bbcbf9394ea456/cramjam-2.11.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0cf1b5a81b21ea175c976c3ab09e00494258f4b49b7995efc86060cced3f0b2e", size = 2034251, upload-time = "2025-07-27T21:21:51.252Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/5c/4ef926c8c3c1bf6da96f9c53450ff334cdb6d0fc1efced0aea97e2090803/cramjam-2.11.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:360c00338ecf48921492455007f904be607fc7818de3d681acbcc542aae2fb36", size = 2155322, upload-time = "2025-07-27T21:21:53.348Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/fb/eb2aef7fb2730e56c5a2c9000817ee8fb4a95c92f19cc6e441afed42ec29/cramjam-2.11.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f31fcc0d30dc3f3e94ea6b4d8e1a855071757c6abf6a7b1e284050ab7d4c299c", size = 2169094, upload-time = "2025-07-27T21:21:55.187Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/80/925a5c668dcee1c6f61775067185c5dc9a63c766d5393e5c60d2af4217a7/cramjam-2.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:033be66fdceb3d63b2c99b257a98380c4ec22c9e4dca54a2bfec3718cd24e184", size = 2159089, upload-time = "2025-07-27T21:21:57.118Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/ac/b2819640eef0592a6de7ca832c0d23c69bd1620f765ce88b60dbc8da9ba2/cramjam-2.11.0-cp310-cp310-win32.whl", hash = "sha256:1c6cea67f6000b81f6bd27d14c8a6f62d00336ca7252fd03ee16f6b70eb5c0d2", size = 1605046, upload-time = "2025-07-27T21:21:58.617Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/f4/06af04727b9556721049e2127656d727306d275c518e3d97f9ed4cffd0d8/cramjam-2.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:98aa4a351b047b0f7f9e971585982065028adc2c162c5c23c5d5734c5ccc1077", size = 1710647, upload-time = "2025-07-27T21:22:00.279Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/89/8001f6a9b6b6e9fa69bec5319789083475d6f26d52aaea209d3ebf939284/cramjam-2.11.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:04cfa39118570e70e920a9b75c733299784b6d269733dbc791d9aaed6edd2615", size = 3559272, upload-time = "2025-07-27T21:22:01.988Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/f3/001d00070ca92e5fbe6aacc768e455568b0cde46b0eb944561a4ea132300/cramjam-2.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:66a18f68506290349a256375d7aa2f645b9f7993c10fc4cc211db214e4e61d2b", size = 1861743, upload-time = "2025-07-27T21:22:03.754Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/35/041a3af01bf3f6158f120070f798546d4383b962b63c35cd91dcbf193e17/cramjam-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50e7d65533857736cd56f6509cf2c4866f28ad84dd15b5bdbf2f8a81e77fa28a", size = 1699631, upload-time = "2025-07-27T21:22:05.192Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/eb/5358b238808abebd0c949c42635c3751204ca7cf82b29b984abe9f5e33c8/cramjam-2.11.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1f71989668458fc327ac15396db28d92df22f8024bb12963929798b2729d2df5", size = 2025603, upload-time = "2025-07-27T21:22:06.726Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/79/19dba7c03a27408d8d11b5a7a4a7908459cfd4e6f375b73264dc66517bf6/cramjam-2.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee77ac543f1e2b22af1e8be3ae589f729491b6090582340aacd77d1d757d9569", size = 1766283, upload-time = "2025-07-27T21:22:08.568Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/ad/40e4b3408501d886d082db465c33971655fe82573c535428e52ab905f4d0/cramjam-2.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad52784120e7e4d8a0b5b0517d185b8bf7f74f5e17272857ddc8951a628d9be1", size = 1854407, upload-time = "2025-07-27T21:22:10.518Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/6e/c1b60ceb6d7ea6ff8b0bf197520aefe23f878bf2bfb0de65f2b0c2f82cd1/cramjam-2.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b86f8e6d9c1b3f9a75b2af870c93ceee0f1b827cd2507387540e053b35d7459", size = 2035793, upload-time = "2025-07-27T21:22:12.504Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/ad/32a8d5f4b1e3717787945ec6d71bd1c6e6bccba4b7e903fc0d9d4e4b08c3/cramjam-2.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:320d61938950d95da2371b46c406ec433e7955fae9f396c8e1bf148ffc187d11", size = 2067499, upload-time = "2025-07-27T21:22:14.067Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/cd/3b5a662736ea62ff7fa4c4a10a85e050bfdaad375cc53dc80427e8afe41c/cramjam-2.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41eafc8c1653a35a5c7e75ad48138f9f60085cc05cd99d592e5298552d944e9f", size = 1981853, upload-time = "2025-07-27T21:22:15.908Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/8e/1dbcfaaa7a702ee82ee683ec3a81656934dd7e04a7bc4ee854033686f98a/cramjam-2.11.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03a7316c6bf763dfa34279335b27702321da44c455a64de58112968c0818ec4a", size = 2034514, upload-time = "2025-07-27T21:22:17.352Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/62/f11709bfdce74af79a88b410dcb76dedc97612166e759136931bf63cfd7b/cramjam-2.11.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:244c2ed8bd7ccbb294a2abe7ca6498db7e89d7eb5e744691dc511a7dc82e65ca", size = 2155343, upload-time = "2025-07-27T21:22:18.854Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/6d/3b98b61841a5376d9a9b8468ae58753a8e6cf22be9534a0fa5af4d8621cc/cramjam-2.11.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:405f8790bad36ce0b4bbdb964ad51507bfc7942c78447f25cb828b870a1d86a0", size = 2169367, upload-time = "2025-07-27T21:22:20.389Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/72/bd5db5c49dbebc8b002f1c4983101b28d2e7fc9419753db1c31ec22b03ef/cramjam-2.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6b1b751a5411032b08fb3ac556160229ca01c6bbe4757bb3a9a40b951ebaac23", size = 2159334, upload-time = "2025-07-27T21:22:22.254Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/32/203c57acdb6eea727e7078b2219984e64ed4ad043c996ed56321301ba167/cramjam-2.11.0-cp311-cp311-win32.whl", hash = "sha256:5251585608778b9ac8effed544933df7ad85b4ba21ee9738b551f17798b215ac", size = 1605313, upload-time = "2025-07-27T21:22:24.126Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/bd/102d6deb87a8524ac11cddcd31a7612b8f20bf9b473c3c645045e3b957c7/cramjam-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:dca88bc8b68ce6d35dafd8c4d5d59a238a56c43fa02b74c2ce5f9dfb0d1ccb46", size = 1710991, upload-time = "2025-07-27T21:22:25.661Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/0d/7c84c913a5fae85b773a9dcf8874390f9d68ba0fcc6630efa7ff1541b950/cramjam-2.11.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dba5c14b8b4f73ea1e65720f5a3fe4280c1d27761238378be8274135c60bbc6e", size = 3553368, upload-time = "2025-07-27T21:22:27.162Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/cc/4f6d185d8a744776f53035e72831ff8eefc2354f46ab836f4bd3c4f6c138/cramjam-2.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:11eb40722b3fcf3e6890fba46c711bf60f8dc26360a24876c85e52d76c33b25b", size = 1860014, upload-time = "2025-07-27T21:22:28.738Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/a8/626c76263085c6d5ded0e71823b411e9522bfc93ba6cc59855a5869296e7/cramjam-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aeb26e2898994b6e8319f19a4d37c481512acdcc6d30e1b5ecc9d8ec57e835cb", size = 1693512, upload-time = "2025-07-27T21:22:30.999Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/52/0851a16a62447532e30ba95a80e638926fdea869a34b4b5b9d0a020083ba/cramjam-2.11.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f8d82081ed7d8fe52c982bd1f06e4c7631a73fe1fb6d4b3b3f2404f87dc40fe", size = 2025285, upload-time = "2025-07-27T21:22:32.954Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/76/122e444f59dbc216451d8e3d8282c9665dc79eaf822f5f1470066be1b695/cramjam-2.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:092a3ec26e0a679305018380e4f652eae1b6dfe3fc3b154ee76aa6b92221a17c", size = 1761327, upload-time = "2025-07-27T21:22:34.484Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/bc/3a0189aef1af2b29632c039c19a7a1b752bc21a4053582a5464183a0ad3d/cramjam-2.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:529d6d667c65fd105d10bd83d1cd3f9869f8fd6c66efac9415c1812281196a92", size = 1854075, upload-time = "2025-07-27T21:22:36.157Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/80/8a6343b13778ce52d94bb8d5365a30c3aa951276b1857201fe79d7e2ad25/cramjam-2.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:555eb9c90c450e0f76e27d9ff064e64a8b8c6478ab1a5594c91b7bc5c82fd9f0", size = 2032710, upload-time = "2025-07-27T21:22:38.17Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/6b/cd1778a207c29eda10791e3dfa018b588001928086e179fc71254793c625/cramjam-2.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5edf4c9e32493035b514cf2ba0c969d81ccb31de63bd05490cc8bfe3b431674e", size = 2068353, upload-time = "2025-07-27T21:22:39.615Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/f0/5c2a5cd5711032f3b191ca50cb786c17689b4a9255f9f768866e6c9f04d9/cramjam-2.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2fe41f48c4d58d923803383b0737f048918b5a0d10390de9628bb6272b107", size = 1978104, upload-time = "2025-07-27T21:22:41.106Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/8b/b363a5fb2c3347504fe9a64f8d0f1e276844f0e532aa7162c061cd1ffee4/cramjam-2.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9ca14cf1cabdb0b77d606db1bb9e9ca593b1dbd421fcaf251ec9a5431ec449f3", size = 2030779, upload-time = "2025-07-27T21:22:42.969Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/7b/d83dad46adb6c988a74361f81ad9c5c22642be53ad88616a19baedd06243/cramjam-2.11.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:309e95bf898829476bccf4fd2c358ec00e7ff73a12f95a3cdeeba4bb1d3683d5", size = 2155297, upload-time = "2025-07-27T21:22:44.6Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/be/60d9be4cb33d8740a4aa94c7513f2ef3c4eba4fd13536f086facbafade71/cramjam-2.11.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:86dca35d2f15ef22922411496c220f3c9e315d5512f316fe417461971cc1648d", size = 2169255, upload-time = "2025-07-27T21:22:46.534Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/b0/4a595f01a243aec8ad272b160b161c44351190c35d98d7787919d962e9e5/cramjam-2.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:193c6488bd2f514cbc0bef5c18fad61a5f9c8d059dd56edf773b3b37f0e85496", size = 2155651, upload-time = "2025-07-27T21:22:48.46Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/47/7776659aaa677046b77f527106e53ddd47373416d8fcdb1e1a881ec5dc06/cramjam-2.11.0-cp312-cp312-win32.whl", hash = "sha256:514e2c008a8b4fa823122ca3ecab896eac41d9aa0f5fc881bd6264486c204e32", size = 1603568, upload-time = "2025-07-27T21:22:50.084Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/b1/d53002729cfd94c5844ddfaf1233c86d29f2dbfc1b764a6562c41c044199/cramjam-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:53fed080476d5f6ad7505883ec5d1ec28ba36c2273db3b3e92d7224fe5e463db", size = 1709287, upload-time = "2025-07-27T21:22:51.534Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/8f/82e35ec3c5387f1864f46b3c24bce89a07af8bb3ef242ae47281db2c1848/cramjam-2.11.0-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:37bed927abc4a7ae2d2669baa3675e21904d8a038ed8e4313326ea7b3be62b2b", size = 3573104, upload-time = "2025-07-27T21:24:40.069Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/4e/0c821918080a32ba1e52c040e12dd02dada67728f07305c5f778b808a807/cramjam-2.11.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:50e4a58635fa8c6897d84847d6e065eb69f92811670fc5e9f2d9e3b6279a02b6", size = 1873441, upload-time = "2025-07-27T21:24:42.333Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/fd/848d077bf6abc4ce84273d8e3f3a70d61a2240519a339462f699d8acf829/cramjam-2.11.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d1ba626dd5f81f7f09bbf59f70b534e2b75e0d6582b056b7bd31b397f1c13e9", size = 1702589, upload-time = "2025-07-27T21:24:44.305Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/1c/899818999bbdb59c601756b413e87d37fd65875d1315346c10e367bb3505/cramjam-2.11.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c71e140d5eb3145d61d59d0be0bf72f07cc4cf4b32cb136b09f712a3b1040f5f", size = 1773646, upload-time = "2025-07-27T21:24:46.495Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/26/c2813c5422c43b3dcd8b6645bc359f08870737c44325ee4accc18f24eee0/cramjam-2.11.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a6ed7926a5cca28edebad7d0fedd2ad492710ae3524d25fc59a2b20546d9ce1", size = 1994179, upload-time = "2025-07-27T21:24:49.131Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/4f/af984f8d7f963f0301812cdd620ddcfd8276461ed7a786c0f89e82b14739/cramjam-2.11.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5eb4ed3cea945b164b0513fd491884993acac2153a27b93a84019c522e8eda82", size = 1714790, upload-time = "2025-07-27T21:24:51.045Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/da/b3301962ccd6fce9fefa1ecd8ea479edaeaa38fadb1f34d5391d2587216a/cramjam-2.11.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:52d5db3369f95b27b9f3c14d067acb0b183333613363ed34268c9e04560f997f", size = 3573546, upload-time = "2025-07-27T21:24:52.944Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/c2/410ddb8ad4b9dfb129284666293cb6559479645da560f7077dc19d6bee9e/cramjam-2.11.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4820516366d455b549a44d0e2210ee7c4575882dda677564ce79092588321d54", size = 1873654, upload-time = "2025-07-27T21:24:54.958Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/99/f68a443c64f7ce7aff5bed369b0aa5b2fac668fa3dfd441837e316e97a1f/cramjam-2.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d9e5db525dc0a950a825202f84ee68d89a072479e07da98795a3469df942d301", size = 1702846, upload-time = "2025-07-27T21:24:57.124Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/02/0ff358ab773def1ee3383587906c453d289953171e9c92db84fdd01bf172/cramjam-2.11.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62ab4971199b2270005359cdc379bc5736071dc7c9a228581c5122d9ffaac50c", size = 1773683, upload-time = "2025-07-27T21:24:59.28Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/31/3298e15f87c9cf2aabdbdd90b153d8644cf989cb42a45d68a1b71e1f7aaf/cramjam-2.11.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24758375cc5414d3035ca967ebb800e8f24604ececcba3c67d6f0218201ebf2d", size = 1994136, upload-time = "2025-07-27T21:25:01.565Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/90/20d1747255f1ee69a412e319da51ea594c18cca195e7a4d4c713f045eff5/cramjam-2.11.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6c2eea545fef1065c7dd4eda991666fd9c783fbc1d226592ccca8d8891c02f23", size = 1714982, upload-time = "2025-07-27T21:25:05.79Z" }, ] [[package]] name = "crawl4ai" version = "0.3.745" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiofiles" }, { name = "aiosqlite" }, @@ -1085,113 +1085,113 @@ dependencies = [ { name = "tf-playwright-stealth" }, { name = "xxhash" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/02/5a/919e64ff2977d7aa1b2cda4d45f16ff8996cd2c2dc1f55936fb6cd214222/crawl4ai-0.3.745.tar.gz", hash = "sha256:990396d57e10ae7ccabf35c34a317dbd8c59a3ceca475eac75320a8808334438" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/5a/919e64ff2977d7aa1b2cda4d45f16ff8996cd2c2dc1f55936fb6cd214222/crawl4ai-0.3.745.tar.gz", hash = "sha256:990396d57e10ae7ccabf35c34a317dbd8c59a3ceca475eac75320a8808334438", size = 117277, upload-time = "2024-11-28T13:46:00.285Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ed/7e/ebe351a457140330b20b6d8289b8f243b21de6e6bce505cd15b230a83bcb/Crawl4AI-0.3.745-py3-none-any.whl", hash = "sha256:763e6aba80959e60e1fe70cb9d954a4cf257eb230af30f51fcd99ff641a7a88d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/7e/ebe351a457140330b20b6d8289b8f243b21de6e6bce505cd15b230a83bcb/Crawl4AI-0.3.745-py3-none-any.whl", hash = "sha256:763e6aba80959e60e1fe70cb9d954a4cf257eb230af30f51fcd99ff641a7a88d", size = 121997, upload-time = "2024-11-28T13:45:57.999Z" }, ] [[package]] name = "cryptography" version = "45.0.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/1e/49527ac611af559665f71cbb8f92b332b5ec9c6fbc4e88b0f8e92f5e85df/cryptography-45.0.5.tar.gz", hash = "sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f0/fb/09e28bc0c46d2c547085e60897fea96310574c70fb21cd58a730a45f3403/cryptography-45.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/05/2194432935e29b91fb649f6149c1a4f9e6d3d9fc880919f4ad1bcc22641e/cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/8b/9ef5da82350175e32de245646b1884fc01124f53eb31164c77f95a08d682/cryptography-45.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/e1/c809f398adde1994ee53438912192d92a1d0fc0f2d7582659d9ef4c28b0c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/8b/07eb6bd5acff58406c5e806eff34a124936f41a4fb52909ffa4d00815f8c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/ef/3333295ed58d900a13c92806b67e62f27876845a9a908c939f040887cca9/cryptography-45.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/9d/44080674dee514dbb82b21d6fa5d1055368f208304e2ab1828d85c9de8f4/cryptography-45.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/d8/0749f7d39f53f8258e5c18a93131919ac465ee1f9dccaf1b3f420235e0b5/cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/d7/92acac187387bf08902b0bf0699816f08553927bdd6ba3654da0010289b4/cryptography-45.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/c2/840e0710da5106a7c3d4153c7215b2736151bba60bf4491bdb421df5056d/cryptography-45.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/92/cc723dd6d71e9747a887b94eb3827825c6c24b9e6ce2bb33b847d31d5eaa/cryptography-45.0.5-cp311-abi3-win32.whl", hash = "sha256:926c3ea71a6043921050eaa639137e13dbe7b4ab25800932a8498364fc1abec9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/10/197da38a5911a48dd5389c043de4aec4b3c94cb836299b01253940788d78/cryptography-45.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:b85980d1e345fe769cfc57c57db2b59cff5464ee0c045d52c0df087e926fbe63" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fe/2b/160ce8c2765e7a481ce57d55eba1546148583e7b6f85514472b1d151711d/cryptography-45.0.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3562c2f23c612f2e4a6964a61d942f891d29ee320edb62ff48ffb99f3de9ae8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/e7/2187be2f871c0221a81f55ee3105d3cf3e273c0a0853651d7011eada0d7e/cryptography-45.0.5-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/cf/84210c447c06104e6be9122661159ad4ce7a8190011669afceeaea150524/cryptography-45.0.5-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3e/6a/cb8b5c8bb82fafffa23aeff8d3a39822593cee6e2f16c5ca5c2ecca344f7/cryptography-45.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/f7/36d2d69df69c94cbb2473871926daf0f01ad8e00fe3986ac3c1e8c4ca4b3/cryptography-45.0.5-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135" }, - { url = "https://mirrors.aliyun.com/pypi/packages/82/c7/f0ea40f016de72f81288e9fe8d1f6748036cb5ba6118774317a3ffc6022d/cryptography-45.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/ae/94b504dc1a3cdf642d710407c62e86296f7da9e66f27ab12a1ee6fdf005b/cryptography-45.0.5-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/2b/aaf0adb845d5dabb43480f18f7ca72e94f92c280aa983ddbd0bcd6ecd037/cryptography-45.0.5-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/e4/f17e02066de63e0100a3a01b56f8f1016973a1d67551beaf585157a86b3f/cryptography-45.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/2e/e2dbd629481b499b14516eed933f3276eb3239f7cee2dcfa4ee6b44d4711/cryptography-45.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/ea/a78a0c38f4c8736287b71c2ea3799d173d5ce778c7d6e3c163a95a05ad2a/cryptography-45.0.5-cp37-abi3-win32.whl", hash = "sha256:1e1da5accc0c750056c556a93c3e9cb828970206c68867712ca5805e46dc806f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/b3/28ac139109d9005ad3f6b6f8976ffede6706a6478e21c889ce36c840918e/cryptography-45.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/8b/34394337abe4566848a2bd49b26bcd4b07fd466afd3e8cce4cb79a390869/cryptography-45.0.5-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:206210d03c1193f4e1ff681d22885181d47efa1ab3018766a7b32a7b3d6e6afd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8b/5d/a19441c1e89afb0f173ac13178606ca6fab0d3bd3ebc29e9ed1318b507fc/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c648025b6840fe62e57107e0a25f604db740e728bd67da4f6f060f03017d5097" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/db/daceb259982a3c2da4e619f45b5bfdec0e922a23de213b2636e78ef0919b/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b8fa8b0a35a9982a3c60ec79905ba5bb090fc0b9addcfd3dc2dd04267e45f25e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/35/5d06ad06402fc522c8bf7eab73422d05e789b4e38fe3206a85e3d6966c11/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:14d96584701a887763384f3c47f0ca7c1cce322aa1c31172680eb596b890ec30" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/79/020a5413347e44c382ef1f7f7e7a66817cd6273e3e6b5a72d18177b08b2f/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57c816dfbd1659a367831baca4b775b2a5b43c003daf52e9d57e1d30bc2e1b0e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/c5/c0e07d84a9a2a8a0ed4f865e58f37c71af3eab7d5e094ff1b21f3f3af3bc/cryptography-45.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b9e38e0a83cd51e07f5a48ff9691cae95a79bea28fe4ded168a8e5c6c77e819d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/71/9bdbcfd58d6ff5084687fe722c58ac718ebedbc98b9f8f93781354e6d286/cryptography-45.0.5-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8c4a6ff8a30e9e3d38ac0539e9a9e02540ab3f827a3394f8852432f6b0ea152e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/63/83516cfb87f4a8756eaa4203f93b283fda23d210fc14e1e594bd5f20edb6/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/11/d2823d2a5a0bd5802b3565437add16f5c8ce1f0778bf3822f89ad2740a38/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/38/6bf177ca6bce4fe14704ab3e93627c5b0ca05242261a2e43ef3168472540/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/6a/69fc67e5266bff68a91bcb81dff8fb0aba4d79a78521a08812048913e16f/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/34/31a1604c9a9ade0fdab61eb48570e09a796f4d9836121266447b0eaf7feb/cryptography-45.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e357286c1b76403dd384d938f93c46b2b058ed4dfcdce64a770f0537ed3feb6f" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/1e/49527ac611af559665f71cbb8f92b332b5ec9c6fbc4e88b0f8e92f5e85df/cryptography-45.0.5.tar.gz", hash = "sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a", size = 744903, upload-time = "2025-07-02T13:06:25.941Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/fb/09e28bc0c46d2c547085e60897fea96310574c70fb21cd58a730a45f3403/cryptography-45.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8", size = 7043092, upload-time = "2025-07-02T13:05:01.514Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/05/2194432935e29b91fb649f6149c1a4f9e6d3d9fc880919f4ad1bcc22641e/cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d", size = 4205926, upload-time = "2025-07-02T13:05:04.741Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/8b/9ef5da82350175e32de245646b1884fc01124f53eb31164c77f95a08d682/cryptography-45.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5", size = 4429235, upload-time = "2025-07-02T13:05:07.084Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/e1/c809f398adde1994ee53438912192d92a1d0fc0f2d7582659d9ef4c28b0c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57", size = 4209785, upload-time = "2025-07-02T13:05:09.321Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/8b/07eb6bd5acff58406c5e806eff34a124936f41a4fb52909ffa4d00815f8c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0", size = 3893050, upload-time = "2025-07-02T13:05:11.069Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/ef/3333295ed58d900a13c92806b67e62f27876845a9a908c939f040887cca9/cryptography-45.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d", size = 4457379, upload-time = "2025-07-02T13:05:13.32Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/9d/44080674dee514dbb82b21d6fa5d1055368f208304e2ab1828d85c9de8f4/cryptography-45.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9", size = 4209355, upload-time = "2025-07-02T13:05:15.017Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/d8/0749f7d39f53f8258e5c18a93131919ac465ee1f9dccaf1b3f420235e0b5/cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27", size = 4456087, upload-time = "2025-07-02T13:05:16.945Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/d7/92acac187387bf08902b0bf0699816f08553927bdd6ba3654da0010289b4/cryptography-45.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e", size = 4332873, upload-time = "2025-07-02T13:05:18.743Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/c2/840e0710da5106a7c3d4153c7215b2736151bba60bf4491bdb421df5056d/cryptography-45.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174", size = 4564651, upload-time = "2025-07-02T13:05:21.382Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/92/cc723dd6d71e9747a887b94eb3827825c6c24b9e6ce2bb33b847d31d5eaa/cryptography-45.0.5-cp311-abi3-win32.whl", hash = "sha256:926c3ea71a6043921050eaa639137e13dbe7b4ab25800932a8498364fc1abec9", size = 2929050, upload-time = "2025-07-02T13:05:23.39Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/10/197da38a5911a48dd5389c043de4aec4b3c94cb836299b01253940788d78/cryptography-45.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:b85980d1e345fe769cfc57c57db2b59cff5464ee0c045d52c0df087e926fbe63", size = 3403224, upload-time = "2025-07-02T13:05:25.202Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/2b/160ce8c2765e7a481ce57d55eba1546148583e7b6f85514472b1d151711d/cryptography-45.0.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3562c2f23c612f2e4a6964a61d942f891d29ee320edb62ff48ffb99f3de9ae8", size = 7017143, upload-time = "2025-07-02T13:05:27.229Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/e7/2187be2f871c0221a81f55ee3105d3cf3e273c0a0853651d7011eada0d7e/cryptography-45.0.5-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd", size = 4197780, upload-time = "2025-07-02T13:05:29.299Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/cf/84210c447c06104e6be9122661159ad4ce7a8190011669afceeaea150524/cryptography-45.0.5-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e", size = 4420091, upload-time = "2025-07-02T13:05:31.221Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/6a/cb8b5c8bb82fafffa23aeff8d3a39822593cee6e2f16c5ca5c2ecca344f7/cryptography-45.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0", size = 4198711, upload-time = "2025-07-02T13:05:33.062Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/f7/36d2d69df69c94cbb2473871926daf0f01ad8e00fe3986ac3c1e8c4ca4b3/cryptography-45.0.5-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135", size = 3883299, upload-time = "2025-07-02T13:05:34.94Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/c7/f0ea40f016de72f81288e9fe8d1f6748036cb5ba6118774317a3ffc6022d/cryptography-45.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7", size = 4450558, upload-time = "2025-07-02T13:05:37.288Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/ae/94b504dc1a3cdf642d710407c62e86296f7da9e66f27ab12a1ee6fdf005b/cryptography-45.0.5-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42", size = 4198020, upload-time = "2025-07-02T13:05:39.102Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/2b/aaf0adb845d5dabb43480f18f7ca72e94f92c280aa983ddbd0bcd6ecd037/cryptography-45.0.5-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492", size = 4449759, upload-time = "2025-07-02T13:05:41.398Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/e4/f17e02066de63e0100a3a01b56f8f1016973a1d67551beaf585157a86b3f/cryptography-45.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0", size = 4319991, upload-time = "2025-07-02T13:05:43.64Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/2e/e2dbd629481b499b14516eed933f3276eb3239f7cee2dcfa4ee6b44d4711/cryptography-45.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a", size = 4554189, upload-time = "2025-07-02T13:05:46.045Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/ea/a78a0c38f4c8736287b71c2ea3799d173d5ce778c7d6e3c163a95a05ad2a/cryptography-45.0.5-cp37-abi3-win32.whl", hash = "sha256:1e1da5accc0c750056c556a93c3e9cb828970206c68867712ca5805e46dc806f", size = 2911769, upload-time = "2025-07-02T13:05:48.329Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/b3/28ac139109d9005ad3f6b6f8976ffede6706a6478e21c889ce36c840918e/cryptography-45.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97", size = 3390016, upload-time = "2025-07-02T13:05:50.811Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/8b/34394337abe4566848a2bd49b26bcd4b07fd466afd3e8cce4cb79a390869/cryptography-45.0.5-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:206210d03c1193f4e1ff681d22885181d47efa1ab3018766a7b32a7b3d6e6afd", size = 3575762, upload-time = "2025-07-02T13:05:53.166Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/5d/a19441c1e89afb0f173ac13178606ca6fab0d3bd3ebc29e9ed1318b507fc/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c648025b6840fe62e57107e0a25f604db740e728bd67da4f6f060f03017d5097", size = 4140906, upload-time = "2025-07-02T13:05:55.914Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/db/daceb259982a3c2da4e619f45b5bfdec0e922a23de213b2636e78ef0919b/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b8fa8b0a35a9982a3c60ec79905ba5bb090fc0b9addcfd3dc2dd04267e45f25e", size = 4374411, upload-time = "2025-07-02T13:05:57.814Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/35/5d06ad06402fc522c8bf7eab73422d05e789b4e38fe3206a85e3d6966c11/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:14d96584701a887763384f3c47f0ca7c1cce322aa1c31172680eb596b890ec30", size = 4140942, upload-time = "2025-07-02T13:06:00.137Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/79/020a5413347e44c382ef1f7f7e7a66817cd6273e3e6b5a72d18177b08b2f/cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57c816dfbd1659a367831baca4b775b2a5b43c003daf52e9d57e1d30bc2e1b0e", size = 4374079, upload-time = "2025-07-02T13:06:02.043Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/c5/c0e07d84a9a2a8a0ed4f865e58f37c71af3eab7d5e094ff1b21f3f3af3bc/cryptography-45.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b9e38e0a83cd51e07f5a48ff9691cae95a79bea28fe4ded168a8e5c6c77e819d", size = 3321362, upload-time = "2025-07-02T13:06:04.463Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/71/9bdbcfd58d6ff5084687fe722c58ac718ebedbc98b9f8f93781354e6d286/cryptography-45.0.5-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8c4a6ff8a30e9e3d38ac0539e9a9e02540ab3f827a3394f8852432f6b0ea152e", size = 3587878, upload-time = "2025-07-02T13:06:06.339Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/63/83516cfb87f4a8756eaa4203f93b283fda23d210fc14e1e594bd5f20edb6/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6", size = 4152447, upload-time = "2025-07-02T13:06:08.345Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/11/d2823d2a5a0bd5802b3565437add16f5c8ce1f0778bf3822f89ad2740a38/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18", size = 4386778, upload-time = "2025-07-02T13:06:10.263Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/38/6bf177ca6bce4fe14704ab3e93627c5b0ca05242261a2e43ef3168472540/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463", size = 4151627, upload-time = "2025-07-02T13:06:13.097Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/6a/69fc67e5266bff68a91bcb81dff8fb0aba4d79a78521a08812048913e16f/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1", size = 4385593, upload-time = "2025-07-02T13:06:15.689Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/34/31a1604c9a9ade0fdab61eb48570e09a796f4d9836121266447b0eaf7feb/cryptography-45.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e357286c1b76403dd384d938f93c46b2b058ed4dfcdce64a770f0537ed3feb6f", size = 3331106, upload-time = "2025-07-02T13:06:18.058Z" }, ] [[package]] name = "cssselect" version = "1.3.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/72/0a/c3ea9573b1dc2e151abfe88c7fe0c26d1892fe6ed02d0cdb30f0d57029d5/cssselect-1.3.0.tar.gz", hash = "sha256:57f8a99424cfab289a1b6a816a43075a4b00948c86b4dcf3ef4ee7e15f7ab0c7" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/0a/c3ea9573b1dc2e151abfe88c7fe0c26d1892fe6ed02d0cdb30f0d57029d5/cssselect-1.3.0.tar.gz", hash = "sha256:57f8a99424cfab289a1b6a816a43075a4b00948c86b4dcf3ef4ee7e15f7ab0c7", size = 42870, upload-time = "2025-03-10T09:30:29.638Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ee/58/257350f7db99b4ae12b614a36256d9cc870d71d9e451e79c2dc3b23d7c3c/cssselect-1.3.0-py3-none-any.whl", hash = "sha256:56d1bf3e198080cc1667e137bc51de9cadfca259f03c2d4e09037b3e01e30f0d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/58/257350f7db99b4ae12b614a36256d9cc870d71d9e451e79c2dc3b23d7c3c/cssselect-1.3.0-py3-none-any.whl", hash = "sha256:56d1bf3e198080cc1667e137bc51de9cadfca259f03c2d4e09037b3e01e30f0d", size = 18786, upload-time = "2025-03-10T09:30:28.048Z" }, ] [[package]] name = "curl-cffi" version = "0.12.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "certifi" }, { name = "cffi" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f4/91/feaf237ab7c5e06cc0a87ed7acdfbd4103beaa3ca5666d78e80b4a044e47/curl_cffi-0.12.0.tar.gz", hash = "sha256:01770d120e2ab82ad076687c7038abe4d614c7e13d7a5378520b027df529dbe7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/91/feaf237ab7c5e06cc0a87ed7acdfbd4103beaa3ca5666d78e80b4a044e47/curl_cffi-0.12.0.tar.gz", hash = "sha256:01770d120e2ab82ad076687c7038abe4d614c7e13d7a5378520b027df529dbe7", size = 150452, upload-time = "2025-07-11T05:27:10.607Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/da/58/587f76b06fd7c4176b033275ab63b90e32b6904e5d7c7844311584376cdc/curl_cffi-0.12.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e663d6692aa923a60fd2f050dad4cccd317dc7dd3d9edceb5230f68017e811eb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/74/e63d74451dcd0a793da1983024f36bbe400e8a3be8eaf860c004f66d489a/curl_cffi-0.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8f7f1745700fcd4f6d5681cdca27426cc3005c3e17ca9a66fe5753c5001a7314" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/83/10addc9189c8eeb35e7b4c13033e84e174f534caef5df9f520403bcd546d/curl_cffi-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:368dd6e354933c62d3b35afbecdc5e7e7817ba748db0d23f7276f89b7eec49e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/fd/700edf216767c0e25632e24ec66dfb4a047cf6966d3246507f8a384970cd/curl_cffi-0.12.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1df994f9ccf27b391259ba0157d415b1e60f01e59914f75280cb9c1eceb3a3c8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fe/0b/559443dd7fcdeb424f353c239db3aa0421659f9bab26a89fc65b703cb486/curl_cffi-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18aadb313a1fe23098e867f9e6e42c57c5c68985521a1fe5fb8ca15bb990341b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/4f/000397ecd769d663dbfdaea10173e4ee860379118c81e40d27faea4382c6/curl_cffi-0.12.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c0875e85eda5a5314bf1974ad1ecdcdd61811759b820b6617ec7be6daf85a1a3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/aa/01/2ff55d2d73b0d4605fff32112b33894e6b6d79106406d9e1185d680e8930/curl_cffi-0.12.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b9650b85964ed06c634cfff4ce926255b80195f73edf629a1272b1a19908811d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/6e/0194d04312fbf6eed0d0fea6dfd361795fcfd53e9dca259a8ad45ff1ccca/curl_cffi-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:cdcbc492a68b7f3592a4dc4eb742281aa74d220f55affbd84645795a7fdb3ddc" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/58/587f76b06fd7c4176b033275ab63b90e32b6904e5d7c7844311584376cdc/curl_cffi-0.12.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e663d6692aa923a60fd2f050dad4cccd317dc7dd3d9edceb5230f68017e811eb", size = 5681054, upload-time = "2025-07-11T05:26:56.671Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/74/e63d74451dcd0a793da1983024f36bbe400e8a3be8eaf860c004f66d489a/curl_cffi-0.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8f7f1745700fcd4f6d5681cdca27426cc3005c3e17ca9a66fe5753c5001a7314", size = 2990114, upload-time = "2025-07-11T05:26:58.833Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/83/10addc9189c8eeb35e7b4c13033e84e174f534caef5df9f520403bcd546d/curl_cffi-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:368dd6e354933c62d3b35afbecdc5e7e7817ba748db0d23f7276f89b7eec49e8", size = 7947282, upload-time = "2025-07-11T05:27:00.497Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/fd/700edf216767c0e25632e24ec66dfb4a047cf6966d3246507f8a384970cd/curl_cffi-0.12.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1df994f9ccf27b391259ba0157d415b1e60f01e59914f75280cb9c1eceb3a3c8", size = 7501158, upload-time = "2025-07-11T05:27:02.17Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/0b/559443dd7fcdeb424f353c239db3aa0421659f9bab26a89fc65b703cb486/curl_cffi-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18aadb313a1fe23098e867f9e6e42c57c5c68985521a1fe5fb8ca15bb990341b", size = 8347933, upload-time = "2025-07-11T05:27:03.579Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/4f/000397ecd769d663dbfdaea10173e4ee860379118c81e40d27faea4382c6/curl_cffi-0.12.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c0875e85eda5a5314bf1974ad1ecdcdd61811759b820b6617ec7be6daf85a1a3", size = 8757614, upload-time = "2025-07-11T05:27:05.736Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/01/2ff55d2d73b0d4605fff32112b33894e6b6d79106406d9e1185d680e8930/curl_cffi-0.12.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b9650b85964ed06c634cfff4ce926255b80195f73edf629a1272b1a19908811d", size = 8731023, upload-time = "2025-07-11T05:27:07.685Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/6e/0194d04312fbf6eed0d0fea6dfd361795fcfd53e9dca259a8ad45ff1ccca/curl_cffi-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:cdcbc492a68b7f3592a4dc4eb742281aa74d220f55affbd84645795a7fdb3ddc", size = 1610837, upload-time = "2025-07-11T05:27:09.275Z" }, ] [[package]] name = "cycler" version = "0.12.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30" }, ] [[package]] name = "dashscope" version = "1.20.11" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiohttp" }, { name = "requests" }, { name = "websocket-client" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/25/21/0ddfa1aae7f45b3039d10d61ede77dedfc70d24ff946e7d0ecb92e9a2c85/dashscope-1.20.11-py3-none-any.whl", hash = "sha256:7367802c5ae136c6c1f4f8a16f9aba628e97adefae8afdebce6bbf518d0065d1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/21/0ddfa1aae7f45b3039d10d61ede77dedfc70d24ff946e7d0ecb92e9a2c85/dashscope-1.20.11-py3-none-any.whl", hash = "sha256:7367802c5ae136c6c1f4f8a16f9aba628e97adefae8afdebce6bbf518d0065d1", size = 1264221, upload-time = "2024-10-14T05:30:25.083Z" }, ] [[package]] name = "datasets" version = "4.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "dill" }, { name = "filelock" }, @@ -1207,146 +1207,146 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e3/9d/348ed92110ba5f9b70b51ca1078d4809767a835aa2b7ce7e74ad2b98323d/datasets-4.0.0.tar.gz", hash = "sha256:9657e7140a9050db13443ba21cb5de185af8af944479b00e7ff1e00a61c8dbf1" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/9d/348ed92110ba5f9b70b51ca1078d4809767a835aa2b7ce7e74ad2b98323d/datasets-4.0.0.tar.gz", hash = "sha256:9657e7140a9050db13443ba21cb5de185af8af944479b00e7ff1e00a61c8dbf1", size = 569566, upload-time = "2025-07-09T14:35:52.431Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/eb/62/eb8157afb21bd229c864521c1ab4fa8e9b4f1b06bafdd8c4668a7a31b5dd/datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/62/eb8157afb21bd229c864521c1ab4fa8e9b4f1b06bafdd8c4668a7a31b5dd/datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d", size = 494825, upload-time = "2025-07-09T14:35:50.658Z" }, ] [[package]] name = "datrie" version = "0.8.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9d/fe/db74bd405d515f06657f11ad529878fd389576dca4812bea6f98d9b31574/datrie-0.8.2.tar.gz", hash = "sha256:525b08f638d5cf6115df6ccd818e5a01298cd230b2dac91c8ff2e6499d18765d" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/fe/db74bd405d515f06657f11ad529878fd389576dca4812bea6f98d9b31574/datrie-0.8.2.tar.gz", hash = "sha256:525b08f638d5cf6115df6ccd818e5a01298cd230b2dac91c8ff2e6499d18765d", size = 63278, upload-time = "2020-03-26T03:31:53.681Z" } [[package]] name = "debugpy" version = "1.8.15" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8c/8b/3a9a28ddb750a76eaec445c7f4d3147ea2c579a97dbd9e25d39001b92b21/debugpy-1.8.15.tar.gz", hash = "sha256:58d7a20b7773ab5ee6bdfb2e6cf622fdf1e40c9d5aef2857d85391526719ac00" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/8b/3a9a28ddb750a76eaec445c7f4d3147ea2c579a97dbd9e25d39001b92b21/debugpy-1.8.15.tar.gz", hash = "sha256:58d7a20b7773ab5ee6bdfb2e6cf622fdf1e40c9d5aef2857d85391526719ac00", size = 1643279, upload-time = "2025-07-15T16:43:29.135Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/69/51/0b4315169f0d945271db037ae6b98c0548a2d48cc036335cd1b2f5516c1b/debugpy-1.8.15-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e9a8125c85172e3ec30985012e7a81ea5e70bbb836637f8a4104f454f9b06c97" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/cc/a5391dedb079280d7b72418022e00ba8227ae0b5bc8b2e3d1ecffc5d6b01/debugpy-1.8.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fd0b6b5eccaa745c214fd240ea82f46049d99ef74b185a3517dad3ea1ec55d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e8/92/acf64b92010c66b33c077dee3862c733798a2c90e7d14b25c01d771e2a0d/debugpy-1.8.15-cp310-cp310-win32.whl", hash = "sha256:8181cce4d344010f6bfe94a531c351a46a96b0f7987750932b2908e7a1e14a55" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/f5/c58c015c9ff78de35901bea3ab4dbf7946d7a4aa867ee73875df06ba6468/debugpy-1.8.15-cp310-cp310-win_amd64.whl", hash = "sha256:af2dcae4e4cd6e8b35f982ccab29fe65f7e8766e10720a717bc80c464584ee21" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/b3/1c44a2ed311199ab11c2299c9474a6c7cd80d19278defd333aeb7c287995/debugpy-1.8.15-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:babc4fb1962dd6a37e94d611280e3d0d11a1f5e6c72ac9b3d87a08212c4b6dd3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/69/e2dcb721491e1c294d348681227c9b44fb95218f379aa88e12a19d85528d/debugpy-1.8.15-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f778e68f2986a58479d0ac4f643e0b8c82fdd97c2e200d4d61e7c2d13838eb53" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/76/4ce63b95d8294dcf2fd1820860b300a420d077df4e93afcaa25a984c2ca7/debugpy-1.8.15-cp311-cp311-win32.whl", hash = "sha256:f9d1b5abd75cd965e2deabb1a06b0e93a1546f31f9f621d2705e78104377c702" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/a7/e5a7c784465eb9c976d84408873d597dc7ce74a0fc69ed009548a1a94813/debugpy-1.8.15-cp311-cp311-win_amd64.whl", hash = "sha256:62954fb904bec463e2b5a415777f6d1926c97febb08ef1694da0e5d1463c5c3b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/4a/4508d256e52897f5cdfee6a6d7580974811e911c6d01321df3264508a5ac/debugpy-1.8.15-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:3dcc7225cb317469721ab5136cda9ff9c8b6e6fb43e87c9e15d5b108b99d01ba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/8d/7f6ef1097e7fecf26b4ef72338d08e41644a41b7ee958a19f494ffcffc29/debugpy-1.8.15-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:047a493ca93c85ccede1dbbaf4e66816794bdc214213dde41a9a61e42d27f8fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/e8/e8c6a9aa33a9c9c6dacbf31747384f6ed2adde4de2e9693c766bdf323aa3/debugpy-1.8.15-cp312-cp312-win32.whl", hash = "sha256:b08e9b0bc260cf324c890626961dad4ffd973f7568fbf57feb3c3a65ab6b6327" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/ad/231050c6177b3476b85fcea01e565dac83607b5233d003ff067e2ee44d8f/debugpy-1.8.15-cp312-cp312-win_amd64.whl", hash = "sha256:e2a4fe357c92334272eb2845fcfcdbec3ef9f22c16cf613c388ac0887aed15fa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/d5/98748d9860e767a1248b5e31ffa7ce8cb7006e97bf8abbf3d891d0a8ba4e/debugpy-1.8.15-py2.py3-none-any.whl", hash = "sha256:bce2e6c5ff4f2e00b98d45e7e01a49c7b489ff6df5f12d881c67d2f1ac635f3d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/51/0b4315169f0d945271db037ae6b98c0548a2d48cc036335cd1b2f5516c1b/debugpy-1.8.15-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e9a8125c85172e3ec30985012e7a81ea5e70bbb836637f8a4104f454f9b06c97", size = 2084890, upload-time = "2025-07-15T16:43:31.239Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/cc/a5391dedb079280d7b72418022e00ba8227ae0b5bc8b2e3d1ecffc5d6b01/debugpy-1.8.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fd0b6b5eccaa745c214fd240ea82f46049d99ef74b185a3517dad3ea1ec55d9", size = 3561470, upload-time = "2025-07-15T16:43:32.515Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/92/acf64b92010c66b33c077dee3862c733798a2c90e7d14b25c01d771e2a0d/debugpy-1.8.15-cp310-cp310-win32.whl", hash = "sha256:8181cce4d344010f6bfe94a531c351a46a96b0f7987750932b2908e7a1e14a55", size = 5229194, upload-time = "2025-07-15T16:43:33.997Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/f5/c58c015c9ff78de35901bea3ab4dbf7946d7a4aa867ee73875df06ba6468/debugpy-1.8.15-cp310-cp310-win_amd64.whl", hash = "sha256:af2dcae4e4cd6e8b35f982ccab29fe65f7e8766e10720a717bc80c464584ee21", size = 5260900, upload-time = "2025-07-15T16:43:35.413Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/b3/1c44a2ed311199ab11c2299c9474a6c7cd80d19278defd333aeb7c287995/debugpy-1.8.15-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:babc4fb1962dd6a37e94d611280e3d0d11a1f5e6c72ac9b3d87a08212c4b6dd3", size = 2183442, upload-time = "2025-07-15T16:43:36.733Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/69/e2dcb721491e1c294d348681227c9b44fb95218f379aa88e12a19d85528d/debugpy-1.8.15-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f778e68f2986a58479d0ac4f643e0b8c82fdd97c2e200d4d61e7c2d13838eb53", size = 3134215, upload-time = "2025-07-15T16:43:38.116Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/76/4ce63b95d8294dcf2fd1820860b300a420d077df4e93afcaa25a984c2ca7/debugpy-1.8.15-cp311-cp311-win32.whl", hash = "sha256:f9d1b5abd75cd965e2deabb1a06b0e93a1546f31f9f621d2705e78104377c702", size = 5154037, upload-time = "2025-07-15T16:43:39.471Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/a7/e5a7c784465eb9c976d84408873d597dc7ce74a0fc69ed009548a1a94813/debugpy-1.8.15-cp311-cp311-win_amd64.whl", hash = "sha256:62954fb904bec463e2b5a415777f6d1926c97febb08ef1694da0e5d1463c5c3b", size = 5178133, upload-time = "2025-07-15T16:43:40.969Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/4a/4508d256e52897f5cdfee6a6d7580974811e911c6d01321df3264508a5ac/debugpy-1.8.15-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:3dcc7225cb317469721ab5136cda9ff9c8b6e6fb43e87c9e15d5b108b99d01ba", size = 2511197, upload-time = "2025-07-15T16:43:42.343Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/8d/7f6ef1097e7fecf26b4ef72338d08e41644a41b7ee958a19f494ffcffc29/debugpy-1.8.15-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:047a493ca93c85ccede1dbbaf4e66816794bdc214213dde41a9a61e42d27f8fc", size = 4229517, upload-time = "2025-07-15T16:43:44.14Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/e8/e8c6a9aa33a9c9c6dacbf31747384f6ed2adde4de2e9693c766bdf323aa3/debugpy-1.8.15-cp312-cp312-win32.whl", hash = "sha256:b08e9b0bc260cf324c890626961dad4ffd973f7568fbf57feb3c3a65ab6b6327", size = 5276132, upload-time = "2025-07-15T16:43:45.529Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/ad/231050c6177b3476b85fcea01e565dac83607b5233d003ff067e2ee44d8f/debugpy-1.8.15-cp312-cp312-win_amd64.whl", hash = "sha256:e2a4fe357c92334272eb2845fcfcdbec3ef9f22c16cf613c388ac0887aed15fa", size = 5317645, upload-time = "2025-07-15T16:43:46.968Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/d5/98748d9860e767a1248b5e31ffa7ce8cb7006e97bf8abbf3d891d0a8ba4e/debugpy-1.8.15-py2.py3-none-any.whl", hash = "sha256:bce2e6c5ff4f2e00b98d45e7e01a49c7b489ff6df5f12d881c67d2f1ac635f3d", size = 5282697, upload-time = "2025-07-15T16:44:07.996Z" }, ] [[package]] name = "decorator" version = "5.2.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] [[package]] name = "deepl" version = "1.18.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/82/46/1dfe623c24aac5a341bf7eedabb8d1f719df8fd3a6f45aefcd0b83e96ce0/deepl-1.18.0.tar.gz", hash = "sha256:5ae41763939441edbca7640fd344280cbee47d490641ce35206910a8b01e778e" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/46/1dfe623c24aac5a341bf7eedabb8d1f719df8fd3a6f45aefcd0b83e96ce0/deepl-1.18.0.tar.gz", hash = "sha256:5ae41763939441edbca7640fd344280cbee47d490641ce35206910a8b01e778e", size = 38888, upload-time = "2024-04-26T10:09:03.701Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d3/02/fe0df955618c204ea51a2cee85461e736a939cf9d593b314adeb1c2c5d2e/deepl-1.18.0-py3-none-any.whl", hash = "sha256:2afe9adc459f5c591282e4d74570a0dc5041554d54dd687f72d3b0b77936e9ce" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/02/fe0df955618c204ea51a2cee85461e736a939cf9d593b314adeb1c2c5d2e/deepl-1.18.0-py3-none-any.whl", hash = "sha256:2afe9adc459f5c591282e4d74570a0dc5041554d54dd687f72d3b0b77936e9ce", size = 35265, upload-time = "2024-04-26T10:09:00.978Z" }, ] [[package]] name = "demjson3" version = "3.0.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f7/d2/6a81a9b5311d50542e11218b470dafd8adbaf1b3e51fc1fddd8a57eed691/demjson3-3.0.6.tar.gz", hash = "sha256:37c83b0c6eb08d25defc88df0a2a4875d58a7809a9650bd6eee7afd8053cdbac" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/d2/6a81a9b5311d50542e11218b470dafd8adbaf1b3e51fc1fddd8a57eed691/demjson3-3.0.6.tar.gz", hash = "sha256:37c83b0c6eb08d25defc88df0a2a4875d58a7809a9650bd6eee7afd8053cdbac", size = 131477, upload-time = "2022-10-22T19:09:05.379Z" } [[package]] name = "deprecated" version = "1.2.18" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, ] [[package]] name = "dill" version = "0.3.8" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847, upload-time = "2024-01-27T23:42:16.145Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252, upload-time = "2024-01-27T23:42:14.239Z" }, ] [[package]] name = "discord-py" version = "2.3.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiohttp" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6f/cb/a360101905102684a4fe6fc543976842383f54ddeeef020959e4965c416e/discord.py-2.3.2.tar.gz", hash = "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/cb/a360101905102684a4fe6fc543976842383f54ddeeef020959e4965c416e/discord.py-2.3.2.tar.gz", hash = "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c", size = 978172, upload-time = "2023-08-10T21:44:07.733Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9c/7e/5f1b24b2ced0c4b3042204f7827b57c7dcb26d368e9b0fde8cec7853cf30/discord.py-2.3.2-py3-none-any.whl", hash = "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/7e/5f1b24b2ced0c4b3042204f7827b57c7dcb26d368e9b0fde8cec7853cf30/discord.py-2.3.2-py3-none-any.whl", hash = "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6", size = 1084904, upload-time = "2023-08-10T21:44:05.285Z" }, ] [[package]] name = "diskcache" version = "5.6.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19" }, ] [[package]] name = "distro" version = "1.9.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] [[package]] name = "docstring-parser" version = "0.17.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, ] [[package]] name = "docutils" version = "0.21.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] [[package]] name = "duckduckgo-search" version = "7.5.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "click" }, { name = "lxml" }, { name = "primp" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/13/dc/919d3d51ed702890a3e6e736e1e152d5d90856393200306e82fb54fde39e/duckduckgo_search-7.5.5.tar.gz", hash = "sha256:44ef03bfa5484bada786590f2d4c213251131765721383a177a0da6fa5c5e41a" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/dc/919d3d51ed702890a3e6e736e1e152d5d90856393200306e82fb54fde39e/duckduckgo_search-7.5.5.tar.gz", hash = "sha256:44ef03bfa5484bada786590f2d4c213251131765721383a177a0da6fa5c5e41a", size = 24768, upload-time = "2025-03-27T08:11:26.951Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fc/da/8376678b4a9ae0f9418d93df9c9cf851dced49c95ceb38daac6651e38f7a/duckduckgo_search-7.5.5-py3-none-any.whl", hash = "sha256:c71a0661aa436f215d9a05d653af424affb58825ab3e79f3b788053cbdee9ebc" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/da/8376678b4a9ae0f9418d93df9c9cf851dced49c95ceb38daac6651e38f7a/duckduckgo_search-7.5.5-py3-none-any.whl", hash = "sha256:c71a0661aa436f215d9a05d653af424affb58825ab3e79f3b788053cbdee9ebc", size = 20421, upload-time = "2025-03-27T08:11:25.515Z" }, ] [[package]] @@ -1369,114 +1369,114 @@ wheels = [ [[package]] name = "editdistance" version = "0.8.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d5/18/9f4f975ca87a390832b1c22478f3702fcdf739f83211e24d054b7551270d/editdistance-0.8.1.tar.gz", hash = "sha256:d1cdf80a5d5014b0c9126a69a42ce55a457b457f6986ff69ca98e4fe4d2d8fed" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/09/c9/302658ce7f4c537a4e85cf578d11bbf7af120a712e1d78fedc6cb8823c65/editdistance-0.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:adeb705f32b93accc74960d227875abff150ee42d676e428536361fe5f8f5388" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/80/0b3c7d2c0e183725986fea5dd2df11f0b4b46320e9a64f6077a121ab1f64/editdistance-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3de77951b105d0972deec7684a0b3d1a9dee69c9b5d34f6e2acc0d76cd4a1c52" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b5/14/681460965c6a4a48321b07f88de2273d097fdca0491ff55db891aacbd291/editdistance-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e88efb052d45e924606c305cb833a80579dca3e8e4ff01309d50ba2c1c0bbd5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/0d/abdbc8e394a9461cf2ae27c16564fadaa65f52bd242dd1582ae5e7736dc3/editdistance-0.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0247e7a1e9c66ea75211a97e725366bff19a52aac2c838ed5f90025630e976dd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/fb/2940d26ebda12efd280ae939436f17ac482930d862df9e774cb8b771ab03/editdistance-0.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67d143429a49ab552411505f550a0fb4285a1d4336e096804d233ec495ac20fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/53/cc/c63d75c7f387d4df0645682c1ab8706c2dfe5c9c0c4999723ce9a3ba0853/editdistance-0.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca9d3be2b10e5d44a950a4bd1e84bca9ebbecd364bce0cf5693bf8224c78eaef" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/38/bb0f734a7571e093184606b930734b12da5b6bff2635eba9312fe4536dd9/editdistance-0.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5c72aa1df8535f2e2b3d8773a1a7da091bc1a7e52bb396e7e48d375ba687e7b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/9f/624fc7a09918f850a057465f02e86f269e139a457f48ff8cabfb12701756/editdistance-0.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a606c34a2a6cc190e4fffc856b36333cdcf1f1fab5b22bd3088e585c22d6ca0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/5c/7fa6cc277f91c477ee370807d51c1826891dc6dfc307544223ce7f2687de/editdistance-0.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5af173d442ffac33b7c7990132f97f88818a3abf4b21c0c702a7022df37c0c5c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/97/556215f71184291155aee340a6d34f0676e7238fdfd10615b6b775ce25fe/editdistance-0.8.1-cp310-cp310-win32.whl", hash = "sha256:fd64b58f5a7b59afd9d75982aaeeacd2a98498bf472fa0360c122ffe6ea4c871" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c8/d1/7ec5f5cbb95838d0eff7f980a660c81acd1363d658f2f5d4ceba38877c5a/editdistance-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:6c7c62c3cae45ca1fa01bb2722b297b9de1e3a244ac44cfba88bdcb488fe6aee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/dc/d0c29fd52d8f9e795653ed2b838a2a48c739cdfff04ac5b79c6c0ecbdf79/editdistance-0.8.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:486105603a273d73d12a54f347dffa70ab281749d7c3879658b377bc49e4b98c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/c6/75fa45d7b78fbea6fd894f4e48895a75bd3c83d4a9a6b57673881d74d3e0/editdistance-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fad081f5f86a175c1a09a4e9e45b95c9349e454c21e181e842e01c85f1f536fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/a3/058d823b6285c3511dc94ed80620c3fb0c18b4aaa708f70ba71f3af28436/editdistance-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cb78e125f6759398885a775f5eed07c2bb72b2f86da43e674c6b6a3335b273b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a0/3a/0b13c7864c93b1e9b9952bd2a33c5ef3c4fd1bf70a5fad6924789e70e5eb/editdistance-0.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3778ca60aa89def9144b70e330bcec5330c7da1d69cb28c612e90b84510a1d3d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/8a/db0fd79e8ddb9b5f86f274107c5d0a27ec4f2af88877df1f26c2c6d150cc/editdistance-0.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fba945eaa0436cf40bc53d7e299dc537c7c71353379a095b7459ff4af910da33" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0d/d2/98be7112750ff17b436dd76f988f1e38570dcec0df8578ee19ef046f22fe/editdistance-0.8.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877f2a0d801f32bc1a1878901ffb947b974361e849c66e314a7f1d786a446b58" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/62/1815e3bf164910c47ba1948c8b5e937a40c7f9763b64e98fb6666b01dd06/editdistance-0.8.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e79d351ca40a6ead5f3763253fd7521572ee0d3e5d42538630e56d10f48db481" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/d3/a832cea7b507a9be54e4ac3d1340fb66dca5f9c16c70bf38d5039e8fdede/editdistance-0.8.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:70ed382b3052a51161bad0149d4665003bf3b949fce0b01bf1253a4cc1a88239" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a3/b4/db291d2a3845cbf8047b4b5aad3b3e038a8a2994d87027b40e1a1b0f4b74/editdistance-0.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a529bfb384c4000775d76739c4e64f73337f0f5a3784933b1321b577a62bed4e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c4/26/7ddeacada4982d0b892a28897e21871d0f25bca165e3663e37c3a272808a/editdistance-0.8.1-cp311-cp311-win32.whl", hash = "sha256:b082232429e731f181af7f7d2bcf79da6ca8fadd04e9086c11e2973f7d330c81" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/a1/778af8590b8b12f03f62eacc3c8744407ade9e3d69be6dabe38d0afbf2dd/editdistance-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:cef1a4359252a49f2c4718e64e9d40027d9d951b289d045bdb278656e59f6af8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cb/4c/7f195588949b4e72436dc7fc902632381f96e586af829685b56daebb38b8/editdistance-0.8.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04af61b3fcdd287a07c15b6ae3b02af01c5e3e9c3aca76b8c1d13bd266b6f57" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/82/31dc1640d830cd7d36865098329f34e4dad3b77f31cfb9404b347e700196/editdistance-0.8.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:18fc8b6eaae01bfd9cf999af726c1e8dcf667d120e81aa7dbd515bea7427f62f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/2a/6b823e71cef694d6f070a1d82be2842706fa193541aab8856a8f42044cd0/editdistance-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a87839450a5987028738d061ffa5ef6a68bac2ddc68c9147a8aae9806629c7f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/31/bfb8e590f922089dc3471ed7828a6da2fc9453eba38c332efa9ee8749fd7/editdistance-0.8.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24b5f9c9673c823d91b5973d0af8b39f883f414a55ade2b9d097138acd10f31e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/c7/57423942b2f847cdbbb46494568d00cd8a45500904ea026f0aad6ca01bc7/editdistance-0.8.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c59248eabfad603f0fba47b0c263d5dc728fb01c2b6b50fb6ca187cec547fdb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/05/dfa4cdcce063596cbf0d7a32c46cd0f4fa70980311b7da64d35f33ad02a0/editdistance-0.8.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e239d88ff52821cf64023fabd06a1d9a07654f364b64bf1284577fd3a79d0e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/14/39608ff724a9523f187c4e28926d78bc68f2798f74777ac6757981108345/editdistance-0.8.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2f7f71698f83e8c83839ac0d876a0f4ef996c86c5460aebd26d85568d4afd0db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/92/4a1c61d72da40dedfd0ff950fdc71ae83f478330c58a8bccfd776518bd67/editdistance-0.8.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:04e229d6f4ce0c12abc9f4cd4023a5b5fa9620226e0207b119c3c2778b036250" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/3d/9877566e724c8a37f2228a84ec5cbf66dbfd0673515baf68a0fe07caff40/editdistance-0.8.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e16721636da6d6b68a2c09eaced35a94f4a4a704ec09f45756d4fd5e128ed18d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/f5/8c50757d198b8ca30ddb91e8b8f0247a8dca04ff2ec30755245f0ab1ff0c/editdistance-0.8.1-cp312-cp312-win32.whl", hash = "sha256:87533cf2ebc3777088d991947274cd7e1014b9c861a8aa65257bcdc0ee492526" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/f0/65101e51dc7c850e7b7581a5d8fa8721a1d7479a0dca6c08386328e19882/editdistance-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:09f01ed51746d90178af7dd7ea4ebb41497ef19f53c7f327e864421743dffb0a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/4c/c9d02eeb47815d35f8d324b52f6704ea7beb032bcb209358cac44047d413/editdistance-0.8.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a4a90c6b03094c07358572027a8d0a13cca7450b1aa6caca98a5f1fa4f0b8961" }, - { url = "https://mirrors.aliyun.com/pypi/packages/af/b0/2818fa6a24595dac069b0bfb9d05658406779a1ded8fd2b0c9066396cf99/editdistance-0.8.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:510a4f9ced348a4fd89ae2e102357d4d801a771e29bb2bc2f130a1692193407f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/d1/3d5e09bcf7fdb7aed705bf74047a8634bd2b8fd92177c25a2547e6dbadfb/editdistance-0.8.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4787fa7228ba6a34b430066d174320f011d605015baa7299c2c4911e6ea6bd46" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/88/fca5d7b1a1edf66ce1e5b6b60bff75842e6814b4f5facbdf4585d88c912d/editdistance-0.8.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee02601375073afccd6b4d811129ce1cb696d47db734784d8dbd1fddcea75447" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/91/0e6285bbe2358d81fd16313d30306b2d0036387348f7bc11d8c076ca3c72/editdistance-0.8.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bc7ad9f9a20e6f351523de77c59249f005242e3f317b5de45d02c378d24f6531" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/18/9f4f975ca87a390832b1c22478f3702fcdf739f83211e24d054b7551270d/editdistance-0.8.1.tar.gz", hash = "sha256:d1cdf80a5d5014b0c9126a69a42ce55a457b457f6986ff69ca98e4fe4d2d8fed", size = 50006, upload-time = "2024-02-10T07:44:53.914Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/c9/302658ce7f4c537a4e85cf578d11bbf7af120a712e1d78fedc6cb8823c65/editdistance-0.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:adeb705f32b93accc74960d227875abff150ee42d676e428536361fe5f8f5388", size = 106150, upload-time = "2024-02-10T07:43:15.903Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/80/0b3c7d2c0e183725986fea5dd2df11f0b4b46320e9a64f6077a121ab1f64/editdistance-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3de77951b105d0972deec7684a0b3d1a9dee69c9b5d34f6e2acc0d76cd4a1c52", size = 80551, upload-time = "2024-02-10T07:43:17.64Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/14/681460965c6a4a48321b07f88de2273d097fdca0491ff55db891aacbd291/editdistance-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e88efb052d45e924606c305cb833a80579dca3e8e4ff01309d50ba2c1c0bbd5", size = 79142, upload-time = "2024-02-10T07:43:19.195Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/0d/abdbc8e394a9461cf2ae27c16564fadaa65f52bd242dd1582ae5e7736dc3/editdistance-0.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0247e7a1e9c66ea75211a97e725366bff19a52aac2c838ed5f90025630e976dd", size = 396768, upload-time = "2024-02-10T07:43:20.912Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/fb/2940d26ebda12efd280ae939436f17ac482930d862df9e774cb8b771ab03/editdistance-0.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67d143429a49ab552411505f550a0fb4285a1d4336e096804d233ec495ac20fc", size = 401846, upload-time = "2024-02-10T07:43:23.169Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/cc/c63d75c7f387d4df0645682c1ab8706c2dfe5c9c0c4999723ce9a3ba0853/editdistance-0.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca9d3be2b10e5d44a950a4bd1e84bca9ebbecd364bce0cf5693bf8224c78eaef", size = 397543, upload-time = "2024-02-10T07:43:24.621Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/38/bb0f734a7571e093184606b930734b12da5b6bff2635eba9312fe4536dd9/editdistance-0.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5c72aa1df8535f2e2b3d8773a1a7da091bc1a7e52bb396e7e48d375ba687e7b2", size = 898934, upload-time = "2024-02-10T07:43:26.926Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/9f/624fc7a09918f850a057465f02e86f269e139a457f48ff8cabfb12701756/editdistance-0.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a606c34a2a6cc190e4fffc856b36333cdcf1f1fab5b22bd3088e585c22d6ca0", size = 959637, upload-time = "2024-02-10T07:43:28.997Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/5c/7fa6cc277f91c477ee370807d51c1826891dc6dfc307544223ce7f2687de/editdistance-0.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5af173d442ffac33b7c7990132f97f88818a3abf4b21c0c702a7022df37c0c5c", size = 911024, upload-time = "2024-02-10T07:43:30.449Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/97/556215f71184291155aee340a6d34f0676e7238fdfd10615b6b775ce25fe/editdistance-0.8.1-cp310-cp310-win32.whl", hash = "sha256:fd64b58f5a7b59afd9d75982aaeeacd2a98498bf472fa0360c122ffe6ea4c871", size = 80834, upload-time = "2024-02-10T07:43:31.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/d1/7ec5f5cbb95838d0eff7f980a660c81acd1363d658f2f5d4ceba38877c5a/editdistance-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:6c7c62c3cae45ca1fa01bb2722b297b9de1e3a244ac44cfba88bdcb488fe6aee", size = 79614, upload-time = "2024-02-10T07:43:33.255Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/dc/d0c29fd52d8f9e795653ed2b838a2a48c739cdfff04ac5b79c6c0ecbdf79/editdistance-0.8.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:486105603a273d73d12a54f347dffa70ab281749d7c3879658b377bc49e4b98c", size = 106079, upload-time = "2024-02-10T07:43:34.34Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/c6/75fa45d7b78fbea6fd894f4e48895a75bd3c83d4a9a6b57673881d74d3e0/editdistance-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fad081f5f86a175c1a09a4e9e45b95c9349e454c21e181e842e01c85f1f536fc", size = 80580, upload-time = "2024-02-10T07:43:35.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/a3/058d823b6285c3511dc94ed80620c3fb0c18b4aaa708f70ba71f3af28436/editdistance-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cb78e125f6759398885a775f5eed07c2bb72b2f86da43e674c6b6a3335b273b", size = 79087, upload-time = "2024-02-10T07:43:36.923Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/3a/0b13c7864c93b1e9b9952bd2a33c5ef3c4fd1bf70a5fad6924789e70e5eb/editdistance-0.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3778ca60aa89def9144b70e330bcec5330c7da1d69cb28c612e90b84510a1d3d", size = 409296, upload-time = "2024-02-10T07:43:38.52Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/8a/db0fd79e8ddb9b5f86f274107c5d0a27ec4f2af88877df1f26c2c6d150cc/editdistance-0.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fba945eaa0436cf40bc53d7e299dc537c7c71353379a095b7459ff4af910da33", size = 412913, upload-time = "2024-02-10T07:43:39.852Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/d2/98be7112750ff17b436dd76f988f1e38570dcec0df8578ee19ef046f22fe/editdistance-0.8.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877f2a0d801f32bc1a1878901ffb947b974361e849c66e314a7f1d786a446b58", size = 407430, upload-time = "2024-02-10T07:43:41.048Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/62/1815e3bf164910c47ba1948c8b5e937a40c7f9763b64e98fb6666b01dd06/editdistance-0.8.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e79d351ca40a6ead5f3763253fd7521572ee0d3e5d42538630e56d10f48db481", size = 909217, upload-time = "2024-02-10T07:43:42.916Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/d3/a832cea7b507a9be54e4ac3d1340fb66dca5f9c16c70bf38d5039e8fdede/editdistance-0.8.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:70ed382b3052a51161bad0149d4665003bf3b949fce0b01bf1253a4cc1a88239", size = 969407, upload-time = "2024-02-10T07:43:44.912Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/b4/db291d2a3845cbf8047b4b5aad3b3e038a8a2994d87027b40e1a1b0f4b74/editdistance-0.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a529bfb384c4000775d76739c4e64f73337f0f5a3784933b1321b577a62bed4e", size = 922112, upload-time = "2024-02-10T07:43:47.047Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/26/7ddeacada4982d0b892a28897e21871d0f25bca165e3663e37c3a272808a/editdistance-0.8.1-cp311-cp311-win32.whl", hash = "sha256:b082232429e731f181af7f7d2bcf79da6ca8fadd04e9086c11e2973f7d330c81", size = 80799, upload-time = "2024-02-10T07:43:48.231Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/a1/778af8590b8b12f03f62eacc3c8744407ade9e3d69be6dabe38d0afbf2dd/editdistance-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:cef1a4359252a49f2c4718e64e9d40027d9d951b289d045bdb278656e59f6af8", size = 79698, upload-time = "2024-02-10T07:43:49.234Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/4c/7f195588949b4e72436dc7fc902632381f96e586af829685b56daebb38b8/editdistance-0.8.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04af61b3fcdd287a07c15b6ae3b02af01c5e3e9c3aca76b8c1d13bd266b6f57", size = 106723, upload-time = "2024-02-10T07:43:50.268Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/82/31dc1640d830cd7d36865098329f34e4dad3b77f31cfb9404b347e700196/editdistance-0.8.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:18fc8b6eaae01bfd9cf999af726c1e8dcf667d120e81aa7dbd515bea7427f62f", size = 80998, upload-time = "2024-02-10T07:43:51.259Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/2a/6b823e71cef694d6f070a1d82be2842706fa193541aab8856a8f42044cd0/editdistance-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a87839450a5987028738d061ffa5ef6a68bac2ddc68c9147a8aae9806629c7f", size = 79248, upload-time = "2024-02-10T07:43:52.873Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/31/bfb8e590f922089dc3471ed7828a6da2fc9453eba38c332efa9ee8749fd7/editdistance-0.8.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24b5f9c9673c823d91b5973d0af8b39f883f414a55ade2b9d097138acd10f31e", size = 415262, upload-time = "2024-02-10T07:43:54.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/c7/57423942b2f847cdbbb46494568d00cd8a45500904ea026f0aad6ca01bc7/editdistance-0.8.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c59248eabfad603f0fba47b0c263d5dc728fb01c2b6b50fb6ca187cec547fdb3", size = 418905, upload-time = "2024-02-10T07:43:55.779Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/05/dfa4cdcce063596cbf0d7a32c46cd0f4fa70980311b7da64d35f33ad02a0/editdistance-0.8.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e239d88ff52821cf64023fabd06a1d9a07654f364b64bf1284577fd3a79d0e", size = 412511, upload-time = "2024-02-10T07:43:57.567Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/14/39608ff724a9523f187c4e28926d78bc68f2798f74777ac6757981108345/editdistance-0.8.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2f7f71698f83e8c83839ac0d876a0f4ef996c86c5460aebd26d85568d4afd0db", size = 917293, upload-time = "2024-02-10T07:43:59.559Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/92/4a1c61d72da40dedfd0ff950fdc71ae83f478330c58a8bccfd776518bd67/editdistance-0.8.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:04e229d6f4ce0c12abc9f4cd4023a5b5fa9620226e0207b119c3c2778b036250", size = 975580, upload-time = "2024-02-10T07:44:01.328Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/3d/9877566e724c8a37f2228a84ec5cbf66dbfd0673515baf68a0fe07caff40/editdistance-0.8.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e16721636da6d6b68a2c09eaced35a94f4a4a704ec09f45756d4fd5e128ed18d", size = 929121, upload-time = "2024-02-10T07:44:02.764Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/f5/8c50757d198b8ca30ddb91e8b8f0247a8dca04ff2ec30755245f0ab1ff0c/editdistance-0.8.1-cp312-cp312-win32.whl", hash = "sha256:87533cf2ebc3777088d991947274cd7e1014b9c861a8aa65257bcdc0ee492526", size = 81039, upload-time = "2024-02-10T07:44:04.134Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/f0/65101e51dc7c850e7b7581a5d8fa8721a1d7479a0dca6c08386328e19882/editdistance-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:09f01ed51746d90178af7dd7ea4ebb41497ef19f53c7f327e864421743dffb0a", size = 79853, upload-time = "2024-02-10T07:44:05.687Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/4c/c9d02eeb47815d35f8d324b52f6704ea7beb032bcb209358cac44047d413/editdistance-0.8.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a4a90c6b03094c07358572027a8d0a13cca7450b1aa6caca98a5f1fa4f0b8961", size = 76455, upload-time = "2024-02-10T07:44:36.838Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/b0/2818fa6a24595dac069b0bfb9d05658406779a1ded8fd2b0c9066396cf99/editdistance-0.8.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:510a4f9ced348a4fd89ae2e102357d4d801a771e29bb2bc2f130a1692193407f", size = 84104, upload-time = "2024-02-10T07:44:37.928Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/d1/3d5e09bcf7fdb7aed705bf74047a8634bd2b8fd92177c25a2547e6dbadfb/editdistance-0.8.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4787fa7228ba6a34b430066d174320f011d605015baa7299c2c4911e6ea6bd46", size = 89058, upload-time = "2024-02-10T07:44:39.113Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/88/fca5d7b1a1edf66ce1e5b6b60bff75842e6814b4f5facbdf4585d88c912d/editdistance-0.8.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee02601375073afccd6b4d811129ce1cb696d47db734784d8dbd1fddcea75447", size = 84635, upload-time = "2024-02-10T07:44:40.714Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/91/0e6285bbe2358d81fd16313d30306b2d0036387348f7bc11d8c076ca3c72/editdistance-0.8.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bc7ad9f9a20e6f351523de77c59249f005242e3f317b5de45d02c378d24f6531", size = 77389, upload-time = "2024-02-10T07:44:41.725Z" }, ] [[package]] name = "elastic-transport" version = "8.12.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f1/5e/9d697ca2511c2ecb3a239be91d5186a14fdbc97e15369c4ca6524c2929e8/elastic-transport-8.12.0.tar.gz", hash = "sha256:48839b942fcce199eece1558ecea6272e116c58da87ca8d495ef12eb61effaf7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/5e/9d697ca2511c2ecb3a239be91d5186a14fdbc97e15369c4ca6524c2929e8/elastic-transport-8.12.0.tar.gz", hash = "sha256:48839b942fcce199eece1558ecea6272e116c58da87ca8d495ef12eb61effaf7", size = 68977, upload-time = "2024-01-19T08:56:39.983Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d6/35/94475b9a18eec053ebce144ff1e28c175772ce82244ada6ffc10b1a65bcc/elastic_transport-8.12.0-py3-none-any.whl", hash = "sha256:87d9dc9dee64a05235e7624ed7e6ab6e5ca16619aa7a6d22e853273b9f1cfbee" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/35/94475b9a18eec053ebce144ff1e28c175772ce82244ada6ffc10b1a65bcc/elastic_transport-8.12.0-py3-none-any.whl", hash = "sha256:87d9dc9dee64a05235e7624ed7e6ab6e5ca16619aa7a6d22e853273b9f1cfbee", size = 59880, upload-time = "2024-01-19T08:56:37.877Z" }, ] [[package]] name = "elasticsearch" version = "8.12.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "elastic-transport" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/dc/e3/9be84318c57c7c1f488586fcf1f37edb907dfad8c9450f66429e04d7568a/elasticsearch-8.12.1.tar.gz", hash = "sha256:00c997720fbd0f2afe5417c8193cf65d116817a0250de0521e30c3e81f00b8ac" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/e3/9be84318c57c7c1f488586fcf1f37edb907dfad8c9450f66429e04d7568a/elasticsearch-8.12.1.tar.gz", hash = "sha256:00c997720fbd0f2afe5417c8193cf65d116817a0250de0521e30c3e81f00b8ac", size = 345835, upload-time = "2024-02-22T04:50:52.634Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d6/6f/79f61e0c869363eccc85322b3004bee26ebabf038e84ce2798c872c69fa8/elasticsearch-8.12.1-py3-none-any.whl", hash = "sha256:cc459b7e0fb88dc85b43b9d7d254cffad552b0063a3e0a12290c8fa5f138c038" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/6f/79f61e0c869363eccc85322b3004bee26ebabf038e84ce2798c872c69fa8/elasticsearch-8.12.1-py3-none-any.whl", hash = "sha256:cc459b7e0fb88dc85b43b9d7d254cffad552b0063a3e0a12290c8fa5f138c038", size = 432136, upload-time = "2024-02-22T04:50:48.223Z" }, ] [[package]] name = "elasticsearch-dsl" version = "8.12.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "elasticsearch" }, { name = "python-dateutil" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/96/85/152eb3ed7af5f4d4a6cca563125491b61109a265a6e7a950a239209f4564/elasticsearch-dsl-8.12.0.tar.gz", hash = "sha256:ce32b8529888a97be911531e7590816cf3b1f608263eff6fb75aa7106e233c88" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/85/152eb3ed7af5f4d4a6cca563125491b61109a265a6e7a950a239209f4564/elasticsearch-dsl-8.12.0.tar.gz", hash = "sha256:ce32b8529888a97be911531e7590816cf3b1f608263eff6fb75aa7106e233c88", size = 78878, upload-time = "2024-01-19T10:51:25.281Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6b/ee/4699000ef357e476a3984fd1eff236f820e3346c4aef7c7772e580b81b31/elasticsearch_dsl-8.12.0-py3-none-any.whl", hash = "sha256:2ea9e6ded64d21a8f1ef72477a4d116c6fbeea631ac32a2e2490b9c0d09a99a6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/ee/4699000ef357e476a3984fd1eff236f820e3346c4aef7c7772e580b81b31/elasticsearch_dsl-8.12.0-py3-none-any.whl", hash = "sha256:2ea9e6ded64d21a8f1ef72477a4d116c6fbeea631ac32a2e2490b9c0d09a99a6", size = 63976, upload-time = "2024-01-19T10:51:21.894Z" }, ] [[package]] name = "et-xmlfile" version = "2.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, ] [[package]] name = "events" version = "0.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/25/ed/e47dec0626edd468c84c04d97769e7ab4ea6457b7f54dcb3f72b17fcd876/Events-0.5-py3-none-any.whl", hash = "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/ed/e47dec0626edd468c84c04d97769e7ab4ea6457b7f54dcb3f72b17fcd876/Events-0.5-py3-none-any.whl", hash = "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd" }, ] [[package]] name = "exceptiongroup" version = "1.3.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] [[package]] @@ -1502,50 +1502,50 @@ wheels = [ [[package]] name = "fake-http-header" version = "0.3.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e3/0b/2849c87d9f13766e29c0a2f4d31681aa72e035016b251ab19d99bde7b592/fake_http_header-0.3.5-py3-none-any.whl", hash = "sha256:cd05f4bebf1b7e38b5f5c03d7fb820c0c17e87d9614fbee0afa39c32c7a2ad3c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/0b/2849c87d9f13766e29c0a2f4d31681aa72e035016b251ab19d99bde7b592/fake_http_header-0.3.5-py3-none-any.whl", hash = "sha256:cd05f4bebf1b7e38b5f5c03d7fb820c0c17e87d9614fbee0afa39c32c7a2ad3c", size = 14938, upload-time = "2024-10-15T07:27:10.671Z" }, ] [[package]] name = "fake-useragent" version = "1.5.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/24/a1/1f662631ab153975fa8dbf09296324ecbaf53370dce922054e8de6b57370/fake-useragent-1.5.1.tar.gz", hash = "sha256:6387269f5a2196b5ba7ed8935852f75486845a1c95c50e72460e6a8e762f5c49" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/a1/1f662631ab153975fa8dbf09296324ecbaf53370dce922054e8de6b57370/fake-useragent-1.5.1.tar.gz", hash = "sha256:6387269f5a2196b5ba7ed8935852f75486845a1c95c50e72460e6a8e762f5c49", size = 22631, upload-time = "2024-03-16T14:28:32.271Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e4/99/60d8cf1b26938c2e0a57e232f7f15641dfcd6f8deda454d73e4145910ff6/fake_useragent-1.5.1-py3-none-any.whl", hash = "sha256:57415096557c8a4e23b62a375c21c55af5fd4ba30549227f562d2c4f5b60e3b3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/99/60d8cf1b26938c2e0a57e232f7f15641dfcd6f8deda454d73e4145910ff6/fake_useragent-1.5.1-py3-none-any.whl", hash = "sha256:57415096557c8a4e23b62a375c21c55af5fd4ba30549227f562d2c4f5b60e3b3", size = 17190, upload-time = "2024-03-16T14:28:30.259Z" }, ] [[package]] name = "fastavro" version = "1.11.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/48/8f/32664a3245247b13702d13d2657ea534daf64e58a3f72a3a2d10598d6916/fastavro-1.11.1.tar.gz", hash = "sha256:bf6acde5ee633a29fb8dfd6dfea13b164722bc3adc05a0e055df080549c1c2f8" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ae/be/53df3fec7fdabc1848896a76afb0f01ab96b58abb29611aa68a994290167/fastavro-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:603aa1c1d1be21fb4bcb63e1efb0711a9ddb337de81391c32dac95c6e0dacfcc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/cc/c7c76a082fbf5aaaf82ab7da7b9ede6fc99eb8f008c084c67d230b29c446/fastavro-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45653b312d4ce297e2bd802ea3ffd17ecbe718e5e8b6e2ae04cd72cb50bb99d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/ff/5f1f0b5e3835e788ba8121d6dd6426cd4c6e58ce1bff02cb7810278648b0/fastavro-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998a53fc552e6bee9acda32af258f02557313c85fb5b48becba5b71ec82f421e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/b8/1ac01433b55460dabeb6d3fbb05ba1c971d57137041e8f53b2e9f46cd033/fastavro-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9f878c9ad819467120cb066f1c73496c42eb24ecdd7c992ec996f465ef4cedad" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/a8/66e599b946ead031a5caba12772e614a7802d95476e8732e2e9481369973/fastavro-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da9e4c231ac4951092c2230ca423d8a3f2966718f072ac1e2c5d2d44c70b2a50" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/e7/17c35e2dfe8a9e4f3735eabdeec366b0edc4041bb1a84fcd528c8efd12af/fastavro-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:7423bfad3199567eeee7ad6816402c7c0ee1658b959e8c10540cfbc60ce96c2a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/63/f33d6fd50d8711f305f07ad8c7b4a25f2092288f376f484c979dcf277b07/fastavro-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3573340e4564e8962e22f814ac937ffe0d4be5eabbd2250f77738dc47e3c8fe9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f4/09/a57ad9d8cb9b8affb2e43c29d8fb8cbdc0f1156f8496067a0712c944bacc/fastavro-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7291cf47735b8bd6ff5d9b33120e6e0974f52fd5dff90cd24151b22018e7fd29" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/70/d6df59309d3754d6d4b0c7beca45b9b1a957d6725aed8da3aca247db3475/fastavro-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf3bb065d657d5bac8b2cb39945194aa086a9b3354f2da7f89c30e4dc20e08e2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/ea/122315154d2a799a2787058435ef0d4d289c0e8e575245419436e9b702ca/fastavro-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8758317c85296b848698132efb13bc44a4fbd6017431cc0f26eaeb0d6fa13d35" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/12/7800de5fec36d55a818adf3db3b085b1a033c4edd60323cf6ca0754cf8cb/fastavro-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad99d57228f83bf3e2214d183fbf6e2fda97fd649b2bdaf8e9110c36cbb02624" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/65/2b74ccfeba9dcc3f7dbe64907307386b4a0af3f71d2846f63254df0f1e1d/fastavro-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:9134090178bdbf9eefd467717ced3dc151e27a7e7bfc728260ce512697efe5a4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/58/8e789b0a2f532b22e2d090c20d27c88f26a5faadcba4c445c6958ae566cf/fastavro-1.11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e8bc238f2637cd5d15238adbe8fb8c58d2e6f1870e0fb28d89508584670bae4b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/3f/02ed44742b1224fe23c9fc9b9b037fc61769df716c083cf80b59a02b9785/fastavro-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b403933081c83fc4d8a012ee64b86e560a024b1280e3711ee74f2abc904886e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/bc/9cc8b19eeee9039dd49719f8b4020771e805def262435f823fa8f27ddeea/fastavro-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f6ecb4b5f77aa756d973b7dd1c2fb4e4c95b4832a3c98b059aa96c61870c709" }, - { url = "https://mirrors.aliyun.com/pypi/packages/39/77/3b73a986606494596b6d3032eadf813a05b59d1623f54384a23de4217d5f/fastavro-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:059893df63ef823b0231b485c9d43016c7e32850cae7bf69f4e9d46dd41c28f2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/1c/b69ceef6494bd0df14752b5d8648b159ad52566127bfd575e9f5ecc0c092/fastavro-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5120ffc9a200699218e01777e695a2f08afb3547ba818184198c757dc39417bd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ef/11/5c2d0db3bd0e6407546fabae9e267bb0824eacfeba79e7dd81ad88afa27d/fastavro-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:7bb9d0d2233f33a52908b6ea9b376fe0baf1144bdfdfb3c6ad326e200a8b56b0" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/8f/32664a3245247b13702d13d2657ea534daf64e58a3f72a3a2d10598d6916/fastavro-1.11.1.tar.gz", hash = "sha256:bf6acde5ee633a29fb8dfd6dfea13b164722bc3adc05a0e055df080549c1c2f8", size = 1016250, upload-time = "2025-05-18T04:54:31.413Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/be/53df3fec7fdabc1848896a76afb0f01ab96b58abb29611aa68a994290167/fastavro-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:603aa1c1d1be21fb4bcb63e1efb0711a9ddb337de81391c32dac95c6e0dacfcc", size = 944225, upload-time = "2025-05-18T04:54:34.586Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/cc/c7c76a082fbf5aaaf82ab7da7b9ede6fc99eb8f008c084c67d230b29c446/fastavro-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45653b312d4ce297e2bd802ea3ffd17ecbe718e5e8b6e2ae04cd72cb50bb99d5", size = 3105189, upload-time = "2025-05-18T04:54:36.855Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/ff/5f1f0b5e3835e788ba8121d6dd6426cd4c6e58ce1bff02cb7810278648b0/fastavro-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998a53fc552e6bee9acda32af258f02557313c85fb5b48becba5b71ec82f421e", size = 3113124, upload-time = "2025-05-18T04:54:40.013Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/b8/1ac01433b55460dabeb6d3fbb05ba1c971d57137041e8f53b2e9f46cd033/fastavro-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9f878c9ad819467120cb066f1c73496c42eb24ecdd7c992ec996f465ef4cedad", size = 3155196, upload-time = "2025-05-18T04:54:42.307Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/a8/66e599b946ead031a5caba12772e614a7802d95476e8732e2e9481369973/fastavro-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da9e4c231ac4951092c2230ca423d8a3f2966718f072ac1e2c5d2d44c70b2a50", size = 3229028, upload-time = "2025-05-18T04:54:44.503Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/e7/17c35e2dfe8a9e4f3735eabdeec366b0edc4041bb1a84fcd528c8efd12af/fastavro-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:7423bfad3199567eeee7ad6816402c7c0ee1658b959e8c10540cfbc60ce96c2a", size = 449177, upload-time = "2025-05-18T04:54:46.127Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/63/f33d6fd50d8711f305f07ad8c7b4a25f2092288f376f484c979dcf277b07/fastavro-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3573340e4564e8962e22f814ac937ffe0d4be5eabbd2250f77738dc47e3c8fe9", size = 957526, upload-time = "2025-05-18T04:54:47.701Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/09/a57ad9d8cb9b8affb2e43c29d8fb8cbdc0f1156f8496067a0712c944bacc/fastavro-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7291cf47735b8bd6ff5d9b33120e6e0974f52fd5dff90cd24151b22018e7fd29", size = 3322808, upload-time = "2025-05-18T04:54:50.419Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/70/d6df59309d3754d6d4b0c7beca45b9b1a957d6725aed8da3aca247db3475/fastavro-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf3bb065d657d5bac8b2cb39945194aa086a9b3354f2da7f89c30e4dc20e08e2", size = 3330870, upload-time = "2025-05-18T04:54:52.406Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/ea/122315154d2a799a2787058435ef0d4d289c0e8e575245419436e9b702ca/fastavro-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8758317c85296b848698132efb13bc44a4fbd6017431cc0f26eaeb0d6fa13d35", size = 3343369, upload-time = "2025-05-18T04:54:54.652Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/12/7800de5fec36d55a818adf3db3b085b1a033c4edd60323cf6ca0754cf8cb/fastavro-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad99d57228f83bf3e2214d183fbf6e2fda97fd649b2bdaf8e9110c36cbb02624", size = 3430629, upload-time = "2025-05-18T04:54:56.513Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/65/2b74ccfeba9dcc3f7dbe64907307386b4a0af3f71d2846f63254df0f1e1d/fastavro-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:9134090178bdbf9eefd467717ced3dc151e27a7e7bfc728260ce512697efe5a4", size = 451621, upload-time = "2025-05-18T04:54:58.156Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/58/8e789b0a2f532b22e2d090c20d27c88f26a5faadcba4c445c6958ae566cf/fastavro-1.11.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e8bc238f2637cd5d15238adbe8fb8c58d2e6f1870e0fb28d89508584670bae4b", size = 939583, upload-time = "2025-05-18T04:54:59.853Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/3f/02ed44742b1224fe23c9fc9b9b037fc61769df716c083cf80b59a02b9785/fastavro-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b403933081c83fc4d8a012ee64b86e560a024b1280e3711ee74f2abc904886e8", size = 3257734, upload-time = "2025-05-18T04:55:02.366Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/bc/9cc8b19eeee9039dd49719f8b4020771e805def262435f823fa8f27ddeea/fastavro-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f6ecb4b5f77aa756d973b7dd1c2fb4e4c95b4832a3c98b059aa96c61870c709", size = 3318218, upload-time = "2025-05-18T04:55:04.352Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/77/3b73a986606494596b6d3032eadf813a05b59d1623f54384a23de4217d5f/fastavro-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:059893df63ef823b0231b485c9d43016c7e32850cae7bf69f4e9d46dd41c28f2", size = 3297296, upload-time = "2025-05-18T04:55:06.175Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/1c/b69ceef6494bd0df14752b5d8648b159ad52566127bfd575e9f5ecc0c092/fastavro-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5120ffc9a200699218e01777e695a2f08afb3547ba818184198c757dc39417bd", size = 3438056, upload-time = "2025-05-18T04:55:08.276Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/11/5c2d0db3bd0e6407546fabae9e267bb0824eacfeba79e7dd81ad88afa27d/fastavro-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:7bb9d0d2233f33a52908b6ea9b376fe0baf1144bdfdfb3c6ad326e200a8b56b0", size = 442824, upload-time = "2025-05-18T04:55:10.385Z" }, ] [[package]] name = "fastembed" version = "0.3.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "huggingface-hub" }, { name = "loguru" }, @@ -1560,15 +1560,15 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ae/20/68a109c8def842ed47a2951873fb2d7d23ee296ef8c195aedbb735670fff/fastembed-0.3.6.tar.gz", hash = "sha256:c93c8ec99b8c008c2d192d6297866b8d70ec7ac8f5696b34eb5ea91f85efd15f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/20/68a109c8def842ed47a2951873fb2d7d23ee296ef8c195aedbb735670fff/fastembed-0.3.6.tar.gz", hash = "sha256:c93c8ec99b8c008c2d192d6297866b8d70ec7ac8f5696b34eb5ea91f85efd15f", size = 35058, upload-time = "2024-08-23T19:34:06.761Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ad/21/f0efccf36cb16a18e32bd151d4469fbf86113b2ec63f45bcc0868401af68/fastembed-0.3.6-py3-none-any.whl", hash = "sha256:2bf70edae28bb4ccd9e01617098c2075b0ba35b88025a3d22b0e1e85b2c488ce" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/21/f0efccf36cb16a18e32bd151d4469fbf86113b2ec63f45bcc0868401af68/fastembed-0.3.6-py3-none-any.whl", hash = "sha256:2bf70edae28bb4ccd9e01617098c2075b0ba35b88025a3d22b0e1e85b2c488ce", size = 55647, upload-time = "2024-08-23T19:34:05.72Z" }, ] [[package]] name = "fastembed-gpu" version = "0.3.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "huggingface-hub", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "loguru", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, @@ -1582,15 +1582,15 @@ dependencies = [ { name = "tokenizers", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "tqdm", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/da/07/7336c7f3d7ee47f33b407eeb50f5eeb152889de538a52a8f1cc637192816/fastembed_gpu-0.3.6.tar.gz", hash = "sha256:ee2de8918b142adbbf48caaffec0c492f864d73c073eea5a3dcd0e8c1041c50d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/07/7336c7f3d7ee47f33b407eeb50f5eeb152889de538a52a8f1cc637192816/fastembed_gpu-0.3.6.tar.gz", hash = "sha256:ee2de8918b142adbbf48caaffec0c492f864d73c073eea5a3dcd0e8c1041c50d", size = 35051, upload-time = "2024-08-23T19:34:11.866Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2b/8d/38a2e63594e91bffe076e861955c8674fa0e7a7ebbda709a9bb1d85f4bd7/fastembed_gpu-0.3.6-py3-none-any.whl", hash = "sha256:4a8ef0ef5e344dc2ede9c4f2ffb4573c9e65c51391eef31d8d3f67b45e82c1c4" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/8d/38a2e63594e91bffe076e861955c8674fa0e7a7ebbda709a9bb1d85f4bd7/fastembed_gpu-0.3.6-py3-none-any.whl", hash = "sha256:4a8ef0ef5e344dc2ede9c4f2ffb4573c9e65c51391eef31d8d3f67b45e82c1c4", size = 55680, upload-time = "2024-08-23T19:34:10.203Z" }, ] [[package]] name = "fastparquet" version = "2024.11.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cramjam" }, { name = "fsspec" }, @@ -1598,59 +1598,59 @@ dependencies = [ { name = "packaging" }, { name = "pandas" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b4/66/862da14f5fde4eff2cedc0f51a8dc34ba145088e5041b45b2d57ac54f922/fastparquet-2024.11.0.tar.gz", hash = "sha256:e3b1fc73fd3e1b70b0de254bae7feb890436cb67e99458b88cb9bd3cc44db419" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/3d/56/476f5b83476a256489879b78513bee737691a80905e246a2daa30ebcc362/fastparquet-2024.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:60ccf587410f0979105e17036df61bb60e1c2b81880dc91895cdb4ee65b71e7f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/ad/4ce73440df874479f7205fe5445090f71ed4e9bd77fdb3b740253ce82703/fastparquet-2024.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5ad5fc14b0567e700bea3cd528a0bd45a6f9371370b49de8889fb3d10a6574a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/37/c3164261d6183d529a59afef2749821b262c8581d837faa91043837c6f76/fastparquet-2024.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b74333914f454344458dab9d1432fda9b70d62e28dc7acb1512d937ef1424ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e6/95/cf4b175c22160ec21e4664830763bfaa80b2cf05133ef854c3f436d01c16/fastparquet-2024.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41d1610130b5cb1ce36467766191c5418cba8631e2bfe3affffaf13f9be4e7a8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2c/31/b6c8cdb6d5df964a192e4e8c8ecd979718afb9ca7e2dc9243a4368b370e9/fastparquet-2024.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d281edd625c33628ba028d3221180283d6161bc5ceb55eae1f0ca1678f864f26" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/e5/8a0575c46a7973849f8f2a88af16618b9c7efe98f249f03e3e3de69c2b86/fastparquet-2024.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fa56b19a29008c34cfe8831e810f770080debcbffc69aabd1df4d47572181f9c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bb/6a/669f8c9cf2fc6e30c9353832f870e5a2e170b458d12c5080837f742d963d/fastparquet-2024.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5914ecfa766b7763201b9f49d832a5e89c2dccad470ca4f9c9b228d9a8349756" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/c0/1374cb43924739f4542e39d972481c1f4c7dd96808a1947450808e4e7df7/fastparquet-2024.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:561202e8f0e859ccc1aa77c4aaad1d7901b2d50fd6f624ca018bae4c3c7a62ce" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/51/e0d6e702523ac923ede6c05e240f4a02533ccf2cea9fec7a43491078e920/fastparquet-2024.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:374cdfa745aa7d5188430528d5841cf823eb9ad16df72ad6dadd898ccccce3be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/c8/5c0fb644c19a8d80b2ae4d8aa7d90c2d85d0bd4a948c5c700bea5c2802ea/fastparquet-2024.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c8401bfd86cccaf0ab7c0ade58c91ae19317ff6092e1d4ad96c2178197d8124" }, - { url = "https://mirrors.aliyun.com/pypi/packages/33/4a/1e532fd1a0d4d8af7ffc7e3a8106c0bcd13ed914a93a61e299b3832dd3d2/fastparquet-2024.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9cca4c6b5969df5561c13786f9d116300db1ec22c7941e237cfca4ce602f59b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/e8/e1ede861bea68394a755d8be1aa2e2d60a3b9f6b551bfd56aeca74987e2e/fastparquet-2024.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a9387e77ac608d8978774caaf1e19de67eaa1386806e514dcb19f741b19cfe5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4f/1e/957090cccaede805583ca3f3e46e2762d0f9bf8860ecbce65197e47d84c1/fastparquet-2024.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6595d3771b3d587a31137e985f751b4d599d5c8e9af9c4858e373fdf5c3f8720" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/72/344787c685fd1531f07ae712a855a7c34d13deaa26c3fd4a9231bea7dbab/fastparquet-2024.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:053695c2f730b78a2d3925df7cd5c6444d6c1560076af907993361cc7accf3e2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/ec/ab9d5685f776a1965797eb68c4364c72edf57cd35beed2df49b34425d1df/fastparquet-2024.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a52eecc6270ae15f0d51347c3f762703dd667ca486f127dc0a21e7e59856ae5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/90/4f/7a4ea9a7ddf0a3409873f0787f355806f9e0b73f42f2acecacdd9a8eff0a/fastparquet-2024.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:e29ff7a367fafa57c6896fb6abc84126e2466811aefd3e4ad4070b9e18820e54" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/76/068ac7ec9b4fc783be21a75a6a90b8c0654da4d46934d969e524ce287787/fastparquet-2024.11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dbad4b014782bd38b58b8e9f514fe958cfa7a6c4e187859232d29fd5c5ddd849" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/9e/6d3b4188ad64ed51173263c07109a5f18f9c84a44fa39ab524fca7420cda/fastparquet-2024.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:403d31109d398b6be7ce84fa3483fc277c6a23f0b321348c0a505eb098a041cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/6c/809220bc9fbe83d107df2d664c3fb62fb81867be8f5218ac66c2e6b6a358/fastparquet-2024.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbbb9057a26acf0abad7adf58781ee357258b7708ee44a289e3bee97e2f55d42" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/2c/b3b3e6ca2e531484289024138cd4709c22512b3fe68066d7f9849da4a76c/fastparquet-2024.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63e0e416e25c15daa174aad8ba991c2e9e5b0dc347e5aed5562124261400f87b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/fe/97ed45092d0311c013996dae633122b7a51c5d9fe8dcbc2c840dc491201e/fastparquet-2024.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2d7f02f57231e6c86d26e9ea71953737202f20e948790e5d4db6d6a1a150dc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/df/02fa6aee6c0d53d1563b5bc22097076c609c4c5baa47056b0b4bed456fcf/fastparquet-2024.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbe4468146b633d8f09d7b196fea0547f213cb5ce5f76e9d1beb29eaa9593a93" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/25/f4f87557589e1923ee0e3bebbc84f08b7c56962bf90f51b116ddc54f2c9f/fastparquet-2024.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:29d5c718817bcd765fc519b17f759cad4945974421ecc1931d3bdc3e05e57fa9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/f9/98cd0c39115879be1044d59c9b76e8292776e99bb93565bf990078fd11c4/fastparquet-2024.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:74a0b3c40ab373442c0fda96b75a36e88745d8b138fcc3a6143e04682cbbb8ca" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/66/862da14f5fde4eff2cedc0f51a8dc34ba145088e5041b45b2d57ac54f922/fastparquet-2024.11.0.tar.gz", hash = "sha256:e3b1fc73fd3e1b70b0de254bae7feb890436cb67e99458b88cb9bd3cc44db419", size = 467192, upload-time = "2024-11-15T19:30:10.413Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/56/476f5b83476a256489879b78513bee737691a80905e246a2daa30ebcc362/fastparquet-2024.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:60ccf587410f0979105e17036df61bb60e1c2b81880dc91895cdb4ee65b71e7f", size = 910272, upload-time = "2024-11-12T20:37:19.594Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/ad/4ce73440df874479f7205fe5445090f71ed4e9bd77fdb3b740253ce82703/fastparquet-2024.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5ad5fc14b0567e700bea3cd528a0bd45a6f9371370b49de8889fb3d10a6574a", size = 684095, upload-time = "2024-11-12T20:37:22.957Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/37/c3164261d6183d529a59afef2749821b262c8581d837faa91043837c6f76/fastparquet-2024.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b74333914f454344458dab9d1432fda9b70d62e28dc7acb1512d937ef1424ee", size = 1700355, upload-time = "2024-11-12T20:37:25.792Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/95/cf4b175c22160ec21e4664830763bfaa80b2cf05133ef854c3f436d01c16/fastparquet-2024.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41d1610130b5cb1ce36467766191c5418cba8631e2bfe3affffaf13f9be4e7a8", size = 1714663, upload-time = "2024-11-12T20:37:28.369Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/31/b6c8cdb6d5df964a192e4e8c8ecd979718afb9ca7e2dc9243a4368b370e9/fastparquet-2024.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d281edd625c33628ba028d3221180283d6161bc5ceb55eae1f0ca1678f864f26", size = 1666729, upload-time = "2024-11-12T20:37:30.243Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/e5/8a0575c46a7973849f8f2a88af16618b9c7efe98f249f03e3e3de69c2b86/fastparquet-2024.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fa56b19a29008c34cfe8831e810f770080debcbffc69aabd1df4d47572181f9c", size = 1741669, upload-time = "2024-11-12T20:37:32.067Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/6a/669f8c9cf2fc6e30c9353832f870e5a2e170b458d12c5080837f742d963d/fastparquet-2024.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5914ecfa766b7763201b9f49d832a5e89c2dccad470ca4f9c9b228d9a8349756", size = 1782359, upload-time = "2024-11-12T20:37:33.806Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/c0/1374cb43924739f4542e39d972481c1f4c7dd96808a1947450808e4e7df7/fastparquet-2024.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:561202e8f0e859ccc1aa77c4aaad1d7901b2d50fd6f624ca018bae4c3c7a62ce", size = 670700, upload-time = "2024-11-12T20:37:35.312Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/51/e0d6e702523ac923ede6c05e240f4a02533ccf2cea9fec7a43491078e920/fastparquet-2024.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:374cdfa745aa7d5188430528d5841cf823eb9ad16df72ad6dadd898ccccce3be", size = 909934, upload-time = "2024-11-12T20:37:37.049Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/c8/5c0fb644c19a8d80b2ae4d8aa7d90c2d85d0bd4a948c5c700bea5c2802ea/fastparquet-2024.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c8401bfd86cccaf0ab7c0ade58c91ae19317ff6092e1d4ad96c2178197d8124", size = 683844, upload-time = "2024-11-12T20:37:38.456Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/4a/1e532fd1a0d4d8af7ffc7e3a8106c0bcd13ed914a93a61e299b3832dd3d2/fastparquet-2024.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9cca4c6b5969df5561c13786f9d116300db1ec22c7941e237cfca4ce602f59b", size = 1791698, upload-time = "2024-11-12T20:37:41.101Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/e8/e1ede861bea68394a755d8be1aa2e2d60a3b9f6b551bfd56aeca74987e2e/fastparquet-2024.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a9387e77ac608d8978774caaf1e19de67eaa1386806e514dcb19f741b19cfe5", size = 1804289, upload-time = "2024-11-12T20:37:43.08Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/1e/957090cccaede805583ca3f3e46e2762d0f9bf8860ecbce65197e47d84c1/fastparquet-2024.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6595d3771b3d587a31137e985f751b4d599d5c8e9af9c4858e373fdf5c3f8720", size = 1753638, upload-time = "2024-11-12T20:37:45.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/72/344787c685fd1531f07ae712a855a7c34d13deaa26c3fd4a9231bea7dbab/fastparquet-2024.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:053695c2f730b78a2d3925df7cd5c6444d6c1560076af907993361cc7accf3e2", size = 1814407, upload-time = "2024-11-12T20:37:47.25Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/ec/ab9d5685f776a1965797eb68c4364c72edf57cd35beed2df49b34425d1df/fastparquet-2024.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a52eecc6270ae15f0d51347c3f762703dd667ca486f127dc0a21e7e59856ae5", size = 1874462, upload-time = "2024-11-12T20:37:49.755Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/4f/7a4ea9a7ddf0a3409873f0787f355806f9e0b73f42f2acecacdd9a8eff0a/fastparquet-2024.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:e29ff7a367fafa57c6896fb6abc84126e2466811aefd3e4ad4070b9e18820e54", size = 671023, upload-time = "2024-11-12T20:37:51.461Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/76/068ac7ec9b4fc783be21a75a6a90b8c0654da4d46934d969e524ce287787/fastparquet-2024.11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dbad4b014782bd38b58b8e9f514fe958cfa7a6c4e187859232d29fd5c5ddd849", size = 915968, upload-time = "2024-11-12T20:37:52.861Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/9e/6d3b4188ad64ed51173263c07109a5f18f9c84a44fa39ab524fca7420cda/fastparquet-2024.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:403d31109d398b6be7ce84fa3483fc277c6a23f0b321348c0a505eb098a041cb", size = 685399, upload-time = "2024-11-12T20:37:54.899Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/6c/809220bc9fbe83d107df2d664c3fb62fb81867be8f5218ac66c2e6b6a358/fastparquet-2024.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbbb9057a26acf0abad7adf58781ee357258b7708ee44a289e3bee97e2f55d42", size = 1758557, upload-time = "2024-11-12T20:37:56.553Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/2c/b3b3e6ca2e531484289024138cd4709c22512b3fe68066d7f9849da4a76c/fastparquet-2024.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63e0e416e25c15daa174aad8ba991c2e9e5b0dc347e5aed5562124261400f87b", size = 1781052, upload-time = "2024-11-12T20:37:58.339Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/fe/97ed45092d0311c013996dae633122b7a51c5d9fe8dcbc2c840dc491201e/fastparquet-2024.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2d7f02f57231e6c86d26e9ea71953737202f20e948790e5d4db6d6a1a150dc", size = 1715797, upload-time = "2024-11-12T20:38:00.694Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/df/02fa6aee6c0d53d1563b5bc22097076c609c4c5baa47056b0b4bed456fcf/fastparquet-2024.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbe4468146b633d8f09d7b196fea0547f213cb5ce5f76e9d1beb29eaa9593a93", size = 1795682, upload-time = "2024-11-12T20:38:02.38Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/25/f4f87557589e1923ee0e3bebbc84f08b7c56962bf90f51b116ddc54f2c9f/fastparquet-2024.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:29d5c718817bcd765fc519b17f759cad4945974421ecc1931d3bdc3e05e57fa9", size = 1857842, upload-time = "2024-11-12T20:38:04.196Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/f9/98cd0c39115879be1044d59c9b76e8292776e99bb93565bf990078fd11c4/fastparquet-2024.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:74a0b3c40ab373442c0fda96b75a36e88745d8b138fcc3a6143e04682cbbb8ca", size = 673269, upload-time = "2024-12-11T21:22:48.073Z" }, ] [[package]] name = "feedparser" version = "6.0.11" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "sgmllib3k" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ff/aa/7af346ebeb42a76bf108027fe7f3328bb4e57a3a96e53e21fd9ef9dd6dd0/feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/aa/7af346ebeb42a76bf108027fe7f3328bb4e57a3a96e53e21fd9ef9dd6dd0/feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5", size = 286197, upload-time = "2023-12-10T16:03:20.854Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7c/d4/8c31aad9cc18f451c49f7f9cfb5799dadffc88177f7917bc90a66459b1d7/feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/d4/8c31aad9cc18f451c49f7f9cfb5799dadffc88177f7917bc90a66459b1d7/feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45", size = 81343, upload-time = "2023-12-10T16:03:19.484Z" }, ] [[package]] name = "filelock" version = "3.15.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/08/dd/49e06f09b6645156550fb9aee9cc1e59aba7efbc972d665a1bd6ae0435d4/filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/dd/49e06f09b6645156550fb9aee9cc1e59aba7efbc972d665a1bd6ae0435d4/filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb", size = 18007, upload-time = "2024-06-22T15:59:14.749Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ae/f0/48285f0262fe47103a4a45972ed2f9b93e4c80b8fd609fa98da78b2a5706/filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/f0/48285f0262fe47103a4a45972ed2f9b93e4c80b8fd609fa98da78b2a5706/filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7", size = 16159, upload-time = "2024-06-22T15:59:12.695Z" }, ] [[package]] name = "flagembedding" version = "1.2.10" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "accelerate" }, { name = "datasets" }, @@ -1658,12 +1658,12 @@ dependencies = [ { name = "torch" }, { name = "transformers" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d5/4c/6bfbbf677aea4a0eb33da8516c8098dd1e1f57dd9bfae8df396c3b9d68ff/FlagEmbedding-1.2.10.tar.gz", hash = "sha256:0d0a553e5cca935bb80576f031023e8cb1775e6f9b29b0ab6bbe210697755d30" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/4c/6bfbbf677aea4a0eb33da8516c8098dd1e1f57dd9bfae8df396c3b9d68ff/FlagEmbedding-1.2.10.tar.gz", hash = "sha256:0d0a553e5cca935bb80576f031023e8cb1775e6f9b29b0ab6bbe210697755d30", size = 141345, upload-time = "2024-05-23T06:01:32.649Z" } [[package]] name = "flasgger" version = "0.9.7.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "flask" }, { name = "jsonschema" }, @@ -1672,12 +1672,12 @@ dependencies = [ { name = "pyyaml" }, { name = "six" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8a/e4/05e80adeadc39f171b51bd29b24a6d9838127f3aaa1b07c1501e662a8cee/flasgger-0.9.7.1.tar.gz", hash = "sha256:ca098e10bfbb12f047acc6299cc70a33851943a746e550d86e65e60d4df245fb" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/e4/05e80adeadc39f171b51bd29b24a6d9838127f3aaa1b07c1501e662a8cee/flasgger-0.9.7.1.tar.gz", hash = "sha256:ca098e10bfbb12f047acc6299cc70a33851943a746e550d86e65e60d4df245fb" } [[package]] name = "flask" version = "3.0.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "blinker" }, { name = "click" }, @@ -1685,200 +1685,200 @@ dependencies = [ { name = "jinja2" }, { name = "werkzeug" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/41/e1/d104c83026f8d35dfd2c261df7d64738341067526406b40190bc063e829a/flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/e1/d104c83026f8d35dfd2c261df7d64738341067526406b40190bc063e829a/flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842", size = 676315, upload-time = "2024-04-07T19:26:11.035Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/61/80/ffe1da13ad9300f87c93af113edd0638c75138c42a0994becfacac078c06/flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/80/ffe1da13ad9300f87c93af113edd0638c75138c42a0994becfacac078c06/flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3", size = 101735, upload-time = "2024-04-07T19:26:08.569Z" }, ] [[package]] name = "flask-cors" version = "5.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "flask" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4f/d0/d9e52b154e603b0faccc0b7c2ad36a764d8755ef4036acbf1582a67fb86b/flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/d0/d9e52b154e603b0faccc0b7c2ad36a764d8755ef4036acbf1582a67fb86b/flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef", size = 30954, upload-time = "2024-08-31T00:44:26.395Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/56/07/1afa0514c876282bebc1c9aee83c6bb98fe6415cf57b88d9b06e7e29bf9c/Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/07/1afa0514c876282bebc1c9aee83c6bb98fe6415cf57b88d9b06e7e29bf9c/Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc", size = 14463, upload-time = "2024-08-31T00:44:24.394Z" }, ] [[package]] name = "flask-login" version = "0.6.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "flask" }, { name = "werkzeug" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d" }, ] [[package]] name = "flask-mail" version = "0.10.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "blinker" }, { name = "flask" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ba/29/e92dc84c675d1e8d260d5768eb3fb65c70cbd33addecf424187587bee862/flask_mail-0.10.0.tar.gz", hash = "sha256:44083e7b02bbcce792209c06252f8569dd5a325a7aaa76afe7330422bd97881d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/29/e92dc84c675d1e8d260d5768eb3fb65c70cbd33addecf424187587bee862/flask_mail-0.10.0.tar.gz", hash = "sha256:44083e7b02bbcce792209c06252f8569dd5a325a7aaa76afe7330422bd97881d", size = 8152, upload-time = "2024-05-23T22:30:12.612Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e4/c0/a81083da779f482494d49195d8b6c9fde21072558253e4a9fb2ec969c3c1/flask_mail-0.10.0-py3-none-any.whl", hash = "sha256:a451e490931bb3441d9b11ebab6812a16bfa81855792ae1bf9c1e1e22c4e51e7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/c0/a81083da779f482494d49195d8b6c9fde21072558253e4a9fb2ec969c3c1/flask_mail-0.10.0-py3-none-any.whl", hash = "sha256:a451e490931bb3441d9b11ebab6812a16bfa81855792ae1bf9c1e1e22c4e51e7", size = 8529, upload-time = "2024-05-23T22:30:10.962Z" }, ] [[package]] name = "flask-session" version = "0.8.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cachelib" }, { name = "flask" }, { name = "msgspec" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/86/d7/0ba4180513abe28eadc208123c76f9f09e290d5939fb2eb68323b9733354/flask_session-0.8.0.tar.gz", hash = "sha256:20e045eb01103694e70be4a49f3a80dbb1b57296a22dc6f44bbf3f83ef0742ff" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/d7/0ba4180513abe28eadc208123c76f9f09e290d5939fb2eb68323b9733354/flask_session-0.8.0.tar.gz", hash = "sha256:20e045eb01103694e70be4a49f3a80dbb1b57296a22dc6f44bbf3f83ef0742ff", size = 940269, upload-time = "2024-03-26T07:56:13.747Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/67/1b/f085ceebb825d1cfaf078852b67cd248a33af2905f40ba9860cc006d966b/flask_session-0.8.0-py3-none-any.whl", hash = "sha256:5dae6e9ddab334f8dc4dea4305af37851f4e7dc0f484caf3351184001195e3b7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/1b/f085ceebb825d1cfaf078852b67cd248a33af2905f40ba9860cc006d966b/flask_session-0.8.0-py3-none-any.whl", hash = "sha256:5dae6e9ddab334f8dc4dea4305af37851f4e7dc0f484caf3351184001195e3b7", size = 24410, upload-time = "2024-03-26T07:56:11.377Z" }, ] [[package]] name = "flatbuffers" version = "25.2.10" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e4/30/eb5dce7994fc71a2f685d98ec33cc660c0a5887db5610137e60d8cbc4489/flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/30/eb5dce7994fc71a2f685d98ec33cc660c0a5887db5610137e60d8cbc4489/flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e", size = 22170, upload-time = "2025-02-11T04:26:46.257Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b8/25/155f9f080d5e4bc0082edfda032ea2bc2b8fab3f4d25d46c1e9dd22a1a89/flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/25/155f9f080d5e4bc0082edfda032ea2bc2b8fab3f4d25d46c1e9dd22a1a89/flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051", size = 30953, upload-time = "2025-02-11T04:26:44.484Z" }, ] [[package]] name = "fonttools" version = "4.59.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8a/27/ec3c723bfdf86f34c5c82bf6305df3e0f0d8ea798d2d3a7cb0c0a866d286/fonttools-4.59.0.tar.gz", hash = "sha256:be392ec3529e2f57faa28709d60723a763904f71a2b63aabe14fee6648fe3b14" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/1c/1f/3dcae710b7c4b56e79442b03db64f6c9f10c3348f7af40339dffcefb581e/fonttools-4.59.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:524133c1be38445c5c0575eacea42dbd44374b310b1ffc4b60ff01d881fabb96" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/0e/ae3a1884fa1549acac1191cc9ec039142f6ac0e9cbc139c2e6a3dab967da/fonttools-4.59.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21e606b2d38fed938dde871c5736822dd6bda7a4631b92e509a1f5cd1b90c5df" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/46/58bff92a7216829159ac7bdb1d05a48ad1b8ab8c539555f12d29fdecfdd4/fonttools-4.59.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e93df708c69a193fc7987192f94df250f83f3851fda49413f02ba5dded639482" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/57/767e31e48861045d89691128bd81fd4c62b62150f9a17a666f731ce4f197/fonttools-4.59.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:62224a9bb85b4b66d1b46d45cbe43d71cbf8f527d332b177e3b96191ffbc1e64" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/78/adb5e9b0af5c6ce469e8b0e112f144eaa84b30dd72a486e9c778a9b03b31/fonttools-4.59.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8974b2a266b54c96709bd5e239979cddfd2dbceed331aa567ea1d7c4a2202db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/92/bc3881097fbf3d56d112bec308c863c058e5d4c9c65f534e8ae58450ab8a/fonttools-4.59.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:209b75943d158f610b78320eacb5539aa9e920bee2c775445b2846c65d20e19d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/54/39cdb23f0eeda2e07ae9cb189f2b6f41da89aabc682d3a387b3ff4a4ed29/fonttools-4.59.0-cp310-cp310-win32.whl", hash = "sha256:4c908a7036f0f3677f8afa577bcd973e3e20ddd2f7c42a33208d18bee95cdb6f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d8/eb/f8388d9e19f95d8df2449febe9b1a38ddd758cfdb7d6de3a05198d785d61/fonttools-4.59.0-cp310-cp310-win_amd64.whl", hash = "sha256:8b4309a2775e4feee7356e63b163969a215d663399cce1b3d3b65e7ec2d9680e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/96/520733d9602fa1bf6592e5354c6721ac6fc9ea72bc98d112d0c38b967199/fonttools-4.59.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:841b2186adce48903c0fef235421ae21549020eca942c1da773ac380b056ab3c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/87/6a/170fce30b9bce69077d8eec9bea2cfd9f7995e8911c71be905e2eba6368b/fonttools-4.59.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9bcc1e77fbd1609198966ded6b2a9897bd6c6bcbd2287a2fc7d75f1a254179c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/b6/7c8166c0066856f1408092f7968ac744060cf72ca53aec9036106f57eeca/fonttools-4.59.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37c377f7cb2ab2eca8a0b319c68146d34a339792f9420fca6cd49cf28d370705" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/0c/707c5a19598eafcafd489b73c4cb1c142102d6197e872f531512d084aa76/fonttools-4.59.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa39475eaccb98f9199eccfda4298abaf35ae0caec676ffc25b3a5e224044464" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/e7/6d33737d9fe632a0f59289b6f9743a86d2a9d0673de2a0c38c0f54729822/fonttools-4.59.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d3972b13148c1d1fbc092b27678a33b3080d1ac0ca305742b0119b75f9e87e38" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/e1/a4c3d089ab034a578820c8f2dff21ef60daf9668034a1e4fb38bb1cc3398/fonttools-4.59.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a408c3c51358c89b29cfa5317cf11518b7ce5de1717abb55c5ae2d2921027de6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/77/ca82b9c12fa4de3c520b7760ee61787640cf3fde55ef1b0bfe1de38c8153/fonttools-4.59.0-cp311-cp311-win32.whl", hash = "sha256:6770d7da00f358183d8fd5c4615436189e4f683bdb6affb02cad3d221d7bb757" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/25/5aa7ca24b560b2f00f260acf32c4cf29d7aaf8656e159a336111c18bc345/fonttools-4.59.0-cp311-cp311-win_amd64.whl", hash = "sha256:84fc186980231a287b28560d3123bd255d3c6b6659828c642b4cf961e2b923d0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/77/b1c8af22f4265e951cd2e5535dbef8859efcef4fb8dee742d368c967cddb/fonttools-4.59.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9b3a78f69dcbd803cf2fb3f972779875b244c1115481dfbdd567b2c22b31f6b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/5a/aeb975699588176bb357e8b398dfd27e5d3a2230d92b81ab8cbb6187358d/fonttools-4.59.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:57bb7e26928573ee7c6504f54c05860d867fd35e675769f3ce01b52af38d48e2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/97/c6101a7e60ae138c4ef75b22434373a0da50a707dad523dd19a4889315bf/fonttools-4.59.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4536f2695fe5c1ffb528d84a35a7d3967e5558d2af58b4775e7ab1449d65767b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bd/6c/fa4d18d641054f7bff878cbea14aa9433f292b9057cb1700d8e91a4d5f4f/fonttools-4.59.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:885bde7d26e5b40e15c47bd5def48b38cbd50830a65f98122a8fb90962af7cd1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/5c/331947fc1377deb928a69bde49f9003364f5115e5cbe351eea99e39412a2/fonttools-4.59.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6801aeddb6acb2c42eafa45bc1cb98ba236871ae6f33f31e984670b749a8e58e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/46/b66469dfa26b8ff0baa7654b2cc7851206c6d57fe3abdabbaab22079a119/fonttools-4.59.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:31003b6a10f70742a63126b80863ab48175fb8272a18ca0846c0482968f0588e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/05/ebfb6b1f3a4328ab69787d106a7d92ccde77ce66e98659df0f9e3f28d93d/fonttools-4.59.0-cp312-cp312-win32.whl", hash = "sha256:fbce6dae41b692a5973d0f2158f782b9ad05babc2c2019a970a1094a23909b1b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/45/d2bdc9ea20bbadec1016fd0db45696d573d7a26d95ab5174ffcb6d74340b/fonttools-4.59.0-cp312-cp312-win_amd64.whl", hash = "sha256:332bfe685d1ac58ca8d62b8d6c71c2e52a6c64bc218dc8f7825c9ea51385aa01" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/9c/df0ef2c51845a13043e5088f7bb988ca6cd5bb82d5d4203d6a158aa58cf2/fonttools-4.59.0-py3-none-any.whl", hash = "sha256:241313683afd3baacb32a6bd124d0bce7404bc5280e12e291bae1b9bba28711d" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/27/ec3c723bfdf86f34c5c82bf6305df3e0f0d8ea798d2d3a7cb0c0a866d286/fonttools-4.59.0.tar.gz", hash = "sha256:be392ec3529e2f57faa28709d60723a763904f71a2b63aabe14fee6648fe3b14", size = 3532521, upload-time = "2025-07-16T12:04:54.613Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/1f/3dcae710b7c4b56e79442b03db64f6c9f10c3348f7af40339dffcefb581e/fonttools-4.59.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:524133c1be38445c5c0575eacea42dbd44374b310b1ffc4b60ff01d881fabb96", size = 2761846, upload-time = "2025-07-16T12:03:33.267Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/0e/ae3a1884fa1549acac1191cc9ec039142f6ac0e9cbc139c2e6a3dab967da/fonttools-4.59.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21e606b2d38fed938dde871c5736822dd6bda7a4631b92e509a1f5cd1b90c5df", size = 2332060, upload-time = "2025-07-16T12:03:36.472Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/46/58bff92a7216829159ac7bdb1d05a48ad1b8ab8c539555f12d29fdecfdd4/fonttools-4.59.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e93df708c69a193fc7987192f94df250f83f3851fda49413f02ba5dded639482", size = 4852354, upload-time = "2025-07-16T12:03:39.102Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/57/767e31e48861045d89691128bd81fd4c62b62150f9a17a666f731ce4f197/fonttools-4.59.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:62224a9bb85b4b66d1b46d45cbe43d71cbf8f527d332b177e3b96191ffbc1e64", size = 4781132, upload-time = "2025-07-16T12:03:41.415Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/78/adb5e9b0af5c6ce469e8b0e112f144eaa84b30dd72a486e9c778a9b03b31/fonttools-4.59.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8974b2a266b54c96709bd5e239979cddfd2dbceed331aa567ea1d7c4a2202db", size = 4832901, upload-time = "2025-07-16T12:03:43.115Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/92/bc3881097fbf3d56d112bec308c863c058e5d4c9c65f534e8ae58450ab8a/fonttools-4.59.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:209b75943d158f610b78320eacb5539aa9e920bee2c775445b2846c65d20e19d", size = 4940140, upload-time = "2025-07-16T12:03:44.781Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/54/39cdb23f0eeda2e07ae9cb189f2b6f41da89aabc682d3a387b3ff4a4ed29/fonttools-4.59.0-cp310-cp310-win32.whl", hash = "sha256:4c908a7036f0f3677f8afa577bcd973e3e20ddd2f7c42a33208d18bee95cdb6f", size = 2215890, upload-time = "2025-07-16T12:03:46.961Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/eb/f8388d9e19f95d8df2449febe9b1a38ddd758cfdb7d6de3a05198d785d61/fonttools-4.59.0-cp310-cp310-win_amd64.whl", hash = "sha256:8b4309a2775e4feee7356e63b163969a215d663399cce1b3d3b65e7ec2d9680e", size = 2260191, upload-time = "2025-07-16T12:03:48.908Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/96/520733d9602fa1bf6592e5354c6721ac6fc9ea72bc98d112d0c38b967199/fonttools-4.59.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:841b2186adce48903c0fef235421ae21549020eca942c1da773ac380b056ab3c", size = 2782387, upload-time = "2025-07-16T12:03:51.424Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/6a/170fce30b9bce69077d8eec9bea2cfd9f7995e8911c71be905e2eba6368b/fonttools-4.59.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9bcc1e77fbd1609198966ded6b2a9897bd6c6bcbd2287a2fc7d75f1a254179c5", size = 2342194, upload-time = "2025-07-16T12:03:53.295Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/b6/7c8166c0066856f1408092f7968ac744060cf72ca53aec9036106f57eeca/fonttools-4.59.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37c377f7cb2ab2eca8a0b319c68146d34a339792f9420fca6cd49cf28d370705", size = 5032333, upload-time = "2025-07-16T12:03:55.177Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/0c/707c5a19598eafcafd489b73c4cb1c142102d6197e872f531512d084aa76/fonttools-4.59.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa39475eaccb98f9199eccfda4298abaf35ae0caec676ffc25b3a5e224044464", size = 4974422, upload-time = "2025-07-16T12:03:57.406Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/e7/6d33737d9fe632a0f59289b6f9743a86d2a9d0673de2a0c38c0f54729822/fonttools-4.59.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d3972b13148c1d1fbc092b27678a33b3080d1ac0ca305742b0119b75f9e87e38", size = 5010631, upload-time = "2025-07-16T12:03:59.449Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/e1/a4c3d089ab034a578820c8f2dff21ef60daf9668034a1e4fb38bb1cc3398/fonttools-4.59.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a408c3c51358c89b29cfa5317cf11518b7ce5de1717abb55c5ae2d2921027de6", size = 5122198, upload-time = "2025-07-16T12:04:01.542Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/77/ca82b9c12fa4de3c520b7760ee61787640cf3fde55ef1b0bfe1de38c8153/fonttools-4.59.0-cp311-cp311-win32.whl", hash = "sha256:6770d7da00f358183d8fd5c4615436189e4f683bdb6affb02cad3d221d7bb757", size = 2214216, upload-time = "2025-07-16T12:04:03.515Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/25/5aa7ca24b560b2f00f260acf32c4cf29d7aaf8656e159a336111c18bc345/fonttools-4.59.0-cp311-cp311-win_amd64.whl", hash = "sha256:84fc186980231a287b28560d3123bd255d3c6b6659828c642b4cf961e2b923d0", size = 2261879, upload-time = "2025-07-16T12:04:05.015Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/77/b1c8af22f4265e951cd2e5535dbef8859efcef4fb8dee742d368c967cddb/fonttools-4.59.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9b3a78f69dcbd803cf2fb3f972779875b244c1115481dfbdd567b2c22b31f6b", size = 2767562, upload-time = "2025-07-16T12:04:06.895Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/5a/aeb975699588176bb357e8b398dfd27e5d3a2230d92b81ab8cbb6187358d/fonttools-4.59.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:57bb7e26928573ee7c6504f54c05860d867fd35e675769f3ce01b52af38d48e2", size = 2335168, upload-time = "2025-07-16T12:04:08.695Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/97/c6101a7e60ae138c4ef75b22434373a0da50a707dad523dd19a4889315bf/fonttools-4.59.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4536f2695fe5c1ffb528d84a35a7d3967e5558d2af58b4775e7ab1449d65767b", size = 4909850, upload-time = "2025-07-16T12:04:10.761Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/6c/fa4d18d641054f7bff878cbea14aa9433f292b9057cb1700d8e91a4d5f4f/fonttools-4.59.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:885bde7d26e5b40e15c47bd5def48b38cbd50830a65f98122a8fb90962af7cd1", size = 4955131, upload-time = "2025-07-16T12:04:12.846Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/5c/331947fc1377deb928a69bde49f9003364f5115e5cbe351eea99e39412a2/fonttools-4.59.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6801aeddb6acb2c42eafa45bc1cb98ba236871ae6f33f31e984670b749a8e58e", size = 4899667, upload-time = "2025-07-16T12:04:14.558Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/46/b66469dfa26b8ff0baa7654b2cc7851206c6d57fe3abdabbaab22079a119/fonttools-4.59.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:31003b6a10f70742a63126b80863ab48175fb8272a18ca0846c0482968f0588e", size = 5051349, upload-time = "2025-07-16T12:04:16.388Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/05/ebfb6b1f3a4328ab69787d106a7d92ccde77ce66e98659df0f9e3f28d93d/fonttools-4.59.0-cp312-cp312-win32.whl", hash = "sha256:fbce6dae41b692a5973d0f2158f782b9ad05babc2c2019a970a1094a23909b1b", size = 2201315, upload-time = "2025-07-16T12:04:18.557Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/45/d2bdc9ea20bbadec1016fd0db45696d573d7a26d95ab5174ffcb6d74340b/fonttools-4.59.0-cp312-cp312-win_amd64.whl", hash = "sha256:332bfe685d1ac58ca8d62b8d6c71c2e52a6c64bc218dc8f7825c9ea51385aa01", size = 2249408, upload-time = "2025-07-16T12:04:20.489Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/9c/df0ef2c51845a13043e5088f7bb988ca6cd5bb82d5d4203d6a158aa58cf2/fonttools-4.59.0-py3-none-any.whl", hash = "sha256:241313683afd3baacb32a6bd124d0bce7404bc5280e12e291bae1b9bba28711d", size = 1128050, upload-time = "2025-07-16T12:04:52.687Z" }, ] [[package]] name = "free-proxy" version = "1.1.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "lxml" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d5/10/3654b44093aa3e587948c770279baca3a8dfe4d14a616142e8c6bf04b09b/free_proxy-1.1.3.tar.gz", hash = "sha256:6d82aa112e3df7725bdbf177e2110bccdf5f3bbd6e1c70b8616ec12ae3bbf98c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/10/3654b44093aa3e587948c770279baca3a8dfe4d14a616142e8c6bf04b09b/free_proxy-1.1.3.tar.gz", hash = "sha256:6d82aa112e3df7725bdbf177e2110bccdf5f3bbd6e1c70b8616ec12ae3bbf98c", size = 5607, upload-time = "2024-11-07T08:42:48.684Z" } [[package]] name = "frozendict" version = "2.4.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e", size = 316416, upload-time = "2024-10-13T12:15:32.449Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a6/7f/e80cdbe0db930b2ba9d46ca35a41b0150156da16dfb79edcc05642690c3b/frozendict-2.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3a05c0a50cab96b4bb0ea25aa752efbfceed5ccb24c007612bc63e51299336f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/98/27e145ff7e8e63caa95fb8ee4fc56c68acb208bef01a89c3678a66f9a34d/frozendict-2.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5b94d5b07c00986f9e37a38dd83c13f5fe3bf3f1ccc8e88edea8fe15d6cd88c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/f1/a10be024a9d53441c997b3661ea80ecba6e3130adc53812a4b95b607cdd1/frozendict-2.4.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c789fd70879ccb6289a603cdebdc4953e7e5dea047d30c1b180529b28257b5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/a6/34c760975e6f1cb4db59a990d58dcf22287e10241c851804670c74c6a27a/frozendict-2.4.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da6a10164c8a50b34b9ab508a9420df38f4edf286b9ca7b7df8a91767baecb34" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/dd/64bddd1ffa9617f50e7e63656b2a7ad7f0a46c86b5f4a3d2c714d0006277/frozendict-2.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9a8a43036754a941601635ea9c788ebd7a7efbed2becba01b54a887b41b175b9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/ae/af06a8bde1947277aad895c2f26c3b8b8b6ee9c0c2ad988fb58a9d1dde3f/frozendict-2.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9905dcf7aa659e6a11b8051114c9fa76dfde3a6e50e6dc129d5aece75b449a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/df/be3fa0457ff661301228f4c59c630699568c8ed9b5480f113b3eea7d0cb3/frozendict-2.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:323f1b674a2cc18f86ab81698e22aba8145d7a755e0ac2cccf142ee2db58620d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/6f/c22e0266b4c85f58b4613fec024e040e93753880527bf92b0c1bc228c27c/frozendict-2.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:eabd21d8e5db0c58b60d26b4bb9839cac13132e88277e1376970172a85ee04b3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/7f/e80cdbe0db930b2ba9d46ca35a41b0150156da16dfb79edcc05642690c3b/frozendict-2.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3a05c0a50cab96b4bb0ea25aa752efbfceed5ccb24c007612bc63e51299336f", size = 37927, upload-time = "2024-10-13T12:14:17.927Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/98/27e145ff7e8e63caa95fb8ee4fc56c68acb208bef01a89c3678a66f9a34d/frozendict-2.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5b94d5b07c00986f9e37a38dd83c13f5fe3bf3f1ccc8e88edea8fe15d6cd88c", size = 37945, upload-time = "2024-10-13T12:14:19.976Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/f1/a10be024a9d53441c997b3661ea80ecba6e3130adc53812a4b95b607cdd1/frozendict-2.4.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c789fd70879ccb6289a603cdebdc4953e7e5dea047d30c1b180529b28257b5", size = 117656, upload-time = "2024-10-13T12:14:22.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/a6/34c760975e6f1cb4db59a990d58dcf22287e10241c851804670c74c6a27a/frozendict-2.4.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da6a10164c8a50b34b9ab508a9420df38f4edf286b9ca7b7df8a91767baecb34", size = 117444, upload-time = "2024-10-13T12:14:24.251Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/dd/64bddd1ffa9617f50e7e63656b2a7ad7f0a46c86b5f4a3d2c714d0006277/frozendict-2.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9a8a43036754a941601635ea9c788ebd7a7efbed2becba01b54a887b41b175b9", size = 116801, upload-time = "2024-10-13T12:14:26.518Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/ae/af06a8bde1947277aad895c2f26c3b8b8b6ee9c0c2ad988fb58a9d1dde3f/frozendict-2.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9905dcf7aa659e6a11b8051114c9fa76dfde3a6e50e6dc129d5aece75b449a2", size = 117329, upload-time = "2024-10-13T12:14:28.485Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/df/be3fa0457ff661301228f4c59c630699568c8ed9b5480f113b3eea7d0cb3/frozendict-2.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:323f1b674a2cc18f86ab81698e22aba8145d7a755e0ac2cccf142ee2db58620d", size = 37522, upload-time = "2024-10-13T12:14:30.418Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/6f/c22e0266b4c85f58b4613fec024e040e93753880527bf92b0c1bc228c27c/frozendict-2.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:eabd21d8e5db0c58b60d26b4bb9839cac13132e88277e1376970172a85ee04b3", size = 34056, upload-time = "2024-10-13T12:14:31.757Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea", size = 16148, upload-time = "2024-10-13T12:15:26.839Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9", size = 16146, upload-time = "2024-10-13T12:15:28.16Z" }, ] [[package]] name = "frozenlist" version = "1.7.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98" }, - { url = "https://mirrors.aliyun.com/pypi/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a", size = 81304, upload-time = "2025-06-09T22:59:46.226Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61", size = 47735, upload-time = "2025-06-09T22:59:48.133Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d", size = 46775, upload-time = "2025-06-09T22:59:49.564Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e", size = 224644, upload-time = "2025-06-09T22:59:51.35Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9", size = 222125, upload-time = "2025-06-09T22:59:52.884Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c", size = 233455, upload-time = "2025-06-09T22:59:54.74Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981", size = 227339, upload-time = "2025-06-09T22:59:56.187Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615", size = 212969, upload-time = "2025-06-09T22:59:57.604Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50", size = 222862, upload-time = "2025-06-09T22:59:59.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa", size = 222492, upload-time = "2025-06-09T23:00:01.026Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577", size = 238250, upload-time = "2025-06-09T23:00:03.401Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59", size = 218720, upload-time = "2025-06-09T23:00:05.282Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e", size = 232585, upload-time = "2025-06-09T23:00:07.962Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd", size = 234248, upload-time = "2025-06-09T23:00:09.428Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718", size = 221621, upload-time = "2025-06-09T23:00:11.32Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e", size = 39578, upload-time = "2025-06-09T23:00:13.526Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464", size = 43830, upload-time = "2025-06-09T23:00:14.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251, upload-time = "2025-06-09T23:00:16.279Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183, upload-time = "2025-06-09T23:00:17.698Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107, upload-time = "2025-06-09T23:00:18.952Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333, upload-time = "2025-06-09T23:00:20.275Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724, upload-time = "2025-06-09T23:00:21.705Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842, upload-time = "2025-06-09T23:00:23.148Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767, upload-time = "2025-06-09T23:00:25.103Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130, upload-time = "2025-06-09T23:00:27.061Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301, upload-time = "2025-06-09T23:00:29.02Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606, upload-time = "2025-06-09T23:00:30.514Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372, upload-time = "2025-06-09T23:00:31.966Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860, upload-time = "2025-06-09T23:00:33.375Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893, upload-time = "2025-06-09T23:00:35.002Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323, upload-time = "2025-06-09T23:00:36.468Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149, upload-time = "2025-06-09T23:00:37.963Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565, upload-time = "2025-06-09T23:00:39.753Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019, upload-time = "2025-06-09T23:00:40.988Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424, upload-time = "2025-06-09T23:00:42.24Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952, upload-time = "2025-06-09T23:00:43.481Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688, upload-time = "2025-06-09T23:00:44.793Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084, upload-time = "2025-06-09T23:00:46.125Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524, upload-time = "2025-06-09T23:00:47.73Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493, upload-time = "2025-06-09T23:00:49.742Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116, upload-time = "2025-06-09T23:00:51.352Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557, upload-time = "2025-06-09T23:00:52.855Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820, upload-time = "2025-06-09T23:00:54.43Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542, upload-time = "2025-06-09T23:00:56.409Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350, upload-time = "2025-06-09T23:00:58.468Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093, upload-time = "2025-06-09T23:01:00.015Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482, upload-time = "2025-06-09T23:01:01.474Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590, upload-time = "2025-06-09T23:01:02.961Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785, upload-time = "2025-06-09T23:01:05.095Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487, upload-time = "2025-06-09T23:01:06.54Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874, upload-time = "2025-06-09T23:01:07.752Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, ] [[package]] name = "fsspec" version = "2025.3.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/34/f4/5721faf47b8c499e776bc34c6a8fc17efdf7fdef0b00f398128bc5dcb4ac/fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/f4/5721faf47b8c499e776bc34c6a8fc17efdf7fdef0b00f398128bc5dcb4ac/fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972", size = 298491, upload-time = "2025-03-07T21:47:56.461Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3", size = 193615, upload-time = "2025-03-07T21:47:54.809Z" }, ] [package.optional-dependencies] @@ -1889,71 +1889,71 @@ http = [ [[package]] name = "future" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a7/b2/4140c69c6a66432916b26158687e821ba631a4c9273c474343badf84d3ba/future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/b2/4140c69c6a66432916b26158687e821ba631a4c9273c474343badf84d3ba/future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05", size = 1228490, upload-time = "2024-02-21T11:52:38.461Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216", size = 491326, upload-time = "2024-02-21T11:52:35.956Z" }, ] [[package]] name = "gensim" version = "4.3.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, { name = "scipy" }, { name = "smart-open" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ec/bc/36ce4d510085cf150f17d79bb5e88cde942aeba2a894aed5893812ea1e6d/gensim-4.3.3.tar.gz", hash = "sha256:84852076a6a3d88d7dac5be245e24c21c3b819b565e14c1b61fa3e5ee76dcf57" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/bc/36ce4d510085cf150f17d79bb5e88cde942aeba2a894aed5893812ea1e6d/gensim-4.3.3.tar.gz", hash = "sha256:84852076a6a3d88d7dac5be245e24c21c3b819b565e14c1b61fa3e5ee76dcf57", size = 23258708, upload-time = "2024-07-19T14:42:35.418Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/27/12/047dc8b6bed7c4833bcdfbafc10af0f96dc3847ce37be63b14bd6e6c7767/gensim-4.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4e72840adfbea35c5804fd559bc0cb6bc9f439926220a37d852b7ce76eb325c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/6e/7c6d7dda41924b83c4b1eb096942b68b85ba305df7f0963ad0642ac0d73f/gensim-4.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4019263c9d9afae7c669f880c17e09461e77a71afce04ed4d79cf71a4cad2848" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/f4/376290613da44ea9d11bdce3a1705ba7cc25f971edb2b460dc192092068c/gensim-4.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dea62d3e2ada547687bde6cbba37efa50b534db77e9d44fd5802676bb072c9d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/de/63/776ee55c773f55fa9d4fc1596f2e5e15de109921a6727dfe29cc4f0baeb7/gensim-4.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fac93ef5e44982defef9d3c1e4cd00245506b8a29cec19ec5e00f0221b8144c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/4a/f07e2f255aedd6bb4bd0ae420a465f228a4a91bc78ac359216ea20557be6/gensim-4.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:7c3409f755fb8d62da99cea65e7a40a99d21f8fd86443a3aaf2d90eb68995021" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/f4/f43fd909aa29fd92f0e6d703d90c0e6507a7c6be3d686a025b1e192afa3a/gensim-4.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:99e7b70352aecc6c1674dde82b75f453e7a5d1cc71ac1cfbc460bf1fe20501b7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/15/aca2fc3b9e97bd0e28be4a4302793c43757b04b828223c6d103c72132f19/gensim-4.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:32a4cac3f3c38af2069eab9524609fc92ebaeb2692b7280cfda365a3517a280a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ef/84/e46049a16fa7daa26ac9e83e41b3bc3b30867da832a5d7cb0779da893255/gensim-4.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071b4329ed1be02446eb7ef637b94c68cf0080c15c57fbcde667fce2e49c3fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/78/4f/f6045d5d5f8e7838c42572607ce440f95dbf4de5da41ae664198c2839c05/gensim-4.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d662bf96e3d741b6ab61a54be842a7cbf5e45193008b2f4225c758cafd7f9cdc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f5/57/f2e6568dbf464a4b270954e5fa3dee4a4054d163a41c0e7bf0a34eb40f0f/gensim-4.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a54bd53a0e6f991abb837f126663353657270e75be53287e8a568ada0b35b1b0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/40/f1/3231b3fd6f7424f28d7d673679c843da0c61659538262a234f9f43ed5b10/gensim-4.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9a65ed1a8c1fc83890b4eb2a45ae2b32e82a0209c970c8c74694d0374c2415cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/76/616bc781bc19ee76b387a101211f73e00cf59368fcc221e77f88ea907d04/gensim-4.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4db485e08a0287e0fd6a029d89b90913d1df38f1dcd34cd2ab758873ba9255f3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/b7/a316ba52548ca405413c23967c1c6c77d00f82cf6b0cb63d268001e023aa/gensim-4.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7198987116373ab99f034b292a04ac841531d12b56345851c98b40a3fcd93a85" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/07/7a0d5e6cab4da2769c8018f2472690ccb8cab191bf2fe46342dfd627486b/gensim-4.3.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6237a50de4da7a037b19b2b6c430b6537243dcdedebf94afeb089e951953e601" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/7b/747fcb06280764cf20353361162eff68c6b0a3be34c43ead5ae393d3b18e/gensim-4.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:c910c2d5a71f532273166a3a82762959973f0513b221a495fa5a2a07652ee66d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/12/047dc8b6bed7c4833bcdfbafc10af0f96dc3847ce37be63b14bd6e6c7767/gensim-4.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4e72840adfbea35c5804fd559bc0cb6bc9f439926220a37d852b7ce76eb325c1", size = 24086876, upload-time = "2024-07-19T14:39:26.268Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/6e/7c6d7dda41924b83c4b1eb096942b68b85ba305df7f0963ad0642ac0d73f/gensim-4.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4019263c9d9afae7c669f880c17e09461e77a71afce04ed4d79cf71a4cad2848", size = 24041730, upload-time = "2024-07-19T14:39:34.431Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/f4/376290613da44ea9d11bdce3a1705ba7cc25f971edb2b460dc192092068c/gensim-4.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dea62d3e2ada547687bde6cbba37efa50b534db77e9d44fd5802676bb072c9d9", size = 26398007, upload-time = "2024-07-19T14:39:41.67Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/63/776ee55c773f55fa9d4fc1596f2e5e15de109921a6727dfe29cc4f0baeb7/gensim-4.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fac93ef5e44982defef9d3c1e4cd00245506b8a29cec19ec5e00f0221b8144c", size = 26506925, upload-time = "2024-07-19T14:39:48.662Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/4a/f07e2f255aedd6bb4bd0ae420a465f228a4a91bc78ac359216ea20557be6/gensim-4.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:7c3409f755fb8d62da99cea65e7a40a99d21f8fd86443a3aaf2d90eb68995021", size = 24012924, upload-time = "2024-07-19T14:39:56.224Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/f4/f43fd909aa29fd92f0e6d703d90c0e6507a7c6be3d686a025b1e192afa3a/gensim-4.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:99e7b70352aecc6c1674dde82b75f453e7a5d1cc71ac1cfbc460bf1fe20501b7", size = 24082968, upload-time = "2024-07-19T14:40:03.849Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/15/aca2fc3b9e97bd0e28be4a4302793c43757b04b828223c6d103c72132f19/gensim-4.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:32a4cac3f3c38af2069eab9524609fc92ebaeb2692b7280cfda365a3517a280a", size = 24036231, upload-time = "2024-07-19T14:40:10.943Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/84/e46049a16fa7daa26ac9e83e41b3bc3b30867da832a5d7cb0779da893255/gensim-4.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071b4329ed1be02446eb7ef637b94c68cf0080c15c57fbcde667fce2e49c3fe", size = 26558362, upload-time = "2024-07-19T14:40:17.997Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/4f/f6045d5d5f8e7838c42572607ce440f95dbf4de5da41ae664198c2839c05/gensim-4.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d662bf96e3d741b6ab61a54be842a7cbf5e45193008b2f4225c758cafd7f9cdc", size = 26662669, upload-time = "2024-07-19T14:40:26.14Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/57/f2e6568dbf464a4b270954e5fa3dee4a4054d163a41c0e7bf0a34eb40f0f/gensim-4.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a54bd53a0e6f991abb837f126663353657270e75be53287e8a568ada0b35b1b0", size = 24010102, upload-time = "2024-07-19T14:40:33.359Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/f1/3231b3fd6f7424f28d7d673679c843da0c61659538262a234f9f43ed5b10/gensim-4.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9a65ed1a8c1fc83890b4eb2a45ae2b32e82a0209c970c8c74694d0374c2415cb", size = 24079041, upload-time = "2024-07-19T14:40:40.907Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/76/616bc781bc19ee76b387a101211f73e00cf59368fcc221e77f88ea907d04/gensim-4.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4db485e08a0287e0fd6a029d89b90913d1df38f1dcd34cd2ab758873ba9255f3", size = 24035496, upload-time = "2024-07-19T14:40:47.667Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/b7/a316ba52548ca405413c23967c1c6c77d00f82cf6b0cb63d268001e023aa/gensim-4.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7198987116373ab99f034b292a04ac841531d12b56345851c98b40a3fcd93a85", size = 26487104, upload-time = "2024-07-19T14:40:54.867Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/07/7a0d5e6cab4da2769c8018f2472690ccb8cab191bf2fe46342dfd627486b/gensim-4.3.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6237a50de4da7a037b19b2b6c430b6537243dcdedebf94afeb089e951953e601", size = 26606101, upload-time = "2024-07-19T14:41:02.539Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/7b/747fcb06280764cf20353361162eff68c6b0a3be34c43ead5ae393d3b18e/gensim-4.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:c910c2d5a71f532273166a3a82762959973f0513b221a495fa5a2a07652ee66d", size = 24009244, upload-time = "2024-07-19T14:41:09.732Z" }, ] [[package]] name = "google" version = "3.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "beautifulsoup4" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/89/97/b49c69893cddea912c7a660a4b6102c6b02cd268f8c7162dd70b7c16f753/google-3.0.0.tar.gz", hash = "sha256:143530122ee5130509ad5e989f0512f7cb218b2d4eddbafbad40fd10e8d8ccbe" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/97/b49c69893cddea912c7a660a4b6102c6b02cd268f8c7162dd70b7c16f753/google-3.0.0.tar.gz", hash = "sha256:143530122ee5130509ad5e989f0512f7cb218b2d4eddbafbad40fd10e8d8ccbe", size = 44978, upload-time = "2020-07-11T14:50:45.678Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ac/35/17c9141c4ae21e9a29a43acdfd848e3e468a810517f862cad07977bf8fe9/google-3.0.0-py2.py3-none-any.whl", hash = "sha256:889cf695f84e4ae2c55fbc0cfdaf4c1e729417fa52ab1db0485202ba173e4935" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/35/17c9141c4ae21e9a29a43acdfd848e3e468a810517f862cad07977bf8fe9/google-3.0.0-py2.py3-none-any.whl", hash = "sha256:889cf695f84e4ae2c55fbc0cfdaf4c1e729417fa52ab1db0485202ba173e4935", size = 45258, upload-time = "2020-07-11T14:49:58.287Z" }, ] [[package]] name = "google-ai-generativelanguage" version = "0.6.15" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, { name = "google-auth" }, { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/11/d1/48fe5d7a43d278e9f6b5ada810b0a3530bbeac7ed7fcbcd366f932f05316/google_ai_generativelanguage-0.6.15.tar.gz", hash = "sha256:8f6d9dc4c12b065fe2d0289026171acea5183ebf2d0b11cefe12f3821e159ec3" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/d1/48fe5d7a43d278e9f6b5ada810b0a3530bbeac7ed7fcbcd366f932f05316/google_ai_generativelanguage-0.6.15.tar.gz", hash = "sha256:8f6d9dc4c12b065fe2d0289026171acea5183ebf2d0b11cefe12f3821e159ec3", size = 1375443, upload-time = "2025-01-13T21:50:47.459Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7c/a3/67b8a6ff5001a1d8864922f2d6488dc2a14367ceb651bc3f09a947f2f306/google_ai_generativelanguage-0.6.15-py3-none-any.whl", hash = "sha256:5a03ef86377aa184ffef3662ca28f19eeee158733e45d7947982eb953c6ebb6c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/a3/67b8a6ff5001a1d8864922f2d6488dc2a14367ceb651bc3f09a947f2f306/google_ai_generativelanguage-0.6.15-py3-none-any.whl", hash = "sha256:5a03ef86377aa184ffef3662ca28f19eeee158733e45d7947982eb953c6ebb6c", size = 1327356, upload-time = "2025-01-13T21:50:44.174Z" }, ] [[package]] name = "google-api-core" version = "2.25.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-auth" }, { name = "googleapis-common-protos" }, @@ -1961,9 +1961,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443, upload-time = "2025-06-12T20:52:20.439Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807, upload-time = "2025-06-12T20:52:19.334Z" }, ] [package.optional-dependencies] @@ -1975,7 +1975,7 @@ grpc = [ [[package]] name = "google-api-python-client" version = "2.177.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, @@ -1983,42 +1983,42 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7a/75/a89cad519fa8910132e3b08571d0e682ae1163643da6f963f1930f3dc788/google_api_python_client-2.177.0.tar.gz", hash = "sha256:9ffd2b57d68f5afa7e6ac64e2c440534eaa056cbb394812a62ff94723c31b50e" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/75/a89cad519fa8910132e3b08571d0e682ae1163643da6f963f1930f3dc788/google_api_python_client-2.177.0.tar.gz", hash = "sha256:9ffd2b57d68f5afa7e6ac64e2c440534eaa056cbb394812a62ff94723c31b50e", size = 13184405, upload-time = "2025-07-23T16:22:46.321Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/47/f5/121248e18ca605a11720c81ae1b52a5a8cb690af9f01887c56de23cd9a5a/google_api_python_client-2.177.0-py3-none-any.whl", hash = "sha256:f2f50f11105ab883eb9b6cf38ec54ea5fd4b429249f76444bec90deba5be79b3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/f5/121248e18ca605a11720c81ae1b52a5a8cb690af9f01887c56de23cd9a5a/google_api_python_client-2.177.0-py3-none-any.whl", hash = "sha256:f2f50f11105ab883eb9b6cf38ec54ea5fd4b429249f76444bec90deba5be79b3", size = 13709470, upload-time = "2025-07-23T16:22:44.081Z" }, ] [[package]] name = "google-auth" version = "2.40.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, ] [[package]] name = "google-auth-httplib2" version = "0.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-auth" }, { name = "httplib2" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842, upload-time = "2023-12-12T17:40:30.722Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253, upload-time = "2023-12-12T17:40:13.055Z" }, ] [[package]] name = "google-cloud-aiplatform" version = "1.64.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "docstring-parser" }, { name = "google-api-core", extra = ["grpc"] }, @@ -2032,15 +2032,15 @@ dependencies = [ { name = "pydantic" }, { name = "shapely" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5e/e3/f86b429d000a9c25f25bcd122e4b6286aeef70a89acfd6ea088324af016c/google-cloud-aiplatform-1.64.0.tar.gz", hash = "sha256:475a612829b283eb8f783e773d37115c30db42e2e50065c8653db0c9bd18b0da" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/e3/f86b429d000a9c25f25bcd122e4b6286aeef70a89acfd6ea088324af016c/google-cloud-aiplatform-1.64.0.tar.gz", hash = "sha256:475a612829b283eb8f783e773d37115c30db42e2e50065c8653db0c9bd18b0da", size = 6258492, upload-time = "2024-08-28T01:03:24.573Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7a/c5/cdf0eaeded413d5f6221f9c4f466a7714c79a1938c2f7221467d4a9b9859/google_cloud_aiplatform-1.64.0-py2.py3-none-any.whl", hash = "sha256:3a79ce2ec047868c348336624a60993464ca977fd258bcf609cc79309a8101c4" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/c5/cdf0eaeded413d5f6221f9c4f466a7714c79a1938c2f7221467d4a9b9859/google_cloud_aiplatform-1.64.0-py2.py3-none-any.whl", hash = "sha256:3a79ce2ec047868c348336624a60993464ca977fd258bcf609cc79309a8101c4", size = 5228409, upload-time = "2024-08-28T01:03:21.275Z" }, ] [[package]] name = "google-cloud-bigquery" version = "3.35.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, { name = "google-auth" }, @@ -2050,28 +2050,28 @@ dependencies = [ { name = "python-dateutil" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/44/e4/9cf03fa81fefd1b9811a7cd6e398804ae0de3b6a4edef810e2acd45cabbc/google_cloud_bigquery-3.35.1.tar.gz", hash = "sha256:599f26cacf190acfe88000f6cc5f4bc9e6baac7899e4f406ca054f1906f71960" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/e4/9cf03fa81fefd1b9811a7cd6e398804ae0de3b6a4edef810e2acd45cabbc/google_cloud_bigquery-3.35.1.tar.gz", hash = "sha256:599f26cacf190acfe88000f6cc5f4bc9e6baac7899e4f406ca054f1906f71960", size = 496433, upload-time = "2025-07-24T15:09:04.108Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/63/50/96fe9bc5b83d3a421e91ed8edc2535de45957e9af398273e3ecb5c3a1094/google_cloud_bigquery-3.35.1-py3-none-any.whl", hash = "sha256:6739a6ba63c6d80735ca2b34b1df2090ff473b80c1a62354caa2debe6dbbd961" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/50/96fe9bc5b83d3a421e91ed8edc2535de45957e9af398273e3ecb5c3a1094/google_cloud_bigquery-3.35.1-py3-none-any.whl", hash = "sha256:6739a6ba63c6d80735ca2b34b1df2090ff473b80c1a62354caa2debe6dbbd961", size = 256877, upload-time = "2025-07-24T15:09:02.443Z" }, ] [[package]] name = "google-cloud-core" version = "2.4.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861, upload-time = "2025-03-10T21:05:38.948Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" }, ] [[package]] name = "google-cloud-resource-manager" version = "1.14.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, { name = "google-auth" }, @@ -2079,15 +2079,15 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6e/ca/a4648f5038cb94af4b3942815942a03aa9398f9fb0bef55b3f1585b9940d/google_cloud_resource_manager-1.14.2.tar.gz", hash = "sha256:962e2d904c550d7bac48372607904ff7bb3277e3bb4a36d80cc9a37e28e6eb74" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/ca/a4648f5038cb94af4b3942815942a03aa9398f9fb0bef55b3f1585b9940d/google_cloud_resource_manager-1.14.2.tar.gz", hash = "sha256:962e2d904c550d7bac48372607904ff7bb3277e3bb4a36d80cc9a37e28e6eb74", size = 446370, upload-time = "2025-03-17T11:35:56.343Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b1/ea/a92631c358da377af34d3a9682c97af83185c2d66363d5939ab4a1169a7f/google_cloud_resource_manager-1.14.2-py3-none-any.whl", hash = "sha256:d0fa954dedd1d2b8e13feae9099c01b8aac515b648e612834f9942d2795a9900" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/ea/a92631c358da377af34d3a9682c97af83185c2d66363d5939ab4a1169a7f/google_cloud_resource_manager-1.14.2-py3-none-any.whl", hash = "sha256:d0fa954dedd1d2b8e13feae9099c01b8aac515b648e612834f9942d2795a9900", size = 394344, upload-time = "2025-03-17T11:35:54.722Z" }, ] [[package]] name = "google-cloud-storage" version = "2.19.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, @@ -2096,43 +2096,43 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/36/76/4d965702e96bb67976e755bed9828fa50306dca003dbee08b67f41dd265e/google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/76/4d965702e96bb67976e755bed9828fa50306dca003dbee08b67f41dd265e/google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2", size = 5535488, upload-time = "2024-12-05T01:35:06.49Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d5/94/6db383d8ee1adf45dc6c73477152b82731fa4c4a46d9c1932cc8757e0fd4/google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/94/6db383d8ee1adf45dc6c73477152b82731fa4c4a46d9c1932cc8757e0fd4/google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba", size = 131787, upload-time = "2024-12-05T01:35:04.736Z" }, ] [[package]] name = "google-crc32c" version = "1.7.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/eb/69/b1b05cf415df0d86691d6a8b4b7e60ab3a6fb6efb783ee5cd3ed1382bfd3/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/3d/92f8928ecd671bd5b071756596971c79d252d09b835cdca5a44177fa87aa/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/33/42/c2d15a73df79d45ed6b430b9e801d0bd8e28ac139a9012d7d58af50a385d/google_crc32c-1.7.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c67ca0a1f5b56162951a9dae987988679a7db682d6f97ce0f6381ebf0fbea4c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/ea/ac59c86a3c694afd117bb669bde32aaf17d0de4305d01d706495f09cbf19/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc5319db92daa516b653600794d5b9f9439a9a121f3e162f94b0e1891c7933cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/60/44/87e77e8476767a4a93f6cf271157c6d948eacec63688c093580af13b04be/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcdf5a64adb747610140572ed18d011896e3b9ae5195f2514b7ff678c80f1603" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c8/bf/21ac7bb305cd7c1a6de9c52f71db0868e104a5b573a4977cd9d0ff830f82/google_crc32c-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:754561c6c66e89d55754106739e22fdaa93fafa8da7221b29c8b8e8270c6ec8a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06" }, - { url = "https://mirrors.aliyun.com/pypi/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/43/31e57ce04530794917dfe25243860ec141de9fadf4aa9783dffe7dac7c39/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e9afc74168b0b2232fb32dd202c93e46b7d5e4bf03e66ba5dc273bb3559589" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/f3/8b84cd4e0ad111e63e30eb89453f8dd308e3ad36f42305cf8c202461cdf0/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa8136cc14dd27f34a3221c0f16fd42d8a40e4778273e61a3c19aedaa44daf6b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload-time = "2025-03-26T14:29:13.32Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/69/b1b05cf415df0d86691d6a8b4b7e60ab3a6fb6efb783ee5cd3ed1382bfd3/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76", size = 30467, upload-time = "2025-03-26T14:31:11.92Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/3d/92f8928ecd671bd5b071756596971c79d252d09b835cdca5a44177fa87aa/google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d", size = 30311, upload-time = "2025-03-26T14:53:14.161Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/42/c2d15a73df79d45ed6b430b9e801d0bd8e28ac139a9012d7d58af50a385d/google_crc32c-1.7.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c67ca0a1f5b56162951a9dae987988679a7db682d6f97ce0f6381ebf0fbea4c", size = 37889, upload-time = "2025-03-26T14:41:27.83Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/ea/ac59c86a3c694afd117bb669bde32aaf17d0de4305d01d706495f09cbf19/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc5319db92daa516b653600794d5b9f9439a9a121f3e162f94b0e1891c7933cb", size = 33028, upload-time = "2025-03-26T14:41:29.141Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/44/87e77e8476767a4a93f6cf271157c6d948eacec63688c093580af13b04be/google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcdf5a64adb747610140572ed18d011896e3b9ae5195f2514b7ff678c80f1603", size = 38026, upload-time = "2025-03-26T14:41:29.921Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/bf/21ac7bb305cd7c1a6de9c52f71db0868e104a5b573a4977cd9d0ff830f82/google_crc32c-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:754561c6c66e89d55754106739e22fdaa93fafa8da7221b29c8b8e8270c6ec8a", size = 33476, upload-time = "2025-03-26T14:29:09.086Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06", size = 30468, upload-time = "2025-03-26T14:32:52.215Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9", size = 30313, upload-time = "2025-03-26T14:57:38.758Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77", size = 33048, upload-time = "2025-03-26T14:41:30.679Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53", size = 32669, upload-time = "2025-03-26T14:41:31.432Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d", size = 33476, upload-time = "2025-03-26T14:29:10.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470, upload-time = "2025-03-26T14:34:31.655Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315, upload-time = "2025-03-26T15:01:54.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180, upload-time = "2025-03-26T14:41:32.168Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794, upload-time = "2025-03-26T14:41:33.264Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477, upload-time = "2025-03-26T14:29:10.94Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/43/31e57ce04530794917dfe25243860ec141de9fadf4aa9783dffe7dac7c39/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e9afc74168b0b2232fb32dd202c93e46b7d5e4bf03e66ba5dc273bb3559589", size = 28242, upload-time = "2025-03-26T14:41:42.858Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/f3/8b84cd4e0ad111e63e30eb89453f8dd308e3ad36f42305cf8c202461cdf0/google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa8136cc14dd27f34a3221c0f16fd42d8a40e4778273e61a3c19aedaa44daf6b", size = 28049, upload-time = "2025-03-26T14:41:44.651Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48", size = 28241, upload-time = "2025-03-26T14:41:45.898Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82", size = 28048, upload-time = "2025-03-26T14:41:46.696Z" }, ] [[package]] name = "google-generativeai" version = "0.8.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-ai-generativelanguage" }, { name = "google-api-core" }, @@ -2144,40 +2144,40 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6e/40/c42ff9ded9f09ec9392879a8e6538a00b2dc185e834a3392917626255419/google_generativeai-0.8.5-py3-none-any.whl", hash = "sha256:22b420817fb263f8ed520b33285f45976d5b21e904da32b80d4fd20c055123a2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/40/c42ff9ded9f09ec9392879a8e6538a00b2dc185e834a3392917626255419/google_generativeai-0.8.5-py3-none-any.whl", hash = "sha256:22b420817fb263f8ed520b33285f45976d5b21e904da32b80d4fd20c055123a2", size = 155427, upload-time = "2025-04-17T00:40:00.67Z" }, ] [[package]] name = "google-resumable-media" version = "2.7.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-crc32c" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099, upload-time = "2024-08-07T22:20:38.555Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251, upload-time = "2024-08-07T22:20:36.409Z" }, ] [[package]] name = "google-search-results" version = "2.4.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/77/30/b3a6f6a2e00f8153549c2fa345c58ae1ce8e5f3153c2fe0484d444c3abcb/google_search_results-2.4.2.tar.gz", hash = "sha256:603a30ecae2af8e600b22635757a6df275dad4b934f975e67878ccd640b78245" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/30/b3a6f6a2e00f8153549c2fa345c58ae1ce8e5f3153c2fe0484d444c3abcb/google_search_results-2.4.2.tar.gz", hash = "sha256:603a30ecae2af8e600b22635757a6df275dad4b934f975e67878ccd640b78245", size = 18818, upload-time = "2023-03-10T11:13:09.953Z" } [[package]] name = "googleapis-common-protos" version = "1.70.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, ] [package.optional-dependencies] @@ -2188,19 +2188,19 @@ grpc = [ [[package]] name = "gprofiler-official" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ec/c1/d9252620d09a064247d1623ebc4732d624921a2ed80a677f8b9ce61810dd/gprofiler-official-1.0.0.tar.gz", hash = "sha256:5015b47f10fbdcb59c57e342e815c9c07afbe57cd3984154f75b845ddef2445d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/c1/d9252620d09a064247d1623ebc4732d624921a2ed80a677f8b9ce61810dd/gprofiler-official-1.0.0.tar.gz", hash = "sha256:5015b47f10fbdcb59c57e342e815c9c07afbe57cd3984154f75b845ddef2445d", size = 9584, upload-time = "2019-04-02T10:52:19.527Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/df/1b/5a87c1a1da8f601c00a0ce4dedb5aab8a5cad6a0f4a5062c4da22a045072/gprofiler_official-1.0.0-py3-none-any.whl", hash = "sha256:c582baf728e5a6cddac964e4085ca385e082c4ef0279e3af1a16a9af07ab5395" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/1b/5a87c1a1da8f601c00a0ce4dedb5aab8a5cad6a0f4a5062c4da22a045072/gprofiler_official-1.0.0-py3-none-any.whl", hash = "sha256:c582baf728e5a6cddac964e4085ca385e082c4ef0279e3af1a16a9af07ab5395", size = 9277, upload-time = "2019-04-02T10:52:17.769Z" }, ] [[package]] name = "graspologic" version = "3.4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anytree" }, { name = "beartype" }, @@ -2209,8 +2209,8 @@ dependencies = [ { name = "hyppo" }, { name = "joblib" }, { name = "matplotlib" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.5", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.5", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy" }, { name = "pot" }, { name = "scikit-learn" }, @@ -2220,62 +2220,62 @@ dependencies = [ { name = "typing-extensions" }, { name = "umap-learn" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/de/83d653cc8029dc8c5f75bc5aea68f6b1e834230f05525fb3e7ac4aeae226/graspologic-3.4.1.tar.gz", hash = "sha256:7561f0b852a2bccd351bff77e8db07d9892f9dfa35a420fdec01690e4fdc8075" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/de/83d653cc8029dc8c5f75bc5aea68f6b1e834230f05525fb3e7ac4aeae226/graspologic-3.4.1.tar.gz", hash = "sha256:7561f0b852a2bccd351bff77e8db07d9892f9dfa35a420fdec01690e4fdc8075", size = 5134018, upload-time = "2024-05-22T22:54:42.797Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6a/0b/9a167cec9cc4555b59cd282e8669998a50cb3f929a9a503965b24fa58a20/graspologic-3.4.1-py3-none-any.whl", hash = "sha256:c6563e087eda599bad1de831d4b7321c0daa7a82f4e85a7d7737ff67e07cdda2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/0b/9a167cec9cc4555b59cd282e8669998a50cb3f929a9a503965b24fa58a20/graspologic-3.4.1-py3-none-any.whl", hash = "sha256:c6563e087eda599bad1de831d4b7321c0daa7a82f4e85a7d7737ff67e07cdda2", size = 5200768, upload-time = "2024-05-22T22:54:39.259Z" }, ] [[package]] name = "graspologic-native" version = "1.2.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/25/2d/62b30d89533643ccf4778a18eb023f291b8877b5d85de3342f07b2d363a7/graspologic_native-1.2.5.tar.gz", hash = "sha256:27ea7e01fa44466c0b4cdd678d4561e5d3dc0cb400015683b7ae1386031257a0" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/2d/62b30d89533643ccf4778a18eb023f291b8877b5d85de3342f07b2d363a7/graspologic_native-1.2.5.tar.gz", hash = "sha256:27ea7e01fa44466c0b4cdd678d4561e5d3dc0cb400015683b7ae1386031257a0", size = 2512729, upload-time = "2025-04-02T19:34:22.961Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ae/86/10748f4c474b0c8f6060dd379bb0c4da5d42779244bb13a58656ffb44a03/graspologic_native-1.2.5-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:bf05f2e162ae2a2a8d6e8cfccbe3586d1faa0b808159ff950478348df557c61e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/42/cc/b75ea35755340bedda29727e5388390c639ea533f55b9249f5ac3003f656/graspologic_native-1.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fff06ed49c3875cf351bb09a92ae7cbc169ce92dcc4c3439e28e801f822ae" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/55/15e6e4f18bf249b529ac4cd1522b03f5c9ef9284a2f7bfaa1fd1f96464fe/graspologic_native-1.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e7e993e7d70fe0d860773fc62812fbb8cb4ef2d11d8661a1f06f8772593915" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/51/21097af79f3d68626539ab829bdbf6cc42933f020e161972927d916e394c/graspologic_native-1.2.5-cp38-abi3-win_amd64.whl", hash = "sha256:c3ef2172d774083d7e2c8e77daccd218571ddeebeb2c1703cebb1a2cc4c56e07" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/86/10748f4c474b0c8f6060dd379bb0c4da5d42779244bb13a58656ffb44a03/graspologic_native-1.2.5-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:bf05f2e162ae2a2a8d6e8cfccbe3586d1faa0b808159ff950478348df557c61e", size = 648437, upload-time = "2025-04-02T19:34:16.29Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/cc/b75ea35755340bedda29727e5388390c639ea533f55b9249f5ac3003f656/graspologic_native-1.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fff06ed49c3875cf351bb09a92ae7cbc169ce92dcc4c3439e28e801f822ae", size = 352044, upload-time = "2025-04-02T19:34:18.153Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/55/15e6e4f18bf249b529ac4cd1522b03f5c9ef9284a2f7bfaa1fd1f96464fe/graspologic_native-1.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e7e993e7d70fe0d860773fc62812fbb8cb4ef2d11d8661a1f06f8772593915", size = 364644, upload-time = "2025-04-02T19:34:19.486Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/51/21097af79f3d68626539ab829bdbf6cc42933f020e161972927d916e394c/graspologic_native-1.2.5-cp38-abi3-win_amd64.whl", hash = "sha256:c3ef2172d774083d7e2c8e77daccd218571ddeebeb2c1703cebb1a2cc4c56e07", size = 210438, upload-time = "2025-04-02T19:34:21.139Z" }, ] [[package]] name = "greenlet" version = "3.2.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, ] [[package]] name = "groq" version = "0.9.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -2284,196 +2284,196 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/48/c8/5ea4aa6b329fd01795acdf4cb0c78d92860253d108eddfc008fccbe56642/groq-0.9.0.tar.gz", hash = "sha256:130ed5e35d3acfaab46b9e7a078eeaebf91052f4a9d71f86f87fb319b5fec332" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/c8/5ea4aa6b329fd01795acdf4cb0c78d92860253d108eddfc008fccbe56642/groq-0.9.0.tar.gz", hash = "sha256:130ed5e35d3acfaab46b9e7a078eeaebf91052f4a9d71f86f87fb319b5fec332", size = 68728, upload-time = "2024-06-11T20:12:03.864Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/59/7c/81b1302925c2452d540c7d7784b316017b69e1f3f19c2996bcb09360437b/groq-0.9.0-py3-none-any.whl", hash = "sha256:d0e46f4ad645504672bb09c8100af3ced3a7db0d5119dc13e4aca535fc455874" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/7c/81b1302925c2452d540c7d7784b316017b69e1f3f19c2996bcb09360437b/groq-0.9.0-py3-none-any.whl", hash = "sha256:d0e46f4ad645504672bb09c8100af3ced3a7db0d5119dc13e4aca535fc455874", size = 103457, upload-time = "2024-06-11T20:12:02.407Z" }, ] [[package]] name = "grpc-google-iam-v1" version = "0.14.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "googleapis-common-protos", extra = ["grpc"] }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b9/4e/8d0ca3b035e41fe0b3f31ebbb638356af720335e5a11154c330169b40777/grpc_google_iam_v1-0.14.2.tar.gz", hash = "sha256:b3e1fc387a1a329e41672197d0ace9de22c78dd7d215048c4c78712073f7bd20" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/4e/8d0ca3b035e41fe0b3f31ebbb638356af720335e5a11154c330169b40777/grpc_google_iam_v1-0.14.2.tar.gz", hash = "sha256:b3e1fc387a1a329e41672197d0ace9de22c78dd7d215048c4c78712073f7bd20", size = 16259, upload-time = "2025-03-17T11:40:23.586Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/66/6f/dd9b178aee7835b96c2e63715aba6516a9d50f6bebbd1cc1d32c82a2a6c3/grpc_google_iam_v1-0.14.2-py3-none-any.whl", hash = "sha256:a3171468459770907926d56a440b2bb643eec1d7ba215f48f3ecece42b4d8351" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/6f/dd9b178aee7835b96c2e63715aba6516a9d50f6bebbd1cc1d32c82a2a6c3/grpc_google_iam_v1-0.14.2-py3-none-any.whl", hash = "sha256:a3171468459770907926d56a440b2bb643eec1d7ba215f48f3ecece42b4d8351", size = 19242, upload-time = "2025-03-17T11:40:22.648Z" }, ] [[package]] name = "grpcio" version = "1.74.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/66/54/68e51a90797ad7afc5b0a7881426c337f6a9168ebab73c3210b76aa7c90d/grpcio-1.74.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/2a/af817c7e9843929e93e54d09c9aee2555c2e8d81b93102a9426b36e91833/grpcio-1.74.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/94/d67756638d7bb07750b07d0826c68e414124574b53840ba1ff777abcd388/grpcio-1.74.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486" }, - { url = "https://mirrors.aliyun.com/pypi/packages/35/f5/c5e4853bf42148fea8532d49e919426585b73eafcf379a712934652a8de9/grpcio-1.74.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fd/75/a1991dd64b331d199935e096cc9daa3415ee5ccbe9f909aa48eded7bba34/grpcio-1.74.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/a4/7cef3dbb3b073d0ce34fd507efc44ac4c9442a0ef9fba4fb3f5c551efef5/grpcio-1.74.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/d3/587920f882b46e835ad96014087054655312400e2f1f1446419e5179a383/grpcio-1.74.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/95/c70a3b15a0bc83334b507e3d2ae20ee8fa38d419b8758a4d838f5c2a7d32/grpcio-1.74.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/06/2e7042d06247d668ae69ea6998eca33f475fd4e2855f94dcb2aa5daef334/grpcio-1.74.0-cp310-cp310-win32.whl", hash = "sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/20/e02b9dcca3ee91124060b65bbf5b8e1af80b3b76a30f694b44b964ab4d71/grpcio-1.74.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/77/b2f06db9f240a5abeddd23a0e49eae2b6ac54d85f0e5267784ce02269c3b/grpcio-1.74.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/99/0ac8678a819c28d9a370a663007581744a9f2a844e32f0fa95e1ddda5b9e/grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/c6/a2d586300d9e14ad72e8dc211c7aecb45fe9846a51e558c5bca0c9102c7f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/57/5f338bf56a7f22584e68d669632e521f0de460bb3749d54533fc3d0fca4f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/82/ea/a4820c4c44c8b35b1903a6c72a5bdccec92d0840cf5c858c498c66786ba5/grpcio-1.74.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a4/17/0537630a921365928f5abb6d14c79ba4dcb3e662e0dbeede8af4138d9dcf/grpcio-1.74.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/a6/85ca6cb9af3f13e1320d0a806658dca432ff88149d5972df1f7b51e87127/grpcio-1.74.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4f/a7/fe2beab970a1e25d2eff108b3cf4f7d9a53c185106377a3d1989216eba45/grpcio-1.74.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/c2/2f9c945c8a248cebc3ccda1b7a1bf1775b9d7d59e444dbb18c0014e23da6/grpcio-1.74.0-cp311-cp311-win32.whl", hash = "sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/d1/a9cf9c94b55becda2199299a12b9feef0c79946b0d9d34c989de6d12d05d/grpcio-1.74.0-cp311-cp311-win_amd64.whl", hash = "sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/54/68e51a90797ad7afc5b0a7881426c337f6a9168ebab73c3210b76aa7c90d/grpcio-1.74.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907", size = 5481935, upload-time = "2025-07-24T18:52:43.756Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/2a/af817c7e9843929e93e54d09c9aee2555c2e8d81b93102a9426b36e91833/grpcio-1.74.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb", size = 10986796, upload-time = "2025-07-24T18:52:47.219Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/94/d67756638d7bb07750b07d0826c68e414124574b53840ba1ff777abcd388/grpcio-1.74.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486", size = 5983663, upload-time = "2025-07-24T18:52:49.463Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/f5/c5e4853bf42148fea8532d49e919426585b73eafcf379a712934652a8de9/grpcio-1.74.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11", size = 6653765, upload-time = "2025-07-24T18:52:51.094Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/75/a1991dd64b331d199935e096cc9daa3415ee5ccbe9f909aa48eded7bba34/grpcio-1.74.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9", size = 6215172, upload-time = "2025-07-24T18:52:53.282Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/a4/7cef3dbb3b073d0ce34fd507efc44ac4c9442a0ef9fba4fb3f5c551efef5/grpcio-1.74.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc", size = 6329142, upload-time = "2025-07-24T18:52:54.927Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/d3/587920f882b46e835ad96014087054655312400e2f1f1446419e5179a383/grpcio-1.74.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e", size = 7018632, upload-time = "2025-07-24T18:52:56.523Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/95/c70a3b15a0bc83334b507e3d2ae20ee8fa38d419b8758a4d838f5c2a7d32/grpcio-1.74.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82", size = 6509641, upload-time = "2025-07-24T18:52:58.495Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/06/2e7042d06247d668ae69ea6998eca33f475fd4e2855f94dcb2aa5daef334/grpcio-1.74.0-cp310-cp310-win32.whl", hash = "sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7", size = 3817478, upload-time = "2025-07-24T18:53:00.128Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/20/e02b9dcca3ee91124060b65bbf5b8e1af80b3b76a30f694b44b964ab4d71/grpcio-1.74.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5", size = 4493971, upload-time = "2025-07-24T18:53:02.068Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/77/b2f06db9f240a5abeddd23a0e49eae2b6ac54d85f0e5267784ce02269c3b/grpcio-1.74.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31", size = 5487368, upload-time = "2025-07-24T18:53:03.548Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/99/0ac8678a819c28d9a370a663007581744a9f2a844e32f0fa95e1ddda5b9e/grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4", size = 10999804, upload-time = "2025-07-24T18:53:05.095Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/c6/a2d586300d9e14ad72e8dc211c7aecb45fe9846a51e558c5bca0c9102c7f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce", size = 5987667, upload-time = "2025-07-24T18:53:07.157Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/57/5f338bf56a7f22584e68d669632e521f0de460bb3749d54533fc3d0fca4f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3", size = 6655612, upload-time = "2025-07-24T18:53:09.244Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/ea/a4820c4c44c8b35b1903a6c72a5bdccec92d0840cf5c858c498c66786ba5/grpcio-1.74.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182", size = 6219544, upload-time = "2025-07-24T18:53:11.221Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/17/0537630a921365928f5abb6d14c79ba4dcb3e662e0dbeede8af4138d9dcf/grpcio-1.74.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d", size = 6334863, upload-time = "2025-07-24T18:53:12.925Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/a6/85ca6cb9af3f13e1320d0a806658dca432ff88149d5972df1f7b51e87127/grpcio-1.74.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f", size = 7019320, upload-time = "2025-07-24T18:53:15.002Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/a7/fe2beab970a1e25d2eff108b3cf4f7d9a53c185106377a3d1989216eba45/grpcio-1.74.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4", size = 6514228, upload-time = "2025-07-24T18:53:16.999Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/c2/2f9c945c8a248cebc3ccda1b7a1bf1775b9d7d59e444dbb18c0014e23da6/grpcio-1.74.0-cp311-cp311-win32.whl", hash = "sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b", size = 3817216, upload-time = "2025-07-24T18:53:20.564Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/d1/a9cf9c94b55becda2199299a12b9feef0c79946b0d9d34c989de6d12d05d/grpcio-1.74.0-cp311-cp311-win_amd64.whl", hash = "sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11", size = 4495380, upload-time = "2025-07-24T18:53:22.058Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8", size = 5445551, upload-time = "2025-07-24T18:53:23.641Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6", size = 10979810, upload-time = "2025-07-24T18:53:25.349Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5", size = 5941946, upload-time = "2025-07-24T18:53:27.387Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49", size = 6621763, upload-time = "2025-07-24T18:53:29.193Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7", size = 6180664, upload-time = "2025-07-24T18:53:30.823Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3", size = 6301083, upload-time = "2025-07-24T18:53:32.454Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707", size = 6994132, upload-time = "2025-07-24T18:53:34.506Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b", size = 6489616, upload-time = "2025-07-24T18:53:36.217Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c", size = 3807083, upload-time = "2025-07-24T18:53:37.911Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc", size = 4490123, upload-time = "2025-07-24T18:53:39.528Z" }, ] [[package]] name = "grpcio-status" version = "1.71.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fd/d1/b6e9877fedae3add1afdeae1f89d1927d296da9cf977eca0eb08fb8a460e/grpcio_status-1.71.2.tar.gz", hash = "sha256:c7a97e176df71cdc2c179cd1847d7fc86cca5832ad12e9798d7fed6b7a1aab50" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/d1/b6e9877fedae3add1afdeae1f89d1927d296da9cf977eca0eb08fb8a460e/grpcio_status-1.71.2.tar.gz", hash = "sha256:c7a97e176df71cdc2c179cd1847d7fc86cca5832ad12e9798d7fed6b7a1aab50", size = 13677, upload-time = "2025-06-28T04:24:05.426Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/67/58/317b0134129b556a93a3b0afe00ee675b5657f0155509e22fcb853bafe2d/grpcio_status-1.71.2-py3-none-any.whl", hash = "sha256:803c98cb6a8b7dc6dbb785b1111aed739f241ab5e9da0bba96888aa74704cfd3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/58/317b0134129b556a93a3b0afe00ee675b5657f0155509e22fcb853bafe2d/grpcio_status-1.71.2-py3-none-any.whl", hash = "sha256:803c98cb6a8b7dc6dbb785b1111aed739f241ab5e9da0bba96888aa74704cfd3", size = 14424, upload-time = "2025-06-28T04:23:42.136Z" }, ] [[package]] name = "h11" version = "0.16.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "h2" version = "4.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "hpack" }, { name = "hyperframe" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682, upload-time = "2025-02-02T07:43:51.815Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957, upload-time = "2025-02-01T11:02:26.481Z" }, ] [[package]] name = "hanziconv" version = "0.3.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/63/71/b89cb63077fd807fe31cf7c016a06e7e579a289d8a37aa24a30282d02dd2/hanziconv-0.3.2.tar.gz", hash = "sha256:208866da6ae305bca19eb98702b65c93bb3a803b496e4287ca740d68892fc4c4" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/71/b89cb63077fd807fe31cf7c016a06e7e579a289d8a37aa24a30282d02dd2/hanziconv-0.3.2.tar.gz", hash = "sha256:208866da6ae305bca19eb98702b65c93bb3a803b496e4287ca740d68892fc4c4", size = 276775, upload-time = "2016-09-01T05:41:15.254Z" } [[package]] name = "hf-transfer" version = "0.1.9" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1a/eb/8fc64f40388c29ce8ce3b2b180a089d4d6b25b1d0d232d016704cb852104/hf_transfer-0.1.9.tar.gz", hash = "sha256:035572865dab29d17e783fbf1e84cf1cb24f3fcf8f1b17db1cfc7fdf139f02bf" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/eb/8fc64f40388c29ce8ce3b2b180a089d4d6b25b1d0d232d016704cb852104/hf_transfer-0.1.9.tar.gz", hash = "sha256:035572865dab29d17e783fbf1e84cf1cb24f3fcf8f1b17db1cfc7fdf139f02bf", size = 25201, upload-time = "2025-01-07T10:05:12.947Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/81/f5/461d2e5f307e5048289b1168d5c642ae3bb2504e88dff1a38b92ed990a21/hf_transfer-0.1.9-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e66acf91df4a8b72f60223059df3003062a5ae111757187ed1a06750a30e911b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/ba/8d9fd9f1083525edfcb389c93738c802f3559cb749324090d7109c8bf4c2/hf_transfer-0.1.9-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:8669dbcc7a3e2e8d61d42cd24da9c50d57770bd74b445c65123291ca842a7e7a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/a2/cd7885bc9959421065a6fae0fe67b6c55becdeda4e69b873e52976f9a9f0/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fd0167c4407a3bc4cdd0307e65ada2294ec04f1813d8a69a5243e379b22e9d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/2e/a072cf196edfeda3310c9a5ade0a0fdd785e6154b3ce24fc738c818da2a7/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee8b10afedcb75f71091bcc197c526a6ebf5c58bbbadb34fdeee6160f55f619f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/84/aec9ef4c0fab93c1ea2b1badff38c78b4b2f86f0555b26d2051dbc920cde/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5828057e313de59300dd1abb489444bc452efe3f479d3c55b31a8f680936ba42" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/63/b560d39651a56603d64f1a0212d0472a44cbd965db2fa62b99d99cb981bf/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc6bd19e1cc177c66bdef15ef8636ad3bde79d5a4f608c158021153b4573509d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/d8/f87ea6f42456254b48915970ed98e993110521e9263472840174d32c880d/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdca9bfb89e6f8f281890cc61a8aff2d3cecaff7e1a4d275574d96ca70098557" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/56/1267c39b65fc8f4e2113b36297320f102718bf5799b544a6cbe22013aa1d/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:89a23f58b7b7effbc047b8ca286f131b17728c99a9f972723323003ffd1bb916" }, - { url = "https://mirrors.aliyun.com/pypi/packages/82/1a/9c748befbe3decf7cb415e34f8a0c3789a0a9c55910dea73d581e48c0ce5/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:dc7fff1345980d6c0ebb92c811d24afa4b98b3e07ed070c8e38cc91fd80478c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/85/4c03da147b6b4b7cb12e074d3d44eee28604a387ed0eaf7eaaead5069c57/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1a6bd16c667ebe89a069ca163060127a794fa3a3525292c900b8c8cc47985b0d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/6e/e597b04f753f1b09e6893075d53a82a30c13855cbaa791402695b01e369f/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d2fde99d502093ade3ab1b53f80da18480e9902aa960dab7f74fb1b9e5bc5746" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/89/d4e234727a26b2546c8fb70a276cd924260d60135f2165bf8b9ed67bb9a4/hf_transfer-0.1.9-cp38-abi3-win32.whl", hash = "sha256:435cc3cdc8524ce57b074032b8fd76eed70a4224d2091232fa6a8cef8fd6803e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/14/f1e15b851d1c2af5b0b1a82bf8eb10bda2da62d98180220ba6fd8879bb5b/hf_transfer-0.1.9-cp38-abi3-win_amd64.whl", hash = "sha256:16f208fc678911c37e11aa7b586bc66a37d02e636208f18b6bc53d29b5df40ad" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/f5/461d2e5f307e5048289b1168d5c642ae3bb2504e88dff1a38b92ed990a21/hf_transfer-0.1.9-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e66acf91df4a8b72f60223059df3003062a5ae111757187ed1a06750a30e911b", size = 1393046, upload-time = "2025-01-07T10:04:51.003Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/ba/8d9fd9f1083525edfcb389c93738c802f3559cb749324090d7109c8bf4c2/hf_transfer-0.1.9-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:8669dbcc7a3e2e8d61d42cd24da9c50d57770bd74b445c65123291ca842a7e7a", size = 1348126, upload-time = "2025-01-07T10:04:45.712Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/a2/cd7885bc9959421065a6fae0fe67b6c55becdeda4e69b873e52976f9a9f0/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fd0167c4407a3bc4cdd0307e65ada2294ec04f1813d8a69a5243e379b22e9d8", size = 3728604, upload-time = "2025-01-07T10:04:14.173Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/2e/a072cf196edfeda3310c9a5ade0a0fdd785e6154b3ce24fc738c818da2a7/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee8b10afedcb75f71091bcc197c526a6ebf5c58bbbadb34fdeee6160f55f619f", size = 3064995, upload-time = "2025-01-07T10:04:18.663Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/84/aec9ef4c0fab93c1ea2b1badff38c78b4b2f86f0555b26d2051dbc920cde/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5828057e313de59300dd1abb489444bc452efe3f479d3c55b31a8f680936ba42", size = 3580908, upload-time = "2025-01-07T10:04:32.834Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/63/b560d39651a56603d64f1a0212d0472a44cbd965db2fa62b99d99cb981bf/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc6bd19e1cc177c66bdef15ef8636ad3bde79d5a4f608c158021153b4573509d", size = 3400839, upload-time = "2025-01-07T10:04:26.122Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/d8/f87ea6f42456254b48915970ed98e993110521e9263472840174d32c880d/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdca9bfb89e6f8f281890cc61a8aff2d3cecaff7e1a4d275574d96ca70098557", size = 3552664, upload-time = "2025-01-07T10:04:40.123Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/56/1267c39b65fc8f4e2113b36297320f102718bf5799b544a6cbe22013aa1d/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:89a23f58b7b7effbc047b8ca286f131b17728c99a9f972723323003ffd1bb916", size = 4073732, upload-time = "2025-01-07T10:04:55.624Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/1a/9c748befbe3decf7cb415e34f8a0c3789a0a9c55910dea73d581e48c0ce5/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:dc7fff1345980d6c0ebb92c811d24afa4b98b3e07ed070c8e38cc91fd80478c5", size = 3390096, upload-time = "2025-01-07T10:04:59.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/85/4c03da147b6b4b7cb12e074d3d44eee28604a387ed0eaf7eaaead5069c57/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1a6bd16c667ebe89a069ca163060127a794fa3a3525292c900b8c8cc47985b0d", size = 3664743, upload-time = "2025-01-07T10:05:05.416Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/6e/e597b04f753f1b09e6893075d53a82a30c13855cbaa791402695b01e369f/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d2fde99d502093ade3ab1b53f80da18480e9902aa960dab7f74fb1b9e5bc5746", size = 3695243, upload-time = "2025-01-07T10:05:11.411Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/89/d4e234727a26b2546c8fb70a276cd924260d60135f2165bf8b9ed67bb9a4/hf_transfer-0.1.9-cp38-abi3-win32.whl", hash = "sha256:435cc3cdc8524ce57b074032b8fd76eed70a4224d2091232fa6a8cef8fd6803e", size = 1086605, upload-time = "2025-01-07T10:05:18.873Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/14/f1e15b851d1c2af5b0b1a82bf8eb10bda2da62d98180220ba6fd8879bb5b/hf_transfer-0.1.9-cp38-abi3-win_amd64.whl", hash = "sha256:16f208fc678911c37e11aa7b586bc66a37d02e636208f18b6bc53d29b5df40ad", size = 1160240, upload-time = "2025-01-07T10:05:14.324Z" }, ] [[package]] name = "hpack" version = "4.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, ] [[package]] name = "html-text" version = "0.6.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "lxml" }, { name = "lxml-html-clean" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5e/35/10ab103fec3b953ed1ba56ff827d00afc170dd204e0785c5fe7d3d6e1ae9/html_text-0.6.2.tar.gz", hash = "sha256:81455b4de5430cf63ce7c45a870fb8629e79ca8518e240f172d62409c2f2ff72" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/35/10ab103fec3b953ed1ba56ff827d00afc170dd204e0785c5fe7d3d6e1ae9/html_text-0.6.2.tar.gz", hash = "sha256:81455b4de5430cf63ce7c45a870fb8629e79ca8518e240f172d62409c2f2ff72", size = 53592, upload-time = "2024-05-01T11:55:13.92Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b8/bd/b28e8456b952268083642d631bec7d74e1564a54bfd2e6f6d13e597bbec0/html_text-0.6.2-py2.py3-none-any.whl", hash = "sha256:d83d619ccd4b4d6172e21084d8a46e29e49ce87a08cc02161e7ca8c2918e7bca" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/bd/b28e8456b952268083642d631bec7d74e1564a54bfd2e6f6d13e597bbec0/html_text-0.6.2-py2.py3-none-any.whl", hash = "sha256:d83d619ccd4b4d6172e21084d8a46e29e49ce87a08cc02161e7ca8c2918e7bca", size = 7694, upload-time = "2024-05-01T11:55:12.315Z" }, ] [[package]] name = "html2text" version = "2024.2.26" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1a/43/e1d53588561e533212117750ee79ad0ba02a41f52a08c1df3396bd466c05/html2text-2024.2.26.tar.gz", hash = "sha256:05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/43/e1d53588561e533212117750ee79ad0ba02a41f52a08c1df3396bd466c05/html2text-2024.2.26.tar.gz", hash = "sha256:05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32", size = 56527, upload-time = "2024-02-27T18:49:24.855Z" } [[package]] name = "html5lib" version = "1.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "six" }, { name = "webencodings" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ac/b6/b55c3f49042f1df3dcd422b7f224f939892ee94f22abcf503a9b7339eaf2/html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/b6/b55c3f49042f1df3dcd422b7f224f939892ee94f22abcf503a9b7339eaf2/html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f", size = 272215, upload-time = "2020-06-22T23:32:38.834Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", size = 112173, upload-time = "2020-06-22T23:32:36.781Z" }, ] [[package]] name = "httpcore" version = "1.0.9" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] name = "httplib2" version = "0.22.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3d/ad/2371116b22d616c194aa25ec410c9c6c37f23599dcd590502b74db197584/httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/ad/2371116b22d616c194aa25ec410c9c6c37f23599dcd590502b74db197584/httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81", size = 351116, upload-time = "2023-03-21T22:29:37.214Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", size = 96854, upload-time = "2023-03-21T22:29:35.683Z" }, ] [[package]] name = "httpx" version = "0.27.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, { name = "certifi" }, @@ -2481,9 +2481,9 @@ dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189, upload-time = "2024-08-27T12:54:01.334Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395, upload-time = "2024-08-27T12:53:59.653Z" }, ] [package.optional-dependencies] @@ -2494,16 +2494,16 @@ socks = [ [[package]] name = "httpx-sse" version = "0.4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998, upload-time = "2025-06-24T13:21:05.71Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054, upload-time = "2025-06-24T13:21:04.772Z" }, ] [[package]] name = "huggingface-hub" version = "0.25.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "filelock" }, { name = "fsspec" }, @@ -2513,50 +2513,50 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/df/fd/5f81bae67096c5ab50d29a0230b8374f0245916cca192f8ee2fada51f4f6/huggingface_hub-0.25.2.tar.gz", hash = "sha256:a1014ea111a5f40ccd23f7f7ba8ac46e20fa3b658ced1f86a00c75c06ec6423c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/fd/5f81bae67096c5ab50d29a0230b8374f0245916cca192f8ee2fada51f4f6/huggingface_hub-0.25.2.tar.gz", hash = "sha256:a1014ea111a5f40ccd23f7f7ba8ac46e20fa3b658ced1f86a00c75c06ec6423c", size = 365806, upload-time = "2024-10-09T08:32:41.565Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/64/09/a535946bf2dc88e61341f39dc507530411bb3ea4eac493e5ec833e8f35bd/huggingface_hub-0.25.2-py3-none-any.whl", hash = "sha256:1897caf88ce7f97fe0110603d8f66ac264e3ba6accdf30cd66cc0fed5282ad25" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/09/a535946bf2dc88e61341f39dc507530411bb3ea4eac493e5ec833e8f35bd/huggingface_hub-0.25.2-py3-none-any.whl", hash = "sha256:1897caf88ce7f97fe0110603d8f66ac264e3ba6accdf30cd66cc0fed5282ad25", size = 436575, upload-time = "2024-10-09T08:32:39.166Z" }, ] [[package]] name = "humanfriendly" version = "10.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pyreadline3", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, ] [[package]] name = "hyperframe" version = "6.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] [[package]] name = "hypothesis" version = "6.136.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "attrs" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "sortedcontainers" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e2/d6/6c45c56cdc532f25a354cf2c93a3bebc21b91b6a48125e3acfd364161447/hypothesis-6.136.6.tar.gz", hash = "sha256:2ad2e4f2012be4d41c6515b0628d84d48af6e6c38b4db50840bd9ac0899f5856" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/d6/6c45c56cdc532f25a354cf2c93a3bebc21b91b6a48125e3acfd364161447/hypothesis-6.136.6.tar.gz", hash = "sha256:2ad2e4f2012be4d41c6515b0628d84d48af6e6c38b4db50840bd9ac0899f5856", size = 458049, upload-time = "2025-07-28T09:48:02.495Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e9/e2/7fd1a9b12740b3472349f7e3c216e94b1b2e03d32c9d842284b57eb5a3f8/hypothesis-6.136.6-py3-none-any.whl", hash = "sha256:1d6296dde36d42263bd44a084c74e91467e78186676e410167f920aa0374a9e7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/e2/7fd1a9b12740b3472349f7e3c216e94b1b2e03d32c9d842284b57eb5a3f8/hypothesis-6.136.6-py3-none-any.whl", hash = "sha256:1d6296dde36d42263bd44a084c74e91467e78186676e410167f920aa0374a9e7", size = 524958, upload-time = "2025-07-28T09:47:58.938Z" }, ] [[package]] name = "hyppo" version = "0.4.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "autograd" }, { name = "numba" }, @@ -2565,78 +2565,78 @@ dependencies = [ { name = "scipy" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/34/87/7940713f929d0280cff1bde207479cb588a0d3a4dd49a0e2e69bfff46363/hyppo-0.4.0-py3-none-any.whl", hash = "sha256:4e75565b8deb601485cd7bc1b5c3f44e6ddf329136fc81e65d011f9b4e95132f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/87/7940713f929d0280cff1bde207479cb588a0d3a4dd49a0e2e69bfff46363/hyppo-0.4.0-py3-none-any.whl", hash = "sha256:4e75565b8deb601485cd7bc1b5c3f44e6ddf329136fc81e65d011f9b4e95132f", size = 146607, upload-time = "2023-05-24T13:50:04.441Z" }, ] [[package]] name = "idna" version = "3.10" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] name = "ijson" version = "3.4.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a3/4f/1cfeada63f5fce87536651268ddf5cca79b8b4bbb457aee4e45777964a0a/ijson-3.4.0.tar.gz", hash = "sha256:5f74dcbad9d592c428d3ca3957f7115a42689ee7ee941458860900236ae9bb13" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/eb/6b/a247ba44004154aaa71f9e6bd9f05ba412f490cc4043618efb29314f035e/ijson-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e27e50f6dcdee648f704abc5d31b976cd2f90b4642ed447cf03296d138433d09" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/1d/8d2009d74373b7dec2a49b1167e396debb896501396c70a674bb9ccc41ff/ijson-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a753be681ac930740a4af9c93cfb4edc49a167faed48061ea650dc5b0f406f1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a7/b2/a85a21ebaba81f64a326c303a94625fb94b84890c52d9efdd8acb38b6312/ijson-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a07c47aed534e0ec198e6a2d4360b259d32ac654af59c015afc517ad7973b7fb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/35/273dfa1f27c38eeaba105496ecb54532199f76c0120177b28315daf5aec3/ijson-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c55f48181e11c597cd7146fb31edc8058391201ead69f8f40d2ecbb0b3e4fc6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4d/37/9d3bb0e200a103ca9f8e9315c4d96ecaca43a3c1957c1ac069ea9dc9c6ba/ijson-3.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd5669f96f79d8a2dd5ae81cbd06770a4d42c435fd4a75c74ef28d9913b697d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/54/8f015c4df30200fd14435dec9c67bf675dff0fee44a16c084a8ec0f82922/ijson-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e3ddd46d16b8542c63b1b8af7006c758d4e21cc1b86122c15f8530fae773461" }, - { url = "https://mirrors.aliyun.com/pypi/packages/88/01/46a0540ad3461332edcc689a8874fa13f0a4c00f60f02d155b70e36f5e0b/ijson-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1504cec7fe04be2bb0cc33b50c9dd3f83f98c0540ad4991d4017373b7853cfe6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/da/8f8df42f3fd7ef279e20eae294738eed62d41ed5b6a4baca5121abc7cf0f/ijson-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2f2ff456adeb216603e25d7915f10584c1b958b6eafa60038d76d08fc8a5fb06" }, - { url = "https://mirrors.aliyun.com/pypi/packages/82/0a/a410d9d3b082cc2ec9738d54935a589974cbe54c0f358e4d17465594d660/ijson-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ab00d75d61613a125fbbb524551658b1ad6919a52271ca16563ca5bc2737bb1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/c6/a3e2a446b8bd2cf91cb4ca7439f128d2b379b5a79794d0ea25e379b0f4f3/ijson-3.4.0-cp310-cp310-win32.whl", hash = "sha256:ada421fd59fe2bfa4cfa64ba39aeba3f0753696cdcd4d50396a85f38b1d12b01" }, - { url = "https://mirrors.aliyun.com/pypi/packages/18/7c/e6620603df42d2ef8a92076eaa5cd2b905366e86e113adf49e7b79970bd3/ijson-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:8c75e82cec05d00ed3a4af5f4edf08f59d536ed1a86ac7e84044870872d82a33" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/0d/3e2998f4d7b7d2db2d511e4f0cf9127b6e2140c325c3cb77be46ae46ff1d/ijson-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e369bf5a173ca51846c243002ad8025d32032532523b06510881ecc8723ee54" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/7b/afef2b08af2fee5ead65fcd972fadc3e31f9ae2b517fe2c378d50a9bf79b/ijson-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26e7da0a3cd2a56a1fde1b34231867693f21c528b683856f6691e95f9f39caec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/4a/39f583a2a13096f5063028bb767622f09cafc9ec254c193deee6c80af59f/ijson-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c28c7f604729be22aa453e604e9617b665fa0c24cd25f9f47a970e8130c571a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/58/5b80efd54b093e479c98d14b31d7794267281f6a8729f2c94fbfab661029/ijson-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed8bcb84d3468940f97869da323ba09ae3e6b950df11dea9b62e2b231ca1e3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/f5/f37659b1647ecc3992216277cd8a45e2194e84e8818178f77c99e1d18463/ijson-3.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:296bc824f4088f2af814aaf973b0435bc887ce3d9f517b1577cc4e7d1afb1cb7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ee/2f/4c580ac4bb5eda059b672ad0a05e4bafdae5182a6ec6ab43546763dafa91/ijson-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8145f8f40617b6a8aa24e28559d0adc8b889e56a203725226a8a60fa3501073f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6d/9e/64ec39718609faab6ed6e1ceb44f9c35d71210ad9c87fff477c03503e8f8/ijson-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b674a97bd503ea21bc85103e06b6493b1b2a12da3372950f53e1c664566a33a4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/b2/f0bf0e4a0962845597996de6de59c0078bc03a1f899e03908220039f4cf6/ijson-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8bc731cf1c3282b021d3407a601a5a327613da9ad3c4cecb1123232623ae1826" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/83/4a2e3611e2b4842b413ec84d2e54adea55ab52e4408ea0f1b1b927e19536/ijson-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42ace5e940e0cf58c9de72f688d6829ddd815096d07927ee7e77df2648006365" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/75/2d332911ac765b44cd7da0cb2b06143521ad5e31dfcc8d8587e6e6168bc8/ijson-3.4.0-cp311-cp311-win32.whl", hash = "sha256:5be39a0df4cd3f02b304382ea8885391900ac62e95888af47525a287c50005e9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/ba/4ad571f9f7fcf5906b26e757b130c1713c5f0198a1e59568f05d53a0816c/ijson-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b1be1781792291e70d2e177acf564ec672a7907ba74f313583bdf39fe81f9b7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/ec/317ee5b2d13e50448833ead3aa906659a32b376191f6abc2a7c6112d2b27/ijson-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:956b148f88259a80a9027ffbe2d91705fae0c004fbfba3e5a24028fbe72311a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/43/b06c96ced30cacecc5d518f89b0fd1c98c294a30ff88848b70ed7b7f72a1/ijson-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b89960f5c721106394c7fba5760b3f67c515b8eb7d80f612388f5eca2f4621" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/df/b4aeafb7ecde463130840ee9be36130823ec94a00525049bf700883378b8/ijson-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a0bb591cf250dd7e9dfab69d634745a7f3272d31cfe879f9156e0a081fd97ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/7c/a80b8e361641609507f62022089626d4b8067f0826f51e1c09e4ba86eba8/ijson-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e92de999977f4c6b660ffcf2b8d59604ccd531edcbfde05b642baf283e0de8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/44/fa416347b9a802e3646c6ff377fc3278bd7d6106e17beb339514b6a3184e/ijson-3.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e9602157a5b869d44b6896e64f502c712a312fcde044c2e586fccb85d3e316e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/c6/41a9ad4d42df50ff6e70fdce79b034f09b914802737ebbdc141153d8d791/ijson-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e83660edb931a425b7ff662eb49db1f10d30ca6d4d350e5630edbed098bc01" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/6f/7d01efda415b8502dce67e067ed9e8a124f53e763002c02207e542e1a2f1/ijson-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:49bf8eac1c7b7913073865a859c215488461f7591b4fa6a33c14b51cb73659d0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/95/6c/0d67024b9ecb57916c5e5ab0350251c9fe2f86dc9c8ca2b605c194bdad6a/ijson-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:160b09273cb42019f1811469508b0a057d19f26434d44752bde6f281da6d3f32" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/43/e10edcc1c6a3b619294de835e7678bfb3a1b8a75955f3689fd66a1e9e7b4/ijson-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2019ff4e6f354aa00c76c8591bd450899111c61f2354ad55cc127e2ce2492c44" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/84/1cbeee8e8190a1ebe6926569a92cf1fa80ddb380c129beb6f86559e1bb24/ijson-3.4.0-cp312-cp312-win32.whl", hash = "sha256:931c007bf6bb8330705429989b2deed6838c22b63358a330bf362b6e458ba0bf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/13/530802bc391c95be6fe9f96e9aa427d94067e7c0b7da7a9092344dc44c4b/ijson-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:71523f2b64cb856a820223e94d23e88369f193017ecc789bb4de198cc9d349eb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a7/22/da919f16ca9254f8a9ea0ba482d2c1d012ce6e4c712dcafd8adb16b16c63/ijson-3.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:54e989c35dba9cf163d532c14bcf0c260897d5f465643f0cd1fba9c908bed7ef" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6d/54/c2afd289e034d11c4909f4ea90c9dae55053bed358064f310c3dd5033657/ijson-3.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:494eeb8e87afef22fbb969a4cb81ac2c535f30406f334fb6136e9117b0bb5380" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/d6/18799b0fca9ecb8a47e22527eedcea3267e95d4567b564ef21d0299e2d12/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81603de95de1688958af65cd2294881a4790edae7de540b70c65c8253c5dc44a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/d6/c58032c69e9e977bf6d954f22cad0cd52092db89c454ea98926744523665/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8524be12c1773e1be466034cc49c1ecbe3d5b47bb86217bd2a57f73f970a6c19" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/03/07c6840454d5d228bb5b4509c9a7ac5b9c0b8258e2b317a53f97372be1eb/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17994696ec895d05e0cfa21b11c68c920c82634b4a3d8b8a1455d6fe9fdee8f7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/c7/da58a9840380308df574dfdb0276c9d802b12f6125f999e92bcef36db552/ijson-3.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0b67727aaee55d43b2e82b6a866c3cbcb2b66a5e9894212190cbd8773d0d9857" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a3/9b/0bc0594d357600c03c3b5a3a34043d764fc3ad3f0757d2f3aae5b28f6c1c/ijson-3.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdc8c5ca0eec789ed99db29c68012dda05027af0860bb360afd28d825238d69d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/1f/506cf2574673da1adcc8a794ebb85bf857cabe6294523978637e646814de/ijson-3.4.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8e6b44b6ec45d5b1a0ee9d97e0e65ab7f62258727004cbbe202bf5f198bc21f7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dc/3d/a7cd8d8a6de0f3084fe4d457a8f76176e11b013867d1cad16c67d25e8bec/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b51e239e4cb537929796e840d349fc731fdc0d58b1a0683ce5465ad725321e0f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/51/aa30abc02aabfc41c95887acf5f1f88da569642d7197fbe5aa105545226d/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed05d43ec02be8ddb1ab59579761f6656b25d241a77fd74f4f0f7ec09074318a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/37/7773659b8d8d98b34234e1237352f6b446a3c12941619686c7d4a8a5c69c/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfeca1aaa59d93fd0a3718cbe5f7ef0effff85cf837e0bceb71831a47f39cc14" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/1f/dd52a84ed140e31a5d226cd47d98d21aa559aead35ef7bae479eab4c494c/ijson-3.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7ca72ca12e9a1dd4252c97d952be34282907f263f7e28fcdff3a01b83981e837" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/4f/1cfeada63f5fce87536651268ddf5cca79b8b4bbb457aee4e45777964a0a/ijson-3.4.0.tar.gz", hash = "sha256:5f74dcbad9d592c428d3ca3957f7115a42689ee7ee941458860900236ae9bb13", size = 65782, upload-time = "2025-05-08T02:37:20.135Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/6b/a247ba44004154aaa71f9e6bd9f05ba412f490cc4043618efb29314f035e/ijson-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e27e50f6dcdee648f704abc5d31b976cd2f90b4642ed447cf03296d138433d09", size = 87609, upload-time = "2025-05-08T02:35:20.535Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/1d/8d2009d74373b7dec2a49b1167e396debb896501396c70a674bb9ccc41ff/ijson-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a753be681ac930740a4af9c93cfb4edc49a167faed48061ea650dc5b0f406f1", size = 59243, upload-time = "2025-05-08T02:35:21.958Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/b2/a85a21ebaba81f64a326c303a94625fb94b84890c52d9efdd8acb38b6312/ijson-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a07c47aed534e0ec198e6a2d4360b259d32ac654af59c015afc517ad7973b7fb", size = 59309, upload-time = "2025-05-08T02:35:23.317Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/35/273dfa1f27c38eeaba105496ecb54532199f76c0120177b28315daf5aec3/ijson-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c55f48181e11c597cd7146fb31edc8058391201ead69f8f40d2ecbb0b3e4fc6", size = 131213, upload-time = "2025-05-08T02:35:24.735Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/37/9d3bb0e200a103ca9f8e9315c4d96ecaca43a3c1957c1ac069ea9dc9c6ba/ijson-3.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd5669f96f79d8a2dd5ae81cbd06770a4d42c435fd4a75c74ef28d9913b697d", size = 125456, upload-time = "2025-05-08T02:35:25.896Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/54/8f015c4df30200fd14435dec9c67bf675dff0fee44a16c084a8ec0f82922/ijson-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e3ddd46d16b8542c63b1b8af7006c758d4e21cc1b86122c15f8530fae773461", size = 130192, upload-time = "2025-05-08T02:35:27.367Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/01/46a0540ad3461332edcc689a8874fa13f0a4c00f60f02d155b70e36f5e0b/ijson-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1504cec7fe04be2bb0cc33b50c9dd3f83f98c0540ad4991d4017373b7853cfe6", size = 132217, upload-time = "2025-05-08T02:35:28.545Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/da/8f8df42f3fd7ef279e20eae294738eed62d41ed5b6a4baca5121abc7cf0f/ijson-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2f2ff456adeb216603e25d7915f10584c1b958b6eafa60038d76d08fc8a5fb06", size = 127118, upload-time = "2025-05-08T02:35:29.726Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/0a/a410d9d3b082cc2ec9738d54935a589974cbe54c0f358e4d17465594d660/ijson-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ab00d75d61613a125fbbb524551658b1ad6919a52271ca16563ca5bc2737bb1", size = 129808, upload-time = "2025-05-08T02:35:31.247Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/c6/a3e2a446b8bd2cf91cb4ca7439f128d2b379b5a79794d0ea25e379b0f4f3/ijson-3.4.0-cp310-cp310-win32.whl", hash = "sha256:ada421fd59fe2bfa4cfa64ba39aeba3f0753696cdcd4d50396a85f38b1d12b01", size = 51160, upload-time = "2025-05-08T02:35:32.964Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/7c/e6620603df42d2ef8a92076eaa5cd2b905366e86e113adf49e7b79970bd3/ijson-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:8c75e82cec05d00ed3a4af5f4edf08f59d536ed1a86ac7e84044870872d82a33", size = 53710, upload-time = "2025-05-08T02:35:34.033Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/0d/3e2998f4d7b7d2db2d511e4f0cf9127b6e2140c325c3cb77be46ae46ff1d/ijson-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e369bf5a173ca51846c243002ad8025d32032532523b06510881ecc8723ee54", size = 87643, upload-time = "2025-05-08T02:35:35.693Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/7b/afef2b08af2fee5ead65fcd972fadc3e31f9ae2b517fe2c378d50a9bf79b/ijson-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26e7da0a3cd2a56a1fde1b34231867693f21c528b683856f6691e95f9f39caec", size = 59260, upload-time = "2025-05-08T02:35:37.166Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/4a/39f583a2a13096f5063028bb767622f09cafc9ec254c193deee6c80af59f/ijson-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c28c7f604729be22aa453e604e9617b665fa0c24cd25f9f47a970e8130c571a", size = 59311, upload-time = "2025-05-08T02:35:38.538Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/58/5b80efd54b093e479c98d14b31d7794267281f6a8729f2c94fbfab661029/ijson-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed8bcb84d3468940f97869da323ba09ae3e6b950df11dea9b62e2b231ca1e3", size = 136125, upload-time = "2025-05-08T02:35:39.976Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/f5/f37659b1647ecc3992216277cd8a45e2194e84e8818178f77c99e1d18463/ijson-3.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:296bc824f4088f2af814aaf973b0435bc887ce3d9f517b1577cc4e7d1afb1cb7", size = 130699, upload-time = "2025-05-08T02:35:41.483Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/2f/4c580ac4bb5eda059b672ad0a05e4bafdae5182a6ec6ab43546763dafa91/ijson-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8145f8f40617b6a8aa24e28559d0adc8b889e56a203725226a8a60fa3501073f", size = 134963, upload-time = "2025-05-08T02:35:43.017Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/9e/64ec39718609faab6ed6e1ceb44f9c35d71210ad9c87fff477c03503e8f8/ijson-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b674a97bd503ea21bc85103e06b6493b1b2a12da3372950f53e1c664566a33a4", size = 137405, upload-time = "2025-05-08T02:35:44.618Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/b2/f0bf0e4a0962845597996de6de59c0078bc03a1f899e03908220039f4cf6/ijson-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8bc731cf1c3282b021d3407a601a5a327613da9ad3c4cecb1123232623ae1826", size = 131861, upload-time = "2025-05-08T02:35:46.22Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/83/4a2e3611e2b4842b413ec84d2e54adea55ab52e4408ea0f1b1b927e19536/ijson-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42ace5e940e0cf58c9de72f688d6829ddd815096d07927ee7e77df2648006365", size = 134297, upload-time = "2025-05-08T02:35:47.401Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/75/2d332911ac765b44cd7da0cb2b06143521ad5e31dfcc8d8587e6e6168bc8/ijson-3.4.0-cp311-cp311-win32.whl", hash = "sha256:5be39a0df4cd3f02b304382ea8885391900ac62e95888af47525a287c50005e9", size = 51161, upload-time = "2025-05-08T02:35:49.164Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/ba/4ad571f9f7fcf5906b26e757b130c1713c5f0198a1e59568f05d53a0816c/ijson-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b1be1781792291e70d2e177acf564ec672a7907ba74f313583bdf39fe81f9b7", size = 53710, upload-time = "2025-05-08T02:35:50.323Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/ec/317ee5b2d13e50448833ead3aa906659a32b376191f6abc2a7c6112d2b27/ijson-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:956b148f88259a80a9027ffbe2d91705fae0c004fbfba3e5a24028fbe72311a9", size = 87212, upload-time = "2025-05-08T02:35:51.835Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/43/b06c96ced30cacecc5d518f89b0fd1c98c294a30ff88848b70ed7b7f72a1/ijson-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b89960f5c721106394c7fba5760b3f67c515b8eb7d80f612388f5eca2f4621", size = 59175, upload-time = "2025-05-08T02:35:52.988Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/df/b4aeafb7ecde463130840ee9be36130823ec94a00525049bf700883378b8/ijson-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a0bb591cf250dd7e9dfab69d634745a7f3272d31cfe879f9156e0a081fd97ee", size = 59011, upload-time = "2025-05-08T02:35:54.394Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/7c/a80b8e361641609507f62022089626d4b8067f0826f51e1c09e4ba86eba8/ijson-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e92de999977f4c6b660ffcf2b8d59604ccd531edcbfde05b642baf283e0de8", size = 146094, upload-time = "2025-05-08T02:35:55.601Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/44/fa416347b9a802e3646c6ff377fc3278bd7d6106e17beb339514b6a3184e/ijson-3.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e9602157a5b869d44b6896e64f502c712a312fcde044c2e586fccb85d3e316e", size = 137903, upload-time = "2025-05-08T02:35:56.814Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/c6/41a9ad4d42df50ff6e70fdce79b034f09b914802737ebbdc141153d8d791/ijson-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e83660edb931a425b7ff662eb49db1f10d30ca6d4d350e5630edbed098bc01", size = 148339, upload-time = "2025-05-08T02:35:58.595Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/6f/7d01efda415b8502dce67e067ed9e8a124f53e763002c02207e542e1a2f1/ijson-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:49bf8eac1c7b7913073865a859c215488461f7591b4fa6a33c14b51cb73659d0", size = 149383, upload-time = "2025-05-08T02:36:00.197Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/6c/0d67024b9ecb57916c5e5ab0350251c9fe2f86dc9c8ca2b605c194bdad6a/ijson-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:160b09273cb42019f1811469508b0a057d19f26434d44752bde6f281da6d3f32", size = 141580, upload-time = "2025-05-08T02:36:01.998Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/43/e10edcc1c6a3b619294de835e7678bfb3a1b8a75955f3689fd66a1e9e7b4/ijson-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2019ff4e6f354aa00c76c8591bd450899111c61f2354ad55cc127e2ce2492c44", size = 150280, upload-time = "2025-05-08T02:36:03.926Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/84/1cbeee8e8190a1ebe6926569a92cf1fa80ddb380c129beb6f86559e1bb24/ijson-3.4.0-cp312-cp312-win32.whl", hash = "sha256:931c007bf6bb8330705429989b2deed6838c22b63358a330bf362b6e458ba0bf", size = 51512, upload-time = "2025-05-08T02:36:05.595Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/13/530802bc391c95be6fe9f96e9aa427d94067e7c0b7da7a9092344dc44c4b/ijson-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:71523f2b64cb856a820223e94d23e88369f193017ecc789bb4de198cc9d349eb", size = 54081, upload-time = "2025-05-08T02:36:07.099Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/22/da919f16ca9254f8a9ea0ba482d2c1d012ce6e4c712dcafd8adb16b16c63/ijson-3.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:54e989c35dba9cf163d532c14bcf0c260897d5f465643f0cd1fba9c908bed7ef", size = 56480, upload-time = "2025-05-08T02:36:54.942Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/54/c2afd289e034d11c4909f4ea90c9dae55053bed358064f310c3dd5033657/ijson-3.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:494eeb8e87afef22fbb969a4cb81ac2c535f30406f334fb6136e9117b0bb5380", size = 55956, upload-time = "2025-05-08T02:36:56.178Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/d6/18799b0fca9ecb8a47e22527eedcea3267e95d4567b564ef21d0299e2d12/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81603de95de1688958af65cd2294881a4790edae7de540b70c65c8253c5dc44a", size = 69394, upload-time = "2025-05-08T02:36:57.699Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/d6/c58032c69e9e977bf6d954f22cad0cd52092db89c454ea98926744523665/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8524be12c1773e1be466034cc49c1ecbe3d5b47bb86217bd2a57f73f970a6c19", size = 70378, upload-time = "2025-05-08T02:36:58.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/03/07c6840454d5d228bb5b4509c9a7ac5b9c0b8258e2b317a53f97372be1eb/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17994696ec895d05e0cfa21b11c68c920c82634b4a3d8b8a1455d6fe9fdee8f7", size = 67770, upload-time = "2025-05-08T02:37:00.162Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/c7/da58a9840380308df574dfdb0276c9d802b12f6125f999e92bcef36db552/ijson-3.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0b67727aaee55d43b2e82b6a866c3cbcb2b66a5e9894212190cbd8773d0d9857", size = 53858, upload-time = "2025-05-08T02:37:01.691Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/9b/0bc0594d357600c03c3b5a3a34043d764fc3ad3f0757d2f3aae5b28f6c1c/ijson-3.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdc8c5ca0eec789ed99db29c68012dda05027af0860bb360afd28d825238d69d", size = 56483, upload-time = "2025-05-08T02:37:03.274Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/1f/506cf2574673da1adcc8a794ebb85bf857cabe6294523978637e646814de/ijson-3.4.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8e6b44b6ec45d5b1a0ee9d97e0e65ab7f62258727004cbbe202bf5f198bc21f7", size = 55957, upload-time = "2025-05-08T02:37:04.865Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/3d/a7cd8d8a6de0f3084fe4d457a8f76176e11b013867d1cad16c67d25e8bec/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b51e239e4cb537929796e840d349fc731fdc0d58b1a0683ce5465ad725321e0f", size = 69394, upload-time = "2025-05-08T02:37:06.142Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/51/aa30abc02aabfc41c95887acf5f1f88da569642d7197fbe5aa105545226d/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed05d43ec02be8ddb1ab59579761f6656b25d241a77fd74f4f0f7ec09074318a", size = 70377, upload-time = "2025-05-08T02:37:07.353Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/37/7773659b8d8d98b34234e1237352f6b446a3c12941619686c7d4a8a5c69c/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfeca1aaa59d93fd0a3718cbe5f7ef0effff85cf837e0bceb71831a47f39cc14", size = 67767, upload-time = "2025-05-08T02:37:08.587Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/1f/dd52a84ed140e31a5d226cd47d98d21aa559aead35ef7bae479eab4c494c/ijson-3.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7ca72ca12e9a1dd4252c97d952be34282907f263f7e28fcdff3a01b83981e837", size = 53864, upload-time = "2025-05-08T02:37:10.044Z" }, ] [[package]] name = "imagesize" version = "1.4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, ] [[package]] @@ -2654,33 +2654,33 @@ wheels = [ [[package]] name = "importlib-metadata" version = "8.7.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, ] [[package]] name = "infinity-emb" version = "0.0.66" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "hf-transfer" }, { name = "huggingface-hub" }, { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/25/f7/aa95638ba1aa5cc4ecdc9ac62a8db8715bf7975e17b4471cb86c7c7c1f56/infinity_emb-0.0.66.tar.gz", hash = "sha256:9c9a361ccebf8e8f626c1f685286518d03d0c35e7d14179ae7c2500b4fc68b98" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/f7/aa95638ba1aa5cc4ecdc9ac62a8db8715bf7975e17b4471cb86c7c7c1f56/infinity_emb-0.0.66.tar.gz", hash = "sha256:9c9a361ccebf8e8f626c1f685286518d03d0c35e7d14179ae7c2500b4fc68b98", size = 73314, upload-time = "2024-10-19T08:28:41.714Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6b/3a/a16d04f2af44295d60d4263455a136682816d03acd85a2f190051cc70288/infinity_emb-0.0.66-py3-none-any.whl", hash = "sha256:1dc6ed9fa48e6cbe83650a7583dbbb4bc393900c39c326bb0aff2ddc090ac018" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/3a/a16d04f2af44295d60d4263455a136682816d03acd85a2f190051cc70288/infinity_emb-0.0.66-py3-none-any.whl", hash = "sha256:1dc6ed9fa48e6cbe83650a7583dbbb4bc393900c39c326bb0aff2ddc090ac018", size = 89176, upload-time = "2024-10-19T08:28:40.079Z" }, ] [[package]] name = "infinity-sdk" -version = "0.6.0.dev5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +version = "0.6.0.dev7" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, { name = "openpyxl" }, @@ -2692,39 +2692,39 @@ dependencies = [ { name = "readerwriterlock" }, { name = "requests" }, { name = "setuptools" }, - { name = "sqlglot" }, + { name = "sqlglot", extra = ["rs"] }, { name = "thrift" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fe/a4/6079bf9790f16badc01e7b79a28c90bec407cfcaa8a2ed37e4a68120f87a/infinity_sdk-0.6.0.dev5-py3-none-any.whl", hash = "sha256:510ac408d5cd9d3d4df33c7c0877f55c5ae8a6019e465190c86d58012a319179" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/ec/f44f451d588f0d1d729eb1fcf1c0006d9fdeb116a33017e94d181dbee851/infinity_sdk-0.6.0.dev7-py3-none-any.whl", hash = "sha256:be4f51b667154ea407c2964769f10ebc00e362d3788e70e6c79f96df4970a40c", size = 75304, upload-time = "2025-10-10T02:42:08.49Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] [[package]] name = "inscriptis" version = "2.6.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "lxml" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/23/f0/77b6eeaa0cde44b8ef0d5d0c2c0ee7ddeb6e6e4aebec33e15b8d0abbf3ed/inscriptis-2.6.0.tar.gz", hash = "sha256:6f164bf45ea6972d61fd048a8e074d5125d215eaa837f8e70c158c97c31c3181" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/f0/77b6eeaa0cde44b8ef0d5d0c2c0ee7ddeb6e6e4aebec33e15b8d0abbf3ed/inscriptis-2.6.0.tar.gz", hash = "sha256:6f164bf45ea6972d61fd048a8e074d5125d215eaa837f8e70c158c97c31c3181", size = 41620, upload-time = "2025-03-22T18:23:26.246Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5e/c4/e1a68d42fa4609231da5f14e159c3d6256fb9e2951928592cebf7c899379/inscriptis-2.6.0-py3-none-any.whl", hash = "sha256:654dbcd0551c2f6004f8069a05cafff3eed2d327d5057adc6e657ba2610f52af" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/c4/e1a68d42fa4609231da5f14e159c3d6256fb9e2951928592cebf7c899379/inscriptis-2.6.0-py3-none-any.whl", hash = "sha256:654dbcd0551c2f6004f8069a05cafff3eed2d327d5057adc6e657ba2610f52af", size = 45120, upload-time = "2025-03-22T18:23:24.404Z" }, ] [[package]] name = "ir-datasets" version = "0.5.11" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "ijson" }, @@ -2742,217 +2742,217 @@ dependencies = [ { name = "warc3-wet-clueweb09" }, { name = "zlib-state" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/80/c6/f02811c51fec845ee87a10bb3675516a2d71935b203e5ddb80b7eb59b1da/ir_datasets-0.5.11.tar.gz", hash = "sha256:06c90af634ae5063c813286b35065debca1a974d26e136403d899f3ecd7ad463" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/c6/f02811c51fec845ee87a10bb3675516a2d71935b203e5ddb80b7eb59b1da/ir_datasets-0.5.11.tar.gz", hash = "sha256:06c90af634ae5063c813286b35065debca1a974d26e136403d899f3ecd7ad463", size = 758463, upload-time = "2025-06-24T07:58:31.375Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/81/38/73fa582d6997362d9b4901b2a8ba1177b2d2896aa59ab8d069a3884e2e0d/ir_datasets-0.5.11-py3-none-any.whl", hash = "sha256:ae78549e5a7fa45e50462b7acb9f0765fc344fec6054108bf3dd063050555206" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/38/73fa582d6997362d9b4901b2a8ba1177b2d2896aa59ab8d069a3884e2e0d/ir_datasets-0.5.11-py3-none-any.whl", hash = "sha256:ae78549e5a7fa45e50462b7acb9f0765fc344fec6054108bf3dd063050555206", size = 866095, upload-time = "2025-06-24T07:58:29.958Z" }, ] [[package]] name = "isodate" version = "0.7.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, ] [[package]] name = "itsdangerous" version = "2.1.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143, upload-time = "2022-03-24T15:12:15.102Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749, upload-time = "2022-03-24T15:12:13.2Z" }, ] [[package]] name = "jinja2" version = "3.1.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "jiter" version = "0.10.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/be/7e/4011b5c77bec97cb2b572f566220364e3e21b51c48c5bd9c4a9c26b41b67/jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/4f/144c1b57c39692efc7ea7d8e247acf28e47d0912800b34d0ad815f6b2824/jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/1f/db977336d332a9406c0b1f0b82be6f71f72526a806cbb2281baf201d38e3/jiter-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8b3e0068c26ddedc7abc6fac37da2d0af16b921e288a5a613f4b86f050354f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/1c/aa30a4a775e8a672ad7f21532bdbfb269f0706b39c6ff14e1f86bdd9e5ff/jiter-0.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:286299b74cc49e25cd42eea19b72aa82c515d2f2ee12d11392c56d8701f52224" }, - { url = "https://mirrors.aliyun.com/pypi/packages/35/df/f8257abc4207830cb18880781b5f5b716bad5b2a22fb4330cfd357407c5b/jiter-0.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ed5649ceeaeffc28d87fb012d25a4cd356dcd53eff5acff1f0466b831dda2a7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/76/9e1516fd7b4278aa13a2cc7f159e56befbea9aa65c71586305e7afa8b0b3/jiter-0.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ab0051160cb758a70716448908ef14ad476c3774bd03ddce075f3c1f90a3d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6d/64/67750672b4354ca20ca18d3d1ccf2c62a072e8a2d452ac3cf8ced73571ef/jiter-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03997d2f37f6b67d2f5c475da4412be584e1cec273c1cfc03d642c46db43f8cf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/4d/5c4e36d48f169a54b53a305114be3efa2bbffd33b648cd1478a688f639c1/jiter-0.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c404a99352d839fed80d6afd6c1d66071f3bacaaa5c4268983fc10f769112e90" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/de/ce4a6166a78810bd83763d2fa13f85f73cbd3743a325469a4a9289af6dae/jiter-0.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66e989410b6666d3ddb27a74c7e50d0829704ede652fd4c858e91f8d64b403d0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a2/a6/3bc9acce53466972964cf4ad85efecb94f9244539ab6da1107f7aed82934/jiter-0.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b532d3af9ef4f6374609a3bcb5e05a1951d3bf6190dc6b176fdb277c9bbf15ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/d8/243c2ab8426a2a4dea85ba2a2ba43df379ccece2145320dfd4799b9633c5/jiter-0.10.0-cp310-cp310-win32.whl", hash = "sha256:da9be20b333970e28b72edc4dff63d4fec3398e05770fb3205f7fb460eb48dd4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/7a/8021bd615ef7788b98fc76ff533eaac846322c170e93cbffa01979197a45/jiter-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:f59e533afed0c5b0ac3eba20d2548c4a550336d8282ee69eb07b37ea526ee4e5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/7e/4011b5c77bec97cb2b572f566220364e3e21b51c48c5bd9c4a9c26b41b67/jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303", size = 317215, upload-time = "2025-05-18T19:03:04.303Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/4f/144c1b57c39692efc7ea7d8e247acf28e47d0912800b34d0ad815f6b2824/jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e", size = 322814, upload-time = "2025-05-18T19:03:06.433Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/1f/db977336d332a9406c0b1f0b82be6f71f72526a806cbb2281baf201d38e3/jiter-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8b3e0068c26ddedc7abc6fac37da2d0af16b921e288a5a613f4b86f050354f", size = 345237, upload-time = "2025-05-18T19:03:07.833Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/1c/aa30a4a775e8a672ad7f21532bdbfb269f0706b39c6ff14e1f86bdd9e5ff/jiter-0.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:286299b74cc49e25cd42eea19b72aa82c515d2f2ee12d11392c56d8701f52224", size = 370999, upload-time = "2025-05-18T19:03:09.338Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/df/f8257abc4207830cb18880781b5f5b716bad5b2a22fb4330cfd357407c5b/jiter-0.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ed5649ceeaeffc28d87fb012d25a4cd356dcd53eff5acff1f0466b831dda2a7", size = 491109, upload-time = "2025-05-18T19:03:11.13Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/76/9e1516fd7b4278aa13a2cc7f159e56befbea9aa65c71586305e7afa8b0b3/jiter-0.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ab0051160cb758a70716448908ef14ad476c3774bd03ddce075f3c1f90a3d6", size = 388608, upload-time = "2025-05-18T19:03:12.911Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/64/67750672b4354ca20ca18d3d1ccf2c62a072e8a2d452ac3cf8ced73571ef/jiter-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03997d2f37f6b67d2f5c475da4412be584e1cec273c1cfc03d642c46db43f8cf", size = 352454, upload-time = "2025-05-18T19:03:14.741Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/4d/5c4e36d48f169a54b53a305114be3efa2bbffd33b648cd1478a688f639c1/jiter-0.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c404a99352d839fed80d6afd6c1d66071f3bacaaa5c4268983fc10f769112e90", size = 391833, upload-time = "2025-05-18T19:03:16.426Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/de/ce4a6166a78810bd83763d2fa13f85f73cbd3743a325469a4a9289af6dae/jiter-0.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66e989410b6666d3ddb27a74c7e50d0829704ede652fd4c858e91f8d64b403d0", size = 523646, upload-time = "2025-05-18T19:03:17.704Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/a6/3bc9acce53466972964cf4ad85efecb94f9244539ab6da1107f7aed82934/jiter-0.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b532d3af9ef4f6374609a3bcb5e05a1951d3bf6190dc6b176fdb277c9bbf15ee", size = 514735, upload-time = "2025-05-18T19:03:19.44Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/d8/243c2ab8426a2a4dea85ba2a2ba43df379ccece2145320dfd4799b9633c5/jiter-0.10.0-cp310-cp310-win32.whl", hash = "sha256:da9be20b333970e28b72edc4dff63d4fec3398e05770fb3205f7fb460eb48dd4", size = 210747, upload-time = "2025-05-18T19:03:21.184Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/7a/8021bd615ef7788b98fc76ff533eaac846322c170e93cbffa01979197a45/jiter-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:f59e533afed0c5b0ac3eba20d2548c4a550336d8282ee69eb07b37ea526ee4e5", size = 207484, upload-time = "2025-05-18T19:03:23.046Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978", size = 317473, upload-time = "2025-05-18T19:03:25.942Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc", size = 321971, upload-time = "2025-05-18T19:03:27.255Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d", size = 345574, upload-time = "2025-05-18T19:03:28.63Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2", size = 371028, upload-time = "2025-05-18T19:03:30.292Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61", size = 491083, upload-time = "2025-05-18T19:03:31.654Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db", size = 388821, upload-time = "2025-05-18T19:03:33.184Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5", size = 352174, upload-time = "2025-05-18T19:03:34.965Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606", size = 391869, upload-time = "2025-05-18T19:03:36.436Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605", size = 523741, upload-time = "2025-05-18T19:03:38.168Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5", size = 514527, upload-time = "2025-05-18T19:03:39.577Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7", size = 210765, upload-time = "2025-05-18T19:03:41.271Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812", size = 209234, upload-time = "2025-05-18T19:03:42.918Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262, upload-time = "2025-05-18T19:03:44.637Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124, upload-time = "2025-05-18T19:03:46.341Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330, upload-time = "2025-05-18T19:03:47.596Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670, upload-time = "2025-05-18T19:03:49.334Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057, upload-time = "2025-05-18T19:03:50.66Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372, upload-time = "2025-05-18T19:03:51.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038, upload-time = "2025-05-18T19:03:53.703Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538, upload-time = "2025-05-18T19:03:55.046Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557, upload-time = "2025-05-18T19:03:56.386Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202, upload-time = "2025-05-18T19:03:57.675Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781, upload-time = "2025-05-18T19:03:59.025Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176, upload-time = "2025-05-18T19:04:00.305Z" }, ] [[package]] name = "jmespath" version = "1.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, ] [[package]] name = "joblib" version = "1.5.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/dc/fe/0f5a938c54105553436dbff7a61dc4fed4b1b2c98852f8833beaf4d5968f/joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/fe/0f5a938c54105553436dbff7a61dc4fed4b1b2c98852f8833beaf4d5968f/joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444", size = 330475, upload-time = "2025-05-23T12:04:37.097Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746, upload-time = "2025-05-23T12:04:35.124Z" }, ] [[package]] name = "json-repair" version = "0.35.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/9c/5ef83a13541c3444e0b949e88b3aa0f4e364e37acf4ffa9de476d36a3de0/json_repair-0.35.0.tar.gz", hash = "sha256:e70f834865a4ae5fe64352c23c1c16d3b70c5dd62dc544a169d8b0932bdbdcaa" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/9c/5ef83a13541c3444e0b949e88b3aa0f4e364e37acf4ffa9de476d36a3de0/json_repair-0.35.0.tar.gz", hash = "sha256:e70f834865a4ae5fe64352c23c1c16d3b70c5dd62dc544a169d8b0932bdbdcaa", size = 29053, upload-time = "2024-12-31T12:03:52.239Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4e/c7/9a63ff79f2f6350c0f5e504bad940386015381b4d171bd73077465a7dbbc/json_repair-0.35.0-py3-none-any.whl", hash = "sha256:1d429407158474d28a996e745b8f8f7dc78957cb2cfbc92120b9f580b5230a9e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/c7/9a63ff79f2f6350c0f5e504bad940386015381b4d171bd73077465a7dbbc/json_repair-0.35.0-py3-none-any.whl", hash = "sha256:1d429407158474d28a996e745b8f8f7dc78957cb2cfbc92120b9f580b5230a9e", size = 19908, upload-time = "2024-12-31T12:03:51.234Z" }, ] [[package]] name = "jsonpath" version = "0.82.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cf/a1/693351acd0a9edca4de9153372a65e75398898ea7f8a5c722ab00f464929/jsonpath-0.82.2.tar.gz", hash = "sha256:d87ef2bcbcded68ee96bc34c1809b69457ecec9b0c4dd471658a12bd391002d1" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/a1/693351acd0a9edca4de9153372a65e75398898ea7f8a5c722ab00f464929/jsonpath-0.82.2.tar.gz", hash = "sha256:d87ef2bcbcded68ee96bc34c1809b69457ecec9b0c4dd471658a12bd391002d1" } [[package]] name = "jsonschema" version = "4.25.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "attrs" }, { name = "jsonschema-specifications" }, { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d5/00/a297a868e9d0784450faa7365c2172a7d6110c763e30ba861867c32ae6a9/jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/00/a297a868e9d0784450faa7365c2172a7d6110c763e30ba861867c32ae6a9/jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f", size = 356830, upload-time = "2025-07-18T15:39:45.11Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fe/54/c86cd8e011fe98803d7e382fd67c0df5ceab8d2b7ad8c5a81524f791551c/jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/54/c86cd8e011fe98803d7e382fd67c0df5ceab8d2b7ad8c5a81524f791551c/jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716", size = 89184, upload-time = "2025-07-18T15:39:42.956Z" }, ] [[package]] name = "jsonschema-specifications" version = "2025.4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, ] [[package]] name = "kaitaistruct" version = "0.10" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/54/04/dd60b9cb65d580ef6cb6eaee975ad1bdd22d46a3f51b07a1e0606710ea88/kaitaistruct-0.10.tar.gz", hash = "sha256:a044dee29173d6afbacf27bcac39daf89b654dd418cfa009ab82d9178a9ae52a" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/04/dd60b9cb65d580ef6cb6eaee975ad1bdd22d46a3f51b07a1e0606710ea88/kaitaistruct-0.10.tar.gz", hash = "sha256:a044dee29173d6afbacf27bcac39daf89b654dd418cfa009ab82d9178a9ae52a", size = 7061, upload-time = "2022-07-09T00:34:06.729Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4e/bf/88ad23efc08708bda9a2647169828e3553bb2093a473801db61f75356395/kaitaistruct-0.10-py2.py3-none-any.whl", hash = "sha256:a97350919adbf37fda881f75e9365e2fb88d04832b7a4e57106ec70119efb235" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/bf/88ad23efc08708bda9a2647169828e3553bb2093a473801db61f75356395/kaitaistruct-0.10-py2.py3-none-any.whl", hash = "sha256:a97350919adbf37fda881f75e9365e2fb88d04832b7a4e57106ec70119efb235", size = 7013, upload-time = "2022-07-09T00:34:03.905Z" }, ] [[package]] name = "kiwisolver" version = "1.4.8" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/47/5f/4d8e9e852d98ecd26cdf8eaf7ed8bc33174033bba5e07001b289f07308fd/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/70/7f5af2a18a76fe92ea14675f8bd88ce53ee79e37900fa5f1a1d8e0b42998/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/13/e15f804a142353aefd089fadc8f1d985561a15358c97aca27b0979cb0785/kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/6d/67d36c4d2054e83fb875c6b59d0809d5c530de8148846b1370475eeeece9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/de/c6/7b9bb8044e150d4d1558423a1568e4f227193662a02231064e3824f37e0a/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b6/38/ad10d437563063eaaedbe2c3540a71101fc7fb07a7e71f855e93ea4de605/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/ce/c0106b3bd7f9e665c5f5bc1e07cc95b5dabd4e08e3dad42dbe2faad467e7/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/87/efb704b1d75dc9758087ba374c0f23d3254505edaedd09cf9d247f7878b9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/b3/fd760dc214ec9a8f208b99e42e8f0130ff4b384eca8b29dd0efc62052176/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a2/09/a27fb36cca3fc01700687cc45dae7a6a5f8eeb5f657b9f710f788748e10d/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/c3/ba0a0346db35fe4dc1f2f2cf8b99362fbb922d7562e5f911f7ce7a7b60fa/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/52/942cf69e562f5ed253ac67d5c92a693745f0bed3c81f49fc0cbebe4d6b00/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/26/2d9668f30d8a494b0411d4d7d4ea1345ba12deb6a75274d58dd6ea01e951/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/99/0dd05071654aa44fe5d5e350729961e7bb535372935a45ac89a8924316e6/kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/fc/822e532262a97442989335394d441cd1d0448c2e46d26d3e04efca84df22/kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31" }, - { url = "https://mirrors.aliyun.com/pypi/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/f9/ae81c47a43e33b93b0a9819cac6723257f5da2a5a60daf46aa5c7226ea85/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/ca/f92b5cb6f4ce0c1ebfcfe3e2e42b96917e16f7090e45b21102941924f18f/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/28/ae0240f732f0484d3a4dc885d055653c47144bdf59b670aae0ec3c65a7c8/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/eb/78d50346c51db22c7203c1611f9b513075f35c4e0e4877c5dde378d66043/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/f8/7259f18c77adca88d5f64f9a522792e178b2691f3748817a8750c2d216ef/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3a/1d/50ad811d1c5dae091e4cf046beba925bcae0a610e79ae4c538f996f63ed5/kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538, upload-time = "2024-12-24T18:30:51.519Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/5f/4d8e9e852d98ecd26cdf8eaf7ed8bc33174033bba5e07001b289f07308fd/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db", size = 124623, upload-time = "2024-12-24T18:28:17.687Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/70/7f5af2a18a76fe92ea14675f8bd88ce53ee79e37900fa5f1a1d8e0b42998/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b", size = 66720, upload-time = "2024-12-24T18:28:19.158Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/13/e15f804a142353aefd089fadc8f1d985561a15358c97aca27b0979cb0785/kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d", size = 65413, upload-time = "2024-12-24T18:28:20.064Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/6d/67d36c4d2054e83fb875c6b59d0809d5c530de8148846b1370475eeeece9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d", size = 1650826, upload-time = "2024-12-24T18:28:21.203Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/c6/7b9bb8044e150d4d1558423a1568e4f227193662a02231064e3824f37e0a/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c", size = 1628231, upload-time = "2024-12-24T18:28:23.851Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/38/ad10d437563063eaaedbe2c3540a71101fc7fb07a7e71f855e93ea4de605/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3", size = 1408938, upload-time = "2024-12-24T18:28:26.687Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/ce/c0106b3bd7f9e665c5f5bc1e07cc95b5dabd4e08e3dad42dbe2faad467e7/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed", size = 1422799, upload-time = "2024-12-24T18:28:30.538Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/87/efb704b1d75dc9758087ba374c0f23d3254505edaedd09cf9d247f7878b9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f", size = 1354362, upload-time = "2024-12-24T18:28:32.943Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/b3/fd760dc214ec9a8f208b99e42e8f0130ff4b384eca8b29dd0efc62052176/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff", size = 2222695, upload-time = "2024-12-24T18:28:35.641Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/09/a27fb36cca3fc01700687cc45dae7a6a5f8eeb5f657b9f710f788748e10d/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d", size = 2370802, upload-time = "2024-12-24T18:28:38.357Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/c3/ba0a0346db35fe4dc1f2f2cf8b99362fbb922d7562e5f911f7ce7a7b60fa/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c", size = 2334646, upload-time = "2024-12-24T18:28:40.941Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/52/942cf69e562f5ed253ac67d5c92a693745f0bed3c81f49fc0cbebe4d6b00/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605", size = 2467260, upload-time = "2024-12-24T18:28:42.273Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/26/2d9668f30d8a494b0411d4d7d4ea1345ba12deb6a75274d58dd6ea01e951/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e", size = 2288633, upload-time = "2024-12-24T18:28:44.87Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/99/0dd05071654aa44fe5d5e350729961e7bb535372935a45ac89a8924316e6/kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751", size = 71885, upload-time = "2024-12-24T18:28:47.346Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/fc/822e532262a97442989335394d441cd1d0448c2e46d26d3e04efca84df22/kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271", size = 65175, upload-time = "2024-12-24T18:28:49.651Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635, upload-time = "2024-12-24T18:28:51.826Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717, upload-time = "2024-12-24T18:28:54.256Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413, upload-time = "2024-12-24T18:28:55.184Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994, upload-time = "2024-12-24T18:28:57.493Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804, upload-time = "2024-12-24T18:29:00.077Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690, upload-time = "2024-12-24T18:29:01.401Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839, upload-time = "2024-12-24T18:29:02.685Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109, upload-time = "2024-12-24T18:29:04.113Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269, upload-time = "2024-12-24T18:29:05.488Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468, upload-time = "2024-12-24T18:29:06.79Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394, upload-time = "2024-12-24T18:29:08.24Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901, upload-time = "2024-12-24T18:29:09.653Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306, upload-time = "2024-12-24T18:29:12.644Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966, upload-time = "2024-12-24T18:29:14.089Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311, upload-time = "2024-12-24T18:29:15.892Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152, upload-time = "2024-12-24T18:29:16.85Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555, upload-time = "2024-12-24T18:29:19.146Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067, upload-time = "2024-12-24T18:29:20.096Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443, upload-time = "2024-12-24T18:29:22.843Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728, upload-time = "2024-12-24T18:29:24.463Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388, upload-time = "2024-12-24T18:29:25.776Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849, upload-time = "2024-12-24T18:29:27.202Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533, upload-time = "2024-12-24T18:29:28.638Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898, upload-time = "2024-12-24T18:29:30.368Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605, upload-time = "2024-12-24T18:29:33.151Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801, upload-time = "2024-12-24T18:29:34.584Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077, upload-time = "2024-12-24T18:29:36.138Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410, upload-time = "2024-12-24T18:29:39.991Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853, upload-time = "2024-12-24T18:29:42.006Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424, upload-time = "2024-12-24T18:29:44.38Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/f9/ae81c47a43e33b93b0a9819cac6723257f5da2a5a60daf46aa5c7226ea85/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a", size = 60403, upload-time = "2024-12-24T18:30:41.372Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/ca/f92b5cb6f4ce0c1ebfcfe3e2e42b96917e16f7090e45b21102941924f18f/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8", size = 58657, upload-time = "2024-12-24T18:30:42.392Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/28/ae0240f732f0484d3a4dc885d055653c47144bdf59b670aae0ec3c65a7c8/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0", size = 84948, upload-time = "2024-12-24T18:30:44.703Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/eb/78d50346c51db22c7203c1611f9b513075f35c4e0e4877c5dde378d66043/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c", size = 81186, upload-time = "2024-12-24T18:30:45.654Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/f8/7259f18c77adca88d5f64f9a522792e178b2691f3748817a8750c2d216ef/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b", size = 80279, upload-time = "2024-12-24T18:30:47.951Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/1d/50ad811d1c5dae091e4cf046beba925bcae0a610e79ae4c538f996f63ed5/kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b", size = 71762, upload-time = "2024-12-24T18:30:48.903Z" }, ] [[package]] name = "langfuse" version = "3.2.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "backoff" }, { name = "httpx" }, @@ -2964,18 +2964,18 @@ dependencies = [ { name = "requests" }, { name = "wrapt" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/61/0d/8fc51099cf337fb3b56cb7d305074bc0223c62e1ccabf80cc6285ccf5b31/langfuse-3.2.1.tar.gz", hash = "sha256:f79b0380dfcf52c7525bb5d7f8e9d8786a6fc8b37867def047bb388930a7beb3" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/0d/8fc51099cf337fb3b56cb7d305074bc0223c62e1ccabf80cc6285ccf5b31/langfuse-3.2.1.tar.gz", hash = "sha256:f79b0380dfcf52c7525bb5d7f8e9d8786a6fc8b37867def047bb388930a7beb3", size = 153369, upload-time = "2025-07-16T09:50:28.434Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/92/b0/8f08df3f0fa584c4132937690c6dd33e0a116f963ecf2b35567f614e0ca7/langfuse-3.2.1-py3-none-any.whl", hash = "sha256:07a84e8c1eed6ac8e149bdda1431fd866e4aee741b66124316336fb2bc7e6a32" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/b0/8f08df3f0fa584c4132937690c6dd33e0a116f963ecf2b35567f614e0ca7/langfuse-3.2.1-py3-none-any.whl", hash = "sha256:07a84e8c1eed6ac8e149bdda1431fd866e4aee741b66124316336fb2bc7e6a32", size = 299315, upload-time = "2025-07-16T09:50:26.582Z" }, ] [[package]] name = "lark" version = "1.2.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80", size = 252132, upload-time = "2024-08-13T19:49:00.652Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size = 111036, upload-time = "2024-08-13T19:48:58.603Z" }, ] [[package]] @@ -2990,7 +2990,7 @@ wheels = [ [[package]] name = "litellm" version = "1.75.5.post1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, @@ -3004,154 +3004,154 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/10/97/6091a020895102a20f1da204ebe68c1293123555476b38e749f95ba5981c/litellm-1.75.5.post1.tar.gz", hash = "sha256:e40a0e4b25032755dc5df7f02742abe9e3b8836236363f605f3bdd363cb5a0d0" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/97/6091a020895102a20f1da204ebe68c1293123555476b38e749f95ba5981c/litellm-1.75.5.post1.tar.gz", hash = "sha256:e40a0e4b25032755dc5df7f02742abe9e3b8836236363f605f3bdd363cb5a0d0", size = 10127846, upload-time = "2025-08-10T16:30:23.788Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8f/76/780f68a3b26227136a5147c76860aacedcae9bf1b7afc1c991ec9cad11bc/litellm-1.75.5.post1-py3-none-any.whl", hash = "sha256:1c72809a9c8f6e132ad06eb7e628f674c775b0ce6bfb58cbd37e8b585d929cb7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/76/780f68a3b26227136a5147c76860aacedcae9bf1b7afc1c991ec9cad11bc/litellm-1.75.5.post1-py3-none-any.whl", hash = "sha256:1c72809a9c8f6e132ad06eb7e628f674c775b0ce6bfb58cbd37e8b585d929cb7", size = 8895997, upload-time = "2025-08-10T16:30:21.325Z" }, ] [[package]] name = "llvmlite" version = "0.44.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/41/75/d4863ddfd8ab5f6e70f4504cf8cc37f4e986ec6910f4ef8502bb7d3c1c71/llvmlite-0.44.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9fbadbfba8422123bab5535b293da1cf72f9f478a65645ecd73e781f962ca614" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/d9/6e8943e1515d2f1003e8278819ec03e4e653e2eeb71e4d00de6cfe59424e/llvmlite-0.44.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cccf8eb28f24840f2689fb1a45f9c0f7e582dd24e088dcf96e424834af11f791" }, - { url = "https://mirrors.aliyun.com/pypi/packages/aa/46/8ffbc114def88cc698906bf5acab54ca9fdf9214fe04aed0e71731fb3688/llvmlite-0.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7202b678cdf904823c764ee0fe2dfe38a76981f4c1e51715b4cb5abb6cf1d9e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/30/1c/9366b29ab050a726af13ebaae8d0dff00c3c58562261c79c635ad4f5eb71/llvmlite-0.44.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40526fb5e313d7b96bda4cbb2c85cd5374e04d80732dd36a282d72a560bb6408" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/07/35e7c594b021ecb1938540f5bce543ddd8713cff97f71d81f021221edc1b/llvmlite-0.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:41e3839150db4330e1b2716c0be3b5c4672525b4c9005e17c7597f835f351ce2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b5/e2/86b245397052386595ad726f9742e5223d7aea999b18c518a50e96c3aca4/llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/ec/506902dc6870249fbe2466d9cf66d531265d0f3a1157213c8f986250c033/llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/fe/d030f1849ebb1f394bb3f7adad5e729b634fb100515594aca25c354ffc62/llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/7a/ce6174664b9077fc673d172e4c888cb0b128e707e306bc33fff8c2035f0d/llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/c6/258801143975a6d09a373f2641237992496e15567b907a4d401839d671b8/llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955" }, - { url = "https://mirrors.aliyun.com/pypi/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880, upload-time = "2025-01-20T11:14:41.342Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/75/d4863ddfd8ab5f6e70f4504cf8cc37f4e986ec6910f4ef8502bb7d3c1c71/llvmlite-0.44.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9fbadbfba8422123bab5535b293da1cf72f9f478a65645ecd73e781f962ca614", size = 28132306, upload-time = "2025-01-20T11:12:18.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/d9/6e8943e1515d2f1003e8278819ec03e4e653e2eeb71e4d00de6cfe59424e/llvmlite-0.44.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cccf8eb28f24840f2689fb1a45f9c0f7e582dd24e088dcf96e424834af11f791", size = 26201096, upload-time = "2025-01-20T11:12:24.544Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/46/8ffbc114def88cc698906bf5acab54ca9fdf9214fe04aed0e71731fb3688/llvmlite-0.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7202b678cdf904823c764ee0fe2dfe38a76981f4c1e51715b4cb5abb6cf1d9e8", size = 42361859, upload-time = "2025-01-20T11:12:31.839Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/1c/9366b29ab050a726af13ebaae8d0dff00c3c58562261c79c635ad4f5eb71/llvmlite-0.44.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40526fb5e313d7b96bda4cbb2c85cd5374e04d80732dd36a282d72a560bb6408", size = 41184199, upload-time = "2025-01-20T11:12:40.049Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/07/35e7c594b021ecb1938540f5bce543ddd8713cff97f71d81f021221edc1b/llvmlite-0.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:41e3839150db4330e1b2716c0be3b5c4672525b4c9005e17c7597f835f351ce2", size = 30332381, upload-time = "2025-01-20T11:12:47.054Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/e2/86b245397052386595ad726f9742e5223d7aea999b18c518a50e96c3aca4/llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3", size = 28132305, upload-time = "2025-01-20T11:12:53.936Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/ec/506902dc6870249fbe2466d9cf66d531265d0f3a1157213c8f986250c033/llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427", size = 26201090, upload-time = "2025-01-20T11:12:59.847Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/fe/d030f1849ebb1f394bb3f7adad5e729b634fb100515594aca25c354ffc62/llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1", size = 42361858, upload-time = "2025-01-20T11:13:07.623Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/7a/ce6174664b9077fc673d172e4c888cb0b128e707e306bc33fff8c2035f0d/llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610", size = 41184200, upload-time = "2025-01-20T11:13:20.058Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/c6/258801143975a6d09a373f2641237992496e15567b907a4d401839d671b8/llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955", size = 30331193, upload-time = "2025-01-20T11:13:26.976Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297, upload-time = "2025-01-20T11:13:32.57Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105, upload-time = "2025-01-20T11:13:38.744Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901, upload-time = "2025-01-20T11:13:46.711Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247, upload-time = "2025-01-20T11:13:56.159Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380, upload-time = "2025-01-20T11:14:02.442Z" }, ] [[package]] name = "loguru" version = "0.7.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "win32-setctime", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, ] [[package]] name = "lxml" version = "5.3.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a1/ce/2789e39eddf2b13fac29878bfa465f0910eb6b0096e29090e5176bc8cf43/lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/a8/f4010166a25d41715527129af2675981a50d3bbf7df09c5d9ab8ca24fbf9/lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/a4/7e45756cecdd7577ddf67a68b69c1db0f5ddbf0c9f65021ee769165ffc5a/lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/e2/ecf845b12323c92748077e1818b64e8b4dba509a4cb12920b3762ebe7552/lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/12/91/619f9fb72cf75e9ceb8700706f7276f23995f6ad757e6d400fbe35ca4990/lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/3b/162a85a8f0fd2a3032ec3f936636911c6e9523a8e263fffcfd581ce98b54/lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/af/dd3f58cc7d946da6ae42909629a2b1d5dd2d1b583334d4af9396697d6863/lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/c1/5ea46b2d4c98f5bf5c83fffab8a0ad293c9bc74df9ecfbafef10f77f7201/lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/51/a0acca077ad35da458f4d3f729ef98effd2b90f003440d35fc36323f8ae6/lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4d/6b/0989c9368986961a6b0f55b46c80404c4b758417acdb6d87bfc3bd5f4967/lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/9e/87492d03ff604fbf656ed2bf3e2e8d28f5d58ea1f00ff27ac27b06509079/lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/cc/9ae1baf5472af88e19e2c454b3710c1be9ecafb20eb474eeabcd88a055d2/lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/10/5594ffaec8c120d75b17e3ad23439b740a51549a9b5fd7484b2179adfe8f/lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/9b/de17f05377c8833343b629905571fb06cff2028f15a6f58ae2267662e341/lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/b4/227be0f1f3cca8255925985164c3838b8b36e441ff0cc10c1d3c6bdba031/lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/ee/19abcebb7fc40319bb71cd6adefa1ad94d09b5660228715854d6cc420713/lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/35/183d32551447e280032b2331738cd850da435a42f850b71ebeaab42c1313/lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/a8/449faa2a3cbe6a99f8d38dcd51a3ee8844c17862841a6f769ea7c2a9cd0f/lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/8a/ae6325e994e2052de92f894363b038351c50ee38749d30cc6b6d96aaf90f/lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/23/0e/caac672ec246d3189a16c4d364ed4f7d6bf856c080215382c06764058c08/lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/a4/1f5fbd3f58d4069000522196b0b776a014f3feec1796da03e495cf23532d/lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ee/73/623ecea6ca3c530dd0a4ed0d00d9702e0e85cd5624e2d5b93b005fe00abd/lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/ce/fb84fb8e3c298f3a245ae3ea6221c2426f1bbaa82d10a88787412a498145/lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/72/4d1ad363748a72c7c0411c28be2b0dc7150d91e823eadad3b91a4514cbea/lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080" }, - { url = "https://mirrors.aliyun.com/pypi/packages/42/07/b29571a58a3a80681722ea8ed0ba569211d9bb8531ad49b5cacf6d409185/lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/93/bde740d5a58cf04cbd38e3dd93ad1e36c2f95553bbf7d57807bc6815d926/lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/ae/e7ad0f0fbe4b6368c5ee1e3ef0c3365098d806d42379c46c1ba2802a52f7/lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/b5/91c2249bfac02ee514ab135e9304b89d55967be7e53e94a879b74eec7a5c/lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/6d/d1f1c5e40c64bf62afd7a3f9b34ce18a586a1cccbf71e783cd0a6d8e8971/lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bd/83/26b1864921869784355459f374896dcf8b44d4af3b15d7697e9156cb2de9/lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/37/396fbd24a70f62b31d988e4500f2068c7f3fd399d2fd45257d13eab51a6f/lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/91/e6136f17459a11ce1757df864b213efbeab7adcb2efa63efb1b846ab6723/lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/7c/2eeecf87c9a1fca4f84f991067c693e67340f2b7127fc3eca8fa29d75ee3/lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/ed/4c38ba58defca84f5f0d0ac2480fdcd99fc7ae4b28fc417c93640a6949ae/lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/22/bbd3995437e5745cb4c2b5d89088d70ab19d4feabf8a27a24cecb9745464/lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/6e/94537acfb5b8f18235d13186d247bca478fea5e87d224644e0fe907df976/lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/e8/4b15df533fe8e8d53363b23a41df9be907330e1fa28c7ca36893fad338ee/lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/f7/b73a431c8500565aa500e99e60b448d305eaf7c0b4c893c7c5a8a69cc595/lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/db/48/4a206623c0d093d0e3b15f415ffb4345b0bdf661a3d0b15a112948c033c7/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/47/577820c45dd954523ae8453b632d91e76da94ca6d9ee40d8c98dd86f916b/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/de/96cb6d3269bc994b4f5ede8ca7bf0840f5de0a278bc6e50cb317ff71cafa/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/43/19b1ef6cbffa4244a217f95cc5f41a6cb4720fed33510a49670b03c5f1a0/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/b2/6a22fb5c0885da3b00e116aee81f0b829ec9ac8f736cd414b4a09413fc7d/lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318, upload-time = "2024-08-10T18:17:29.668Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ce/2789e39eddf2b13fac29878bfa465f0910eb6b0096e29090e5176bc8cf43/lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656", size = 8124570, upload-time = "2024-08-10T18:09:04.096Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/a8/f4010166a25d41715527129af2675981a50d3bbf7df09c5d9ab8ca24fbf9/lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d", size = 4413042, upload-time = "2024-08-10T18:09:08.841Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/a4/7e45756cecdd7577ddf67a68b69c1db0f5ddbf0c9f65021ee769165ffc5a/lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a", size = 5139213, upload-time = "2024-08-10T18:09:12.622Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/e2/ecf845b12323c92748077e1818b64e8b4dba509a4cb12920b3762ebe7552/lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8", size = 4838814, upload-time = "2024-08-10T18:09:16.222Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/91/619f9fb72cf75e9ceb8700706f7276f23995f6ad757e6d400fbe35ca4990/lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330", size = 5425084, upload-time = "2024-08-10T18:09:19.795Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/3b/162a85a8f0fd2a3032ec3f936636911c6e9523a8e263fffcfd581ce98b54/lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965", size = 4875993, upload-time = "2024-08-10T18:09:23.776Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/af/dd3f58cc7d946da6ae42909629a2b1d5dd2d1b583334d4af9396697d6863/lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22", size = 5012462, upload-time = "2024-08-10T18:09:27.642Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/c1/5ea46b2d4c98f5bf5c83fffab8a0ad293c9bc74df9ecfbafef10f77f7201/lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b", size = 4815288, upload-time = "2024-08-10T18:09:31.633Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/51/a0acca077ad35da458f4d3f729ef98effd2b90f003440d35fc36323f8ae6/lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7", size = 5472435, upload-time = "2024-08-10T18:09:35.758Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/6b/0989c9368986961a6b0f55b46c80404c4b758417acdb6d87bfc3bd5f4967/lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8", size = 4976354, upload-time = "2024-08-10T18:09:39.51Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/9e/87492d03ff604fbf656ed2bf3e2e8d28f5d58ea1f00ff27ac27b06509079/lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32", size = 5029973, upload-time = "2024-08-10T18:09:42.978Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/cc/9ae1baf5472af88e19e2c454b3710c1be9ecafb20eb474eeabcd88a055d2/lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86", size = 4888837, upload-time = "2024-08-10T18:09:46.185Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/10/5594ffaec8c120d75b17e3ad23439b740a51549a9b5fd7484b2179adfe8f/lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5", size = 5530555, upload-time = "2024-08-10T18:09:50.366Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/9b/de17f05377c8833343b629905571fb06cff2028f15a6f58ae2267662e341/lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03", size = 5405314, upload-time = "2024-08-10T18:09:54.58Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/b4/227be0f1f3cca8255925985164c3838b8b36e441ff0cc10c1d3c6bdba031/lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7", size = 5079303, upload-time = "2024-08-10T18:09:58.032Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/ee/19abcebb7fc40319bb71cd6adefa1ad94d09b5660228715854d6cc420713/lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80", size = 3475126, upload-time = "2024-08-10T18:10:01.43Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/35/183d32551447e280032b2331738cd850da435a42f850b71ebeaab42c1313/lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3", size = 3805065, upload-time = "2024-08-10T18:10:05.189Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/a8/449faa2a3cbe6a99f8d38dcd51a3ee8844c17862841a6f769ea7c2a9cd0f/lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b", size = 8141056, upload-time = "2024-08-10T18:10:09.455Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/8a/ae6325e994e2052de92f894363b038351c50ee38749d30cc6b6d96aaf90f/lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18", size = 4425238, upload-time = "2024-08-10T18:10:13.348Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197, upload-time = "2024-08-10T18:10:16.825Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809, upload-time = "2024-08-10T18:10:20.046Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593, upload-time = "2024-08-10T18:10:23.641Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/0e/caac672ec246d3189a16c4d364ed4f7d6bf856c080215382c06764058c08/lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e", size = 4866657, upload-time = "2024-08-10T18:10:26.528Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/a4/1f5fbd3f58d4069000522196b0b776a014f3feec1796da03e495cf23532d/lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c", size = 4967017, upload-time = "2024-08-10T18:10:29.639Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/73/623ecea6ca3c530dd0a4ed0d00d9702e0e85cd5624e2d5b93b005fe00abd/lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16", size = 4810730, upload-time = "2024-08-10T18:10:33.387Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/ce/fb84fb8e3c298f3a245ae3ea6221c2426f1bbaa82d10a88787412a498145/lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79", size = 5455154, upload-time = "2024-08-10T18:10:36.897Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/72/4d1ad363748a72c7c0411c28be2b0dc7150d91e823eadad3b91a4514cbea/lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080", size = 4969416, upload-time = "2024-08-10T18:10:40.331Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/07/b29571a58a3a80681722ea8ed0ba569211d9bb8531ad49b5cacf6d409185/lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654", size = 5013672, upload-time = "2024-08-10T18:10:43.768Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/93/bde740d5a58cf04cbd38e3dd93ad1e36c2f95553bbf7d57807bc6815d926/lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d", size = 4878644, upload-time = "2024-08-10T18:10:47.901Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531, upload-time = "2024-08-10T18:10:51.581Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065, upload-time = "2024-08-10T18:10:54.841Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775, upload-time = "2024-08-10T18:10:57.808Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/ae/e7ad0f0fbe4b6368c5ee1e3ef0c3365098d806d42379c46c1ba2802a52f7/lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9", size = 3474226, upload-time = "2024-08-10T18:11:00.73Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/b5/91c2249bfac02ee514ab135e9304b89d55967be7e53e94a879b74eec7a5c/lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1", size = 3814971, upload-time = "2024-08-10T18:11:03.743Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/6d/d1f1c5e40c64bf62afd7a3f9b34ce18a586a1cccbf71e783cd0a6d8e8971/lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859", size = 8171753, upload-time = "2024-08-10T18:11:07.859Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/83/26b1864921869784355459f374896dcf8b44d4af3b15d7697e9156cb2de9/lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e", size = 4441955, upload-time = "2024-08-10T18:11:12.251Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778, upload-time = "2024-08-10T18:11:16.233Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628, upload-time = "2024-08-10T18:11:19.507Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215, upload-time = "2024-08-10T18:11:23.708Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/37/396fbd24a70f62b31d988e4500f2068c7f3fd399d2fd45257d13eab51a6f/lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a", size = 4813963, upload-time = "2024-08-10T18:11:26.997Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/91/e6136f17459a11ce1757df864b213efbeab7adcb2efa63efb1b846ab6723/lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3", size = 4923353, upload-time = "2024-08-10T18:11:30.478Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/7c/2eeecf87c9a1fca4f84f991067c693e67340f2b7127fc3eca8fa29d75ee3/lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1", size = 4740541, upload-time = "2024-08-10T18:11:34.344Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/ed/4c38ba58defca84f5f0d0ac2480fdcd99fc7ae4b28fc417c93640a6949ae/lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d", size = 5346504, upload-time = "2024-08-10T18:11:37.595Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/22/bbd3995437e5745cb4c2b5d89088d70ab19d4feabf8a27a24cecb9745464/lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c", size = 4898077, upload-time = "2024-08-10T18:11:40.867Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/6e/94537acfb5b8f18235d13186d247bca478fea5e87d224644e0fe907df976/lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99", size = 4946543, upload-time = "2024-08-10T18:11:44.954Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/e8/4b15df533fe8e8d53363b23a41df9be907330e1fa28c7ca36893fad338ee/lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff", size = 4816841, upload-time = "2024-08-10T18:11:49.046Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341, upload-time = "2024-08-10T18:11:52.295Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539, upload-time = "2024-08-10T18:11:55.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542, upload-time = "2024-08-10T18:11:59.351Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", size = 3486454, upload-time = "2024-08-10T18:12:02.696Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", size = 3816857, upload-time = "2024-08-10T18:12:06.456Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/f7/b73a431c8500565aa500e99e60b448d305eaf7c0b4c893c7c5a8a69cc595/lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c", size = 3925431, upload-time = "2024-08-10T18:15:59.002Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/48/4a206623c0d093d0e3b15f415ffb4345b0bdf661a3d0b15a112948c033c7/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a", size = 4216683, upload-time = "2024-08-10T18:16:03.004Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/47/577820c45dd954523ae8453b632d91e76da94ca6d9ee40d8c98dd86f916b/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005", size = 4326732, upload-time = "2024-08-10T18:16:06.973Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/de/96cb6d3269bc994b4f5ede8ca7bf0840f5de0a278bc6e50cb317ff71cafa/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce", size = 4218377, upload-time = "2024-08-10T18:16:10.836Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/43/19b1ef6cbffa4244a217f95cc5f41a6cb4720fed33510a49670b03c5f1a0/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83", size = 4351237, upload-time = "2024-08-10T18:16:14.652Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/b2/6a22fb5c0885da3b00e116aee81f0b829ec9ac8f736cd414b4a09413fc7d/lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba", size = 3487557, upload-time = "2024-08-10T18:16:18.255Z" }, ] [[package]] name = "lxml-html-clean" version = "0.4.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "lxml" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/79/b6/466e71db127950fb8d172026a8f0a9f0dc6f64c8e78e2ca79f252e5790b8/lxml_html_clean-0.4.2.tar.gz", hash = "sha256:91291e7b5db95430abf461bc53440964d58e06cc468950f9e47db64976cebcb3" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/b6/466e71db127950fb8d172026a8f0a9f0dc6f64c8e78e2ca79f252e5790b8/lxml_html_clean-0.4.2.tar.gz", hash = "sha256:91291e7b5db95430abf461bc53440964d58e06cc468950f9e47db64976cebcb3", size = 21622, upload-time = "2025-04-09T11:33:59.432Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4e/0b/942cb7278d6caad79343ad2ddd636ed204a47909b969d19114a3097f5aa3/lxml_html_clean-0.4.2-py3-none-any.whl", hash = "sha256:74ccfba277adcfea87a1e9294f47dd86b05d65b4da7c5b07966e3d5f3be8a505" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/0b/942cb7278d6caad79343ad2ddd636ed204a47909b969d19114a3097f5aa3/lxml_html_clean-0.4.2-py3-none-any.whl", hash = "sha256:74ccfba277adcfea87a1e9294f47dd86b05d65b4da7c5b07966e3d5f3be8a505", size = 14184, upload-time = "2025-04-09T11:33:57.988Z" }, ] [[package]] name = "lz4" version = "4.4.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c6/5a/945f5086326d569f14c84ac6f7fcc3229f0b9b1e8cc536b951fd53dfb9e1/lz4-4.4.4.tar.gz", hash = "sha256:070fd0627ec4393011251a094e08ed9fdcc78cb4e7ab28f507638eee4e39abda" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b0/80/4054e99cda2e003097f59aeb3ad470128f3298db5065174a84564d2d6983/lz4-4.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f170abb8416c4efca48e76cac2c86c3185efdf841aecbe5c190121c42828ced0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/4e/f92424d5734e772b05ddbeec739e2566e2a2336995b36a180e1dd9411e9a/lz4-4.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d33a5105cd96ebd32c3e78d7ece6123a9d2fb7c18b84dec61f27837d9e0c496c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a2/70/71ffd496067cba6ba352e10b89c0e9cee3e4bc4717ba866b6aa350f4c7ac/lz4-4.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ebbc5b76b4f0018988825a7e9ce153be4f0d4eba34e6c1f2fcded120573e88" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/59/cf34d1e232b11e1ae7122300be00529f369a7cd80f74ac351d58c4c4eedf/lz4-4.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc64d6dfa7a89397529b22638939e70d85eaedc1bd68e30a29c78bfb65d4f715" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/f6/3a00a98ff5b872d572cc6e9c88e0f6275bea0f3ed1dc1b8f8b736c85784c/lz4-4.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a355223a284f42a723c120ce68827de66d5cb872a38732b3d5abbf544fa2fe26" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/de/6aeb602786174bad290609c0c988afb1077b74a80eaea23ebc3b5de6e2fa/lz4-4.4.4-cp310-cp310-win32.whl", hash = "sha256:b28228197775b7b5096898851d59ef43ccaf151136f81d9c436bc9ba560bc2ba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e4/b5/1f52c8b17d02ae637f85911c0135ca08be1c9bbdfb3e7de1c4ae7af0bac6/lz4-4.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:45e7c954546de4f85d895aa735989d77f87dd649f503ce1c8a71a151b092ed36" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/e7/123587e7dae6cdba48393e4fdad2b9412f43f51346afe9ca6f697029de11/lz4-4.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:e3fc90f766401684740978cd781d73b9685bd81b5dbf7257542ef9de4612e4d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/e8/63843dc5ecb1529eb38e1761ceed04a0ad52a9ad8929ab8b7930ea2e4976/lz4-4.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ddfc7194cd206496c445e9e5b0c47f970ce982c725c87bd22de028884125b68f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e4/94/c53de5f07c7dc11cf459aab2a1d754f5df5f693bfacbbe1e4914bfd02f1e/lz4-4.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:714f9298c86f8e7278f1c6af23e509044782fa8220eb0260f8f8f1632f820550" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fe/59/c22d516dd0352f2a3415d1f665ccef2f3e74ecec3ca6a8f061a38f97d50d/lz4-4.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8474c91de47733856c6686df3c4aca33753741da7e757979369c2c0d32918ba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/af/665685072e71f3f0e626221b7922867ec249cd8376aca761078c8f11f5da/lz4-4.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80dd27d7d680ea02c261c226acf1d41de2fd77af4fb2da62b278a9376e380de0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/90/04/b4557ae381d3aa451388a29755cc410066f5e2f78c847f66f154f4520a68/lz4-4.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b7d6dddfd01b49aedb940fdcaf32f41dc58c926ba35f4e31866aeec2f32f4f4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/e4/03636979f4e8bf92c557f998ca98ee4e6ef92e92eaf0ed6d3c7f2524e790/lz4-4.4.4-cp311-cp311-win32.whl", hash = "sha256:4134b9fd70ac41954c080b772816bb1afe0c8354ee993015a83430031d686a4c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/f0/9efe53b4945441a5d2790d455134843ad86739855b7e6199977bf6dc8898/lz4-4.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:f5024d3ca2383470f7c4ef4d0ed8eabad0b22b23eeefde1c192cf1a38d5e9f78" }, - { url = "https://mirrors.aliyun.com/pypi/packages/87/c8/1675527549ee174b9e1db089f7ddfbb962a97314657269b1e0344a5eaf56/lz4-4.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:6ea715bb3357ea1665f77874cf8f55385ff112553db06f3742d3cdcec08633f7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/2d/5523b4fabe11cd98f040f715728d1932eb7e696bfe94391872a823332b94/lz4-4.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:23ae267494fdd80f0d2a131beff890cf857f1b812ee72dbb96c3204aab725553" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/06/1a5bbcacbfb48d8ee5b6eb3fca6aa84143a81d92946bdb5cd6b005f1863e/lz4-4.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fff9f3a1ed63d45cb6514bfb8293005dc4141341ce3500abdfeb76124c0b9b2e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fa/08/39eb7ac907f73e11a69a11576a75a9e36406b3241c0ba41453a7eb842abb/lz4-4.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ea7f07329f85a8eda4d8cf937b87f27f0ac392c6400f18bea2c667c8b7f8ecc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/26/05840fbd4233e8d23e88411a066ab19f1e9de332edddb8df2b6a95c7fddc/lz4-4.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ccab8f7f7b82f9fa9fc3b0ba584d353bd5aa818d5821d77d5b9447faad2aaad" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/5d/5f2db18c298a419932f3ab2023deb689863cf8fd7ed875b1c43492479af2/lz4-4.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43e9d48b2daf80e486213128b0763deed35bbb7a59b66d1681e205e1702d735" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c4/e6/736ab5f128694b0f6aac58343bcf37163437ac95997276cd0be3ea4c3342/lz4-4.4.4-cp312-cp312-win32.whl", hash = "sha256:33e01e18e4561b0381b2c33d58e77ceee850a5067f0ece945064cbaac2176962" }, - { url = "https://mirrors.aliyun.com/pypi/packages/40/b8/243430cb62319175070e06e3a94c4c7bd186a812e474e22148ae1290d47d/lz4-4.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d21d1a2892a2dcc193163dd13eaadabb2c1b803807a5117d8f8588b22eaf9f12" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/e1/0686c91738f3e6c2e1a243e0fdd4371667c4d2e5009b0a3605806c2aa020/lz4-4.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:2f4f2965c98ab254feddf6b5072854a6935adab7bc81412ec4fe238f07b85f62" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/5a/945f5086326d569f14c84ac6f7fcc3229f0b9b1e8cc536b951fd53dfb9e1/lz4-4.4.4.tar.gz", hash = "sha256:070fd0627ec4393011251a094e08ed9fdcc78cb4e7ab28f507638eee4e39abda", size = 171884, upload-time = "2025-04-01T22:55:58.62Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/80/4054e99cda2e003097f59aeb3ad470128f3298db5065174a84564d2d6983/lz4-4.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f170abb8416c4efca48e76cac2c86c3185efdf841aecbe5c190121c42828ced0", size = 220896, upload-time = "2025-04-01T22:55:13.577Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/4e/f92424d5734e772b05ddbeec739e2566e2a2336995b36a180e1dd9411e9a/lz4-4.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d33a5105cd96ebd32c3e78d7ece6123a9d2fb7c18b84dec61f27837d9e0c496c", size = 189679, upload-time = "2025-04-01T22:55:15.471Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/70/71ffd496067cba6ba352e10b89c0e9cee3e4bc4717ba866b6aa350f4c7ac/lz4-4.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ebbc5b76b4f0018988825a7e9ce153be4f0d4eba34e6c1f2fcded120573e88", size = 1237940, upload-time = "2025-04-01T22:55:16.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/59/cf34d1e232b11e1ae7122300be00529f369a7cd80f74ac351d58c4c4eedf/lz4-4.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc64d6dfa7a89397529b22638939e70d85eaedc1bd68e30a29c78bfb65d4f715", size = 1264105, upload-time = "2025-04-01T22:55:17.606Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/f6/3a00a98ff5b872d572cc6e9c88e0f6275bea0f3ed1dc1b8f8b736c85784c/lz4-4.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a355223a284f42a723c120ce68827de66d5cb872a38732b3d5abbf544fa2fe26", size = 1184179, upload-time = "2025-04-01T22:55:19.206Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/de/6aeb602786174bad290609c0c988afb1077b74a80eaea23ebc3b5de6e2fa/lz4-4.4.4-cp310-cp310-win32.whl", hash = "sha256:b28228197775b7b5096898851d59ef43ccaf151136f81d9c436bc9ba560bc2ba", size = 88265, upload-time = "2025-04-01T22:55:20.215Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/b5/1f52c8b17d02ae637f85911c0135ca08be1c9bbdfb3e7de1c4ae7af0bac6/lz4-4.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:45e7c954546de4f85d895aa735989d77f87dd649f503ce1c8a71a151b092ed36", size = 99916, upload-time = "2025-04-01T22:55:21.332Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/e7/123587e7dae6cdba48393e4fdad2b9412f43f51346afe9ca6f697029de11/lz4-4.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:e3fc90f766401684740978cd781d73b9685bd81b5dbf7257542ef9de4612e4d2", size = 89746, upload-time = "2025-04-01T22:55:22.205Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/e8/63843dc5ecb1529eb38e1761ceed04a0ad52a9ad8929ab8b7930ea2e4976/lz4-4.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ddfc7194cd206496c445e9e5b0c47f970ce982c725c87bd22de028884125b68f", size = 220898, upload-time = "2025-04-01T22:55:23.085Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/94/c53de5f07c7dc11cf459aab2a1d754f5df5f693bfacbbe1e4914bfd02f1e/lz4-4.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:714f9298c86f8e7278f1c6af23e509044782fa8220eb0260f8f8f1632f820550", size = 189685, upload-time = "2025-04-01T22:55:24.413Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/59/c22d516dd0352f2a3415d1f665ccef2f3e74ecec3ca6a8f061a38f97d50d/lz4-4.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8474c91de47733856c6686df3c4aca33753741da7e757979369c2c0d32918ba", size = 1239225, upload-time = "2025-04-01T22:55:25.737Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/af/665685072e71f3f0e626221b7922867ec249cd8376aca761078c8f11f5da/lz4-4.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80dd27d7d680ea02c261c226acf1d41de2fd77af4fb2da62b278a9376e380de0", size = 1265881, upload-time = "2025-04-01T22:55:26.817Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/04/b4557ae381d3aa451388a29755cc410066f5e2f78c847f66f154f4520a68/lz4-4.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b7d6dddfd01b49aedb940fdcaf32f41dc58c926ba35f4e31866aeec2f32f4f4", size = 1185593, upload-time = "2025-04-01T22:55:27.896Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/e4/03636979f4e8bf92c557f998ca98ee4e6ef92e92eaf0ed6d3c7f2524e790/lz4-4.4.4-cp311-cp311-win32.whl", hash = "sha256:4134b9fd70ac41954c080b772816bb1afe0c8354ee993015a83430031d686a4c", size = 88259, upload-time = "2025-04-01T22:55:29.03Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/f0/9efe53b4945441a5d2790d455134843ad86739855b7e6199977bf6dc8898/lz4-4.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:f5024d3ca2383470f7c4ef4d0ed8eabad0b22b23eeefde1c192cf1a38d5e9f78", size = 99916, upload-time = "2025-04-01T22:55:29.933Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/c8/1675527549ee174b9e1db089f7ddfbb962a97314657269b1e0344a5eaf56/lz4-4.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:6ea715bb3357ea1665f77874cf8f55385ff112553db06f3742d3cdcec08633f7", size = 89741, upload-time = "2025-04-01T22:55:31.184Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/2d/5523b4fabe11cd98f040f715728d1932eb7e696bfe94391872a823332b94/lz4-4.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:23ae267494fdd80f0d2a131beff890cf857f1b812ee72dbb96c3204aab725553", size = 220669, upload-time = "2025-04-01T22:55:32.032Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/06/1a5bbcacbfb48d8ee5b6eb3fca6aa84143a81d92946bdb5cd6b005f1863e/lz4-4.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fff9f3a1ed63d45cb6514bfb8293005dc4141341ce3500abdfeb76124c0b9b2e", size = 189661, upload-time = "2025-04-01T22:55:33.413Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/08/39eb7ac907f73e11a69a11576a75a9e36406b3241c0ba41453a7eb842abb/lz4-4.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ea7f07329f85a8eda4d8cf937b87f27f0ac392c6400f18bea2c667c8b7f8ecc", size = 1238775, upload-time = "2025-04-01T22:55:34.835Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/26/05840fbd4233e8d23e88411a066ab19f1e9de332edddb8df2b6a95c7fddc/lz4-4.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ccab8f7f7b82f9fa9fc3b0ba584d353bd5aa818d5821d77d5b9447faad2aaad", size = 1265143, upload-time = "2025-04-01T22:55:35.933Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/5d/5f2db18c298a419932f3ab2023deb689863cf8fd7ed875b1c43492479af2/lz4-4.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43e9d48b2daf80e486213128b0763deed35bbb7a59b66d1681e205e1702d735", size = 1185032, upload-time = "2025-04-01T22:55:37.454Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/e6/736ab5f128694b0f6aac58343bcf37163437ac95997276cd0be3ea4c3342/lz4-4.4.4-cp312-cp312-win32.whl", hash = "sha256:33e01e18e4561b0381b2c33d58e77ceee850a5067f0ece945064cbaac2176962", size = 88284, upload-time = "2025-04-01T22:55:38.536Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/b8/243430cb62319175070e06e3a94c4c7bd186a812e474e22148ae1290d47d/lz4-4.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d21d1a2892a2dcc193163dd13eaadabb2c1b803807a5117d8f8588b22eaf9f12", size = 99918, upload-time = "2025-04-01T22:55:39.628Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/e1/0686c91738f3e6c2e1a243e0fdd4371667c4d2e5009b0a3605806c2aa020/lz4-4.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:2f4f2965c98ab254feddf6b5072854a6935adab7bc81412ec4fe238f07b85f62", size = 89736, upload-time = "2025-04-01T22:55:40.5Z" }, ] [[package]] @@ -3169,31 +3169,31 @@ wheels = [ [[package]] name = "markdown" version = "3.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/22/02/4785861427848cc11e452cc62bb541006a1087cf04a1de83aedd5530b948/Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/02/4785861427848cc11e452cc62bb541006a1087cf04a1de83aedd5530b948/Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224", size = 354715, upload-time = "2024-03-14T15:37:59.775Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fc/b3/0c0c994fe49cd661084f8d5dc06562af53818cc0abefaca35bdc894577c3/Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/b3/0c0c994fe49cd661084f8d5dc06562af53818cc0abefaca35bdc894577c3/Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f", size = 105381, upload-time = "2024-03-14T15:37:57.457Z" }, ] [[package]] name = "markdown-it-py" version = "3.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] [[package]] name = "markdown-to-json" version = "2.1.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b5/1a/d235321eac5ba6de9f83dd172b9549eb03fd149ecda4c8c25cdc9a5224bc/markdown_to_json-2.1.1.tar.gz", hash = "sha256:27642c42acd9130d1449f791f57fd0c4bbf58c7a76cfb5af6d42010ca97b1107" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/1a/d235321eac5ba6de9f83dd172b9549eb03fd149ecda4c8c25cdc9a5224bc/markdown_to_json-2.1.1.tar.gz", hash = "sha256:27642c42acd9130d1449f791f57fd0c4bbf58c7a76cfb5af6d42010ca97b1107", size = 51343, upload-time = "2024-05-09T19:08:44.729Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/47/2b/dac4143951a16c0c03e8fe217c9fa784838d02a29c52ef0e8b265befea8f/markdown_to_json-2.1.1-py3-none-any.whl", hash = "sha256:c73b8a3ac7fbde65463dbaeba8bb925d1d54377cbb01a064cd65e1f3e394bd62" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/2b/dac4143951a16c0c03e8fe217c9fa784838d02a29c52ef0e8b265befea8f/markdown_to_json-2.1.1-py3-none-any.whl", hash = "sha256:c73b8a3ac7fbde65463dbaeba8bb925d1d54377cbb01a064cd65e1f3e394bd62", size = 52647, upload-time = "2024-05-09T19:08:42.959Z" }, ] [[package]] @@ -3212,48 +3212,48 @@ wheels = [ [[package]] name = "markupsafe" version = "3.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48" }, - { url = "https://mirrors.aliyun.com/pypi/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, ] [[package]] name = "matplotlib" version = "3.10.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version < '3.11'" }, - { name = "contourpy", version = "1.3.3", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version < '3.11'" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version >= '3.11'" }, { name = "cycler" }, { name = "fonttools" }, { name = "kiwisolver" }, @@ -3263,35 +3263,35 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d0/ea/2bba25d289d389c7451f331ecd593944b3705f06ddf593fa7be75037d308/matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/81/cc70b5138c926604e8c9ed810ed4c79e8116ba72e02230852f5c12c87ba2/matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/9a/0ff45b6bfa42bb16de597e6058edf2361c298ad5ef93b327728145161bbf/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c21ae75651c0231b3ba014b6d5e08fb969c40cdb5a011e33e99ed0c9ea86ecb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/c7/1866e972fed6d71ef136efbc980d4d1854ab7ef1ea8152bbd995ca231c81/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e39755580b08e30e3620efc659330eac5d6534ab7eae50fa5e31f53ee4e30" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/b9/748f6626d534ab7e255bdc39dc22634d337cf3ce200f261b5d65742044a1/matplotlib-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf4636203e1190871d3a73664dea03d26fb019b66692cbfd642faafdad6208e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/78/8bf07bd8fb67ea5665a6af188e70b57fcb2ab67057daa06b85a08e59160a/matplotlib-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:fd5641a9bb9d55f4dd2afe897a53b537c834b9012684c8444cc105895c8c16fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f5/bd/af9f655456f60fe1d575f54fb14704ee299b16e999704817a7645dfce6b0/matplotlib-3.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0ef061f74cd488586f552d0c336b2f078d43bc00dc473d2c3e7bfee2272f3fa8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/86/e1c86690610661cd716eda5f9d0b35eaf606ae6c9b6736687cfc8f2d0cd8/matplotlib-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96985d14dc5f4a736bbea4b9de9afaa735f8a0fc2ca75be2fa9e96b2097369d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/51/a9f8e49af3883dacddb2da1af5fca1f7468677f1188936452dd9aaaeb9ed/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5f0283da91e9522bdba4d6583ed9d5521566f63729ffb68334f86d0bb98049" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/e3/c82963a3b86d6e6d5874cbeaa390166458a7f1961bab9feb14d3d1a10f02/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/34/24da1027e7fcdd9e82da3194c470143c551852757a4b473a09a012f5b945/matplotlib-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c0b9849a17bce080a16ebcb80a7b714b5677d0ec32161a2cc0a8e5a6030ae220" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/da/948a017c3ea13fd4a97afad5fdebe2f5bbc4d28c0654510ce6fd6b06b7bd/matplotlib-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:eef6ed6c03717083bc6d69c2d7ee8624205c29a8e6ea5a31cd3492ecdbaee1e1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/d1/f54d43e95384b312ffa4a74a4326c722f3b8187aaaa12e9a84cdf3037131/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:86ab63d66bbc83fdb6733471d3bff40897c1e9921cba112accd748eee4bce5e4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/a4/fbfc00c2346177c95b353dcf9b5a004106abe8730a62cb6f27e79df0a698/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a48f9c08bf7444b5d2391a83e75edb464ccda3c380384b36532a0962593a1751" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/b9/59e120d24a2ec5fc2d30646adb2efb4621aab3c6d83d66fb2a7a182db032/matplotlib-3.10.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb73d8aa75a237457988f9765e4dfe1c0d2453c5ca4eabc897d4309672c8e014" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811, upload-time = "2025-05-08T19:10:54.39Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/ea/2bba25d289d389c7451f331ecd593944b3705f06ddf593fa7be75037d308/matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7", size = 8167862, upload-time = "2025-05-08T19:09:39.563Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/81/cc70b5138c926604e8c9ed810ed4c79e8116ba72e02230852f5c12c87ba2/matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb", size = 8042149, upload-time = "2025-05-08T19:09:42.413Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/9a/0ff45b6bfa42bb16de597e6058edf2361c298ad5ef93b327728145161bbf/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c21ae75651c0231b3ba014b6d5e08fb969c40cdb5a011e33e99ed0c9ea86ecb", size = 8453719, upload-time = "2025-05-08T19:09:44.901Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/c7/1866e972fed6d71ef136efbc980d4d1854ab7ef1ea8152bbd995ca231c81/matplotlib-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e39755580b08e30e3620efc659330eac5d6534ab7eae50fa5e31f53ee4e30", size = 8590801, upload-time = "2025-05-08T19:09:47.404Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/b9/748f6626d534ab7e255bdc39dc22634d337cf3ce200f261b5d65742044a1/matplotlib-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf4636203e1190871d3a73664dea03d26fb019b66692cbfd642faafdad6208e8", size = 9402111, upload-time = "2025-05-08T19:09:49.474Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/78/8bf07bd8fb67ea5665a6af188e70b57fcb2ab67057daa06b85a08e59160a/matplotlib-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:fd5641a9bb9d55f4dd2afe897a53b537c834b9012684c8444cc105895c8c16fd", size = 8057213, upload-time = "2025-05-08T19:09:51.489Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/bd/af9f655456f60fe1d575f54fb14704ee299b16e999704817a7645dfce6b0/matplotlib-3.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0ef061f74cd488586f552d0c336b2f078d43bc00dc473d2c3e7bfee2272f3fa8", size = 8178873, upload-time = "2025-05-08T19:09:53.857Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/86/e1c86690610661cd716eda5f9d0b35eaf606ae6c9b6736687cfc8f2d0cd8/matplotlib-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96985d14dc5f4a736bbea4b9de9afaa735f8a0fc2ca75be2fa9e96b2097369d", size = 8052205, upload-time = "2025-05-08T19:09:55.684Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/51/a9f8e49af3883dacddb2da1af5fca1f7468677f1188936452dd9aaaeb9ed/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5f0283da91e9522bdba4d6583ed9d5521566f63729ffb68334f86d0bb98049", size = 8465823, upload-time = "2025-05-08T19:09:57.442Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/e3/c82963a3b86d6e6d5874cbeaa390166458a7f1961bab9feb14d3d1a10f02/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b", size = 8606464, upload-time = "2025-05-08T19:09:59.471Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/34/24da1027e7fcdd9e82da3194c470143c551852757a4b473a09a012f5b945/matplotlib-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c0b9849a17bce080a16ebcb80a7b714b5677d0ec32161a2cc0a8e5a6030ae220", size = 9413103, upload-time = "2025-05-08T19:10:03.208Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/da/948a017c3ea13fd4a97afad5fdebe2f5bbc4d28c0654510ce6fd6b06b7bd/matplotlib-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:eef6ed6c03717083bc6d69c2d7ee8624205c29a8e6ea5a31cd3492ecdbaee1e1", size = 8065492, upload-time = "2025-05-08T19:10:05.271Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689, upload-time = "2025-05-08T19:10:07.602Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466, upload-time = "2025-05-08T19:10:09.383Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252, upload-time = "2025-05-08T19:10:11.958Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321, upload-time = "2025-05-08T19:10:14.47Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972, upload-time = "2025-05-08T19:10:16.569Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954, upload-time = "2025-05-08T19:10:18.663Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/d1/f54d43e95384b312ffa4a74a4326c722f3b8187aaaa12e9a84cdf3037131/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:86ab63d66bbc83fdb6733471d3bff40897c1e9921cba112accd748eee4bce5e4", size = 8162896, upload-time = "2025-05-08T19:10:46.432Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/a4/fbfc00c2346177c95b353dcf9b5a004106abe8730a62cb6f27e79df0a698/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a48f9c08bf7444b5d2391a83e75edb464ccda3c380384b36532a0962593a1751", size = 8039702, upload-time = "2025-05-08T19:10:49.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/b9/59e120d24a2ec5fc2d30646adb2efb4621aab3c6d83d66fb2a7a182db032/matplotlib-3.10.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb73d8aa75a237457988f9765e4dfe1c0d2453c5ca4eabc897d4309672c8e014", size = 8594298, upload-time = "2025-05-08T19:10:51.738Z" }, ] [[package]] name = "mcp" version = "1.12.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, { name = "httpx" }, @@ -3305,39 +3305,39 @@ dependencies = [ { name = "starlette" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/66/85/f36d538b1286b7758f35c1b69d93f2719d2df90c01bd074eadd35f6afc35/mcp-1.12.2.tar.gz", hash = "sha256:a4b7c742c50ce6ed6d6a6c096cca0e3893f5aecc89a59ed06d47c4e6ba41edcc" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/85/f36d538b1286b7758f35c1b69d93f2719d2df90c01bd074eadd35f6afc35/mcp-1.12.2.tar.gz", hash = "sha256:a4b7c742c50ce6ed6d6a6c096cca0e3893f5aecc89a59ed06d47c4e6ba41edcc", size = 426202, upload-time = "2025-07-24T18:29:05.175Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2f/cf/3fd38cfe43962452e4bfadc6966b2ea0afaf8e0286cb3991c247c8c33ebd/mcp-1.12.2-py3-none-any.whl", hash = "sha256:b86d584bb60193a42bd78aef01882c5c42d614e416cbf0480149839377ab5a5f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/cf/3fd38cfe43962452e4bfadc6966b2ea0afaf8e0286cb3991c247c8c33ebd/mcp-1.12.2-py3-none-any.whl", hash = "sha256:b86d584bb60193a42bd78aef01882c5c42d614e416cbf0480149839377ab5a5f", size = 158473, upload-time = "2025-07-24T18:29:03.419Z" }, ] [[package]] name = "mdurl" version = "0.1.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] name = "mini-racer" version = "0.12.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8c/2d/e051f58e17117b1b8b11a7d17622c1528fa9002c553943c6b677c1b412da/mini_racer-0.12.4.tar.gz", hash = "sha256:84c67553ce9f3736d4c617d8a3f882949d37a46cfb47fe11dab33dd6704e62a4" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/2d/e051f58e17117b1b8b11a7d17622c1528fa9002c553943c6b677c1b412da/mini_racer-0.12.4.tar.gz", hash = "sha256:84c67553ce9f3736d4c617d8a3f882949d37a46cfb47fe11dab33dd6704e62a4", size = 447529, upload-time = "2024-06-20T14:44:39.992Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/71/fe/1452b6c74cae9e8cd7b6a16d8b1ef08bba4dd0ed373a95f3b401c2e712ea/mini_racer-0.12.4-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:bce8a3cee946575a352f5e65335903bc148da42c036d0c738ac67e931600e455" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/ae/c22478eff26e6136341e6b40d34f8d285f910ca4d2e2a0ca4703ef87be79/mini_racer-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:56c832e6ac2db6a304d1e8e80030615297aafbc6940f64f3479af4ba16abccd5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/89/f062aa116b14fcace91f0af86a37605f0ba7c07a01c8101b5ea104d489b1/mini_racer-0.12.4-py3-none-manylinux_2_31_aarch64.whl", hash = "sha256:b82c4bd2976e280ed0a72c9c2de01b13f18ccfbe6f4892cbc22aae04410fac3c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9c/a1/09122c88a0dd0a2141b0ea068d70f5d31acd0015d6f3157b8efd3ff7e026/mini_racer-0.12.4-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:69a1c44d02a9069b881684cef15a2d747fe0743df29eadc881fda7002aae5fd2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/3b/826e41f92631560e5c6ca2aa4ef9005bdccf9290c1e7ddebe05e0a3b8c7c/mini_racer-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:499dbc267dfe60e954bc1b6c3787f7b10fc41fe1975853c9a6ddb55eb83dc4d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/37/15b30316630d1f63b025f058dc92efa75931a37315c34ca07f80be2cc405/mini_racer-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:231f949f5787d18351939f1fe59e5a6fe134bccb5ecf8f836b9beab69d91c8d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/0e/a9943f90b4a8a6d3849b81a00a00d2db128d876365385af382a0e2caf191/mini_racer-0.12.4-py3-none-win_amd64.whl", hash = "sha256:9446e3bd6a4eb9fbedf1861326f7476080995a31c9b69308acef17e5b7ecaa1b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/fe/1452b6c74cae9e8cd7b6a16d8b1ef08bba4dd0ed373a95f3b401c2e712ea/mini_racer-0.12.4-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:bce8a3cee946575a352f5e65335903bc148da42c036d0c738ac67e931600e455", size = 15701219, upload-time = "2024-06-20T14:44:21.96Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/ae/c22478eff26e6136341e6b40d34f8d285f910ca4d2e2a0ca4703ef87be79/mini_racer-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:56c832e6ac2db6a304d1e8e80030615297aafbc6940f64f3479af4ba16abccd5", size = 14566436, upload-time = "2024-06-20T14:44:24.496Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/89/f062aa116b14fcace91f0af86a37605f0ba7c07a01c8101b5ea104d489b1/mini_racer-0.12.4-py3-none-manylinux_2_31_aarch64.whl", hash = "sha256:b82c4bd2976e280ed0a72c9c2de01b13f18ccfbe6f4892cbc22aae04410fac3c", size = 14931664, upload-time = "2024-06-20T14:44:27.385Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/a1/09122c88a0dd0a2141b0ea068d70f5d31acd0015d6f3157b8efd3ff7e026/mini_racer-0.12.4-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:69a1c44d02a9069b881684cef15a2d747fe0743df29eadc881fda7002aae5fd2", size = 14955238, upload-time = "2024-06-20T14:44:30.217Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/3b/826e41f92631560e5c6ca2aa4ef9005bdccf9290c1e7ddebe05e0a3b8c7c/mini_racer-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:499dbc267dfe60e954bc1b6c3787f7b10fc41fe1975853c9a6ddb55eb83dc4d9", size = 15211136, upload-time = "2024-06-20T14:44:33.509Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/37/15b30316630d1f63b025f058dc92efa75931a37315c34ca07f80be2cc405/mini_racer-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:231f949f5787d18351939f1fe59e5a6fe134bccb5ecf8f836b9beab69d91c8d9", size = 15128684, upload-time = "2024-06-20T14:44:35.644Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/0e/a9943f90b4a8a6d3849b81a00a00d2db128d876365385af382a0e2caf191/mini_racer-0.12.4-py3-none-win_amd64.whl", hash = "sha256:9446e3bd6a4eb9fbedf1861326f7476080995a31c9b69308acef17e5b7ecaa1b", size = 13674040, upload-time = "2024-06-20T14:44:37.851Z" }, ] [[package]] name = "minio" version = "7.2.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "argon2-cffi" }, { name = "certifi" }, @@ -3345,155 +3345,155 @@ dependencies = [ { name = "typing-extensions" }, { name = "urllib3" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3a/2e/7bd24eb2e02a19a03bd0e73e59c051c62c62cabdd305ccbc59a90143752c/minio-7.2.4.tar.gz", hash = "sha256:d504d8464e5198fb74dd9b572cc88b185ae7997c17705e8c09f3fef2f439d984" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/2e/7bd24eb2e02a19a03bd0e73e59c051c62c62cabdd305ccbc59a90143752c/minio-7.2.4.tar.gz", hash = "sha256:d504d8464e5198fb74dd9b572cc88b185ae7997c17705e8c09f3fef2f439d984", size = 134100, upload-time = "2024-02-11T00:41:07.19Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a8/29/17ec9cecedad692cf18abd0b5e57d7008d1dda8929915e7cfee76ea0e849/minio-7.2.4-py3-none-any.whl", hash = "sha256:91b51c21d25e3ee6d51f52eab126d6c974371add0d77951e42c322a59c5533e7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/29/17ec9cecedad692cf18abd0b5e57d7008d1dda8929915e7cfee76ea0e849/minio-7.2.4-py3-none-any.whl", hash = "sha256:91b51c21d25e3ee6d51f52eab126d6c974371add0d77951e42c322a59c5533e7", size = 92644, upload-time = "2024-02-11T00:41:04.907Z" }, ] [[package]] name = "mistralai" version = "0.4.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "httpx" }, { name = "orjson" }, { name = "pydantic" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fa/20/4204f461588310b3a7ffbbbb7fa573493dc1c8185d376ee72516c04575bf/mistralai-0.4.2.tar.gz", hash = "sha256:5eb656710517168ae053f9847b0bb7f617eda07f1f93f946ad6c91a4d407fd93" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/20/4204f461588310b3a7ffbbbb7fa573493dc1c8185d376ee72516c04575bf/mistralai-0.4.2.tar.gz", hash = "sha256:5eb656710517168ae053f9847b0bb7f617eda07f1f93f946ad6c91a4d407fd93", size = 14234, upload-time = "2024-07-04T09:22:43.992Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4f/fe/79dad76b8d94b62d9e2aab8446183190e1dc384c617d06c3c93307850e11/mistralai-0.4.2-py3-none-any.whl", hash = "sha256:63c98eea139585f0a3b2c4c6c09c453738bac3958055e6f2362d3866e96b0168" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/fe/79dad76b8d94b62d9e2aab8446183190e1dc384c617d06c3c93307850e11/mistralai-0.4.2-py3-none-any.whl", hash = "sha256:63c98eea139585f0a3b2c4c6c09c453738bac3958055e6f2362d3866e96b0168", size = 20334, upload-time = "2024-07-04T09:22:42.211Z" }, ] [[package]] name = "mistune" version = "3.1.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c4/79/bda47f7dd7c3c55770478d6d02c9960c430b0cf1773b72366ff89126ea31/mistune-3.1.3.tar.gz", hash = "sha256:a7035c21782b2becb6be62f8f25d3df81ccb4d6fa477a6525b15af06539f02a0" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/79/bda47f7dd7c3c55770478d6d02c9960c430b0cf1773b72366ff89126ea31/mistune-3.1.3.tar.gz", hash = "sha256:a7035c21782b2becb6be62f8f25d3df81ccb4d6fa477a6525b15af06539f02a0", size = 94347, upload-time = "2025-03-19T14:27:24.955Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/01/4d/23c4e4f09da849e127e9f123241946c23c1e30f45a88366879e064211815/mistune-3.1.3-py3-none-any.whl", hash = "sha256:1a32314113cff28aa6432e99e522677c8587fd83e3d51c29b82a52409c842bd9" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/4d/23c4e4f09da849e127e9f123241946c23c1e30f45a88366879e064211815/mistune-3.1.3-py3-none-any.whl", hash = "sha256:1a32314113cff28aa6432e99e522677c8587fd83e3d51c29b82a52409c842bd9", size = 53410, upload-time = "2025-03-19T14:27:23.451Z" }, ] [[package]] name = "mmh3" version = "4.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/63/96/aa247e82878b123468f0079ce2ac77e948315bab91ce45d2934a62e0af95/mmh3-4.1.0.tar.gz", hash = "sha256:a1cf25348b9acd229dda464a094d6170f47d2850a1fcb762a3b6172d2ce6ca4a" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ef/5a/8609dc74421858f7e94a89dc69221ab9b2c14d0d63a139b46ec190eedc44/mmh3-4.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be5ac76a8b0cd8095784e51e4c1c9c318c19edcd1709a06eb14979c8d850c31a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/6c/e7a0f07c7082c76964b1ff46aa852f36e2ec6a9c3530dec0afa0b3162fc2/mmh3-4.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98a49121afdfab67cd80e912b36404139d7deceb6773a83620137aaa0da5714c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/84/60ca728ec7d7e1779a98000d64941c6221786124b4f07bf105a627055890/mmh3-4.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5259ac0535874366e7d1a5423ef746e0d36a9e3c14509ce6511614bdc5a7ef5b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/22/f2ec190b491f712d9ef5ea6252204b6f05255ac9af54a7b505adc3128aed/mmh3-4.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5950827ca0453a2be357696da509ab39646044e3fa15cad364eb65d78797437" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/b9/c1e8065671e1d2f4e280c9c57389e74964f4a5792cac26717ad592002c7d/mmh3-4.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1dd0f652ae99585b9dd26de458e5f08571522f0402155809fd1dc8852a613a39" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6b/18/92bbdb102ab2b4e80084e927187d871758280eb067c649693e42bfc6d0d1/mmh3-4.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d25548070942fab1e4a6f04d1626d67e66d0b81ed6571ecfca511f3edf07e6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/cd/391ce1d1bb559871a5d3a6bbb30b82bf51d3e3b42c4e8589cccb201953da/mmh3-4.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53db8d9bad3cb66c8f35cbc894f336273f63489ce4ac416634932e3cbe79eb5b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/87/4b01a43336bd506478850d1bc3d180648b2d26b4acf1fc4bf1df72bf562f/mmh3-4.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75da0f615eb55295a437264cc0b736753f830b09d102aa4c2a7d719bc445ec05" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e8/12/b464149a1b7181c7ce431ebf3d24fa994863f2f1abc75b78d202dde966e0/mmh3-4.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b926b07fd678ea84b3a2afc1fa22ce50aeb627839c44382f3d0291e945621e1a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/3e/f4eb45a23fc17b970394c1fe74eba157514577ae2d63757684241651d754/mmh3-4.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c5b053334f9b0af8559d6da9dc72cef0a65b325ebb3e630c680012323c950bb6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/3b/83934fd9494371357da0ca026d55ad427c199d611b97b6ffeecacfd8e720/mmh3-4.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5bf33dc43cd6de2cb86e0aa73a1cc6530f557854bbbe5d59f41ef6de2e353d7b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b6/c4/5bcd709ea7269173d7e925402f05e05cf12194ef53cc9912a5ad166f8ded/mmh3-4.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fa7eacd2b830727ba3dd65a365bed8a5c992ecd0c8348cf39a05cc77d22f4970" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/6a/4c0680d64475e551d7f4cc78bf0fd247c711ed2717f6bb311934993d1e69/mmh3-4.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42dfd6742b9e3eec599f85270617debfa0bbb913c545bb980c8a4fa7b2d047da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/bc/e2ed99e580b3dd121f6462147bd5f521c57b3c81c692aa2d416b0678c89f/mmh3-4.1.0-cp310-cp310-win32.whl", hash = "sha256:2974ad343f0d39dcc88e93ee6afa96cedc35a9883bc067febd7ff736e207fa47" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/2b/3aec865da7feb52830782d9fb7c54115cc18815680c244301adf9080622f/mmh3-4.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:74699a8984ded645c1a24d6078351a056f5a5f1fe5838870412a68ac5e28d865" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/2a/925439189ccf562bdcb839aed6263d718359f0c376d673beb3b83d3864ac/mmh3-4.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:f0dc874cedc23d46fc488a987faa6ad08ffa79e44fb08e3cd4d4cf2877c00a00" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/d6/86beea107e7e9700df9522466346c23a2f54faa81337c86fd17002aa95a6/mmh3-4.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3280a463855b0eae64b681cd5b9ddd9464b73f81151e87bb7c91a811d25619e6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/08/65fa5489044e2afc304e8540c6c607d5d7b136ddc5cd8315c13de0adc34c/mmh3-4.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:97ac57c6c3301769e757d444fa7c973ceb002cb66534b39cbab5e38de61cd896" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/aa/98511d3ea3f6ba958136d913be3be3c1009be935a20ecc7b2763f0a605b6/mmh3-4.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b6502cdb4dbd880244818ab363c8770a48cdccecf6d729ade0241b736b5ec0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/b7/1a93f81643435b0e57f1046c4ffe46f0214693eaede0d9b0a1a236776e70/mmh3-4.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52ba2da04671a9621580ddabf72f06f0e72c1c9c3b7b608849b58b11080d8f14" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/9e/2ff70246aefd9cf146bc6a420c28ed475a0d1a325f31ee203be02f9215d4/mmh3-4.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a5fef4c4ecc782e6e43fbeab09cff1bac82c998a1773d3a5ee6a3605cde343e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dc/cb/57bc1fdbdbe6837aebfca982494e23e2498ee2a89585c9054713b22e4167/mmh3-4.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5135358a7e00991f73b88cdc8eda5203bf9de22120d10a834c5761dbeb07dd13" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4d/c2/46d7d2721b69fbdfd30231309e6395f62ff6744e5c00dd8113b9faa06fba/mmh3-4.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cff9ae76a54f7c6fe0167c9c4028c12c1f6de52d68a31d11b6790bb2ae685560" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/a4/7ba4bcc838818bcf018e26d118d5ddb605c23c4fad040dc4d811f1cfcb04/mmh3-4.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f02576a4d106d7830ca90278868bf0983554dd69183b7bbe09f2fcd51cf54f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/ed/8e80d1038e7bb15eaf739711d1fc36f2341acb6b1b95fa77003f2799c91e/mmh3-4.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:073d57425a23721730d3ff5485e2da489dd3c90b04e86243dd7211f889898106" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/22/a6a70ca81f0ce8fe2f3a68d89c1184c2d2d0fbe0ee305da50e972c5ff9fa/mmh3-4.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:71e32ddec7f573a1a0feb8d2cf2af474c50ec21e7a8263026e8d3b4b629805db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/20/abe50b605760f1f5b6e0b436c650649e69ca478d0f41b154f300367c09e4/mmh3-4.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7cbb20b29d57e76a58b40fd8b13a9130db495a12d678d651b459bf61c0714cea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/80/a1fc99d3ee50b573df0bfbb1ad518463af78d2ebca44bfca3b3f9473d651/mmh3-4.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a42ad267e131d7847076bb7e31050f6c4378cd38e8f1bf7a0edd32f30224d5c9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9e/51/6c9ee2ddf3b386f45ff83b6926a5e826635757d91dab04cbf16eee05f9a7/mmh3-4.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a013979fc9390abadc445ea2527426a0e7a4495c19b74589204f9b71bcaafeb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ee/fa/4b377f244c27fac5f0343cc4dc0d2eb0a08049afc8d5322d07be7461a768/mmh3-4.1.0-cp311-cp311-win32.whl", hash = "sha256:1d3b1cdad7c71b7b88966301789a478af142bddcb3a2bee563f7a7d40519a00f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/b0/500ef56c29b276d796bfdb47c16d34fa18a68945e4d730a6fa7d483583ed/mmh3-4.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0dc6dc32eb03727467da8e17deffe004fbb65e8b5ee2b502d36250d7a3f4e2ec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/84/94795e6e710c3861f8f355a12be9c9f4b8433a538c983e75bd4c00496a8a/mmh3-4.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9ae3a5c1b32dda121c7dc26f9597ef7b01b4c56a98319a7fe86c35b8bc459ae6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/18/45/b4d41e86b00eed8c500adbe0007129861710e181c7f49c507ef6beae9496/mmh3-4.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0033d60c7939168ef65ddc396611077a7268bde024f2c23bdc283a19123f9e9c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/d4/f041b8704cb8d1aad3717105daa582e29818b78a540622dfed84cd00d88f/mmh3-4.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d6af3e2287644b2b08b5924ed3a88c97b87b44ad08e79ca9f93d3470a54a41c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cb/bb/8f75378e1a83b323f9ed06248333c383e7dac614c2f95e1419965cb91693/mmh3-4.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d82eb4defa245e02bb0b0dc4f1e7ee284f8d212633389c91f7fba99ba993f0a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3e/50/5e36c1945bd83e780a37361fc1999fc4c5a59ecc10a373557fdf0e58eb1f/mmh3-4.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba245e94b8d54765e14c2d7b6214e832557e7856d5183bc522e17884cab2f45d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/c7/6ae37e7519a938226469476b84bcea2650e2a2cc7a848e6a206ea98ecee3/mmh3-4.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb04e2feeabaad6231e89cd43b3d01a4403579aa792c9ab6fdeef45cc58d4ec0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/47/6613f69f57f1e5045e66b22fae9c2fb39ef754c455805d3917f6073e316e/mmh3-4.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e3b1a27def545ce11e36158ba5d5390cdbc300cfe456a942cc89d649cf7e3b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/0a/e423db18ce7b479c4b96381a112b443f0985c611de420f95c58a9f934080/mmh3-4.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce0ab79ff736d7044e5e9b3bfe73958a55f79a4ae672e6213e92492ad5e734d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4c/7b/bfeb68bee5bddc8baf7ef630b93edc0a533202d84eb076dbb6c77e7e5fd5/mmh3-4.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b02268be6e0a8eeb8a924d7db85f28e47344f35c438c1e149878bb1c47b1cd3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/a6/b82e30143997c05776887f5177f724e3b714aa7e7346fbe2ec70f52abcd0/mmh3-4.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:deb887f5fcdaf57cf646b1e062d56b06ef2f23421c80885fce18b37143cba828" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/60/a3d5872cf7610fcb13e36c472476020c5cf217b23c092bad452eb7784407/mmh3-4.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99dd564e9e2b512eb117bd0cbf0f79a50c45d961c2a02402787d581cec5448d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/d5/742173a94c78f4edab71c04097f6f9150c47f8fd034d592f5f34a9444719/mmh3-4.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:08373082dfaa38fe97aa78753d1efd21a1969e51079056ff552e687764eafdfe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/7a/a1db0efe7c67b761d83be3d50e35ef26628ef56b3b8bc776d07412ee8b16/mmh3-4.1.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:54b9c6a2ea571b714e4fe28d3e4e2db37abfd03c787a58074ea21ee9a8fd1740" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/78/1ff8da7c859cd09704e2f500588d171eda9688fcf6f29e028ef261262a16/mmh3-4.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a7b1edf24c69e3513f879722b97ca85e52f9032f24a52284746877f6a7304086" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/c7/cf16ace81fc9fbe54a75c914306252af26c6ea485366bb3b579bf6e3dbb8/mmh3-4.1.0-cp312-cp312-win32.whl", hash = "sha256:411da64b951f635e1e2284b71d81a5a83580cea24994b328f8910d40bed67276" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/0b/b3b1637dca9414451edf287fd91e667e7231d5ffd7498137fe011951fc0a/mmh3-4.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bebc3ecb6ba18292e3d40c8712482b4477abd6981c2ebf0e60869bd90f8ac3a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/6c/c0f06040c58112ccbd0df989055ede98f7c1a1f392dc6a3fc63ec6c124ec/mmh3-4.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:168473dd608ade6a8d2ba069600b35199a9af837d96177d3088ca91f2b3798e3" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/96/aa247e82878b123468f0079ce2ac77e948315bab91ce45d2934a62e0af95/mmh3-4.1.0.tar.gz", hash = "sha256:a1cf25348b9acd229dda464a094d6170f47d2850a1fcb762a3b6172d2ce6ca4a", size = 26357, upload-time = "2024-01-09T06:46:04.536Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/5a/8609dc74421858f7e94a89dc69221ab9b2c14d0d63a139b46ec190eedc44/mmh3-4.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be5ac76a8b0cd8095784e51e4c1c9c318c19edcd1709a06eb14979c8d850c31a", size = 39433, upload-time = "2024-01-09T06:44:25.903Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/6c/e7a0f07c7082c76964b1ff46aa852f36e2ec6a9c3530dec0afa0b3162fc2/mmh3-4.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98a49121afdfab67cd80e912b36404139d7deceb6773a83620137aaa0da5714c", size = 29280, upload-time = "2024-01-09T06:44:27.035Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/84/60ca728ec7d7e1779a98000d64941c6221786124b4f07bf105a627055890/mmh3-4.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5259ac0535874366e7d1a5423ef746e0d36a9e3c14509ce6511614bdc5a7ef5b", size = 30130, upload-time = "2024-01-09T06:44:28.502Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/22/f2ec190b491f712d9ef5ea6252204b6f05255ac9af54a7b505adc3128aed/mmh3-4.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5950827ca0453a2be357696da509ab39646044e3fa15cad364eb65d78797437", size = 68837, upload-time = "2024-01-09T06:44:29.959Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/b9/c1e8065671e1d2f4e280c9c57389e74964f4a5792cac26717ad592002c7d/mmh3-4.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1dd0f652ae99585b9dd26de458e5f08571522f0402155809fd1dc8852a613a39", size = 72275, upload-time = "2024-01-09T06:44:31.02Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/18/92bbdb102ab2b4e80084e927187d871758280eb067c649693e42bfc6d0d1/mmh3-4.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d25548070942fab1e4a6f04d1626d67e66d0b81ed6571ecfca511f3edf07e6", size = 70919, upload-time = "2024-01-09T06:44:32.581Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/cd/391ce1d1bb559871a5d3a6bbb30b82bf51d3e3b42c4e8589cccb201953da/mmh3-4.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53db8d9bad3cb66c8f35cbc894f336273f63489ce4ac416634932e3cbe79eb5b", size = 65885, upload-time = "2024-01-09T06:44:34.462Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/87/4b01a43336bd506478850d1bc3d180648b2d26b4acf1fc4bf1df72bf562f/mmh3-4.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75da0f615eb55295a437264cc0b736753f830b09d102aa4c2a7d719bc445ec05", size = 67610, upload-time = "2024-01-09T06:44:35.589Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/12/b464149a1b7181c7ce431ebf3d24fa994863f2f1abc75b78d202dde966e0/mmh3-4.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b926b07fd678ea84b3a2afc1fa22ce50aeb627839c44382f3d0291e945621e1a", size = 74888, upload-time = "2024-01-09T06:44:36.532Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/3e/f4eb45a23fc17b970394c1fe74eba157514577ae2d63757684241651d754/mmh3-4.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c5b053334f9b0af8559d6da9dc72cef0a65b325ebb3e630c680012323c950bb6", size = 72969, upload-time = "2024-01-09T06:44:37.473Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/3b/83934fd9494371357da0ca026d55ad427c199d611b97b6ffeecacfd8e720/mmh3-4.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5bf33dc43cd6de2cb86e0aa73a1cc6530f557854bbbe5d59f41ef6de2e353d7b", size = 80338, upload-time = "2024-01-09T06:44:38.523Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/c4/5bcd709ea7269173d7e925402f05e05cf12194ef53cc9912a5ad166f8ded/mmh3-4.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fa7eacd2b830727ba3dd65a365bed8a5c992ecd0c8348cf39a05cc77d22f4970", size = 76580, upload-time = "2024-01-09T06:44:39.505Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/6a/4c0680d64475e551d7f4cc78bf0fd247c711ed2717f6bb311934993d1e69/mmh3-4.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42dfd6742b9e3eec599f85270617debfa0bbb913c545bb980c8a4fa7b2d047da", size = 75325, upload-time = "2024-01-09T06:44:40.532Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/bc/e2ed99e580b3dd121f6462147bd5f521c57b3c81c692aa2d416b0678c89f/mmh3-4.1.0-cp310-cp310-win32.whl", hash = "sha256:2974ad343f0d39dcc88e93ee6afa96cedc35a9883bc067febd7ff736e207fa47", size = 31235, upload-time = "2024-01-09T06:44:41.467Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/2b/3aec865da7feb52830782d9fb7c54115cc18815680c244301adf9080622f/mmh3-4.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:74699a8984ded645c1a24d6078351a056f5a5f1fe5838870412a68ac5e28d865", size = 31271, upload-time = "2024-01-09T06:44:42.881Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/2a/925439189ccf562bdcb839aed6263d718359f0c376d673beb3b83d3864ac/mmh3-4.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:f0dc874cedc23d46fc488a987faa6ad08ffa79e44fb08e3cd4d4cf2877c00a00", size = 30147, upload-time = "2024-01-09T06:44:44.173Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/d6/86beea107e7e9700df9522466346c23a2f54faa81337c86fd17002aa95a6/mmh3-4.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3280a463855b0eae64b681cd5b9ddd9464b73f81151e87bb7c91a811d25619e6", size = 39427, upload-time = "2024-01-09T06:44:45.686Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/08/65fa5489044e2afc304e8540c6c607d5d7b136ddc5cd8315c13de0adc34c/mmh3-4.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:97ac57c6c3301769e757d444fa7c973ceb002cb66534b39cbab5e38de61cd896", size = 29281, upload-time = "2024-01-09T06:44:46.554Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/aa/98511d3ea3f6ba958136d913be3be3c1009be935a20ecc7b2763f0a605b6/mmh3-4.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b6502cdb4dbd880244818ab363c8770a48cdccecf6d729ade0241b736b5ec0", size = 30130, upload-time = "2024-01-09T06:44:47.463Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/b7/1a93f81643435b0e57f1046c4ffe46f0214693eaede0d9b0a1a236776e70/mmh3-4.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52ba2da04671a9621580ddabf72f06f0e72c1c9c3b7b608849b58b11080d8f14", size = 69072, upload-time = "2024-01-09T06:44:48.385Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/9e/2ff70246aefd9cf146bc6a420c28ed475a0d1a325f31ee203be02f9215d4/mmh3-4.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a5fef4c4ecc782e6e43fbeab09cff1bac82c998a1773d3a5ee6a3605cde343e", size = 72470, upload-time = "2024-01-09T06:44:49.291Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/cb/57bc1fdbdbe6837aebfca982494e23e2498ee2a89585c9054713b22e4167/mmh3-4.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5135358a7e00991f73b88cdc8eda5203bf9de22120d10a834c5761dbeb07dd13", size = 71251, upload-time = "2024-01-09T06:44:50.839Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/c2/46d7d2721b69fbdfd30231309e6395f62ff6744e5c00dd8113b9faa06fba/mmh3-4.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cff9ae76a54f7c6fe0167c9c4028c12c1f6de52d68a31d11b6790bb2ae685560", size = 66035, upload-time = "2024-01-09T06:44:52.407Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/a4/7ba4bcc838818bcf018e26d118d5ddb605c23c4fad040dc4d811f1cfcb04/mmh3-4.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f02576a4d106d7830ca90278868bf0983554dd69183b7bbe09f2fcd51cf54f", size = 67844, upload-time = "2024-01-09T06:44:53.566Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/ed/8e80d1038e7bb15eaf739711d1fc36f2341acb6b1b95fa77003f2799c91e/mmh3-4.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:073d57425a23721730d3ff5485e2da489dd3c90b04e86243dd7211f889898106", size = 76724, upload-time = "2024-01-09T06:44:54.51Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/22/a6a70ca81f0ce8fe2f3a68d89c1184c2d2d0fbe0ee305da50e972c5ff9fa/mmh3-4.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:71e32ddec7f573a1a0feb8d2cf2af474c50ec21e7a8263026e8d3b4b629805db", size = 75004, upload-time = "2024-01-09T06:44:55.517Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/20/abe50b605760f1f5b6e0b436c650649e69ca478d0f41b154f300367c09e4/mmh3-4.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7cbb20b29d57e76a58b40fd8b13a9130db495a12d678d651b459bf61c0714cea", size = 82230, upload-time = "2024-01-09T06:44:56.538Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/80/a1fc99d3ee50b573df0bfbb1ad518463af78d2ebca44bfca3b3f9473d651/mmh3-4.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a42ad267e131d7847076bb7e31050f6c4378cd38e8f1bf7a0edd32f30224d5c9", size = 78679, upload-time = "2024-01-09T06:44:57.477Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/51/6c9ee2ddf3b386f45ff83b6926a5e826635757d91dab04cbf16eee05f9a7/mmh3-4.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a013979fc9390abadc445ea2527426a0e7a4495c19b74589204f9b71bcaafeb", size = 77382, upload-time = "2024-01-09T06:44:59.02Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/fa/4b377f244c27fac5f0343cc4dc0d2eb0a08049afc8d5322d07be7461a768/mmh3-4.1.0-cp311-cp311-win32.whl", hash = "sha256:1d3b1cdad7c71b7b88966301789a478af142bddcb3a2bee563f7a7d40519a00f", size = 31232, upload-time = "2024-01-09T06:45:01.285Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/b0/500ef56c29b276d796bfdb47c16d34fa18a68945e4d730a6fa7d483583ed/mmh3-4.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0dc6dc32eb03727467da8e17deffe004fbb65e8b5ee2b502d36250d7a3f4e2ec", size = 31276, upload-time = "2024-01-09T06:45:03.417Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/84/94795e6e710c3861f8f355a12be9c9f4b8433a538c983e75bd4c00496a8a/mmh3-4.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9ae3a5c1b32dda121c7dc26f9597ef7b01b4c56a98319a7fe86c35b8bc459ae6", size = 30142, upload-time = "2024-01-09T06:45:05.347Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/45/b4d41e86b00eed8c500adbe0007129861710e181c7f49c507ef6beae9496/mmh3-4.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0033d60c7939168ef65ddc396611077a7268bde024f2c23bdc283a19123f9e9c", size = 39495, upload-time = "2024-01-09T06:45:07.01Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/d4/f041b8704cb8d1aad3717105daa582e29818b78a540622dfed84cd00d88f/mmh3-4.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d6af3e2287644b2b08b5924ed3a88c97b87b44ad08e79ca9f93d3470a54a41c5", size = 29334, upload-time = "2024-01-09T06:45:08.022Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/bb/8f75378e1a83b323f9ed06248333c383e7dac614c2f95e1419965cb91693/mmh3-4.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d82eb4defa245e02bb0b0dc4f1e7ee284f8d212633389c91f7fba99ba993f0a2", size = 30144, upload-time = "2024-01-09T06:45:09.437Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/50/5e36c1945bd83e780a37361fc1999fc4c5a59ecc10a373557fdf0e58eb1f/mmh3-4.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba245e94b8d54765e14c2d7b6214e832557e7856d5183bc522e17884cab2f45d", size = 69094, upload-time = "2024-01-09T06:45:10.531Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/c7/6ae37e7519a938226469476b84bcea2650e2a2cc7a848e6a206ea98ecee3/mmh3-4.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb04e2feeabaad6231e89cd43b3d01a4403579aa792c9ab6fdeef45cc58d4ec0", size = 72611, upload-time = "2024-01-09T06:45:12.27Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/47/6613f69f57f1e5045e66b22fae9c2fb39ef754c455805d3917f6073e316e/mmh3-4.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e3b1a27def545ce11e36158ba5d5390cdbc300cfe456a942cc89d649cf7e3b2", size = 71462, upload-time = "2024-01-09T06:45:13.274Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/0a/e423db18ce7b479c4b96381a112b443f0985c611de420f95c58a9f934080/mmh3-4.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce0ab79ff736d7044e5e9b3bfe73958a55f79a4ae672e6213e92492ad5e734d5", size = 66165, upload-time = "2024-01-09T06:45:15.003Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/7b/bfeb68bee5bddc8baf7ef630b93edc0a533202d84eb076dbb6c77e7e5fd5/mmh3-4.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b02268be6e0a8eeb8a924d7db85f28e47344f35c438c1e149878bb1c47b1cd3", size = 68088, upload-time = "2024-01-09T06:45:16.192Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/a6/b82e30143997c05776887f5177f724e3b714aa7e7346fbe2ec70f52abcd0/mmh3-4.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:deb887f5fcdaf57cf646b1e062d56b06ef2f23421c80885fce18b37143cba828", size = 76241, upload-time = "2024-01-09T06:45:17.191Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/60/a3d5872cf7610fcb13e36c472476020c5cf217b23c092bad452eb7784407/mmh3-4.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99dd564e9e2b512eb117bd0cbf0f79a50c45d961c2a02402787d581cec5448d5", size = 74538, upload-time = "2024-01-09T06:45:18.999Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/d5/742173a94c78f4edab71c04097f6f9150c47f8fd034d592f5f34a9444719/mmh3-4.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:08373082dfaa38fe97aa78753d1efd21a1969e51079056ff552e687764eafdfe", size = 81793, upload-time = "2024-01-09T06:45:20.534Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/7a/a1db0efe7c67b761d83be3d50e35ef26628ef56b3b8bc776d07412ee8b16/mmh3-4.1.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:54b9c6a2ea571b714e4fe28d3e4e2db37abfd03c787a58074ea21ee9a8fd1740", size = 78217, upload-time = "2024-01-09T06:45:21.761Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/78/1ff8da7c859cd09704e2f500588d171eda9688fcf6f29e028ef261262a16/mmh3-4.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a7b1edf24c69e3513f879722b97ca85e52f9032f24a52284746877f6a7304086", size = 77052, upload-time = "2024-01-09T06:45:22.824Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/c7/cf16ace81fc9fbe54a75c914306252af26c6ea485366bb3b579bf6e3dbb8/mmh3-4.1.0-cp312-cp312-win32.whl", hash = "sha256:411da64b951f635e1e2284b71d81a5a83580cea24994b328f8910d40bed67276", size = 31277, upload-time = "2024-01-09T06:45:24.009Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/0b/b3b1637dca9414451edf287fd91e667e7231d5ffd7498137fe011951fc0a/mmh3-4.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bebc3ecb6ba18292e3d40c8712482b4477abd6981c2ebf0e60869bd90f8ac3a9", size = 31318, upload-time = "2024-01-09T06:45:25.169Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/6c/c0f06040c58112ccbd0df989055ede98f7c1a1f392dc6a3fc63ec6c124ec/mmh3-4.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:168473dd608ade6a8d2ba069600b35199a9af837d96177d3088ca91f2b3798e3", size = 30147, upload-time = "2024-01-09T06:45:26.214Z" }, ] [[package]] name = "mpmath" version = "1.3.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] [[package]] name = "msal" version = "1.33.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cryptography" }, { name = "pyjwt", extra = ["crypto"] }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d5/da/81acbe0c1fd7e9e4ec35f55dadeba9833a847b9a6ba2e2d1e4432da901dd/msal-1.33.0.tar.gz", hash = "sha256:836ad80faa3e25a7d71015c990ce61f704a87328b1e73bcbb0623a18cbf17510" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/da/81acbe0c1fd7e9e4ec35f55dadeba9833a847b9a6ba2e2d1e4432da901dd/msal-1.33.0.tar.gz", hash = "sha256:836ad80faa3e25a7d71015c990ce61f704a87328b1e73bcbb0623a18cbf17510", size = 153801, upload-time = "2025-07-22T19:36:33.693Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/86/5b/fbc73e91f7727ae1e79b21ed833308e99dc11cc1cd3d4717f579775de5e9/msal-1.33.0-py3-none-any.whl", hash = "sha256:c0cd41cecf8eaed733ee7e3be9e040291eba53b0f262d3ae9c58f38b04244273" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/5b/fbc73e91f7727ae1e79b21ed833308e99dc11cc1cd3d4717f579775de5e9/msal-1.33.0-py3-none-any.whl", hash = "sha256:c0cd41cecf8eaed733ee7e3be9e040291eba53b0f262d3ae9c58f38b04244273", size = 116853, upload-time = "2025-07-22T19:36:32.403Z" }, ] [[package]] name = "msal-extensions" version = "1.3.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "msal" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, ] [[package]] name = "msgspec" version = "0.19.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cf/9b/95d8ce458462b8b71b8a70fa94563b2498b89933689f3a7b8911edfae3d7/msgspec-0.19.0.tar.gz", hash = "sha256:604037e7cd475345848116e89c553aa9a233259733ab51986ac924ab1b976f8e" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/13/40/817282b42f58399762267b30deb8ac011d8db373f8da0c212c85fbe62b8f/msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/99/bd7ed738c00f223a8119928661167a89124140792af18af513e6519b0d54/msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/27/322badde18eb234e36d4a14122b89edd4e2973cdbc3da61ca7edf40a1ccd/msgspec-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe2c4bf29bf4e89790b3117470dea2c20b59932772483082c468b990d45fb947" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/65/080509c5774a1592b2779d902a70b5fe008532759927e011f068145a16cb/msgspec-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e87ecfa9795ee5214861eab8326b0e75475c2e68a384002aa135ea2a27d909" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/2e/1c23c6b4ca6f4285c30a39def1054e2bee281389e4b681b5e3711bd5a8c9/msgspec-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3c4ec642689da44618f68c90855a10edbc6ac3ff7c1d94395446c65a776e712a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/83/fe/95f9654518879f3359d1e76bc41189113aa9102452170ab7c9a9a4ee52f6/msgspec-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2719647625320b60e2d8af06b35f5b12d4f4d281db30a15a1df22adb2295f633" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/f6/71ca7e87a1fb34dfe5efea8156c9ef59dd55613aeda2ca562f122cd22012/msgspec-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:695b832d0091edd86eeb535cd39e45f3919f48d997685f7ac31acb15e0a2ed90" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/d4/2ec2567ac30dab072cce3e91fb17803c52f0a37aab6b0c24375d2b20a581/msgspec-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa77046904db764b0462036bc63ef71f02b75b8f72e9c9dd4c447d6da1ed8f8e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/c0/18226e4328897f4f19875cb62bb9259fe47e901eade9d9376ab5f251a929/msgspec-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:047cfa8675eb3bad68722cfe95c60e7afabf84d1bd8938979dd2b92e9e4a9551" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/25/3a4b24d468203d8af90d1d351b77ea3cffb96b29492855cf83078f16bfe4/msgspec-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e78f46ff39a427e10b4a61614a2777ad69559cc8d603a7c05681f5a595ea98f7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/2e/db7e189b57901955239f7689b5dcd6ae9458637a9c66747326726c650523/msgspec-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c7adf191e4bd3be0e9231c3b6dc20cf1199ada2af523885efc2ed218eafd011" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/97/7c8895c9074a97052d7e4a1cc1230b7b6e2ca2486714eb12c3f08bb9d284/msgspec-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f04cad4385e20be7c7176bb8ae3dca54a08e9756cfc97bcdb4f18560c3042063" }, - { url = "https://mirrors.aliyun.com/pypi/packages/61/61/e892997bcaa289559b4d5869f066a8021b79f4bf8e955f831b095f47a4cd/msgspec-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45c8fb410670b3b7eb884d44a75589377c341ec1392b778311acdbfa55187716" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/3d/71b2dffd3a1c743ffe13296ff701ee503feaebc3f04d0e75613b6563c374/msgspec-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:70eaef4934b87193a27d802534dc466778ad8d536e296ae2f9334e182ac27b6c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/5f/a70c24f075e3e7af2fae5414c7048b0e11389685b7f717bb55ba282a34a7/msgspec-0.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f98bd8962ad549c27d63845b50af3f53ec468b6318400c9f1adfe8b092d7b62f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/89/b0/1b9763938cfae12acf14b682fcf05c92855974d921a5a985ecc197d1c672/msgspec-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43bbb237feab761b815ed9df43b266114203f53596f9b6e6f00ebd79d178cdf2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/87/81/0c8c93f0b92c97e326b279795f9c5b956c5a97af28ca0fbb9fd86c83737a/msgspec-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfc033c02c3e0aec52b71710d7f84cb3ca5eb407ab2ad23d75631153fdb1f12" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d0/ef/c5422ce8af73928d194a6606f8ae36e93a52fd5e8df5abd366903a5ca8da/msgspec-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d911c442571605e17658ca2b416fd8579c5050ac9adc5e00c2cb3126c97f73bc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/2b/4137bc2ed45660444842d042be2cf5b18aa06efd2cda107cff18253b9653/msgspec-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:757b501fa57e24896cf40a831442b19a864f56d253679f34f260dcb002524a6c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9d/e6/8ad51bdc806aac1dc501e8fe43f759f9ed7284043d722b53323ea421c360/msgspec-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f0f65f29b45e2816d8bded36e6b837a4bf5fb60ec4bc3c625fa2c6da4124537" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/ef/27dd35a7049c9a4f4211c6cd6a8c9db0a50647546f003a5867827ec45391/msgspec-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/9b/95d8ce458462b8b71b8a70fa94563b2498b89933689f3a7b8911edfae3d7/msgspec-0.19.0.tar.gz", hash = "sha256:604037e7cd475345848116e89c553aa9a233259733ab51986ac924ab1b976f8e", size = 216934, upload-time = "2024-12-27T17:40:28.597Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/40/817282b42f58399762267b30deb8ac011d8db373f8da0c212c85fbe62b8f/msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259", size = 190019, upload-time = "2024-12-27T17:39:13.803Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/99/bd7ed738c00f223a8119928661167a89124140792af18af513e6519b0d54/msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36", size = 183680, upload-time = "2024-12-27T17:39:17.847Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/27/322badde18eb234e36d4a14122b89edd4e2973cdbc3da61ca7edf40a1ccd/msgspec-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe2c4bf29bf4e89790b3117470dea2c20b59932772483082c468b990d45fb947", size = 209334, upload-time = "2024-12-27T17:39:19.065Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/65/080509c5774a1592b2779d902a70b5fe008532759927e011f068145a16cb/msgspec-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e87ecfa9795ee5214861eab8326b0e75475c2e68a384002aa135ea2a27d909", size = 211551, upload-time = "2024-12-27T17:39:21.767Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/2e/1c23c6b4ca6f4285c30a39def1054e2bee281389e4b681b5e3711bd5a8c9/msgspec-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3c4ec642689da44618f68c90855a10edbc6ac3ff7c1d94395446c65a776e712a", size = 215099, upload-time = "2024-12-27T17:39:24.71Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/fe/95f9654518879f3359d1e76bc41189113aa9102452170ab7c9a9a4ee52f6/msgspec-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2719647625320b60e2d8af06b35f5b12d4f4d281db30a15a1df22adb2295f633", size = 218211, upload-time = "2024-12-27T17:39:27.396Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/f6/71ca7e87a1fb34dfe5efea8156c9ef59dd55613aeda2ca562f122cd22012/msgspec-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:695b832d0091edd86eeb535cd39e45f3919f48d997685f7ac31acb15e0a2ed90", size = 186174, upload-time = "2024-12-27T17:39:29.647Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/d4/2ec2567ac30dab072cce3e91fb17803c52f0a37aab6b0c24375d2b20a581/msgspec-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa77046904db764b0462036bc63ef71f02b75b8f72e9c9dd4c447d6da1ed8f8e", size = 187939, upload-time = "2024-12-27T17:39:32.347Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/c0/18226e4328897f4f19875cb62bb9259fe47e901eade9d9376ab5f251a929/msgspec-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:047cfa8675eb3bad68722cfe95c60e7afabf84d1bd8938979dd2b92e9e4a9551", size = 182202, upload-time = "2024-12-27T17:39:33.633Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/25/3a4b24d468203d8af90d1d351b77ea3cffb96b29492855cf83078f16bfe4/msgspec-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e78f46ff39a427e10b4a61614a2777ad69559cc8d603a7c05681f5a595ea98f7", size = 209029, upload-time = "2024-12-27T17:39:35.023Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/2e/db7e189b57901955239f7689b5dcd6ae9458637a9c66747326726c650523/msgspec-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c7adf191e4bd3be0e9231c3b6dc20cf1199ada2af523885efc2ed218eafd011", size = 210682, upload-time = "2024-12-27T17:39:36.384Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/97/7c8895c9074a97052d7e4a1cc1230b7b6e2ca2486714eb12c3f08bb9d284/msgspec-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f04cad4385e20be7c7176bb8ae3dca54a08e9756cfc97bcdb4f18560c3042063", size = 214003, upload-time = "2024-12-27T17:39:39.097Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/61/e892997bcaa289559b4d5869f066a8021b79f4bf8e955f831b095f47a4cd/msgspec-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45c8fb410670b3b7eb884d44a75589377c341ec1392b778311acdbfa55187716", size = 216833, upload-time = "2024-12-27T17:39:41.203Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/3d/71b2dffd3a1c743ffe13296ff701ee503feaebc3f04d0e75613b6563c374/msgspec-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:70eaef4934b87193a27d802534dc466778ad8d536e296ae2f9334e182ac27b6c", size = 186184, upload-time = "2024-12-27T17:39:43.702Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/5f/a70c24f075e3e7af2fae5414c7048b0e11389685b7f717bb55ba282a34a7/msgspec-0.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f98bd8962ad549c27d63845b50af3f53ec468b6318400c9f1adfe8b092d7b62f", size = 190485, upload-time = "2024-12-27T17:39:44.974Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/b0/1b9763938cfae12acf14b682fcf05c92855974d921a5a985ecc197d1c672/msgspec-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43bbb237feab761b815ed9df43b266114203f53596f9b6e6f00ebd79d178cdf2", size = 183910, upload-time = "2024-12-27T17:39:46.401Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/81/0c8c93f0b92c97e326b279795f9c5b956c5a97af28ca0fbb9fd86c83737a/msgspec-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfc033c02c3e0aec52b71710d7f84cb3ca5eb407ab2ad23d75631153fdb1f12", size = 210633, upload-time = "2024-12-27T17:39:49.099Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/ef/c5422ce8af73928d194a6606f8ae36e93a52fd5e8df5abd366903a5ca8da/msgspec-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d911c442571605e17658ca2b416fd8579c5050ac9adc5e00c2cb3126c97f73bc", size = 213594, upload-time = "2024-12-27T17:39:51.204Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/2b/4137bc2ed45660444842d042be2cf5b18aa06efd2cda107cff18253b9653/msgspec-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:757b501fa57e24896cf40a831442b19a864f56d253679f34f260dcb002524a6c", size = 214053, upload-time = "2024-12-27T17:39:52.866Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/e6/8ad51bdc806aac1dc501e8fe43f759f9ed7284043d722b53323ea421c360/msgspec-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f0f65f29b45e2816d8bded36e6b837a4bf5fb60ec4bc3c625fa2c6da4124537", size = 219081, upload-time = "2024-12-27T17:39:55.142Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/ef/27dd35a7049c9a4f4211c6cd6a8c9db0a50647546f003a5867827ec45391/msgspec-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0", size = 187467, upload-time = "2024-12-27T17:39:56.531Z" }, ] [[package]] @@ -3512,132 +3512,132 @@ wheels = [ [[package]] name = "multidict" version = "6.6.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3d/2c/5dad12e82fbdf7470f29bff2171484bf07cb3b16ada60a6589af8f376440/multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0b/67/414933982bce2efce7cbcb3169eaaf901e0f25baec69432b4874dfb1f297/multidict-6.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/fe/d8a3ee1fad37dc2ef4f75488b0d9d4f25bf204aad8306cbab63d97bff64a/multidict-6.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/e0/265d89af8c98240265d82b8cbcf35897f83b76cd59ee3ab3879050fd8c45/multidict-6.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dd7793bab517e706c9ed9d7310b06c8672fd0aeee5781bfad612f56b8e0f7d14" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e6/05/6b759379f7e8e04ccc97cfb2a5dcc5cdbd44a97f072b2272dc51281e6a40/multidict-6.6.3-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:72d8815f2cd3cf3df0f83cac3f3ef801d908b2d90409ae28102e0553af85545a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4e/f5/8d5a15488edd9a91fa4aad97228d785df208ed6298580883aa3d9def1959/multidict-6.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:531e331a2ee53543ab32b16334e2deb26f4e6b9b28e41f8e0c87e99a6c8e2d69" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/b5/a8f317d47d0ac5bb746d6d8325885c8967c2a8ce0bb57be5399e3642cccb/multidict-6.6.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:42ca5aa9329a63be8dc49040f63817d1ac980e02eeddba763a9ae5b4027b9c9c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/88/18b2a0d5e80515fa22716556061189c2853ecf2aa2133081ebbe85ebea38/multidict-6.6.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:208b9b9757060b9faa6f11ab4bc52846e4f3c2fb8b14d5680c8aac80af3dc751" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/bf/ebfcfd6b55a1b05ef16d0775ae34c0fe15e8dab570d69ca9941073b969e7/multidict-6.6.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:acf6b97bd0884891af6a8b43d0f586ab2fcf8e717cbd47ab4bdddc09e20652d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/11/780615a98fd3775fc309d0234d563941af69ade2df0bb82c91dda6ddaea1/multidict-6.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:68e9e12ed00e2089725669bdc88602b0b6f8d23c0c95e52b95f0bc69f7fe9b55" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/3d/35f33045e21034b388686213752cabc3a1b9d03e20969e6fa8f1b1d82db1/multidict-6.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05db2f66c9addb10cfa226e1acb363450fab2ff8a6df73c622fefe2f5af6d4e7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/cc/ff84c03b95b430015d2166d9aae775a3985d757b94f6635010d0038d9241/multidict-6.6.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0db58da8eafb514db832a1b44f8fa7906fdd102f7d982025f816a93ba45e3dcb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/f0/8cd49a0b37bdea673a4b793c2093f2f4ba8e7c9d6d7c9bd672fd6d38cd11/multidict-6.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:14117a41c8fdb3ee19c743b1c027da0736fdb79584d61a766da53d399b71176c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/19/5d9a0cfdafe65d82b616a45ae950975820289069f885328e8185e64283c2/multidict-6.6.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:877443eaaabcd0b74ff32ebeed6f6176c71850feb7d6a1d2db65945256ea535c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e6/dc/c90066151da87d1e489f147b9b4327927241e65f1876702fafec6729c014/multidict-6.6.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:70b72e749a4f6e7ed8fb334fa8d8496384840319512746a5f42fa0aec79f4d61" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/39/458afb0cccbb0ee9164365273be3e039efddcfcb94ef35924b7dbdb05db0/multidict-6.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43571f785b86afd02b3855c5ac8e86ec921b760298d6f82ff2a61daf5a35330b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/35/38/0016adac3990426610a081787011177e661875546b434f50a26319dc8372/multidict-6.6.3-cp310-cp310-win32.whl", hash = "sha256:20c5a0c3c13a15fd5ea86c42311859f970070e4e24de5a550e99d7c271d76318" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/d2/17897a8f3f2c5363d969b4c635aa40375fe1f09168dc09a7826780bfb2a4/multidict-6.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab0a34a007704c625e25a9116c6770b4d3617a071c8a7c30cd338dfbadfe6485" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2d/5f/d4a717c1e457fe44072e33fa400d2b93eb0f2819c4d669381f925b7cba1f/multidict-6.6.3-cp310-cp310-win_arm64.whl", hash = "sha256:769841d70ca8bdd140a715746199fc6473414bd02efd678d75681d2d6a8986c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/f0/1a39863ced51f639c81a5463fbfa9eb4df59c20d1a8769ab9ef4ca57ae04/multidict-6.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18f4eba0cbac3546b8ae31e0bbc55b02c801ae3cbaf80c247fcdd89b456ff58c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/0e/a7cfa451c7b0365cd844e90b41e21fab32edaa1e42fc0c9f68461ce44ed7/multidict-6.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef43b5dd842382329e4797c46f10748d8c2b6e0614f46b4afe4aee9ac33159df" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/bb/a14a4efc5ee748cc1904b0748be278c31b9295ce5f4d2ef66526f410b94d/multidict-6.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bd1fd5eec01494e0f2e8e446a74a85d5e49afb63d75a9934e4a5423dba21d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/f8/410677d563c2d55e063ef74fe578f9d53fe6b0a51649597a5861f83ffa15/multidict-6.6.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5bd8d6f793a787153956cd35e24f60485bf0651c238e207b9a54f7458b16d539" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fd/df/2b787f80059314a98e1ec6a4cc7576244986df3e56b3c755e6fc7c99e038/multidict-6.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf99b4daf908c73856bd87ee0a2499c3c9a3d19bb04b9c6025e66af3fd07462" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/f2/f9117089151b9a8ab39f9019620d10d9718eec2ac89e7ca9d30f3ec78e96/multidict-6.6.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b9e59946b49dafaf990fd9c17ceafa62976e8471a14952163d10a7a630413a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/2d/7115300ec5b699faa152c56799b089a53ed69e399c3c2d528251f0aeda1a/multidict-6.6.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e2db616467070d0533832d204c54eea6836a5e628f2cb1e6dfd8cd6ba7277cb7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/15/ea/ff4bab367623e39c20d3b07637225c7688d79e4f3cc1f3b9f89867677f9a/multidict-6.6.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7394888236621f61dcdd25189b2768ae5cc280f041029a5bcf1122ac63df79f9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/07/2c9246cda322dfe08be85f1b8739646f2c4c5113a1422d7a407763422ec4/multidict-6.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f114d8478733ca7388e7c7e0ab34b72547476b97009d643644ac33d4d3fe1821" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/62/279c13d584207d5697a752a66ffc9bb19355a95f7659140cb1b3cf82180e/multidict-6.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdf22e4db76d323bcdc733514bf732e9fb349707c98d341d40ebcc6e9318ef3d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/cc/e06636f48c6d51e724a8bc8d9e1db5f136fe1df066d7cafe37ef4000f86a/multidict-6.6.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e995a34c3d44ab511bfc11aa26869b9d66c2d8c799fa0e74b28a473a692532d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/89/a4/66c9d8fb9acf3b226cdd468ed009537ac65b520aebdc1703dd6908b19d33/multidict-6.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:766a4a5996f54361d8d5a9050140aa5362fe48ce51c755a50c0bc3706460c430" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cf/01/c69e0317be556e46257826d5449feb4e6aa0d18573e567a48a2c14156f1f/multidict-6.6.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3893a0d7d28a7fe6ca7a1f760593bc13038d1d35daf52199d431b61d2660602b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/da/9cc1da0299762d20e626fe0042e71b5694f9f72d7d3f9678397cbaa71b2b/multidict-6.6.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:934796c81ea996e61914ba58064920d6cad5d99140ac3167901eb932150e2e56" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e6/91/b22756afec99cc31105ddd4a52f95ab32b1a4a58f4d417979c570c4a922e/multidict-6.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ed948328aec2072bc00f05d961ceadfd3e9bfc2966c1319aeaf7b7c21219183" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/f1/adcc185b878036a20399d5be5228f3cbe7f823d78985d101d425af35c800/multidict-6.6.3-cp311-cp311-win32.whl", hash = "sha256:9f5b28c074c76afc3e4c610c488e3493976fe0e596dd3db6c8ddfbb0134dcac5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/d4/27652c1c6526ea6b4f5ddd397e93f4232ff5de42bea71d339bc6a6cc497f/multidict-6.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc7f6fbc61b1c16050a389c630da0b32fc6d4a3d191394ab78972bf5edc568c2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/18/23f4932019804e56d3c2413e237f866444b774b0263bcb81df2fdecaf593/multidict-6.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:d4e47d8faffaae822fb5cba20937c048d4f734f43572e7079298a6c39fb172cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/a0/6b57988ea102da0623ea814160ed78d45a2645e4bbb499c2896d12833a70/multidict-6.6.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:056bebbeda16b2e38642d75e9e5310c484b7c24e3841dc0fb943206a72ec89d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/7a/d1e92665b0850c6c0508f101f9cf0410c1afa24973e1115fe9c6a185ebf7/multidict-6.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e5f481cccb3c5c5e5de5d00b5141dc589c1047e60d07e85bbd7dea3d4580d63f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/6f/dd104490e01be6ef8bf9573705d8572f8c2d2c561f06e3826b081d9e6591/multidict-6.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10bea2ee839a759ee368b5a6e47787f399b41e70cf0c20d90dfaf4158dfb4e55" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/fe/06e0e01b1b0611e6581b7fd5a85b43dacc08b6cea3034f902f383b0873e5/multidict-6.6.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2334cfb0fa9549d6ce2c21af2bfbcd3ac4ec3646b1b1581c88e3e2b1779ec92b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/71/4f0e558fb77696b89c233c1ee2d92f3e1d5459070a0e89153c9e9e804186/multidict-6.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8fee016722550a2276ca2cb5bb624480e0ed2bd49125b2b73b7010b9090e888" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/25/cca0e68228addad24903801ed1ab42e21307a1b4b6dd2cf63da5d3ae082a/multidict-6.6.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5511cb35f5c50a2db21047c875eb42f308c5583edf96bd8ebf7d770a9d68f6d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/a3/46f2d420d86bbcb8fe660b26a10a219871a0fbf4d43cb846a4031533f3e0/multidict-6.6.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:712b348f7f449948e0a6c4564a21c7db965af900973a67db432d724619b3c680" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9e/73/1c743542fe00794a2ec7466abd3f312ccb8fad8dff9f36d42e18fb1ec33e/multidict-6.6.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e15d2138ee2694e038e33b7c3da70e6b0ad8868b9f8094a72e1414aeda9c1a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a4/11/6ec9dcbe2264b92778eeb85407d1df18812248bf3506a5a1754bc035db0c/multidict-6.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8df25594989aebff8a130f7899fa03cbfcc5d2b5f4a461cf2518236fe6f15961" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/2b/631b1e2afeb5f1696846d747d36cda075bfdc0bc7245d6ba5c319278d6c4/multidict-6.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:159ca68bfd284a8860f8d8112cf0521113bffd9c17568579e4d13d1f1dc76b65" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/0e/7e3b93f79efeb6111d3bf9a1a69e555ba1d07ad1c11bceb56b7310d0d7ee/multidict-6.6.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e098c17856a8c9ade81b4810888c5ad1914099657226283cab3062c0540b0643" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/9e/086846c1d6601948e7de556ee464a2d4c85e33883e749f46b9547d7b0704/multidict-6.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:67c92ed673049dec52d7ed39f8cf9ebbadf5032c774058b4406d18c8f8fe7063" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8c/7b/86ec260118e522f1a31550e87b23542294880c97cfbf6fb18cc67b044c66/multidict-6.6.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:bd0578596e3a835ef451784053cfd327d607fc39ea1a14812139339a18a0dbc3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8c/bd/22ce8f47abb0be04692c9fc4638508b8340987b18691aa7775d927b73f72/multidict-6.6.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:346055630a2df2115cd23ae271910b4cae40f4e336773550dca4889b12916e75" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/9c/91b7ac1691be95cd1f4a26e36a74b97cda6aa9820632d31aab4410f46ebd/multidict-6.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:555ff55a359302b79de97e0468e9ee80637b0de1fce77721639f7cd9440b3a10" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/5c/4d7adc739884f7a9fbe00d1eac8c034023ef8bad71f2ebe12823ca2e3649/multidict-6.6.3-cp312-cp312-win32.whl", hash = "sha256:73ab034fb8d58ff85c2bcbadc470efc3fafeea8affcf8722855fb94557f14cc5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/a3/0fbc7afdf7cb1aa12a086b02959307848eb6bcc8f66fcb66c0cb57e2a2c1/multidict-6.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:04cbcce84f63b9af41bad04a54d4cc4e60e90c35b9e6ccb130be2d75b71f8c17" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b8/95/8c825bd70ff9b02462dc18d1295dd08d3e9e4eb66856d292ffa62cfe1920/multidict-6.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:0f1130b896ecb52d2a1e615260f3ea2af55fa7dc3d7c3003ba0c3121a759b18b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/2c/5dad12e82fbdf7470f29bff2171484bf07cb3b16ada60a6589af8f376440/multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc", size = 101006, upload-time = "2025-06-30T15:53:46.929Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/67/414933982bce2efce7cbcb3169eaaf901e0f25baec69432b4874dfb1f297/multidict-6.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817", size = 77017, upload-time = "2025-06-30T15:50:58.931Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/fe/d8a3ee1fad37dc2ef4f75488b0d9d4f25bf204aad8306cbab63d97bff64a/multidict-6.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140", size = 44897, upload-time = "2025-06-30T15:51:00.999Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/e0/265d89af8c98240265d82b8cbcf35897f83b76cd59ee3ab3879050fd8c45/multidict-6.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dd7793bab517e706c9ed9d7310b06c8672fd0aeee5781bfad612f56b8e0f7d14", size = 44574, upload-time = "2025-06-30T15:51:02.449Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/05/6b759379f7e8e04ccc97cfb2a5dcc5cdbd44a97f072b2272dc51281e6a40/multidict-6.6.3-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:72d8815f2cd3cf3df0f83cac3f3ef801d908b2d90409ae28102e0553af85545a", size = 225729, upload-time = "2025-06-30T15:51:03.794Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/f5/8d5a15488edd9a91fa4aad97228d785df208ed6298580883aa3d9def1959/multidict-6.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:531e331a2ee53543ab32b16334e2deb26f4e6b9b28e41f8e0c87e99a6c8e2d69", size = 242515, upload-time = "2025-06-30T15:51:05.002Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/b5/a8f317d47d0ac5bb746d6d8325885c8967c2a8ce0bb57be5399e3642cccb/multidict-6.6.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:42ca5aa9329a63be8dc49040f63817d1ac980e02eeddba763a9ae5b4027b9c9c", size = 222224, upload-time = "2025-06-30T15:51:06.148Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/88/18b2a0d5e80515fa22716556061189c2853ecf2aa2133081ebbe85ebea38/multidict-6.6.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:208b9b9757060b9faa6f11ab4bc52846e4f3c2fb8b14d5680c8aac80af3dc751", size = 253124, upload-time = "2025-06-30T15:51:07.375Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/bf/ebfcfd6b55a1b05ef16d0775ae34c0fe15e8dab570d69ca9941073b969e7/multidict-6.6.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:acf6b97bd0884891af6a8b43d0f586ab2fcf8e717cbd47ab4bdddc09e20652d8", size = 251529, upload-time = "2025-06-30T15:51:08.691Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/11/780615a98fd3775fc309d0234d563941af69ade2df0bb82c91dda6ddaea1/multidict-6.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:68e9e12ed00e2089725669bdc88602b0b6f8d23c0c95e52b95f0bc69f7fe9b55", size = 241627, upload-time = "2025-06-30T15:51:10.605Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/3d/35f33045e21034b388686213752cabc3a1b9d03e20969e6fa8f1b1d82db1/multidict-6.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05db2f66c9addb10cfa226e1acb363450fab2ff8a6df73c622fefe2f5af6d4e7", size = 239351, upload-time = "2025-06-30T15:51:12.18Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/cc/ff84c03b95b430015d2166d9aae775a3985d757b94f6635010d0038d9241/multidict-6.6.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0db58da8eafb514db832a1b44f8fa7906fdd102f7d982025f816a93ba45e3dcb", size = 233429, upload-time = "2025-06-30T15:51:13.533Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/f0/8cd49a0b37bdea673a4b793c2093f2f4ba8e7c9d6d7c9bd672fd6d38cd11/multidict-6.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:14117a41c8fdb3ee19c743b1c027da0736fdb79584d61a766da53d399b71176c", size = 243094, upload-time = "2025-06-30T15:51:14.815Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/19/5d9a0cfdafe65d82b616a45ae950975820289069f885328e8185e64283c2/multidict-6.6.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:877443eaaabcd0b74ff32ebeed6f6176c71850feb7d6a1d2db65945256ea535c", size = 248957, upload-time = "2025-06-30T15:51:16.076Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/dc/c90066151da87d1e489f147b9b4327927241e65f1876702fafec6729c014/multidict-6.6.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:70b72e749a4f6e7ed8fb334fa8d8496384840319512746a5f42fa0aec79f4d61", size = 243590, upload-time = "2025-06-30T15:51:17.413Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/39/458afb0cccbb0ee9164365273be3e039efddcfcb94ef35924b7dbdb05db0/multidict-6.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43571f785b86afd02b3855c5ac8e86ec921b760298d6f82ff2a61daf5a35330b", size = 237487, upload-time = "2025-06-30T15:51:19.039Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/38/0016adac3990426610a081787011177e661875546b434f50a26319dc8372/multidict-6.6.3-cp310-cp310-win32.whl", hash = "sha256:20c5a0c3c13a15fd5ea86c42311859f970070e4e24de5a550e99d7c271d76318", size = 41390, upload-time = "2025-06-30T15:51:20.362Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/d2/17897a8f3f2c5363d969b4c635aa40375fe1f09168dc09a7826780bfb2a4/multidict-6.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab0a34a007704c625e25a9116c6770b4d3617a071c8a7c30cd338dfbadfe6485", size = 45954, upload-time = "2025-06-30T15:51:21.383Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/5f/d4a717c1e457fe44072e33fa400d2b93eb0f2819c4d669381f925b7cba1f/multidict-6.6.3-cp310-cp310-win_arm64.whl", hash = "sha256:769841d70ca8bdd140a715746199fc6473414bd02efd678d75681d2d6a8986c5", size = 42981, upload-time = "2025-06-30T15:51:22.809Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/f0/1a39863ced51f639c81a5463fbfa9eb4df59c20d1a8769ab9ef4ca57ae04/multidict-6.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18f4eba0cbac3546b8ae31e0bbc55b02c801ae3cbaf80c247fcdd89b456ff58c", size = 76445, upload-time = "2025-06-30T15:51:24.01Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/0e/a7cfa451c7b0365cd844e90b41e21fab32edaa1e42fc0c9f68461ce44ed7/multidict-6.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef43b5dd842382329e4797c46f10748d8c2b6e0614f46b4afe4aee9ac33159df", size = 44610, upload-time = "2025-06-30T15:51:25.158Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/bb/a14a4efc5ee748cc1904b0748be278c31b9295ce5f4d2ef66526f410b94d/multidict-6.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bd1fd5eec01494e0f2e8e446a74a85d5e49afb63d75a9934e4a5423dba21d", size = 44267, upload-time = "2025-06-30T15:51:26.326Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/f8/410677d563c2d55e063ef74fe578f9d53fe6b0a51649597a5861f83ffa15/multidict-6.6.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5bd8d6f793a787153956cd35e24f60485bf0651c238e207b9a54f7458b16d539", size = 230004, upload-time = "2025-06-30T15:51:27.491Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/df/2b787f80059314a98e1ec6a4cc7576244986df3e56b3c755e6fc7c99e038/multidict-6.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf99b4daf908c73856bd87ee0a2499c3c9a3d19bb04b9c6025e66af3fd07462", size = 247196, upload-time = "2025-06-30T15:51:28.762Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/f2/f9117089151b9a8ab39f9019620d10d9718eec2ac89e7ca9d30f3ec78e96/multidict-6.6.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b9e59946b49dafaf990fd9c17ceafa62976e8471a14952163d10a7a630413a9", size = 225337, upload-time = "2025-06-30T15:51:30.025Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/2d/7115300ec5b699faa152c56799b089a53ed69e399c3c2d528251f0aeda1a/multidict-6.6.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e2db616467070d0533832d204c54eea6836a5e628f2cb1e6dfd8cd6ba7277cb7", size = 257079, upload-time = "2025-06-30T15:51:31.716Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/ea/ff4bab367623e39c20d3b07637225c7688d79e4f3cc1f3b9f89867677f9a/multidict-6.6.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7394888236621f61dcdd25189b2768ae5cc280f041029a5bcf1122ac63df79f9", size = 255461, upload-time = "2025-06-30T15:51:33.029Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/07/2c9246cda322dfe08be85f1b8739646f2c4c5113a1422d7a407763422ec4/multidict-6.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f114d8478733ca7388e7c7e0ab34b72547476b97009d643644ac33d4d3fe1821", size = 246611, upload-time = "2025-06-30T15:51:34.47Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/62/279c13d584207d5697a752a66ffc9bb19355a95f7659140cb1b3cf82180e/multidict-6.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdf22e4db76d323bcdc733514bf732e9fb349707c98d341d40ebcc6e9318ef3d", size = 243102, upload-time = "2025-06-30T15:51:36.525Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/cc/e06636f48c6d51e724a8bc8d9e1db5f136fe1df066d7cafe37ef4000f86a/multidict-6.6.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e995a34c3d44ab511bfc11aa26869b9d66c2d8c799fa0e74b28a473a692532d6", size = 238693, upload-time = "2025-06-30T15:51:38.278Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/a4/66c9d8fb9acf3b226cdd468ed009537ac65b520aebdc1703dd6908b19d33/multidict-6.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:766a4a5996f54361d8d5a9050140aa5362fe48ce51c755a50c0bc3706460c430", size = 246582, upload-time = "2025-06-30T15:51:39.709Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/01/c69e0317be556e46257826d5449feb4e6aa0d18573e567a48a2c14156f1f/multidict-6.6.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3893a0d7d28a7fe6ca7a1f760593bc13038d1d35daf52199d431b61d2660602b", size = 253355, upload-time = "2025-06-30T15:51:41.013Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/da/9cc1da0299762d20e626fe0042e71b5694f9f72d7d3f9678397cbaa71b2b/multidict-6.6.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:934796c81ea996e61914ba58064920d6cad5d99140ac3167901eb932150e2e56", size = 247774, upload-time = "2025-06-30T15:51:42.291Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/91/b22756afec99cc31105ddd4a52f95ab32b1a4a58f4d417979c570c4a922e/multidict-6.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ed948328aec2072bc00f05d961ceadfd3e9bfc2966c1319aeaf7b7c21219183", size = 242275, upload-time = "2025-06-30T15:51:43.642Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/f1/adcc185b878036a20399d5be5228f3cbe7f823d78985d101d425af35c800/multidict-6.6.3-cp311-cp311-win32.whl", hash = "sha256:9f5b28c074c76afc3e4c610c488e3493976fe0e596dd3db6c8ddfbb0134dcac5", size = 41290, upload-time = "2025-06-30T15:51:45.264Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/d4/27652c1c6526ea6b4f5ddd397e93f4232ff5de42bea71d339bc6a6cc497f/multidict-6.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc7f6fbc61b1c16050a389c630da0b32fc6d4a3d191394ab78972bf5edc568c2", size = 45942, upload-time = "2025-06-30T15:51:46.377Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/18/23f4932019804e56d3c2413e237f866444b774b0263bcb81df2fdecaf593/multidict-6.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:d4e47d8faffaae822fb5cba20937c048d4f734f43572e7079298a6c39fb172cb", size = 42880, upload-time = "2025-06-30T15:51:47.561Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/a0/6b57988ea102da0623ea814160ed78d45a2645e4bbb499c2896d12833a70/multidict-6.6.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:056bebbeda16b2e38642d75e9e5310c484b7c24e3841dc0fb943206a72ec89d6", size = 76514, upload-time = "2025-06-30T15:51:48.728Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/7a/d1e92665b0850c6c0508f101f9cf0410c1afa24973e1115fe9c6a185ebf7/multidict-6.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e5f481cccb3c5c5e5de5d00b5141dc589c1047e60d07e85bbd7dea3d4580d63f", size = 45394, upload-time = "2025-06-30T15:51:49.986Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/6f/dd104490e01be6ef8bf9573705d8572f8c2d2c561f06e3826b081d9e6591/multidict-6.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10bea2ee839a759ee368b5a6e47787f399b41e70cf0c20d90dfaf4158dfb4e55", size = 43590, upload-time = "2025-06-30T15:51:51.331Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/fe/06e0e01b1b0611e6581b7fd5a85b43dacc08b6cea3034f902f383b0873e5/multidict-6.6.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2334cfb0fa9549d6ce2c21af2bfbcd3ac4ec3646b1b1581c88e3e2b1779ec92b", size = 237292, upload-time = "2025-06-30T15:51:52.584Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/71/4f0e558fb77696b89c233c1ee2d92f3e1d5459070a0e89153c9e9e804186/multidict-6.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8fee016722550a2276ca2cb5bb624480e0ed2bd49125b2b73b7010b9090e888", size = 258385, upload-time = "2025-06-30T15:51:53.913Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/25/cca0e68228addad24903801ed1ab42e21307a1b4b6dd2cf63da5d3ae082a/multidict-6.6.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5511cb35f5c50a2db21047c875eb42f308c5583edf96bd8ebf7d770a9d68f6d", size = 242328, upload-time = "2025-06-30T15:51:55.672Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/a3/46f2d420d86bbcb8fe660b26a10a219871a0fbf4d43cb846a4031533f3e0/multidict-6.6.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:712b348f7f449948e0a6c4564a21c7db965af900973a67db432d724619b3c680", size = 268057, upload-time = "2025-06-30T15:51:57.037Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/73/1c743542fe00794a2ec7466abd3f312ccb8fad8dff9f36d42e18fb1ec33e/multidict-6.6.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e15d2138ee2694e038e33b7c3da70e6b0ad8868b9f8094a72e1414aeda9c1a", size = 269341, upload-time = "2025-06-30T15:51:59.111Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/11/6ec9dcbe2264b92778eeb85407d1df18812248bf3506a5a1754bc035db0c/multidict-6.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8df25594989aebff8a130f7899fa03cbfcc5d2b5f4a461cf2518236fe6f15961", size = 256081, upload-time = "2025-06-30T15:52:00.533Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/2b/631b1e2afeb5f1696846d747d36cda075bfdc0bc7245d6ba5c319278d6c4/multidict-6.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:159ca68bfd284a8860f8d8112cf0521113bffd9c17568579e4d13d1f1dc76b65", size = 253581, upload-time = "2025-06-30T15:52:02.43Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/0e/7e3b93f79efeb6111d3bf9a1a69e555ba1d07ad1c11bceb56b7310d0d7ee/multidict-6.6.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e098c17856a8c9ade81b4810888c5ad1914099657226283cab3062c0540b0643", size = 250750, upload-time = "2025-06-30T15:52:04.26Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/9e/086846c1d6601948e7de556ee464a2d4c85e33883e749f46b9547d7b0704/multidict-6.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:67c92ed673049dec52d7ed39f8cf9ebbadf5032c774058b4406d18c8f8fe7063", size = 251548, upload-time = "2025-06-30T15:52:06.002Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/7b/86ec260118e522f1a31550e87b23542294880c97cfbf6fb18cc67b044c66/multidict-6.6.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:bd0578596e3a835ef451784053cfd327d607fc39ea1a14812139339a18a0dbc3", size = 262718, upload-time = "2025-06-30T15:52:07.707Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/bd/22ce8f47abb0be04692c9fc4638508b8340987b18691aa7775d927b73f72/multidict-6.6.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:346055630a2df2115cd23ae271910b4cae40f4e336773550dca4889b12916e75", size = 259603, upload-time = "2025-06-30T15:52:09.58Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/9c/91b7ac1691be95cd1f4a26e36a74b97cda6aa9820632d31aab4410f46ebd/multidict-6.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:555ff55a359302b79de97e0468e9ee80637b0de1fce77721639f7cd9440b3a10", size = 251351, upload-time = "2025-06-30T15:52:10.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/5c/4d7adc739884f7a9fbe00d1eac8c034023ef8bad71f2ebe12823ca2e3649/multidict-6.6.3-cp312-cp312-win32.whl", hash = "sha256:73ab034fb8d58ff85c2bcbadc470efc3fafeea8affcf8722855fb94557f14cc5", size = 41860, upload-time = "2025-06-30T15:52:12.334Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/a3/0fbc7afdf7cb1aa12a086b02959307848eb6bcc8f66fcb66c0cb57e2a2c1/multidict-6.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:04cbcce84f63b9af41bad04a54d4cc4e60e90c35b9e6ccb130be2d75b71f8c17", size = 45982, upload-time = "2025-06-30T15:52:13.6Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/95/8c825bd70ff9b02462dc18d1295dd08d3e9e4eb66856d292ffa62cfe1920/multidict-6.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:0f1130b896ecb52d2a1e615260f3ea2af55fa7dc3d7c3003ba0c3121a759b18b", size = 43210, upload-time = "2025-06-30T15:52:14.893Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a", size = 12313, upload-time = "2025-06-30T15:53:45.437Z" }, ] [[package]] name = "multiprocess" version = "0.70.16" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "dill" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603, upload-time = "2024-01-28T18:52:34.85Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ef/76/6e712a2623d146d314f17598df5de7224c85c0060ef63fd95cc15a25b3fa/multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/ab/1e6e8009e380e22254ff539ebe117861e5bdb3bff1fc977920972237c6c7/multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/76/6e712a2623d146d314f17598df5de7224c85c0060ef63fd95cc15a25b3fa/multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee", size = 134980, upload-time = "2024-01-28T18:52:15.731Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/ab/1e6e8009e380e22254ff539ebe117861e5bdb3bff1fc977920972237c6c7/multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec", size = 134982, upload-time = "2024-01-28T18:52:17.783Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824, upload-time = "2024-01-28T18:52:26.062Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519, upload-time = "2024-01-28T18:52:28.115Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741, upload-time = "2024-01-28T18:52:29.395Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628, upload-time = "2024-01-28T18:52:30.853Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351, upload-time = "2024-01-28T18:52:31.981Z" }, ] [[package]] name = "multitasking" version = "0.0.12" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/17/0d/74f0293dfd7dcc3837746d0138cbedd60b31701ecc75caec7d3f281feba0/multitasking-0.0.12.tar.gz", hash = "sha256:2fba2fa8ed8c4b85e227c5dd7dc41c7d658de3b6f247927316175a57349b84d1" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/0d/74f0293dfd7dcc3837746d0138cbedd60b31701ecc75caec7d3f281feba0/multitasking-0.0.12.tar.gz", hash = "sha256:2fba2fa8ed8c4b85e227c5dd7dc41c7d658de3b6f247927316175a57349b84d1", size = 19984, upload-time = "2025-07-20T21:27:51.636Z" } [[package]] name = "mygene" version = "3.2.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "biothings-client" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0a/ec/a256003f84196aa3fdd65a7c6f5adfc0688398fb66442eba75b39c9b7627/mygene-3.2.2.tar.gz", hash = "sha256:e729cabbc28cf5afb221bca1ab637883b375cb1a3e2f067587ec79f71affdaea" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/ec/a256003f84196aa3fdd65a7c6f5adfc0688398fb66442eba75b39c9b7627/mygene-3.2.2.tar.gz", hash = "sha256:e729cabbc28cf5afb221bca1ab637883b375cb1a3e2f067587ec79f71affdaea", size = 5399, upload-time = "2021-04-05T21:24:30.934Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a7/b7/132b1673c0ec00881d49d56c09624942fa0ebd2fc21d73d80647efa082e9/mygene-3.2.2-py2.py3-none-any.whl", hash = "sha256:18d85d1b28ecee2be31d844607fb0c5f7d7c58573278432df819ee2a5e88fe46" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/b7/132b1673c0ec00881d49d56c09624942fa0ebd2fc21d73d80647efa082e9/mygene-3.2.2-py2.py3-none-any.whl", hash = "sha256:18d85d1b28ecee2be31d844607fb0c5f7d7c58573278432df819ee2a5e88fe46", size = 5357, upload-time = "2021-04-05T21:24:29.07Z" }, ] [[package]] name = "nest-asyncio" version = "1.6.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, ] [[package]] name = "networkx" version = "3.4.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, ] [[package]] name = "networkx" version = "3.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } resolution-markers = [ "python_full_version >= '3.12' and sys_platform == 'darwin'", "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", @@ -3646,216 +3646,216 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, ] [[package]] name = "nltk" version = "3.9.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "click" }, { name = "joblib" }, { name = "regex" }, { name = "tqdm" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3c/87/db8be88ad32c2d042420b6fd9ffd4a149f9a0d7f0e86b3f543be2eeeedd2/nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/87/db8be88ad32c2d042420b6fd9ffd4a149f9a0d7f0e86b3f543be2eeeedd2/nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868", size = 2904691, upload-time = "2024-08-18T19:48:37.769Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4d/66/7d9e26593edda06e8cb531874633f7c2372279c3b0f46235539fe546df8b/nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/66/7d9e26593edda06e8cb531874633f7c2372279c3b0f46235539fe546df8b/nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1", size = 1505442, upload-time = "2024-08-18T19:48:21.909Z" }, ] [[package]] name = "numba" version = "0.61.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "llvmlite" }, { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1c/a0/e21f57604304aa03ebb8e098429222722ad99176a4f979d34af1d1ee80da/numba-0.61.2.tar.gz", hash = "sha256:8750ee147940a6637b80ecf7f95062185ad8726c8c28a2295b8ec1160a196f7d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/a0/e21f57604304aa03ebb8e098429222722ad99176a4f979d34af1d1ee80da/numba-0.61.2.tar.gz", hash = "sha256:8750ee147940a6637b80ecf7f95062185ad8726c8c28a2295b8ec1160a196f7d", size = 2820615, upload-time = "2025-04-09T02:58:07.659Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/eb/ca/f470be59552ccbf9531d2d383b67ae0b9b524d435fb4a0d229fef135116e/numba-0.61.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:cf9f9fc00d6eca0c23fc840817ce9f439b9f03c8f03d6246c0e7f0cb15b7162a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f5/13/3bdf52609c80d460a3b4acfb9fdb3817e392875c0d6270cf3fd9546f138b/numba-0.61.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ea0247617edcb5dd61f6106a56255baab031acc4257bddaeddb3a1003b4ca3fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e2/7d/bfb2805bcfbd479f04f835241ecf28519f6e3609912e3a985aed45e21370/numba-0.61.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae8c7a522c26215d5f62ebec436e3d341f7f590079245a2f1008dfd498cc1642" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/27/797b2004745c92955470c73c82f0e300cf033c791f45bdecb4b33b12bdea/numba-0.61.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd1e74609855aa43661edffca37346e4e8462f6903889917e9f41db40907daa2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/c6/c2fb11e50482cb310afae87a997707f6c7d8a48967b9696271347441f650/numba-0.61.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae45830b129c6137294093b269ef0a22998ccc27bf7cf096ab8dcf7bca8946f9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/97/c99d1056aed767503c228f7099dc11c402906b42a4757fec2819329abb98/numba-0.61.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:efd3db391df53aaa5cfbee189b6c910a5b471488749fd6606c3f33fc984c2ae2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/95/9e/63c549f37136e892f006260c3e2613d09d5120672378191f2dc387ba65a2/numba-0.61.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:49c980e4171948ffebf6b9a2520ea81feed113c1f4890747ba7f59e74be84b1b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/97/c8/8740616c8436c86c1b9a62e72cb891177d2c34c2d24ddcde4c390371bf4c/numba-0.61.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3945615cd73c2c7eba2a85ccc9c1730c21cd3958bfcf5a44302abae0fb07bb60" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/06/66e99ae06507c31d15ff3ecd1f108f2f59e18b6e08662cd5f8a5853fbd18/numba-0.61.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbfdf4eca202cebade0b7d43896978e146f39398909a42941c9303f82f403a18" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/a4/2b309a6a9f6d4d8cfba583401c7c2f9ff887adb5d54d8e2e130274c0973f/numba-0.61.2-cp311-cp311-win_amd64.whl", hash = "sha256:76bcec9f46259cedf888041b9886e257ae101c6268261b19fda8cfbc52bec9d1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/a0/c6b7b9c615cfa3b98c4c63f4316e3f6b3bbe2387740277006551784218cd/numba-0.61.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:34fba9406078bac7ab052efbf0d13939426c753ad72946baaa5bf9ae0ebb8dd2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/4a/fe4e3c2ecad72d88f5f8cd04e7f7cff49e718398a2fac02d2947480a00ca/numba-0.61.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ddce10009bc097b080fc96876d14c051cc0c7679e99de3e0af59014dab7dfe8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9a/2d/e518df036feab381c23a624dac47f8445ac55686ec7f11083655eb707da3/numba-0.61.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b1bb509d01f23d70325d3a5a0e237cbc9544dd50e50588bc581ba860c213546" }, - { url = "https://mirrors.aliyun.com/pypi/packages/10/0f/23cced68ead67b75d77cfcca3df4991d1855c897ee0ff3fe25a56ed82108/numba-0.61.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48a53a3de8f8793526cbe330f2a39fe9a6638efcbf11bd63f3d2f9757ae345cd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/1d/ddb3e704c5a8fb90142bf9dc195c27db02a08a99f037395503bfbc1d14b3/numba-0.61.2-cp312-cp312-win_amd64.whl", hash = "sha256:97cf4f12c728cf77c9c1d7c23707e4d8fb4632b46275f8f3397de33e5877af18" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/ca/f470be59552ccbf9531d2d383b67ae0b9b524d435fb4a0d229fef135116e/numba-0.61.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:cf9f9fc00d6eca0c23fc840817ce9f439b9f03c8f03d6246c0e7f0cb15b7162a", size = 2775663, upload-time = "2025-04-09T02:57:34.143Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/13/3bdf52609c80d460a3b4acfb9fdb3817e392875c0d6270cf3fd9546f138b/numba-0.61.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ea0247617edcb5dd61f6106a56255baab031acc4257bddaeddb3a1003b4ca3fd", size = 2778344, upload-time = "2025-04-09T02:57:36.609Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/7d/bfb2805bcfbd479f04f835241ecf28519f6e3609912e3a985aed45e21370/numba-0.61.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae8c7a522c26215d5f62ebec436e3d341f7f590079245a2f1008dfd498cc1642", size = 3824054, upload-time = "2025-04-09T02:57:38.162Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/27/797b2004745c92955470c73c82f0e300cf033c791f45bdecb4b33b12bdea/numba-0.61.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd1e74609855aa43661edffca37346e4e8462f6903889917e9f41db40907daa2", size = 3518531, upload-time = "2025-04-09T02:57:39.709Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/c6/c2fb11e50482cb310afae87a997707f6c7d8a48967b9696271347441f650/numba-0.61.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae45830b129c6137294093b269ef0a22998ccc27bf7cf096ab8dcf7bca8946f9", size = 2831612, upload-time = "2025-04-09T02:57:41.559Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/97/c99d1056aed767503c228f7099dc11c402906b42a4757fec2819329abb98/numba-0.61.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:efd3db391df53aaa5cfbee189b6c910a5b471488749fd6606c3f33fc984c2ae2", size = 2775825, upload-time = "2025-04-09T02:57:43.442Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/9e/63c549f37136e892f006260c3e2613d09d5120672378191f2dc387ba65a2/numba-0.61.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:49c980e4171948ffebf6b9a2520ea81feed113c1f4890747ba7f59e74be84b1b", size = 2778695, upload-time = "2025-04-09T02:57:44.968Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/c8/8740616c8436c86c1b9a62e72cb891177d2c34c2d24ddcde4c390371bf4c/numba-0.61.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3945615cd73c2c7eba2a85ccc9c1730c21cd3958bfcf5a44302abae0fb07bb60", size = 3829227, upload-time = "2025-04-09T02:57:46.63Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/06/66e99ae06507c31d15ff3ecd1f108f2f59e18b6e08662cd5f8a5853fbd18/numba-0.61.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbfdf4eca202cebade0b7d43896978e146f39398909a42941c9303f82f403a18", size = 3523422, upload-time = "2025-04-09T02:57:48.222Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/a4/2b309a6a9f6d4d8cfba583401c7c2f9ff887adb5d54d8e2e130274c0973f/numba-0.61.2-cp311-cp311-win_amd64.whl", hash = "sha256:76bcec9f46259cedf888041b9886e257ae101c6268261b19fda8cfbc52bec9d1", size = 2831505, upload-time = "2025-04-09T02:57:50.108Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/a0/c6b7b9c615cfa3b98c4c63f4316e3f6b3bbe2387740277006551784218cd/numba-0.61.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:34fba9406078bac7ab052efbf0d13939426c753ad72946baaa5bf9ae0ebb8dd2", size = 2776626, upload-time = "2025-04-09T02:57:51.857Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/4a/fe4e3c2ecad72d88f5f8cd04e7f7cff49e718398a2fac02d2947480a00ca/numba-0.61.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ddce10009bc097b080fc96876d14c051cc0c7679e99de3e0af59014dab7dfe8", size = 2779287, upload-time = "2025-04-09T02:57:53.658Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/2d/e518df036feab381c23a624dac47f8445ac55686ec7f11083655eb707da3/numba-0.61.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b1bb509d01f23d70325d3a5a0e237cbc9544dd50e50588bc581ba860c213546", size = 3885928, upload-time = "2025-04-09T02:57:55.206Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/0f/23cced68ead67b75d77cfcca3df4991d1855c897ee0ff3fe25a56ed82108/numba-0.61.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48a53a3de8f8793526cbe330f2a39fe9a6638efcbf11bd63f3d2f9757ae345cd", size = 3577115, upload-time = "2025-04-09T02:57:56.818Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/1d/ddb3e704c5a8fb90142bf9dc195c27db02a08a99f037395503bfbc1d14b3/numba-0.61.2-cp312-cp312-win_amd64.whl", hash = "sha256:97cf4f12c728cf77c9c1d7c23707e4d8fb4632b46275f8f3397de33e5877af18", size = 2831929, upload-time = "2025-04-09T02:57:58.45Z" }, ] [[package]] name = "numpy" version = "1.26.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload-time = "2024-02-05T23:52:15.314Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload-time = "2024-02-05T23:52:47.569Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload-time = "2024-02-05T23:53:15.637Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload-time = "2024-02-05T23:53:42.16Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload-time = "2024-02-05T23:54:11.696Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload-time = "2024-02-05T23:54:26.453Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload-time = "2024-02-05T23:54:53.933Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901, upload-time = "2024-02-05T23:55:32.801Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868, upload-time = "2024-02-05T23:55:56.28Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109, upload-time = "2024-02-05T23:56:20.368Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613, upload-time = "2024-02-05T23:56:56.054Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172, upload-time = "2024-02-05T23:57:21.56Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643, upload-time = "2024-02-05T23:57:56.585Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803, upload-time = "2024-02-05T23:58:08.963Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754, upload-time = "2024-02-05T23:58:36.364Z" }, ] [[package]] name = "nvidia-cublas-cu12" version = "12.6.4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb", size = 393138322, upload-time = "2024-11-20T17:40:25.65Z" }, ] [[package]] name = "nvidia-cuda-cupti-cu12" version = "12.6.80" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132", size = 8917980, upload-time = "2024-11-20T17:36:04.019Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73", size = 8917972, upload-time = "2024-10-01T16:58:06.036Z" }, ] [[package]] name = "nvidia-cuda-nvrtc-cu12" version = "12.6.77" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53", size = 23650380, upload-time = "2024-10-01T17:00:14.643Z" }, ] [[package]] name = "nvidia-cuda-runtime-cu12" version = "12.6.77" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7", size = 897690, upload-time = "2024-11-20T17:35:30.697Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8", size = 897678, upload-time = "2024-10-01T16:57:33.821Z" }, ] [[package]] name = "nvidia-cudnn-cu12" version = "9.5.1.17" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386, upload-time = "2024-10-25T19:54:26.39Z" }, ] [[package]] name = "nvidia-cufft-cu12" version = "11.3.0.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632, upload-time = "2024-11-20T17:41:32.357Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca", size = 200221622, upload-time = "2024-10-01T17:03:58.79Z" }, ] [[package]] name = "nvidia-cufile-cu12" version = "1.11.1.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159", size = 1142103, upload-time = "2024-11-20T17:42:11.83Z" }, ] [[package]] name = "nvidia-curand-cu12" version = "10.3.7.77" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf", size = 56279010, upload-time = "2024-11-20T17:42:50.958Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117", size = 56279000, upload-time = "2024-10-01T17:04:45.274Z" }, ] [[package]] name = "nvidia-cusolver-cu12" version = "11.7.1.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790, upload-time = "2024-11-20T17:43:43.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6", size = 158229780, upload-time = "2024-10-01T17:05:39.875Z" }, ] [[package]] name = "nvidia-cusparse-cu12" version = "12.5.4.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367, upload-time = "2024-11-20T17:44:54.824Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f", size = 216561357, upload-time = "2024-10-01T17:06:29.861Z" }, ] [[package]] name = "nvidia-cusparselt-cu12" version = "0.6.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796, upload-time = "2024-10-15T21:29:17.709Z" }, ] [[package]] name = "nvidia-nccl-cu12" version = "2.26.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755, upload-time = "2025-03-13T00:29:55.296Z" }, ] [[package]] name = "nvidia-nvjitlink-cu12" version = "12.6.85" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971, upload-time = "2024-11-20T17:46:53.366Z" }, ] [[package]] name = "nvidia-nvtx-cu12" version = "12.6.77" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2", size = 89276, upload-time = "2024-11-20T17:38:27.621Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1", size = 89265, upload-time = "2024-10-01T17:00:38.172Z" }, ] [[package]] @@ -3884,49 +3884,49 @@ wheels = [ [[package]] name = "ollama" version = "0.2.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "httpx" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/aa/2b/bda3e59080b136e90367bebb67d5072922a912f0e0b6f49be1b4eb79c109/ollama-0.2.1.tar.gz", hash = "sha256:fa316baa9a81eac3beb4affb0a17deb3008fdd6ed05b123c26306cfbe4c349b6" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/2b/bda3e59080b136e90367bebb67d5072922a912f0e0b6f49be1b4eb79c109/ollama-0.2.1.tar.gz", hash = "sha256:fa316baa9a81eac3beb4affb0a17deb3008fdd6ed05b123c26306cfbe4c349b6", size = 9918, upload-time = "2024-06-05T19:00:52.447Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7d/b7/8cc05807bfbc5b92da7fb94c525e1e56572a08eea7cdf3656e6c5dc6f9b1/ollama-0.2.1-py3-none-any.whl", hash = "sha256:b6e2414921c94f573a903d1069d682ba2fb2607070ea9e19ca4a7872f2a460ec" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/b7/8cc05807bfbc5b92da7fb94c525e1e56572a08eea7cdf3656e6c5dc6f9b1/ollama-0.2.1-py3-none-any.whl", hash = "sha256:b6e2414921c94f573a903d1069d682ba2fb2607070ea9e19ca4a7872f2a460ec", size = 9738, upload-time = "2024-06-05T19:00:47.437Z" }, ] [[package]] name = "onnx" version = "1.18.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, { name = "protobuf" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3d/60/e56e8ec44ed34006e6d4a73c92a04d9eea6163cc12440e35045aec069175/onnx-1.18.0.tar.gz", hash = "sha256:3d8dbf9e996629131ba3aa1afd1d8239b660d1f830c6688dd7e03157cccd6b9c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/60/e56e8ec44ed34006e6d4a73c92a04d9eea6163cc12440e35045aec069175/onnx-1.18.0.tar.gz", hash = "sha256:3d8dbf9e996629131ba3aa1afd1d8239b660d1f830c6688dd7e03157cccd6b9c", size = 12563009, upload-time = "2025-05-12T22:03:09.626Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8e/e3/ab8a09c0af43373e0422de461956a1737581325260659aeffae22a7dad18/onnx-1.18.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:4a3b50d94620e2c7c1404d1d59bc53e665883ae3fecbd856cc86da0639fd0fc3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/5b/3cfd183961a0a872fe29c95f8d07264890ec65c75c94b99a4dabc950df29/onnx-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e189652dad6e70a0465035c55cc565c27aa38803dd4f4e74e4b952ee1c2de94b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/52/fa649429016c5790f68c614cdebfbefd3e72ba1c458966305297d540f713/onnx-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb1f271b1523b29f324bfd223f6a4cfbdc5a2f2f16e73563671932d33663365" }, - { url = "https://mirrors.aliyun.com/pypi/packages/42/52/dc166de41a5f72738b0bdfb2a19e0ebe4743cf3ecc9ae381ea3425bcb332/onnx-1.18.0-cp310-cp310-win32.whl", hash = "sha256:e03071041efd82e0317b3c45433b2f28146385b80f26f82039bc68048ac1a7a0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/f9/e766a3b85b7651ddfc5f9648e0e9dc24e88b7e88ea7f8c23187530e818ea/onnx-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:9235b3493951e11e75465d56f4cd97e3e9247f096160dd3466bfabe4cbc938bc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/3a/a336dac4db1eddba2bf577191e5b7d3e4c26fcee5ec518a5a5b11d13540d/onnx-1.18.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:735e06d8d0cf250dc498f54038831401063c655a8d6e5975b2527a4e7d24be3e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/3a/56475a111120d1e5d11939acbcbb17c92198c8e64a205cd68e00bdfd8a1f/onnx-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73160799472e1a86083f786fecdf864cf43d55325492a9b5a1cfa64d8a523ecc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cf/03/5eb5e9ef446ed9e78c4627faf3c1bc25e0f707116dd00e9811de232a8df5/onnx-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6acafb3823238bbe8f4340c7ac32fb218689442e074d797bee1c5c9a02fdae75" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/4e/70943125729ce453271a6e46bb847b4a612496f64db6cbc6cb1f49f41ce1/onnx-1.18.0-cp311-cp311-win32.whl", hash = "sha256:4c8c4bbda760c654e65eaffddb1a7de71ec02e60092d33f9000521f897c99be9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/b0/435fd764011911e8f599e3361f0f33425b1004662c1ea33a0ad22e43db2d/onnx-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5810194f0f6be2e58c8d6dedc6119510df7a14280dd07ed5f0f0a85bd74816a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/f0/9e31f4b4626d60f1c034f71b411810bc9fafe31f4e7dd3598effd1b50e05/onnx-1.18.0-cp311-cp311-win_arm64.whl", hash = "sha256:aa1b7483fac6cdec26922174fc4433f8f5c2f239b1133c5625063bb3b35957d0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a7/fe/16228aca685392a7114625b89aae98b2dc4058a47f0f467a376745efe8d0/onnx-1.18.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:521bac578448667cbb37c50bf05b53c301243ede8233029555239930996a625b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1e/77/ba50a903a9b5e6f9be0fa50f59eb2fca4a26ee653375408fbc72c3acbf9f/onnx-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4da451bf1c5ae381f32d430004a89f0405bc57a8471b0bddb6325a5b334aa40" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/23/25ec2ba723ac62b99e8fed6d7b59094dadb15e38d4c007331cc9ae3dfa5f/onnx-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99afac90b4cdb1471432203c3c1f74e16549c526df27056d39f41a9a47cfb4af" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/4d/2c253a36070fb43f340ff1d2c450df6a9ef50b938adcd105693fee43c4ee/onnx-1.18.0-cp312-cp312-win32.whl", hash = "sha256:ee159b41a3ae58d9c7341cf432fc74b96aaf50bd7bb1160029f657b40dc69715" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e8/92/048ba8fafe6b2b9a268ec2fb80def7e66c0b32ab2cae74de886981f05a27/onnx-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:102c04edc76b16e9dfeda5a64c1fccd7d3d2913b1544750c01d38f1ac3c04e05" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/66/bbc4ffedd44165dcc407a51ea4c592802a5391ce3dc94aa5045350f64635/onnx-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:911b37d724a5d97396f3c2ef9ea25361c55cbc9aa18d75b12a52b620b67145af" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/e3/ab8a09c0af43373e0422de461956a1737581325260659aeffae22a7dad18/onnx-1.18.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:4a3b50d94620e2c7c1404d1d59bc53e665883ae3fecbd856cc86da0639fd0fc3", size = 18280145, upload-time = "2025-05-12T22:01:49.875Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/5b/3cfd183961a0a872fe29c95f8d07264890ec65c75c94b99a4dabc950df29/onnx-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e189652dad6e70a0465035c55cc565c27aa38803dd4f4e74e4b952ee1c2de94b", size = 17422721, upload-time = "2025-05-12T22:01:52.841Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/52/fa649429016c5790f68c614cdebfbefd3e72ba1c458966305297d540f713/onnx-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb1f271b1523b29f324bfd223f6a4cfbdc5a2f2f16e73563671932d33663365", size = 17584220, upload-time = "2025-05-12T22:01:56.458Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/52/dc166de41a5f72738b0bdfb2a19e0ebe4743cf3ecc9ae381ea3425bcb332/onnx-1.18.0-cp310-cp310-win32.whl", hash = "sha256:e03071041efd82e0317b3c45433b2f28146385b80f26f82039bc68048ac1a7a0", size = 15734494, upload-time = "2025-05-12T22:01:59.704Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/f9/e766a3b85b7651ddfc5f9648e0e9dc24e88b7e88ea7f8c23187530e818ea/onnx-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:9235b3493951e11e75465d56f4cd97e3e9247f096160dd3466bfabe4cbc938bc", size = 15848421, upload-time = "2025-05-12T22:02:03.01Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/3a/a336dac4db1eddba2bf577191e5b7d3e4c26fcee5ec518a5a5b11d13540d/onnx-1.18.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:735e06d8d0cf250dc498f54038831401063c655a8d6e5975b2527a4e7d24be3e", size = 18281831, upload-time = "2025-05-12T22:02:06.429Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/3a/56475a111120d1e5d11939acbcbb17c92198c8e64a205cd68e00bdfd8a1f/onnx-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73160799472e1a86083f786fecdf864cf43d55325492a9b5a1cfa64d8a523ecc", size = 17424359, upload-time = "2025-05-12T22:02:09.866Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/03/5eb5e9ef446ed9e78c4627faf3c1bc25e0f707116dd00e9811de232a8df5/onnx-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6acafb3823238bbe8f4340c7ac32fb218689442e074d797bee1c5c9a02fdae75", size = 17586006, upload-time = "2025-05-12T22:02:13.217Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/4e/70943125729ce453271a6e46bb847b4a612496f64db6cbc6cb1f49f41ce1/onnx-1.18.0-cp311-cp311-win32.whl", hash = "sha256:4c8c4bbda760c654e65eaffddb1a7de71ec02e60092d33f9000521f897c99be9", size = 15734988, upload-time = "2025-05-12T22:02:16.561Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/b0/435fd764011911e8f599e3361f0f33425b1004662c1ea33a0ad22e43db2d/onnx-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5810194f0f6be2e58c8d6dedc6119510df7a14280dd07ed5f0f0a85bd74816a", size = 15849576, upload-time = "2025-05-12T22:02:19.569Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/f0/9e31f4b4626d60f1c034f71b411810bc9fafe31f4e7dd3598effd1b50e05/onnx-1.18.0-cp311-cp311-win_arm64.whl", hash = "sha256:aa1b7483fac6cdec26922174fc4433f8f5c2f239b1133c5625063bb3b35957d0", size = 15822961, upload-time = "2025-05-12T22:02:22.735Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/fe/16228aca685392a7114625b89aae98b2dc4058a47f0f467a376745efe8d0/onnx-1.18.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:521bac578448667cbb37c50bf05b53c301243ede8233029555239930996a625b", size = 18285770, upload-time = "2025-05-12T22:02:26.116Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/77/ba50a903a9b5e6f9be0fa50f59eb2fca4a26ee653375408fbc72c3acbf9f/onnx-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4da451bf1c5ae381f32d430004a89f0405bc57a8471b0bddb6325a5b334aa40", size = 17421291, upload-time = "2025-05-12T22:02:29.645Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/23/25ec2ba723ac62b99e8fed6d7b59094dadb15e38d4c007331cc9ae3dfa5f/onnx-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99afac90b4cdb1471432203c3c1f74e16549c526df27056d39f41a9a47cfb4af", size = 17584084, upload-time = "2025-05-12T22:02:32.789Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/4d/2c253a36070fb43f340ff1d2c450df6a9ef50b938adcd105693fee43c4ee/onnx-1.18.0-cp312-cp312-win32.whl", hash = "sha256:ee159b41a3ae58d9c7341cf432fc74b96aaf50bd7bb1160029f657b40dc69715", size = 15734892, upload-time = "2025-05-12T22:02:35.527Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/92/048ba8fafe6b2b9a268ec2fb80def7e66c0b32ab2cae74de886981f05a27/onnx-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:102c04edc76b16e9dfeda5a64c1fccd7d3d2913b1544750c01d38f1ac3c04e05", size = 15850336, upload-time = "2025-05-12T22:02:38.545Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/66/bbc4ffedd44165dcc407a51ea4c592802a5391ce3dc94aa5045350f64635/onnx-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:911b37d724a5d97396f3c2ef9ea25361c55cbc9aa18d75b12a52b620b67145af", size = 15823802, upload-time = "2025-05-12T22:02:42.037Z" }, ] [[package]] name = "onnxruntime" version = "1.19.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "coloredlogs" }, { name = "flatbuffers" }, @@ -3936,27 +3936,27 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/39/18/272d3d7406909141d3c9943796e3e97cafa53f4342d9231c0cfd8cb05702/onnxruntime-1.19.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:84fa57369c06cadd3c2a538ae2a26d76d583e7c34bdecd5769d71ca5c0fc750e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d8/d3/eb93f4ae511cfc725d0c69e07008800f8ac018de19ea1e497b306f174ccc/onnxruntime-1.19.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdc471a66df0c1cdef774accef69e9f2ca168c851ab5e4f2f3341512c7ef4666" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ca/4b/ce5958074abe4b6e8d1da9c10e443e01a681558a9ec17e5cc7619438e094/onnxruntime-1.19.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e3a4ce906105d99ebbe817f536d50a91ed8a4d1592553f49b3c23c4be2560ae6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/0f/6df82dfe02467d12adbaa05c2bd17519c29c7df531ed600231f0c741ad22/onnxruntime-1.19.2-cp310-cp310-win32.whl", hash = "sha256:4b3d723cc154c8ddeb9f6d0a8c0d6243774c6b5930847cc83170bfe4678fafb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/d8/68b63dc86b502169d017a86fe8bc718f4b0055ef1f6895bfaddd04f2eead/onnxruntime-1.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:17ed7382d2c58d4b7354fb2b301ff30b9bf308a1c7eac9546449cd122d21cae5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/ff/77bee5df55f034ee81d2e1bc58b2b8511b9c54f06ce6566cb562c5d95aa5/onnxruntime-1.19.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d863e8acdc7232d705d49e41087e10b274c42f09e259016a46f32c34e06dc4fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/78/e29f5fb76e0f6524f3520e8e5b9d53282784b45d14068c5112db9f712b0a/onnxruntime-1.19.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1dfe4f660a71b31caa81fc298a25f9612815215a47b286236e61d540350d7b6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/60/ce/be4152da5c1030ab5a159a4a792ed9abad6ba498d79ef0aeba593ff7b5bf/onnxruntime-1.19.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a36511dc07c5c964b916697e42e366fa43c48cdb3d3503578d78cef30417cb84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/00/9740a074eb0e0a21ff13a2c4f32aecc5b21110b2c9b9177d8ac132b66e2d/onnxruntime-1.19.2-cp311-cp311-win32.whl", hash = "sha256:50cbb8dc69d6befad4746a69760e5b00cc3ff0a59c6c3fb27f8afa20e2cab7e7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1e/f5/9d995a685f97508b3254f17015b4a78641b0625e79480a7aed7a7a105d7c/onnxruntime-1.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:1c3e5d415b78337fa0b1b75291e9ea9fb2a4c1f148eb5811e7212fed02cfffa8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/a5/2a02687a88fc8a2507bef65876c90e96b9f8de5ba1f810acbf67c140fc67/onnxruntime-1.19.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:68e7051bef9cfefcbb858d2d2646536829894d72a4130c24019219442b1dd2ed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/64/da42254ec14452cad2cdd4cf407094841c0a378c0d08944e9a36172197e9/onnxruntime-1.19.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2d366fbcc205ce68a8a3bde2185fd15c604d9645888703785b61ef174265168" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/92/3574f6836f33b1b25f272293e72538c38451b12c2d9aa08630bb6bc0f057/onnxruntime-1.19.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:477b93df4db467e9cbf34051662a4b27c18e131fa1836e05974eae0d6e4cf29b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/c9/8c37e413a830cac7f7dc094fffbd0c998c8bcb66a6f0b0a3201a49bc742b/onnxruntime-1.19.2-cp312-cp312-win32.whl", hash = "sha256:9a174073dc5608fad05f7cf7f320b52e8035e73d80b0a23c80f840e5a97c0147" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/c0/59768846533786a82cafb38d8d2f900ad666bc91f0ae634774d286fa3c47/onnxruntime-1.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:190103273ea4507638ffc31d66a980594b237874b65379e273125150eb044857" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/18/272d3d7406909141d3c9943796e3e97cafa53f4342d9231c0cfd8cb05702/onnxruntime-1.19.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:84fa57369c06cadd3c2a538ae2a26d76d583e7c34bdecd5769d71ca5c0fc750e", size = 16776408, upload-time = "2024-09-04T06:37:02.431Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/d3/eb93f4ae511cfc725d0c69e07008800f8ac018de19ea1e497b306f174ccc/onnxruntime-1.19.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdc471a66df0c1cdef774accef69e9f2ca168c851ab5e4f2f3341512c7ef4666", size = 11491779, upload-time = "2024-09-04T06:37:06.203Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/4b/ce5958074abe4b6e8d1da9c10e443e01a681558a9ec17e5cc7619438e094/onnxruntime-1.19.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e3a4ce906105d99ebbe817f536d50a91ed8a4d1592553f49b3c23c4be2560ae6", size = 13170428, upload-time = "2024-09-04T06:37:09.032Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/0f/6df82dfe02467d12adbaa05c2bd17519c29c7df531ed600231f0c741ad22/onnxruntime-1.19.2-cp310-cp310-win32.whl", hash = "sha256:4b3d723cc154c8ddeb9f6d0a8c0d6243774c6b5930847cc83170bfe4678fafb3", size = 9591305, upload-time = "2024-09-04T06:37:11.482Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/d8/68b63dc86b502169d017a86fe8bc718f4b0055ef1f6895bfaddd04f2eead/onnxruntime-1.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:17ed7382d2c58d4b7354fb2b301ff30b9bf308a1c7eac9546449cd122d21cae5", size = 11084902, upload-time = "2024-09-04T06:37:14.647Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/ff/77bee5df55f034ee81d2e1bc58b2b8511b9c54f06ce6566cb562c5d95aa5/onnxruntime-1.19.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d863e8acdc7232d705d49e41087e10b274c42f09e259016a46f32c34e06dc4fd", size = 16779187, upload-time = "2024-09-04T06:37:18.245Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/78/e29f5fb76e0f6524f3520e8e5b9d53282784b45d14068c5112db9f712b0a/onnxruntime-1.19.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1dfe4f660a71b31caa81fc298a25f9612815215a47b286236e61d540350d7b6", size = 11496005, upload-time = "2024-09-04T06:37:20.998Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/ce/be4152da5c1030ab5a159a4a792ed9abad6ba498d79ef0aeba593ff7b5bf/onnxruntime-1.19.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a36511dc07c5c964b916697e42e366fa43c48cdb3d3503578d78cef30417cb84", size = 13167809, upload-time = "2024-09-04T06:37:24.221Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/00/9740a074eb0e0a21ff13a2c4f32aecc5b21110b2c9b9177d8ac132b66e2d/onnxruntime-1.19.2-cp311-cp311-win32.whl", hash = "sha256:50cbb8dc69d6befad4746a69760e5b00cc3ff0a59c6c3fb27f8afa20e2cab7e7", size = 9591445, upload-time = "2024-09-04T06:37:26.766Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/f5/9d995a685f97508b3254f17015b4a78641b0625e79480a7aed7a7a105d7c/onnxruntime-1.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:1c3e5d415b78337fa0b1b75291e9ea9fb2a4c1f148eb5811e7212fed02cfffa8", size = 11085695, upload-time = "2024-09-04T06:37:29.473Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/a5/2a02687a88fc8a2507bef65876c90e96b9f8de5ba1f810acbf67c140fc67/onnxruntime-1.19.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:68e7051bef9cfefcbb858d2d2646536829894d72a4130c24019219442b1dd2ed", size = 16790434, upload-time = "2024-09-04T06:37:32.77Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/64/da42254ec14452cad2cdd4cf407094841c0a378c0d08944e9a36172197e9/onnxruntime-1.19.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2d366fbcc205ce68a8a3bde2185fd15c604d9645888703785b61ef174265168", size = 11486028, upload-time = "2024-09-04T06:37:35.364Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/92/3574f6836f33b1b25f272293e72538c38451b12c2d9aa08630bb6bc0f057/onnxruntime-1.19.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:477b93df4db467e9cbf34051662a4b27c18e131fa1836e05974eae0d6e4cf29b", size = 13175054, upload-time = "2024-09-04T06:37:38.192Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/c9/8c37e413a830cac7f7dc094fffbd0c998c8bcb66a6f0b0a3201a49bc742b/onnxruntime-1.19.2-cp312-cp312-win32.whl", hash = "sha256:9a174073dc5608fad05f7cf7f320b52e8035e73d80b0a23c80f840e5a97c0147", size = 9592681, upload-time = "2024-09-04T06:37:41.328Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/c0/59768846533786a82cafb38d8d2f900ad666bc91f0ae634774d286fa3c47/onnxruntime-1.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:190103273ea4507638ffc31d66a980594b237874b65379e273125150eb044857", size = 11086411, upload-time = "2024-09-04T06:37:44.123Z" }, ] [[package]] name = "onnxruntime-gpu" version = "1.19.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "coloredlogs", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "flatbuffers", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, @@ -3966,18 +3966,18 @@ dependencies = [ { name = "sympy", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d0/9c/3fa310e0730643051eb88e884f19813a6c8b67d0fbafcda610d960e589db/onnxruntime_gpu-1.19.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a49740e079e7c5215830d30cde3df792e903df007aa0b0fd7aa797937061b27a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/82/95e3446724f9e99299c40495d5e04cb7cb319c3a4836c724dbdceb2facd9/onnxruntime_gpu-1.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:b895920bb5e4241299f68874e0becdc2635ea0142939c11e7ff5ae5b28993613" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/33/06e856502a1d482532cfa7d4c7ca775dfddcd851c7bd1833f5177e567055/onnxruntime_gpu-1.19.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:562fc7c755393eaad9751e56149339dd201ffbfdb3ef5f43ff21d0619ba9045f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/0f/d0f728c950b23d8293ceb2f261e16b8a83d967f979b4834d26a86d609a94/onnxruntime_gpu-1.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:522f7495918176cb8c1a3c78bde7152d984f7096acc786c73a27643af8af87c9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/55/49e5b4b4d6e9a8841dcdec2f102069716b626bf6ce9640b832a9497504eb/onnxruntime_gpu-1.19.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:554a02a3fac0119707eb87327908afd21c4e6f0fa5bf9a034398f098adc316c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/5a/0f700aaff26be2c17c9ababba125d79094742d99afe7c24a020010cf2569/onnxruntime_gpu-1.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c6165a405027e3c0f11d189ae7013b5d66919b3381f9bfb3405c0c0cf07968" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/9c/3fa310e0730643051eb88e884f19813a6c8b67d0fbafcda610d960e589db/onnxruntime_gpu-1.19.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a49740e079e7c5215830d30cde3df792e903df007aa0b0fd7aa797937061b27a", size = 226178508, upload-time = "2024-09-04T06:43:40.83Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/82/95e3446724f9e99299c40495d5e04cb7cb319c3a4836c724dbdceb2facd9/onnxruntime_gpu-1.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:b895920bb5e4241299f68874e0becdc2635ea0142939c11e7ff5ae5b28993613", size = 226376693, upload-time = "2024-09-04T06:43:53.348Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/33/06e856502a1d482532cfa7d4c7ca775dfddcd851c7bd1833f5177e567055/onnxruntime_gpu-1.19.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:562fc7c755393eaad9751e56149339dd201ffbfdb3ef5f43ff21d0619ba9045f", size = 226175096, upload-time = "2024-09-04T06:44:07.847Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/0f/d0f728c950b23d8293ceb2f261e16b8a83d967f979b4834d26a86d609a94/onnxruntime_gpu-1.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:522f7495918176cb8c1a3c78bde7152d984f7096acc786c73a27643af8af87c9", size = 226377266, upload-time = "2024-09-04T06:44:21.548Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/55/49e5b4b4d6e9a8841dcdec2f102069716b626bf6ce9640b832a9497504eb/onnxruntime_gpu-1.19.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:554a02a3fac0119707eb87327908afd21c4e6f0fa5bf9a034398f098adc316c5", size = 226163139, upload-time = "2024-09-04T06:44:34.076Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/5a/0f700aaff26be2c17c9ababba125d79094742d99afe7c24a020010cf2569/onnxruntime_gpu-1.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c6165a405027e3c0f11d189ae7013b5d66919b3381f9bfb3405c0c0cf07968", size = 226370340, upload-time = "2024-09-04T06:44:46.276Z" }, ] [[package]] name = "openai" version = "1.99.9" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, @@ -3988,85 +3988,85 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8a/d2/ef89c6f3f36b13b06e271d3cc984ddd2f62508a0972c1cbcc8485a6644ff/openai-1.99.9.tar.gz", hash = "sha256:f2082d155b1ad22e83247c3de3958eb4255b20ccf4a1de2e6681b6957b554e92" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/d2/ef89c6f3f36b13b06e271d3cc984ddd2f62508a0972c1cbcc8485a6644ff/openai-1.99.9.tar.gz", hash = "sha256:f2082d155b1ad22e83247c3de3958eb4255b20ccf4a1de2e6681b6957b554e92", size = 506992, upload-time = "2025-08-12T02:31:10.054Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e8/fb/df274ca10698ee77b07bff952f302ea627cc12dac6b85289485dd77db6de/openai-1.99.9-py3-none-any.whl", hash = "sha256:9dbcdb425553bae1ac5d947147bebbd630d91bbfc7788394d4c4f3a35682ab3a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/fb/df274ca10698ee77b07bff952f302ea627cc12dac6b85289485dd77db6de/openai-1.99.9-py3-none-any.whl", hash = "sha256:9dbcdb425553bae1ac5d947147bebbd630d91bbfc7788394d4c4f3a35682ab3a", size = 786816, upload-time = "2025-08-12T02:31:08.34Z" }, ] [[package]] name = "opencv-python" version = "4.10.0.84" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4a/e7/b70a2d9ab205110d715906fc8ec83fbb00404aeb3a37a0654fdb68eb0c8c/opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/e7/b70a2d9ab205110d715906fc8ec83fbb00404aeb3a37a0654fdb68eb0c8c/opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526", size = 95103981, upload-time = "2024-06-17T18:29:56.757Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/66/82/564168a349148298aca281e342551404ef5521f33fba17b388ead0a84dc5/opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251" }, - { url = "https://mirrors.aliyun.com/pypi/packages/64/4a/016cda9ad7cf18c58ba074628a4eaae8aa55f3fd06a266398cef8831a5b9/opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/e4/7a987ebecfe5ceaf32db413b67ff18eb3092c598408862fff4d7cc3fd19b/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/a4/d2537f47fd7fcfba966bd806e3ec18e7ee1681056d4b0a9c8d983983e4d5/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1e/39/bbf57e7b9dab623e8773f6ff36385456b7ae7fa9357a5e53db732c347eac/opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/6c/fab8113424af5049f85717e8e527ca3773299a3c6b02506e66436e19874f/opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/82/564168a349148298aca281e342551404ef5521f33fba17b388ead0a84dc5/opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251", size = 54835524, upload-time = "2024-06-18T04:57:32.973Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/4a/016cda9ad7cf18c58ba074628a4eaae8aa55f3fd06a266398cef8831a5b9/opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98", size = 56475426, upload-time = "2024-06-17T19:34:10.927Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/e4/7a987ebecfe5ceaf32db413b67ff18eb3092c598408862fff4d7cc3fd19b/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6", size = 41746971, upload-time = "2024-06-17T20:00:25.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/a4/d2537f47fd7fcfba966bd806e3ec18e7ee1681056d4b0a9c8d983983e4d5/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f", size = 62548253, upload-time = "2024-06-17T18:29:43.659Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/39/bbf57e7b9dab623e8773f6ff36385456b7ae7fa9357a5e53db732c347eac/opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236", size = 28737688, upload-time = "2024-06-17T18:28:13.177Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/6c/fab8113424af5049f85717e8e527ca3773299a3c6b02506e66436e19874f/opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe", size = 38842521, upload-time = "2024-06-17T18:28:21.813Z" }, ] [[package]] name = "opencv-python-headless" version = "4.10.0.84" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2f/7e/d20f68a5f1487adf19d74378d349932a386b1ece3be9be9915e5986db468/opencv-python-headless-4.10.0.84.tar.gz", hash = "sha256:f2017c6101d7c2ef8d7bc3b414c37ff7f54d64413a1847d89970b6b7069b4e1a" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/7e/d20f68a5f1487adf19d74378d349932a386b1ece3be9be9915e5986db468/opencv-python-headless-4.10.0.84.tar.gz", hash = "sha256:f2017c6101d7c2ef8d7bc3b414c37ff7f54d64413a1847d89970b6b7069b4e1a", size = 95117755, upload-time = "2024-06-17T18:32:15.606Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/1c/9b/583c8d9259f6fc19413f83fd18dd8e6cbc8eefb0b4dc6da52dd151fe3272/opencv_python_headless-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a4f4bcb07d8f8a7704d9c8564c224c8b064c63f430e95b61ac0bffaa374d330e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/7b/b4c67f5dad7a9a61c47f7a39e4050e8a4628bd64b3c3daaeb755d759f928/opencv_python_headless-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:5ae454ebac0eb0a0b932e3406370aaf4212e6a3fdb5038cc86c7aea15a6851da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/61/f838ce2046f3ec3591ea59ea3549085e399525d3b4558c4ed60b55ed88c0/opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46071015ff9ab40fccd8a163da0ee14ce9846349f06c6c8c0f2870856ffa45db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/09/248f86a404567303cdf120e4a301f389b68e3b18e5c0cc428de327da609c/opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:377d08a7e48a1405b5e84afcbe4798464ce7ee17081c1c23619c8b398ff18295" }, - { url = "https://mirrors.aliyun.com/pypi/packages/30/c0/66f88d58500e990a9a0a5c06f98862edf1d0a3a430781218a8c193948438/opencv_python_headless-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:9092404b65458ed87ce932f613ffbb1106ed2c843577501e5768912360fc50ec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/26/d0/22f68eb23eea053a31655960f133c0be9726c6a881547e6e9e7e2a946c4f/opencv_python_headless-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:afcf28bd1209dd58810d33defb622b325d3cbe49dcd7a43a902982c33e5fad05" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/9b/583c8d9259f6fc19413f83fd18dd8e6cbc8eefb0b4dc6da52dd151fe3272/opencv_python_headless-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a4f4bcb07d8f8a7704d9c8564c224c8b064c63f430e95b61ac0bffaa374d330e", size = 54835657, upload-time = "2024-06-18T04:58:12.904Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/7b/b4c67f5dad7a9a61c47f7a39e4050e8a4628bd64b3c3daaeb755d759f928/opencv_python_headless-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:5ae454ebac0eb0a0b932e3406370aaf4212e6a3fdb5038cc86c7aea15a6851da", size = 56475470, upload-time = "2024-06-17T19:34:39.604Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/61/f838ce2046f3ec3591ea59ea3549085e399525d3b4558c4ed60b55ed88c0/opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46071015ff9ab40fccd8a163da0ee14ce9846349f06c6c8c0f2870856ffa45db", size = 29329705, upload-time = "2024-06-17T20:00:49.406Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/09/248f86a404567303cdf120e4a301f389b68e3b18e5c0cc428de327da609c/opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:377d08a7e48a1405b5e84afcbe4798464ce7ee17081c1c23619c8b398ff18295", size = 49858781, upload-time = "2024-06-17T18:31:49.495Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/c0/66f88d58500e990a9a0a5c06f98862edf1d0a3a430781218a8c193948438/opencv_python_headless-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:9092404b65458ed87ce932f613ffbb1106ed2c843577501e5768912360fc50ec", size = 28675298, upload-time = "2024-06-17T18:28:56.897Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/d0/22f68eb23eea053a31655960f133c0be9726c6a881547e6e9e7e2a946c4f/opencv_python_headless-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:afcf28bd1209dd58810d33defb622b325d3cbe49dcd7a43a902982c33e5fad05", size = 38754031, upload-time = "2024-06-17T18:29:04.871Z" }, ] [[package]] name = "opendal" version = "0.45.20" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2f/3f/927dfe1349ae58b9238b8eafba747af648d660a9425f486dda01a10f0b78/opendal-0.45.20.tar.gz", hash = "sha256:9f6f90d9e9f9d6e9e5a34aa7729169ef34d2f1869ad1e01ddc39b1c0ce0c9405" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f7/d9/b74575762bd9178b0498125f270268e0fb122ee991188e053048da7f002c/opendal-0.45.20-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d6069cef67f501eda221da63320bd1291aee967f5f8678ccee9e6e566ab37c78" }, - { url = "https://mirrors.aliyun.com/pypi/packages/56/f6/0af7d8a4afe5bae6222c4715f0563fa8c257f0525802da47120e28314353/opendal-0.45.20-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52c4bf9433a3fa17d1f7b18f386a8f601c4b41e3fae9a839d0a861867d6086a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/16/cf0cfc0838c7837f5642824738ad57f84cee658b4cfdd2b25fdfb52ca8a7/opendal-0.45.20-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:088bc9b20c5f07bbb19a9ff45c32dd3d42cf2d0b4ef40a2319ca27cdc635bf0f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/76/e903436877895fcf948e36aa728b4b56a3a600c4fd3297d8e4bc38a843be/opendal-0.45.20-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:55efb4388fa03f309de497bf9b9854377fc4045da069c72c9d2df21d24c686cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/10/7863a90a592ed6bfb2ddde104db23a00586004e2197f86a255ad9f8a9401/opendal-0.45.20-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:49c966cda40dc6b7b100ea6150d2f29e01ed7db694c5a5168c5fc451872ec77c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/a3/b77497101e320bcaebb7e99c43d61ca1886795c6a83001d4426cdbc3683d/opendal-0.45.20-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:e81af55e1d8c145119dfa4c9cacd1fd60c1c1fba2207ec5064cb6baae8c3c86b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/36/21495e4a405d47ece52df98c323ba9467f43e0641e04819ab5732bf0f370/opendal-0.45.20-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdfcb6840ab8bbd29c36a2a329c1f691023b3cd6a26f8a285dc89f39526017" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/28/bb822cad3f3ef15836227751dad46554c499bbefcf0eb34b4cc7e9975e9b/opendal-0.45.20-cp310-cp310-win_amd64.whl", hash = "sha256:e3987c4766a3611ea8cb3a216f21d083ac3e7fa91eb2ff7c0ebe5dc6e6958cce" }, - { url = "https://mirrors.aliyun.com/pypi/packages/84/77/6427e16b8630f0cc71f4a1b01648ed3264f1e04f1f6d9b5d09e5c6a4dd2f/opendal-0.45.20-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35acdd8001e4a741532834fdbff3020ffb10b40028bb49fbe93c4f8197d66d8c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/12/1f/83e415334739f1ab4dba55cdd349abf0b66612249055afb422a354b96ac8/opendal-0.45.20-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:629bfe8d384364bced6cbeb01f49b99779fa5151c68048a1869ff645ddcfcb25" }, - { url = "https://mirrors.aliyun.com/pypi/packages/49/94/c5de6ed54a02d7413636c2ccefa71d8dd09c2ada1cd6ecab202feb1fdeda/opendal-0.45.20-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12cc5ac7e441fb93d86d1673112d9fb08580fc3226f864434f4a56a72efec53" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/83/713a1e1de8cbbd69af50e26644bbdeef3c1068b89f442417376fa3c0f591/opendal-0.45.20-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:45a3adae1f473052234fc4054a6f210df3ded9aff10db8d545d0a37eff3b13cc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/78/c9651e753aaf6eb61887ca372a3f9c2ae57dae03c3159d24deaf018c26dc/opendal-0.45.20-cp311-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d8947857052c85a4b0e251d50e23f5f68f0cdd9e509e32e614a5e4b2fc7424c4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/9d/5d8c20c0fc93df5e349e5694167de30afdc54c5755704cc64764a6cbb309/opendal-0.45.20-cp311-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:891d2f9114efeef648973049ed15e56477e8feb9e48b540bd8d6105ea22a253c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/39/05262f748a2085522e0c85f03eab945589313dc9caedc002872c39162776/opendal-0.45.20-cp311-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:539de9b825f6783d6289d88c0c9ac5415daa4d892d761e3540c565bda51e8997" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/83/cc7c6de29b0a7585cd445258d174ca204d37729c3874ad08e515b0bf331c/opendal-0.45.20-cp311-abi3-win_amd64.whl", hash = "sha256:145efd56aa33b493d5b652c3e4f5ae5097ab69d38c132d80f108e9f5c1e4d863" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/3f/927dfe1349ae58b9238b8eafba747af648d660a9425f486dda01a10f0b78/opendal-0.45.20.tar.gz", hash = "sha256:9f6f90d9e9f9d6e9e5a34aa7729169ef34d2f1869ad1e01ddc39b1c0ce0c9405", size = 990267, upload-time = "2025-05-26T07:02:11.819Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/d9/b74575762bd9178b0498125f270268e0fb122ee991188e053048da7f002c/opendal-0.45.20-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d6069cef67f501eda221da63320bd1291aee967f5f8678ccee9e6e566ab37c78", size = 26875698, upload-time = "2025-05-26T07:00:58.995Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/f6/0af7d8a4afe5bae6222c4715f0563fa8c257f0525802da47120e28314353/opendal-0.45.20-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52c4bf9433a3fa17d1f7b18f386a8f601c4b41e3fae9a839d0a861867d6086a", size = 12990070, upload-time = "2025-05-26T07:01:02.78Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/16/cf0cfc0838c7837f5642824738ad57f84cee658b4cfdd2b25fdfb52ca8a7/opendal-0.45.20-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:088bc9b20c5f07bbb19a9ff45c32dd3d42cf2d0b4ef40a2319ca27cdc635bf0f", size = 14396627, upload-time = "2025-05-26T07:01:05.913Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/76/e903436877895fcf948e36aa728b4b56a3a600c4fd3297d8e4bc38a843be/opendal-0.45.20-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:55efb4388fa03f309de497bf9b9854377fc4045da069c72c9d2df21d24c686cb", size = 13432079, upload-time = "2025-05-26T07:01:09.086Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/10/7863a90a592ed6bfb2ddde104db23a00586004e2197f86a255ad9f8a9401/opendal-0.45.20-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:49c966cda40dc6b7b100ea6150d2f29e01ed7db694c5a5168c5fc451872ec77c", size = 13629580, upload-time = "2025-05-26T07:01:12.144Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/a3/b77497101e320bcaebb7e99c43d61ca1886795c6a83001d4426cdbc3683d/opendal-0.45.20-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:e81af55e1d8c145119dfa4c9cacd1fd60c1c1fba2207ec5064cb6baae8c3c86b", size = 13316269, upload-time = "2025-05-26T07:01:15.058Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/36/21495e4a405d47ece52df98c323ba9467f43e0641e04819ab5732bf0f370/opendal-0.45.20-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdfcb6840ab8bbd29c36a2a329c1f691023b3cd6a26f8a285dc89f39526017", size = 14576544, upload-time = "2025-05-26T07:01:18.227Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/28/bb822cad3f3ef15836227751dad46554c499bbefcf0eb34b4cc7e9975e9b/opendal-0.45.20-cp310-cp310-win_amd64.whl", hash = "sha256:e3987c4766a3611ea8cb3a216f21d083ac3e7fa91eb2ff7c0ebe5dc6e6958cce", size = 14944417, upload-time = "2025-05-26T07:01:21.531Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/77/6427e16b8630f0cc71f4a1b01648ed3264f1e04f1f6d9b5d09e5c6a4dd2f/opendal-0.45.20-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:35acdd8001e4a741532834fdbff3020ffb10b40028bb49fbe93c4f8197d66d8c", size = 26910966, upload-time = "2025-05-26T07:01:24.987Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/1f/83e415334739f1ab4dba55cdd349abf0b66612249055afb422a354b96ac8/opendal-0.45.20-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:629bfe8d384364bced6cbeb01f49b99779fa5151c68048a1869ff645ddcfcb25", size = 13002770, upload-time = "2025-05-26T07:01:30.385Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/94/c5de6ed54a02d7413636c2ccefa71d8dd09c2ada1cd6ecab202feb1fdeda/opendal-0.45.20-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12cc5ac7e441fb93d86d1673112d9fb08580fc3226f864434f4a56a72efec53", size = 14387218, upload-time = "2025-05-26T07:01:33.017Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/83/713a1e1de8cbbd69af50e26644bbdeef3c1068b89f442417376fa3c0f591/opendal-0.45.20-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:45a3adae1f473052234fc4054a6f210df3ded9aff10db8d545d0a37eff3b13cc", size = 13424302, upload-time = "2025-05-26T07:01:36.417Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/78/c9651e753aaf6eb61887ca372a3f9c2ae57dae03c3159d24deaf018c26dc/opendal-0.45.20-cp311-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d8947857052c85a4b0e251d50e23f5f68f0cdd9e509e32e614a5e4b2fc7424c4", size = 13622483, upload-time = "2025-05-26T07:01:38.886Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/9d/5d8c20c0fc93df5e349e5694167de30afdc54c5755704cc64764a6cbb309/opendal-0.45.20-cp311-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:891d2f9114efeef648973049ed15e56477e8feb9e48b540bd8d6105ea22a253c", size = 13320229, upload-time = "2025-05-26T07:01:41.965Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/39/05262f748a2085522e0c85f03eab945589313dc9caedc002872c39162776/opendal-0.45.20-cp311-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:539de9b825f6783d6289d88c0c9ac5415daa4d892d761e3540c565bda51e8997", size = 14574280, upload-time = "2025-05-26T07:01:44.413Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/83/cc7c6de29b0a7585cd445258d174ca204d37729c3874ad08e515b0bf331c/opendal-0.45.20-cp311-abi3-win_amd64.whl", hash = "sha256:145efd56aa33b493d5b652c3e4f5ae5097ab69d38c132d80f108e9f5c1e4d863", size = 14929888, upload-time = "2025-05-26T07:01:46.929Z" }, ] [[package]] name = "openpyxl" version = "3.1.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "et-xmlfile" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, ] [[package]] name = "opensearch-py" version = "2.7.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "certifi" }, { name = "events" }, @@ -4074,53 +4074,53 @@ dependencies = [ { name = "requests" }, { name = "urllib3" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c4/ca/5be52de5c69ecd327c16f3fc0dba82b7ffda5bbd0c0e215bdf23a4d12b12/opensearch_py-2.7.1.tar.gz", hash = "sha256:67ab76e9373669bc71da417096df59827c08369ac3795d5438c9a8be21cbd759" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/ca/5be52de5c69ecd327c16f3fc0dba82b7ffda5bbd0c0e215bdf23a4d12b12/opensearch_py-2.7.1.tar.gz", hash = "sha256:67ab76e9373669bc71da417096df59827c08369ac3795d5438c9a8be21cbd759", size = 226630, upload-time = "2024-08-22T16:12:36.455Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/80/8f/db678ae203d761922a73920215ea53a79faf3bb1ec6aa9511f809c8e234c/opensearch_py-2.7.1-py3-none-any.whl", hash = "sha256:5417650eba98a1c7648e502207cebf3a12beab623ffe0ebbf55f9b1b4b6e44e9" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/8f/db678ae203d761922a73920215ea53a79faf3bb1ec6aa9511f809c8e234c/opensearch_py-2.7.1-py3-none-any.whl", hash = "sha256:5417650eba98a1c7648e502207cebf3a12beab623ffe0ebbf55f9b1b4b6e44e9", size = 325380, upload-time = "2024-08-22T16:12:34.67Z" }, ] [[package]] name = "opentelemetry-api" version = "1.36.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/27/d2/c782c88b8afbf961d6972428821c302bd1e9e7bc361352172f0ca31296e2/opentelemetry_api-1.36.0.tar.gz", hash = "sha256:9a72572b9c416d004d492cbc6e61962c0501eaf945ece9b5a0f56597d8348aa0" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/d2/c782c88b8afbf961d6972428821c302bd1e9e7bc361352172f0ca31296e2/opentelemetry_api-1.36.0.tar.gz", hash = "sha256:9a72572b9c416d004d492cbc6e61962c0501eaf945ece9b5a0f56597d8348aa0", size = 64780, upload-time = "2025-07-29T15:12:06.02Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/bb/ee/6b08dde0a022c463b88f55ae81149584b125a42183407dc1045c486cc870/opentelemetry_api-1.36.0-py3-none-any.whl", hash = "sha256:02f20bcacf666e1333b6b1f04e647dc1d5111f86b8e510238fcc56d7762cda8c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/ee/6b08dde0a022c463b88f55ae81149584b125a42183407dc1045c486cc870/opentelemetry_api-1.36.0-py3-none-any.whl", hash = "sha256:02f20bcacf666e1333b6b1f04e647dc1d5111f86b8e510238fcc56d7762cda8c", size = 65564, upload-time = "2025-07-29T15:11:47.998Z" }, ] [[package]] name = "opentelemetry-exporter-otlp" version = "1.36.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "opentelemetry-exporter-otlp-proto-grpc" }, { name = "opentelemetry-exporter-otlp-proto-http" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/7f/d31294ac28d567a14aefd855756bab79fed69c5a75df712f228f10c47e04/opentelemetry_exporter_otlp-1.36.0.tar.gz", hash = "sha256:72f166ea5a8923ac42889337f903e93af57db8893de200369b07401e98e4e06b" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/7f/d31294ac28d567a14aefd855756bab79fed69c5a75df712f228f10c47e04/opentelemetry_exporter_otlp-1.36.0.tar.gz", hash = "sha256:72f166ea5a8923ac42889337f903e93af57db8893de200369b07401e98e4e06b", size = 6144, upload-time = "2025-07-29T15:12:07.153Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a0/a2/8966111a285124f3d6156a663ddf2aeddd52843c1a3d6b56cbd9b6c3fd0e/opentelemetry_exporter_otlp-1.36.0-py3-none-any.whl", hash = "sha256:de93b7c45bcc78296998775d52add7c63729e83ef2cd6560730a6b336d7f6494" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/a2/8966111a285124f3d6156a663ddf2aeddd52843c1a3d6b56cbd9b6c3fd0e/opentelemetry_exporter_otlp-1.36.0-py3-none-any.whl", hash = "sha256:de93b7c45bcc78296998775d52add7c63729e83ef2cd6560730a6b336d7f6494", size = 7018, upload-time = "2025-07-29T15:11:50.498Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" version = "1.36.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/34/da/7747e57eb341c59886052d733072bc878424bf20f1d8cf203d508bbece5b/opentelemetry_exporter_otlp_proto_common-1.36.0.tar.gz", hash = "sha256:6c496ccbcbe26b04653cecadd92f73659b814c6e3579af157d8716e5f9f25cbf" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/da/7747e57eb341c59886052d733072bc878424bf20f1d8cf203d508bbece5b/opentelemetry_exporter_otlp_proto_common-1.36.0.tar.gz", hash = "sha256:6c496ccbcbe26b04653cecadd92f73659b814c6e3579af157d8716e5f9f25cbf", size = 20302, upload-time = "2025-07-29T15:12:07.71Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d0/ed/22290dca7db78eb32e0101738366b5bbda00d0407f00feffb9bf8c3fdf87/opentelemetry_exporter_otlp_proto_common-1.36.0-py3-none-any.whl", hash = "sha256:0fc002a6ed63eac235ada9aa7056e5492e9a71728214a61745f6ad04b923f840" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/ed/22290dca7db78eb32e0101738366b5bbda00d0407f00feffb9bf8c3fdf87/opentelemetry_exporter_otlp_proto_common-1.36.0-py3-none-any.whl", hash = "sha256:0fc002a6ed63eac235ada9aa7056e5492e9a71728214a61745f6ad04b923f840", size = 18349, upload-time = "2025-07-29T15:11:51.327Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" version = "1.36.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, @@ -4130,15 +4130,15 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/72/6f/6c1b0bdd0446e5532294d1d41bf11fbaea39c8a2423a4cdfe4fe6b708127/opentelemetry_exporter_otlp_proto_grpc-1.36.0.tar.gz", hash = "sha256:b281afbf7036b325b3588b5b6c8bb175069e3978d1bd24071f4a59d04c1e5bbf" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/6f/6c1b0bdd0446e5532294d1d41bf11fbaea39c8a2423a4cdfe4fe6b708127/opentelemetry_exporter_otlp_proto_grpc-1.36.0.tar.gz", hash = "sha256:b281afbf7036b325b3588b5b6c8bb175069e3978d1bd24071f4a59d04c1e5bbf", size = 23822, upload-time = "2025-07-29T15:12:08.292Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0c/67/5f6bd188d66d0fd8e81e681bbf5822e53eb150034e2611dd2b935d3ab61a/opentelemetry_exporter_otlp_proto_grpc-1.36.0-py3-none-any.whl", hash = "sha256:734e841fc6a5d6f30e7be4d8053adb703c70ca80c562ae24e8083a28fadef211" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/67/5f6bd188d66d0fd8e81e681bbf5822e53eb150034e2611dd2b935d3ab61a/opentelemetry_exporter_otlp_proto_grpc-1.36.0-py3-none-any.whl", hash = "sha256:734e841fc6a5d6f30e7be4d8053adb703c70ca80c562ae24e8083a28fadef211", size = 18828, upload-time = "2025-07-29T15:11:52.235Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" version = "1.36.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "opentelemetry-api" }, @@ -4148,199 +4148,199 @@ dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/25/85/6632e7e5700ba1ce5b8a065315f92c1e6d787ccc4fb2bdab15139eaefc82/opentelemetry_exporter_otlp_proto_http-1.36.0.tar.gz", hash = "sha256:dd3637f72f774b9fc9608ab1ac479f8b44d09b6fb5b2f3df68a24ad1da7d356e" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/85/6632e7e5700ba1ce5b8a065315f92c1e6d787ccc4fb2bdab15139eaefc82/opentelemetry_exporter_otlp_proto_http-1.36.0.tar.gz", hash = "sha256:dd3637f72f774b9fc9608ab1ac479f8b44d09b6fb5b2f3df68a24ad1da7d356e", size = 16213, upload-time = "2025-07-29T15:12:08.932Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7f/41/a680d38b34f8f5ddbd78ed9f0042e1cc712d58ec7531924d71cb1e6c629d/opentelemetry_exporter_otlp_proto_http-1.36.0-py3-none-any.whl", hash = "sha256:3d769f68e2267e7abe4527f70deb6f598f40be3ea34c6adc35789bea94a32902" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/41/a680d38b34f8f5ddbd78ed9f0042e1cc712d58ec7531924d71cb1e6c629d/opentelemetry_exporter_otlp_proto_http-1.36.0-py3-none-any.whl", hash = "sha256:3d769f68e2267e7abe4527f70deb6f598f40be3ea34c6adc35789bea94a32902", size = 18752, upload-time = "2025-07-29T15:11:53.164Z" }, ] [[package]] name = "opentelemetry-proto" version = "1.36.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fd/02/f6556142301d136e3b7e95ab8ea6a5d9dc28d879a99f3dd673b5f97dca06/opentelemetry_proto-1.36.0.tar.gz", hash = "sha256:0f10b3c72f74c91e0764a5ec88fd8f1c368ea5d9c64639fb455e2854ef87dd2f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/02/f6556142301d136e3b7e95ab8ea6a5d9dc28d879a99f3dd673b5f97dca06/opentelemetry_proto-1.36.0.tar.gz", hash = "sha256:0f10b3c72f74c91e0764a5ec88fd8f1c368ea5d9c64639fb455e2854ef87dd2f", size = 46152, upload-time = "2025-07-29T15:12:15.717Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b3/57/3361e06136225be8180e879199caea520f38026f8071366241ac458beb8d/opentelemetry_proto-1.36.0-py3-none-any.whl", hash = "sha256:151b3bf73a09f94afc658497cf77d45a565606f62ce0c17acb08cd9937ca206e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/57/3361e06136225be8180e879199caea520f38026f8071366241ac458beb8d/opentelemetry_proto-1.36.0-py3-none-any.whl", hash = "sha256:151b3bf73a09f94afc658497cf77d45a565606f62ce0c17acb08cd9937ca206e", size = 72537, upload-time = "2025-07-29T15:12:02.243Z" }, ] [[package]] name = "opentelemetry-sdk" version = "1.36.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4c/85/8567a966b85a2d3f971c4d42f781c305b2b91c043724fa08fd37d158e9dc/opentelemetry_sdk-1.36.0.tar.gz", hash = "sha256:19c8c81599f51b71670661ff7495c905d8fdf6976e41622d5245b791b06fa581" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/85/8567a966b85a2d3f971c4d42f781c305b2b91c043724fa08fd37d158e9dc/opentelemetry_sdk-1.36.0.tar.gz", hash = "sha256:19c8c81599f51b71670661ff7495c905d8fdf6976e41622d5245b791b06fa581", size = 162557, upload-time = "2025-07-29T15:12:16.76Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0b/59/7bed362ad1137ba5886dac8439e84cd2df6d087be7c09574ece47ae9b22c/opentelemetry_sdk-1.36.0-py3-none-any.whl", hash = "sha256:19fe048b42e98c5c1ffe85b569b7073576ad4ce0bcb6e9b4c6a39e890a6c45fb" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/59/7bed362ad1137ba5886dac8439e84cd2df6d087be7c09574ece47ae9b22c/opentelemetry_sdk-1.36.0-py3-none-any.whl", hash = "sha256:19fe048b42e98c5c1ffe85b569b7073576ad4ce0bcb6e9b4c6a39e890a6c45fb", size = 119995, upload-time = "2025-07-29T15:12:03.181Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" version = "0.57b0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7e/31/67dfa252ee88476a29200b0255bda8dfc2cf07b56ad66dc9a6221f7dc787/opentelemetry_semantic_conventions-0.57b0.tar.gz", hash = "sha256:609a4a79c7891b4620d64c7aac6898f872d790d75f22019913a660756f27ff32" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/31/67dfa252ee88476a29200b0255bda8dfc2cf07b56ad66dc9a6221f7dc787/opentelemetry_semantic_conventions-0.57b0.tar.gz", hash = "sha256:609a4a79c7891b4620d64c7aac6898f872d790d75f22019913a660756f27ff32", size = 124225, upload-time = "2025-07-29T15:12:17.873Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/05/75/7d591371c6c39c73de5ce5da5a2cc7b72d1d1cd3f8f4638f553c01c37b11/opentelemetry_semantic_conventions-0.57b0-py3-none-any.whl", hash = "sha256:757f7e76293294f124c827e514c2a3144f191ef175b069ce8d1211e1e38e9e78" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/75/7d591371c6c39c73de5ce5da5a2cc7b72d1d1cd3f8f4638f553c01c37b11/opentelemetry_semantic_conventions-0.57b0-py3-none-any.whl", hash = "sha256:757f7e76293294f124c827e514c2a3144f191ef175b069ce8d1211e1e38e9e78", size = 201627, upload-time = "2025-07-29T15:12:04.174Z" }, ] [[package]] name = "orjson" version = "3.10.18" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469" }, - { url = "https://mirrors.aliyun.com/pypi/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92" }, - { url = "https://mirrors.aliyun.com/pypi/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17" }, - { url = "https://mirrors.aliyun.com/pypi/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad" }, - { url = "https://mirrors.aliyun.com/pypi/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406" }, - { url = "https://mirrors.aliyun.com/pypi/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810, upload-time = "2025-04-29T23:30:08.423Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927, upload-time = "2025-04-29T23:28:08.643Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995, upload-time = "2025-04-29T23:28:11.503Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893, upload-time = "2025-04-29T23:28:12.751Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017, upload-time = "2025-04-29T23:28:14.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290, upload-time = "2025-04-29T23:28:16.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828, upload-time = "2025-04-29T23:28:18.065Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806, upload-time = "2025-04-29T23:28:19.782Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005, upload-time = "2025-04-29T23:28:21.367Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418, upload-time = "2025-04-29T23:28:23.097Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288, upload-time = "2025-04-29T23:28:25.02Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181, upload-time = "2025-04-29T23:28:26.318Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694, upload-time = "2025-04-29T23:28:28.092Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600, upload-time = "2025-04-29T23:28:29.422Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929, upload-time = "2025-04-29T23:28:30.716Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364, upload-time = "2025-04-29T23:28:32.392Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995, upload-time = "2025-04-29T23:28:34.024Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894, upload-time = "2025-04-29T23:28:35.318Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016, upload-time = "2025-04-29T23:28:36.674Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290, upload-time = "2025-04-29T23:28:38.3Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829, upload-time = "2025-04-29T23:28:39.657Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805, upload-time = "2025-04-29T23:28:40.969Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008, upload-time = "2025-04-29T23:28:42.284Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419, upload-time = "2025-04-29T23:28:43.673Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292, upload-time = "2025-04-29T23:28:45.573Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182, upload-time = "2025-04-29T23:28:47.229Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695, upload-time = "2025-04-29T23:28:48.564Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603, upload-time = "2025-04-29T23:28:50.442Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400, upload-time = "2025-04-29T23:28:51.838Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184, upload-time = "2025-04-29T23:28:53.612Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279, upload-time = "2025-04-29T23:28:55.055Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799, upload-time = "2025-04-29T23:28:56.828Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791, upload-time = "2025-04-29T23:28:58.751Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059, upload-time = "2025-04-29T23:29:00.129Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359, upload-time = "2025-04-29T23:29:01.704Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853, upload-time = "2025-04-29T23:29:03.576Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131, upload-time = "2025-04-29T23:29:05.753Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834, upload-time = "2025-04-29T23:29:07.35Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368, upload-time = "2025-04-29T23:29:09.301Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359, upload-time = "2025-04-29T23:29:10.813Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466, upload-time = "2025-04-29T23:29:12.26Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683, upload-time = "2025-04-29T23:29:13.865Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754, upload-time = "2025-04-29T23:29:15.338Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218, upload-time = "2025-04-29T23:29:17.324Z" }, ] [[package]] name = "ormsgpack" version = "1.5.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c5/70/11a6ab33136c2f98bb64e96743a55c7a87b87bae0413460cab7cc5764951/ormsgpack-1.5.0.tar.gz", hash = "sha256:00c0743ebaa8d21f1c868fbb609c99151ea79e67fec98b51a29077efd91ce348" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e5/6f/38f1466b866eecc3ad9851573dec366b7eb7f0788725324825561a1e9abd/ormsgpack-1.5.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:98efdbb1f8c4a172a05143bbbc000491ca7d99644521ad90a15d5e96c7895fba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/31/4542b5c2e25218f559722907866c8d00da4fad2842912920a0add257904a/ormsgpack-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091bb019b3101dd034515dce13852f4250aa2ee406e6ac5cb8d745dc1e146e6f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/cd/228d631ef16b640301d99515dc906a4d5339096ca83eb30b7bf64b2623eb/ormsgpack-1.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ee97e618e852891e3cb3ae8a0662d54e289e7ba438fc245e70643c6f0b16849" }, - { url = "https://mirrors.aliyun.com/pypi/packages/53/9b/1fc54fd06f4b46f4ce5041ce3382abe36e7f63f621809925369435494eaa/ormsgpack-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:789725c38493509a1c7a0a25d9baaf2edc120a38a51e0a85008215de25d034c7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/0b/53946f84370eafd9f80ffeeb21ecbeb0caac2ab044c79e33e555fb1b95bc/ormsgpack-1.5.0-cp310-none-win_amd64.whl", hash = "sha256:c81dfcaef16e1a0583f1491441d6f7888b742d72dc299fdeddc3da069774ede4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/5d/86e05c01247081ecae8dfbf8bf160ff4d7656417f6a01bae51d23d60cc2c/ormsgpack-1.5.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a94c8dfe1ee41e536b9c20af48b191fcae89b015b7fcbef0909915c0d5624c8d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3e/36/389f38d3ab157ceeef91971f82eec7058bc7c3453d9d4d5ef29892889761/ormsgpack-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb56792348a674ae8915106bdf7b02720852fc195fdda23884c24a5f05e5fd9a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/db/2c/a51aec641011337813cdca24e5a4eee6f5a2bbc7f489af04144fc9d8a307/ormsgpack-1.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:044856a25c2fa19261f02a6f7a9b5329e90a51cf45780cface01b667cc21ac3d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/66/d33df5efe53dae713dba02876fe179c2e1bfeab7dc1896d4c8cef7d6fb80/ormsgpack-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:111214ea0970eb49b19ffa1fde94996f09efa4d8585986be9d5b81492fa1e3fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/c0/f4270c6622198fc121847a9e9b567e88cc60e84b0125647163e97fa9a89b/ormsgpack-1.5.0-cp311-none-win_amd64.whl", hash = "sha256:e2a28bfc10cb492659c8704007567483aa3cfc863a22fd5973309373f396c9e1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/19/df1626f9c149a20d2273eecf97ae913a026be2730264db86126ac3e594db/ormsgpack-1.5.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a921b0d54b5fb5ba1ea4e87c65caa8992736224f1fc5ce8f46a882e918c8e22d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/82/cc/bad6d4a237ff0943cb1c8c4a12fe95bcd7ff81c0f8bca26340efd599aa1d/ormsgpack-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6d423668e2c3abdbc474562b1c73360ff7326f06cb9532dcb73254b5b63dae4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/d1/3ed38a54923fe04eace750c0f0adbc149fb2b028375c71e864aee5e2d6d6/ormsgpack-1.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb2dd4ed3e503a8266dcbfbb8d810a36baa34e4bb4229e90e9c213058a06d74" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/52/0261a80de2486793b4844c2668b17f49d03a20aba13a8d3d975831b1d866/ormsgpack-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f13bd643df1324e8797caba4c5c0168a87524df8424e8413ba29723e89a586a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/f0/2ebda08824d4f658c5ad048bcbe64e352b637b661b4d26c51d7403d30569/ormsgpack-1.5.0-cp312-none-win_amd64.whl", hash = "sha256:e016da381a126478c4bafab0ae19d3a2537f6471341ecced4bb61471e8841cad" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/70/11a6ab33136c2f98bb64e96743a55c7a87b87bae0413460cab7cc5764951/ormsgpack-1.5.0.tar.gz", hash = "sha256:00c0743ebaa8d21f1c868fbb609c99151ea79e67fec98b51a29077efd91ce348", size = 54353, upload-time = "2024-04-20T07:13:53.382Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/6f/38f1466b866eecc3ad9851573dec366b7eb7f0788725324825561a1e9abd/ormsgpack-1.5.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:98efdbb1f8c4a172a05143bbbc000491ca7d99644521ad90a15d5e96c7895fba", size = 426631, upload-time = "2024-04-20T07:13:09.925Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/31/4542b5c2e25218f559722907866c8d00da4fad2842912920a0add257904a/ormsgpack-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091bb019b3101dd034515dce13852f4250aa2ee406e6ac5cb8d745dc1e146e6f", size = 276769, upload-time = "2024-04-20T07:13:12.213Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/cd/228d631ef16b640301d99515dc906a4d5339096ca83eb30b7bf64b2623eb/ormsgpack-1.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ee97e618e852891e3cb3ae8a0662d54e289e7ba438fc245e70643c6f0b16849", size = 280603, upload-time = "2024-04-20T07:13:14.085Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/9b/1fc54fd06f4b46f4ce5041ce3382abe36e7f63f621809925369435494eaa/ormsgpack-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:789725c38493509a1c7a0a25d9baaf2edc120a38a51e0a85008215de25d034c7", size = 276102, upload-time = "2024-04-20T07:13:15.431Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/0b/53946f84370eafd9f80ffeeb21ecbeb0caac2ab044c79e33e555fb1b95bc/ormsgpack-1.5.0-cp310-none-win_amd64.whl", hash = "sha256:c81dfcaef16e1a0583f1491441d6f7888b742d72dc299fdeddc3da069774ede4", size = 154899, upload-time = "2024-04-20T07:13:17.279Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/5d/86e05c01247081ecae8dfbf8bf160ff4d7656417f6a01bae51d23d60cc2c/ormsgpack-1.5.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a94c8dfe1ee41e536b9c20af48b191fcae89b015b7fcbef0909915c0d5624c8d", size = 426630, upload-time = "2024-04-20T07:13:18.991Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/36/389f38d3ab157ceeef91971f82eec7058bc7c3453d9d4d5ef29892889761/ormsgpack-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb56792348a674ae8915106bdf7b02720852fc195fdda23884c24a5f05e5fd9a", size = 276769, upload-time = "2024-04-20T07:13:21.044Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/2c/a51aec641011337813cdca24e5a4eee6f5a2bbc7f489af04144fc9d8a307/ormsgpack-1.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:044856a25c2fa19261f02a6f7a9b5329e90a51cf45780cface01b667cc21ac3d", size = 280602, upload-time = "2024-04-20T07:13:22.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/66/d33df5efe53dae713dba02876fe179c2e1bfeab7dc1896d4c8cef7d6fb80/ormsgpack-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:111214ea0970eb49b19ffa1fde94996f09efa4d8585986be9d5b81492fa1e3fc", size = 276101, upload-time = "2024-04-20T07:13:24.927Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/c0/f4270c6622198fc121847a9e9b567e88cc60e84b0125647163e97fa9a89b/ormsgpack-1.5.0-cp311-none-win_amd64.whl", hash = "sha256:e2a28bfc10cb492659c8704007567483aa3cfc863a22fd5973309373f396c9e1", size = 154908, upload-time = "2024-04-20T07:13:26.759Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/19/df1626f9c149a20d2273eecf97ae913a026be2730264db86126ac3e594db/ormsgpack-1.5.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a921b0d54b5fb5ba1ea4e87c65caa8992736224f1fc5ce8f46a882e918c8e22d", size = 427447, upload-time = "2024-04-20T07:13:28.226Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/cc/bad6d4a237ff0943cb1c8c4a12fe95bcd7ff81c0f8bca26340efd599aa1d/ormsgpack-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6d423668e2c3abdbc474562b1c73360ff7326f06cb9532dcb73254b5b63dae4", size = 276867, upload-time = "2024-04-20T07:13:29.436Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/d1/3ed38a54923fe04eace750c0f0adbc149fb2b028375c71e864aee5e2d6d6/ormsgpack-1.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb2dd4ed3e503a8266dcbfbb8d810a36baa34e4bb4229e90e9c213058a06d74", size = 280728, upload-time = "2024-04-20T07:13:31.459Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/52/0261a80de2486793b4844c2668b17f49d03a20aba13a8d3d975831b1d866/ormsgpack-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f13bd643df1324e8797caba4c5c0168a87524df8424e8413ba29723e89a586a", size = 276644, upload-time = "2024-04-20T07:13:32.708Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/f0/2ebda08824d4f658c5ad048bcbe64e352b637b661b4d26c51d7403d30569/ormsgpack-1.5.0-cp312-none-win_amd64.whl", hash = "sha256:e016da381a126478c4bafab0ae19d3a2537f6471341ecced4bb61471e8841cad", size = 155198, upload-time = "2024-04-20T07:13:34.759Z" }, ] [[package]] name = "outcome" version = "1.3.0.post0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "attrs" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/98/df/77698abfac98571e65ffeb0c1fba8ffd692ab8458d617a0eed7d9a8d38f2/outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/df/77698abfac98571e65ffeb0c1fba8ffd692ab8458d617a0eed7d9a8d38f2/outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/55/8b/5ab7257531a5d830fc8000c476e63c935488d74609b50f9384a643ec0a62/outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/8b/5ab7257531a5d830fc8000c476e63c935488d74609b50f9384a643ec0a62/outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b" }, ] [[package]] name = "packaging" version = "24.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, ] [[package]] name = "pandas" version = "2.2.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57" }, - { url = "https://mirrors.aliyun.com/pypi/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213, upload-time = "2024-09-20T13:10:04.827Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827, upload-time = "2024-09-20T13:08:42.347Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897, upload-time = "2024-09-20T13:08:45.807Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908, upload-time = "2024-09-20T18:37:13.513Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210, upload-time = "2024-09-20T13:08:48.325Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292, upload-time = "2024-09-20T19:01:54.443Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379, upload-time = "2024-09-20T13:08:50.882Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471, upload-time = "2024-09-20T13:08:53.332Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222, upload-time = "2024-09-20T13:08:56.254Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274, upload-time = "2024-09-20T13:08:58.645Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836, upload-time = "2024-09-20T19:01:57.571Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505, upload-time = "2024-09-20T13:09:01.501Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420, upload-time = "2024-09-20T19:02:00.678Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457, upload-time = "2024-09-20T13:09:04.105Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166, upload-time = "2024-09-20T13:09:06.917Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893, upload-time = "2024-09-20T13:09:09.655Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475, upload-time = "2024-09-20T13:09:14.718Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645, upload-time = "2024-09-20T19:02:03.88Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445, upload-time = "2024-09-20T13:09:17.621Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235, upload-time = "2024-09-20T19:02:07.094Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756, upload-time = "2024-09-20T13:09:20.474Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248, upload-time = "2024-09-20T13:09:23.137Z" }, ] [[package]] name = "parameterized" version = "0.9.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1", size = 24351, upload-time = "2023-03-27T02:01:11.592Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475, upload-time = "2023-03-27T02:01:09.31Z" }, ] [[package]] name = "patsy" version = "1.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d1/81/74f6a65b848ffd16c18f920620ce999fe45fe27f01ab3911260ce4ed85e4/patsy-1.0.1.tar.gz", hash = "sha256:e786a9391eec818c054e359b737bbce692f051aee4c661f4141cc88fb459c0c4" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/81/74f6a65b848ffd16c18f920620ce999fe45fe27f01ab3911260ce4ed85e4/patsy-1.0.1.tar.gz", hash = "sha256:e786a9391eec818c054e359b737bbce692f051aee4c661f4141cc88fb459c0c4", size = 396010, upload-time = "2024-11-12T14:10:54.642Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/87/2b/b50d3d08ea0fc419c183a84210571eba005328efa62b6b98bc28e9ead32a/patsy-1.0.1-py2.py3-none-any.whl", hash = "sha256:751fb38f9e97e62312e921a1954b81e1bb2bcda4f5eeabaf94db251ee791509c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/2b/b50d3d08ea0fc419c183a84210571eba005328efa62b6b98bc28e9ead32a/patsy-1.0.1-py2.py3-none-any.whl", hash = "sha256:751fb38f9e97e62312e921a1954b81e1bb2bcda4f5eeabaf94db251ee791509c", size = 232923, upload-time = "2024-11-12T14:10:52.85Z" }, ] [[package]] @@ -4359,671 +4359,671 @@ wheels = [ [[package]] name = "pdfminer-six" version = "20221105" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "charset-normalizer" }, { name = "cryptography" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ac/6e/89c532d108e362cbaf76fdb972e7a5e85723c225f08e1646fb86878d4f7f/pdfminer.six-20221105.tar.gz", hash = "sha256:8448ab7b939d18b64820478ecac5394f482d7a79f5f7eaa7703c6c959c175e1d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/6e/89c532d108e362cbaf76fdb972e7a5e85723c225f08e1646fb86878d4f7f/pdfminer.six-20221105.tar.gz", hash = "sha256:8448ab7b939d18b64820478ecac5394f482d7a79f5f7eaa7703c6c959c175e1d", size = 7361391, upload-time = "2022-11-05T16:33:46.725Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/46/68/b3fb5f073bcd3df9143a3520289c147351bfa3c1b096d44081f38fd1c247/pdfminer.six-20221105-py3-none-any.whl", hash = "sha256:1eaddd712d5b2732f8ac8486824533514f8ba12a0787b3d5fe1e686cd826532d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/68/b3fb5f073bcd3df9143a3520289c147351bfa3c1b096d44081f38fd1c247/pdfminer.six-20221105-py3-none-any.whl", hash = "sha256:1eaddd712d5b2732f8ac8486824533514f8ba12a0787b3d5fe1e686cd826532d", size = 5613896, upload-time = "2022-11-05T16:33:45.016Z" }, ] [[package]] name = "pdfplumber" version = "0.10.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pdfminer-six" }, { name = "pillow" }, { name = "pypdfium2" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/25/b2/a3ebd1987165b088dfa328fc1ddbf621b62f1785a4daafb4090c91246b61/pdfplumber-0.10.4.tar.gz", hash = "sha256:1c83a4e1fe75525ce1f161fa55a8142209a2da69b45542ce2c45be879e804fd6" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/b2/a3ebd1987165b088dfa328fc1ddbf621b62f1785a4daafb4090c91246b61/pdfplumber-0.10.4.tar.gz", hash = "sha256:1c83a4e1fe75525ce1f161fa55a8142209a2da69b45542ce2c45be879e804fd6", size = 102756, upload-time = "2024-02-10T23:38:01.106Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/53/5c/4523bfce8ba473b0e33931f9638f69c3573b3b72b0c63c73d779848d182f/pdfplumber-0.10.4-py3-none-any.whl", hash = "sha256:c8f200259703324cd39a5c93b181a0d2370a6b2b6da670c117e75c3da6aca4a4" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/5c/4523bfce8ba473b0e33931f9638f69c3573b3b72b0c63c73d779848d182f/pdfplumber-0.10.4-py3-none-any.whl", hash = "sha256:c8f200259703324cd39a5c93b181a0d2370a6b2b6da670c117e75c3da6aca4a4", size = 54718, upload-time = "2024-02-10T23:37:58.882Z" }, ] [[package]] name = "peewee" version = "3.17.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8d/a5/89cdbc4a7f6d7a0624c120be102db770ee717aa371066581e3daf2beb96f/peewee-3.17.1.tar.gz", hash = "sha256:e009ac4227c4fdc0058a56e822ad5987684f0a1fbb20fed577200785102581c3" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/a5/89cdbc4a7f6d7a0624c120be102db770ee717aa371066581e3daf2beb96f/peewee-3.17.1.tar.gz", hash = "sha256:e009ac4227c4fdc0058a56e822ad5987684f0a1fbb20fed577200785102581c3", size = 2951636, upload-time = "2024-02-05T15:04:14.549Z" } [[package]] name = "pillow" version = "10.4.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597" }, - { url = "https://mirrors.aliyun.com/pypi/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload-time = "2024-07-01T09:45:22.07Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload-time = "2024-07-01T09:45:25.292Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload-time = "2024-07-01T09:45:27.94Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload-time = "2024-07-01T09:45:30.305Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload-time = "2024-07-01T09:45:32.868Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload-time = "2024-07-01T09:45:35.279Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload-time = "2024-07-01T09:45:37.74Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload-time = "2024-07-01T09:45:39.89Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload-time = "2024-07-01T09:45:42.771Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload-time = "2024-07-01T09:45:45.176Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload-time = "2024-07-01T09:45:47.274Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265, upload-time = "2024-07-01T09:45:49.812Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655, upload-time = "2024-07-01T09:45:52.462Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304, upload-time = "2024-07-01T09:45:55.006Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804, upload-time = "2024-07-01T09:45:58.437Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126, upload-time = "2024-07-01T09:46:00.713Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541, upload-time = "2024-07-01T09:46:03.235Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616, upload-time = "2024-07-01T09:46:05.356Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802, upload-time = "2024-07-01T09:46:08.145Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213, upload-time = "2024-07-01T09:46:10.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498, upload-time = "2024-07-01T09:46:12.685Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219, upload-time = "2024-07-01T09:46:14.83Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350, upload-time = "2024-07-01T09:46:17.177Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980, upload-time = "2024-07-01T09:46:19.169Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799, upload-time = "2024-07-01T09:46:21.883Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973, upload-time = "2024-07-01T09:46:24.321Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054, upload-time = "2024-07-01T09:46:26.825Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484, upload-time = "2024-07-01T09:46:29.355Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375, upload-time = "2024-07-01T09:46:31.756Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773, upload-time = "2024-07-01T09:46:33.73Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690, upload-time = "2024-07-01T09:46:36.587Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951, upload-time = "2024-07-01T09:46:38.777Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427, upload-time = "2024-07-01T09:46:43.15Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload-time = "2024-07-01T09:48:04.815Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload-time = "2024-07-01T09:48:07.206Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload-time = "2024-07-01T09:48:09.66Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload-time = "2024-07-01T09:48:12.529Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload-time = "2024-07-01T09:48:14.891Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload-time = "2024-07-01T09:48:17.601Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload-time = "2024-07-01T09:48:20.293Z" }, ] [[package]] name = "platformdirs" version = "4.3.8" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, ] [[package]] name = "playwright" version = "1.54.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "greenlet" }, { name = "pyee" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f3/09/33d5bfe393a582d8dac72165a9e88b274143c9df411b65ece1cc13f42988/playwright-1.54.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bf3b845af744370f1bd2286c2a9536f474cc8a88dc995b72ea9a5be714c9a77d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/7b/51882dc584f7aa59f446f2bb34e33c0e5f015de4e31949e5b7c2c10e54f0/playwright-1.54.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:780928b3ca2077aea90414b37e54edd0c4bbb57d1aafc42f7aa0b3fd2c2fac02" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/a1/7aa8ae175b240c0ec8849fcf000e078f3c693f9aa2ffd992da6550ea0dff/playwright-1.54.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:81d0b6f28843b27f288cfe438af0a12a4851de57998009a519ea84cee6fbbfb9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/a9/45084fd23b6206f954198296ce39b0acf50debfdf3ec83a593e4d73c9c8a/playwright-1.54.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:09919f45cc74c64afb5432646d7fef0d19fff50990c862cb8d9b0577093f40cc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/d4/6a692f4c6db223adc50a6e53af405b45308db39270957a6afebddaa80ea2/playwright-1.54.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ae206c55737e8e3eae51fb385d61c0312eeef31535643bb6232741b41b6fdc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/7a/4ee60a1c3714321db187bebbc40d52cea5b41a856925156325058b5fca5a/playwright-1.54.0-py3-none-win32.whl", hash = "sha256:0b108622ffb6906e28566f3f31721cd57dda637d7e41c430287804ac01911f56" }, - { url = "https://mirrors.aliyun.com/pypi/packages/aa/77/8f8fae05a242ef639de963d7ae70a69d0da61d6d72f1207b8bbf74ffd3e7/playwright-1.54.0-py3-none-win_amd64.whl", hash = "sha256:9e5aee9ae5ab1fdd44cd64153313a2045b136fcbcfb2541cc0a3d909132671a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/33/ff/99a6f4292a90504f2927d34032a4baf6adb498dc3f7cf0f3e0e22899e310/playwright-1.54.0-py3-none-win_arm64.whl", hash = "sha256:a975815971f7b8dca505c441a4c56de1aeb56a211290f8cc214eeef5524e8d75" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/09/33d5bfe393a582d8dac72165a9e88b274143c9df411b65ece1cc13f42988/playwright-1.54.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bf3b845af744370f1bd2286c2a9536f474cc8a88dc995b72ea9a5be714c9a77d", size = 40439034, upload-time = "2025-07-22T13:58:04.816Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/7b/51882dc584f7aa59f446f2bb34e33c0e5f015de4e31949e5b7c2c10e54f0/playwright-1.54.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:780928b3ca2077aea90414b37e54edd0c4bbb57d1aafc42f7aa0b3fd2c2fac02", size = 38702308, upload-time = "2025-07-22T13:58:08.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/a1/7aa8ae175b240c0ec8849fcf000e078f3c693f9aa2ffd992da6550ea0dff/playwright-1.54.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:81d0b6f28843b27f288cfe438af0a12a4851de57998009a519ea84cee6fbbfb9", size = 40439037, upload-time = "2025-07-22T13:58:11.37Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/a9/45084fd23b6206f954198296ce39b0acf50debfdf3ec83a593e4d73c9c8a/playwright-1.54.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:09919f45cc74c64afb5432646d7fef0d19fff50990c862cb8d9b0577093f40cc", size = 45920135, upload-time = "2025-07-22T13:58:14.494Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/d4/6a692f4c6db223adc50a6e53af405b45308db39270957a6afebddaa80ea2/playwright-1.54.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ae206c55737e8e3eae51fb385d61c0312eeef31535643bb6232741b41b6fdc", size = 45302695, upload-time = "2025-07-22T13:58:18.901Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/7a/4ee60a1c3714321db187bebbc40d52cea5b41a856925156325058b5fca5a/playwright-1.54.0-py3-none-win32.whl", hash = "sha256:0b108622ffb6906e28566f3f31721cd57dda637d7e41c430287804ac01911f56", size = 35469309, upload-time = "2025-07-22T13:58:21.917Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/77/8f8fae05a242ef639de963d7ae70a69d0da61d6d72f1207b8bbf74ffd3e7/playwright-1.54.0-py3-none-win_amd64.whl", hash = "sha256:9e5aee9ae5ab1fdd44cd64153313a2045b136fcbcfb2541cc0a3d909132671a2", size = 35469311, upload-time = "2025-07-22T13:58:24.707Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/ff/99a6f4292a90504f2927d34032a4baf6adb498dc3f7cf0f3e0e22899e310/playwright-1.54.0-py3-none-win_arm64.whl", hash = "sha256:a975815971f7b8dca505c441a4c56de1aeb56a211290f8cc214eeef5524e8d75", size = 31239119, upload-time = "2025-07-22T13:58:27.56Z" }, ] [[package]] name = "pluggy" version = "1.6.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "pluginlib" version = "0.9.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/58/38/ca974ba2d8ccc7954d8ccb0394cce184ac6269bd1fbfe06f70a0da3c8946/pluginlib-0.9.4.tar.gz", hash = "sha256:88727037138f759a3952f6391ae3751536f04ad8be6023607620ea49695a3a83" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/38/ca974ba2d8ccc7954d8ccb0394cce184ac6269bd1fbfe06f70a0da3c8946/pluginlib-0.9.4.tar.gz", hash = "sha256:88727037138f759a3952f6391ae3751536f04ad8be6023607620ea49695a3a83", size = 46541, upload-time = "2024-11-24T17:14:53.814Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b0/b5/c869b3d2ed1613afeb02c635be11f5d35fa5b2b665f4d059cfe5b8e82941/pluginlib-0.9.4-py2.py3-none-any.whl", hash = "sha256:d4cfb7d74a6d2454e256b6512fbc4bc2dd7620cb7764feb67331ef56ce4b33f2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/b5/c869b3d2ed1613afeb02c635be11f5d35fa5b2b665f4d059cfe5b8e82941/pluginlib-0.9.4-py2.py3-none-any.whl", hash = "sha256:d4cfb7d74a6d2454e256b6512fbc4bc2dd7620cb7764feb67331ef56ce4b33f2", size = 25132, upload-time = "2024-11-24T17:14:52.824Z" }, ] [[package]] name = "polars-lts-cpu" version = "1.9.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/30/b8/81ada35f862f5b37456b225f578128ec4314da5a4627b440acf01180fbf9/polars_lts_cpu-1.9.0.tar.gz", hash = "sha256:4848ea9f54cb59cce6b08a5f161a5c488684b55729f2ef076c65ca92759a985e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/b8/81ada35f862f5b37456b225f578128ec4314da5a4627b440acf01180fbf9/polars_lts_cpu-1.9.0.tar.gz", hash = "sha256:4848ea9f54cb59cce6b08a5f161a5c488684b55729f2ef076c65ca92759a985e", size = 4028430, upload-time = "2024-10-01T19:54:10.186Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9a/72/24127dc03627b0c50bef745b1ee0451359de17ba8e9a70027fdaf9f9c76e/polars_lts_cpu-1.9.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1dd1484b9a08171184f198c7b1a1bdcea7457de4a7d481949ec410ee96a49bc9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0d/36/2a816b333b2eeff12187026958cd2fac4a31f7f00e8a50f85ab08ed0531e/polars_lts_cpu-1.9.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:fb10fd9f8a8c0145ec70c4f3dde3ff1ef9f18079964c9dc40e8aac3d7521a862" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/df/c393f41ad2da9cb0397436e3b424a1982b093d02181fc09ffbcefb98ff30/polars_lts_cpu-1.9.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eb332c2e5837759a5cb406566a1d0feddf145711176cf89f745dba010c739a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/27/f2/cad4bccf463695de44869d17419f891b356b3af7275dcc64be95264317e5/polars_lts_cpu-1.9.0-cp38-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:e8f8eb6d9c5b017eb4ffb6276255b77d4eb8508e30bc2898e2b55ebe8bc9715c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/53/b1/d7236cbcef17de67938be6068ff72153bffed7543b496a93b31b88e0c670/polars_lts_cpu-1.9.0-cp38-abi3-win_amd64.whl", hash = "sha256:0c199d86e3d6b775264e3a596ebfbae4c7848b88bed4248822b7b498b2e96f2d" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/72/24127dc03627b0c50bef745b1ee0451359de17ba8e9a70027fdaf9f9c76e/polars_lts_cpu-1.9.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1dd1484b9a08171184f198c7b1a1bdcea7457de4a7d481949ec410ee96a49bc9", size = 31460193, upload-time = "2024-10-01T19:53:28.538Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/36/2a816b333b2eeff12187026958cd2fac4a31f7f00e8a50f85ab08ed0531e/polars_lts_cpu-1.9.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:fb10fd9f8a8c0145ec70c4f3dde3ff1ef9f18079964c9dc40e8aac3d7521a862", size = 28171134, upload-time = "2024-10-01T19:53:32.642Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/df/c393f41ad2da9cb0397436e3b424a1982b093d02181fc09ffbcefb98ff30/polars_lts_cpu-1.9.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eb332c2e5837759a5cb406566a1d0feddf145711176cf89f745dba010c739a9", size = 32625386, upload-time = "2024-10-01T19:53:37.792Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/f2/cad4bccf463695de44869d17419f891b356b3af7275dcc64be95264317e5/polars_lts_cpu-1.9.0-cp38-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:e8f8eb6d9c5b017eb4ffb6276255b77d4eb8508e30bc2898e2b55ebe8bc9715c", size = 29764771, upload-time = "2024-10-01T19:53:41.776Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/b1/d7236cbcef17de67938be6068ff72153bffed7543b496a93b31b88e0c670/polars_lts_cpu-1.9.0-cp38-abi3-win_amd64.whl", hash = "sha256:0c199d86e3d6b775264e3a596ebfbae4c7848b88bed4248822b7b498b2e96f2d", size = 32949159, upload-time = "2024-10-01T19:53:45.92Z" }, ] [[package]] name = "pooch" version = "1.8.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "packaging" }, { name = "platformdirs" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c6/77/b3d3e00c696c16cf99af81ef7b1f5fe73bd2a307abca41bd7605429fe6e5/pooch-1.8.2.tar.gz", hash = "sha256:76561f0de68a01da4df6af38e9955c4c9d1a5c90da73f7e40276a5728ec83d10" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/77/b3d3e00c696c16cf99af81ef7b1f5fe73bd2a307abca41bd7605429fe6e5/pooch-1.8.2.tar.gz", hash = "sha256:76561f0de68a01da4df6af38e9955c4c9d1a5c90da73f7e40276a5728ec83d10", size = 59353, upload-time = "2024-06-06T16:53:46.224Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-py3-none-any.whl", hash = "sha256:3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-py3-none-any.whl", hash = "sha256:3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47", size = 64574, upload-time = "2024-06-06T16:53:44.343Z" }, ] [[package]] name = "pot" version = "0.9.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, { name = "scipy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c1/40/3e0c8dd88328d944f9d82b30cafd2a1c911bddff0b8bccc8dc9dd5e45b7c/pot-0.9.5.tar.gz", hash = "sha256:9644ee7ff51c3cffa3c2632b9dd9dff4f3520266f9fb771450935ffb646d6042" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/87/53/acd66a8e50f992e6ca578181009e81d367ad738d0ac135f63d0de3ca92cd/POT-0.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:34d766c38e65a69c087b01a854fe89fbd152c3e8af93da2227b6c40aed6d37b9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/51/43c68e7cb1dc7c40286d9e19f6cb599108cd01c2b32307296eba9cb01a05/POT-0.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5407377256de11b6fdc94bbba9b50ea5a2301570905fc9014541cc8473806d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/87/17069069948e40fa0e41366e6412322c7849d4b2a0ddae0428d10b571604/POT-0.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f37039cd356198c1fb994e7d935b9bf75d44f2a40319d298bf8cc149eb360d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/49/7bbb5ac2989abd775ae200cdbcf1a2e023cf07e8d1d6afc7d673d4e380d3/POT-0.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00a18427c9abdd107a2285ea0a814c6b22e95a1af8f88a37c56f23cd216f7a6b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/97/ad/1724a238cef180c04a3d63e8702cbe91f0abe946eb7a55c3857cd0ac1d9b/POT-0.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0dc608cea1107289a58dec33cddc1b0a3fea77ff36d66e2c8ac7aeea543969a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/e9/a1901cbbf765b765ab4adace1711adc3eef01db526dc898e31fbdca653a5/POT-0.9.5-cp310-cp310-win32.whl", hash = "sha256:8312bee055389db47adab063749c8d77b5981534177ca6cd9b91e4fb68f69d00" }, - { url = "https://mirrors.aliyun.com/pypi/packages/95/00/2ef88c57c0ee5ff55a95bcb3ff62d904039bb460809d7577ec314b5e7186/POT-0.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:043706d69202ac87e140121ba32ed1b038f2b3fc4a5549586187239a583cd50d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/81/c9eaa405d40567452d102385a2077b4d34f7961dd7ea3354b7749efd4ea7/POT-0.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b5f000da00e408ff781672a4895bfa8daacec055bd534c9e66ead479f3c6d83c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/32/8d319ab8eee96397569115aac644b19136170966667c59b026c277e1b026/POT-0.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9eddd9ff29bdb17d4db8ba00ba18d42656c694a128591502bf59afc1369e1bb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/23/7c/ed772734847ada457af0fdb9dd7073bd3823915721bf64147a1434da5a0c/POT-0.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7eb9b88c73387a9966775a6f6d077d9d071814783701d2656dc05b5032a9662d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/af/a99bc77cf4f79ec04b23d415da005e83aa2a2b91d4216045c87f46d3109f/POT-0.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f44446056f5fc9d132ed8e431732c33cbe754fb1e6d73636f1b6ae811be7df" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/e8/efc53871cc5b086565702e123d62b37aa40320023b46b30923bb9055b287/POT-0.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7f5d27bc9063e01b03d906bb77e7b3428065fdd72ed64233b249584ead2e2bf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/dd/aab8edf448d68fa6be6454887667e04a7bf2b2a5929f2ec35c49f83ef286/POT-0.9.5-cp311-cp311-win32.whl", hash = "sha256:cd79a8b4d35b706f2124f73ebff3bb1ce3450e01cc8f610eda3b6ce13616b829" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fe/ee/9cd8b16e4e8e7254951b83fc6f871763e7e1315078b17b7008662833ed63/POT-0.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:6680aadb69df2f75a413fe9c58bd1c5cb744d017a7c8ba8841654fd0dc75433b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cb/95/deecc996c5e147159f37191b90a6cf4ee2494e40badc79bed743bfb6478b/POT-0.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7d57f96b333c9816a2af7817753108739b38155e52648c5967681dbd89d92ed2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d3/d3/d9ae1ae96ad461a900b4ffb38f0a830201d4c43135e1a3be48a82e77303e/POT-0.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:afad647c78f999439f8c5cbcf74b03c5c0afefb08727cd7d68994130fabfc761" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/97/ca785fc539388696838f34ab6bde8ee8ad625999221e3746c8d410f8c20f/POT-0.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bca891c28592d6e0e8f04b35989de7005f0fb9b3923f00537f1b269c5084aa7b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/bd/fd000d9217a6cb47f25414d1bfce885fcb28fc23876266422a3a2d8fab31/POT-0.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:088c930a5fcd1e8e36fb6af710df47ce6e9331b6b5a28eb09c673df4186dcb10" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5b/39/9c3eed29e954ddbac3ebe68123213826c8995e8acf8b54aa79d1956fda6a/POT-0.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfb18268fac1e982e21821a03f802802a0d579c4690988b764115dd886dc38f5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/83/8d/bf8af71e2f36da7598da946a81fbaebb362abaebf6eeba81ebc8efbc860a/POT-0.9.5-cp312-cp312-win32.whl", hash = "sha256:931fa46ff8e01d47309207243988c783a2d8364452bc080b130c5d319349ad3f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/95/14902c778117ad9ac7af62dd1d951942440c57df991d7f937f416ee6320f/POT-0.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:be786612b391c2e4d3b5db4e7d51cdb2360284e3a6949990051c2eb102f60d3c" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/40/3e0c8dd88328d944f9d82b30cafd2a1c911bddff0b8bccc8dc9dd5e45b7c/pot-0.9.5.tar.gz", hash = "sha256:9644ee7ff51c3cffa3c2632b9dd9dff4f3520266f9fb771450935ffb646d6042", size = 440808, upload-time = "2024-11-07T10:05:05.567Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/53/acd66a8e50f992e6ca578181009e81d367ad738d0ac135f63d0de3ca92cd/POT-0.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:34d766c38e65a69c087b01a854fe89fbd152c3e8af93da2227b6c40aed6d37b9", size = 410989, upload-time = "2024-11-07T10:04:04.166Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/51/43c68e7cb1dc7c40286d9e19f6cb599108cd01c2b32307296eba9cb01a05/POT-0.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5407377256de11b6fdc94bbba9b50ea5a2301570905fc9014541cc8473806d9", size = 351111, upload-time = "2024-11-07T10:04:06.604Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/87/17069069948e40fa0e41366e6412322c7849d4b2a0ddae0428d10b571604/POT-0.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f37039cd356198c1fb994e7d935b9bf75d44f2a40319d298bf8cc149eb360d5", size = 344289, upload-time = "2024-11-07T10:04:08.151Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/49/7bbb5ac2989abd775ae200cdbcf1a2e023cf07e8d1d6afc7d673d4e380d3/POT-0.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00a18427c9abdd107a2285ea0a814c6b22e95a1af8f88a37c56f23cd216f7a6b", size = 858699, upload-time = "2024-11-07T10:04:10.231Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/ad/1724a238cef180c04a3d63e8702cbe91f0abe946eb7a55c3857cd0ac1d9b/POT-0.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0dc608cea1107289a58dec33cddc1b0a3fea77ff36d66e2c8ac7aeea543969a", size = 865565, upload-time = "2024-11-07T10:04:12.421Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/e9/a1901cbbf765b765ab4adace1711adc3eef01db526dc898e31fbdca653a5/POT-0.9.5-cp310-cp310-win32.whl", hash = "sha256:8312bee055389db47adab063749c8d77b5981534177ca6cd9b91e4fb68f69d00", size = 344137, upload-time = "2024-11-07T10:04:14.693Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/00/2ef88c57c0ee5ff55a95bcb3ff62d904039bb460809d7577ec314b5e7186/POT-0.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:043706d69202ac87e140121ba32ed1b038f2b3fc4a5549586187239a583cd50d", size = 348385, upload-time = "2024-11-07T10:04:15.851Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/81/c9eaa405d40567452d102385a2077b4d34f7961dd7ea3354b7749efd4ea7/POT-0.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b5f000da00e408ff781672a4895bfa8daacec055bd534c9e66ead479f3c6d83c", size = 410977, upload-time = "2024-11-07T10:04:17.396Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/32/8d319ab8eee96397569115aac644b19136170966667c59b026c277e1b026/POT-0.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9eddd9ff29bdb17d4db8ba00ba18d42656c694a128591502bf59afc1369e1bb3", size = 351059, upload-time = "2024-11-07T10:04:18.821Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/7c/ed772734847ada457af0fdb9dd7073bd3823915721bf64147a1434da5a0c/POT-0.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7eb9b88c73387a9966775a6f6d077d9d071814783701d2656dc05b5032a9662d", size = 344293, upload-time = "2024-11-07T10:04:20.193Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/af/a99bc77cf4f79ec04b23d415da005e83aa2a2b91d4216045c87f46d3109f/POT-0.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f44446056f5fc9d132ed8e431732c33cbe754fb1e6d73636f1b6ae811be7df", size = 891139, upload-time = "2024-11-07T10:04:22.344Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/e8/efc53871cc5b086565702e123d62b37aa40320023b46b30923bb9055b287/POT-0.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7f5d27bc9063e01b03d906bb77e7b3428065fdd72ed64233b249584ead2e2bf", size = 897470, upload-time = "2024-11-07T10:04:23.686Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/dd/aab8edf448d68fa6be6454887667e04a7bf2b2a5929f2ec35c49f83ef286/POT-0.9.5-cp311-cp311-win32.whl", hash = "sha256:cd79a8b4d35b706f2124f73ebff3bb1ce3450e01cc8f610eda3b6ce13616b829", size = 343915, upload-time = "2024-11-07T10:04:24.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/ee/9cd8b16e4e8e7254951b83fc6f871763e7e1315078b17b7008662833ed63/POT-0.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:6680aadb69df2f75a413fe9c58bd1c5cb744d017a7c8ba8841654fd0dc75433b", size = 348566, upload-time = "2024-11-07T10:04:26.557Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/95/deecc996c5e147159f37191b90a6cf4ee2494e40badc79bed743bfb6478b/POT-0.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7d57f96b333c9816a2af7817753108739b38155e52648c5967681dbd89d92ed2", size = 410824, upload-time = "2024-11-07T10:04:28.313Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/d3/d9ae1ae96ad461a900b4ffb38f0a830201d4c43135e1a3be48a82e77303e/POT-0.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:afad647c78f999439f8c5cbcf74b03c5c0afefb08727cd7d68994130fabfc761", size = 351023, upload-time = "2024-11-07T10:04:29.851Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/97/ca785fc539388696838f34ab6bde8ee8ad625999221e3746c8d410f8c20f/POT-0.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bca891c28592d6e0e8f04b35989de7005f0fb9b3923f00537f1b269c5084aa7b", size = 344150, upload-time = "2024-11-07T10:04:30.974Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/bd/fd000d9217a6cb47f25414d1bfce885fcb28fc23876266422a3a2d8fab31/POT-0.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:088c930a5fcd1e8e36fb6af710df47ce6e9331b6b5a28eb09c673df4186dcb10", size = 894749, upload-time = "2024-11-07T10:04:32.163Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/39/9c3eed29e954ddbac3ebe68123213826c8995e8acf8b54aa79d1956fda6a/POT-0.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfb18268fac1e982e21821a03f802802a0d579c4690988b764115dd886dc38f5", size = 901694, upload-time = "2024-11-07T10:04:33.919Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/8d/bf8af71e2f36da7598da946a81fbaebb362abaebf6eeba81ebc8efbc860a/POT-0.9.5-cp312-cp312-win32.whl", hash = "sha256:931fa46ff8e01d47309207243988c783a2d8364452bc080b130c5d319349ad3f", size = 343682, upload-time = "2024-11-07T10:04:35.19Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/95/14902c778117ad9ac7af62dd1d951942440c57df991d7f937f416ee6320f/POT-0.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:be786612b391c2e4d3b5db4e7d51cdb2360284e3a6949990051c2eb102f60d3c", size = 347949, upload-time = "2024-11-07T10:04:36.497Z" }, ] [[package]] name = "primp" version = "0.15.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/56/0b/a87556189da4de1fc6360ca1aa05e8335509633f836cdd06dd17f0743300/primp-0.15.0.tar.gz", hash = "sha256:1af8ea4b15f57571ff7fc5e282a82c5eb69bc695e19b8ddeeda324397965b30a" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/0b/a87556189da4de1fc6360ca1aa05e8335509633f836cdd06dd17f0743300/primp-0.15.0.tar.gz", hash = "sha256:1af8ea4b15f57571ff7fc5e282a82c5eb69bc695e19b8ddeeda324397965b30a", size = 113022, upload-time = "2025-04-17T11:41:05.315Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f5/5a/146ac964b99ea7657ad67eb66f770be6577dfe9200cb28f9a95baffd6c3f/primp-0.15.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1b281f4ca41a0c6612d4c6e68b96e28acfe786d226a427cd944baa8d7acd644f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/8a/cc2321e32db3ce64d6e32950d5bcbea01861db97bfb20b5394affc45b387/primp-0.15.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:489cbab55cd793ceb8f90bb7423c6ea64ebb53208ffcf7a044138e3c66d77299" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/7b/cbd5d999a07ff2a21465975d4eb477ae6f69765e8fe8c9087dab250180d8/primp-0.15.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b45c23f94016215f62d2334552224236217aaeb716871ce0e4dcfa08eb161" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/6e/a6221c612e61303aec2bcac3f0a02e8b67aee8c0db7bdc174aeb8010f975/primp-0.15.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e985a9cba2e3f96a323722e5440aa9eccaac3178e74b884778e926b5249df080" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/54/bfeef5aca613dc660a69d0760a26c6b8747d8fdb5a7f20cb2cee53c9862f/primp-0.15.0-cp38-abi3-manylinux_2_34_armv7l.whl", hash = "sha256:6b84a6ffa083e34668ff0037221d399c24d939b5629cd38223af860de9e17a83" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/96/84078e09f16a1dad208f2fe0f8a81be2cf36e024675b0f9eec0c2f6e2182/primp-0.15.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:592f6079646bdf5abbbfc3b0a28dac8de943f8907a250ce09398cda5eaebd260" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/80/8a7a9587d3eb85be3d0b64319f2f690c90eb7953e3f73a9ddd9e46c8dc42/primp-0.15.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a728e5a05f37db6189eb413d22c78bd143fa59dd6a8a26dacd43332b3971fe8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/dd/f0183ed0145e58cf9d286c1b2c14f63ccee987a4ff79ac85acc31b5d86bd/primp-0.15.0-cp38-abi3-win_amd64.whl", hash = "sha256:aeb6bd20b06dfc92cfe4436939c18de88a58c640752cf7f30d9e4ae893cdec32" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/5a/146ac964b99ea7657ad67eb66f770be6577dfe9200cb28f9a95baffd6c3f/primp-0.15.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1b281f4ca41a0c6612d4c6e68b96e28acfe786d226a427cd944baa8d7acd644f", size = 3178914, upload-time = "2025-04-17T11:40:59.558Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/8a/cc2321e32db3ce64d6e32950d5bcbea01861db97bfb20b5394affc45b387/primp-0.15.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:489cbab55cd793ceb8f90bb7423c6ea64ebb53208ffcf7a044138e3c66d77299", size = 2955079, upload-time = "2025-04-17T11:40:57.398Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/7b/cbd5d999a07ff2a21465975d4eb477ae6f69765e8fe8c9087dab250180d8/primp-0.15.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b45c23f94016215f62d2334552224236217aaeb716871ce0e4dcfa08eb161", size = 3281018, upload-time = "2025-04-17T11:40:55.308Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/6e/a6221c612e61303aec2bcac3f0a02e8b67aee8c0db7bdc174aeb8010f975/primp-0.15.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e985a9cba2e3f96a323722e5440aa9eccaac3178e74b884778e926b5249df080", size = 3255229, upload-time = "2025-04-17T11:40:47.811Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/54/bfeef5aca613dc660a69d0760a26c6b8747d8fdb5a7f20cb2cee53c9862f/primp-0.15.0-cp38-abi3-manylinux_2_34_armv7l.whl", hash = "sha256:6b84a6ffa083e34668ff0037221d399c24d939b5629cd38223af860de9e17a83", size = 3014522, upload-time = "2025-04-17T11:40:50.191Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/96/84078e09f16a1dad208f2fe0f8a81be2cf36e024675b0f9eec0c2f6e2182/primp-0.15.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:592f6079646bdf5abbbfc3b0a28dac8de943f8907a250ce09398cda5eaebd260", size = 3418567, upload-time = "2025-04-17T11:41:01.595Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/80/8a7a9587d3eb85be3d0b64319f2f690c90eb7953e3f73a9ddd9e46c8dc42/primp-0.15.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a728e5a05f37db6189eb413d22c78bd143fa59dd6a8a26dacd43332b3971fe8", size = 3606279, upload-time = "2025-04-17T11:41:03.61Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/dd/f0183ed0145e58cf9d286c1b2c14f63ccee987a4ff79ac85acc31b5d86bd/primp-0.15.0-cp38-abi3-win_amd64.whl", hash = "sha256:aeb6bd20b06dfc92cfe4436939c18de88a58c640752cf7f30d9e4ae893cdec32", size = 3149967, upload-time = "2025-04-17T11:41:07.067Z" }, ] [[package]] name = "proces" version = "0.1.7" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2c/3d/4159b57736ced0fd22553226df20a985ef7655519c80ffcb8a9fb49ebeee/proces-0.1.7.tar.gz", hash = "sha256:70a05d9e973dd685f7a9092c58be695a8181a411d63796c213232fd3fdc43775" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/3d/4159b57736ced0fd22553226df20a985ef7655519c80ffcb8a9fb49ebeee/proces-0.1.7.tar.gz", hash = "sha256:70a05d9e973dd685f7a9092c58be695a8181a411d63796c213232fd3fdc43775" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6f/88/06cc0c7d890ed8d7e16ef0e56880dea516a21643fb1f3a69a50f4cc6f716/proces-0.1.7-py3-none-any.whl", hash = "sha256:308325bbc96877263f06e57e5e9c760c4b42cc722887ad60be6b18fc37d68762" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/88/06cc0c7d890ed8d7e16ef0e56880dea516a21643fb1f3a69a50f4cc6f716/proces-0.1.7-py3-none-any.whl", hash = "sha256:308325bbc96877263f06e57e5e9c760c4b42cc722887ad60be6b18fc37d68762" }, ] [[package]] name = "prompt-toolkit" version = "3.0.51" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940, upload-time = "2025-04-15T09:18:47.731Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810, upload-time = "2025-04-15T09:18:44.753Z" }, ] [[package]] name = "propcache" version = "0.3.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614" }, - { url = "https://mirrors.aliyun.com/pypi/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70" }, - { url = "https://mirrors.aliyun.com/pypi/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10" }, - { url = "https://mirrors.aliyun.com/pypi/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, ] [[package]] name = "proto-plus" version = "1.26.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, ] [[package]] name = "protobuf" version = "5.27.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/71/a5/d61e4263e62e6db1990c120d682870e5c50a30fb6b26119a214c7a014847/protobuf-5.27.2.tar.gz", hash = "sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/a5/d61e4263e62e6db1990c120d682870e5c50a30fb6b26119a214c7a014847/protobuf-5.27.2.tar.gz", hash = "sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714", size = 401640, upload-time = "2024-06-25T20:54:53.874Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e9/9d/318d07d4edd1dc1a29ae67f7bb42b6e8a570f817ebe8608bf3c9c518d4e8/protobuf-5.27.2-cp310-abi3-win32.whl", hash = "sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/04/73b8fd7f34f3a2b2b64aa31a173b8aebbdb0c55523df4c027846bb44bc1e/protobuf-5.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/44/6ae304790fad936bb4cf09907a05d669b7600458a02b6c960fdaaeeab06e/protobuf-5.27.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/c7/a534268f9c3780be1ba50f5ed96243fa9cf6224a445de662c34e91ce0e61/protobuf-5.27.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/27/e4/8dc4546be46873f8950cb44cdfe19b79d66d26e53c4ee5e3440406257fcd/protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3a/fa/4c3ac5527ed2e5f3577167ecd5f8180ffcdc8bdd59c9f143409c19706456/protobuf-5.27.2-py3-none-any.whl", hash = "sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/9d/318d07d4edd1dc1a29ae67f7bb42b6e8a570f817ebe8608bf3c9c518d4e8/protobuf-5.27.2-cp310-abi3-win32.whl", hash = "sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38", size = 405829, upload-time = "2024-06-25T20:54:22.034Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/04/73b8fd7f34f3a2b2b64aa31a173b8aebbdb0c55523df4c027846bb44bc1e/protobuf-5.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505", size = 426919, upload-time = "2024-06-25T20:54:28.399Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/44/6ae304790fad936bb4cf09907a05d669b7600458a02b6c960fdaaeeab06e/protobuf-5.27.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5", size = 412246, upload-time = "2024-06-25T20:54:30.159Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/c7/a534268f9c3780be1ba50f5ed96243fa9cf6224a445de662c34e91ce0e61/protobuf-5.27.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b", size = 307143, upload-time = "2024-06-25T20:54:36.048Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/e4/8dc4546be46873f8950cb44cdfe19b79d66d26e53c4ee5e3440406257fcd/protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e", size = 309259, upload-time = "2024-06-25T20:54:38.074Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/fa/4c3ac5527ed2e5f3577167ecd5f8180ffcdc8bdd59c9f143409c19706456/protobuf-5.27.2-py3-none-any.whl", hash = "sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470", size = 164772, upload-time = "2024-06-25T20:54:52.196Z" }, ] [[package]] name = "psutil" version = "7.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, ] [[package]] name = "psycopg2-binary" version = "2.9.9" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fc/07/e720e53bfab016ebcc34241695ccc06a9e3d91ba19b40ca81317afbdc440/psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0a/7c/6aaf8c3cb05d86d2c3f407b95bac0c71a43f2718e38c1091972aacb5e1b2/psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/3d/acab427845198794aafd963dd073ee35810e2c52606e8a28c12db39821fb/psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/be/6c787962d706e55a528ef1693dd7251de657ae60e4d9d767ed61e8e2975c/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/83/50/a054076c6358753661cd1da59f4dabc03e83d51690371f3fd1edb9e2cf72/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9c/02/826dc5cdfc9515423ec912ba00cc2e4eb09f69e0339b177c9c742f2a09a2/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/0d/486e3fa27f39a00168abfcf14a3d8444f437f4b755cc34316da1124f293d/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/af/bce37630c525d2b9cf93f930110fc98616d6aca308d59b833b83b3a38176/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/76/e46dae1b2273814ef80231f86d59cadf94ec36fd757045ed713c5b75cde7/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/6d/e97245eabff29d7c2de5fc1fc17cf7ef427beee93d20a5ae114c6e6718bd/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/a7/2cd2c9d5e23b556c11e3b7da41895808d9b056f8f34f50de4375a35b4951/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/41/815d19767e2adb1a585213b801c954f46102f305c352c4a4f96287342d44/psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/4c/9233e0e206634a5387f3ab40f334a5325fb8bef2ca4e12ee7dbdeaf96afc/psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/ac/702d300f3df169b9d0cbef0340d9f34a78bc18dc2dbafbcb39ff0f165cf8/psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7a/1f/a6cf0cdf944253f7c45d90fbc876cc8bed5cc9942349306245715c0d88d6/psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/0b/3adf561107c865928455891156d1dde5325253f7f4316fe56cd2c3f73570/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/98/c2fedcbf0a9607519a010dcf88571138b2251062dbde3610cdba5ba1eee1/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/05/81e8bc7fca95574c9323e487d9ce1b58a4cfcc17f89b8fe843af46361211/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/85/62825cabc6aad53104b7b6d12eb2ad74737d268630032d07b74d4444cb72/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/b0/9ca2b8e01a0912c9a14234fd5df7a241a1e44778c5797bf4b8eaa8dc3d3a/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/17/ba28bb0022db5e2015a82d2df1c4b0d419c37fa07a588b3aff3adc4939f6/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/92/b463556409cdc12791cd8b1dae0072bf8efe817ef68b7ea3d9cf7d0e5656/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/57/96576e07132d7f7a1ac1df939575e6fdd8951aea337ee152b586bb51a971/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/ae/cedd56e1f4a2b0e37213283caf3733a875c4c76f3372241e19c0d2a87355/psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/1f/7ae31759142999a8d06b3e250c1346c4abcdcada8fa884376775dc1de686/psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a7/d0/5f2db14e7b53552276ab613399a83f83f85b173a862d3f20580bc7231139/psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/18/ca/da384fd47233e300e3e485c90e7aab5d7def896d1281239f75901faf87d4/psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/66/fa53d2d3d92f6e1ef469d92afc6a4fe3f6e8a9a04b687aa28fb1f1d954ee/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/37/2429360ac5547378202db14eec0dde76edbe1f6627df5a43c7e164922859/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/2a/c0530b59d7e0d09824bc2102ecdcec0456b8ca4d47c0caa82e86fce3ed4c/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/57/9f172b900795ea37246c78b5f52e00f4779984370855b3e161600156906d/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119" }, - { url = "https://mirrors.aliyun.com/pypi/packages/94/68/1176fc14ea76861b7b8360be5176e87fb20d5091b137c76570eb4e237324/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/bb/aec2646a705a09079d008ce88073401cd61fc9b04f92af3eb282caa3a2ec/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07" }, - { url = "https://mirrors.aliyun.com/pypi/packages/14/33/12818c157e333cb9d9e6753d1b2463b6f60dbc1fade115f8e4dc5c52cac4/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/56/a2/7851c68fe8768f3c9c246198b6356ee3e4a8a7f6820cc798443faada3400/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/ee/3ba07c6dc7c3294e717e94720da1597aedc82a10b1b180203ce183d4631a/psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/08/9c66c269b0d417a0af9fb969535f0371b8c538633535a7a6a5ca3f9231e2/psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/07/e720e53bfab016ebcc34241695ccc06a9e3d91ba19b40ca81317afbdc440/psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c", size = 384973, upload-time = "2023-10-03T12:48:55.128Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/7c/6aaf8c3cb05d86d2c3f407b95bac0c71a43f2718e38c1091972aacb5e1b2/psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202", size = 2822503, upload-time = "2023-10-03T12:45:56.956Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/3d/acab427845198794aafd963dd073ee35810e2c52606e8a28c12db39821fb/psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7", size = 2552645, upload-time = "2023-10-03T12:46:00.447Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/be/6c787962d706e55a528ef1693dd7251de657ae60e4d9d767ed61e8e2975c/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b", size = 2850980, upload-time = "2023-10-03T12:46:02.666Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/50/a054076c6358753661cd1da59f4dabc03e83d51690371f3fd1edb9e2cf72/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9", size = 3080543, upload-time = "2023-10-03T12:46:06.768Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/02/826dc5cdfc9515423ec912ba00cc2e4eb09f69e0339b177c9c742f2a09a2/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84", size = 3264316, upload-time = "2023-10-03T12:46:08.966Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/0d/486e3fa27f39a00168abfcf14a3d8444f437f4b755cc34316da1124f293d/psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e", size = 3019508, upload-time = "2023-10-03T12:46:11.576Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/af/bce37630c525d2b9cf93f930110fc98616d6aca308d59b833b83b3a38176/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98", size = 2355821, upload-time = "2023-10-03T12:46:14.864Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/76/e46dae1b2273814ef80231f86d59cadf94ec36fd757045ed713c5b75cde7/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245", size = 2534855, upload-time = "2023-10-03T12:46:17.217Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/6d/e97245eabff29d7c2de5fc1fc17cf7ef427beee93d20a5ae114c6e6718bd/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e", size = 2486614, upload-time = "2023-10-03T12:46:19.877Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/a7/2cd2c9d5e23b556c11e3b7da41895808d9b056f8f34f50de4375a35b4951/psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f", size = 2454928, upload-time = "2023-10-03T12:46:22.908Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/41/815d19767e2adb1a585213b801c954f46102f305c352c4a4f96287342d44/psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682", size = 1025249, upload-time = "2023-10-03T12:46:24.819Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/4c/9233e0e206634a5387f3ab40f334a5325fb8bef2ca4e12ee7dbdeaf96afc/psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0", size = 1163645, upload-time = "2023-10-03T12:46:27.677Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/ac/702d300f3df169b9d0cbef0340d9f34a78bc18dc2dbafbcb39ff0f165cf8/psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26", size = 2822581, upload-time = "2023-10-03T12:46:30.64Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/1f/a6cf0cdf944253f7c45d90fbc876cc8bed5cc9942349306245715c0d88d6/psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f", size = 2552633, upload-time = "2023-10-03T12:46:32.808Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/0b/3adf561107c865928455891156d1dde5325253f7f4316fe56cd2c3f73570/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2", size = 2851075, upload-time = "2023-10-03T12:46:35.138Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/98/c2fedcbf0a9607519a010dcf88571138b2251062dbde3610cdba5ba1eee1/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0", size = 3080509, upload-time = "2023-10-03T12:46:37.44Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/05/81e8bc7fca95574c9323e487d9ce1b58a4cfcc17f89b8fe843af46361211/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53", size = 3264303, upload-time = "2023-10-03T12:46:40.73Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/85/62825cabc6aad53104b7b6d12eb2ad74737d268630032d07b74d4444cb72/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be", size = 3019515, upload-time = "2023-10-03T12:46:43.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/b0/9ca2b8e01a0912c9a14234fd5df7a241a1e44778c5797bf4b8eaa8dc3d3a/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", size = 2355892, upload-time = "2023-10-03T12:46:45.632Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/17/ba28bb0022db5e2015a82d2df1c4b0d419c37fa07a588b3aff3adc4939f6/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359", size = 2534903, upload-time = "2023-10-03T12:46:47.934Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/92/b463556409cdc12791cd8b1dae0072bf8efe817ef68b7ea3d9cf7d0e5656/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2", size = 2486597, upload-time = "2023-10-03T12:46:50.598Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/57/96576e07132d7f7a1ac1df939575e6fdd8951aea337ee152b586bb51a971/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc", size = 2454908, upload-time = "2023-10-03T12:46:52.903Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/ae/cedd56e1f4a2b0e37213283caf3733a875c4c76f3372241e19c0d2a87355/psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d", size = 1024240, upload-time = "2023-10-03T12:46:55.009Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/1f/7ae31759142999a8d06b3e250c1346c4abcdcada8fa884376775dc1de686/psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", size = 1163655, upload-time = "2023-10-03T12:46:57.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/d0/5f2db14e7b53552276ab613399a83f83f85b173a862d3f20580bc7231139/psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf", size = 2823784, upload-time = "2023-10-03T12:47:00.404Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/ca/da384fd47233e300e3e485c90e7aab5d7def896d1281239f75901faf87d4/psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d", size = 2553308, upload-time = "2023-11-01T10:40:33.984Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/66/fa53d2d3d92f6e1ef469d92afc6a4fe3f6e8a9a04b687aa28fb1f1d954ee/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212", size = 2851283, upload-time = "2023-10-03T12:47:02.736Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/37/2429360ac5547378202db14eec0dde76edbe1f6627df5a43c7e164922859/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493", size = 3081839, upload-time = "2023-10-03T12:47:05.027Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/2a/c0530b59d7e0d09824bc2102ecdcec0456b8ca4d47c0caa82e86fce3ed4c/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996", size = 3264488, upload-time = "2023-10-03T12:47:08.962Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/57/9f172b900795ea37246c78b5f52e00f4779984370855b3e161600156906d/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119", size = 3020700, upload-time = "2023-10-03T12:47:12.23Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/68/1176fc14ea76861b7b8360be5176e87fb20d5091b137c76570eb4e237324/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba", size = 2355968, upload-time = "2023-10-03T12:47:14.817Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/bb/aec2646a705a09079d008ce88073401cd61fc9b04f92af3eb282caa3a2ec/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07", size = 2536101, upload-time = "2023-10-03T12:47:17.454Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/33/12818c157e333cb9d9e6753d1b2463b6f60dbc1fade115f8e4dc5c52cac4/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb", size = 2487064, upload-time = "2023-10-03T12:47:20.717Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/a2/7851c68fe8768f3c9c246198b6356ee3e4a8a7f6820cc798443faada3400/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe", size = 2456257, upload-time = "2023-10-03T12:47:23.004Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/ee/3ba07c6dc7c3294e717e94720da1597aedc82a10b1b180203ce183d4631a/psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93", size = 1024709, upload-time = "2023-10-28T09:37:24.991Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/08/9c66c269b0d417a0af9fb969535f0371b8c538633535a7a6a5ca3f9231e2/psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab", size = 1163864, upload-time = "2023-10-28T09:37:28.155Z" }, ] [[package]] name = "py" version = "1.11.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", size = 207796, upload-time = "2021-11-04T17:17:01.377Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378", size = 98708, upload-time = "2021-11-04T17:17:00.152Z" }, ] [[package]] name = "py-mini-racer" version = "0.6.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/50/97/a578b918b2e5923dd754cb60bb8b8aeffc85255ffb92566e3c65b148ff72/py_mini_racer-0.6.0.tar.gz", hash = "sha256:f71e36b643d947ba698c57cd9bd2232c83ca997b0802fc2f7f79582377040c11" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/97/a578b918b2e5923dd754cb60bb8b8aeffc85255ffb92566e3c65b148ff72/py_mini_racer-0.6.0.tar.gz", hash = "sha256:f71e36b643d947ba698c57cd9bd2232c83ca997b0802fc2f7f79582377040c11", size = 5994836, upload-time = "2021-04-22T07:58:35.993Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/29/a9/8ce0ca222ef04d602924a1e099be93f5435ca6f3294182a30574d4159ca2/py_mini_racer-0.6.0-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:42896c24968481dd953eeeb11de331f6870917811961c9b26ba09071e07180e2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/a9/8ce0ca222ef04d602924a1e099be93f5435ca6f3294182a30574d4159ca2/py_mini_racer-0.6.0-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:42896c24968481dd953eeeb11de331f6870917811961c9b26ba09071e07180e2", size = 5416149, upload-time = "2021-04-22T07:58:25.615Z" }, ] [[package]] name = "pyarrow" version = "17.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/27/4e/ea6d43f324169f8aec0e57569443a38bab4b398d09769ca64f7b4d467de3/pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/39/5d/78d4b040bc5ff2fc6c3d03e80fca396b742f6c125b8af06bcf7427f931bc/pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/73/8ed168db7642e91180330e4ea9f3ff8bab404678f00d32d7df0871a4933b/pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/36/e78c24be99242063f6d0590ef68c857ea07bdea470242c361e9a15bd57a4/pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545" }, - { url = "https://mirrors.aliyun.com/pypi/packages/18/4c/3db637d7578f683b0a8fb8999b436bdbedd6e3517bd4f90c70853cf3ad20/pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/3c/0580626896c842614a523e66b351181ed5bb14e5dfc263cd68cea2c46d90/pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ee/fb/c1b47f0ada36d856a352da261a44d7344d8f22e2f7db3945f8c3b81be5dd/pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/09/b0a02908180a25d57312ab5919069c39fddf30602568980419f4b02393f6/pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/46/ce89f87c2936f5bb9d879473b9663ce7a4b1f4359acc2f0eb39865eaa1af/pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/8e/ce2e9b2146de422f6638333c01903140e9ada244a2a477918a368306c64c/pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/c8/5675719570eb1acd809481c6d64e2136ffb340bc387f4ca62dce79516cea/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/78/3931194f16ab681ebb87ad252e7b8d2c8b23dad49706cadc865dff4a1dd3/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d8/81/69b6606093363f55a2a574c018901c40952d4e902e670656d18213c71ad7/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4c/21/9ca93b84b92ef927814cb7ba37f0774a484c849d58f0b692b16af8eebcfb/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/30/d1/63a7c248432c71c7d3ee803e706590a0b81ce1a8d2b2ae49677774b813bb/pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/62/ce6ac1275a432b4a27c55fe96c58147f111d8ba1ad800a112d31859fae2f/pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/0a/dbd0c134e7a0c30bea439675cc120012337202e5fac7163ba839aa3691d2/pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cb/05/3f4a16498349db79090767620d6dc23c1ec0c658a668d61d76b87706c65d/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/0c/ea2107236740be8fa0e0d4a293a095c9f43546a2465bb7df34eee9126b09/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/b0/b9164a8bc495083c10c281cc65064553ec87b7537d6f742a89d5953a2a3e/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f1/c4/9625418a1413005e486c006e56675334929fad864347c5ae7c1b2e7fe639/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/49/baafe2a964f663413be3bd1cf5c45ed98c5e42e804e2328e18f4570027c1/pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/4e/ea6d43f324169f8aec0e57569443a38bab4b398d09769ca64f7b4d467de3/pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28", size = 1112479, upload-time = "2024-07-17T10:41:25.092Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/5d/78d4b040bc5ff2fc6c3d03e80fca396b742f6c125b8af06bcf7427f931bc/pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07", size = 28994846, upload-time = "2024-07-16T10:29:13.082Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/73/8ed168db7642e91180330e4ea9f3ff8bab404678f00d32d7df0871a4933b/pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655", size = 27165908, upload-time = "2024-07-16T10:29:20.362Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/36/e78c24be99242063f6d0590ef68c857ea07bdea470242c361e9a15bd57a4/pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545", size = 39264209, upload-time = "2024-07-16T10:29:27.621Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/4c/3db637d7578f683b0a8fb8999b436bdbedd6e3517bd4f90c70853cf3ad20/pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2", size = 39862883, upload-time = "2024-07-16T10:29:34.34Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/3c/0580626896c842614a523e66b351181ed5bb14e5dfc263cd68cea2c46d90/pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8", size = 38723009, upload-time = "2024-07-16T10:29:41.123Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/fb/c1b47f0ada36d856a352da261a44d7344d8f22e2f7db3945f8c3b81be5dd/pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047", size = 39855626, upload-time = "2024-07-16T10:29:49.004Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/09/b0a02908180a25d57312ab5919069c39fddf30602568980419f4b02393f6/pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087", size = 25147242, upload-time = "2024-07-16T10:29:56.195Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/46/ce89f87c2936f5bb9d879473b9663ce7a4b1f4359acc2f0eb39865eaa1af/pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977", size = 29028748, upload-time = "2024-07-16T10:30:02.609Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/8e/ce2e9b2146de422f6638333c01903140e9ada244a2a477918a368306c64c/pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3", size = 27190965, upload-time = "2024-07-16T10:30:10.718Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/c8/5675719570eb1acd809481c6d64e2136ffb340bc387f4ca62dce79516cea/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15", size = 39269081, upload-time = "2024-07-16T10:30:18.878Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/78/3931194f16ab681ebb87ad252e7b8d2c8b23dad49706cadc865dff4a1dd3/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597", size = 39864921, upload-time = "2024-07-16T10:30:27.008Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/81/69b6606093363f55a2a574c018901c40952d4e902e670656d18213c71ad7/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420", size = 38740798, upload-time = "2024-07-16T10:30:34.814Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/21/9ca93b84b92ef927814cb7ba37f0774a484c849d58f0b692b16af8eebcfb/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4", size = 39871877, upload-time = "2024-07-16T10:30:42.672Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/d1/63a7c248432c71c7d3ee803e706590a0b81ce1a8d2b2ae49677774b813bb/pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03", size = 25151089, upload-time = "2024-07-16T10:30:49.279Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/62/ce6ac1275a432b4a27c55fe96c58147f111d8ba1ad800a112d31859fae2f/pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22", size = 29019418, upload-time = "2024-07-16T10:30:55.573Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/0a/dbd0c134e7a0c30bea439675cc120012337202e5fac7163ba839aa3691d2/pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053", size = 27152197, upload-time = "2024-07-16T10:31:02.036Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/05/3f4a16498349db79090767620d6dc23c1ec0c658a668d61d76b87706c65d/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a", size = 39263026, upload-time = "2024-07-16T10:31:10.351Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/0c/ea2107236740be8fa0e0d4a293a095c9f43546a2465bb7df34eee9126b09/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc", size = 39880798, upload-time = "2024-07-16T10:31:17.66Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/b0/b9164a8bc495083c10c281cc65064553ec87b7537d6f742a89d5953a2a3e/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a", size = 38715172, upload-time = "2024-07-16T10:31:25.965Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/c4/9625418a1413005e486c006e56675334929fad864347c5ae7c1b2e7fe639/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b", size = 39874508, upload-time = "2024-07-16T10:31:33.721Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/49/baafe2a964f663413be3bd1cf5c45ed98c5e42e804e2328e18f4570027c1/pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7", size = 25099235, upload-time = "2024-07-16T10:31:40.893Z" }, ] [[package]] name = "pyasn1" version = "0.6.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, ] [[package]] name = "pyasn1-modules" version = "0.4.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, ] [[package]] name = "pyclipper" version = "1.3.0.post5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1b/3d/e5b5ff36b24f3fc9b962a68ce4f6932ab698b8ba860261f402be37b85d17/pyclipper-1.3.0.post5.tar.gz", hash = "sha256:c0239f928e0bf78a3efc2f2f615a10bfcdb9f33012d46d64c8d1225b4bde7096" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/00/38/abcb5e560563b54c552b23a128d824230658cf6c28ae2003f89c23691084/pyclipper-1.3.0.post5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c45f99b8180dd4df4c86642657ca92b7d5289a5e3724521822e0f9461961fe2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/27/04f7977ac7ad12091d32333dea5dcdf704ed189bfb3f54f460b015b9c6f9/pyclipper-1.3.0.post5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:567ffd419a0bdc3727fa4562cfa1f18484691817a2bc0bc675750aa28ed98bd4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/81/4aa8403e587a4c60e00b479c11254a6e3200f3b985dcf4caecf0d8c21261/pyclipper-1.3.0.post5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:59c8c75661a6d87e98b1655851578a2917d3c8859912c9a4f1956b9830940fd9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/c0/adb676ffe4f11d7d2cd2adf847865e419d24380f8de93c5a4cbddf83b23e/pyclipper-1.3.0.post5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a496efa146d2d88b59350021739e4685e439dc569b6654e9e6d5e42e9a0b1666" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2d/85/d6c927908ec53303f8e417aefa7b345061389151bdee434fc6fc16e422a7/pyclipper-1.3.0.post5-cp310-cp310-win32.whl", hash = "sha256:02a98d09af9b60bcf8e9480d153c0839e20b92689f5602f87242a4933842fecd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/60/61/354f484ab7969a601327646bbaeb1b799508b4e81946ea4d52bbf9d779c6/pyclipper-1.3.0.post5-cp310-cp310-win_amd64.whl", hash = "sha256:847f1e2fc3994bb498fe675f55c98129b95dc26a5c92304ba4cf0ab40721ea3d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/47/9c6a9d2523735d7a5ec2991e6a05370b96e19db26c5628fedd1143dc6e4f/pyclipper-1.3.0.post5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b7a983ae019932bfa0a1971a2dc8c856704add5f3d567bed8fac02dbc0e7f0bf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/f0/3e4ca96c1adb32f254ba0ba3a5a4cf4bd6794c285177f10357f3574d11d5/pyclipper-1.3.0.post5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8760075c395b924f894aa16ee06e8c040c6f9b63e0903e49de3cc8d82d9e637" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/79/64cfb4bf0338c3dcd4ef4b819f0fb48a65bc9a9b5b2644cf21a665d08ae8/pyclipper-1.3.0.post5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4ea61ca5899d3346c614951342c506f119601ed0a1f4889a9cc236558afec6b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/64/9c8e0c7d96d32c63e38f92da92e4e38685e30773644d9dcb73d2325beb47/pyclipper-1.3.0.post5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46499b361ae067662b22578401d83d57716f3cc0071d592feb07d504b439fea7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/da/ff/29f1fa1473d6c8abaa2f41f60dfeb23e92819ee67f7d9387715e53e2a414/pyclipper-1.3.0.post5-cp311-cp311-win32.whl", hash = "sha256:d5c77e39ab05a6cf277c819639968b21e6959e996ea1a074afc24236541708ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/ec/56da9f2d5d846f144530d5313a05078afb7cfc26ec179be5af35f057d064/pyclipper-1.3.0.post5-cp311-cp311-win_amd64.whl", hash = "sha256:0f78a1c18ff4f9276f78d9353d6ed4309c3886a9d0172437e48328aef499165e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/87/f0/2a9dbd3359bd834b24691ba65b1011c1a7a7cafd92691165506ece1eeb3b/pyclipper-1.3.0.post5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5237282f906049c307e6c90333c7d56f6b8712bf087ef97b141830c40b09ca0a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/28/30/1b532eff31728e0233684eada4153773af11a81992bb9791160ed27760af/pyclipper-1.3.0.post5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aca8635573646b65c054399433fb3493637f1445db942de8a52fca9ef493ba3d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/95/e0d4c036c1c936b6f7e6265d15f56b5b3ceb4a4d7dcb491cdd2604882f93/pyclipper-1.3.0.post5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1158a2b13d59bdfab33d1d928f7b72c8c7fb8a76e7d2283839cb45d7c0ff2140" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/f3/c5b39f3515d7af0c96b67f6eb13b62d0cd471f348ebafa106d6fcb8d9d33/pyclipper-1.3.0.post5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a041f1a7982b17cf92fd3be349ec41ff1901792149c166bf283f469567b52d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/67/1fe463403bbd2ea7ca79f328a118b12fff495d0e83c98bdf5afd187ccccc/pyclipper-1.3.0.post5-cp312-cp312-win32.whl", hash = "sha256:bf3a2ccd6e4e078250b0a31a12c519b0be6d1bc160acfceee62407dbd68558f6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/f0/760e614b84dd4d8f03dd5432dda100d699e23074c263b38b6c117adc8395/pyclipper-1.3.0.post5-cp312-cp312-win_amd64.whl", hash = "sha256:2ce6e0a6ab32182c26537965cf521822cd11a28a7ffcef48635a94c6ca8559ef" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/3d/e5b5ff36b24f3fc9b962a68ce4f6932ab698b8ba860261f402be37b85d17/pyclipper-1.3.0.post5.tar.gz", hash = "sha256:c0239f928e0bf78a3efc2f2f615a10bfcdb9f33012d46d64c8d1225b4bde7096", size = 164719, upload-time = "2023-09-07T13:02:33.824Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/38/abcb5e560563b54c552b23a128d824230658cf6c28ae2003f89c23691084/pyclipper-1.3.0.post5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c45f99b8180dd4df4c86642657ca92b7d5289a5e3724521822e0f9461961fe2", size = 277364, upload-time = "2023-09-07T13:01:22.438Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/27/04f7977ac7ad12091d32333dea5dcdf704ed189bfb3f54f460b015b9c6f9/pyclipper-1.3.0.post5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:567ffd419a0bdc3727fa4562cfa1f18484691817a2bc0bc675750aa28ed98bd4", size = 145678, upload-time = "2023-09-07T13:01:23.976Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/81/4aa8403e587a4c60e00b479c11254a6e3200f3b985dcf4caecf0d8c21261/pyclipper-1.3.0.post5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:59c8c75661a6d87e98b1655851578a2917d3c8859912c9a4f1956b9830940fd9", size = 908260, upload-time = "2023-09-07T13:01:25.587Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/c0/adb676ffe4f11d7d2cd2adf847865e419d24380f8de93c5a4cbddf83b23e/pyclipper-1.3.0.post5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a496efa146d2d88b59350021739e4685e439dc569b6654e9e6d5e42e9a0b1666", size = 926738, upload-time = "2023-09-07T13:01:27.732Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/85/d6c927908ec53303f8e417aefa7b345061389151bdee434fc6fc16e422a7/pyclipper-1.3.0.post5-cp310-cp310-win32.whl", hash = "sha256:02a98d09af9b60bcf8e9480d153c0839e20b92689f5602f87242a4933842fecd", size = 99179, upload-time = "2023-09-07T13:01:29.823Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/61/354f484ab7969a601327646bbaeb1b799508b4e81946ea4d52bbf9d779c6/pyclipper-1.3.0.post5-cp310-cp310-win_amd64.whl", hash = "sha256:847f1e2fc3994bb498fe675f55c98129b95dc26a5c92304ba4cf0ab40721ea3d", size = 108216, upload-time = "2023-09-07T13:01:31.04Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/47/9c6a9d2523735d7a5ec2991e6a05370b96e19db26c5628fedd1143dc6e4f/pyclipper-1.3.0.post5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b7a983ae019932bfa0a1971a2dc8c856704add5f3d567bed8fac02dbc0e7f0bf", size = 279155, upload-time = "2023-09-07T13:01:32.539Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/f0/3e4ca96c1adb32f254ba0ba3a5a4cf4bd6794c285177f10357f3574d11d5/pyclipper-1.3.0.post5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8760075c395b924f894aa16ee06e8c040c6f9b63e0903e49de3cc8d82d9e637", size = 146859, upload-time = "2023-09-07T13:01:33.926Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/79/64cfb4bf0338c3dcd4ef4b819f0fb48a65bc9a9b5b2644cf21a665d08ae8/pyclipper-1.3.0.post5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4ea61ca5899d3346c614951342c506f119601ed0a1f4889a9cc236558afec6b", size = 952640, upload-time = "2023-09-07T13:01:35.936Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/64/9c8e0c7d96d32c63e38f92da92e4e38685e30773644d9dcb73d2325beb47/pyclipper-1.3.0.post5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46499b361ae067662b22578401d83d57716f3cc0071d592feb07d504b439fea7", size = 971470, upload-time = "2023-09-07T13:01:37.513Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/ff/29f1fa1473d6c8abaa2f41f60dfeb23e92819ee67f7d9387715e53e2a414/pyclipper-1.3.0.post5-cp311-cp311-win32.whl", hash = "sha256:d5c77e39ab05a6cf277c819639968b21e6959e996ea1a074afc24236541708ff", size = 99360, upload-time = "2023-09-07T13:01:39.159Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/ec/56da9f2d5d846f144530d5313a05078afb7cfc26ec179be5af35f057d064/pyclipper-1.3.0.post5-cp311-cp311-win_amd64.whl", hash = "sha256:0f78a1c18ff4f9276f78d9353d6ed4309c3886a9d0172437e48328aef499165e", size = 108311, upload-time = "2023-09-07T13:01:40.526Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/f0/2a9dbd3359bd834b24691ba65b1011c1a7a7cafd92691165506ece1eeb3b/pyclipper-1.3.0.post5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5237282f906049c307e6c90333c7d56f6b8712bf087ef97b141830c40b09ca0a", size = 278102, upload-time = "2023-09-07T13:01:41.778Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/30/1b532eff31728e0233684eada4153773af11a81992bb9791160ed27760af/pyclipper-1.3.0.post5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aca8635573646b65c054399433fb3493637f1445db942de8a52fca9ef493ba3d", size = 145946, upload-time = "2023-09-07T13:01:43.768Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/95/e0d4c036c1c936b6f7e6265d15f56b5b3ceb4a4d7dcb491cdd2604882f93/pyclipper-1.3.0.post5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1158a2b13d59bdfab33d1d928f7b72c8c7fb8a76e7d2283839cb45d7c0ff2140", size = 947205, upload-time = "2023-09-07T13:01:45.147Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/f3/c5b39f3515d7af0c96b67f6eb13b62d0cd471f348ebafa106d6fcb8d9d33/pyclipper-1.3.0.post5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a041f1a7982b17cf92fd3be349ec41ff1901792149c166bf283f469567b52d6", size = 966618, upload-time = "2023-09-07T13:01:47.346Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/67/1fe463403bbd2ea7ca79f328a118b12fff495d0e83c98bdf5afd187ccccc/pyclipper-1.3.0.post5-cp312-cp312-win32.whl", hash = "sha256:bf3a2ccd6e4e078250b0a31a12c519b0be6d1bc160acfceee62407dbd68558f6", size = 98762, upload-time = "2023-09-07T13:01:49.468Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/f0/760e614b84dd4d8f03dd5432dda100d699e23074c263b38b6c117adc8395/pyclipper-1.3.0.post5-cp312-cp312-win_amd64.whl", hash = "sha256:2ce6e0a6ab32182c26537965cf521822cd11a28a7ffcef48635a94c6ca8559ef", size = 108190, upload-time = "2023-09-07T13:01:50.699Z" }, ] [[package]] name = "pycparser" version = "2.22" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, ] [[package]] name = "pycryptodome" version = "3.23.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886, upload-time = "2025-05-17T17:21:20.614Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151, upload-time = "2025-05-17T17:21:22.666Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461, upload-time = "2025-05-17T17:21:25.225Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440, upload-time = "2025-05-17T17:21:27.991Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005, upload-time = "2025-05-17T17:21:31.37Z" }, ] [[package]] name = "pycryptodomex" version = "3.20.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/31/a4/b03a16637574312c1b54c55aedeed8a4cb7d101d44058d46a0e5706c63e1/pycryptodomex-3.20.0.tar.gz", hash = "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7a/09/668b587ddaf2aa0f94ea45bca73e7c564816fd9329a05e8f7f870425981d/pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4c/c4/9b1e8fca01c4b5a0e1c6f52ba19478b2692af4694afe8c89ebbe24348604/pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4e/b9/91af61ec562b87c0932122666603a37cd17f991bc05faf9123b598d1e518/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/7a/3162173af8597f0399b45c6aaa4939ccae908476fdf1b3a3cc30631fc9fb/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/43/e67f7767a76db1067008127a04617165579e6a65b5c3acb230c7383ca514/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bb/29/fb592db3f98b1ed330561518ff4706e869045b0cf27632a4310444731aa1/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/ca/7f296284fad77182ad2b2c198a7ece14b04cc9e6e905b1082c015f2254d3/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/7d/0f2b09490b98cc6a902ac15dda8760c568b9c18cfe70e0ef7a16de64d53a/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/1c/375adb14b71ee1c8d8232904e928b3e7af5bbbca7c04e4bec94fe8e90c3d/pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/e8/1b92184ab7e5595bf38000587e6f8cf9556ebd1bf0a583619bee2057afbd/pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/df/3f1ea084e43b91e6d2b6b3493cc948864c17ea5d93ff1261a03812fbfd1a/pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/f3/83ffbdfa0c8f9154bcd8866895f6cae5a3ec749da8b0840603cf936c4412/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/9d/c113e640aaf02af5631ae2686b742aac5cd0e1402b9d6512b1c7ec5ef05d/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e4/8a/7c621942787a20d4cb7c32f0c49f183781c6b8753e6ba4f92e57a6d8b1f5/pycryptodomex-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/a4/b03a16637574312c1b54c55aedeed8a4cb7d101d44058d46a0e5706c63e1/pycryptodomex-3.20.0.tar.gz", hash = "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e", size = 4794613, upload-time = "2024-01-10T11:32:34.067Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/09/668b587ddaf2aa0f94ea45bca73e7c564816fd9329a05e8f7f870425981d/pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c", size = 2430400, upload-time = "2024-01-10T11:31:44.072Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/c4/9b1e8fca01c4b5a0e1c6f52ba19478b2692af4694afe8c89ebbe24348604/pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3", size = 1593362, upload-time = "2024-01-10T11:31:47.048Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/b9/91af61ec562b87c0932122666603a37cd17f991bc05faf9123b598d1e518/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623", size = 2065201, upload-time = "2024-01-10T11:31:49.86Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/7a/3162173af8597f0399b45c6aaa4939ccae908476fdf1b3a3cc30631fc9fb/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4", size = 2139169, upload-time = "2024-01-10T11:31:53.189Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/43/e67f7767a76db1067008127a04617165579e6a65b5c3acb230c7383ca514/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5", size = 2167742, upload-time = "2024-01-10T11:31:56.322Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/29/fb592db3f98b1ed330561518ff4706e869045b0cf27632a4310444731aa1/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed", size = 2057793, upload-time = "2024-01-10T11:31:58.39Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/ca/7f296284fad77182ad2b2c198a7ece14b04cc9e6e905b1082c015f2254d3/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7", size = 2196243, upload-time = "2024-01-10T11:32:01.309Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/7d/0f2b09490b98cc6a902ac15dda8760c568b9c18cfe70e0ef7a16de64d53a/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43", size = 2158708, upload-time = "2024-01-10T11:32:03.55Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/1c/375adb14b71ee1c8d8232904e928b3e7af5bbbca7c04e4bec94fe8e90c3d/pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e", size = 1726798, upload-time = "2024-01-10T11:32:05.521Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/e8/1b92184ab7e5595bf38000587e6f8cf9556ebd1bf0a583619bee2057afbd/pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc", size = 1762906, upload-time = "2024-01-10T11:32:07.563Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/df/3f1ea084e43b91e6d2b6b3493cc948864c17ea5d93ff1261a03812fbfd1a/pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b", size = 1569076, upload-time = "2024-01-10T11:32:14.793Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/f3/83ffbdfa0c8f9154bcd8866895f6cae5a3ec749da8b0840603cf936c4412/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea", size = 1609872, upload-time = "2024-01-10T11:32:17.268Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/9d/c113e640aaf02af5631ae2686b742aac5cd0e1402b9d6512b1c7ec5ef05d/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781", size = 1640752, upload-time = "2024-01-10T11:32:20.027Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/8a/7c621942787a20d4cb7c32f0c49f183781c6b8753e6ba4f92e57a6d8b1f5/pycryptodomex-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499", size = 1744274, upload-time = "2024-01-10T11:32:22.083Z" }, ] [[package]] name = "pydantic" version = "2.9.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917, upload-time = "2024-09-17T15:59:54.273Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928, upload-time = "2024-09-17T15:59:51.827Z" }, ] [[package]] name = "pydantic-core" version = "2.23.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5c/8b/d3ae387f66277bd8104096d6ec0a145f4baa2966ebb2cad746c0920c9526/pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/76/f68272e4c3a7df8777798282c5e47d508274917f29992d84e1898f8908c7/pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/69/5f945b4416f42ea3f3bc9d2aaec66c76084a6ff4ff27555bf9415ab43189/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/ab/891a7b0054bcc297fb02d44d05c50e68154e31788f2d9d41d0b72c89fdf7/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/7c/6e3fa122075d78f277a8431c4c608f061881b76c2b7faca01d317ee39b5d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/6f/22d5692b7ab63fc4acbc74de6ff61d185804a83160adba5e6cc6068e1128/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/ac/1e647dc1121c028b691028fa61a4e7477e6aeb5132628fde41dd34c1671f/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/75/984740c17f12c3ce18b5a2fcc4bdceb785cce7df1511a4ce89bca17c7e2d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a0/74/13c5f606b64d93f0721e7768cd3e8b2102164866c207b8cd6f90bb15d24f/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/18/03/9c4aa5919457c7b57a016c1ab513b1a926ed9b2bb7915bf8e506bf65c34b/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/2c/053d33f029c5dc65e5cf44ff03ceeefb7cce908f8f3cca9265e7f9b540c8/pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119" }, - { url = "https://mirrors.aliyun.com/pypi/packages/de/81/7dfe464eca78d76d31dd661b04b5f2036ec72ea8848dd87ab7375e185c23/pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/8f/89c1405176903e567c5f99ec53387449e62f1121894aa9fc2c4fdc51a59b/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/a5/1a194447d0da1ef492e3470680c66048fef56fc1f1a25cafbea4bc1d1c48/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/a5/1df8541651de4455e7d587cf556201b4f7997191e110bca3b589218745a5/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/31/a3899b5ce02c4316865e390107f145089876dff7e1dfc770a231d836aed8/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/aa/98e190f8745d5ec831f6d5449344c48c0627ac5fed4e5340a44b74878f8e/pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/35/b6e00b6abb2acfee3e8f85558c02a0822e9a8b2f2d812ea8b9079b118ba0/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/46/7bee6d32b69191cd649bbbd2361af79c472d72cb29bb2024f0b6e350ba06/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64" }, - { url = "https://mirrors.aliyun.com/pypi/packages/39/ef/7b34f1b122a81b68ed0a7d0e564da9ccdc9a2924c8d6c6b5b11fa3a56970/pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2f/76/37b7e76c645843ff46c1d73e046207311ef298d3f7b2f7d8f6ac60113071/pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231" }, - { url = "https://mirrors.aliyun.com/pypi/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36" }, - { url = "https://mirrors.aliyun.com/pypi/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/18/812222b6d18c2d13eebbb0f7cdc170a408d9ced65794fdb86147c77e1982/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/36/c1f3642ac3f05e6bb4aec3ffc399fa3f84895d259cf5f0ce3054b7735c29/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156, upload-time = "2024-09-16T16:06:44.786Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/8b/d3ae387f66277bd8104096d6ec0a145f4baa2966ebb2cad746c0920c9526/pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b", size = 1867835, upload-time = "2024-09-16T16:03:57.223Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/76/f68272e4c3a7df8777798282c5e47d508274917f29992d84e1898f8908c7/pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166", size = 1776689, upload-time = "2024-09-16T16:03:59.266Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/69/5f945b4416f42ea3f3bc9d2aaec66c76084a6ff4ff27555bf9415ab43189/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb", size = 1800748, upload-time = "2024-09-16T16:04:01.011Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/ab/891a7b0054bcc297fb02d44d05c50e68154e31788f2d9d41d0b72c89fdf7/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916", size = 1806469, upload-time = "2024-09-16T16:04:02.323Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/7c/6e3fa122075d78f277a8431c4c608f061881b76c2b7faca01d317ee39b5d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07", size = 2002246, upload-time = "2024-09-16T16:04:03.688Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/6f/22d5692b7ab63fc4acbc74de6ff61d185804a83160adba5e6cc6068e1128/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232", size = 2659404, upload-time = "2024-09-16T16:04:05.299Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/ac/1e647dc1121c028b691028fa61a4e7477e6aeb5132628fde41dd34c1671f/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2", size = 2053940, upload-time = "2024-09-16T16:04:06.604Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/75/984740c17f12c3ce18b5a2fcc4bdceb785cce7df1511a4ce89bca17c7e2d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f", size = 1921437, upload-time = "2024-09-16T16:04:08.071Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/74/13c5f606b64d93f0721e7768cd3e8b2102164866c207b8cd6f90bb15d24f/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3", size = 1966129, upload-time = "2024-09-16T16:04:10.363Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/03/9c4aa5919457c7b57a016c1ab513b1a926ed9b2bb7915bf8e506bf65c34b/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071", size = 2110908, upload-time = "2024-09-16T16:04:12.412Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/2c/053d33f029c5dc65e5cf44ff03ceeefb7cce908f8f3cca9265e7f9b540c8/pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119", size = 1735278, upload-time = "2024-09-16T16:04:13.732Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/81/7dfe464eca78d76d31dd661b04b5f2036ec72ea8848dd87ab7375e185c23/pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f", size = 1917453, upload-time = "2024-09-16T16:04:15.996Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", size = 1865160, upload-time = "2024-09-16T16:04:18.628Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", size = 1776777, upload-time = "2024-09-16T16:04:20.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", size = 1799244, upload-time = "2024-09-16T16:04:21.799Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/8f/89c1405176903e567c5f99ec53387449e62f1121894aa9fc2c4fdc51a59b/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607", size = 1805307, upload-time = "2024-09-16T16:04:23.324Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/a5/1a194447d0da1ef492e3470680c66048fef56fc1f1a25cafbea4bc1d1c48/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd", size = 2000663, upload-time = "2024-09-16T16:04:25.203Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/a5/1df8541651de4455e7d587cf556201b4f7997191e110bca3b589218745a5/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea", size = 2655941, upload-time = "2024-09-16T16:04:27.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/31/a3899b5ce02c4316865e390107f145089876dff7e1dfc770a231d836aed8/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e", size = 2052105, upload-time = "2024-09-16T16:04:28.611Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/aa/98e190f8745d5ec831f6d5449344c48c0627ac5fed4e5340a44b74878f8e/pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b", size = 1919967, upload-time = "2024-09-16T16:04:30.045Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/35/b6e00b6abb2acfee3e8f85558c02a0822e9a8b2f2d812ea8b9079b118ba0/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0", size = 1964291, upload-time = "2024-09-16T16:04:32.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/46/7bee6d32b69191cd649bbbd2361af79c472d72cb29bb2024f0b6e350ba06/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64", size = 2109666, upload-time = "2024-09-16T16:04:33.923Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/ef/7b34f1b122a81b68ed0a7d0e564da9ccdc9a2924c8d6c6b5b11fa3a56970/pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f", size = 1732940, upload-time = "2024-09-16T16:04:35.467Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/76/37b7e76c645843ff46c1d73e046207311ef298d3f7b2f7d8f6ac60113071/pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3", size = 1916804, upload-time = "2024-09-16T16:04:37.06Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", size = 1856459, upload-time = "2024-09-16T16:04:38.438Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", size = 1770007, upload-time = "2024-09-16T16:04:40.229Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", size = 1790245, upload-time = "2024-09-16T16:04:41.794Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", size = 1801260, upload-time = "2024-09-16T16:04:43.991Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", size = 1996872, upload-time = "2024-09-16T16:04:45.593Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", size = 2661617, upload-time = "2024-09-16T16:04:47.3Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", size = 2071831, upload-time = "2024-09-16T16:04:48.893Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", size = 1917453, upload-time = "2024-09-16T16:04:51.099Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", size = 1968793, upload-time = "2024-09-16T16:04:52.604Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872, upload-time = "2024-09-16T16:04:54.41Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535, upload-time = "2024-09-16T16:04:55.828Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992, upload-time = "2024-09-16T16:04:57.395Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135, upload-time = "2024-09-16T16:06:10.45Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583, upload-time = "2024-09-16T16:06:12.298Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637, upload-time = "2024-09-16T16:06:14.092Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/18/812222b6d18c2d13eebbb0f7cdc170a408d9ced65794fdb86147c77e1982/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068", size = 1941963, upload-time = "2024-09-16T16:06:16.757Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/36/c1f3642ac3f05e6bb4aec3ffc399fa3f84895d259cf5f0ce3054b7735c29/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801", size = 1915332, upload-time = "2024-09-16T16:06:18.677Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", size = 1957926, upload-time = "2024-09-16T16:06:20.591Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", size = 2100342, upload-time = "2024-09-16T16:06:22.888Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", size = 1920344, upload-time = "2024-09-16T16:06:24.849Z" }, ] [[package]] name = "pydantic-settings" version = "2.10.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, ] [[package]] name = "pydash" version = "7.0.7" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1a/15/dfb29b8c49e40b9bfd2482f0d81b49deeef8146cc528d21dd8e67751e945/pydash-7.0.7.tar.gz", hash = "sha256:cc935d5ac72dd41fb4515bdf982e7c864c8b5eeea16caffbab1936b849aaa49a" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/15/dfb29b8c49e40b9bfd2482f0d81b49deeef8146cc528d21dd8e67751e945/pydash-7.0.7.tar.gz", hash = "sha256:cc935d5ac72dd41fb4515bdf982e7c864c8b5eeea16caffbab1936b849aaa49a", size = 184993, upload-time = "2024-01-28T02:22:34.143Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ad/bf/7f7413f9f2aad4c1167cb05a231903fe65847fc91b7115a4dd9d9ebd4f1f/pydash-7.0.7-py3-none-any.whl", hash = "sha256:c3c5b54eec0a562e0080d6f82a14ad4d5090229847b7e554235b5c1558c745e1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/bf/7f7413f9f2aad4c1167cb05a231903fe65847fc91b7115a4dd9d9ebd4f1f/pydash-7.0.7-py3-none-any.whl", hash = "sha256:c3c5b54eec0a562e0080d6f82a14ad4d5090229847b7e554235b5c1558c745e1", size = 110286, upload-time = "2024-01-28T02:22:31.355Z" }, ] [[package]] name = "pydivert" version = "2.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cf/71/2da9bcf742df3ab23f75f10fedca074951dd13a84bda8dea3077f68ae9a6/pydivert-2.1.0.tar.gz", hash = "sha256:f0e150f4ff591b78e35f514e319561dadff7f24a82186a171dd4d465483de5b4" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/71/2da9bcf742df3ab23f75f10fedca074951dd13a84bda8dea3077f68ae9a6/pydivert-2.1.0.tar.gz", hash = "sha256:f0e150f4ff591b78e35f514e319561dadff7f24a82186a171dd4d465483de5b4", size = 91057, upload-time = "2017-10-20T21:36:58.165Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ca/8f/86d7931c62013a5a7ebf4e1642a87d4a6050c0f570e714f61b0df1984c62/pydivert-2.1.0-py2.py3-none-any.whl", hash = "sha256:382db488e3c37c03ec9ec94e061a0b24334d78dbaeebb7d4e4d32ce4355d9da1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/8f/86d7931c62013a5a7ebf4e1642a87d4a6050c0f570e714f61b0df1984c62/pydivert-2.1.0-py2.py3-none-any.whl", hash = "sha256:382db488e3c37c03ec9ec94e061a0b24334d78dbaeebb7d4e4d32ce4355d9da1", size = 104718, upload-time = "2017-10-20T21:36:56.726Z" }, ] [[package]] name = "pyee" version = "13.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, ] [[package]] name = "pyexecjs" version = "1.5.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ba/8e/aedef81641c8dca6fd0fb7294de5bed9c45f3397d67fddf755c1042c2642/PyExecJS-1.5.1.tar.gz", hash = "sha256:34cc1d070976918183ff7bdc0ad71f8157a891c92708c00c5fbbff7a769f505c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/8e/aedef81641c8dca6fd0fb7294de5bed9c45f3397d67fddf755c1042c2642/PyExecJS-1.5.1.tar.gz", hash = "sha256:34cc1d070976918183ff7bdc0ad71f8157a891c92708c00c5fbbff7a769f505c", size = 13344, upload-time = "2018-01-18T04:33:55.126Z" } [[package]] name = "pygments" version = "2.19.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pyicu" version = "2.15.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/88/b0/c8b61bac55424e2ff80e20d7251c3f002baff3c07c34cee3849e3505d8f5/pyicu-2.15.3.tar.gz", hash = "sha256:f32e78e1cb64d0aeb14f027e037a8944861d3114548818a6adf0081ef51aefc3" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/b0/c8b61bac55424e2ff80e20d7251c3f002baff3c07c34cee3849e3505d8f5/pyicu-2.15.3.tar.gz", hash = "sha256:f32e78e1cb64d0aeb14f027e037a8944861d3114548818a6adf0081ef51aefc3", size = 267569, upload-time = "2025-09-15T20:58:50.936Z" } [[package]] name = "pyjwt" version = "2.8.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/30/72/8259b2bccfe4673330cea843ab23f86858a419d8f1493f66d413a76c7e3b/PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/72/8259b2bccfe4673330cea843ab23f86858a419d8f1493f66d413a76c7e3b/PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de", size = 78313, upload-time = "2023-07-18T20:02:22.594Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2b/4f/e04a8067c7c96c364cef7ef73906504e2f40d690811c021e1a1901473a19/PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/4f/e04a8067c7c96c364cef7ef73906504e2f40d690811c021e1a1901473a19/PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320", size = 22591, upload-time = "2023-07-18T20:02:21.561Z" }, ] [package.optional-dependencies] @@ -5034,16 +5034,16 @@ crypto = [ [[package]] name = "pymysql" version = "1.1.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b3/8f/ce59b5e5ed4ce8512f879ff1fa5ab699d211ae2495f1adaa5fbba2a1eada/pymysql-1.1.1.tar.gz", hash = "sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/8f/ce59b5e5ed4ce8512f879ff1fa5ab699d211ae2495f1adaa5fbba2a1eada/pymysql-1.1.1.tar.gz", hash = "sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0", size = 47678, upload-time = "2024-05-21T11:03:43.722Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0c/94/e4181a1f6286f545507528c78016e00065ea913276888db2262507693ce5/PyMySQL-1.1.1-py3-none-any.whl", hash = "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/94/e4181a1f6286f545507528c78016e00065ea913276888db2262507693ce5/PyMySQL-1.1.1-py3-none-any.whl", hash = "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c", size = 44972, upload-time = "2024-05-21T11:03:41.216Z" }, ] [[package]] name = "pynndescent" version = "0.5.13" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "joblib" }, { name = "llvmlite" }, @@ -5051,159 +5051,159 @@ dependencies = [ { name = "scikit-learn" }, { name = "scipy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7e/58/560a4db5eb3794d922fe55804b10326534ded3d971e1933c1eef91193f5e/pynndescent-0.5.13.tar.gz", hash = "sha256:d74254c0ee0a1eeec84597d5fe89fedcf778593eeabe32c2f97412934a9800fb" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/58/560a4db5eb3794d922fe55804b10326534ded3d971e1933c1eef91193f5e/pynndescent-0.5.13.tar.gz", hash = "sha256:d74254c0ee0a1eeec84597d5fe89fedcf778593eeabe32c2f97412934a9800fb", size = 2975955, upload-time = "2024-06-17T15:48:32.914Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d2/53/d23a97e0a2c690d40b165d1062e2c4ccc796be458a1ce59f6ba030434663/pynndescent-0.5.13-py3-none-any.whl", hash = "sha256:69aabb8f394bc631b6ac475a1c7f3994c54adf3f51cd63b2730fefba5771b949" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/53/d23a97e0a2c690d40b165d1062e2c4ccc796be458a1ce59f6ba030434663/pynndescent-0.5.13-py3-none-any.whl", hash = "sha256:69aabb8f394bc631b6ac475a1c7f3994c54adf3f51cd63b2730fefba5771b949", size = 56850, upload-time = "2024-06-17T15:48:31.184Z" }, ] [[package]] name = "pyodbc" version = "5.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a0/36/a1ac7d23a1611e7ccd4d27df096f3794e8d1e7faa040260d9d41b6fc3185/pyodbc-5.2.0.tar.gz", hash = "sha256:de8be39809c8ddeeee26a4b876a6463529cd487a60d1393eb2a93e9bcd44a8f5" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/30/01/05c4a4ec122c4a8a37fa1be5bdbf6fb23724a2ee3b1b771bb46f710158a9/pyodbc-5.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb0850e3e3782f57457feed297e220bb20c3e8fd7550d7a6b6bb96112bd9b6fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/22/ba718cc5508bdfbb53e1906018d7f597be37241c769dda8a48f52af96fe3/pyodbc-5.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0dae0fb86078c87acf135dbe5afd3c7d15d52ab0db5965c44159e84058c3e2fb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/e4/9d859ea3642059c10a6644a00ccb1f8b8e02c1e4f49ab34250db1273c2c5/pyodbc-5.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6493b9c7506ca964b80ad638d0dc82869df7058255d71f04fdd1405e88bcb36b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/a7/98c3555c10cfeb343ec7eea69ecb17476aa3ace72131ea8a4a1f8250318c/pyodbc-5.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e04de873607fb960e71953c164c83e8e5d9291ce0d69e688e54947b254b04902" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/c1/d5b16dd62eb70f281bc90cdc1e3c46af7acda3f0f6afb34553206506ccb2/pyodbc-5.2.0-cp310-cp310-win32.whl", hash = "sha256:74135cb10c1dcdbd99fe429c61539c232140e62939fa7c69b0a373cc552e4a08" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f5/12/22c83669abee4ca5915aa89172cf1673b58ca05f44dabeb8b0bac9b7fecc/pyodbc-5.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d287121eeaa562b9ab3d4c52fa77c793dfedd127049273eb882a05d3d67a8ce8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/a2/5907ce319a571eb1e271d6a475920edfeacd92da1021bb2a15ed1b7f6ac1/pyodbc-5.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4627779f0a608b51ce2d2fe6d1d395384e65ca36248bf9dbb6d7cf2c8fda1cab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/b8/bd438ab2bb9481615142784b0c9778079a87ae1bca7a0fe8aabfc088aa9f/pyodbc-5.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d997d3b6551273647825c734158ca8a6f682df269f6b3975f2499c01577ddec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8b/82/cf71ae99b511a7f20c380ce470de233a0291fa3798afa74e0adc8fad1675/pyodbc-5.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5102007a8c78dd2fc1c1b6f6147de8cfc020f81013e4b46c33e66aaa7d1bf7b1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/ea/03fe042f4a390df05e753ddd21c6cab006baae1eee71ce230f6e2a883944/pyodbc-5.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e3cbc7075a46c411b531ada557c4aef13d034060a70077717124cabc1717e2d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/80/48178bb50990147adb72ec9e377e94517a0dfaf2f2a6e3fe477d9a33671f/pyodbc-5.2.0-cp311-cp311-win32.whl", hash = "sha256:de1ee7ec2eb326b7be5e2c4ce20d472c5ef1a6eb838d126d1d26779ff5486e49" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/6b/f0ad7d8a535d58f35f375ffbf367c68d0ec54452a431d23b0ebee4cd44c6/pyodbc-5.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:113f904b9852c12f10c7a3288f5a3563ecdbbefe3ccc829074a9eb8255edcd29" }, - { url = "https://mirrors.aliyun.com/pypi/packages/26/26/104525b728fedfababd3143426b9d0008c70f0d604a3bf5d4773977d83f4/pyodbc-5.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be43d1ece4f2cf4d430996689d89a1a15aeb3a8da8262527e5ced5aee27e89c3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4f/7d/bb632488b603bcd2a6753b858e8bc7dd56146dd19bd72003cc09ae6e3fc0/pyodbc-5.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9f7badd0055221a744d76c11440c0856fd2846ed53b6555cf8f0a8893a3e4b03" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/38/a1b9bfe5a7062672268553c2d6ff93676173b0fb4bd583e8c4f74a0e296f/pyodbc-5.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad633c52f4f4e7691daaa2278d6e6ebb2fe4ae7709e610e22c7dd1a1d620cf8b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/82/ddb1c41c682550116f391aa6cab2052910046a30d63014bbe6d09c4958f4/pyodbc-5.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97d086a8f7a302b74c9c2e77bedf954a603b19168af900d4d3a97322e773df63" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/29/fec0e739d0c1cab155843ed71d0717f5e1694effe3f28d397168f48bcd92/pyodbc-5.2.0-cp312-cp312-win32.whl", hash = "sha256:0e4412f8e608db2a4be5bcc75f9581f386ed6a427dbcb5eac795049ba6fc205e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/7f/3a47e022a97b017ffb73351a1061e4401bcb5aa4fc0162d04f4e5452e4fc/pyodbc-5.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1f5686b142759c5b2bdbeaa0692622c2ebb1f10780eb3c174b85f5607fbcf55" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/36/a1ac7d23a1611e7ccd4d27df096f3794e8d1e7faa040260d9d41b6fc3185/pyodbc-5.2.0.tar.gz", hash = "sha256:de8be39809c8ddeeee26a4b876a6463529cd487a60d1393eb2a93e9bcd44a8f5", size = 116908, upload-time = "2024-10-16T01:40:13.425Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/01/05c4a4ec122c4a8a37fa1be5bdbf6fb23724a2ee3b1b771bb46f710158a9/pyodbc-5.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb0850e3e3782f57457feed297e220bb20c3e8fd7550d7a6b6bb96112bd9b6fe", size = 72483, upload-time = "2024-10-16T01:39:23.697Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/22/ba718cc5508bdfbb53e1906018d7f597be37241c769dda8a48f52af96fe3/pyodbc-5.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0dae0fb86078c87acf135dbe5afd3c7d15d52ab0db5965c44159e84058c3e2fb", size = 71794, upload-time = "2024-10-16T01:39:25.372Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/e4/9d859ea3642059c10a6644a00ccb1f8b8e02c1e4f49ab34250db1273c2c5/pyodbc-5.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6493b9c7506ca964b80ad638d0dc82869df7058255d71f04fdd1405e88bcb36b", size = 332850, upload-time = "2024-10-16T01:39:27.789Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/a7/98c3555c10cfeb343ec7eea69ecb17476aa3ace72131ea8a4a1f8250318c/pyodbc-5.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e04de873607fb960e71953c164c83e8e5d9291ce0d69e688e54947b254b04902", size = 336009, upload-time = "2024-10-16T01:39:29.694Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/c1/d5b16dd62eb70f281bc90cdc1e3c46af7acda3f0f6afb34553206506ccb2/pyodbc-5.2.0-cp310-cp310-win32.whl", hash = "sha256:74135cb10c1dcdbd99fe429c61539c232140e62939fa7c69b0a373cc552e4a08", size = 62407, upload-time = "2024-10-16T01:39:31.894Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/12/22c83669abee4ca5915aa89172cf1673b58ca05f44dabeb8b0bac9b7fecc/pyodbc-5.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d287121eeaa562b9ab3d4c52fa77c793dfedd127049273eb882a05d3d67a8ce8", size = 68874, upload-time = "2024-10-16T01:39:33.325Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/a2/5907ce319a571eb1e271d6a475920edfeacd92da1021bb2a15ed1b7f6ac1/pyodbc-5.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4627779f0a608b51ce2d2fe6d1d395384e65ca36248bf9dbb6d7cf2c8fda1cab", size = 72536, upload-time = "2024-10-16T01:39:34.715Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/b8/bd438ab2bb9481615142784b0c9778079a87ae1bca7a0fe8aabfc088aa9f/pyodbc-5.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d997d3b6551273647825c734158ca8a6f682df269f6b3975f2499c01577ddec", size = 71825, upload-time = "2024-10-16T01:39:36.343Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/82/cf71ae99b511a7f20c380ce470de233a0291fa3798afa74e0adc8fad1675/pyodbc-5.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5102007a8c78dd2fc1c1b6f6147de8cfc020f81013e4b46c33e66aaa7d1bf7b1", size = 342304, upload-time = "2024-10-16T01:39:37.82Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/ea/03fe042f4a390df05e753ddd21c6cab006baae1eee71ce230f6e2a883944/pyodbc-5.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e3cbc7075a46c411b531ada557c4aef13d034060a70077717124cabc1717e2d", size = 346186, upload-time = "2024-10-16T01:39:39.3Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/80/48178bb50990147adb72ec9e377e94517a0dfaf2f2a6e3fe477d9a33671f/pyodbc-5.2.0-cp311-cp311-win32.whl", hash = "sha256:de1ee7ec2eb326b7be5e2c4ce20d472c5ef1a6eb838d126d1d26779ff5486e49", size = 62418, upload-time = "2024-10-16T01:39:40.797Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/6b/f0ad7d8a535d58f35f375ffbf367c68d0ec54452a431d23b0ebee4cd44c6/pyodbc-5.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:113f904b9852c12f10c7a3288f5a3563ecdbbefe3ccc829074a9eb8255edcd29", size = 68871, upload-time = "2024-10-16T01:39:41.997Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/26/104525b728fedfababd3143426b9d0008c70f0d604a3bf5d4773977d83f4/pyodbc-5.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be43d1ece4f2cf4d430996689d89a1a15aeb3a8da8262527e5ced5aee27e89c3", size = 73014, upload-time = "2024-10-16T01:39:43.332Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/7d/bb632488b603bcd2a6753b858e8bc7dd56146dd19bd72003cc09ae6e3fc0/pyodbc-5.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9f7badd0055221a744d76c11440c0856fd2846ed53b6555cf8f0a8893a3e4b03", size = 72515, upload-time = "2024-10-16T01:39:44.506Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/38/a1b9bfe5a7062672268553c2d6ff93676173b0fb4bd583e8c4f74a0e296f/pyodbc-5.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad633c52f4f4e7691daaa2278d6e6ebb2fe4ae7709e610e22c7dd1a1d620cf8b", size = 348561, upload-time = "2024-10-16T01:39:45.986Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/82/ddb1c41c682550116f391aa6cab2052910046a30d63014bbe6d09c4958f4/pyodbc-5.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97d086a8f7a302b74c9c2e77bedf954a603b19168af900d4d3a97322e773df63", size = 353962, upload-time = "2024-10-16T01:39:47.254Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/29/fec0e739d0c1cab155843ed71d0717f5e1694effe3f28d397168f48bcd92/pyodbc-5.2.0-cp312-cp312-win32.whl", hash = "sha256:0e4412f8e608db2a4be5bcc75f9581f386ed6a427dbcb5eac795049ba6fc205e", size = 63050, upload-time = "2024-10-16T01:39:48.8Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/7f/3a47e022a97b017ffb73351a1061e4401bcb5aa4fc0162d04f4e5452e4fc/pyodbc-5.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1f5686b142759c5b2bdbeaa0692622c2ebb1f10780eb3c174b85f5607fbcf55", size = 69485, upload-time = "2024-10-16T01:39:49.732Z" }, ] [[package]] name = "pyopenssl" version = "25.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cryptography" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/04/8c/cd89ad05804f8e3c17dea8f178c3f40eeab5694c30e0c9f5bcd49f576fc3/pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/8c/cd89ad05804f8e3c17dea8f178c3f40eeab5694c30e0c9f5bcd49f576fc3/pyopenssl-25.1.0.tar.gz", hash = "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b", size = 179937, upload-time = "2025-05-17T16:28:31.31Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", size = 56771, upload-time = "2025-05-17T16:28:29.197Z" }, ] [[package]] name = "pyparsing" version = "3.2.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, ] [[package]] name = "pypdf" version = "6.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/20/ac/a300a03c3b34967c050677ccb16e7a4b65607ee5df9d51e8b6d713de4098/pypdf-6.0.0.tar.gz", hash = "sha256:282a99d2cc94a84a3a3159f0d9358c0af53f85b4d28d76ea38b96e9e5ac2a08d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/ac/a300a03c3b34967c050677ccb16e7a4b65607ee5df9d51e8b6d713de4098/pypdf-6.0.0.tar.gz", hash = "sha256:282a99d2cc94a84a3a3159f0d9358c0af53f85b4d28d76ea38b96e9e5ac2a08d", size = 5033827, upload-time = "2025-08-11T14:22:02.352Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2c/83/2cacc506eb322bb31b747bc06ccb82cc9aa03e19ee9c1245e538e49d52be/pypdf-6.0.0-py3-none-any.whl", hash = "sha256:56ea60100ce9f11fc3eec4f359da15e9aec3821b036c1f06d2b660d35683abb8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/83/2cacc506eb322bb31b747bc06ccb82cc9aa03e19ee9c1245e538e49d52be/pypdf-6.0.0-py3-none-any.whl", hash = "sha256:56ea60100ce9f11fc3eec4f359da15e9aec3821b036c1f06d2b660d35683abb8", size = 310465, upload-time = "2025-08-11T14:22:00.481Z" }, ] [[package]] name = "pypdf2" version = "3.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9f/bb/18dc3062d37db6c491392007dfd1a7f524bb95886eb956569ac38a23a784/PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/bb/18dc3062d37db6c491392007dfd1a7f524bb95886eb956569ac38a23a784/PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440", size = 227419, upload-time = "2022-12-31T10:36:13.13Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8e/5e/c86a5643653825d3c913719e788e41386bee415c2b87b4f955432f2de6b2/pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/5e/c86a5643653825d3c913719e788e41386bee415c2b87b4f955432f2de6b2/pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928", size = 232572, upload-time = "2022-12-31T10:36:10.327Z" }, ] [[package]] name = "pypdfium2" version = "4.30.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/14/838b3ba247a0ba92e4df5d23f2bea9478edcfd72b78a39d6ca36ccd84ad2/pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16", size = 140239, upload-time = "2024-05-09T18:33:17.552Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/9a/c8ff5cc352c1b60b0b97642ae734f51edbab6e28b45b4fcdfe5306ee3c83/pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab", size = 2837254, upload-time = "2024-05-09T18:32:48.653Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/8b/27d4d5409f3c76b985f4ee4afe147b606594411e15ac4dc1c3363c9a9810/pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de", size = 2707624, upload-time = "2024-05-09T18:32:51.458Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/63/28a73ca17c24b41a205d658e177d68e198d7dde65a8c99c821d231b6ee3d/pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854", size = 2793126, upload-time = "2024-05-09T18:32:53.581Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/96/53b3ebf0955edbd02ac6da16a818ecc65c939e98fdeb4e0958362bd385c8/pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2", size = 2591077, upload-time = "2024-05-09T18:32:55.99Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/ee/0394e56e7cab8b5b21f744d988400948ef71a9a892cbeb0b200d324ab2c7/pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad", size = 2864431, upload-time = "2024-05-09T18:32:57.911Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/cd/3f1edf20a0ef4a212a5e20a5900e64942c5a374473671ac0780eaa08ea80/pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f", size = 2812008, upload-time = "2024-05-09T18:32:59.886Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/91/2d517db61845698f41a2a974de90762e50faeb529201c6b3574935969045/pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163", size = 6181543, upload-time = "2024-05-09T18:33:02.597Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/c4/ed1315143a7a84b2c7616569dfb472473968d628f17c231c39e29ae9d780/pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e", size = 6175911, upload-time = "2024-05-09T18:33:05.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/c4/9e62d03f414e0e3051c56d5943c3bf42aa9608ede4e19dc96438364e9e03/pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be", size = 6267430, upload-time = "2024-05-09T18:33:08.067Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/47/eda4904f715fb98561e34012826e883816945934a851745570521ec89520/pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e", size = 2775951, upload-time = "2024-05-09T18:33:10.567Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/bd/56d9ec6b9f0fc4e0d95288759f3179f0fcd34b1a1526b75673d2f6d5196f/pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c", size = 2892098, upload-time = "2024-05-09T18:33:13.107Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118, upload-time = "2024-05-09T18:33:15.489Z" }, ] [[package]] name = "pyreadline3" version = "3.5.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, ] [[package]] name = "pysocks" version = "1.7.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, ] [[package]] name = "pystemmer" version = "2.2.0.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/26/12/0378ba4391a4674067ae9db0e025ec998f6ca74caddd22fbdc59dc19aafb/pystemmer-2.2.0.3.tar.gz", hash = "sha256:9ac74c8d0f3358dbb050f64cddbb8d55021d831d92305d7c20780ea8d6c0020e" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c1/c6/6929a114ca2b41464cd7643b5c7b12be2ef0bf43f65c3bba02bff3ddf163/PyStemmer-2.2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2935aa78a89b04899de4a8b8b6339806e0d5cd93811de52e98829b5762cf913c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/27/b3950b55542ec9c59bae8484d0a3dcb88cba9f2268222f60f5538c8a853f/PyStemmer-2.2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:31c9d3c808647d4c569737b32b40ed23c67133d2b89033ebc8b5756cadf6f1c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/f5/d3fd6aab0d6a3c400f9a1bff1ec091ec29a34d76ef3b69b8472801b6d979/PyStemmer-2.2.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:584ead989545a60919e4015371dd2f69ff0ca985e76618d41930f77b9e248286" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9e/53/2fd493c6d61d8cd42a3b3f8268ddae6658f3c8bad0adf205f218606f236d/PyStemmer-2.2.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be904f4d0d522de98ff9f0a348d8748c2f95926523b7b04ee75b50967289782d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/76/b09792a8141fb03f27f61c75333c84793298287f0d978a755deeb96029c3/PyStemmer-2.2.0.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7024cdbcf4bbc2a5e1c277e11a10cb2b7481b7f99946cdcfa7271d5e9799399a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/f2/3275207f690d1eeea8e0e4900f5311e9e48497b39946e5519b8b4aa068d3/PyStemmer-2.2.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aa0f70f84c69b7a6a38ddbea51a29f855c42120e8069ea4c450021a2c7dc42d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/cd/224127bdf035803cfeddf83029d4398ee122289f11f5c2252832885de1c0/PyStemmer-2.2.0.3-cp310-cp310-win32.whl", hash = "sha256:85e583ec705b1b1c0503bc9cdbca027d3446cbc7cf7de3d29f1e0ab58999e5fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/aa/94/51643ed8adb4a558c784fbbb9df2877a103cd34a55d2c2863030badab54d/PyStemmer-2.2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:4556b2718bb22052f39a50f3166c4ee0e140c58ee06bbab31d57d765159d2f00" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/bf/664bf08622e54aea83006f168ee3655a50dcebc7df66e023557bb1333d00/PyStemmer-2.2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0c76ac603ff774fe3137340083315f34d6afbcd4ebebab99c1564c00c1c318ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/59/b2/591ff043474cc1d2e2a86d4460b310b6bcc6d4fa91cc59ef4ffb887e64e4/PyStemmer-2.2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ee100ba27a07d2fc3bd29cdd619cdff51735ed059002574c550697d1d160b7c9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/8d/c5101e915ea17f0ae8faefe7370c997315947d488bc1c8087dc5dee0bf43/PyStemmer-2.2.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3932f794e84bf29bdf4952d018b00c290fd06b055648f8e8fb9132e6684c4472" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b2/9b/91b46ea4d920014181b2d7256086a68ae1374f40bfeebee13497674511c4/PyStemmer-2.2.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74f6e0bb2034880bf4688ab5b95f97bb90952086682a93f080b260b454f933e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/02/cbd096d8ba891384ba9f3e684ff3cfd52b7f7131581b7f5e4b35d66b7289/PyStemmer-2.2.0.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:af925366939839e4bf11f426388201195c305a3edcdd9097e8775fbd083ff309" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/ad/10571e4e79bd9b9101bf33d274b7ad2b09c4d5ddc241cdac451ea889cdc0/PyStemmer-2.2.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b199cbab2ce93ee1dd76da4d0523af5af4446d775b7bcb75dfdfcd2a8226404e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c4/01/e4ae834f93cffda555907b7d8e4678113d94d2874ba39e7cbbfe14ad9373/PyStemmer-2.2.0.3-cp311-cp311-win32.whl", hash = "sha256:e9bbaa5aa38a2f82bb1eaa6b97396e58c3a7f87e46607f52c7fda53927616eda" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e8/d4/6ee439fe4ece1ebe40d13b7ef6f6bcd42a9775d725032db40b226be66bf8/PyStemmer-2.2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:258af638eb68273f130c9878de2bb4a427fe99e86900b9b9b09c1cd7a185c189" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/7c/e09c0ac1aa4571ded5f46c14aed5d4828b61392611ed1c59a8faf1cae98a/PyStemmer-2.2.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c30c44241065beb9432273874f199fc109473338d9f2c921a3387fd534fd94a7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/dc/b30dd228af740b1ffe18e4cfb48c22aa8481ce1e79405cac31086fb849bd/PyStemmer-2.2.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6adf0b86b6be85f0cf80b2b255b2b0179782b4a3f39c0a6c5b3dd07af5f95eb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/68/eb9f06b2f04f408ed748d6e71fb2f5ae92deba0eea07b6e374268d53cb49/PyStemmer-2.2.0.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d42b41082553fa23a4ce191860fd7caffdeaf8507e84db630a97ed154bd2320" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/9f/e95eeccf29ad1e1dbf4a14b76e2fa5b6698ba06f10634b097b32258839b0/PyStemmer-2.2.0.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec763ee2994402c534bf898ff318edd158c32071c3ffbdcd7ae7b7c884250471" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3b/4b/8b077958eb760ac647d2362f8bd1ae99097fa351edc6333fde0dee51f912/PyStemmer-2.2.0.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:264f09d5f70b09c845a6f0d0d4973de674056fd50452cb9383ffae8fc0967f1d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7f/13/3d57038439293d93e40af89b48bc06c714f8cd8c5d22a9e6d0870f49ea1c/PyStemmer-2.2.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5634f38a781b9a893550c23380af080ca5291d19c2bcb1753a34022d1d0de7cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9a/95/f04305ab6fc6312b4727b725ab983342237c7bb95d21d98bd768146dd9e8/PyStemmer-2.2.0.3-cp312-cp312-win32.whl", hash = "sha256:186c2e90ea2c3d0fab21f10f17b48fb7d716cba5f49b68f7f0fe539db4ff0499" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/73/861a71009f20331cd22aff1c37b9c39ef3af4d57d6816df79d51b3fca533/PyStemmer-2.2.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:320c1da333f5f8571e2b313c9fa6c0a7a79d8a00a2ad0bf29932d931d236d7e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/69/cbc7f00bda703f5b60a665a68e2d9df6c077c59da2bea52f35b1a496c549/PyStemmer-2.2.0.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ff3feeac41968fd8b50e9d6b8a03a5f15b27e765a0826f06dc32155f8f22909c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/31/eb832a4296814acd8c403eb555ada436700d93ab526c824e2f1d69dced15/PyStemmer-2.2.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:41a31d8ad810063e2cc675d93d0951dbfbb6ede278e111f15d74b7d781612364" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f4/37/a4a4b8d9db835fbf55dd532c357286b58c7418188a38233f067ef0197ad0/PyStemmer-2.2.0.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4abcb516040d7a561eb95c60125f9f5636080c154f46d365b14cd33197ac74fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/11/e9a0e32573391d335560e9caf05011d15c8b7d368f318d3a4516275c8938/PyStemmer-2.2.0.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8c307f1d5084e6074bc1826df9453887e589e92bab63851991b444f68a08b7e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/ac/3822a77de25f035b77130bfd30dcf57871c8818795918c3806b9b4e152ca/PyStemmer-2.2.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7f0d5f36922ea94599f79f86383972e91cdeab28918f8e1535cd589d2b5fb345" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/12/0378ba4391a4674067ae9db0e025ec998f6ca74caddd22fbdc59dc19aafb/pystemmer-2.2.0.3.tar.gz", hash = "sha256:9ac74c8d0f3358dbb050f64cddbb8d55021d831d92305d7c20780ea8d6c0020e", size = 303860, upload-time = "2024-10-10T00:36:25.921Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/c6/6929a114ca2b41464cd7643b5c7b12be2ef0bf43f65c3bba02bff3ddf163/PyStemmer-2.2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2935aa78a89b04899de4a8b8b6339806e0d5cd93811de52e98829b5762cf913c", size = 214158, upload-time = "2024-10-10T00:56:09.408Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/27/b3950b55542ec9c59bae8484d0a3dcb88cba9f2268222f60f5538c8a853f/PyStemmer-2.2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:31c9d3c808647d4c569737b32b40ed23c67133d2b89033ebc8b5756cadf6f1c1", size = 220116, upload-time = "2024-10-10T00:56:11.454Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/f5/d3fd6aab0d6a3c400f9a1bff1ec091ec29a34d76ef3b69b8472801b6d979/PyStemmer-2.2.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:584ead989545a60919e4015371dd2f69ff0ca985e76618d41930f77b9e248286", size = 612115, upload-time = "2024-10-10T00:56:13.639Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/53/2fd493c6d61d8cd42a3b3f8268ddae6658f3c8bad0adf205f218606f236d/PyStemmer-2.2.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be904f4d0d522de98ff9f0a348d8748c2f95926523b7b04ee75b50967289782d", size = 646708, upload-time = "2024-10-10T00:56:16.149Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/76/b09792a8141fb03f27f61c75333c84793298287f0d978a755deeb96029c3/PyStemmer-2.2.0.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7024cdbcf4bbc2a5e1c277e11a10cb2b7481b7f99946cdcfa7271d5e9799399a", size = 626855, upload-time = "2024-10-10T00:56:18.097Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/f2/3275207f690d1eeea8e0e4900f5311e9e48497b39946e5519b8b4aa068d3/PyStemmer-2.2.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aa0f70f84c69b7a6a38ddbea51a29f855c42120e8069ea4c450021a2c7dc42d8", size = 656727, upload-time = "2024-10-10T00:56:19.931Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/cd/224127bdf035803cfeddf83029d4398ee122289f11f5c2252832885de1c0/PyStemmer-2.2.0.3-cp310-cp310-win32.whl", hash = "sha256:85e583ec705b1b1c0503bc9cdbca027d3446cbc7cf7de3d29f1e0ab58999e5fe", size = 141159, upload-time = "2024-10-10T00:56:22.08Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/94/51643ed8adb4a558c784fbbb9df2877a103cd34a55d2c2863030badab54d/PyStemmer-2.2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:4556b2718bb22052f39a50f3166c4ee0e140c58ee06bbab31d57d765159d2f00", size = 184865, upload-time = "2024-10-10T00:56:23.506Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/bf/664bf08622e54aea83006f168ee3655a50dcebc7df66e023557bb1333d00/PyStemmer-2.2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0c76ac603ff774fe3137340083315f34d6afbcd4ebebab99c1564c00c1c318ee", size = 214216, upload-time = "2024-10-10T00:56:25.104Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/b2/591ff043474cc1d2e2a86d4460b310b6bcc6d4fa91cc59ef4ffb887e64e4/PyStemmer-2.2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ee100ba27a07d2fc3bd29cdd619cdff51735ed059002574c550697d1d160b7c9", size = 220087, upload-time = "2024-10-10T00:56:27.2Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/8d/c5101e915ea17f0ae8faefe7370c997315947d488bc1c8087dc5dee0bf43/PyStemmer-2.2.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3932f794e84bf29bdf4952d018b00c290fd06b055648f8e8fb9132e6684c4472", size = 630851, upload-time = "2024-10-10T00:56:28.817Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/9b/91b46ea4d920014181b2d7256086a68ae1374f40bfeebee13497674511c4/PyStemmer-2.2.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74f6e0bb2034880bf4688ab5b95f97bb90952086682a93f080b260b454f933e", size = 669279, upload-time = "2024-10-10T00:56:31.5Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/02/cbd096d8ba891384ba9f3e684ff3cfd52b7f7131581b7f5e4b35d66b7289/PyStemmer-2.2.0.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:af925366939839e4bf11f426388201195c305a3edcdd9097e8775fbd083ff309", size = 639701, upload-time = "2024-10-10T00:56:33.817Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/ad/10571e4e79bd9b9101bf33d274b7ad2b09c4d5ddc241cdac451ea889cdc0/PyStemmer-2.2.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b199cbab2ce93ee1dd76da4d0523af5af4446d775b7bcb75dfdfcd2a8226404e", size = 673230, upload-time = "2024-10-10T00:56:35.939Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/01/e4ae834f93cffda555907b7d8e4678113d94d2874ba39e7cbbfe14ad9373/PyStemmer-2.2.0.3-cp311-cp311-win32.whl", hash = "sha256:e9bbaa5aa38a2f82bb1eaa6b97396e58c3a7f87e46607f52c7fda53927616eda", size = 140866, upload-time = "2024-10-10T00:56:38.397Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/d4/6ee439fe4ece1ebe40d13b7ef6f6bcd42a9775d725032db40b226be66bf8/PyStemmer-2.2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:258af638eb68273f130c9878de2bb4a427fe99e86900b9b9b09c1cd7a185c189", size = 184766, upload-time = "2024-10-10T00:56:39.974Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/7c/e09c0ac1aa4571ded5f46c14aed5d4828b61392611ed1c59a8faf1cae98a/PyStemmer-2.2.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c30c44241065beb9432273874f199fc109473338d9f2c921a3387fd534fd94a7", size = 214916, upload-time = "2024-10-10T00:56:41.803Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/dc/b30dd228af740b1ffe18e4cfb48c22aa8481ce1e79405cac31086fb849bd/PyStemmer-2.2.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6adf0b86b6be85f0cf80b2b255b2b0179782b4a3f39c0a6c5b3dd07af5f95eb", size = 220418, upload-time = "2024-10-10T00:56:43.354Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/68/eb9f06b2f04f408ed748d6e71fb2f5ae92deba0eea07b6e374268d53cb49/PyStemmer-2.2.0.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d42b41082553fa23a4ce191860fd7caffdeaf8507e84db630a97ed154bd2320", size = 644074, upload-time = "2024-10-10T00:56:45.45Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/9f/e95eeccf29ad1e1dbf4a14b76e2fa5b6698ba06f10634b097b32258839b0/PyStemmer-2.2.0.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec763ee2994402c534bf898ff318edd158c32071c3ffbdcd7ae7b7c884250471", size = 683255, upload-time = "2024-10-10T00:56:47.314Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/4b/8b077958eb760ac647d2362f8bd1ae99097fa351edc6333fde0dee51f912/PyStemmer-2.2.0.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:264f09d5f70b09c845a6f0d0d4973de674056fd50452cb9383ffae8fc0967f1d", size = 649228, upload-time = "2024-10-10T00:56:50.101Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/13/3d57038439293d93e40af89b48bc06c714f8cd8c5d22a9e6d0870f49ea1c/PyStemmer-2.2.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5634f38a781b9a893550c23380af080ca5291d19c2bcb1753a34022d1d0de7cb", size = 682987, upload-time = "2024-10-10T00:56:53.094Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/95/f04305ab6fc6312b4727b725ab983342237c7bb95d21d98bd768146dd9e8/PyStemmer-2.2.0.3-cp312-cp312-win32.whl", hash = "sha256:186c2e90ea2c3d0fab21f10f17b48fb7d716cba5f49b68f7f0fe539db4ff0499", size = 141412, upload-time = "2024-10-10T00:56:54.64Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/73/861a71009f20331cd22aff1c37b9c39ef3af4d57d6816df79d51b3fca533/PyStemmer-2.2.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:320c1da333f5f8571e2b313c9fa6c0a7a79d8a00a2ad0bf29932d931d236d7e8", size = 185395, upload-time = "2024-10-10T00:56:56.177Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/69/cbc7f00bda703f5b60a665a68e2d9df6c077c59da2bea52f35b1a496c549/PyStemmer-2.2.0.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ff3feeac41968fd8b50e9d6b8a03a5f15b27e765a0826f06dc32155f8f22909c", size = 170258, upload-time = "2024-10-10T00:58:12.984Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/31/eb832a4296814acd8c403eb555ada436700d93ab526c824e2f1d69dced15/PyStemmer-2.2.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:41a31d8ad810063e2cc675d93d0951dbfbb6ede278e111f15d74b7d781612364", size = 173914, upload-time = "2024-10-10T00:58:14.569Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/37/a4a4b8d9db835fbf55dd532c357286b58c7418188a38233f067ef0197ad0/PyStemmer-2.2.0.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4abcb516040d7a561eb95c60125f9f5636080c154f46d365b14cd33197ac74fd", size = 218327, upload-time = "2024-10-10T00:58:16.79Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/11/e9a0e32573391d335560e9caf05011d15c8b7d368f318d3a4516275c8938/PyStemmer-2.2.0.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8c307f1d5084e6074bc1826df9453887e589e92bab63851991b444f68a08b7e", size = 225148, upload-time = "2024-10-10T00:58:18.347Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/ac/3822a77de25f035b77130bfd30dcf57871c8818795918c3806b9b4e152ca/PyStemmer-2.2.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7f0d5f36922ea94599f79f86383972e91cdeab28918f8e1535cd589d2b5fb345", size = 180277, upload-time = "2024-10-10T00:58:19.973Z" }, ] [[package]] name = "pytest" version = "8.3.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, @@ -5212,139 +5212,139 @@ dependencies = [ { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, ] [[package]] name = "python-calamine" version = "0.4.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/cc/03/269f96535705b2f18c8977fa58e76763b4e4727a9b3ae277a9468c8ffe05/python_calamine-0.4.0.tar.gz", hash = "sha256:94afcbae3fec36d2d7475095a59d4dc6fae45829968c743cb799ebae269d7bbf" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8e/06/885c73fd472cb76af4c4650174a7c11b77a8bc40585044bc445ac694e5e6/python_calamine-0.4.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:06011f11fd8d2dbfe0bc9bd8bd135c191aafe66f2d0c9eecf0ae3cb38f42f888" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2d/76/28c704d875046cc1ca92d8c6e680f6bc38d83735397fee821929691fd57f/python_calamine-0.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12e350e5967bf3206a8b472d9b6c348ff37ae791dba1a1715e076b2c39328557" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/71/dc00e6d9187044d15c85cd0875d8aba2b3e0d3051ceabd47d859f40f69c8/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35be298f69006e86b0311a538c1c9694ce3012237c33572d3dfe2bea6b5b9820" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/f6/be5e35263ceec21e77810d7900235124a9a83fd3c0afbbbb79da658d535c/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7abb10367aea435ca473b9b698636db912f2ab164f19a6c9675710ed926f33ac" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9c/61/0068297ec0000b2c0755a608c8068a1070b8193675d2ff603d390291aa45/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:58c2c4440982ec6db64c826136661f84f84bc0d8ee0cdd64a38128cd217797eb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/1d/8a9c7d491d31db1def335f4d90b85ea29940d84c59a27a88a442b840fda7/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e58cd89154fd1b5ef77c609f63dce108d390ece5a5f3225ca3ebedc8d343e9d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/40/87/a104c11b320d3fb7f1997d97207addce0fe5e1d41e5fd2e8adb0fb8b1325/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90f85e04c281d96c6dc5551176fc4e32c95257c3a2d384a947b3e68275c7d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8c/a5/9580de7758950b39c5048787909b639771c92ad6ea7f909a48dca1dbc6fe/python_calamine-0.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f8408b01d8097b2e662d0205ca09695788fb5f3492ade27de4ad4160cb6bd4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/a1/2b8282ec4acdf1879f9d46d84a6907843d2a331639719a2cfc90356345b5/python_calamine-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3d61957c10d37e6bf508fafdf52e6bb3112db8196e30bca8bc4b4560db2cc5f5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/95/03/ae56667a2a2eb273eea5f754212454c7073f8abe8e0fdca0edfbe5c0cf37/python_calamine-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5a58bbfcad9c1192dada189e367ed46e72037fcaec585e970fa919b92e07a57" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/60/d7aaa39977ab401de33ae975dd704805ec9c76117f0fe3a53e45b718822c/python_calamine-0.4.0-cp310-cp310-win32.whl", hash = "sha256:f06415096bcd9218b6c15d39ee2006ec0f32282e3d08605391d2a8a52187f9ca" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/f5/fdfeccd7d66e5c9c834288df6a657858a046d94a6e4cd624418cb5bb96dd/python_calamine-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:e457d1e07acb2798b72e70bd4e88f07cd486ca5129a19fadc6aa19a2cd4e76e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/a5/bcd82326d0ff1ab5889e7a5e13c868b483fc56398e143aae8e93149ba43b/python_calamine-0.4.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d1687f8c4d7852920c7b4e398072f183f88dd273baf5153391edc88b7454b8c0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/1a/a681f1d2f28164552e91ef47bcde6708098aa64a5f5fe3952f22362d340a/python_calamine-0.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:258d04230bebbbafa370a15838049d912d6a0a2c4da128943d8160ca4b6db58e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/92/2fc911431733739d4e7a633cefa903fa49a6b7a61e8765bad29a4a7c47b1/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686e491634934f059553d55f77ac67ca4c235452d5b444f98fe79b3579f1ea5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f4/f0/48bfae6802eb360028ca6c15e9edf42243aadd0006b6ac3e9edb41a57119/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4480af7babcc2f919c638a554b06b7b145d9ab3da47fd696d68c2fc6f67f9541" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a4/dc/f8c956e15bac9d5d1e05cd1b907ae780e40522d2fd103c8c6e2f21dff4ed/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e405b87a8cd1e90a994e570705898634f105442029f25bab7da658ee9cbaa771" }, - { url = "https://mirrors.aliyun.com/pypi/packages/54/3f/e69ab97c7734fb850fba2f506b775912fd59f04e17488582c8fbf52dbc72/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a831345ee42615f0dfcb0ed60a3b1601d2f946d4166edae64fd9a6f9bbd57fc1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/03/b4c056b468908d87a3de94389166e0f4dba725a70bc39e03bc039ba96f6b/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9951b8e4cafb3e1623bb5dfc31a18d38ef43589275f9657e99dfcbe4c8c4b33e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/4f/b9092f7c970894054083656953184e44cb2dadff8852425e950d4ca419af/python_calamine-0.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a6619fe3b5c9633ed8b178684605f8076c9d8d85b29ade15f7a7713fcfdee2d0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/64/da/137239027bf253aabe7063450950085ec9abd827d0cbc5170f585f38f464/python_calamine-0.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2cc45b8e76ee331f6ea88ca23677be0b7a05b502cd4423ba2c2bc8dad53af1be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/96/74c38bcf6b6825d5180c0e147b85be8c52dbfba11848b1e98ba358e32a64/python_calamine-0.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1b2cfb7ced1a7c80befa0cfddfe4aae65663eb4d63c4ae484b9b7a80ebe1b528" }, - { url = "https://mirrors.aliyun.com/pypi/packages/33/95/9d7b8fe8b32d99a6c79534df3132cfe40e9df4a0f5204048bf5e66ddbd93/python_calamine-0.4.0-cp311-cp311-win32.whl", hash = "sha256:04f4e32ee16814fc1fafc49300be8eeb280d94878461634768b51497e1444bd6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/e3/1c6cd9fd499083bea6ff1c30033ee8215b9f64e862babf5be170cacae190/python_calamine-0.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:a8543f69afac2213c0257bb56215b03dadd11763064a9d6b19786f27d1bef586" }, - { url = "https://mirrors.aliyun.com/pypi/packages/94/1c/3105d19fbab6b66874ce8831652caedd73b23b72e88ce18addf8ceca8c12/python_calamine-0.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:54622e35ec7c3b6f07d119da49aa821731c185e951918f152c2dbf3bec1e15d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/60/f951513aaaa470b3a38a87d65eca45e0a02bc329b47864f5a17db563f746/python_calamine-0.4.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:74bca5d44a73acf3dcfa5370820797fcfd225c8c71abcddea987c5b4f5077e98" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/3f/789955bbc77831c639890758f945eb2b25d6358065edf00da6751226cf31/python_calamine-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf80178f5d1b0ee2ccfffb8549c50855f6249e930664adc5807f4d0d6c2b269c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/4c/f87d17d996f647030a40bfd124fe45fe893c002bee35ae6aca9910a923ae/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65cfef345386ae86f7720f1be93495a40fd7e7feabb8caa1df5025d7fbc58a1f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/d2/3269367303f6c0488cf1bfebded3f9fe968d118a988222e04c9b2636bf2e/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f23e6214dbf9b29065a5dcfd6a6c674dd0e251407298c9138611c907d53423ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/6d/c7ac35f5c7125e8bd07eb36773f300fda20dd2da635eae78a8cebb0b6ab7/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d792d304ee232ab01598e1d3ab22e074a32c2511476b5fb4f16f4222d9c2a265" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/81/5ea8792a2e9ab5e2a05872db3a4d3ed3538ad5af1861282c789e2f13a8cf/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf813425918fd68f3e991ef7c4b5015be0a1a95fc4a8ab7e73c016ef1b881bb4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/6e/989e56e6f073fc0981a74ba7a393881eb351bb143e5486aa629b5e5d6a8b/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbe2a0ccb4d003635888eea83a995ff56b0748c8c76fc71923544f5a4a7d4cd7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/92/2c9bd64277c6fe4be695d7d5a803b38d953ec8565037486be7506642c27c/python_calamine-0.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7b3bb5f0d910b9b03c240987560f843256626fd443279759df4e91b717826d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/64/fa/fc758ca37701d354a6bc7d63118699f1c73788a1f2e1b44d720824992764/python_calamine-0.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bd2c0fc2b5eabd08ceac8a2935bffa88dbc6116db971aa8c3f244bad3fd0f644" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/52/40d7e08ae0ddba331cdc9f7fb3e92972f8f38d7afbd00228158ff6d1fceb/python_calamine-0.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:85b547cb1c5b692a0c2406678d666dbc1cec65a714046104683fe4f504a1721d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/de/e8a071c0adfda73285d891898a24f6e99338328c404f497ff5b0e6bc3d45/python_calamine-0.4.0-cp312-cp312-win32.whl", hash = "sha256:4c2a1e3a0db4d6de4587999a21cc35845648c84fba81c03dd6f3072c690888e4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/f2/7fdfada13f80db12356853cf08697ff4e38800a1809c2bdd26ee60962e7a/python_calamine-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b193c89ffcc146019475cd121c552b23348411e19c04dedf5c766a20db64399a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/66/d37412ad854480ce32f50d9f74f2a2f88b1b8a6fbc32f70aabf3211ae89e/python_calamine-0.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:43a0f15e0b60c75a71b21a012b911d5d6f5fa052afad2a8edbc728af43af0fcf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/8c/120a1128ff422dba43d6f2d1d19520bca95abf1e5135bbe3b84a782d3927/python_calamine-0.4.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4f9a44015de9e19a876babf707dc55708881930024220c8ae926ea0255f705fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/db/62/6062945b8d5fc73d0a0c44b25ee5f4037cc32d62b57688a0d0ca6763006d/python_calamine-0.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2c10bb42e0d0810368e78ee9359902f999a1f09bcc2391b060f91f981f75ae21" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9f/7b/597470e5349056a1dd7b0e3a7e838da53cba9186771ca5501a264446f4ad/python_calamine-0.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:494c5dd1dcee25935ff9d7a9eef6b0f629266d1670aef3ee5e0d38370dcb3352" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/e9/fabdd376713409e6ae6a8fee41293304798d794a64c9d407ba90f765f3d4/python_calamine-0.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f04fb24e70ab4403fc367b9b779eaa3bf61c140908d9115ddfe1e221372d5d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/8e/cc09cc14276662cea1f161a20bdce46d8bfd409e84027a0a0515a00b63f5/python_calamine-0.4.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:49dce56dbd1efc024b63b913595f1a9bef6f66a6467aefad7dcd548654fedb5b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/b4/44f560b0ab4dcb49845f5c5361641885f8dc2b7e9b35fbf59242191c2eeb/python_calamine-0.4.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7dc1755cd0b10ce5e2d80e77e9f19c13ed405b354178c3547ba5a11d34fce6ea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/4b/ee4ac45d500bd2ae189f84adf3338dbd32579fa2198a05ad180666578575/python_calamine-0.4.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9d981efa53ddfd8733555ad4c9368c8c1254bd8d1e162c93b8d341ead6acc5a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/99/835a9cd3ed503cb51fd15039b6404c536d201a6f3d16a6e069ce1079c5e4/python_calamine-0.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b370998567de0cd7a36a8ac73acabefea8397ad2d9aad3cf245b5d35f74cb990" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/03/269f96535705b2f18c8977fa58e76763b4e4727a9b3ae277a9468c8ffe05/python_calamine-0.4.0.tar.gz", hash = "sha256:94afcbae3fec36d2d7475095a59d4dc6fae45829968c743cb799ebae269d7bbf", size = 127737, upload-time = "2025-07-04T06:05:28.626Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/06/885c73fd472cb76af4c4650174a7c11b77a8bc40585044bc445ac694e5e6/python_calamine-0.4.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:06011f11fd8d2dbfe0bc9bd8bd135c191aafe66f2d0c9eecf0ae3cb38f42f888", size = 832924, upload-time = "2025-07-04T06:03:13.192Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/76/28c704d875046cc1ca92d8c6e680f6bc38d83735397fee821929691fd57f/python_calamine-0.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12e350e5967bf3206a8b472d9b6c348ff37ae791dba1a1715e076b2c39328557", size = 812087, upload-time = "2025-07-04T06:03:14.781Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/71/dc00e6d9187044d15c85cd0875d8aba2b3e0d3051ceabd47d859f40f69c8/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35be298f69006e86b0311a538c1c9694ce3012237c33572d3dfe2bea6b5b9820", size = 874841, upload-time = "2025-07-04T06:03:16.403Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/f6/be5e35263ceec21e77810d7900235124a9a83fd3c0afbbbb79da658d535c/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7abb10367aea435ca473b9b698636db912f2ab164f19a6c9675710ed926f33ac", size = 878236, upload-time = "2025-07-04T06:03:18.016Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/61/0068297ec0000b2c0755a608c8068a1070b8193675d2ff603d390291aa45/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:58c2c4440982ec6db64c826136661f84f84bc0d8ee0cdd64a38128cd217797eb", size = 1015229, upload-time = "2025-07-04T06:03:19.714Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/1d/8a9c7d491d31db1def335f4d90b85ea29940d84c59a27a88a442b840fda7/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e58cd89154fd1b5ef77c609f63dce108d390ece5a5f3225ca3ebedc8d343e9d5", size = 924810, upload-time = "2025-07-04T06:03:20.922Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/87/a104c11b320d3fb7f1997d97207addce0fe5e1d41e5fd2e8adb0fb8b1325/python_calamine-0.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90f85e04c281d96c6dc5551176fc4e32c95257c3a2d384a947b3e68275c7d6", size = 887826, upload-time = "2025-07-04T06:03:22.607Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/a5/9580de7758950b39c5048787909b639771c92ad6ea7f909a48dca1dbc6fe/python_calamine-0.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f8408b01d8097b2e662d0205ca09695788fb5f3492ade27de4ad4160cb6bd4", size = 929943, upload-time = "2025-07-04T06:03:24.272Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/a1/2b8282ec4acdf1879f9d46d84a6907843d2a331639719a2cfc90356345b5/python_calamine-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3d61957c10d37e6bf508fafdf52e6bb3112db8196e30bca8bc4b4560db2cc5f5", size = 1052980, upload-time = "2025-07-04T06:03:26.022Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/03/ae56667a2a2eb273eea5f754212454c7073f8abe8e0fdca0edfbe5c0cf37/python_calamine-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5a58bbfcad9c1192dada189e367ed46e72037fcaec585e970fa919b92e07a57", size = 1058045, upload-time = "2025-07-04T06:03:27.342Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/60/d7aaa39977ab401de33ae975dd704805ec9c76117f0fe3a53e45b718822c/python_calamine-0.4.0-cp310-cp310-win32.whl", hash = "sha256:f06415096bcd9218b6c15d39ee2006ec0f32282e3d08605391d2a8a52187f9ca", size = 663988, upload-time = "2025-07-04T06:03:29.004Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/f5/fdfeccd7d66e5c9c834288df6a657858a046d94a6e4cd624418cb5bb96dd/python_calamine-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:e457d1e07acb2798b72e70bd4e88f07cd486ca5129a19fadc6aa19a2cd4e76e8", size = 692422, upload-time = "2025-07-04T06:03:30.624Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/a5/bcd82326d0ff1ab5889e7a5e13c868b483fc56398e143aae8e93149ba43b/python_calamine-0.4.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d1687f8c4d7852920c7b4e398072f183f88dd273baf5153391edc88b7454b8c0", size = 833019, upload-time = "2025-07-04T06:03:32.214Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/1a/a681f1d2f28164552e91ef47bcde6708098aa64a5f5fe3952f22362d340a/python_calamine-0.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:258d04230bebbbafa370a15838049d912d6a0a2c4da128943d8160ca4b6db58e", size = 812268, upload-time = "2025-07-04T06:03:33.855Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/92/2fc911431733739d4e7a633cefa903fa49a6b7a61e8765bad29a4a7c47b1/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686e491634934f059553d55f77ac67ca4c235452d5b444f98fe79b3579f1ea5", size = 875733, upload-time = "2025-07-04T06:03:35.154Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/f0/48bfae6802eb360028ca6c15e9edf42243aadd0006b6ac3e9edb41a57119/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4480af7babcc2f919c638a554b06b7b145d9ab3da47fd696d68c2fc6f67f9541", size = 878325, upload-time = "2025-07-04T06:03:36.638Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/dc/f8c956e15bac9d5d1e05cd1b907ae780e40522d2fd103c8c6e2f21dff4ed/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e405b87a8cd1e90a994e570705898634f105442029f25bab7da658ee9cbaa771", size = 1015038, upload-time = "2025-07-04T06:03:37.971Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/3f/e69ab97c7734fb850fba2f506b775912fd59f04e17488582c8fbf52dbc72/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a831345ee42615f0dfcb0ed60a3b1601d2f946d4166edae64fd9a6f9bbd57fc1", size = 924969, upload-time = "2025-07-04T06:03:39.253Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/03/b4c056b468908d87a3de94389166e0f4dba725a70bc39e03bc039ba96f6b/python_calamine-0.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9951b8e4cafb3e1623bb5dfc31a18d38ef43589275f9657e99dfcbe4c8c4b33e", size = 888020, upload-time = "2025-07-04T06:03:41.099Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/4f/b9092f7c970894054083656953184e44cb2dadff8852425e950d4ca419af/python_calamine-0.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a6619fe3b5c9633ed8b178684605f8076c9d8d85b29ade15f7a7713fcfdee2d0", size = 930337, upload-time = "2025-07-04T06:03:42.89Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/da/137239027bf253aabe7063450950085ec9abd827d0cbc5170f585f38f464/python_calamine-0.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2cc45b8e76ee331f6ea88ca23677be0b7a05b502cd4423ba2c2bc8dad53af1be", size = 1054568, upload-time = "2025-07-04T06:03:44.153Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/96/74c38bcf6b6825d5180c0e147b85be8c52dbfba11848b1e98ba358e32a64/python_calamine-0.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1b2cfb7ced1a7c80befa0cfddfe4aae65663eb4d63c4ae484b9b7a80ebe1b528", size = 1058317, upload-time = "2025-07-04T06:03:45.873Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/33/95/9d7b8fe8b32d99a6c79534df3132cfe40e9df4a0f5204048bf5e66ddbd93/python_calamine-0.4.0-cp311-cp311-win32.whl", hash = "sha256:04f4e32ee16814fc1fafc49300be8eeb280d94878461634768b51497e1444bd6", size = 663934, upload-time = "2025-07-04T06:03:47.407Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/e3/1c6cd9fd499083bea6ff1c30033ee8215b9f64e862babf5be170cacae190/python_calamine-0.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:a8543f69afac2213c0257bb56215b03dadd11763064a9d6b19786f27d1bef586", size = 692535, upload-time = "2025-07-04T06:03:48.699Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/1c/3105d19fbab6b66874ce8831652caedd73b23b72e88ce18addf8ceca8c12/python_calamine-0.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:54622e35ec7c3b6f07d119da49aa821731c185e951918f152c2dbf3bec1e15d6", size = 671751, upload-time = "2025-07-04T06:03:49.979Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/60/f951513aaaa470b3a38a87d65eca45e0a02bc329b47864f5a17db563f746/python_calamine-0.4.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:74bca5d44a73acf3dcfa5370820797fcfd225c8c71abcddea987c5b4f5077e98", size = 826603, upload-time = "2025-07-04T06:03:51.245Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/3f/789955bbc77831c639890758f945eb2b25d6358065edf00da6751226cf31/python_calamine-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf80178f5d1b0ee2ccfffb8549c50855f6249e930664adc5807f4d0d6c2b269c", size = 805826, upload-time = "2025-07-04T06:03:52.482Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/4c/f87d17d996f647030a40bfd124fe45fe893c002bee35ae6aca9910a923ae/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65cfef345386ae86f7720f1be93495a40fd7e7feabb8caa1df5025d7fbc58a1f", size = 874989, upload-time = "2025-07-04T06:03:53.794Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/d2/3269367303f6c0488cf1bfebded3f9fe968d118a988222e04c9b2636bf2e/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f23e6214dbf9b29065a5dcfd6a6c674dd0e251407298c9138611c907d53423ff", size = 877504, upload-time = "2025-07-04T06:03:55.095Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/6d/c7ac35f5c7125e8bd07eb36773f300fda20dd2da635eae78a8cebb0b6ab7/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d792d304ee232ab01598e1d3ab22e074a32c2511476b5fb4f16f4222d9c2a265", size = 1014171, upload-time = "2025-07-04T06:03:56.777Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/81/5ea8792a2e9ab5e2a05872db3a4d3ed3538ad5af1861282c789e2f13a8cf/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf813425918fd68f3e991ef7c4b5015be0a1a95fc4a8ab7e73c016ef1b881bb4", size = 926737, upload-time = "2025-07-04T06:03:58.024Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/6e/989e56e6f073fc0981a74ba7a393881eb351bb143e5486aa629b5e5d6a8b/python_calamine-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbe2a0ccb4d003635888eea83a995ff56b0748c8c76fc71923544f5a4a7d4cd7", size = 887032, upload-time = "2025-07-04T06:03:59.298Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/92/2c9bd64277c6fe4be695d7d5a803b38d953ec8565037486be7506642c27c/python_calamine-0.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7b3bb5f0d910b9b03c240987560f843256626fd443279759df4e91b717826d2", size = 929700, upload-time = "2025-07-04T06:04:01.388Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/fa/fc758ca37701d354a6bc7d63118699f1c73788a1f2e1b44d720824992764/python_calamine-0.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bd2c0fc2b5eabd08ceac8a2935bffa88dbc6116db971aa8c3f244bad3fd0f644", size = 1053971, upload-time = "2025-07-04T06:04:02.704Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/52/40d7e08ae0ddba331cdc9f7fb3e92972f8f38d7afbd00228158ff6d1fceb/python_calamine-0.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:85b547cb1c5b692a0c2406678d666dbc1cec65a714046104683fe4f504a1721d", size = 1057057, upload-time = "2025-07-04T06:04:04.014Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/de/e8a071c0adfda73285d891898a24f6e99338328c404f497ff5b0e6bc3d45/python_calamine-0.4.0-cp312-cp312-win32.whl", hash = "sha256:4c2a1e3a0db4d6de4587999a21cc35845648c84fba81c03dd6f3072c690888e4", size = 665540, upload-time = "2025-07-04T06:04:05.679Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/f2/7fdfada13f80db12356853cf08697ff4e38800a1809c2bdd26ee60962e7a/python_calamine-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b193c89ffcc146019475cd121c552b23348411e19c04dedf5c766a20db64399a", size = 695366, upload-time = "2025-07-04T06:04:06.977Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/66/d37412ad854480ce32f50d9f74f2a2f88b1b8a6fbc32f70aabf3211ae89e/python_calamine-0.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:43a0f15e0b60c75a71b21a012b911d5d6f5fa052afad2a8edbc728af43af0fcf", size = 670740, upload-time = "2025-07-04T06:04:08.656Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/8c/120a1128ff422dba43d6f2d1d19520bca95abf1e5135bbe3b84a782d3927/python_calamine-0.4.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4f9a44015de9e19a876babf707dc55708881930024220c8ae926ea0255f705fe", size = 835210, upload-time = "2025-07-04T06:05:05.543Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/62/6062945b8d5fc73d0a0c44b25ee5f4037cc32d62b57688a0d0ca6763006d/python_calamine-0.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2c10bb42e0d0810368e78ee9359902f999a1f09bcc2391b060f91f981f75ae21", size = 816045, upload-time = "2025-07-04T06:05:06.86Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/7b/597470e5349056a1dd7b0e3a7e838da53cba9186771ca5501a264446f4ad/python_calamine-0.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:494c5dd1dcee25935ff9d7a9eef6b0f629266d1670aef3ee5e0d38370dcb3352", size = 875895, upload-time = "2025-07-04T06:05:08.259Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/e9/fabdd376713409e6ae6a8fee41293304798d794a64c9d407ba90f765f3d4/python_calamine-0.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f04fb24e70ab4403fc367b9b779eaa3bf61c140908d9115ddfe1e221372d5d4", size = 889049, upload-time = "2025-07-04T06:05:09.717Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/8e/cc09cc14276662cea1f161a20bdce46d8bfd409e84027a0a0515a00b63f5/python_calamine-0.4.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:49dce56dbd1efc024b63b913595f1a9bef6f66a6467aefad7dcd548654fedb5b", size = 931721, upload-time = "2025-07-04T06:05:11.086Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/b4/44f560b0ab4dcb49845f5c5361641885f8dc2b7e9b35fbf59242191c2eeb/python_calamine-0.4.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7dc1755cd0b10ce5e2d80e77e9f19c13ed405b354178c3547ba5a11d34fce6ea", size = 1054521, upload-time = "2025-07-04T06:05:12.497Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/4b/ee4ac45d500bd2ae189f84adf3338dbd32579fa2198a05ad180666578575/python_calamine-0.4.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9d981efa53ddfd8733555ad4c9368c8c1254bd8d1e162c93b8d341ead6acc5a9", size = 1059692, upload-time = "2025-07-04T06:05:13.857Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/99/835a9cd3ed503cb51fd15039b6404c536d201a6f3d16a6e069ce1079c5e4/python_calamine-0.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b370998567de0cd7a36a8ac73acabefea8397ad2d9aad3cf245b5d35f74cb990", size = 694046, upload-time = "2025-07-04T06:05:15.16Z" }, ] [[package]] name = "python-dateutil" version = "2.8.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324, upload-time = "2021-07-14T08:19:19.783Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702, upload-time = "2021-07-14T08:19:18.161Z" }, ] [[package]] name = "python-docx" version = "1.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "lxml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, ] [[package]] name = "python-dotenv" version = "1.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115, upload-time = "2024-01-23T06:33:00.505Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" }, ] [[package]] name = "python-multipart" version = "0.0.20" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] [[package]] name = "python-pptx" version = "1.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "lxml" }, { name = "pillow" }, { name = "typing-extensions" }, { name = "xlsxwriter" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/52/a9/0c0db8d37b2b8a645666f7fd8accea4c6224e013c42b1d5c17c93590cd06/python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/a9/0c0db8d37b2b8a645666f7fd8accea4c6224e013c42b1d5c17c93590cd06/python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095", size = 10109297, upload-time = "2024-08-07T17:33:37.772Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba", size = 472788, upload-time = "2024-08-07T17:33:28.192Z" }, ] [[package]] name = "pytz" version = "2025.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] [[package]] name = "pywencai" version = "0.12.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "fake-useragent" }, { name = "pandas" }, @@ -5352,66 +5352,66 @@ dependencies = [ { name = "pyexecjs" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/38/4b/bddead56c178dcb3abd3ea39f03d2f2e767828d6687b5221f06e95846df4/pywencai-0.12.2.tar.gz", hash = "sha256:7a916de189588710b94ef85eff9f107e1d1c83a7def0856f43e46d7dae1cf55b" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/4b/bddead56c178dcb3abd3ea39f03d2f2e767828d6687b5221f06e95846df4/pywencai-0.12.2.tar.gz", hash = "sha256:7a916de189588710b94ef85eff9f107e1d1c83a7def0856f43e46d7dae1cf55b", size = 903567, upload-time = "2024-01-09T15:13:47.731Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/77/4b/28d33a6edb8d98402789cbf418063a302a96d04d563163aef07ef5f97f22/pywencai-0.12.2-py3-none-any.whl", hash = "sha256:cd8e87771f057fe6e7019b41e07d9f2cea0600e41bbc445cd0e840d4ac1dde8c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/4b/28d33a6edb8d98402789cbf418063a302a96d04d563163aef07ef5f97f22/pywencai-0.12.2-py3-none-any.whl", hash = "sha256:cd8e87771f057fe6e7019b41e07d9f2cea0600e41bbc445cd0e840d4ac1dde8c", size = 911134, upload-time = "2024-01-09T15:13:45.911Z" }, ] [[package]] name = "pywin32" version = "311" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151" }, - { url = "https://mirrors.aliyun.com/pypi/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, ] [[package]] name = "pyyaml" version = "6.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, ] [[package]] name = "qianfan" version = "0.4.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiohttp" }, { name = "aiolimiter" }, @@ -5429,9 +5429,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions", marker = "python_full_version <= '3.10'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3a/10/e06dd2f67a1f5f5a8eefac2e13c8f6d79c76e86a520c8976af33700d9c43/qianfan-0.4.6.tar.gz", hash = "sha256:90c2bf6f5fa1d1ae6ff63d982ce7b5fcb771c73048e81a147bcc24abed7eaefe" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/10/e06dd2f67a1f5f5a8eefac2e13c8f6d79c76e86a520c8976af33700d9c43/qianfan-0.4.6.tar.gz", hash = "sha256:90c2bf6f5fa1d1ae6ff63d982ce7b5fcb771c73048e81a147bcc24abed7eaefe", size = 312564, upload-time = "2024-08-17T08:43:14.448Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/53/77/0fa3a283114078cd3b34465e4eeef03dae3cd94a63b81ec773d8883267a6/qianfan-0.4.6-py3-none-any.whl", hash = "sha256:7d8746356a2b88b42333e5fbb74c9ef7897d59b732e651a69e38e1be4512f0b1" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/77/0fa3a283114078cd3b34465e4eeef03dae3cd94a63b81ec773d8883267a6/qianfan-0.4.6-py3-none-any.whl", hash = "sha256:7d8746356a2b88b42333e5fbb74c9ef7897d59b732e651a69e38e1be4512f0b1", size = 446480, upload-time = "2024-08-17T08:43:12.686Z" }, ] [[package]] @@ -5644,7 +5644,7 @@ requires-dist = [ { name = "httpx", extras = ["socks"], specifier = "==0.27.2" }, { name = "huggingface-hub", specifier = ">=0.25.0,<0.26.0" }, { name = "infinity-emb", specifier = ">=0.0.66,<0.0.67" }, - { name = "infinity-sdk", specifier = "==0.6.0.dev5" }, + { name = "infinity-sdk", specifier = "==0.6.0.dev7" }, { name = "itsdangerous", specifier = "==2.1.2" }, { name = "json-repair", specifier = "==0.35.0" }, { name = "langfuse", specifier = ">=2.60.0" }, @@ -5747,19 +5747,19 @@ test = [ [[package]] name = "rank-bm25" version = "0.2.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fc/0a/f9579384aa017d8b4c15613f86954b92a95a93d641cc849182467cf0bb3b/rank_bm25-0.2.2.tar.gz", hash = "sha256:096ccef76f8188563419aaf384a02f0ea459503fdf77901378d4fd9d87e5e51d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/0a/f9579384aa017d8b4c15613f86954b92a95a93d641cc849182467cf0bb3b/rank_bm25-0.2.2.tar.gz", hash = "sha256:096ccef76f8188563419aaf384a02f0ea459503fdf77901378d4fd9d87e5e51d", size = 8347, upload-time = "2022-02-16T12:10:52.196Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2a/21/f691fb2613100a62b3fa91e9988c991e9ca5b89ea31c0d3152a3210344f9/rank_bm25-0.2.2-py3-none-any.whl", hash = "sha256:7bd4a95571adadfc271746fa146a4bcfd89c0cf731e49c3d1ad863290adbe8ae" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/21/f691fb2613100a62b3fa91e9988c991e9ca5b89ea31c0d3152a3210344f9/rank_bm25-0.2.2-py3-none-any.whl", hash = "sha256:7bd4a95571adadfc271746fa146a4bcfd89c0cf731e49c3d1ad863290adbe8ae", size = 8584, upload-time = "2022-02-16T12:10:50.626Z" }, ] [[package]] name = "ranx" version = "0.3.20" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cbor2" }, { name = "fastparquet" }, @@ -5775,35 +5775,35 @@ dependencies = [ { name = "tabulate" }, { name = "tqdm" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/35/fe/4d4e7c69137afdeb5a4a85afcf04b84f087a284b7f22034e2e13e121de83/ranx-0.3.20.tar.gz", hash = "sha256:8afc6f2042c40645e5d1fd80c35ed75a885e18bd2db7e95cc7ec32a0b41e59ea" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/fe/4d4e7c69137afdeb5a4a85afcf04b84f087a284b7f22034e2e13e121de83/ranx-0.3.20.tar.gz", hash = "sha256:8afc6f2042c40645e5d1fd80c35ed75a885e18bd2db7e95cc7ec32a0b41e59ea", size = 51526, upload-time = "2024-07-01T17:40:29.448Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/1e/30/53f41b7b728a48da8974075f56c57200d7b11e4e9fa93be3cabf8218dc0c/ranx-0.3.20-py3-none-any.whl", hash = "sha256:e056e4d5981b0328b045868cc7064fc57a545f36009fbe9bb602295ec33335de" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/30/53f41b7b728a48da8974075f56c57200d7b11e4e9fa93be3cabf8218dc0c/ranx-0.3.20-py3-none-any.whl", hash = "sha256:e056e4d5981b0328b045868cc7064fc57a545f36009fbe9bb602295ec33335de", size = 99318, upload-time = "2024-07-01T17:40:27.095Z" }, ] [[package]] name = "readability-lxml" version = "0.8.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "chardet" }, { name = "cssselect" }, { name = "lxml" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b9/62/6de3a9a8524c1a1ee0f2aee0dfbad13a36ebbca0db402abcf4e790496512/readability-lxml-0.8.1.tar.gz", hash = "sha256:e51fea56b5909aaf886d307d48e79e096293255afa567b7d08bca94d25b1a4e1" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/62/6de3a9a8524c1a1ee0f2aee0dfbad13a36ebbca0db402abcf4e790496512/readability-lxml-0.8.1.tar.gz", hash = "sha256:e51fea56b5909aaf886d307d48e79e096293255afa567b7d08bca94d25b1a4e1", size = 15878, upload-time = "2020-07-04T00:45:51.112Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/39/a6/cfe22aaa19ac69b97d127043a76a5bbcb0ef24f3a0b22793c46608190caa/readability_lxml-0.8.1-py3-none-any.whl", hash = "sha256:e0d366a21b1bd6cca17de71a4e6ea16fcfaa8b0a5b4004e39e2c7eff884e6305" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/a6/cfe22aaa19ac69b97d127043a76a5bbcb0ef24f3a0b22793c46608190caa/readability_lxml-0.8.1-py3-none-any.whl", hash = "sha256:e0d366a21b1bd6cca17de71a4e6ea16fcfaa8b0a5b4004e39e2c7eff884e6305", size = 20691, upload-time = "2020-07-04T00:45:49.348Z" }, ] [[package]] name = "readerwriterlock" version = "1.0.9" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a4/b9/6b7c390440ec23bf5fdf33e76d6c3b697a788b983c11cb2739d6541835d6/readerwriterlock-1.0.9.tar.gz", hash = "sha256:b7c4cc003435d7a8ff15b312b0a62a88d9800ba6164af88991f87f8b748f9bea" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/b9/6b7c390440ec23bf5fdf33e76d6c3b697a788b983c11cb2739d6541835d6/readerwriterlock-1.0.9.tar.gz", hash = "sha256:b7c4cc003435d7a8ff15b312b0a62a88d9800ba6164af88991f87f8b748f9bea", size = 16595, upload-time = "2021-09-06T03:41:21.75Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c2/5a/2f2e7fc026d5e64b5408aa3fbe0296a6407b8481196cae4daacacb3a3ae0/readerwriterlock-1.0.9-py3-none-any.whl", hash = "sha256:8c4b704e60d15991462081a27ef46762fea49b478aa4426644f2146754759ca7" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/5a/2f2e7fc026d5e64b5408aa3fbe0296a6407b8481196cae4daacacb3a3ae0/readerwriterlock-1.0.9-py3-none-any.whl", hash = "sha256:8c4b704e60d15991462081a27ef46762fea49b478aa4426644f2146754759ca7", size = 9999, upload-time = "2021-09-06T03:41:19.435Z" }, ] [[package]] @@ -5815,109 +5815,109 @@ sdist = { url = "https://mirrors.aliyun.com/pypi/packages/34/12/944f61bc67a1e918 [[package]] name = "referencing" version = "0.36.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, ] [[package]] name = "regex" version = "2025.7.31" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a6/36/9995080bbdabd4683dd11ab54edcf4fc0e2e4ce4d3eea8034b49fa5dd6ef/regex-2025.7.31.tar.gz", hash = "sha256:80a1af156ea8670ae63184e5c112b481326ece1879e09447f6fbb49d1b49330b" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/03/30/51cf963b5acdb63f3c8e80c4129db3a997f2508e18bf8afc8696fb7408ab/regex-2025.7.31-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b40a8f8064c3b8032babb2049b7ab40812cbb394179556deb7c40c1e3b28630f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9d/fc/0fd637c648eb7b14cef655266129428fc23e0ceb0a14f5d816ba5e0b76f8/regex-2025.7.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f6aef1895f27875421e6d8047747702d6e512793c6d95614c56479a375541edb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/c7/dfdc769cfa01258f3ded76fd3e34d7aad9e96862513adccbdb2a7d02342f/regex-2025.7.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f124ff95b4cbedfd762897d4bd9da2b20b7574df1d60d498f16a42d398d524e9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/61/29f8152131ac78545a4382e14747246b9727ed9467f4f28becb6e97d7c2d/regex-2025.7.31-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea5b162c50745694606f50170cc7cc84c14193ac5fd6ecb26126e826a7c12bd6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8b/f3/10f827814b9d130c9f9e41bd4378b2386e6dd25e8a4c69837692f28db829/regex-2025.7.31-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f970a3e058f587988a18ed4ddff6a6363fa72a41dfb29077d0efe8dc4df00da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/f9/b6454bedb3af4ae4aba3fdf54d6476872303b63c3d93d3dfc88b43c43334/regex-2025.7.31-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2dadf5788af5b10a78b996d24263e352e5f99dbfce9db4c48e9c875a9a7d051c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/28/f81b34d07c70dcd988b9bb124d47ae06c591bcabc2b703b601a61d39528c/regex-2025.7.31-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f67f9f8216a8e645c568daf104abc52cd5387127af8e8b17c7bc11b014d88fcb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/e6/3f25e64d853ca18151016329963f9e3a2c9d43f1bf36e172b23bf4e1a6bb/regex-2025.7.31-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:407da7504642830d4211d39dc23b8a9d400913b3f2d242774b8d17ead3487e00" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/bc/5b976ca504f2fabb340600bad426e530a25f2c0109c009834af42d5cfd33/regex-2025.7.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff7753bd717a9f2286d2171d758eebf11b3bfb21e6520b201e01169ec9cd5ec0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/56/a8/4b0db3eb6d4dbbc10777f20831fde816137a2a5737498c4c7f715e29f2f6/regex-2025.7.31-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:de088fe37d4c58a42401bf4ce2328b00a760c7d85473ccf6e489094e13452510" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5a/57/5281baa48d9f25fc7f48f5c216c3825ae5b29c11b0c016535ae3f493b9df/regex-2025.7.31-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:67d708f8bfb89dcd57c3190cb5c343c7f40d3c81319a00c8188982a08c64b977" }, - { url = "https://mirrors.aliyun.com/pypi/packages/51/82/b193e9a4afdb982d39edbb5c2e245a7af2b2e3963c32265fcf2d960b0008/regex-2025.7.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3fe81cd00ef1eaef1ef00d61200bacb55b1a130570cd9be2e793b98981c6cd9c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/fc/aa19a1eba4168922d3e6e66671380b86a3841a2a8f6a54ecdcf217424e88/regex-2025.7.31-cp310-cp310-win32.whl", hash = "sha256:8542ee1fd8c8be4db1c58902956a220bdbe7c38362decec989f57ace0e37f14c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bb/ae/9b0c128ef88c3068f949025f6671261e504c6e586f9138309705b82524ee/regex-2025.7.31-cp310-cp310-win_amd64.whl", hash = "sha256:77be56e167e2685828ab0abc1bdf38db3ab385e624c3ea2694b0d4ea70a2b7bc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ba/54/4e598d171a32a67cf7b3912ded0b71dcbab30235f293963c6021359c1233/regex-2025.7.31-cp310-cp310-win_arm64.whl", hash = "sha256:7ddc7ab76d917cb680a3b0fa53fc2971d40cc17415539007e15fa31c829dcf2b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/a7/85a371e31fb4f675593e0f731f89c54423e0b98d02a3e580fde206ba763b/regex-2025.7.31-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55dc9f4094656d273562718d68cd8363f688e0b813d62696aad346bcd7b1c7d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/75/2e738c69d43c086514f687f0d73a8cdc089e6823ad83192d2f6b2cf2b0c7/regex-2025.7.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8ff37cac0e1c7ba943bf46f6431b0c86cbe42d42ae862ff7b152b4ccc232bdd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/7a/8b7b102bc524c3bf8eb3389afcdc381f47cff95a05c5370803e6fd5dfe44/regex-2025.7.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:622aa4ca90d7cf38433d425a4f00543b08d3b109cca379df8f31827cf5e2ecb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/db/335b829ae5cde7e5de00c0576d899e180605f8c8befee9d58e014d49d4f3/regex-2025.7.31-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbd4ee61dddfcff625f8642e940ba61121b28e98d0eca24d79114209e3e8ce1b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0d/34/418288618d3c2e68bbf4be3138bcfa1dd088b869923ea8f0bdac37576fa1/regex-2025.7.31-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca7c9af8f33540b51f1b76092e732b62211092af947239e5db471323ae39ead4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/b8/b570626227c257725cd7ecfee07f697850fc45d38df710a13b2b6d681943/regex-2025.7.31-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:beda88db2cae5dc82a64cba465f7e8686392d96116f87e664af46c4dfcdd9cbc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/42/af/d35718c1ccd68bd2e7b6b26a83c4919516d73610ddf124796797a7858749/regex-2025.7.31-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055baef91bb31474bd919fd245cf154db00cbac449596952d3e6bc1e1b226808" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/32/3daed29302ead90198cdcd1367edaa6913899f3d224c41eddcf8750979cc/regex-2025.7.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:02e660c2d02854eed41b13f0e2c98d24efce4fb439aa316742f8d32aeda2803b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/30/7f823c11f9b83f3a6c333a37322aa5867d7983447f8a83a07eccd49bd847/regex-2025.7.31-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4372ca5c43d0e255e68a9aa6812d9be3447c4ce7ba7cb1429c7b96d2c63ee9b1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/0e/a1cae70e3b5e44f5ad5672c1a17011c4ae37250987ce7635c2547c2cc570/regex-2025.7.31-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:481f069facacb4f40bf37a51748a88952f5dd5707dd849f216d53bf5522c8add" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/ce/c8e3a31a449145572f9a97960e873546f7f842448dd2a0e68d4f667a77a4/regex-2025.7.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e8b4896ec5a9d0ae73d04e260ff6e1f366985b46505b2fa36d91501e4a7a98f0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/eb/0429111b7493459d3f765cc921d1e2ce1228b049895f3a1cb8525d51526a/regex-2025.7.31-cp311-cp311-win32.whl", hash = "sha256:47ceaa1e5eb243595306dfd5e5e294e251900aa94a0e2e1037fce125f432d2fb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/4a/5b05e5ff93eb852eec932eb71a7465e4a879a77f9558b2c132af0fcec438/regex-2025.7.31-cp311-cp311-win_amd64.whl", hash = "sha256:c4f6b34f509bb26507509b6f9ba85debcc6ca512d2d4a6fd5e96b9de2c187c83" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/0c/a27e089caaef5a393b72cc65f88a6ba23805f72b6c431c19928d2870a3a3/regex-2025.7.31-cp311-cp311-win_arm64.whl", hash = "sha256:75f74892df1593036e83b48ba50d1e1951af650b6fabbfcf7531e7082e3561d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/2a/6cde7309f9a90a04b43492eef04893dd551b4284cfbde3650bdab1f2d45e/regex-2025.7.31-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1af64eed343f19e1f09da9e9e8cfb82570050c4ed9fec400f9f118aab383da4b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/aa/6ad49cb0e7e5c4ba6219b33aacf1b8217ecd4d8ec9c1173d8166b7b6d7b8/regex-2025.7.31-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:eab98712c0a6d053fb67b021fae43422f7eab8fa2aaa25034f5ef01585112cc7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b6/99/67826894a43447144e610c76a958db9b31318435b04bc21974ccc75fcfce/regex-2025.7.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34dcb7c4d89b83e7e3cb5a2679595f6f97d253815ed9402edbdfc56780668b89" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2f/22/14dcf80def58a009143ced54665721ff7706440ce93b37bd34a36eebbe24/regex-2025.7.31-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52f1925d123338835e5b13e5ef8e6a744c02aef8e538e661ad5c76185e6ad87a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/af/51/f36e6c8fdb62304ed1ba29bbc2d381ecdf37273cc32a4d65a5e64b1fa002/regex-2025.7.31-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:569c2b6812d223ae82a2a13c36362ca5933b88011ba869111eba8fb769ccf492" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/4c/29083c291e329df4e883304c6d7a99beb6dba9cc31b4f99737cf0a75269a/regex-2025.7.31-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:27f17ade67d06ce4abff48f2ee99c6419f73e70882fe7ca51960916c75844e1f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/4e/7450d9d63edaa6abf7561fdf8ce540d7140bbd9d493328e3852c4bf9222c/regex-2025.7.31-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45622fab3a90590a41a541afea739a732bf110dd081c15c84538b115cf5f59f5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/36/57cb185286dc2c63f0e08e68a8f724870a0c7939a9029cb6e14fea159100/regex-2025.7.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:defab878ce91944baf2ade775895a097ad7eeeab3618d87b4c29753aad98a5c4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/88/bd/f38a26e98f4f504dc669cbb79e0df3b3eb09a1bcebf8b9eac91f62afc36a/regex-2025.7.31-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8ae02caf994a0a0d958b9b0fc5aebbdb48fa93491a582dd00db3733d258a6ac4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c2/b8/96ce23733a03f6c673ed9dd23766bdbf678c340d6c08016ffde04f53508b/regex-2025.7.31-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7c40ab21112711363d7612f35781c8b2d2d59c27e0a057a6486eea60ee01e54" }, - { url = "https://mirrors.aliyun.com/pypi/packages/10/4a/f50cd8c4699049513df4d999e1ff61c054304a29e099ed4848de36e52fb7/regex-2025.7.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4723c01dd28c1b1de5f463bba8672e3d0dc3d94d5db056e4bbc3cbc84bf23c1c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/05/5b68b3d392f6158f5ea968f193f3f6271609b8197483809bdf4c2081989b/regex-2025.7.31-cp312-cp312-win32.whl", hash = "sha256:3ebf32b2b2f60aecd6f8d375ff310849251946cf953aac69b8b5b10e3ccebaf9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0d/6c/703c6c8d4bdf771cf65466d09bd300ca663041a53d30555beca43b26d3dc/regex-2025.7.31-cp312-cp312-win_amd64.whl", hash = "sha256:12f9ab65b4cc771dd6d8af806ded7425ca50d2a188d2fc3a5aba3dc49f5684b7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/90/f13eddf7ded8857b51f8d9ebebbc0e862ffb739f2a0f7fcff30a0f95676a/regex-2025.7.31-cp312-cp312-win_arm64.whl", hash = "sha256:fd454ed1fe245f983c2376b6f01948d6ec4a1e5869a8c883e320e1739cc63e57" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/36/9995080bbdabd4683dd11ab54edcf4fc0e2e4ce4d3eea8034b49fa5dd6ef/regex-2025.7.31.tar.gz", hash = "sha256:80a1af156ea8670ae63184e5c112b481326ece1879e09447f6fbb49d1b49330b", size = 407354, upload-time = "2025-07-30T00:13:26.283Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/30/51cf963b5acdb63f3c8e80c4129db3a997f2508e18bf8afc8696fb7408ab/regex-2025.7.31-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b40a8f8064c3b8032babb2049b7ab40812cbb394179556deb7c40c1e3b28630f", size = 489341, upload-time = "2025-07-30T00:10:50.324Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/fc/0fd637c648eb7b14cef655266129428fc23e0ceb0a14f5d816ba5e0b76f8/regex-2025.7.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f6aef1895f27875421e6d8047747702d6e512793c6d95614c56479a375541edb", size = 293048, upload-time = "2025-07-30T00:10:53.852Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/c7/dfdc769cfa01258f3ded76fd3e34d7aad9e96862513adccbdb2a7d02342f/regex-2025.7.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f124ff95b4cbedfd762897d4bd9da2b20b7574df1d60d498f16a42d398d524e9", size = 290094, upload-time = "2025-07-30T00:10:55.428Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/61/29f8152131ac78545a4382e14747246b9727ed9467f4f28becb6e97d7c2d/regex-2025.7.31-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea5b162c50745694606f50170cc7cc84c14193ac5fd6ecb26126e826a7c12bd6", size = 788555, upload-time = "2025-07-30T00:10:57.293Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/f3/10f827814b9d130c9f9e41bd4378b2386e6dd25e8a4c69837692f28db829/regex-2025.7.31-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f970a3e058f587988a18ed4ddff6a6363fa72a41dfb29077d0efe8dc4df00da", size = 859934, upload-time = "2025-07-30T00:10:59.139Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/f9/b6454bedb3af4ae4aba3fdf54d6476872303b63c3d93d3dfc88b43c43334/regex-2025.7.31-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2dadf5788af5b10a78b996d24263e352e5f99dbfce9db4c48e9c875a9a7d051c", size = 906535, upload-time = "2025-07-30T00:11:00.849Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/28/f81b34d07c70dcd988b9bb124d47ae06c591bcabc2b703b601a61d39528c/regex-2025.7.31-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f67f9f8216a8e645c568daf104abc52cd5387127af8e8b17c7bc11b014d88fcb", size = 794549, upload-time = "2025-07-30T00:11:02.503Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/e6/3f25e64d853ca18151016329963f9e3a2c9d43f1bf36e172b23bf4e1a6bb/regex-2025.7.31-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:407da7504642830d4211d39dc23b8a9d400913b3f2d242774b8d17ead3487e00", size = 790436, upload-time = "2025-07-30T00:11:04.175Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/bc/5b976ca504f2fabb340600bad426e530a25f2c0109c009834af42d5cfd33/regex-2025.7.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff7753bd717a9f2286d2171d758eebf11b3bfb21e6520b201e01169ec9cd5ec0", size = 778553, upload-time = "2025-07-30T00:11:05.863Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/a8/4b0db3eb6d4dbbc10777f20831fde816137a2a5737498c4c7f715e29f2f6/regex-2025.7.31-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:de088fe37d4c58a42401bf4ce2328b00a760c7d85473ccf6e489094e13452510", size = 853076, upload-time = "2025-07-30T00:11:07.521Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/57/5281baa48d9f25fc7f48f5c216c3825ae5b29c11b0c016535ae3f493b9df/regex-2025.7.31-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:67d708f8bfb89dcd57c3190cb5c343c7f40d3c81319a00c8188982a08c64b977", size = 843760, upload-time = "2025-07-30T00:11:09.032Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/82/b193e9a4afdb982d39edbb5c2e245a7af2b2e3963c32265fcf2d960b0008/regex-2025.7.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3fe81cd00ef1eaef1ef00d61200bacb55b1a130570cd9be2e793b98981c6cd9c", size = 782475, upload-time = "2025-07-30T00:11:10.721Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/fc/aa19a1eba4168922d3e6e66671380b86a3841a2a8f6a54ecdcf217424e88/regex-2025.7.31-cp310-cp310-win32.whl", hash = "sha256:8542ee1fd8c8be4db1c58902956a220bdbe7c38362decec989f57ace0e37f14c", size = 268730, upload-time = "2025-07-30T00:11:12.236Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/ae/9b0c128ef88c3068f949025f6671261e504c6e586f9138309705b82524ee/regex-2025.7.31-cp310-cp310-win_amd64.whl", hash = "sha256:77be56e167e2685828ab0abc1bdf38db3ab385e624c3ea2694b0d4ea70a2b7bc", size = 280420, upload-time = "2025-07-30T00:11:13.509Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/54/4e598d171a32a67cf7b3912ded0b71dcbab30235f293963c6021359c1233/regex-2025.7.31-cp310-cp310-win_arm64.whl", hash = "sha256:7ddc7ab76d917cb680a3b0fa53fc2971d40cc17415539007e15fa31c829dcf2b", size = 272887, upload-time = "2025-07-30T00:11:15.933Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/a7/85a371e31fb4f675593e0f731f89c54423e0b98d02a3e580fde206ba763b/regex-2025.7.31-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55dc9f4094656d273562718d68cd8363f688e0b813d62696aad346bcd7b1c7d4", size = 489345, upload-time = "2025-07-30T00:11:17.22Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/75/2e738c69d43c086514f687f0d73a8cdc089e6823ad83192d2f6b2cf2b0c7/regex-2025.7.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8ff37cac0e1c7ba943bf46f6431b0c86cbe42d42ae862ff7b152b4ccc232bdd", size = 293052, upload-time = "2025-07-30T00:11:18.751Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/7a/8b7b102bc524c3bf8eb3389afcdc381f47cff95a05c5370803e6fd5dfe44/regex-2025.7.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:622aa4ca90d7cf38433d425a4f00543b08d3b109cca379df8f31827cf5e2ecb3", size = 290098, upload-time = "2025-07-30T00:11:20.32Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/db/335b829ae5cde7e5de00c0576d899e180605f8c8befee9d58e014d49d4f3/regex-2025.7.31-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbd4ee61dddfcff625f8642e940ba61121b28e98d0eca24d79114209e3e8ce1b", size = 798391, upload-time = "2025-07-30T00:11:21.972Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/34/418288618d3c2e68bbf4be3138bcfa1dd088b869923ea8f0bdac37576fa1/regex-2025.7.31-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca7c9af8f33540b51f1b76092e732b62211092af947239e5db471323ae39ead4", size = 867849, upload-time = "2025-07-30T00:11:23.703Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/b8/b570626227c257725cd7ecfee07f697850fc45d38df710a13b2b6d681943/regex-2025.7.31-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:beda88db2cae5dc82a64cba465f7e8686392d96116f87e664af46c4dfcdd9cbc", size = 915110, upload-time = "2025-07-30T00:11:25.62Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/af/d35718c1ccd68bd2e7b6b26a83c4919516d73610ddf124796797a7858749/regex-2025.7.31-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055baef91bb31474bd919fd245cf154db00cbac449596952d3e6bc1e1b226808", size = 803693, upload-time = "2025-07-30T00:11:27.652Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/32/3daed29302ead90198cdcd1367edaa6913899f3d224c41eddcf8750979cc/regex-2025.7.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:02e660c2d02854eed41b13f0e2c98d24efce4fb439aa316742f8d32aeda2803b", size = 787634, upload-time = "2025-07-30T00:11:29.436Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/30/7f823c11f9b83f3a6c333a37322aa5867d7983447f8a83a07eccd49bd847/regex-2025.7.31-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4372ca5c43d0e255e68a9aa6812d9be3447c4ce7ba7cb1429c7b96d2c63ee9b1", size = 863173, upload-time = "2025-07-30T00:11:31.126Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/0e/a1cae70e3b5e44f5ad5672c1a17011c4ae37250987ce7635c2547c2cc570/regex-2025.7.31-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:481f069facacb4f40bf37a51748a88952f5dd5707dd849f216d53bf5522c8add", size = 853953, upload-time = "2025-07-30T00:11:32.692Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/ce/c8e3a31a449145572f9a97960e873546f7f842448dd2a0e68d4f667a77a4/regex-2025.7.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e8b4896ec5a9d0ae73d04e260ff6e1f366985b46505b2fa36d91501e4a7a98f0", size = 792225, upload-time = "2025-07-30T00:11:34.264Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/eb/0429111b7493459d3f765cc921d1e2ce1228b049895f3a1cb8525d51526a/regex-2025.7.31-cp311-cp311-win32.whl", hash = "sha256:47ceaa1e5eb243595306dfd5e5e294e251900aa94a0e2e1037fce125f432d2fb", size = 268744, upload-time = "2025-07-30T00:11:36.115Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/4a/5b05e5ff93eb852eec932eb71a7465e4a879a77f9558b2c132af0fcec438/regex-2025.7.31-cp311-cp311-win_amd64.whl", hash = "sha256:c4f6b34f509bb26507509b6f9ba85debcc6ca512d2d4a6fd5e96b9de2c187c83", size = 280437, upload-time = "2025-07-30T00:11:37.892Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/0c/a27e089caaef5a393b72cc65f88a6ba23805f72b6c431c19928d2870a3a3/regex-2025.7.31-cp311-cp311-win_arm64.whl", hash = "sha256:75f74892df1593036e83b48ba50d1e1951af650b6fabbfcf7531e7082e3561d4", size = 272887, upload-time = "2025-07-30T00:11:39.685Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/2a/6cde7309f9a90a04b43492eef04893dd551b4284cfbde3650bdab1f2d45e/regex-2025.7.31-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1af64eed343f19e1f09da9e9e8cfb82570050c4ed9fec400f9f118aab383da4b", size = 490326, upload-time = "2025-07-30T00:11:41.146Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/aa/6ad49cb0e7e5c4ba6219b33aacf1b8217ecd4d8ec9c1173d8166b7b6d7b8/regex-2025.7.31-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:eab98712c0a6d053fb67b021fae43422f7eab8fa2aaa25034f5ef01585112cc7", size = 293732, upload-time = "2025-07-30T00:11:43.003Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/99/67826894a43447144e610c76a958db9b31318435b04bc21974ccc75fcfce/regex-2025.7.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34dcb7c4d89b83e7e3cb5a2679595f6f97d253815ed9402edbdfc56780668b89", size = 290272, upload-time = "2025-07-30T00:11:44.485Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/22/14dcf80def58a009143ced54665721ff7706440ce93b37bd34a36eebbe24/regex-2025.7.31-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52f1925d123338835e5b13e5ef8e6a744c02aef8e538e661ad5c76185e6ad87a", size = 801903, upload-time = "2025-07-30T00:11:46.006Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/51/f36e6c8fdb62304ed1ba29bbc2d381ecdf37273cc32a4d65a5e64b1fa002/regex-2025.7.31-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:569c2b6812d223ae82a2a13c36362ca5933b88011ba869111eba8fb769ccf492", size = 872405, upload-time = "2025-07-30T00:11:47.669Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/4c/29083c291e329df4e883304c6d7a99beb6dba9cc31b4f99737cf0a75269a/regex-2025.7.31-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:27f17ade67d06ce4abff48f2ee99c6419f73e70882fe7ca51960916c75844e1f", size = 920078, upload-time = "2025-07-30T00:11:49.526Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/4e/7450d9d63edaa6abf7561fdf8ce540d7140bbd9d493328e3852c4bf9222c/regex-2025.7.31-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45622fab3a90590a41a541afea739a732bf110dd081c15c84538b115cf5f59f5", size = 804512, upload-time = "2025-07-30T00:11:51.408Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/36/57cb185286dc2c63f0e08e68a8f724870a0c7939a9029cb6e14fea159100/regex-2025.7.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:defab878ce91944baf2ade775895a097ad7eeeab3618d87b4c29753aad98a5c4", size = 790672, upload-time = "2025-07-30T00:11:53.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/bd/f38a26e98f4f504dc669cbb79e0df3b3eb09a1bcebf8b9eac91f62afc36a/regex-2025.7.31-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8ae02caf994a0a0d958b9b0fc5aebbdb48fa93491a582dd00db3733d258a6ac4", size = 867146, upload-time = "2025-07-30T00:11:55.476Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/b8/96ce23733a03f6c673ed9dd23766bdbf678c340d6c08016ffde04f53508b/regex-2025.7.31-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7c40ab21112711363d7612f35781c8b2d2d59c27e0a057a6486eea60ee01e54", size = 858552, upload-time = "2025-07-30T00:11:57.5Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/4a/f50cd8c4699049513df4d999e1ff61c054304a29e099ed4848de36e52fb7/regex-2025.7.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4723c01dd28c1b1de5f463bba8672e3d0dc3d94d5db056e4bbc3cbc84bf23c1c", size = 794274, upload-time = "2025-07-30T00:11:59.074Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/05/5b68b3d392f6158f5ea968f193f3f6271609b8197483809bdf4c2081989b/regex-2025.7.31-cp312-cp312-win32.whl", hash = "sha256:3ebf32b2b2f60aecd6f8d375ff310849251946cf953aac69b8b5b10e3ccebaf9", size = 269105, upload-time = "2025-07-30T00:12:00.673Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/6c/703c6c8d4bdf771cf65466d09bd300ca663041a53d30555beca43b26d3dc/regex-2025.7.31-cp312-cp312-win_amd64.whl", hash = "sha256:12f9ab65b4cc771dd6d8af806ded7425ca50d2a188d2fc3a5aba3dc49f5684b7", size = 279788, upload-time = "2025-07-30T00:12:02.535Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/90/f13eddf7ded8857b51f8d9ebebbc0e862ffb739f2a0f7fcff30a0f95676a/regex-2025.7.31-cp312-cp312-win_arm64.whl", hash = "sha256:fd454ed1fe245f983c2376b6f01948d6ec4a1e5869a8c883e320e1739cc63e57", size = 272991, upload-time = "2025-07-30T00:12:04.373Z" }, ] [[package]] name = "replicate" version = "0.31.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "httpx" }, { name = "packaging" }, { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/02/eb/04fbc1787d4d688feafa080b11e6672e819d170d9fda9ae4a2c2ac1e3dc2/replicate-0.31.0.tar.gz", hash = "sha256:6503f5266e08f7bd0f125f735a7dd68a298496b9f057be0f101aa7e8c7280728" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/eb/04fbc1787d4d688feafa080b11e6672e819d170d9fda9ae4a2c2ac1e3dc2/replicate-0.31.0.tar.gz", hash = "sha256:6503f5266e08f7bd0f125f735a7dd68a298496b9f057be0f101aa7e8c7280728", size = 49894, upload-time = "2024-07-31T23:27:03.984Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/77/0f/f6067b7076faee22aef6190f703524e8ba8eac490191352c5cb0253c4823/replicate-0.31.0-py3-none-any.whl", hash = "sha256:27ee067ccb4c37d8c2fc5ab87bb312da36447dfcd12527002bbd0b78f6ef195a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/0f/f6067b7076faee22aef6190f703524e8ba8eac490191352c5cb0253c4823/replicate-0.31.0-py3-none-any.whl", hash = "sha256:27ee067ccb4c37d8c2fc5ab87bb312da36447dfcd12527002bbd0b78f6ef195a", size = 42950, upload-time = "2024-07-31T23:27:02.219Z" }, ] [[package]] name = "reportlab" version = "4.4.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "charset-normalizer" }, { name = "pillow" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/2f/83/3d44b873fa71ddc7d323c577fe4cfb61e05b34d14e64b6a232f9cfbff89d/reportlab-4.4.3.tar.gz", hash = "sha256:073b0975dab69536acd3251858e6b0524ed3e087e71f1d0d1895acb50acf9c7b" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/83/3d44b873fa71ddc7d323c577fe4cfb61e05b34d14e64b6a232f9cfbff89d/reportlab-4.4.3.tar.gz", hash = "sha256:073b0975dab69536acd3251858e6b0524ed3e087e71f1d0d1895acb50acf9c7b", size = 3887532, upload-time = "2025-07-23T11:18:23.799Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/52/c8/aaf4e08679e7b1dc896ad30de0d0527f0fd55582c2e6deee4f2cc899bf9f/reportlab-4.4.3-py3-none-any.whl", hash = "sha256:df905dc5ec5ddaae91fc9cb3371af863311271d555236410954961c5ee6ee1b5" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/c8/aaf4e08679e7b1dc896ad30de0d0527f0fd55582c2e6deee4f2cc899bf9f/reportlab-4.4.3-py3-none-any.whl", hash = "sha256:df905dc5ec5ddaae91fc9cb3371af863311271d555236410954961c5ee6ee1b5", size = 1953896, upload-time = "2025-07-23T11:18:20.572Z" }, ] [[package]] name = "requests" version = "2.32.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/86/ec/535bf6f9bd280de6a4637526602a146a68fde757100ecf8c9333173392db/requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/ec/535bf6f9bd280de6a4637526602a146a68fde757100ecf8c9333173392db/requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289", size = 130327, upload-time = "2024-05-21T18:51:32.819Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c3/20/748e38b466e0819491f0ce6e90ebe4184966ee304fe483e2c414b0f4ef07/requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/20/748e38b466e0819491f0ce6e90ebe4184966ee304fe483e2c414b0f4ef07/requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c", size = 63902, upload-time = "2024-05-21T18:51:29.562Z" }, ] [package.optional-dependencies] @@ -5928,141 +5928,141 @@ socks = [ [[package]] name = "requests-toolbelt" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, ] [[package]] name = "retry" version = "0.9.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "decorator" }, { name = "py" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9d/72/75d0b85443fbc8d9f38d08d2b1b67cc184ce35280e4a3813cda2f445f3a4/retry-0.9.2.tar.gz", hash = "sha256:f8bfa8b99b69c4506d6f5bd3b0aabf77f98cdb17f3c9fc3f5ca820033336fba4" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/72/75d0b85443fbc8d9f38d08d2b1b67cc184ce35280e4a3813cda2f445f3a4/retry-0.9.2.tar.gz", hash = "sha256:f8bfa8b99b69c4506d6f5bd3b0aabf77f98cdb17f3c9fc3f5ca820033336fba4", size = 6448, upload-time = "2016-05-11T13:58:51.541Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4b/0d/53aea75710af4528a25ed6837d71d117602b01946b307a3912cb3cfcbcba/retry-0.9.2-py2.py3-none-any.whl", hash = "sha256:ccddf89761fa2c726ab29391837d4327f819ea14d244c232a1d24c67a2f98606" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/0d/53aea75710af4528a25ed6837d71d117602b01946b307a3912cb3cfcbcba/retry-0.9.2-py2.py3-none-any.whl", hash = "sha256:ccddf89761fa2c726ab29391837d4327f819ea14d244c232a1d24c67a2f98606", size = 7986, upload-time = "2016-05-11T13:58:39.925Z" }, ] [[package]] name = "rich" version = "14.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, ] [[package]] name = "roman-numbers" version = "1.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/ce/e9f6b0d260f48713f2d735e0986ee4ead311cd168c217c5f94b0fad6817b/roman_numbers-1.0.2.tar.gz", hash = "sha256:fb84b7755ba972d549e73fac1c100f0eeb9fc247474d43d0f433c0b72152c699" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/ce/e9f6b0d260f48713f2d735e0986ee4ead311cd168c217c5f94b0fad6817b/roman_numbers-1.0.2.tar.gz", hash = "sha256:fb84b7755ba972d549e73fac1c100f0eeb9fc247474d43d0f433c0b72152c699", size = 2574, upload-time = "2021-01-11T11:54:59.584Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/9a/85/09e9e6bd6cd4cc0ed463d2b6ce3c7741698d45ca157318730a1346df4819/roman_numbers-1.0.2-py3-none-any.whl", hash = "sha256:ffbc00aaf41538208f975d1b1ccfe80372bae1866e7cd632862d8c6b45edf447" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/85/09e9e6bd6cd4cc0ed463d2b6ce3c7741698d45ca157318730a1346df4819/roman_numbers-1.0.2-py3-none-any.whl", hash = "sha256:ffbc00aaf41538208f975d1b1ccfe80372bae1866e7cd632862d8c6b45edf447", size = 3724, upload-time = "2021-01-11T11:54:57.686Z" }, ] [[package]] name = "roman-numerals-py" version = "3.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, ] [[package]] name = "rpds-py" version = "0.26.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b9/31/1459645f036c3dfeacef89e8e5825e430c77dde8489f3b99eaafcd4a60f5/rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/ff/3d0727f35836cc8773d3eeb9a46c40cc405854e36a8d2e951f3a8391c976/rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/ce/badc5e06120a54099ae287fa96d82cbb650a5f85cf247ffe19c7b157fd1f/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec671691e72dff75817386aa02d81e708b5a7ec0dec6669ec05213ff6b77e1bd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1e/a5/fa5d96a66c95d06c62d7a30707b6a4cfec696ab8ae280ee7be14e961e118/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a1cb5d6ce81379401bbb7f6dbe3d56de537fb8235979843f0d53bc2e9815a79" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/a7/7049d66750f18605c591a9db47d4a059e112a0c9ff8de8daf8fa0f446bba/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f789e32fa1fb6a7bf890e0124e7b42d1e60d28ebff57fe806719abb75f0e9a3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0e/f1/528d02c7d6b29d29fac8fd784b354d3571cc2153f33f842599ef0cf20dd2/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c55b0a669976cf258afd718de3d9ad1b7d1fe0a91cd1ab36f38b03d4d4aeaaf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/15/93/fde36cd6e4685df2cd08508f6c45a841e82f5bb98c8d5ecf05649522acb5/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70d9ec912802ecfd6cd390dadb34a9578b04f9bcb8e863d0a7598ba5e9e7ccc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/f2/5007553aaba1dcae5d663143683c3dfd03d9395289f495f0aebc93e90f24/rpds_py-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3021933c2cb7def39d927b9862292e0f4c75a13d7de70eb0ab06efed4c508c19" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/a7/ce52c75c1e624a79e48a69e611f1c08844564e44c85db2b6f711d76d10ce/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a7898b6ca3b7d6659e55cdac825a2e58c638cbf335cde41f4619e290dd0ad11" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/d5/e119db99341cc75b538bf4cb80504129fa22ce216672fb2c28e4a101f4d9/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:12bff2ad9447188377f1b2794772f91fe68bb4bbfa5a39d7941fbebdbf8c500f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/94/d28272a0b02f5fe24c78c20e13bbcb95f03dc1451b68e7830ca040c60bd6/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:191aa858f7d4902e975d4cf2f2d9243816c91e9605070aeb09c0a800d187e323" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/e0/8c41166602f1b791da892d976057eba30685486d2e2c061ce234679c922b/rpds_py-0.26.0-cp310-cp310-win32.whl", hash = "sha256:b37a04d9f52cb76b6b78f35109b513f6519efb481d8ca4c321f6a3b9580b3f45" }, - { url = "https://mirrors.aliyun.com/pypi/packages/87/f0/509736bb752a7ab50fb0270c2a4134d671a7b3038030837e5536c3de0e0b/rpds_py-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:38721d4c9edd3eb6670437d8d5e2070063f305bfa2d5aa4278c51cedcd508a84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/4c/4ee8f7e512030ff79fda1df3243c88d70fc874634e2dbe5df13ba4210078/rpds_py-0.26.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e8cb77286025bdb21be2941d64ac6ca016130bfdcd228739e8ab137eb4406ed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fa/9d/3dc16be00f14fc1f03c71b1d67c8df98263ab2710a2fbd65a6193214a527/rpds_py-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e09330b21d98adc8ccb2dbb9fc6cb434e8908d4c119aeaa772cb1caab5440a0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/5a/7f1bf8f045da2866324a08ae80af63e64e7bfaf83bd31f865a7b91a58601/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9c1b92b774b2e68d11193dc39620d62fd8ab33f0a3c77ecdabe19c179cdbc1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/45/8a/04479398c755a066ace10e3d158866beb600867cacae194c50ffa783abd0/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:824e6d3503ab990d7090768e4dfd9e840837bae057f212ff9f4f05ec6d1975e7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/88/9203f47268db488a1b6d469d69c12201ede776bb728b9d9f29dbfd7df406/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ad7fd2258228bf288f2331f0a6148ad0186b2e3643055ed0db30990e59817a6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f5/b4/01ce5d1e853ddf81fbbd4311ab1eff0b3cf162d559288d10fd127e2588b5/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dc23bbb3e06ec1ea72d515fb572c1fea59695aefbffb106501138762e1e915e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/a2/004c99936997bfc644d590a9defd9e9c93f8286568f9c16cdaf3e14429a7/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80bf832ac7b1920ee29a426cdca335f96a2b5caa839811803e999b41ba9030d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/1b/ef5fba4a8f81ce04c427bfd96223f92f05e6cd72291ce9d7523db3b03a6c/rpds_py-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0919f38f5542c0a87e7b4afcafab6fd2c15386632d249e9a087498571250abe3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/80/5c54195aec456b292f7bd8aa61741c8232964063fd8a75fdde9c1e982328/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d422b945683e409000c888e384546dbab9009bb92f7c0b456e217988cf316107" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/1c/1845c1b1fd6d827187c43afe1841d91678d7241cbdb5420a4c6de180a538/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a7711fa562ba2da1aa757e11024ad6d93bad6ad7ede5afb9af144623e5f76a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/ff/9e979329dd131aa73a438c077252ddabd7df6d1a7ad7b9aacf6261f10faa/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238e8c8610cb7c29460e37184f6799547f7e09e6a9bdbdab4e8edb90986a2318" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/8b/d78cfe034b71ffbe72873a136e71acc7a831a03e37771cfe59f33f6de8a2/rpds_py-0.26.0-cp311-cp311-win32.whl", hash = "sha256:893b022bfbdf26d7bedb083efeea624e8550ca6eb98bf7fea30211ce95b9201a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/94/c1/3c8c94c7dd3905dbfde768381ce98778500a80db9924731d87ddcdb117e9/rpds_py-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:87a5531de9f71aceb8af041d72fc4cab4943648d91875ed56d2e629bef6d4c03" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/93/e936fbed1b734eabf36ccb5d93c6a2e9246fbb13c1da011624b7286fae3e/rpds_py-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:de2713f48c1ad57f89ac25b3cb7daed2156d8e822cf0eca9b96a6f990718cc41" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ef/9a/1f033b0b31253d03d785b0cd905bc127e555ab496ea6b4c7c2e1f951f2fd/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3c0909c5234543ada2515c05dc08595b08d621ba919629e94427e8e03539c958" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/29/5f88023fd6aaaa8ca3c4a6357ebb23f6f07da6079093ccf27c99efce87db/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c1fb0cda2abcc0ac62f64e2ea4b4e64c57dfd6b885e693095460c61bde7bb18e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/6c/13eaebd28b439da6964dde22712b52e53fe2824af0223b8e403249d10405/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d142d2d6cf9b31c12aa4878d82ed3b2324226270b89b676ac62ccd7df52d08" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/fc/3bb9c486b06da19448646f96147796de23c5811ef77cbfc26f17307b6a9d/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a547e21c5610b7e9093d870be50682a6a6cf180d6da0f42c47c306073bfdbbf6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/15/18/9d1b79eb4d18e64ba8bba9e7dec6f9d6920b639f22f07ee9368ca35d4673/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35e9a70a0f335371275cdcd08bc5b8051ac494dd58bff3bbfb421038220dc871" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4f/5a/175ad7191bdbcd28785204621b225ad70e85cdfd1e09cc414cb554633b21/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dfa6115c6def37905344d56fb54c03afc49104e2ca473d5dedec0f6606913b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/45/6a67ecf6d61c4d4aff4bc056e864eec4b2447787e11d1c2c9a0242c6e92a/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:313cfcd6af1a55a286a3c9a25f64af6d0e46cf60bc5798f1db152d97a216ff6f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/ba/16589da828732b46454c61858950a78fe4c931ea4bf95f17432ffe64b241/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7bf2496fa563c046d05e4d232d7b7fd61346e2402052064b773e5c378bf6f73" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/4b/00092999fc7c0c266045e984d56b7314734cc400a6c6dc4d61a35f135a9d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:aa81873e2c8c5aa616ab8e017a481a96742fdf9313c40f14338ca7dbf50cb55f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/96/0c/43737053cde1f93ac4945157f7be1428724ab943e2132a0d235a7e161d4e/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:68ffcf982715f5b5b7686bdd349ff75d422e8f22551000c24b30eaa1b7f7ae84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/46/8e38f6161466e60a997ed7e9951ae5de131dedc3cf778ad35994b4af823d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6188de70e190847bb6db3dc3981cbadff87d27d6fe9b4f0e18726d55795cee9b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2c/ac/65da605e9f1dd643ebe615d5bbd11b6efa1d69644fc4bf623ea5ae385a82/rpds_py-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1c962145c7473723df9722ba4c058de12eb5ebedcb4e27e7d902920aa3831ee8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/51/f2/b5c85b758a00c513bb0389f8fc8e61eb5423050c91c958cdd21843faa3e6/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f61a9326f80ca59214d1cceb0a09bb2ece5b2563d4e0cd37bfd5515c28510674" }, - { url = "https://mirrors.aliyun.com/pypi/packages/23/e0/25db45e391251118e915e541995bb5f5ac5691a3b98fb233020ba53afc9b/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:183f857a53bcf4b1b42ef0f57ca553ab56bdd170e49d8091e96c51c3d69ca696" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0b/73/dd5ee6075bb6491be3a646b301dfd814f9486d924137a5098e61f0487e16/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:941c1cfdf4799d623cf3aa1d326a6b4fdb7a5799ee2687f3516738216d2262fb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2f/10/84b522ff58763a5c443f5bcedc1820240e454ce4e620e88520f04589e2ea/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72a8d9564a717ee291f554eeb4bfeafe2309d5ec0aa6c475170bdab0f9ee8e88" }, - { url = "https://mirrors.aliyun.com/pypi/packages/06/ea/8667604229a10a520fcbf78b30ccc278977dcc0627beb7ea2c96b3becef0/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:511d15193cbe013619dd05414c35a7dedf2088fcee93c6bbb7c77859765bd4e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/e6/9ed5b625c0661c4882fc8cdf302bf8e96c73c40de99c31e0b95ed37d508c/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aea1f9741b603a8d8fedb0ed5502c2bc0accbc51f43e2ad1337fe7259c2b77a5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/58/212c7b6fd51946047fb45d3733da27e2fa8f7384a13457c874186af691b1/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4019a9d473c708cf2f16415688ef0b4639e07abaa569d72f74745bbeffafa2c7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/aa/f5/a40ba78748ae8ebf4934d4b88e77b98497378bc2c24ba55ebe87a4e87057/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:093d63b4b0f52d98ebae33b8c50900d3d67e0666094b1be7a12fffd7f65de74b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/a6/33b1fc0c9f7dcfcfc4a4353daa6308b3ece22496ceece348b3e7a7559a09/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2abe21d8ba64cded53a2a677e149ceb76dcf44284202d737178afe7ba540c1eb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/2d/ceb3f9c12f8cfa56d34995097f6cd99da1325642c60d1b6680dd9df03ed8/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:4feb7511c29f8442cbbc28149a92093d32e815a28aa2c50d333826ad2a20fdf0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385, upload-time = "2025-07-01T15:57:13.958Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/31/1459645f036c3dfeacef89e8e5825e430c77dde8489f3b99eaafcd4a60f5/rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37", size = 372466, upload-time = "2025-07-01T15:53:40.55Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/ff/3d0727f35836cc8773d3eeb9a46c40cc405854e36a8d2e951f3a8391c976/rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0", size = 357825, upload-time = "2025-07-01T15:53:42.247Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/ce/badc5e06120a54099ae287fa96d82cbb650a5f85cf247ffe19c7b157fd1f/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec671691e72dff75817386aa02d81e708b5a7ec0dec6669ec05213ff6b77e1bd", size = 381530, upload-time = "2025-07-01T15:53:43.585Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/a5/fa5d96a66c95d06c62d7a30707b6a4cfec696ab8ae280ee7be14e961e118/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a1cb5d6ce81379401bbb7f6dbe3d56de537fb8235979843f0d53bc2e9815a79", size = 396933, upload-time = "2025-07-01T15:53:45.78Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/a7/7049d66750f18605c591a9db47d4a059e112a0c9ff8de8daf8fa0f446bba/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f789e32fa1fb6a7bf890e0124e7b42d1e60d28ebff57fe806719abb75f0e9a3", size = 513973, upload-time = "2025-07-01T15:53:47.085Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/f1/528d02c7d6b29d29fac8fd784b354d3571cc2153f33f842599ef0cf20dd2/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c55b0a669976cf258afd718de3d9ad1b7d1fe0a91cd1ab36f38b03d4d4aeaaf", size = 402293, upload-time = "2025-07-01T15:53:48.117Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/93/fde36cd6e4685df2cd08508f6c45a841e82f5bb98c8d5ecf05649522acb5/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70d9ec912802ecfd6cd390dadb34a9578b04f9bcb8e863d0a7598ba5e9e7ccc", size = 383787, upload-time = "2025-07-01T15:53:50.874Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/f2/5007553aaba1dcae5d663143683c3dfd03d9395289f495f0aebc93e90f24/rpds_py-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3021933c2cb7def39d927b9862292e0f4c75a13d7de70eb0ab06efed4c508c19", size = 416312, upload-time = "2025-07-01T15:53:52.046Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/a7/ce52c75c1e624a79e48a69e611f1c08844564e44c85db2b6f711d76d10ce/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a7898b6ca3b7d6659e55cdac825a2e58c638cbf335cde41f4619e290dd0ad11", size = 558403, upload-time = "2025-07-01T15:53:53.192Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/d5/e119db99341cc75b538bf4cb80504129fa22ce216672fb2c28e4a101f4d9/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:12bff2ad9447188377f1b2794772f91fe68bb4bbfa5a39d7941fbebdbf8c500f", size = 588323, upload-time = "2025-07-01T15:53:54.336Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/94/d28272a0b02f5fe24c78c20e13bbcb95f03dc1451b68e7830ca040c60bd6/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:191aa858f7d4902e975d4cf2f2d9243816c91e9605070aeb09c0a800d187e323", size = 554541, upload-time = "2025-07-01T15:53:55.469Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/e0/8c41166602f1b791da892d976057eba30685486d2e2c061ce234679c922b/rpds_py-0.26.0-cp310-cp310-win32.whl", hash = "sha256:b37a04d9f52cb76b6b78f35109b513f6519efb481d8ca4c321f6a3b9580b3f45", size = 220442, upload-time = "2025-07-01T15:53:56.524Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/f0/509736bb752a7ab50fb0270c2a4134d671a7b3038030837e5536c3de0e0b/rpds_py-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:38721d4c9edd3eb6670437d8d5e2070063f305bfa2d5aa4278c51cedcd508a84", size = 231314, upload-time = "2025-07-01T15:53:57.842Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/4c/4ee8f7e512030ff79fda1df3243c88d70fc874634e2dbe5df13ba4210078/rpds_py-0.26.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e8cb77286025bdb21be2941d64ac6ca016130bfdcd228739e8ab137eb4406ed", size = 372610, upload-time = "2025-07-01T15:53:58.844Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/9d/3dc16be00f14fc1f03c71b1d67c8df98263ab2710a2fbd65a6193214a527/rpds_py-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e09330b21d98adc8ccb2dbb9fc6cb434e8908d4c119aeaa772cb1caab5440a0", size = 358032, upload-time = "2025-07-01T15:53:59.985Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/5a/7f1bf8f045da2866324a08ae80af63e64e7bfaf83bd31f865a7b91a58601/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9c1b92b774b2e68d11193dc39620d62fd8ab33f0a3c77ecdabe19c179cdbc1", size = 381525, upload-time = "2025-07-01T15:54:01.162Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/8a/04479398c755a066ace10e3d158866beb600867cacae194c50ffa783abd0/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:824e6d3503ab990d7090768e4dfd9e840837bae057f212ff9f4f05ec6d1975e7", size = 397089, upload-time = "2025-07-01T15:54:02.319Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/88/9203f47268db488a1b6d469d69c12201ede776bb728b9d9f29dbfd7df406/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ad7fd2258228bf288f2331f0a6148ad0186b2e3643055ed0db30990e59817a6", size = 514255, upload-time = "2025-07-01T15:54:03.38Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/b4/01ce5d1e853ddf81fbbd4311ab1eff0b3cf162d559288d10fd127e2588b5/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dc23bbb3e06ec1ea72d515fb572c1fea59695aefbffb106501138762e1e915e", size = 402283, upload-time = "2025-07-01T15:54:04.923Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/a2/004c99936997bfc644d590a9defd9e9c93f8286568f9c16cdaf3e14429a7/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80bf832ac7b1920ee29a426cdca335f96a2b5caa839811803e999b41ba9030d", size = 383881, upload-time = "2025-07-01T15:54:06.482Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/1b/ef5fba4a8f81ce04c427bfd96223f92f05e6cd72291ce9d7523db3b03a6c/rpds_py-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0919f38f5542c0a87e7b4afcafab6fd2c15386632d249e9a087498571250abe3", size = 415822, upload-time = "2025-07-01T15:54:07.605Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/80/5c54195aec456b292f7bd8aa61741c8232964063fd8a75fdde9c1e982328/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d422b945683e409000c888e384546dbab9009bb92f7c0b456e217988cf316107", size = 558347, upload-time = "2025-07-01T15:54:08.591Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/1c/1845c1b1fd6d827187c43afe1841d91678d7241cbdb5420a4c6de180a538/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a7711fa562ba2da1aa757e11024ad6d93bad6ad7ede5afb9af144623e5f76a", size = 587956, upload-time = "2025-07-01T15:54:09.963Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/ff/9e979329dd131aa73a438c077252ddabd7df6d1a7ad7b9aacf6261f10faa/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238e8c8610cb7c29460e37184f6799547f7e09e6a9bdbdab4e8edb90986a2318", size = 554363, upload-time = "2025-07-01T15:54:11.073Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/8b/d78cfe034b71ffbe72873a136e71acc7a831a03e37771cfe59f33f6de8a2/rpds_py-0.26.0-cp311-cp311-win32.whl", hash = "sha256:893b022bfbdf26d7bedb083efeea624e8550ca6eb98bf7fea30211ce95b9201a", size = 220123, upload-time = "2025-07-01T15:54:12.382Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/c1/3c8c94c7dd3905dbfde768381ce98778500a80db9924731d87ddcdb117e9/rpds_py-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:87a5531de9f71aceb8af041d72fc4cab4943648d91875ed56d2e629bef6d4c03", size = 231732, upload-time = "2025-07-01T15:54:13.434Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/93/e936fbed1b734eabf36ccb5d93c6a2e9246fbb13c1da011624b7286fae3e/rpds_py-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:de2713f48c1ad57f89ac25b3cb7daed2156d8e822cf0eca9b96a6f990718cc41", size = 221917, upload-time = "2025-07-01T15:54:14.559Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d", size = 363933, upload-time = "2025-07-01T15:54:15.734Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136", size = 350447, upload-time = "2025-07-01T15:54:16.922Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582", size = 384711, upload-time = "2025-07-01T15:54:18.101Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e", size = 400865, upload-time = "2025-07-01T15:54:19.295Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15", size = 517763, upload-time = "2025-07-01T15:54:20.858Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8", size = 406651, upload-time = "2025-07-01T15:54:22.508Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a", size = 386079, upload-time = "2025-07-01T15:54:23.987Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323", size = 421379, upload-time = "2025-07-01T15:54:25.073Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158", size = 562033, upload-time = "2025-07-01T15:54:26.225Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3", size = 591639, upload-time = "2025-07-01T15:54:27.424Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2", size = 557105, upload-time = "2025-07-01T15:54:29.93Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44", size = 223272, upload-time = "2025-07-01T15:54:31.128Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c", size = 234995, upload-time = "2025-07-01T15:54:32.195Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8", size = 223198, upload-time = "2025-07-01T15:54:33.271Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/9a/1f033b0b31253d03d785b0cd905bc127e555ab496ea6b4c7c2e1f951f2fd/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3c0909c5234543ada2515c05dc08595b08d621ba919629e94427e8e03539c958", size = 373226, upload-time = "2025-07-01T15:56:16.578Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/29/5f88023fd6aaaa8ca3c4a6357ebb23f6f07da6079093ccf27c99efce87db/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c1fb0cda2abcc0ac62f64e2ea4b4e64c57dfd6b885e693095460c61bde7bb18e", size = 359230, upload-time = "2025-07-01T15:56:17.978Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/6c/13eaebd28b439da6964dde22712b52e53fe2824af0223b8e403249d10405/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d142d2d6cf9b31c12aa4878d82ed3b2324226270b89b676ac62ccd7df52d08", size = 382363, upload-time = "2025-07-01T15:56:19.977Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/fc/3bb9c486b06da19448646f96147796de23c5811ef77cbfc26f17307b6a9d/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a547e21c5610b7e9093d870be50682a6a6cf180d6da0f42c47c306073bfdbbf6", size = 397146, upload-time = "2025-07-01T15:56:21.39Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/18/9d1b79eb4d18e64ba8bba9e7dec6f9d6920b639f22f07ee9368ca35d4673/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35e9a70a0f335371275cdcd08bc5b8051ac494dd58bff3bbfb421038220dc871", size = 514804, upload-time = "2025-07-01T15:56:22.78Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/5a/175ad7191bdbcd28785204621b225ad70e85cdfd1e09cc414cb554633b21/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dfa6115c6def37905344d56fb54c03afc49104e2ca473d5dedec0f6606913b4", size = 402820, upload-time = "2025-07-01T15:56:24.584Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/45/6a67ecf6d61c4d4aff4bc056e864eec4b2447787e11d1c2c9a0242c6e92a/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:313cfcd6af1a55a286a3c9a25f64af6d0e46cf60bc5798f1db152d97a216ff6f", size = 384567, upload-time = "2025-07-01T15:56:26.064Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ba/16589da828732b46454c61858950a78fe4c931ea4bf95f17432ffe64b241/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7bf2496fa563c046d05e4d232d7b7fd61346e2402052064b773e5c378bf6f73", size = 416520, upload-time = "2025-07-01T15:56:27.608Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/4b/00092999fc7c0c266045e984d56b7314734cc400a6c6dc4d61a35f135a9d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:aa81873e2c8c5aa616ab8e017a481a96742fdf9313c40f14338ca7dbf50cb55f", size = 559362, upload-time = "2025-07-01T15:56:29.078Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/0c/43737053cde1f93ac4945157f7be1428724ab943e2132a0d235a7e161d4e/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:68ffcf982715f5b5b7686bdd349ff75d422e8f22551000c24b30eaa1b7f7ae84", size = 588113, upload-time = "2025-07-01T15:56:30.485Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/46/8e38f6161466e60a997ed7e9951ae5de131dedc3cf778ad35994b4af823d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6188de70e190847bb6db3dc3981cbadff87d27d6fe9b4f0e18726d55795cee9b", size = 555429, upload-time = "2025-07-01T15:56:31.956Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/ac/65da605e9f1dd643ebe615d5bbd11b6efa1d69644fc4bf623ea5ae385a82/rpds_py-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1c962145c7473723df9722ba4c058de12eb5ebedcb4e27e7d902920aa3831ee8", size = 231950, upload-time = "2025-07-01T15:56:33.337Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/f2/b5c85b758a00c513bb0389f8fc8e61eb5423050c91c958cdd21843faa3e6/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f61a9326f80ca59214d1cceb0a09bb2ece5b2563d4e0cd37bfd5515c28510674", size = 373505, upload-time = "2025-07-01T15:56:34.716Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/e0/25db45e391251118e915e541995bb5f5ac5691a3b98fb233020ba53afc9b/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:183f857a53bcf4b1b42ef0f57ca553ab56bdd170e49d8091e96c51c3d69ca696", size = 359468, upload-time = "2025-07-01T15:56:36.219Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/73/dd5ee6075bb6491be3a646b301dfd814f9486d924137a5098e61f0487e16/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:941c1cfdf4799d623cf3aa1d326a6b4fdb7a5799ee2687f3516738216d2262fb", size = 382680, upload-time = "2025-07-01T15:56:37.644Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2f/10/84b522ff58763a5c443f5bcedc1820240e454ce4e620e88520f04589e2ea/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72a8d9564a717ee291f554eeb4bfeafe2309d5ec0aa6c475170bdab0f9ee8e88", size = 397035, upload-time = "2025-07-01T15:56:39.241Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/ea/8667604229a10a520fcbf78b30ccc278977dcc0627beb7ea2c96b3becef0/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:511d15193cbe013619dd05414c35a7dedf2088fcee93c6bbb7c77859765bd4e8", size = 514922, upload-time = "2025-07-01T15:56:40.645Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/e6/9ed5b625c0661c4882fc8cdf302bf8e96c73c40de99c31e0b95ed37d508c/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aea1f9741b603a8d8fedb0ed5502c2bc0accbc51f43e2ad1337fe7259c2b77a5", size = 402822, upload-time = "2025-07-01T15:56:42.137Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/58/212c7b6fd51946047fb45d3733da27e2fa8f7384a13457c874186af691b1/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4019a9d473c708cf2f16415688ef0b4639e07abaa569d72f74745bbeffafa2c7", size = 384336, upload-time = "2025-07-01T15:56:44.239Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/f5/a40ba78748ae8ebf4934d4b88e77b98497378bc2c24ba55ebe87a4e87057/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:093d63b4b0f52d98ebae33b8c50900d3d67e0666094b1be7a12fffd7f65de74b", size = 416871, upload-time = "2025-07-01T15:56:46.284Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/a6/33b1fc0c9f7dcfcfc4a4353daa6308b3ece22496ceece348b3e7a7559a09/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2abe21d8ba64cded53a2a677e149ceb76dcf44284202d737178afe7ba540c1eb", size = 559439, upload-time = "2025-07-01T15:56:48.549Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/2d/ceb3f9c12f8cfa56d34995097f6cd99da1325642c60d1b6680dd9df03ed8/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:4feb7511c29f8442cbbc28149a92093d32e815a28aa2c50d333826ad2a20fdf0", size = 588380, upload-time = "2025-07-01T15:56:50.086Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c", size = 555334, upload-time = "2025-07-01T15:56:51.703Z" }, ] [[package]] name = "rsa" version = "4.9.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, ] [[package]] @@ -6081,97 +6081,97 @@ wheels = [ [[package]] name = "ruamel-base" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ea/77/60a0945f4b4eac4b6bd74d1b8e103ae58d0f07b934f962bb4c49e6ec205e/ruamel.base-1.0.0.tar.gz", hash = "sha256:c041333a0f0f00cd6593eb36aa83abb1a9e7544e83ba7a42aa7ac7476cee5cf3" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/77/60a0945f4b4eac4b6bd74d1b8e103ae58d0f07b934f962bb4c49e6ec205e/ruamel.base-1.0.0.tar.gz", hash = "sha256:c041333a0f0f00cd6593eb36aa83abb1a9e7544e83ba7a42aa7ac7476cee5cf3", size = 5219, upload-time = "2015-08-27T15:26:52.744Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/1f/e1/5257f5d1636a26fdb50cdcc0e7e5e65d230b88c2dd5090ac797b9e45d1d3/ruamel.base-1.0.0-py3-none-any.whl", hash = "sha256:3613a90afcf0735540804af2a693f630a0bccebefec9b4023a39e88950bb294e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/e1/5257f5d1636a26fdb50cdcc0e7e5e65d230b88c2dd5090ac797b9e45d1d3/ruamel.base-1.0.0-py3-none-any.whl", hash = "sha256:3613a90afcf0735540804af2a693f630a0bccebefec9b4023a39e88950bb294e", size = 4385, upload-time = "2015-08-27T17:22:13.538Z" }, ] [[package]] name = "ruamel-yaml" version = "0.18.14" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/39/87/6da0df742a4684263261c253f00edd5829e6aca970fff69e75028cccc547/ruamel.yaml-0.18.14.tar.gz", hash = "sha256:7227b76aaec364df15936730efbf7d72b30c0b79b1d578bbb8e3dcb2d81f52b7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/87/6da0df742a4684263261c253f00edd5829e6aca970fff69e75028cccc547/ruamel.yaml-0.18.14.tar.gz", hash = "sha256:7227b76aaec364df15936730efbf7d72b30c0b79b1d578bbb8e3dcb2d81f52b7", size = 145511, upload-time = "2025-06-09T08:51:09.828Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/af/6d/6fe4805235e193aad4aaf979160dd1f3c487c57d48b810c816e6e842171b/ruamel.yaml-0.18.14-py3-none-any.whl", hash = "sha256:710ff198bb53da66718c7db27eec4fbcc9aa6ca7204e4c1df2f282b6fe5eb6b2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/6d/6fe4805235e193aad4aaf979160dd1f3c487c57d48b810c816e6e842171b/ruamel.yaml-0.18.14-py3-none-any.whl", hash = "sha256:710ff198bb53da66718c7db27eec4fbcc9aa6ca7204e4c1df2f282b6fe5eb6b2", size = 118570, upload-time = "2025-06-09T08:51:06.348Z" }, ] [[package]] name = "ruamel-yaml-clib" version = "0.2.12" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969" }, - { url = "https://mirrors.aliyun.com/pypi/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680" }, - { url = "https://mirrors.aliyun.com/pypi/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301, upload-time = "2024-10-20T10:12:35.876Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728, upload-time = "2024-10-20T10:12:37.858Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230, upload-time = "2024-10-20T10:12:39.457Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712, upload-time = "2024-10-20T10:12:41.119Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936, upload-time = "2024-10-21T11:26:37.419Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580, upload-time = "2024-10-21T11:26:39.503Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393, upload-time = "2024-12-11T19:58:13.873Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326, upload-time = "2024-10-20T10:12:42.967Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079, upload-time = "2024-10-20T10:12:44.117Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload-time = "2024-10-20T10:12:45.162Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload-time = "2024-10-20T10:12:46.758Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload-time = "2024-10-20T10:12:48.605Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload-time = "2024-10-20T10:12:51.124Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload-time = "2024-10-21T11:26:41.438Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload-time = "2024-10-21T11:26:43.62Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload-time = "2024-12-11T19:58:15.592Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload-time = "2024-10-20T10:12:52.865Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload-time = "2024-10-20T10:12:54.652Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" }, ] [[package]] name = "s3transfer" version = "0.10.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c0/0a/1cdbabf9edd0ea7747efdf6c9ab4e7061b085aa7f9bfc36bb1601563b069/s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/0a/1cdbabf9edd0ea7747efdf6c9ab4e7061b085aa7f9bfc36bb1601563b069/s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7", size = 145287, upload-time = "2024-11-20T21:06:05.981Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/66/05/7957af15543b8c9799209506df4660cba7afc4cf94bfb60513827e96bed6/s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/05/7957af15543b8c9799209506df4660cba7afc4cf94bfb60513827e96bed6/s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", size = 83175, upload-time = "2024-11-20T21:06:03.961Z" }, ] [[package]] name = "safetensors" version = "0.5.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135" }, - { url = "https://mirrors.aliyun.com/pypi/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210, upload-time = "2025-02-26T09:15:13.155Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917, upload-time = "2025-02-26T09:15:03.702Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419, upload-time = "2025-02-26T09:15:01.765Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493, upload-time = "2025-02-26T09:14:51.812Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400, upload-time = "2025-02-26T09:14:53.549Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891, upload-time = "2025-02-26T09:14:55.717Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694, upload-time = "2025-02-26T09:14:57.036Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642, upload-time = "2025-02-26T09:15:00.544Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241, upload-time = "2025-02-26T09:14:58.303Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001, upload-time = "2025-02-26T09:15:05.79Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013, upload-time = "2025-02-26T09:15:07.892Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687, upload-time = "2025-02-26T09:15:09.979Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147, upload-time = "2025-02-26T09:15:11.185Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677, upload-time = "2025-02-26T09:15:16.554Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878, upload-time = "2025-02-26T09:15:14.99Z" }, ] [[package]] name = "scholarly" version = "1.7.11" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "arrow" }, { name = "beautifulsoup4" }, @@ -6186,87 +6186,87 @@ dependencies = [ { name = "sphinx-rtd-theme" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/62/2d/38f22adc8abc1166d2c039e477bd8d7782fe32a72f5c80aed94b23348ac1/scholarly-1.7.11.tar.gz", hash = "sha256:2c983dd44d9d9398a6f2605102ae6e5586023b41ebbaec1461917ee48eb153f0" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/2d/38f22adc8abc1166d2c039e477bd8d7782fe32a72f5c80aed94b23348ac1/scholarly-1.7.11.tar.gz", hash = "sha256:2c983dd44d9d9398a6f2605102ae6e5586023b41ebbaec1461917ee48eb153f0", size = 38819, upload-time = "2023-01-16T22:01:00.087Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b6/23/4340a9068b451b7bb03ff02243bd7aea4c1869781f41e2387c9348629edd/scholarly-1.7.11-py3-none-any.whl", hash = "sha256:be404853e0d020254de32d2050c54dc201f1f36efa4a9d3f8e740d3be4361b20" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/23/4340a9068b451b7bb03ff02243bd7aea4c1869781f41e2387c9348629edd/scholarly-1.7.11-py3-none-any.whl", hash = "sha256:be404853e0d020254de32d2050c54dc201f1f36efa4a9d3f8e740d3be4361b20", size = 39380, upload-time = "2023-01-16T22:00:57.549Z" }, ] [[package]] name = "scikit-learn" version = "1.5.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "joblib" }, { name = "numpy" }, { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/bf/8a/06e499bca463905000f50e461c9445e949aafdd33ea3b62024aa2238b83d/scikit_learn-1.5.0.tar.gz", hash = "sha256:789e3db01c750ed6d496fa2db7d50637857b451e57bcae863bff707c1247bef7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/8a/06e499bca463905000f50e461c9445e949aafdd33ea3b62024aa2238b83d/scikit_learn-1.5.0.tar.gz", hash = "sha256:789e3db01c750ed6d496fa2db7d50637857b451e57bcae863bff707c1247bef7", size = 7820839, upload-time = "2024-05-21T16:34:07.711Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/87/d1/900698985c526e4c06c03028b9272993f248ae43a739a2f30a91d8f1a5af/scikit_learn-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12e40ac48555e6b551f0a0a5743cc94cc5a765c9513fe708e01f0aa001da2801" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/e5/42e5bf73aeadc0f32152de33593f3f97ae8a59bb4c46d7725e7d3d76f4c4/scikit_learn-1.5.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f405c4dae288f5f6553b10c4ac9ea7754d5180ec11e296464adb5d6ac68b6ef5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/12/a31235236e7c62f5f7b68ea471fb82bac818be5b40f608a51a44fa8388bf/scikit_learn-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df8ccabbf583315f13160a4bb06037bde99ea7d8211a69787a6b7c5d4ebb6fc3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1e/7d/1a2ea8eb5b4df373c30c7418cf26305a4a05e2a0e56c80a8043b791595f3/scikit_learn-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c75ea812cd83b1385bbfa94ae971f0d80adb338a9523f6bbcb5e0b0381151d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/1a/7313c0d70ec8bfcf83cdd49696679d54d9d1a062a60fba270e7b4fc457f2/scikit_learn-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:a90c5da84829a0b9b4bf00daf62754b2be741e66b5946911f5bdfaa869fcedd6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/d4/70a9393ab88862c070a263a464042ab4e572a1353b4c3c308bc72a5b68cf/scikit_learn-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a65af2d8a6cce4e163a7951a4cfbfa7fceb2d5c013a4b593686c7f16445cf9d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/97/dfc635bd435655c1216756b543e0427579df144914a055a188d3c0ffd52f/scikit_learn-1.5.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:4c0c56c3005f2ec1db3787aeaabefa96256580678cec783986836fc64f8ff622" }, - { url = "https://mirrors.aliyun.com/pypi/packages/61/f5/18dc69d22ec950225237d42b61d3338affc46e5ea63c27c6915f3678f5f2/scikit_learn-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f77547165c00625551e5c250cefa3f03f2fc92c5e18668abd90bfc4be2e0bff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/c0/63d3a8da39a2ee051df229111aa93f6dca2b56f8080abd34993938166455/scikit_learn-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:118a8d229a41158c9f90093e46b3737120a165181a1b58c03461447aa4657415" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/20/6d1a0a61d468b37a142fd90bb93c73bc1c2205db4a69ac630ed218c31612/scikit_learn-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:a03b09f9f7f09ffe8c5efffe2e9de1196c696d811be6798ad5eddf323c6f4d40" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1e/21/fe8e90eb7dc796ed384daaf45a83e729a41fa7a9bf14bc1a0b69fd05b39a/scikit_learn-1.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:460806030c666addee1f074788b3978329a5bfdc9b7d63e7aad3f6d45c67a210" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/4b/c035ce6771dd56283cd587e941054ebb38a14868729e28a0f7c6c9ff9ebd/scikit_learn-1.5.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:1b94d6440603752b27842eda97f6395f570941857456c606eb1d638efdb38184" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/a1/e64f125382f2fc46dd1f3a3c2d390f02db896e3803a3e7898c4ca48390e0/scikit_learn-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d82c2e573f0f2f2f0be897e7a31fcf4e73869247738ab8c3ce7245549af58ab8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/54/e70102a9c12d27d985ba659f336851732415e5a02864bef2ead36afaf15d/scikit_learn-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3a10e1d9e834e84d05e468ec501a356226338778769317ee0b84043c0d8fb06" }, - { url = "https://mirrors.aliyun.com/pypi/packages/57/ed/f607ebf69f87bcce2e3fa329bd78da8cafd3d51190a19d58012d2d7f2252/scikit_learn-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:855fc5fa8ed9e4f08291203af3d3e5fbdc4737bd617a371559aaa2088166046e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/d1/900698985c526e4c06c03028b9272993f248ae43a739a2f30a91d8f1a5af/scikit_learn-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12e40ac48555e6b551f0a0a5743cc94cc5a765c9513fe708e01f0aa001da2801", size = 12096286, upload-time = "2024-05-21T16:33:03.119Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/e5/42e5bf73aeadc0f32152de33593f3f97ae8a59bb4c46d7725e7d3d76f4c4/scikit_learn-1.5.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f405c4dae288f5f6553b10c4ac9ea7754d5180ec11e296464adb5d6ac68b6ef5", size = 10969691, upload-time = "2024-05-21T16:33:07.004Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/12/a31235236e7c62f5f7b68ea471fb82bac818be5b40f608a51a44fa8388bf/scikit_learn-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df8ccabbf583315f13160a4bb06037bde99ea7d8211a69787a6b7c5d4ebb6fc3", size = 12490175, upload-time = "2024-05-21T16:33:10.22Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/7d/1a2ea8eb5b4df373c30c7418cf26305a4a05e2a0e56c80a8043b791595f3/scikit_learn-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c75ea812cd83b1385bbfa94ae971f0d80adb338a9523f6bbcb5e0b0381151d4", size = 13343697, upload-time = "2024-05-21T16:33:13.68Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/1a/7313c0d70ec8bfcf83cdd49696679d54d9d1a062a60fba270e7b4fc457f2/scikit_learn-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:a90c5da84829a0b9b4bf00daf62754b2be741e66b5946911f5bdfaa869fcedd6", size = 10962967, upload-time = "2024-05-21T16:33:16.646Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/d4/70a9393ab88862c070a263a464042ab4e572a1353b4c3c308bc72a5b68cf/scikit_learn-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a65af2d8a6cce4e163a7951a4cfbfa7fceb2d5c013a4b593686c7f16445cf9d", size = 12081437, upload-time = "2024-05-21T16:33:20.16Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/97/dfc635bd435655c1216756b543e0427579df144914a055a188d3c0ffd52f/scikit_learn-1.5.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:4c0c56c3005f2ec1db3787aeaabefa96256580678cec783986836fc64f8ff622", size = 10973963, upload-time = "2024-05-21T16:33:22.801Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/f5/18dc69d22ec950225237d42b61d3338affc46e5ea63c27c6915f3678f5f2/scikit_learn-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f77547165c00625551e5c250cefa3f03f2fc92c5e18668abd90bfc4be2e0bff", size = 12480861, upload-time = "2024-05-21T16:33:26.131Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/c0/63d3a8da39a2ee051df229111aa93f6dca2b56f8080abd34993938166455/scikit_learn-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:118a8d229a41158c9f90093e46b3737120a165181a1b58c03461447aa4657415", size = 13328661, upload-time = "2024-05-21T16:33:29.627Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/20/6d1a0a61d468b37a142fd90bb93c73bc1c2205db4a69ac630ed218c31612/scikit_learn-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:a03b09f9f7f09ffe8c5efffe2e9de1196c696d811be6798ad5eddf323c6f4d40", size = 10972963, upload-time = "2024-05-21T16:33:32.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/21/fe8e90eb7dc796ed384daaf45a83e729a41fa7a9bf14bc1a0b69fd05b39a/scikit_learn-1.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:460806030c666addee1f074788b3978329a5bfdc9b7d63e7aad3f6d45c67a210", size = 12096541, upload-time = "2024-05-21T16:33:36.475Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/4b/c035ce6771dd56283cd587e941054ebb38a14868729e28a0f7c6c9ff9ebd/scikit_learn-1.5.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:1b94d6440603752b27842eda97f6395f570941857456c606eb1d638efdb38184", size = 11031507, upload-time = "2024-05-21T16:33:39.896Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/a1/e64f125382f2fc46dd1f3a3c2d390f02db896e3803a3e7898c4ca48390e0/scikit_learn-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d82c2e573f0f2f2f0be897e7a31fcf4e73869247738ab8c3ce7245549af58ab8", size = 12082985, upload-time = "2024-05-21T16:33:42.807Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/54/e70102a9c12d27d985ba659f336851732415e5a02864bef2ead36afaf15d/scikit_learn-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3a10e1d9e834e84d05e468ec501a356226338778769317ee0b84043c0d8fb06", size = 13065320, upload-time = "2024-05-21T16:33:45.65Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/ed/f607ebf69f87bcce2e3fa329bd78da8cafd3d51190a19d58012d2d7f2252/scikit_learn-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:855fc5fa8ed9e4f08291203af3d3e5fbdc4737bd617a371559aaa2088166046e", size = 10938084, upload-time = "2024-05-21T16:33:49.011Z" }, ] [[package]] name = "scipy" version = "1.12.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/30/85/cdbf2c3c460fe5aae812917866392068a88d02f07de0fe31ce738734c477/scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c7/d9/214971dae573bd7e9303b56d2612dae439decbfc0dae0f539a591c0562ce/scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/14/549fd7066a112c4bdf1cc11228d11284bc784ea09124fc4d663f28815564/scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/1d/0582401b6d77865e080c90f39e52f65ca2bdc94e668e0bfbed8977dae3f4/scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f5/aa/8e6071a5e4dca4ec68b5b22e4991ee74c59c5d372112b9c236ec1faff57d/scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/9e/43b86ec57ecdc9931b43aaf727f9d71743bfd06bdddfd441165bd3d8c6be/scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fd/a7/5f829b100d208c85163aecba93faf01d088d944fc91585338751d812f1e4/scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/32/7915195ca4643508fe9730691eaed57b879646279572b10b02bdadf165c5/scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/d4/e6c57acc61e59cd46acca27af1f400094d5dee218e372cc604b8162b97cb/scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/c5/d40abc1a857c1c6519e1a4e096d6aee86861eddac019fb736b6af8a58d25/scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/b8/7169935f9a2ea9e274ad8c21d6133d492079e6ebc3fc69a915c2375616b0/scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/64/e7/4dbb779d09d1cb757ddbe42cae7c4fe8270497566bb902138d637b04d88c/scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9a/25/5b30cb3efc9566f0ebeaeca1976150316353c17031ad7868ef46de5ab8dc/scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0d/4a/b2b2cae0c5dfd46361245a67102886ed7188805bdf7044e36fe838bbcf26/scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/ba/744bbdd65eb3fce1412dd4633fc425ad39e6b4068b5b158aee1cd3afeb54/scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/db/fd/81feac476e1ae495b51b8c3636aee1f50a1c5ca2a3557f5b0043d4e2fb02/scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/7d/850bfe9462fff393130519eb54f97d43ad9c280ec4297b4cb98b7c2e96cd/scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7e/7f/504b7b3834d8c9229831c6c58a44943e29a34004eeb34c7ff150add4e001/scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f3/31/91a2a3c5eb85d2bfa86d7c98f2df5d77dcdefb3d80ca9f9037ad04393acf/scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/85/cdbf2c3c460fe5aae812917866392068a88d02f07de0fe31ce738734c477/scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3", size = 56811768, upload-time = "2024-01-20T21:13:43.442Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/d9/214971dae573bd7e9303b56d2612dae439decbfc0dae0f539a591c0562ce/scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b", size = 38900384, upload-time = "2024-01-20T21:10:31.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/14/549fd7066a112c4bdf1cc11228d11284bc784ea09124fc4d663f28815564/scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1", size = 31357553, upload-time = "2024-01-20T21:10:38.509Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/1d/0582401b6d77865e080c90f39e52f65ca2bdc94e668e0bfbed8977dae3f4/scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563", size = 34789974, upload-time = "2024-01-20T21:10:45.054Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/aa/8e6071a5e4dca4ec68b5b22e4991ee74c59c5d372112b9c236ec1faff57d/scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c", size = 38441046, upload-time = "2024-01-20T21:10:51.285Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/9e/43b86ec57ecdc9931b43aaf727f9d71743bfd06bdddfd441165bd3d8c6be/scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd", size = 38630107, upload-time = "2024-01-20T21:10:58.406Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/a7/5f829b100d208c85163aecba93faf01d088d944fc91585338751d812f1e4/scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2", size = 46191228, upload-time = "2024-01-20T21:11:05.92Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/32/7915195ca4643508fe9730691eaed57b879646279572b10b02bdadf165c5/scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08", size = 38908720, upload-time = "2024-01-20T21:11:13.467Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/d4/e6c57acc61e59cd46acca27af1f400094d5dee218e372cc604b8162b97cb/scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c", size = 31392892, upload-time = "2024-01-20T21:11:18.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/c5/d40abc1a857c1c6519e1a4e096d6aee86861eddac019fb736b6af8a58d25/scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467", size = 34733860, upload-time = "2024-01-20T21:11:26.666Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/b8/7169935f9a2ea9e274ad8c21d6133d492079e6ebc3fc69a915c2375616b0/scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a", size = 38418720, upload-time = "2024-01-20T21:11:33.479Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/e7/4dbb779d09d1cb757ddbe42cae7c4fe8270497566bb902138d637b04d88c/scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba", size = 38652247, upload-time = "2024-01-20T21:11:40.229Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/25/5b30cb3efc9566f0ebeaeca1976150316353c17031ad7868ef46de5ab8dc/scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70", size = 46162940, upload-time = "2024-01-20T21:11:47.726Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/4a/b2b2cae0c5dfd46361245a67102886ed7188805bdf7044e36fe838bbcf26/scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372", size = 38911995, upload-time = "2024-01-20T21:11:54.759Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/ba/744bbdd65eb3fce1412dd4633fc425ad39e6b4068b5b158aee1cd3afeb54/scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3", size = 31433326, upload-time = "2024-01-20T21:12:00.295Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/fd/81feac476e1ae495b51b8c3636aee1f50a1c5ca2a3557f5b0043d4e2fb02/scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc", size = 34165749, upload-time = "2024-01-20T21:12:06.38Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/7d/850bfe9462fff393130519eb54f97d43ad9c280ec4297b4cb98b7c2e96cd/scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c", size = 37790844, upload-time = "2024-01-20T21:12:12.826Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/7f/504b7b3834d8c9229831c6c58a44943e29a34004eeb34c7ff150add4e001/scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338", size = 38026369, upload-time = "2024-01-20T21:12:19.69Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/31/91a2a3c5eb85d2bfa86d7c98f2df5d77dcdefb3d80ca9f9037ad04393acf/scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c", size = 45816713, upload-time = "2024-01-20T21:12:26.619Z" }, ] [[package]] name = "seaborn" version = "0.13.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "matplotlib" }, { name = "numpy" }, { name = "pandas" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, ] [[package]] name = "selenium" version = "4.22.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "certifi" }, { name = "trio" }, @@ -6275,15 +6275,15 @@ dependencies = [ { name = "urllib3", extra = ["socks"] }, { name = "websocket-client" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/04/93/fe0473c381dddce4db9527cf442d5949460fab4a92713fb5984386054323/selenium-4.22.0.tar.gz", hash = "sha256:903c8c9d61b3eea6fcc9809dc7d9377e04e2ac87709876542cc8f863e482c4ce" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/93/fe0473c381dddce4db9527cf442d5949460fab4a92713fb5984386054323/selenium-4.22.0.tar.gz", hash = "sha256:903c8c9d61b3eea6fcc9809dc7d9377e04e2ac87709876542cc8f863e482c4ce", size = 9242392, upload-time = "2024-06-20T20:48:05.959Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f2/7d/3a0b9c229d87a189b64c3b8e6d87a970a1ef7875995dc31bd18e65fa1c17/selenium-4.22.0-py3-none-any.whl", hash = "sha256:e424991196e9857e19bf04fe5c1c0a4aac076794ff5e74615b1124e729d93104" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/7d/3a0b9c229d87a189b64c3b8e6d87a970a1ef7875995dc31bd18e65fa1c17/selenium-4.22.0-py3-none-any.whl", hash = "sha256:e424991196e9857e19bf04fe5c1c0a4aac076794ff5e74615b1124e729d93104", size = 9437133, upload-time = "2024-06-20T20:48:01.936Z" }, ] [[package]] name = "selenium-wire" version = "5.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "blinker" }, { name = "brotli" }, @@ -6300,15 +6300,15 @@ dependencies = [ { name = "wsproto" }, { name = "zstandard" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9f/00/60b39e8a1efe6919d1390f07d84a3eeba4aeae5b829f2f848344c798f783/selenium-wire-5.1.0.tar.gz", hash = "sha256:b1cd4eae44d9959381abe3bb186472520d063c658e279f98555def3d4e6dd29b" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/00/60b39e8a1efe6919d1390f07d84a3eeba4aeae5b829f2f848344c798f783/selenium-wire-5.1.0.tar.gz", hash = "sha256:b1cd4eae44d9959381abe3bb186472520d063c658e279f98555def3d4e6dd29b", size = 62145825, upload-time = "2022-10-15T14:31:11.057Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/eb/7e/9548b365eab16730b6a8da25c6e1f83f3b84fb6092ecd2d4d69933d08a45/selenium_wire-5.1.0-py3-none-any.whl", hash = "sha256:fbf930d9992f8b6d24bb16b3e6221bab596a41f6ae7548270b7d5a92f3402622" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/7e/9548b365eab16730b6a8da25c6e1f83f3b84fb6092ecd2d4d69933d08a45/selenium_wire-5.1.0-py3-none-any.whl", hash = "sha256:fbf930d9992f8b6d24bb16b3e6221bab596a41f6ae7548270b7d5a92f3402622", size = 239589, upload-time = "2022-10-15T14:31:06.068Z" }, ] [[package]] name = "sentence-transformers" version = "3.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "huggingface-hub" }, { name = "numpy" }, @@ -6319,134 +6319,134 @@ dependencies = [ { name = "tqdm" }, { name = "transformers" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/41/fb/2368f84127920d86330b533792e66b26264e92b729b5c1998aaa33d2e22f/sentence_transformers-3.0.1.tar.gz", hash = "sha256:8a3d2c537cc4d1014ccc20ac92be3d6135420a3bc60ae29a3a8a9b4bb35fbff6" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/fb/2368f84127920d86330b533792e66b26264e92b729b5c1998aaa33d2e22f/sentence_transformers-3.0.1.tar.gz", hash = "sha256:8a3d2c537cc4d1014ccc20ac92be3d6135420a3bc60ae29a3a8a9b4bb35fbff6", size = 177258, upload-time = "2024-06-07T13:01:12.32Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/58/4b/922436953394e1bfda05e4bf1fe0e80f609770f256c59a9df7a9254f3e0d/sentence_transformers-3.0.1-py3-none-any.whl", hash = "sha256:01050cc4053c49b9f5b78f6980b5a72db3fd3a0abb9169b1792ac83875505ee6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/4b/922436953394e1bfda05e4bf1fe0e80f609770f256c59a9df7a9254f3e0d/sentence_transformers-3.0.1-py3-none-any.whl", hash = "sha256:01050cc4053c49b9f5b78f6980b5a72db3fd3a0abb9169b1792ac83875505ee6", size = 227071, upload-time = "2024-06-07T13:01:10.063Z" }, ] [[package]] name = "setuptools" version = "75.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/07/37/b31be7e4b9f13b59cde9dcaeff112d401d49e0dc5b37ed4a9fc8fb12f409/setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/37/b31be7e4b9f13b59cde9dcaeff112d401d49e0dc5b37ed4a9fc8fb12f409/setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec", size = 1350308, upload-time = "2024-10-16T10:21:56.437Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/31/2d/90165d51ecd38f9a02c6832198c13a4e48652485e2ccf863ebb942c531b6/setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/2d/90165d51ecd38f9a02c6832198c13a4e48652485e2ccf863ebb942c531b6/setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8", size = 1249825, upload-time = "2024-10-16T10:21:53.64Z" }, ] [[package]] name = "sgmllib3k" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750, upload-time = "2010-08-24T14:33:52.445Z" } [[package]] name = "shapely" version = "2.0.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ad/99/c47247f4d688bbb5346df5ff1de5d9792b6d95cbbb2fd7b71f45901c1878/shapely-2.0.5.tar.gz", hash = "sha256:bff2366bc786bfa6cb353d6b47d0443c570c32776612e527ee47b6df63fcfe32" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e1/7a/06aafb341a411c594d3f8f2733f2f644814facf0e6e947a829840f562d36/shapely-2.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89d34787c44f77a7d37d55ae821f3a784fa33592b9d217a45053a93ade899375" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/ad/8a9fe3b987058ff4f4ba8a7d7d19894f9952add4b1ba7649597cbba715d6/shapely-2.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:798090b426142df2c5258779c1d8d5734ec6942f778dab6c6c30cfe7f3bf64ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/83/94/bd391727c29dd014d4a24b92c54717de202e2aa6a0d9b5e9320215b11683/shapely-2.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45211276900c4790d6bfc6105cbf1030742da67594ea4161a9ce6812a6721e68" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a0/b6/8cbd3674fdbd235ef9b82e055b884034ae9526a26a119dd4b7636303cd39/shapely-2.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e119444bc27ca33e786772b81760f2028d930ac55dafe9bc50ef538b794a8e1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/bb/965b1dba79a63912f5a9deb183bab4f725d5c2e39c5ede1b36041b9547e8/shapely-2.0.5-cp310-cp310-win32.whl", hash = "sha256:9a4492a2b2ccbeaebf181e7310d2dfff4fdd505aef59d6cb0f217607cb042fb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/70/a5/6d171586eb850c7582d2aeab027e1027ace4eb3a6c2e5f05746b8ab039e0/shapely-2.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:1e5cb5ee72f1bc7ace737c9ecd30dc174a5295fae412972d3879bac2e82c8fae" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/3d/0d3ab80860cda6afbce9736fa1f091f452092d344fdd4e3c65e5fe7b1111/shapely-2.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bbfb048a74cf273db9091ff3155d373020852805a37dfc846ab71dde4be93ec" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/68/6b51b7587547f6bbd0965cf957505a0ebec93510e840572a983003b3a0a9/shapely-2.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93be600cbe2fbaa86c8eb70656369f2f7104cd231f0d6585c7d0aa555d6878b8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7e/4e/4e83b9f3d7f0ce523c92bdf3dfe0292738d8ad2b589971390d6205bc843e/shapely-2.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8e71bb9a46814019f6644c4e2560a09d44b80100e46e371578f35eaaa9da1c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/a8/c8b0f1a165e161247caf0fc265d61de3c4ea27d7c313c7ebfb1c4f6ddea4/shapely-2.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5251c28a29012e92de01d2e84f11637eb1d48184ee8f22e2df6c8c578d26760" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/04/111f8d42ad50fb60b2fa725c64a6988d2a49ea513c4feb4e02f93dc353bd/shapely-2.0.5-cp311-cp311-win32.whl", hash = "sha256:35110e80070d664781ec7955c7de557456b25727a0257b354830abb759bf8311" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/1b/092fff53cbeced411eed2717592e31cadd3e52f0ebaba5f2df3f34913f96/shapely-2.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c6b78c0007a34ce7144f98b7418800e0a6a5d9a762f2244b00ea560525290c9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/df/8062f14cb7aa502b8bda358103facedc80b87eec41e3391182655ff40615/shapely-2.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:03bd7b5fa5deb44795cc0a503999d10ae9d8a22df54ae8d4a4cd2e8a93466195" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5d/e7/719f384857c39aa51aa19d09d7cac84aeab1b25a7d0dab62433bf7b419e9/shapely-2.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ff9521991ed9e201c2e923da014e766c1aa04771bc93e6fe97c27dcf0d40ace" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a4/77/c05e794a65263deb020d7e25623234975dd96881f9e8cde341810ca683e7/shapely-2.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b65365cfbf657604e50d15161ffcc68de5cdb22a601bbf7823540ab4918a98d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/32/b7687654b6e747ceae8f9fa4cc7489a8ebf275c64caf811f949d87e89f5d/shapely-2.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21f64e647a025b61b19585d2247137b3a38a35314ea68c66aaf507a1c03ef6fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/9c/5b68b3cd484065c7d33d83168d2ecfebfeeaa6d88bc9cfd830de2df490ac/shapely-2.0.5-cp312-cp312-win32.whl", hash = "sha256:3ac7dc1350700c139c956b03d9c3df49a5b34aaf91d024d1510a09717ea39199" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/c3/e98e3eb9f06def32b8e2454ab718cafb99149f023dff023e257125132d6e/shapely-2.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:30e8737983c9d954cd17feb49eb169f02f1da49e24e5171122cf2c2b62d65c95" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/99/c47247f4d688bbb5346df5ff1de5d9792b6d95cbbb2fd7b71f45901c1878/shapely-2.0.5.tar.gz", hash = "sha256:bff2366bc786bfa6cb353d6b47d0443c570c32776612e527ee47b6df63fcfe32", size = 282188, upload-time = "2024-07-13T10:52:59.762Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/7a/06aafb341a411c594d3f8f2733f2f644814facf0e6e947a829840f562d36/shapely-2.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89d34787c44f77a7d37d55ae821f3a784fa33592b9d217a45053a93ade899375", size = 1448988, upload-time = "2024-07-13T10:51:57.088Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/ad/8a9fe3b987058ff4f4ba8a7d7d19894f9952add4b1ba7649597cbba715d6/shapely-2.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:798090b426142df2c5258779c1d8d5734ec6942f778dab6c6c30cfe7f3bf64ff", size = 1282276, upload-time = "2024-07-13T10:52:00.504Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/94/bd391727c29dd014d4a24b92c54717de202e2aa6a0d9b5e9320215b11683/shapely-2.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45211276900c4790d6bfc6105cbf1030742da67594ea4161a9ce6812a6721e68", size = 2382885, upload-time = "2024-07-13T19:44:01.741Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/b6/8cbd3674fdbd235ef9b82e055b884034ae9526a26a119dd4b7636303cd39/shapely-2.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e119444bc27ca33e786772b81760f2028d930ac55dafe9bc50ef538b794a8e1", size = 2468813, upload-time = "2024-07-13T10:52:02.942Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/bb/965b1dba79a63912f5a9deb183bab4f725d5c2e39c5ede1b36041b9547e8/shapely-2.0.5-cp310-cp310-win32.whl", hash = "sha256:9a4492a2b2ccbeaebf181e7310d2dfff4fdd505aef59d6cb0f217607cb042fb3", size = 1294921, upload-time = "2024-07-13T10:52:05.028Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/70/a5/6d171586eb850c7582d2aeab027e1027ace4eb3a6c2e5f05746b8ab039e0/shapely-2.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:1e5cb5ee72f1bc7ace737c9ecd30dc174a5295fae412972d3879bac2e82c8fae", size = 1441040, upload-time = "2024-07-13T10:52:06.856Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/3d/0d3ab80860cda6afbce9736fa1f091f452092d344fdd4e3c65e5fe7b1111/shapely-2.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bbfb048a74cf273db9091ff3155d373020852805a37dfc846ab71dde4be93ec", size = 1448893, upload-time = "2024-07-13T10:52:08.6Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/68/6b51b7587547f6bbd0965cf957505a0ebec93510e840572a983003b3a0a9/shapely-2.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93be600cbe2fbaa86c8eb70656369f2f7104cd231f0d6585c7d0aa555d6878b8", size = 1282344, upload-time = "2024-07-13T10:52:10.403Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/4e/4e83b9f3d7f0ce523c92bdf3dfe0292738d8ad2b589971390d6205bc843e/shapely-2.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8e71bb9a46814019f6644c4e2560a09d44b80100e46e371578f35eaaa9da1c", size = 2444118, upload-time = "2024-07-13T19:44:08.876Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/a8/c8b0f1a165e161247caf0fc265d61de3c4ea27d7c313c7ebfb1c4f6ddea4/shapely-2.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5251c28a29012e92de01d2e84f11637eb1d48184ee8f22e2df6c8c578d26760", size = 2528510, upload-time = "2024-07-13T10:52:12.592Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/04/111f8d42ad50fb60b2fa725c64a6988d2a49ea513c4feb4e02f93dc353bd/shapely-2.0.5-cp311-cp311-win32.whl", hash = "sha256:35110e80070d664781ec7955c7de557456b25727a0257b354830abb759bf8311", size = 1294608, upload-time = "2024-07-13T10:52:14.644Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/1b/092fff53cbeced411eed2717592e31cadd3e52f0ebaba5f2df3f34913f96/shapely-2.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c6b78c0007a34ce7144f98b7418800e0a6a5d9a762f2244b00ea560525290c9", size = 1441974, upload-time = "2024-07-13T10:52:16.986Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/df/8062f14cb7aa502b8bda358103facedc80b87eec41e3391182655ff40615/shapely-2.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:03bd7b5fa5deb44795cc0a503999d10ae9d8a22df54ae8d4a4cd2e8a93466195", size = 1449608, upload-time = "2024-07-13T10:52:19.011Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/e7/719f384857c39aa51aa19d09d7cac84aeab1b25a7d0dab62433bf7b419e9/shapely-2.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ff9521991ed9e201c2e923da014e766c1aa04771bc93e6fe97c27dcf0d40ace", size = 1284057, upload-time = "2024-07-13T10:52:21.008Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/77/c05e794a65263deb020d7e25623234975dd96881f9e8cde341810ca683e7/shapely-2.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b65365cfbf657604e50d15161ffcc68de5cdb22a601bbf7823540ab4918a98d", size = 2440805, upload-time = "2024-07-13T19:44:15.317Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/32/b7687654b6e747ceae8f9fa4cc7489a8ebf275c64caf811f949d87e89f5d/shapely-2.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21f64e647a025b61b19585d2247137b3a38a35314ea68c66aaf507a1c03ef6fe", size = 2524570, upload-time = "2024-07-13T10:52:23.25Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/9c/5b68b3cd484065c7d33d83168d2ecfebfeeaa6d88bc9cfd830de2df490ac/shapely-2.0.5-cp312-cp312-win32.whl", hash = "sha256:3ac7dc1350700c139c956b03d9c3df49a5b34aaf91d024d1510a09717ea39199", size = 1295383, upload-time = "2024-07-13T10:52:25.72Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/c3/e98e3eb9f06def32b8e2454ab718cafb99149f023dff023e257125132d6e/shapely-2.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:30e8737983c9d954cd17feb49eb169f02f1da49e24e5171122cf2c2b62d65c95", size = 1442365, upload-time = "2024-07-13T10:52:27.433Z" }, ] [[package]] name = "shellingham" version = "1.5.4" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] name = "six" version = "1.16.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041, upload-time = "2021-05-05T14:18:18.379Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053, upload-time = "2021-05-05T14:18:17.237Z" }, ] [[package]] name = "smart-open" version = "7.3.0.post1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/18/2b/5e7234c68ed5bc872ad6ae77b8a421c2ed70dcb1190b44dc1abdeed5e347/smart_open-7.3.0.post1.tar.gz", hash = "sha256:ce6a3d9bc1afbf6234ad13c010b77f8cd36d24636811e3c52c3b5160f5214d1e" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/2b/5e7234c68ed5bc872ad6ae77b8a421c2ed70dcb1190b44dc1abdeed5e347/smart_open-7.3.0.post1.tar.gz", hash = "sha256:ce6a3d9bc1afbf6234ad13c010b77f8cd36d24636811e3c52c3b5160f5214d1e", size = 51557, upload-time = "2025-07-03T10:06:31.271Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/08/5b/a2a3d4514c64818925f4e886d39981f1926eeb5288a4549c6b3c17ed66bb/smart_open-7.3.0.post1-py3-none-any.whl", hash = "sha256:c73661a2c24bf045c1e04e08fffc585b59af023fe783d57896f590489db66fb4" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/5b/a2a3d4514c64818925f4e886d39981f1926eeb5288a4549c6b3c17ed66bb/smart_open-7.3.0.post1-py3-none-any.whl", hash = "sha256:c73661a2c24bf045c1e04e08fffc585b59af023fe783d57896f590489db66fb4", size = 61946, upload-time = "2025-07-03T10:06:29.599Z" }, ] [[package]] name = "sniffio" version = "1.3.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] name = "snowballstemmer" version = "2.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699, upload-time = "2021-11-16T18:38:38.009Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002, upload-time = "2021-11-16T18:38:34.792Z" }, ] [[package]] name = "socksio" version = "1.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f8/5c/48a7d9495be3d1c651198fd99dbb6ce190e2274d0f28b9051307bdec6b85/socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/5c/48a7d9495be3d1c651198fd99dbb6ce190e2274d0f28b9051307bdec6b85/socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac", size = 19055, upload-time = "2020-04-17T15:50:34.664Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/37/c3/6eeb6034408dac0fa653d126c9204ade96b819c936e136c5e8a6897eee9c/socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/c3/6eeb6034408dac0fa653d126c9204ade96b819c936e136c5e8a6897eee9c/socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3", size = 12763, upload-time = "2020-04-17T15:50:31.878Z" }, ] [[package]] name = "sortedcontainers" version = "2.4.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, ] [[package]] name = "soupsieve" version = "2.7" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, ] [[package]] name = "sphinx" version = "8.1.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'darwin'", "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", @@ -6471,15 +6471,15 @@ dependencies = [ { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, ] [[package]] name = "sphinx" version = "8.2.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } resolution-markers = [ "python_full_version >= '3.12' and sys_platform == 'darwin'", "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", @@ -6507,131 +6507,174 @@ dependencies = [ { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, ] [[package]] name = "sphinx-rtd-theme" version = "3.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "docutils" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-jquery" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463, upload-time = "2024-11-13T11:06:04.545Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13", size = 7655561, upload-time = "2024-11-13T11:06:02.094Z" }, ] [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, ] [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, ] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, ] [[package]] name = "sphinxcontrib-jquery" version = "4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae" }, ] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, ] [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, ] [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, ] [[package]] name = "sqlglot" -version = "11.7.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f9/df/3f9d14480946f6e3808d29f986ce8b6f6cca2a0c974dbed583366010f764/sqlglot-11.7.1.tar.gz", hash = "sha256:72624837266f3760b17ea9d20abf86835735f17d7eb7351c919b2188dc7905aa" } +version = "27.25.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/d9/edb44720d2146f2db727fbb95862821f706390b4237a28c5a453684bc3d3/sqlglot-27.25.2.tar.gz", hash = "sha256:987a91e2057ab8d52392586722b86d77b6e8911c503e72f02eb456a7356d7891", size = 5489830, upload-time = "2025-10-09T13:49:14.015Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6c/60/ddcb8f70eaad61b5e574a8adba10cc8d8c0b8e5ad87f4836c3c52767a2ad/sqlglot-11.7.1-py3-none-any.whl", hash = "sha256:1ed7f5965eb4c917821f8a324af6586432d0019628c2e067958d1470637e1398" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/23/f5916711e661bc454093796f04b3e4d93eb11d466ce0ebae12a9ebbe2202/sqlglot-27.25.2-py3-none-any.whl", hash = "sha256:38ff8a4c02ad74dc20fd487ce9113499e4f9790eaf338ae61862c963e5a1b87d", size = 523058, upload-time = "2025-10-09T13:49:11.33Z" }, +] + +[package.optional-dependencies] +rs = [ + { name = "sqlglotrs" }, +] + +[[package]] +name = "sqlglotrs" +version = "0.7.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/6c/d354837398376f19468ee59b86812d13e9faa64848af295a2af8ea0c635a/sqlglotrs-0.7.2.tar.gz", hash = "sha256:7c7b5f422a54307de0311b574dc631c099b0fdfc4b5d624b92b11b8df0b5fa16", size = 15879, upload-time = "2025-10-08T13:41:44.992Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/15/1aacfa3db8e30417e24af7fb1643faa6e91ba06a151947167e3c920ce898/sqlglotrs-0.7.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d5099a417f2b3765283cd19e461554dbad4e7532181ed45be1f1a4a936947d9b", size = 314650, upload-time = "2025-10-08T13:41:37.854Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/8a/43d0e84258e8c6fe1abfd290d75aa80bf354e0ea9d380dda24f026e2fdfb/sqlglotrs-0.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:98d4b5c2a3b42a4d263d70bc645ad8b54ff47f96e26d12f9e9d01e68b192f7ee", size = 300287, upload-time = "2025-10-08T13:41:31.083Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/27/7569cef7d950acfec2f513e5c0d8277bfae6f416250d3169735059554264/sqlglotrs-0.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:314272be1f683fc906b9b0ab51a0481135b1f7bfd17490352101c55cf48c65e4", size = 332757, upload-time = "2025-10-08T13:40:43.592Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/ec/64600c99985859849dcb900d04658606e6492b1184a7ed7818b237febd21/sqlglotrs-0.7.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b75e593f632ca268543630e8162625234063a71f257fa91cf43d57084d91e35", size = 342769, upload-time = "2025-10-08T13:40:52.147Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/f2/a89b6ab69668ca4a8c2aeac9504c3c200fd392ec1345dd687b4b8210fad5/sqlglotrs-0.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd1bc10aa211ae36032ad40b9edee9798bcc2fc9ba923ad96aa124bf551dd778", size = 487336, upload-time = "2025-10-08T13:41:08.868Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/d3/0e36fa7a0c8586e503fb6d00f69979f0f0e12e8bf4f2b69f676b4d78220e/sqlglotrs-0.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00185011defe3e0b34ef95b5167f429be78e14900c64fe157875c1a36db3375c", size = 365907, upload-time = "2025-10-08T13:41:16.944Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/67/470b946d56a559eb32f89905665f98331e9a88f44cbe7933639edf5f91c5/sqlglotrs-0.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40ee2afa455a58f0122e850579b137eb75194f1cbf53fe14058c9afabe035326", size = 343593, upload-time = "2025-10-08T13:41:24.582Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/51/aeec0876ce7f0162f8a9483e8081e0611c2fb526209b2328dc943430d667/sqlglotrs-0.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e56828fe2424e0e6cb7c712daacb1490f0c79e002473896823e6e5179273e3a", size = 363451, upload-time = "2025-10-08T13:41:00.938Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/4b/750cb6c510b8791f02d9bdb088d6c4bad59ed7ba8c1a1d31923eed0a8966/sqlglotrs-0.7.2-cp310-cp310-win32.whl", hash = "sha256:12611a0fcd39d1df6f49ab4cd0dbd489e5ceb5cc130d72d957edb5e697fa2d8d", size = 184067, upload-time = "2025-10-08T13:41:45.858Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/dc/3b369f8cd141075b8368b07fca21ceb932445766c6bdd77a123c4588d9f3/sqlglotrs-0.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:f0b811637d233c94039e8f9b8794733fa2cf33df73e879fdf4bef6849ba3ab2f", size = 195991, upload-time = "2025-10-08T13:41:53.089Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/7b/c2c3b529cee5a17668b1d2882586e555e4aa32b4a1e943cc4f35c6e2bb3c/sqlglotrs-0.7.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3d14c7e2d17fd9ca0b89f9336f02de75b4db925ad4115484540223708b48363d", size = 314528, upload-time = "2025-10-08T13:41:39.244Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/c9/d9ad655f38ccaae368eb4b8383ab67e8fa2a36bf8e70c6a89b660f827878/sqlglotrs-0.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cf0f4263159f14d9c4af009cff4ad65eaf9c337307a07c60b9aeeeb8d2996aff", size = 300230, upload-time = "2025-10-08T13:41:32.404Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/8b/fe513853b230e2160d47342ab82422882443f0c7b9a1870d78060081606b/sqlglotrs-0.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:852964242dd88b69f39f2294aff9cf64e9f8d40d674d8c48d97c7ec14114048a", size = 332764, upload-time = "2025-10-08T13:40:45.802Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/a7/486e56f294f11042260c58634f9b9f7d8f81b25618eccd49fae43b97054b/sqlglotrs-0.7.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cf10c01062188985d7edb7833224326414466c4a4e7f26305c4c5ceed3fcde8", size = 342698, upload-time = "2025-10-08T13:40:53.912Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/ec/5a8bc583d0fb6f9889de0e7577350d4d65d83a7f45798cd03d822616cf1b/sqlglotrs-0.7.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d31db3c81a8655e019f000f041393fea304c60748d17fba1a4c4804a20082e89", size = 487464, upload-time = "2025-10-08T13:41:10.45Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/f2/1b6c95b27d4cf976232de97f101fedb8f06355a3a7fd68cc6af040fef6c6/sqlglotrs-0.7.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:109961c34895ad6a83632e99fe1f78edf4172075a4aac7547a95950b1dca3362", size = 366035, upload-time = "2025-10-08T13:41:18.181Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/69/44f34b7a1ad02f9f718736860f146922ed658f2e33d636e1dcc2b7da7a59/sqlglotrs-0.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92eb6383e44876b8f68510c52c71b6ffe410f18937cb48583be0e19d4e97036e", size = 343529, upload-time = "2025-10-08T13:41:25.971Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/c5/e171d0d30f33a6622156c8d3c3ffa46de66b8b443c487646aa2d18976774/sqlglotrs-0.7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:082e5f5363e7601c78eee16f717adedb5ebb90941cc8e919dfc121b60f6810cb", size = 363781, upload-time = "2025-10-08T13:41:02.274Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/e1/117a6dd8af4f1d80fc2ca5bdc4edd12e67875b292b4428886fa24592564b/sqlglotrs-0.7.2-cp311-cp311-win32.whl", hash = "sha256:2655d8424066fc9c4a90ea33edf51aaf88a71f9b762c3d6f079c90b56e574420", size = 183670, upload-time = "2025-10-08T13:41:47.484Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/0c/033771f2b877a8c3d95eeeb242dd538c46be5fe233575d300eb994d54c1f/sqlglotrs-0.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8db2f8b10bbfca510bd6d3f5b07702945ea9e3081c6ea8ae9358ce5d13f3786", size = 196113, upload-time = "2025-10-08T13:41:54.314Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/c7/2a38e9df27c2d7b1636685f32b68b4c4a8f4c3da699a7aad0bede6d9a195/sqlglotrs-0.7.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e04fe42ca43d11d4b832d44673abb3f2269016d9e5af31474cc46862a00e79fd", size = 311037, upload-time = "2025-10-08T13:41:40.68Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/9a/123c12d7b164555bf91b1d6e7c8e95d69a92db742ddd138b179004012424/sqlglotrs-0.7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7dcc177db4190aff89017e97da710028ec08083498446acd76042f92bdf17e92", size = 296873, upload-time = "2025-10-08T13:41:33.682Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/a3/4220fee40542739ce2055f5c4f3b611472f93dc52552bd5acf637c9484d1/sqlglotrs-0.7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e5c741d86c08d88fc7fd1eb41055a264649fbf8fa252f3b09b62ffe826d88bf", size = 332201, upload-time = "2025-10-08T13:40:47.757Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/70/a5c3f2bd82c89bebc798f503b77761b64ea48424503467fd666c54382a36/sqlglotrs-0.7.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54a008319bb47531fe87f79f40b0ad3275f619cda3215d33526276285f2ef1db", size = 342400, upload-time = "2025-10-08T13:40:56.086Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/30/b58c09fbb7d316044333e0a999c686a15be36e8f0d28879166f7a555edb9/sqlglotrs-0.7.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a3334b5d0639f5b55bb0cbac410719966889157747f816a71a962ca3c2c5e31", size = 485041, upload-time = "2025-10-08T13:41:12.315Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/7f/a8c59adad7bdbc069f49dac9d0485f591d05c6060c69c995f30ecdf584e1/sqlglotrs-0.7.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7470963a61f82f894e6821fa59054e03390786e19f1afc25a334a336ac2cec7", size = 366928, upload-time = "2025-10-08T13:41:19.585Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/d8/a133cf8a257d7e254f3668c6bfe63910e9320a4e3b1db18d60bc0832587d/sqlglotrs-0.7.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef6cacc278c5ae98e6dab19384da381636af19b76af524f55ad5a0b7a790c49b", size = 342893, upload-time = "2025-10-08T13:41:27.268Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/33/e097fa8874a271b28b0798ddc16db7e839bf7eb2fe23a154a31e74583071/sqlglotrs-0.7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d891a2c70091e4f2a2e8162d9fe4b5df24ef34bb5b4bb2ea55f61089b2f466df", size = 362581, upload-time = "2025-10-08T13:41:03.579Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/27/5654c974c1fc2eda40b114f0381b06b9f60c67ce02b126f3a006c527756d/sqlglotrs-0.7.2-cp312-cp312-win32.whl", hash = "sha256:ea58effa219f88c60f30cab2aaa1975c4301ddecdc5ee2eb2c8fa28caaee0566", size = 183206, upload-time = "2025-10-08T13:41:49.065Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/3c/f7cd40d70ffa0cb39785196e0ca863d34aa39282c3a9ffcd0416f144f670/sqlglotrs-0.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:ee8956eafb7232d73b0736fc65e0994385bae701c98660ff83af73ab42097cf5", size = 195805, upload-time = "2025-10-08T13:41:55.559Z" }, ] [[package]] name = "sse-starlette" version = "3.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a", size = 20985, upload-time = "2025-07-27T09:07:44.565Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", size = 11297, upload-time = "2025-07-27T09:07:43.268Z" }, ] [[package]] name = "starlette" version = "0.47.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" }, ] [[package]] name = "statsmodels" version = "0.14.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, { name = "packaging" }, @@ -6639,260 +6682,260 @@ dependencies = [ { name = "patsy" }, { name = "scipy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/64/cc/8c1bf59bf8203dea1bf2ea811cfe667d7bcc6909c83d8afb02b08e30f50b/statsmodels-0.14.5.tar.gz", hash = "sha256:de260e58cccfd2ceddf835b55a357233d6ca853a1aa4f90f7553a52cc71c6ddf" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/3a/2c/55b2a5d10c1a211ecab3f792021d2581bbe1c5ca0a1059f6715dddc6899d/statsmodels-0.14.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9fc2b5cdc0c95cba894849651fec1fa1511d365e3eb72b0cc75caac44077cd48" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/d9/6967475805de06691e951072d05e40e3f1c71b6221bb92401193ee19bd2a/statsmodels-0.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b8d96b0bbaeabd3a557c35cc7249baa9cfbc6dd305c32a9f2cbdd7f46c037e7f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/a8/803c280419a7312e2472969fe72cf461c1210a27770a662cbe3b5cd7c6fe/statsmodels-0.14.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:145bc39b2cb201efb6c83cc3f2163c269e63b0d4809801853dec6f440bd3bc37" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/25/edf20acbd670934b02cd9344e29c9a03ce040122324b3491bb075ae76b2d/statsmodels-0.14.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7c14fb2617bb819fb2532e1424e1da2b98a3419a80e95f33365a72d437d474e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/64/22/8b1e38310272e766abd6093607000a81827420a3348f09eff08a9e54cbaf/statsmodels-0.14.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1e9742d8a5ac38a3bfc4b7f4b0681903920f20cbbf466d72b1fd642033846108" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/6f/6de51f1077b7cef34611f1d6721392ea170153251b4d977efcf6d100f779/statsmodels-0.14.5-cp310-cp310-win_amd64.whl", hash = "sha256:1cab9e6fce97caf4239cdb2df375806937da5d0b7ba2699b13af33a07f438464" }, - { url = "https://mirrors.aliyun.com/pypi/packages/14/30/fd49902b30416b828de763e161c0d6e2cc04d119ae4fbdd3f3b43dc8f1be/statsmodels-0.14.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b7091a8442076c708c926de3603653a160955e80a2b6d931475b7bb8ddc02e5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ca/c1/2654541ff6f5790d01d1e5ba36405fde873f4a854f473e90b4fe56b37333/statsmodels-0.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:128872be8f3208f4446d91ea9e4261823902fc7997fee7e1a983eb62fd3b7c6e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/da/6ebb64d0db4e86c0d2d9cde89e03247702da0ab191789f7813d4f9a348da/statsmodels-0.14.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ad5aee04ae7196c429df2174df232c057e478c5fa63193d01c8ec9aae04d31" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/49/ac803ca093ec3845184a752a91cd84511245e1f97103b15cfe32794a3bb0/statsmodels-0.14.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f402fc793458dd6d96e099acb44cd1de1428565bf7ef3030878a8daff091f08a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/c8/ae82feb00582f4814fac5d2cb3ec32f93866b413cf5878b2fe93688ec63c/statsmodels-0.14.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:26c028832730aebfbfd4e7501694e1f9ad31ec8536e776716673f4e7afd4059a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/ac/4276459ea71aa46e2967ea283fc88ee5631c11f29a06787e16cf4aece1b8/statsmodels-0.14.5-cp311-cp311-win_amd64.whl", hash = "sha256:ec56f771d9529cdc17ed2fb2a950d100b6e83a7c5372aae8ac5bb065c474b856" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/a5/fcc4f5f16355660ce7a1742e28a43e3a9391b492fc4ff29fdd6893e81c05/statsmodels-0.14.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:37e7364a39f9aa3b51d15a208c2868b90aadb8412f868530f5cba9197cb00eaa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/6f/db0cf5efa48277ac6218d9b981c8fd5e63c4c43e0d9d65015fdc38eed0ef/statsmodels-0.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4263d7f4d0f1d5ac6eb4db22e1ee34264a14d634b9332c975c9d9109b6b46e12" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/93/4ddc3bc4a59c51e6a57c49df1b889882c40d9e141e855b3517f6a8de3232/statsmodels-0.14.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86224f6e36f38486e471e75759d241fe2912d8bc25ab157d54ee074c6aedbf45" }, - { url = "https://mirrors.aliyun.com/pypi/packages/66/de/dc6bf2f6e8c8eb4c5815560ebdbdf2d69a767bc0f65fde34bc086cf5b36d/statsmodels-0.14.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3dd760a6fa80cd5e0371685c697bb9c2c0e6e1f394d975e596a1e6d0bbb9372" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/4f/2d5a8d14bebdf2b03b3ea89b8c6a2c837bb406ba5b7a41add8bd303bce29/statsmodels-0.14.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6264fb00e02f858b86bd01ef2dc05055a71d4a0cc7551b9976b07b0f0e6cf24f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/df/4c/2feda3a9f0e17444a84ba5398ada6a4d2e1b8f832760048f04e2b8ea0c41/statsmodels-0.14.5-cp312-cp312-win_amd64.whl", hash = "sha256:b2ed065bfbaf8bb214c7201656df840457c2c8c65e1689e3eb09dc7440f9c61c" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/cc/8c1bf59bf8203dea1bf2ea811cfe667d7bcc6909c83d8afb02b08e30f50b/statsmodels-0.14.5.tar.gz", hash = "sha256:de260e58cccfd2ceddf835b55a357233d6ca853a1aa4f90f7553a52cc71c6ddf", size = 20525016, upload-time = "2025-07-07T12:14:23.195Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/2c/55b2a5d10c1a211ecab3f792021d2581bbe1c5ca0a1059f6715dddc6899d/statsmodels-0.14.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9fc2b5cdc0c95cba894849651fec1fa1511d365e3eb72b0cc75caac44077cd48", size = 10058241, upload-time = "2025-07-07T12:13:16.286Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/d9/6967475805de06691e951072d05e40e3f1c71b6221bb92401193ee19bd2a/statsmodels-0.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b8d96b0bbaeabd3a557c35cc7249baa9cfbc6dd305c32a9f2cbdd7f46c037e7f", size = 9734017, upload-time = "2025-07-07T12:05:08.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/a8/803c280419a7312e2472969fe72cf461c1210a27770a662cbe3b5cd7c6fe/statsmodels-0.14.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:145bc39b2cb201efb6c83cc3f2163c269e63b0d4809801853dec6f440bd3bc37", size = 10459677, upload-time = "2025-07-07T14:21:51.809Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/25/edf20acbd670934b02cd9344e29c9a03ce040122324b3491bb075ae76b2d/statsmodels-0.14.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7c14fb2617bb819fb2532e1424e1da2b98a3419a80e95f33365a72d437d474e", size = 10678631, upload-time = "2025-07-07T14:22:05.496Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/22/8b1e38310272e766abd6093607000a81827420a3348f09eff08a9e54cbaf/statsmodels-0.14.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1e9742d8a5ac38a3bfc4b7f4b0681903920f20cbbf466d72b1fd642033846108", size = 10699273, upload-time = "2025-07-07T14:22:19.487Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/6f/6de51f1077b7cef34611f1d6721392ea170153251b4d977efcf6d100f779/statsmodels-0.14.5-cp310-cp310-win_amd64.whl", hash = "sha256:1cab9e6fce97caf4239cdb2df375806937da5d0b7ba2699b13af33a07f438464", size = 9644785, upload-time = "2025-07-07T12:05:20.927Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/30/fd49902b30416b828de763e161c0d6e2cc04d119ae4fbdd3f3b43dc8f1be/statsmodels-0.14.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b7091a8442076c708c926de3603653a160955e80a2b6d931475b7bb8ddc02e5", size = 10053330, upload-time = "2025-07-07T12:07:39.689Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/c1/2654541ff6f5790d01d1e5ba36405fde873f4a854f473e90b4fe56b37333/statsmodels-0.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:128872be8f3208f4446d91ea9e4261823902fc7997fee7e1a983eb62fd3b7c6e", size = 9735555, upload-time = "2025-07-07T12:13:28.935Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/da/6ebb64d0db4e86c0d2d9cde89e03247702da0ab191789f7813d4f9a348da/statsmodels-0.14.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ad5aee04ae7196c429df2174df232c057e478c5fa63193d01c8ec9aae04d31", size = 10307522, upload-time = "2025-07-07T14:22:32.853Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/49/ac803ca093ec3845184a752a91cd84511245e1f97103b15cfe32794a3bb0/statsmodels-0.14.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f402fc793458dd6d96e099acb44cd1de1428565bf7ef3030878a8daff091f08a", size = 10474665, upload-time = "2025-07-07T14:22:46.011Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/c8/ae82feb00582f4814fac5d2cb3ec32f93866b413cf5878b2fe93688ec63c/statsmodels-0.14.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:26c028832730aebfbfd4e7501694e1f9ad31ec8536e776716673f4e7afd4059a", size = 10713120, upload-time = "2025-07-07T14:23:00.067Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/ac/4276459ea71aa46e2967ea283fc88ee5631c11f29a06787e16cf4aece1b8/statsmodels-0.14.5-cp311-cp311-win_amd64.whl", hash = "sha256:ec56f771d9529cdc17ed2fb2a950d100b6e83a7c5372aae8ac5bb065c474b856", size = 9640980, upload-time = "2025-07-07T12:05:33.085Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/a5/fcc4f5f16355660ce7a1742e28a43e3a9391b492fc4ff29fdd6893e81c05/statsmodels-0.14.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:37e7364a39f9aa3b51d15a208c2868b90aadb8412f868530f5cba9197cb00eaa", size = 10042891, upload-time = "2025-07-07T12:13:41.671Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/6f/db0cf5efa48277ac6218d9b981c8fd5e63c4c43e0d9d65015fdc38eed0ef/statsmodels-0.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4263d7f4d0f1d5ac6eb4db22e1ee34264a14d634b9332c975c9d9109b6b46e12", size = 9698912, upload-time = "2025-07-07T12:07:54.674Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/93/4ddc3bc4a59c51e6a57c49df1b889882c40d9e141e855b3517f6a8de3232/statsmodels-0.14.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86224f6e36f38486e471e75759d241fe2912d8bc25ab157d54ee074c6aedbf45", size = 10237801, upload-time = "2025-07-07T14:23:12.593Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/de/dc6bf2f6e8c8eb4c5815560ebdbdf2d69a767bc0f65fde34bc086cf5b36d/statsmodels-0.14.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3dd760a6fa80cd5e0371685c697bb9c2c0e6e1f394d975e596a1e6d0bbb9372", size = 10424154, upload-time = "2025-07-07T14:23:25.365Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/4f/2d5a8d14bebdf2b03b3ea89b8c6a2c837bb406ba5b7a41add8bd303bce29/statsmodels-0.14.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6264fb00e02f858b86bd01ef2dc05055a71d4a0cc7551b9976b07b0f0e6cf24f", size = 10652915, upload-time = "2025-07-07T14:23:39.337Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/4c/2feda3a9f0e17444a84ba5398ada6a4d2e1b8f832760048f04e2b8ea0c41/statsmodels-0.14.5-cp312-cp312-win_amd64.whl", hash = "sha256:b2ed065bfbaf8bb214c7201656df840457c2c8c65e1689e3eb09dc7440f9c61c", size = 9611236, upload-time = "2025-07-07T12:08:06.794Z" }, ] [[package]] name = "strenum" version = "0.4.15" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/85/ad/430fb60d90e1d112a62ff57bdd1f286ec73a2a0331272febfddd21f330e1/StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/ad/430fb60d90e1d112a62ff57bdd1f286ec73a2a0331272febfddd21f330e1/StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/81/69/297302c5f5f59c862faa31e6cb9a4cd74721cd1e052b38e464c5b402df8b/StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/69/297302c5f5f59c862faa31e6cb9a4cd74721cd1e052b38e464c5b402df8b/StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659" }, ] [[package]] name = "sympy" version = "1.14.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] [[package]] name = "tabulate" version = "0.9.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, ] [[package]] name = "tavily-python" version = "0.5.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "httpx" }, { name = "requests" }, { name = "tiktoken" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/db/ff/ba1a3769c34d022aeba544ff7b18cbcd0d23a6358fc3566b2101c6bf2817/tavily_python-0.5.1.tar.gz", hash = "sha256:44b0eefe79a057cd11d3cd03780b63b4913400122350e38285acfb502c2fffc1" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/ff/ba1a3769c34d022aeba544ff7b18cbcd0d23a6358fc3566b2101c6bf2817/tavily_python-0.5.1.tar.gz", hash = "sha256:44b0eefe79a057cd11d3cd03780b63b4913400122350e38285acfb502c2fffc1", size = 107503, upload-time = "2025-02-07T00:22:06.99Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a5/cd/71088461d7720128c78802289b3b36298f42745e5f8c334b0ffc157b881e/tavily_python-0.5.1-py3-none-any.whl", hash = "sha256:169601f703c55cf338758dcacfa7102473b479a9271d65a3af6fc3668990f757" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/cd/71088461d7720128c78802289b3b36298f42745e5f8c334b0ffc157b881e/tavily_python-0.5.1-py3-none-any.whl", hash = "sha256:169601f703c55cf338758dcacfa7102473b479a9271d65a3af6fc3668990f757", size = 43767, upload-time = "2025-02-07T00:22:04.99Z" }, ] [[package]] name = "tenacity" version = "8.5.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a3/4d/6a19536c50b849338fcbe9290d562b52cbdcf30d8963d3588a68a4107df1/tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/4d/6a19536c50b849338fcbe9290d562b52cbdcf30d8963d3588a68a4107df1/tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78", size = 47309, upload-time = "2024-07-05T07:25:31.836Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165, upload-time = "2024-07-05T07:25:29.591Z" }, ] [[package]] name = "tencentcloud-sdk-python" version = "3.0.1215" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fd/4c/7a320c65d605e817bedd1205c77a612be7d4dde621182cc7c00e334207ce/tencentcloud-sdk-python-3.0.1215.tar.gz", hash = "sha256:24441e69d418301d50be0279cb148a747fc272b836e41d18e213750093f490c6" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/4c/7a320c65d605e817bedd1205c77a612be7d4dde621182cc7c00e334207ce/tencentcloud-sdk-python-3.0.1215.tar.gz", hash = "sha256:24441e69d418301d50be0279cb148a747fc272b836e41d18e213750093f490c6", size = 9566281, upload-time = "2024-08-19T20:24:26.541Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/55/08/98090d1a139e8995053ed22e099b43aa4dea8cffe056f8f0bc5178aeecbd/tencentcloud_sdk_python-3.0.1215-py2.py3-none-any.whl", hash = "sha256:899ced749baf74846f1eabf452f74aa0e48d1965f0ca7828a8b73b446f76f5f2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/08/98090d1a139e8995053ed22e099b43aa4dea8cffe056f8f0bc5178aeecbd/tencentcloud_sdk_python-3.0.1215-py2.py3-none-any.whl", hash = "sha256:899ced749baf74846f1eabf452f74aa0e48d1965f0ca7828a8b73b446f76f5f2", size = 10265517, upload-time = "2024-08-19T20:24:19.52Z" }, ] [[package]] name = "tf-playwright-stealth" version = "1.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "fake-http-header" }, { name = "playwright" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d6/6b/32bb58c65991f91aeaaf7473b650175d9d4af5dd383983d177d49ccba08d/tf_playwright_stealth-1.2.0.tar.gz", hash = "sha256:7bb8d32d3e60324fbf6b9eeae540b8cd9f3b9e07baeb33b025dbc98ad47658ba" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/6b/32bb58c65991f91aeaaf7473b650175d9d4af5dd383983d177d49ccba08d/tf_playwright_stealth-1.2.0.tar.gz", hash = "sha256:7bb8d32d3e60324fbf6b9eeae540b8cd9f3b9e07baeb33b025dbc98ad47658ba", size = 23362, upload-time = "2025-06-13T04:51:04.97Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/11/3d/2653f4cf49660bb44eeac8270617cc4c0287d61716f249f55053f0af0724/tf_playwright_stealth-1.2.0-py3-none-any.whl", hash = "sha256:26ee47ee89fa0f43c606fe37c188ea3ccd36f96ea90c01d167b768df457e7886" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/3d/2653f4cf49660bb44eeac8270617cc4c0287d61716f249f55053f0af0724/tf_playwright_stealth-1.2.0-py3-none-any.whl", hash = "sha256:26ee47ee89fa0f43c606fe37c188ea3ccd36f96ea90c01d167b768df457e7886", size = 33151, upload-time = "2025-06-13T04:51:03.769Z" }, ] [[package]] name = "threadpoolctl" version = "3.6.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] [[package]] name = "thrift" version = "0.20.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3c/2d/8946864f716ac82dcc88d290ed613cba7a80ec75df4f553ec3ff275f486e/thrift-0.20.0.tar.gz", hash = "sha256:4dd662eadf6b8aebe8a41729527bd69adf6ceaa2a8681cbef64d1273b3e8feba" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/2d/8946864f716ac82dcc88d290ed613cba7a80ec75df4f553ec3ff275f486e/thrift-0.20.0.tar.gz", hash = "sha256:4dd662eadf6b8aebe8a41729527bd69adf6ceaa2a8681cbef64d1273b3e8feba", size = 62295, upload-time = "2024-03-22T22:53:08.228Z" } [[package]] name = "tika" version = "2.6.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "requests" }, { name = "setuptools" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fd/b8/055ed37d6413fef4e4af99cd7e0edc4ddfb8fc167b730b25005d212e2049/tika-2.6.0.tar.gz", hash = "sha256:56670eb812944eb25ed73f1b3b075aa41e7a135b74b240822f28b819e5b373da" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/b8/055ed37d6413fef4e4af99cd7e0edc4ddfb8fc167b730b25005d212e2049/tika-2.6.0.tar.gz", hash = "sha256:56670eb812944eb25ed73f1b3b075aa41e7a135b74b240822f28b819e5b373da", size = 27452, upload-time = "2023-01-01T22:56:31.397Z" } [[package]] name = "tiktoken" version = "0.7.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c4/4a/abaec53e93e3ef37224a4dd9e2fc6bb871e7a538c2b6b9d2a6397271daf4/tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/96/10/28d59d43d72a0ebd4211371d0bf10c935cdecbb62b812ae04c58bfc37d96/tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/0c/d4125348dedd1f8f38e3f85245e7fc38858ffc77c9b7edfb762a8191ba0b/tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b9/ab/f9c7675747f259d133d66065106cf732a7c2bef6043062fbca8e011f7f4d/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/8c/7d1007557b343d5cf18349802e94d3a14397121e9105b4661f8cd753f9bf/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/40/61d6354cb64a563fce475a2907039be9fe809ca5f801213856353b01a35b/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/6c/83ca40527d072739f0704b9f59b325786c444ca63672a77cb69adc8181f7/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/1f/a5d72755118e9e1b62cdf3ef9138eb83d49088f3cb37a9540025c81c0e75/tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/eb/57492b2568eea1d546da5cc1ae7559d924275280db80ba07e6f9b89a914b/tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/30/ef/e07dbfcb2f85c84abaa1b035a9279575a8da0236305491dc22ae099327f7/tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/9b/f36db825b1e9904c3a2646439cb9923fc1e09208e2e071c6d9dd64ead131/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/61/b4/b80d1fe33015e782074e96bbbf4108ccd283b8deea86fb43c15d18b7c351/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/40/c66ff3a21af6d62a7e0ff428d12002c4e0389f776d3ff96dcaa0bb354eee/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/80/f4c9e255ff236e6a69ce44b927629cefc1b63d3a00e2d1c9ed540c9492d2/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/10/c04b4ff592a5f46b28ebf4c2353f735c02ae7f0ce1b165d00748ced6467e/tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1d/46/4cdda4186ce900608f522da34acf442363346688c71b938a90a52d7b84cc/tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b6/30/09ced367d280072d7a3e21f34263dfbbf6378661e7a0f6414e7c18971083/tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e6/7b/c949e4954441a879a67626963dff69096e3c774758b9f2bb0853f7b4e1e7/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/81/1842a22f15586072280364c2ab1e40835adaf64e42fe80e52aff921ee021/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6d/87/51a133a3d5307cf7ae3754249b0faaa91d3414b85c3d36f80b54d6817aa6/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/1f/c93517dc6d3b2c9e988b8e24f87a8b2d4a4ab28920a3a3f3ea338397ae0c/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/4b/48ca098cb580c099b5058bf62c4cb5e90ca6130fa43ef4df27088536245b/tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/4a/abaec53e93e3ef37224a4dd9e2fc6bb871e7a538c2b6b9d2a6397271daf4/tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6", size = 33437, upload-time = "2024-05-13T18:03:28.793Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/96/10/28d59d43d72a0ebd4211371d0bf10c935cdecbb62b812ae04c58bfc37d96/tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f", size = 961465, upload-time = "2024-05-13T18:02:31.978Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/0c/d4125348dedd1f8f38e3f85245e7fc38858ffc77c9b7edfb762a8191ba0b/tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225", size = 906849, upload-time = "2024-05-13T18:02:33.535Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/ab/f9c7675747f259d133d66065106cf732a7c2bef6043062fbca8e011f7f4d/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590", size = 1048795, upload-time = "2024-05-13T18:02:35.411Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/8c/7d1007557b343d5cf18349802e94d3a14397121e9105b4661f8cd753f9bf/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c", size = 1080866, upload-time = "2024-05-13T18:02:37.583Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/40/61d6354cb64a563fce475a2907039be9fe809ca5f801213856353b01a35b/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311", size = 1092776, upload-time = "2024-05-13T18:02:39.51Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/6c/83ca40527d072739f0704b9f59b325786c444ca63672a77cb69adc8181f7/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5", size = 1142591, upload-time = "2024-05-13T18:02:40.793Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/1f/a5d72755118e9e1b62cdf3ef9138eb83d49088f3cb37a9540025c81c0e75/tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702", size = 798864, upload-time = "2024-05-13T18:02:42.567Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/eb/57492b2568eea1d546da5cc1ae7559d924275280db80ba07e6f9b89a914b/tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f", size = 961468, upload-time = "2024-05-13T18:02:43.788Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/ef/e07dbfcb2f85c84abaa1b035a9279575a8da0236305491dc22ae099327f7/tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f", size = 907005, upload-time = "2024-05-13T18:02:45.327Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/9b/f36db825b1e9904c3a2646439cb9923fc1e09208e2e071c6d9dd64ead131/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b", size = 1049183, upload-time = "2024-05-13T18:02:46.574Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/b4/b80d1fe33015e782074e96bbbf4108ccd283b8deea86fb43c15d18b7c351/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992", size = 1080830, upload-time = "2024-05-13T18:02:48.444Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/40/c66ff3a21af6d62a7e0ff428d12002c4e0389f776d3ff96dcaa0bb354eee/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1", size = 1092967, upload-time = "2024-05-13T18:02:50.006Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/80/f4c9e255ff236e6a69ce44b927629cefc1b63d3a00e2d1c9ed540c9492d2/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89", size = 1142682, upload-time = "2024-05-13T18:02:51.814Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/10/c04b4ff592a5f46b28ebf4c2353f735c02ae7f0ce1b165d00748ced6467e/tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb", size = 799009, upload-time = "2024-05-13T18:02:53.057Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/46/4cdda4186ce900608f522da34acf442363346688c71b938a90a52d7b84cc/tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908", size = 960446, upload-time = "2024-05-13T18:02:54.409Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/30/09ced367d280072d7a3e21f34263dfbbf6378661e7a0f6414e7c18971083/tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410", size = 906652, upload-time = "2024-05-13T18:02:56.25Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/7b/c949e4954441a879a67626963dff69096e3c774758b9f2bb0853f7b4e1e7/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704", size = 1047904, upload-time = "2024-05-13T18:02:57.707Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/81/1842a22f15586072280364c2ab1e40835adaf64e42fe80e52aff921ee021/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350", size = 1079836, upload-time = "2024-05-13T18:02:59.009Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/87/51a133a3d5307cf7ae3754249b0faaa91d3414b85c3d36f80b54d6817aa6/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4", size = 1092472, upload-time = "2024-05-13T18:03:00.597Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/1f/c93517dc6d3b2c9e988b8e24f87a8b2d4a4ab28920a3a3f3ea338397ae0c/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97", size = 1141881, upload-time = "2024-05-13T18:03:02.743Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/4b/48ca098cb580c099b5058bf62c4cb5e90ca6130fa43ef4df27088536245b/tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f", size = 799281, upload-time = "2024-05-13T18:03:04.036Z" }, ] [[package]] name = "tokenizers" version = "0.15.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c0/44/625db94e91c6196b6574359fa70bfe28e8eabf57a1b894f8f0ec69727fd1/tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a8/6e/489419d98730b3d02381f10a8b97c5bf55b45742d1b347cdd0ffe267b827/tokenizers-0.15.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:52f6130c9cbf70544287575a985bf44ae1bda2da7e8c24e97716080593638012" }, - { url = "https://mirrors.aliyun.com/pypi/packages/01/04/45d88b8bddc09bf56ae1631721393255b75798af515c65c26389713a2072/tokenizers-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:054c1cc9c6d68f7ffa4e810b3d5131e0ba511b6e4be34157aa08ee54c2f8d9ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cc/bf/819bf4445ed68ffaf73b0f6245bcbd21a5cd58e86dabbef315a6d0b707b3/tokenizers-0.15.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9b9b070fdad06e347563b88c278995735292ded1132f8657084989a4c84a6d5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/b3/70d3fe0ad25e065322cd902624cad4ff2647484fe823360f58af6927b48c/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea621a7eef4b70e1f7a4e84dd989ae3f0eeb50fc8690254eacc08acb623e82f1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/1b/58e77f2b57651e8c1b4f1b7144a1250509f2e7a1f55073d12620968ae4bb/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7fd9a5141634fa3aa8d6b7be362e6ae1b4cda60da81388fa533e0b552c98fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dc/d5/45dd421f45b3c1a446ffd9486cef29ed568b5978f66a1803fa46a44aa9be/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44f2a832cd0825295f7179eaf173381dc45230f9227ec4b44378322d900447c9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fa/6b/6b757cf6f7c30009a6759d3f7b833d974b3cd50d24d5824c695e077cb1bf/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b9ec69247a23747669ec4b0ca10f8e3dfb3545d550258129bd62291aabe8605" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/5d/cf5e122ce4f1a29f165b2a69dc33d1ff30bce303343d58a54775ddba5d51/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b6a4c78da863ff26dbd5ad9a8ecc33d8a8d97b535172601cf00aee9d7ce9ce" }, - { url = "https://mirrors.aliyun.com/pypi/packages/aa/0b/dd9e5124fe73a01f36f5c7554ac97b9612af5e0bd401d6a606a3f52a060a/tokenizers-0.15.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5ab2a4d21dcf76af60e05af8063138849eb1d6553a0d059f6534357bce8ba364" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/0c/3435e3d54f825d4fa363a7ab2680b243314377eb2ed28e87ade70b861e7b/tokenizers-0.15.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a47acfac7e511f6bbfcf2d3fb8c26979c780a91e06fb5b9a43831b2c0153d024" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/bf/a804747020f1b221131b74b5f29c24b47a5d2cee4b1311ce394ca9ce242a/tokenizers-0.15.2-cp310-none-win32.whl", hash = "sha256:064ff87bb6acdbd693666de9a4b692add41308a2c0ec0770d6385737117215f2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/87/0bf37626c5f1ea2462e0398be88c287f3d40c696c255ba478bf525bdc852/tokenizers-0.15.2-cp310-none-win_amd64.whl", hash = "sha256:3b919afe4df7eb6ac7cafd2bd14fb507d3f408db7a68c43117f579c984a73843" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/11/933d68d395f5486d935e1c15da80bc96bf3f48595652069d19e0e9894386/tokenizers-0.15.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:89cd1cb93e4b12ff39bb2d626ad77e35209de9309a71e4d3d4672667b4b256e7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/4f/a4c12cc058a899c1caaa1e689c3df9a698e20e891d4005aa6ec2174a9339/tokenizers-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cfed5c64e5be23d7ee0f0e98081a25c2a46b0b77ce99a4f0605b1ec43dd481fa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e9/13/b86ea87b7e3b4a2ca154220dc4eb19a56a3864ec03e9630d15d1bac10da1/tokenizers-0.15.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a907d76dcfda37023ba203ab4ceeb21bc5683436ebefbd895a0841fd52f6f6f2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/23/e4985657ea42ad432d6dc2100b2687e70a6bae730f1f8c52f81d9e6ccf3a/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20ea60479de6fc7b8ae756b4b097572372d7e4032e2521c1bbf3d90c90a99ff0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/d5/e1ad46939d6de48d41bbd8b302f87ecde79847855210e75517a832b29490/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48e2b9335be2bc0171df9281385c2ed06a15f5cf121c44094338306ab7b33f2c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/d1/4d319a035f819af3290ec5a09482ad659d9d2a0aea33890fb5720ce81841/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:112a1dd436d2cc06e6ffdc0b06d55ac019a35a63afd26475205cb4b1bf0bfbff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/39/facfca8e598126a0001d4295e6b1ee670d241aa6f4fcdd97016065b43c5d/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4620cca5c2817177ee8706f860364cc3a8845bc1e291aaf661fb899e5d1c45b0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/15/0b/c09b2c0dc688c82adadaa0d5080983de3ce920f4a5cbadb7eaa5302ad251/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccd73a82751c523b3fc31ff8194702e4af4db21dc20e55b30ecc2079c5d43cb7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/3b/d8e60712e509a6f5d01bf0eb4470452b72277be4883656206d4ccd7e02de/tokenizers-0.15.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:107089f135b4ae7817affe6264f8c7a5c5b4fd9a90f9439ed495f54fcea56fb4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/61/1c26c8e54af9bab32743e0484601a60738f33797f91040da2a4104f07e70/tokenizers-0.15.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0ff110ecc57b7aa4a594396525a3451ad70988e517237fe91c540997c4e50e29" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/54/451e96d8514b1afbef955f7420e1180e015c3f4eb085ad38189c0e83ee87/tokenizers-0.15.2-cp311-none-win32.whl", hash = "sha256:6d76f00f5c32da36c61f41c58346a4fa7f0a61be02f4301fd30ad59834977cc3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c1/02/40725eebedea8175918bd59ab80b2174d6ef3b3ef9ac8ec996e84c38d3ca/tokenizers-0.15.2-cp311-none-win_amd64.whl", hash = "sha256:cc90102ed17271cf0a1262babe5939e0134b3890345d11a19c3145184b706055" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ae/ca/ea4b5aa70d4d26f2d05620c265b07b5a249157767c1673f5753b8bfc7db1/tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/99/5a55a9b6e2db274c0969ad57d989d02efae90f9e558983a561c9b2b7ea1a/tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51" }, - { url = "https://mirrors.aliyun.com/pypi/packages/82/cc/29bb3a25c06b90ce82bb20ef074011481de5c44413a1e1eb10cfd93080fb/tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c0/ae/f6a974be9b2e1615f3de3cc9e4fc2897a86357400801c58143c67cbbad2e/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/42/340b91f675b494c4ecc0a256c5dd88b4003dbfde05afff90b970738fdfb4/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6f/b2/8a965abc17fff309eb06e98ce429a19a5e04f731a669a6113b9e182f8a79/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/16/dad7b4aa6e34a395aef7ae7b010d8b5ebefdf3df81510de53d7f17d2f0fc/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f6/de/3707df0c1d7bf55e6a4dba724700353bfee8e292fdd8ccfe93416549124d/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/dd/7b8da304d152bb46f13bc2ba5bd545480ab6ce39d94a53eef07f7624d235/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/aa/66e8a81e07a791ca6ee9d74ee6de1ffbcd3985149f13aeb530bd409baba0/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/e1/aed3bc98785c54bd26bf6dd3d2f54cc00de33e8b1f922a23131372eedec8/tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c9/ea/5800f4941a713b2feed955b6a256aacc1ca68a6699916d2668622c075d38/tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/2c/9c2f7a0601cccc8cf169006873ed7775ad76804e98b7236d1f345faf69f8/tokenizers-0.15.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9b648a58281c4672212fab04e60648fde574877d0139cd4b4f93fe28ca8944" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/4f/93ccada67079065f892a2c4e7159caf0ce65084fdf60253815ca964403af/tokenizers-0.15.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7c7d18b733be6bbca8a55084027f7be428c947ddf871c500ee603e375013ffba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fd/8f/1dbeaf8b2a2c00e5172d8ed000fba94edb1d424fd50dcbdcc755fbf3c0aa/tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:13ca3611de8d9ddfbc4dc39ef54ab1d2d4aaa114ac8727dfdc6a6ec4be017378" }, - { url = "https://mirrors.aliyun.com/pypi/packages/89/ed/b055d12637754471e4344f4e85c6268ef76801b0113ce1f789c5d84eaae9/tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:237d1bf3361cf2e6463e6c140628e6406766e8b27274f5fcc62c747ae3c6f094" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/e1/d0b441575a3ac0262c2c73773f79dd50c94e13c9dfda0d953f1c79d47ef5/tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67a0fe1e49e60c664915e9fb6b0cb19bac082ab1f309188230e4b2920230edb3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a8/cd/6fe89c549d3aad886295cb9875105a75fa0d82ce80e4721cb43e6eb0830e/tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4e022fe65e99230b8fd89ebdfea138c24421f91c1a4f4781a8f5016fd5cdfb4d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9f/4d/29e5052a11d1a9f8eb156e48c123731e6219e4f3d72cd6d7787fdf4eff7a/tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d857be2df69763362ac699f8b251a8cd3fac9d21893de129bc788f8baaef2693" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/44/625db94e91c6196b6574359fa70bfe28e8eabf57a1b894f8f0ec69727fd1/tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91", size = 320256, upload-time = "2024-02-12T02:28:50.62Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/6e/489419d98730b3d02381f10a8b97c5bf55b45742d1b347cdd0ffe267b827/tokenizers-0.15.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:52f6130c9cbf70544287575a985bf44ae1bda2da7e8c24e97716080593638012", size = 2578411, upload-time = "2024-02-12T02:24:30.127Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/04/45d88b8bddc09bf56ae1631721393255b75798af515c65c26389713a2072/tokenizers-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:054c1cc9c6d68f7ffa4e810b3d5131e0ba511b6e4be34157aa08ee54c2f8d9ee", size = 2412452, upload-time = "2024-02-12T02:24:33.265Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cc/bf/819bf4445ed68ffaf73b0f6245bcbd21a5cd58e86dabbef315a6d0b707b3/tokenizers-0.15.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9b9b070fdad06e347563b88c278995735292ded1132f8657084989a4c84a6d5", size = 3643451, upload-time = "2024-02-12T02:24:35.713Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/b3/70d3fe0ad25e065322cd902624cad4ff2647484fe823360f58af6927b48c/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea621a7eef4b70e1f7a4e84dd989ae3f0eeb50fc8690254eacc08acb623e82f1", size = 3534105, upload-time = "2024-02-12T02:24:38.176Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/1b/58e77f2b57651e8c1b4f1b7144a1250509f2e7a1f55073d12620968ae4bb/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7fd9a5141634fa3aa8d6b7be362e6ae1b4cda60da81388fa533e0b552c98fd", size = 3398034, upload-time = "2024-02-12T02:24:41.635Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/d5/45dd421f45b3c1a446ffd9486cef29ed568b5978f66a1803fa46a44aa9be/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44f2a832cd0825295f7179eaf173381dc45230f9227ec4b44378322d900447c9", size = 3926740, upload-time = "2024-02-12T02:24:44.264Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/6b/6b757cf6f7c30009a6759d3f7b833d974b3cd50d24d5824c695e077cb1bf/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b9ec69247a23747669ec4b0ca10f8e3dfb3545d550258129bd62291aabe8605", size = 4032027, upload-time = "2024-02-12T02:24:46.246Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/5d/cf5e122ce4f1a29f165b2a69dc33d1ff30bce303343d58a54775ddba5d51/tokenizers-0.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b6a4c78da863ff26dbd5ad9a8ecc33d8a8d97b535172601cf00aee9d7ce9ce", size = 3577319, upload-time = "2024-02-12T02:24:47.995Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/0b/dd9e5124fe73a01f36f5c7554ac97b9612af5e0bd401d6a606a3f52a060a/tokenizers-0.15.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5ab2a4d21dcf76af60e05af8063138849eb1d6553a0d059f6534357bce8ba364", size = 9682328, upload-time = "2024-02-12T02:24:50.604Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/0c/3435e3d54f825d4fa363a7ab2680b243314377eb2ed28e87ade70b861e7b/tokenizers-0.15.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a47acfac7e511f6bbfcf2d3fb8c26979c780a91e06fb5b9a43831b2c0153d024", size = 9995619, upload-time = "2024-02-12T02:24:52.953Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/bf/a804747020f1b221131b74b5f29c24b47a5d2cee4b1311ce394ca9ce242a/tokenizers-0.15.2-cp310-none-win32.whl", hash = "sha256:064ff87bb6acdbd693666de9a4b692add41308a2c0ec0770d6385737117215f2", size = 2013446, upload-time = "2024-02-12T02:24:55.494Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/87/0bf37626c5f1ea2462e0398be88c287f3d40c696c255ba478bf525bdc852/tokenizers-0.15.2-cp310-none-win_amd64.whl", hash = "sha256:3b919afe4df7eb6ac7cafd2bd14fb507d3f408db7a68c43117f579c984a73843", size = 2192649, upload-time = "2024-02-12T02:24:57.898Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/11/933d68d395f5486d935e1c15da80bc96bf3f48595652069d19e0e9894386/tokenizers-0.15.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:89cd1cb93e4b12ff39bb2d626ad77e35209de9309a71e4d3d4672667b4b256e7", size = 2578922, upload-time = "2024-02-12T02:25:00.049Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/4f/a4c12cc058a899c1caaa1e689c3df9a698e20e891d4005aa6ec2174a9339/tokenizers-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cfed5c64e5be23d7ee0f0e98081a25c2a46b0b77ce99a4f0605b1ec43dd481fa", size = 2412317, upload-time = "2024-02-12T02:25:02.21Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/13/b86ea87b7e3b4a2ca154220dc4eb19a56a3864ec03e9630d15d1bac10da1/tokenizers-0.15.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a907d76dcfda37023ba203ab4ceeb21bc5683436ebefbd895a0841fd52f6f6f2", size = 3643051, upload-time = "2024-02-12T02:25:04.296Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/23/e4985657ea42ad432d6dc2100b2687e70a6bae730f1f8c52f81d9e6ccf3a/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20ea60479de6fc7b8ae756b4b097572372d7e4032e2521c1bbf3d90c90a99ff0", size = 3534327, upload-time = "2024-02-12T02:25:06.752Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/d5/e1ad46939d6de48d41bbd8b302f87ecde79847855210e75517a832b29490/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48e2b9335be2bc0171df9281385c2ed06a15f5cf121c44094338306ab7b33f2c", size = 3398296, upload-time = "2024-02-12T02:25:09.378Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/d1/4d319a035f819af3290ec5a09482ad659d9d2a0aea33890fb5720ce81841/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:112a1dd436d2cc06e6ffdc0b06d55ac019a35a63afd26475205cb4b1bf0bfbff", size = 3927353, upload-time = "2024-02-12T02:25:11.791Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/39/facfca8e598126a0001d4295e6b1ee670d241aa6f4fcdd97016065b43c5d/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4620cca5c2817177ee8706f860364cc3a8845bc1e291aaf661fb899e5d1c45b0", size = 4030091, upload-time = "2024-02-12T02:25:13.548Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/0b/c09b2c0dc688c82adadaa0d5080983de3ce920f4a5cbadb7eaa5302ad251/tokenizers-0.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccd73a82751c523b3fc31ff8194702e4af4db21dc20e55b30ecc2079c5d43cb7", size = 3577167, upload-time = "2024-02-12T02:25:16.03Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/3b/d8e60712e509a6f5d01bf0eb4470452b72277be4883656206d4ccd7e02de/tokenizers-0.15.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:107089f135b4ae7817affe6264f8c7a5c5b4fd9a90f9439ed495f54fcea56fb4", size = 9683503, upload-time = "2024-02-12T02:25:17.774Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/61/1c26c8e54af9bab32743e0484601a60738f33797f91040da2a4104f07e70/tokenizers-0.15.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0ff110ecc57b7aa4a594396525a3451ad70988e517237fe91c540997c4e50e29", size = 9996038, upload-time = "2024-02-12T02:25:20.299Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/54/451e96d8514b1afbef955f7420e1180e015c3f4eb085ad38189c0e83ee87/tokenizers-0.15.2-cp311-none-win32.whl", hash = "sha256:6d76f00f5c32da36c61f41c58346a4fa7f0a61be02f4301fd30ad59834977cc3", size = 2013591, upload-time = "2024-02-12T02:25:23.323Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/02/40725eebedea8175918bd59ab80b2174d6ef3b3ef9ac8ec996e84c38d3ca/tokenizers-0.15.2-cp311-none-win_amd64.whl", hash = "sha256:cc90102ed17271cf0a1262babe5939e0134b3890345d11a19c3145184b706055", size = 2192797, upload-time = "2024-02-12T02:25:25.021Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/ca/ea4b5aa70d4d26f2d05620c265b07b5a249157767c1673f5753b8bfc7db1/tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670", size = 2574444, upload-time = "2024-02-12T02:25:27.417Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/99/5a55a9b6e2db274c0969ad57d989d02efae90f9e558983a561c9b2b7ea1a/tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51", size = 2411608, upload-time = "2024-02-12T02:25:29.74Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/cc/29bb3a25c06b90ce82bb20ef074011481de5c44413a1e1eb10cfd93080fb/tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98", size = 3652367, upload-time = "2024-02-12T02:25:32.079Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/ae/f6a974be9b2e1615f3de3cc9e4fc2897a86357400801c58143c67cbbad2e/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66", size = 3529509, upload-time = "2024-02-12T02:25:34.042Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/42/340b91f675b494c4ecc0a256c5dd88b4003dbfde05afff90b970738fdfb4/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd", size = 3396516, upload-time = "2024-02-12T02:25:35.884Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6f/b2/8a965abc17fff309eb06e98ce429a19a5e04f731a669a6113b9e182f8a79/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38", size = 3918811, upload-time = "2024-02-12T02:25:37.85Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/16/dad7b4aa6e34a395aef7ae7b010d8b5ebefdf3df81510de53d7f17d2f0fc/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c", size = 4025494, upload-time = "2024-02-12T02:25:40.247Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f6/de/3707df0c1d7bf55e6a4dba724700353bfee8e292fdd8ccfe93416549124d/tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456", size = 3575314, upload-time = "2024-02-12T02:25:42.212Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/dd/7b8da304d152bb46f13bc2ba5bd545480ab6ce39d94a53eef07f7624d235/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834", size = 9682779, upload-time = "2024-02-12T02:25:44.027Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/aa/66e8a81e07a791ca6ee9d74ee6de1ffbcd3985149f13aeb530bd409baba0/tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d", size = 9995614, upload-time = "2024-02-12T02:25:46.804Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/e1/aed3bc98785c54bd26bf6dd3d2f54cc00de33e8b1f922a23131372eedec8/tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b", size = 2011030, upload-time = "2024-02-12T02:25:49.829Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/ea/5800f4941a713b2feed955b6a256aacc1ca68a6699916d2668622c075d38/tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221", size = 2180523, upload-time = "2024-02-12T02:25:51.542Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/2c/9c2f7a0601cccc8cf169006873ed7775ad76804e98b7236d1f345faf69f8/tokenizers-0.15.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9b648a58281c4672212fab04e60648fde574877d0139cd4b4f93fe28ca8944", size = 2576142, upload-time = "2024-02-12T02:27:38.631Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/4f/93ccada67079065f892a2c4e7159caf0ce65084fdf60253815ca964403af/tokenizers-0.15.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7c7d18b733be6bbca8a55084027f7be428c947ddf871c500ee603e375013ffba", size = 2412714, upload-time = "2024-02-12T02:27:40.439Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/8f/1dbeaf8b2a2c00e5172d8ed000fba94edb1d424fd50dcbdcc755fbf3c0aa/tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:13ca3611de8d9ddfbc4dc39ef54ab1d2d4aaa114ac8727dfdc6a6ec4be017378", size = 3646249, upload-time = "2024-02-12T02:27:42.447Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/ed/b055d12637754471e4344f4e85c6268ef76801b0113ce1f789c5d84eaae9/tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:237d1bf3361cf2e6463e6c140628e6406766e8b27274f5fcc62c747ae3c6f094", size = 3534330, upload-time = "2024-02-12T02:27:44.578Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/e1/d0b441575a3ac0262c2c73773f79dd50c94e13c9dfda0d953f1c79d47ef5/tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67a0fe1e49e60c664915e9fb6b0cb19bac082ab1f309188230e4b2920230edb3", size = 3579864, upload-time = "2024-02-12T02:27:47.452Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/cd/6fe89c549d3aad886295cb9875105a75fa0d82ce80e4721cb43e6eb0830e/tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4e022fe65e99230b8fd89ebdfea138c24421f91c1a4f4781a8f5016fd5cdfb4d", size = 9684097, upload-time = "2024-02-12T02:27:49.776Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/4d/29e5052a11d1a9f8eb156e48c123731e6219e4f3d72cd6d7787fdf4eff7a/tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d857be2df69763362ac699f8b251a8cd3fac9d21893de129bc788f8baaef2693", size = 9997782, upload-time = "2024-02-12T02:27:52.987Z" }, ] [[package]] name = "tomli" version = "2.2.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] name = "torch" version = "2.7.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.5", source = { registry = "https://mirrors.aliyun.com/pypi/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.5", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version >= '3.11'" }, { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, @@ -6913,36 +6956,36 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6a/27/2e06cb52adf89fe6e020963529d17ed51532fc73c1e6d1b18420ef03338c/torch-2.7.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a103b5d782af5bd119b81dbcc7ffc6fa09904c423ff8db397a1e6ea8fd71508f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/7c/0a5b3aee977596459ec45be2220370fde8e017f651fecc40522fd478cb1e/torch-2.7.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:fe955951bdf32d182ee8ead6c3186ad54781492bf03d547d31771a01b3d6fb7d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f9/91/3d709cfc5e15995fb3fe7a6b564ce42280d3a55676dad672205e94f34ac9/torch-2.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:885453d6fba67d9991132143bf7fa06b79b24352f4506fd4d10b309f53454162" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/f6/5da3918414e07da9866ecb9330fe6ffdebe15cb9a4c5ada7d4b6e0a6654d/torch-2.7.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:d72acfdb86cee2a32c0ce0101606f3758f0d8bb5f8f31e7920dc2809e963aa7c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/56/2eae3494e3d375533034a8e8cf0ba163363e996d85f0629441fa9d9843fe/torch-2.7.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:236f501f2e383f1cb861337bdf057712182f910f10aeaf509065d54d339e49b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e5/94/34b80bd172d0072c9979708ccd279c2da2f55c3ef318eceec276ab9544a4/torch-2.7.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:06eea61f859436622e78dd0cdd51dbc8f8c6d76917a9cf0555a333f9eac31ec1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/9e/acf04ff375b0b49a45511c55d188bcea5c942da2aaf293096676110086d1/torch-2.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:8273145a2e0a3c6f9fd2ac36762d6ee89c26d430e612b95a99885df083b04e52" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5b/2b/d36d57c66ff031f93b4fa432e86802f84991477e522adcdffd314454326b/torch-2.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:aea4fc1bf433d12843eb2c6b2204861f43d8364597697074c8d38ae2507f8730" }, - { url = "https://mirrors.aliyun.com/pypi/packages/87/93/fb505a5022a2e908d81fe9a5e0aa84c86c0d5f408173be71c6018836f34e/torch-2.7.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:27ea1e518df4c9de73af7e8a720770f3628e7f667280bce2be7a16292697e3fa" }, - { url = "https://mirrors.aliyun.com/pypi/packages/56/7e/67c3fe2b8c33f40af06326a3d6ae7776b3e3a01daa8f71d125d78594d874/torch-2.7.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c33360cfc2edd976c2633b3b66c769bdcbbf0e0b6550606d188431c81e7dd1fc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/37/a37495502bc7a23bf34f89584fa5a78e25bae7b8da513bc1b8f97afb7009/torch-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:d8bf6e1856ddd1807e79dc57e54d3335f2b62e6f316ed13ed3ecfe1fc1df3d8b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3a/60/04b77281c730bb13460628e518c52721257814ac6c298acd25757f6a175c/torch-2.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:787687087412c4bd68d315e39bc1223f08aae1d16a9e9771d95eabbb04ae98fb" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/27/2e06cb52adf89fe6e020963529d17ed51532fc73c1e6d1b18420ef03338c/torch-2.7.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a103b5d782af5bd119b81dbcc7ffc6fa09904c423ff8db397a1e6ea8fd71508f", size = 99089441, upload-time = "2025-06-04T17:38:48.268Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/7c/0a5b3aee977596459ec45be2220370fde8e017f651fecc40522fd478cb1e/torch-2.7.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:fe955951bdf32d182ee8ead6c3186ad54781492bf03d547d31771a01b3d6fb7d", size = 821154516, upload-time = "2025-06-04T17:36:28.556Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/91/3d709cfc5e15995fb3fe7a6b564ce42280d3a55676dad672205e94f34ac9/torch-2.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:885453d6fba67d9991132143bf7fa06b79b24352f4506fd4d10b309f53454162", size = 216093147, upload-time = "2025-06-04T17:39:38.132Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/f6/5da3918414e07da9866ecb9330fe6ffdebe15cb9a4c5ada7d4b6e0a6654d/torch-2.7.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:d72acfdb86cee2a32c0ce0101606f3758f0d8bb5f8f31e7920dc2809e963aa7c", size = 68630914, upload-time = "2025-06-04T17:39:31.162Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/56/2eae3494e3d375533034a8e8cf0ba163363e996d85f0629441fa9d9843fe/torch-2.7.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:236f501f2e383f1cb861337bdf057712182f910f10aeaf509065d54d339e49b2", size = 99093039, upload-time = "2025-06-04T17:39:06.963Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/94/34b80bd172d0072c9979708ccd279c2da2f55c3ef318eceec276ab9544a4/torch-2.7.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:06eea61f859436622e78dd0cdd51dbc8f8c6d76917a9cf0555a333f9eac31ec1", size = 821174704, upload-time = "2025-06-04T17:37:03.799Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/9e/acf04ff375b0b49a45511c55d188bcea5c942da2aaf293096676110086d1/torch-2.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:8273145a2e0a3c6f9fd2ac36762d6ee89c26d430e612b95a99885df083b04e52", size = 216095937, upload-time = "2025-06-04T17:39:24.83Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/2b/d36d57c66ff031f93b4fa432e86802f84991477e522adcdffd314454326b/torch-2.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:aea4fc1bf433d12843eb2c6b2204861f43d8364597697074c8d38ae2507f8730", size = 68640034, upload-time = "2025-06-04T17:39:17.989Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/93/fb505a5022a2e908d81fe9a5e0aa84c86c0d5f408173be71c6018836f34e/torch-2.7.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:27ea1e518df4c9de73af7e8a720770f3628e7f667280bce2be7a16292697e3fa", size = 98948276, upload-time = "2025-06-04T17:39:12.852Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/7e/67c3fe2b8c33f40af06326a3d6ae7776b3e3a01daa8f71d125d78594d874/torch-2.7.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c33360cfc2edd976c2633b3b66c769bdcbbf0e0b6550606d188431c81e7dd1fc", size = 821025792, upload-time = "2025-06-04T17:34:58.747Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/37/a37495502bc7a23bf34f89584fa5a78e25bae7b8da513bc1b8f97afb7009/torch-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:d8bf6e1856ddd1807e79dc57e54d3335f2b62e6f316ed13ed3ecfe1fc1df3d8b", size = 216050349, upload-time = "2025-06-04T17:38:59.709Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/60/04b77281c730bb13460628e518c52721257814ac6c298acd25757f6a175c/torch-2.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:787687087412c4bd68d315e39bc1223f08aae1d16a9e9771d95eabbb04ae98fb", size = 68645146, upload-time = "2025-06-04T17:38:52.97Z" }, ] [[package]] name = "tqdm" version = "4.67.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] [[package]] name = "transformers" version = "4.36.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "filelock" }, { name = "huggingface-hub" }, @@ -6955,28 +6998,28 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f5/49/eb90797a0efaa0ba66ae0cb6047f8b9daca9f8764cff3bd1068d8be5e997/transformers-4.36.2.tar.gz", hash = "sha256:d8068e897e47793281501e547d2bbdfc5b8556409c2cb6c3d9e2ca77d4c0b4ec" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/49/eb90797a0efaa0ba66ae0cb6047f8b9daca9f8764cff3bd1068d8be5e997/transformers-4.36.2.tar.gz", hash = "sha256:d8068e897e47793281501e547d2bbdfc5b8556409c2cb6c3d9e2ca77d4c0b4ec", size = 7112436, upload-time = "2023-12-18T18:24:45.132Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/20/0a/739426a81f7635b422fbe6cb8d1d99d1235579a6ac8024c13d743efa6847/transformers-4.36.2-py3-none-any.whl", hash = "sha256:462066c4f74ee52516f12890dcc9ec71d1a5e97998db621668455117a54330f6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/0a/739426a81f7635b422fbe6cb8d1d99d1235579a6ac8024c13d743efa6847/transformers-4.36.2-py3-none-any.whl", hash = "sha256:462066c4f74ee52516f12890dcc9ec71d1a5e97998db621668455117a54330f6", size = 8218893, upload-time = "2023-12-18T18:24:40.021Z" }, ] [[package]] name = "trec-car-tools" version = "2.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cbor" }, { name = "numpy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d4/71/7b62e2e56de6cdf0c648f0033a9faa41b8f712bacd71968af96277185400/trec-car-tools-2.6.tar.gz", hash = "sha256:2fce2de120224fd569b151d5bed358a4ed334e643889b9e3dfe3e5a3d15d21c8" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/71/7b62e2e56de6cdf0c648f0033a9faa41b8f712bacd71968af96277185400/trec-car-tools-2.6.tar.gz", hash = "sha256:2fce2de120224fd569b151d5bed358a4ed334e643889b9e3dfe3e5a3d15d21c8", size = 7513, upload-time = "2022-02-01T16:37:20.451Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/36/75/661b406371f96622975eb25f9e70945d97fbe6b8e5af40342c59191962a3/trec_car_tools-2.6-py3-none-any.whl", hash = "sha256:e6f0373259e1c234222da7270ab54ca7af7a6f8d0dd32b13e158c1659d3991cf" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/75/661b406371f96622975eb25f9e70945d97fbe6b8e5af40342c59191962a3/trec_car_tools-2.6-py3-none-any.whl", hash = "sha256:e6f0373259e1c234222da7270ab54ca7af7a6f8d0dd32b13e158c1659d3991cf", size = 8414, upload-time = "2022-02-01T16:37:22.102Z" }, ] [[package]] name = "trio" version = "0.30.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "attrs" }, { name = "cffi", marker = "(implementation_name != 'pypy' and os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (implementation_name != 'pypy' and os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, @@ -6986,103 +7029,103 @@ dependencies = [ { name = "sniffio" }, { name = "sortedcontainers" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/01/c1/68d582b4d3a1c1f8118e18042464bb12a7c1b75d64d75111b297687041e3/trio-0.30.0.tar.gz", hash = "sha256:0781c857c0c81f8f51e0089929a26b5bb63d57f927728a5586f7e36171f064df" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/01/c1/68d582b4d3a1c1f8118e18042464bb12a7c1b75d64d75111b297687041e3/trio-0.30.0.tar.gz", hash = "sha256:0781c857c0c81f8f51e0089929a26b5bb63d57f927728a5586f7e36171f064df", size = 593776, upload-time = "2025-04-21T00:48:19.507Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/69/8e/3f6dfda475ecd940e786defe6df6c500734e686c9cd0a0f8ef6821e9b2f2/trio-0.30.0-py3-none-any.whl", hash = "sha256:3bf4f06b8decf8d3cf00af85f40a89824669e2d033bb32469d34840edcfc22a5" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/8e/3f6dfda475ecd940e786defe6df6c500734e686c9cd0a0f8ef6821e9b2f2/trio-0.30.0-py3-none-any.whl", hash = "sha256:3bf4f06b8decf8d3cf00af85f40a89824669e2d033bb32469d34840edcfc22a5", size = 499194, upload-time = "2025-04-21T00:48:17.167Z" }, ] [[package]] name = "trio-websocket" version = "0.12.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "outcome" }, { name = "trio" }, { name = "wsproto" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d1/3c/8b4358e81f2f2cfe71b66a267f023a91db20a817b9425dd964873796980a/trio_websocket-0.12.2.tar.gz", hash = "sha256:22c72c436f3d1e264d0910a3951934798dcc5b00ae56fc4ee079d46c7cf20fae" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/3c/8b4358e81f2f2cfe71b66a267f023a91db20a817b9425dd964873796980a/trio_websocket-0.12.2.tar.gz", hash = "sha256:22c72c436f3d1e264d0910a3951934798dcc5b00ae56fc4ee079d46c7cf20fae", size = 33549, upload-time = "2025-02-25T05:16:58.947Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c7/19/eb640a397bba49ba49ef9dbe2e7e5c04202ba045b6ce2ec36e9cadc51e04/trio_websocket-0.12.2-py3-none-any.whl", hash = "sha256:df605665f1db533f4a386c94525870851096a223adcb97f72a07e8b4beba45b6" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/19/eb640a397bba49ba49ef9dbe2e7e5c04202ba045b6ce2ec36e9cadc51e04/trio_websocket-0.12.2-py3-none-any.whl", hash = "sha256:df605665f1db533f4a386c94525870851096a223adcb97f72a07e8b4beba45b6", size = 21221, upload-time = "2025-02-25T05:16:57.545Z" }, ] [[package]] name = "triton" version = "3.3.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "setuptools", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8d/a9/549e51e9b1b2c9b854fd761a1d23df0ba2fbc60bd0c13b489ffa518cfcb7/triton-3.3.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b74db445b1c562844d3cfad6e9679c72e93fdfb1a90a24052b03bb5c49d1242e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/21/2f/3e56ea7b58f80ff68899b1dbe810ff257c9d177d288c6b0f55bf2fe4eb50/triton-3.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b31e3aa26f8cb3cc5bf4e187bf737cbacf17311e1112b781d4a059353dfd731b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/24/5f/950fb373bf9c01ad4eb5a8cd5eaf32cdf9e238c02f9293557a2129b9c4ac/triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/a9/549e51e9b1b2c9b854fd761a1d23df0ba2fbc60bd0c13b489ffa518cfcb7/triton-3.3.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b74db445b1c562844d3cfad6e9679c72e93fdfb1a90a24052b03bb5c49d1242e", size = 155600257, upload-time = "2025-05-29T23:39:36.085Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/2f/3e56ea7b58f80ff68899b1dbe810ff257c9d177d288c6b0f55bf2fe4eb50/triton-3.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b31e3aa26f8cb3cc5bf4e187bf737cbacf17311e1112b781d4a059353dfd731b", size = 155689937, upload-time = "2025-05-29T23:39:44.182Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/5f/950fb373bf9c01ad4eb5a8cd5eaf32cdf9e238c02f9293557a2129b9c4ac/triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43", size = 155669138, upload-time = "2025-05-29T23:39:51.771Z" }, ] [[package]] name = "typer" version = "0.16.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" }, ] [[package]] name = "types-python-dateutil" version = "2.9.0.20250708" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c9/95/6bdde7607da2e1e99ec1c1672a759d42f26644bbacf939916e086db34870/types_python_dateutil-2.9.0.20250708.tar.gz", hash = "sha256:ccdbd75dab2d6c9696c350579f34cffe2c281e4c5f27a585b2a2438dd1d5c8ab" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/95/6bdde7607da2e1e99ec1c1672a759d42f26644bbacf939916e086db34870/types_python_dateutil-2.9.0.20250708.tar.gz", hash = "sha256:ccdbd75dab2d6c9696c350579f34cffe2c281e4c5f27a585b2a2438dd1d5c8ab", size = 15834, upload-time = "2025-07-08T03:14:03.382Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/72/52/43e70a8e57fefb172c22a21000b03ebcc15e47e97f5cb8495b9c2832efb4/types_python_dateutil-2.9.0.20250708-py3-none-any.whl", hash = "sha256:4d6d0cc1cc4d24a2dc3816024e502564094497b713f7befda4d5bc7a8e3fd21f" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/52/43e70a8e57fefb172c22a21000b03ebcc15e47e97f5cb8495b9c2832efb4/types_python_dateutil-2.9.0.20250708-py3-none-any.whl", hash = "sha256:4d6d0cc1cc4d24a2dc3816024e502564094497b713f7befda4d5bc7a8e3fd21f", size = 17724, upload-time = "2025-07-08T03:14:02.593Z" }, ] [[package]] name = "types-requests" version = "2.32.4.20250611" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, ] [[package]] name = "typing-extensions" version = "4.14.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, ] [[package]] name = "typing-inspection" version = "0.4.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] [[package]] name = "tzdata" version = "2025.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, ] [[package]] @@ -7100,7 +7143,7 @@ wheels = [ [[package]] name = "umap-learn" version = "0.5.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numba" }, { name = "numpy" }, @@ -7109,36 +7152,36 @@ dependencies = [ { name = "scipy" }, { name = "tqdm" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/36/c0/a21f7e83dc471cb4bdb7bfb10244eb63a0c0b68ee2939b6698add0377eee/umap-learn-0.5.6.tar.gz", hash = "sha256:5b3917a862c23ba0fc83bfcd67a7b719dec85b3d9c01fdc7d894cce455df4e03" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/c0/a21f7e83dc471cb4bdb7bfb10244eb63a0c0b68ee2939b6698add0377eee/umap-learn-0.5.6.tar.gz", hash = "sha256:5b3917a862c23ba0fc83bfcd67a7b719dec85b3d9c01fdc7d894cce455df4e03", size = 89627, upload-time = "2024-04-03T16:53:18.592Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d1/1b/46802a050b1c55d10c4f59fc6afd2b45ac9b4f62b2e12092d3f599286f14/umap_learn-0.5.6-py3-none-any.whl", hash = "sha256:881cc0c2ee845b790bf0455aa1664f9f68b838d9d0fe12a1291b85c5a559c913" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/1b/46802a050b1c55d10c4f59fc6afd2b45ac9b4f62b2e12092d3f599286f14/umap_learn-0.5.6-py3-none-any.whl", hash = "sha256:881cc0c2ee845b790bf0455aa1664f9f68b838d9d0fe12a1291b85c5a559c913", size = 85712, upload-time = "2024-04-03T16:53:16.834Z" }, ] [[package]] name = "unlzw3" version = "0.2.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/08/f1/72b313366285263aaba21a17714fbef597d7662a8737a928b2b4784eb46e/unlzw3-0.2.3.tar.gz", hash = "sha256:ede5d928c792fff9da406f20334f9739693327f448f383ae1df1774627197bbb" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/f1/72b313366285263aaba21a17714fbef597d7662a8737a928b2b4784eb46e/unlzw3-0.2.3.tar.gz", hash = "sha256:ede5d928c792fff9da406f20334f9739693327f448f383ae1df1774627197bbb", size = 5426, upload-time = "2024-12-20T16:05:55.889Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/4d/fb/617af9b317ac75f5663285d3a3cc38903a76d63c6e7397768307545f4ff4/unlzw3-0.2.3-py3-none-any.whl", hash = "sha256:7760fb4f3afa1225623944c061991d89a061f7fb78665dbc4cddfdb562bb4a8b" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/fb/617af9b317ac75f5663285d3a3cc38903a76d63c6e7397768307545f4ff4/unlzw3-0.2.3-py3-none-any.whl", hash = "sha256:7760fb4f3afa1225623944c061991d89a061f7fb78665dbc4cddfdb562bb4a8b", size = 6729, upload-time = "2024-12-20T16:05:53.278Z" }, ] [[package]] name = "uritemplate" version = "4.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, ] [[package]] name = "urllib3" version = "2.5.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, ] [package.optional-dependencies] @@ -7149,45 +7192,45 @@ socks = [ [[package]] name = "uvicorn" version = "0.35.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, ] [[package]] name = "valkey" version = "6.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "async-timeout", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/45/f7/b552b7a67017e6233cd8a3b783ce8c4b548e29df98daedd7fb4c4c2cc8f8/valkey-6.0.2.tar.gz", hash = "sha256:dc2e91512b82d1da0b91ab0cdbd8c97c0c0250281728cb32f9398760df9caeae" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/f7/b552b7a67017e6233cd8a3b783ce8c4b548e29df98daedd7fb4c4c2cc8f8/valkey-6.0.2.tar.gz", hash = "sha256:dc2e91512b82d1da0b91ab0cdbd8c97c0c0250281728cb32f9398760df9caeae", size = 4602149, upload-time = "2024-09-11T11:54:05.014Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/d7/cb/b1eac0fe9cbdbba0a5cf189f5778fe54ba7d7c9f26c2f62ca8d759b38f52/valkey-6.0.2-py3-none-any.whl", hash = "sha256:dbbdd65439ee0dc5689502c54f1899504cc7268e85cb7fe8935f062178ff5805" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/cb/b1eac0fe9cbdbba0a5cf189f5778fe54ba7d7c9f26c2f62ca8d759b38f52/valkey-6.0.2-py3-none-any.whl", hash = "sha256:dbbdd65439ee0dc5689502c54f1899504cc7268e85cb7fe8935f062178ff5805", size = 260101, upload-time = "2024-09-11T11:54:02.963Z" }, ] [[package]] name = "vertexai" version = "1.64.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google-cloud-aiplatform" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0a/36/2dcb9e212bc1ccaff83c897702e74d01cac65c2a664818e9cb5577a8418e/vertexai-1.64.0.tar.gz", hash = "sha256:d8bb42b64fe294180104e9210819dce694b50b27daf64b8b7725878eac65986c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/36/2dcb9e212bc1ccaff83c897702e74d01cac65c2a664818e9cb5577a8418e/vertexai-1.64.0.tar.gz", hash = "sha256:d8bb42b64fe294180104e9210819dce694b50b27daf64b8b7725878eac65986c", size = 9289, upload-time = "2024-08-28T01:03:34.903Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f7/98/ce77d9111ffd3cd49154c44a9863b8507a0eb141058fb3fb6c04a65104c7/vertexai-1.64.0-py3-none-any.whl", hash = "sha256:967c17c09e28bc7d34ff6b2ef51a1953ded4750809bf174dd8b6c9c15017180e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/98/ce77d9111ffd3cd49154c44a9863b8507a0eb141058fb3fb6c04a65104c7/vertexai-1.64.0-py3-none-any.whl", hash = "sha256:967c17c09e28bc7d34ff6b2ef51a1953ded4750809bf174dd8b6c9c15017180e", size = 7274, upload-time = "2024-08-28T01:03:33.324Z" }, ] [[package]] name = "volcengine" version = "1.0.194" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "google" }, { name = "protobuf" }, @@ -7197,12 +7240,12 @@ dependencies = [ { name = "retry" }, { name = "six" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/21/6d/0b29d9bb3895990391ec1e3722f153c24f94a4f1bea2d2d4f418050fae89/volcengine-1.0.194.tar.gz", hash = "sha256:cab0ea38291ca7b2bbffe130a7b173cf6fdc4a1af61cf7792c35296d5498766c" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/6d/0b29d9bb3895990391ec1e3722f153c24f94a4f1bea2d2d4f418050fae89/volcengine-1.0.194.tar.gz", hash = "sha256:cab0ea38291ca7b2bbffe130a7b173cf6fdc4a1af61cf7792c35296d5498766c", size = 356685, upload-time = "2025-07-17T12:23:39.106Z" } [[package]] name = "voyageai" version = "0.2.3" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "aiohttp" }, { name = "aiolimiter" }, @@ -7210,136 +7253,136 @@ dependencies = [ { name = "requests" }, { name = "tenacity" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/7e/8c/23240073e306e6f49f6d2a33de28ca74fe36ebcd34bca3cfbcedcdd0ce63/voyageai-0.2.3.tar.gz", hash = "sha256:28322aa7a64cdaa774be6fcf3e4fd6a08694ea25acd5fadd1eff1b8ef8dab68a" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/8c/23240073e306e6f49f6d2a33de28ca74fe36ebcd34bca3cfbcedcdd0ce63/voyageai-0.2.3.tar.gz", hash = "sha256:28322aa7a64cdaa774be6fcf3e4fd6a08694ea25acd5fadd1eff1b8ef8dab68a", size = 15374, upload-time = "2024-05-29T08:12:46.798Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/bf/7c/43fb4689fe287eceb701f389863aab35211835d63bbb9a798cfefa80d7de/voyageai-0.2.3-py3-none-any.whl", hash = "sha256:59c4958bd991e83cedb5a82d5e14ac698ce67e42713ea10467631a48ee272b15" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/7c/43fb4689fe287eceb701f389863aab35211835d63bbb9a798cfefa80d7de/voyageai-0.2.3-py3-none-any.whl", hash = "sha256:59c4958bd991e83cedb5a82d5e14ac698ce67e42713ea10467631a48ee272b15", size = 19748, upload-time = "2024-05-29T08:12:44.968Z" }, ] [[package]] name = "warc3-wet" version = "0.2.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/21/c6/24c9b4a2b2b1741b57d7f44ff9790eb4ef28de898c17c2b1ca1efabf8c96/warc3_wet-0.2.5.tar.gz", hash = "sha256:15e50402dabaa1e95307f1e2a6169cfd5f137b70761d9f0b16a10aa6de227970" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/c6/24c9b4a2b2b1741b57d7f44ff9790eb4ef28de898c17c2b1ca1efabf8c96/warc3_wet-0.2.5.tar.gz", hash = "sha256:15e50402dabaa1e95307f1e2a6169cfd5f137b70761d9f0b16a10aa6de227970", size = 17937, upload-time = "2024-07-17T08:33:51.765Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f4/99/0a5582a106679fd9439af51631b6c6cb627fd96cbc85a02927e6812605b8/warc3_wet-0.2.5-py3-none-any.whl", hash = "sha256:5a9a525383fb1af159734baa75f349a7c4ec7bccd1b938681b5748515d2bf624" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/99/0a5582a106679fd9439af51631b6c6cb627fd96cbc85a02927e6812605b8/warc3_wet-0.2.5-py3-none-any.whl", hash = "sha256:5a9a525383fb1af159734baa75f349a7c4ec7bccd1b938681b5748515d2bf624", size = 18657, upload-time = "2024-07-17T08:33:50.086Z" }, ] [[package]] name = "warc3-wet-clueweb09" version = "0.2.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9f/c1/dd817bf57e0274dacb10e0ac868cb6cd70876950cf361c41879c030a2b8b/warc3-wet-clueweb09-0.2.5.tar.gz", hash = "sha256:3054bfc07da525d5967df8ca3175f78fa3f78514c82643f8c81fbca96300b836" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/c1/dd817bf57e0274dacb10e0ac868cb6cd70876950cf361c41879c030a2b8b/warc3-wet-clueweb09-0.2.5.tar.gz", hash = "sha256:3054bfc07da525d5967df8ca3175f78fa3f78514c82643f8c81fbca96300b836", size = 17853, upload-time = "2020-12-07T23:59:04.599Z" } [[package]] name = "wcwidth" version = "0.2.13" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, ] [[package]] name = "webdriver-manager" version = "4.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "packaging" }, { name = "python-dotenv" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e5/50/2958aa25647e86334b30b4f8c819cc4fd5f15d3d0115042a4c924ec6e94d/webdriver_manager-4.0.1.tar.gz", hash = "sha256:25ec177c6a2ce9c02fb8046f1b2732701a9418d6a977967bb065d840a3175d87" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/50/2958aa25647e86334b30b4f8c819cc4fd5f15d3d0115042a4c924ec6e94d/webdriver_manager-4.0.1.tar.gz", hash = "sha256:25ec177c6a2ce9c02fb8046f1b2732701a9418d6a977967bb065d840a3175d87", size = 25708, upload-time = "2023-09-25T06:34:54.614Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/b1/51/b5c11cf739ac4eecde611794a0ec9df420d0239d51e73bc19eb44f02b48b/webdriver_manager-4.0.1-py2.py3-none-any.whl", hash = "sha256:d7970052295bb9cda2c1a24cf0b872dd2c41ababcc78f7b6b8dc37a41e979a7e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/51/b5c11cf739ac4eecde611794a0ec9df420d0239d51e73bc19eb44f02b48b/webdriver_manager-4.0.1-py2.py3-none-any.whl", hash = "sha256:d7970052295bb9cda2c1a24cf0b872dd2c41ababcc78f7b6b8dc37a41e979a7e", size = 27665, upload-time = "2023-09-25T06:34:53.307Z" }, ] [[package]] name = "webencodings" version = "0.5.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, ] [[package]] name = "websocket-client" version = "1.8.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, ] [[package]] name = "websockets" version = "15.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792" }, - { url = "https://mirrors.aliyun.com/pypi/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065" }, - { url = "https://mirrors.aliyun.com/pypi/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215" }, - { url = "https://mirrors.aliyun.com/pypi/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04" }, - { url = "https://mirrors.aliyun.com/pypi/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] [[package]] name = "werkzeug" version = "3.0.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d4/f9/0ba83eaa0df9b9e9d1efeb2ea351d0677c37d41ee5d0f91e98423c7281c9/werkzeug-3.0.6.tar.gz", hash = "sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/f9/0ba83eaa0df9b9e9d1efeb2ea351d0677c37d41ee5d0f91e98423c7281c9/werkzeug-3.0.6.tar.gz", hash = "sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d", size = 805170, upload-time = "2024-10-25T18:52:31.688Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/6c/69/05837f91dfe42109203ffa3e488214ff86a6d68b2ed6c167da6cdc42349b/werkzeug-3.0.6-py3-none-any.whl", hash = "sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/69/05837f91dfe42109203ffa3e488214ff86a6d68b2ed6c167da6cdc42349b/werkzeug-3.0.6-py3-none-any.whl", hash = "sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17", size = 227979, upload-time = "2024-10-25T18:52:30.129Z" }, ] [[package]] name = "wikipedia" version = "1.4.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "requests" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/67/35/25e68fbc99e672127cc6fbb14b8ec1ba3dfef035bf1e4c90f78f24a80b7d/wikipedia-1.4.0.tar.gz", hash = "sha256:db0fad1829fdd441b1852306e9856398204dc0786d2996dd2e0c8bb8e26133b2" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/35/25e68fbc99e672127cc6fbb14b8ec1ba3dfef035bf1e4c90f78f24a80b7d/wikipedia-1.4.0.tar.gz", hash = "sha256:db0fad1829fdd441b1852306e9856398204dc0786d2996dd2e0c8bb8e26133b2", size = 27748, upload-time = "2014-11-15T15:59:49.808Z" } [[package]] name = "win-unicode-console" @@ -7350,243 +7393,243 @@ sdist = { url = "https://mirrors.aliyun.com/pypi/packages/89/8d/7aad74930380c897 [[package]] name = "win32-setctime" version = "1.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, ] [[package]] name = "word2number" version = "1.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/4a/29/a31940c848521f0725f0df6b25dca8917f13a2025b0e8fcbe5d0457e45e6/word2number-1.1.zip", hash = "sha256:70e27a5d387f67b04c71fbb7621c05930b19bfd26efd6851e6e0f9969dcde7d0" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/29/a31940c848521f0725f0df6b25dca8917f13a2025b0e8fcbe5d0457e45e6/word2number-1.1.zip", hash = "sha256:70e27a5d387f67b04c71fbb7621c05930b19bfd26efd6851e6e0f9969dcde7d0", size = 9723, upload-time = "2017-06-02T15:45:14.488Z" } [[package]] name = "wrapt" version = "1.17.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72" }, - { url = "https://mirrors.aliyun.com/pypi/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563" }, - { url = "https://mirrors.aliyun.com/pypi/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925" }, - { url = "https://mirrors.aliyun.com/pypi/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae" }, - { url = "https://mirrors.aliyun.com/pypi/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307, upload-time = "2025-01-14T10:33:13.616Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486, upload-time = "2025-01-14T10:33:15.947Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777, upload-time = "2025-01-14T10:33:17.462Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314, upload-time = "2025-01-14T10:33:21.282Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947, upload-time = "2025-01-14T10:33:24.414Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778, upload-time = "2025-01-14T10:33:26.152Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716, upload-time = "2025-01-14T10:33:27.372Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548, upload-time = "2025-01-14T10:33:28.52Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334, upload-time = "2025-01-14T10:33:29.643Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427, upload-time = "2025-01-14T10:33:30.832Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774, upload-time = "2025-01-14T10:33:32.897Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308, upload-time = "2025-01-14T10:33:33.992Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488, upload-time = "2025-01-14T10:33:35.264Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776, upload-time = "2025-01-14T10:33:38.28Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776, upload-time = "2025-01-14T10:33:40.678Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420, upload-time = "2025-01-14T10:33:41.868Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199, upload-time = "2025-01-14T10:33:43.598Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307, upload-time = "2025-01-14T10:33:48.499Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025, upload-time = "2025-01-14T10:33:51.191Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879, upload-time = "2025-01-14T10:33:52.328Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419, upload-time = "2025-01-14T10:33:53.551Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773, upload-time = "2025-01-14T10:33:56.323Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, ] [[package]] name = "wsproto" version = "1.2.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "h11" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425, upload-time = "2022-08-23T19:58:21.447Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226, upload-time = "2022-08-23T19:58:19.96Z" }, ] [[package]] name = "xgboost" version = "1.6.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "numpy" }, { name = "scipy" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/77/89/92b399140a7688443fc182b54240822c903e906121d63446eb2f84350e99/xgboost-1.6.0.tar.gz", hash = "sha256:9c944c2495cb426b8a365021565755c39ee0b53156cf5e53a4346bdad2e3b734" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/89/92b399140a7688443fc182b54240822c903e906121d63446eb2f84350e99/xgboost-1.6.0.tar.gz", hash = "sha256:9c944c2495cb426b8a365021565755c39ee0b53156cf5e53a4346bdad2e3b734", size = 775427, upload-time = "2022-04-16T04:16:36.568Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/f1/71/abca2240b5d19aa3e90c8228cf307962fc9f598acc3c623fb49db83b4092/xgboost-1.6.0-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:5f7fd61024c41d0c424a8272dfd27797a0393a616b717c05c0f981a49a47b4fd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/49/d0/85c9c40e7ca1a4bc05278c1e57a89c43ab846be4cb5227871ca7605921a6/xgboost-1.6.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:ad27c6a72f6abef6d20e67f957fb25553bb09a6d1c4eaf08cb8ee7efca288255" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/be/18970943eb7e9d9ded5e37e87c1dc02c8a961416f725f2734629f26d69d5/xgboost-1.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:b1d532b8d548dd3acb4bd5f56632339e48167d9e2ec0eda2d8d6b4cd772e03b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/bf/64/c467a20848adc3d1c3f45d60df9c7cd0c40a548fd534a9f842a35114039d/xgboost-1.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:640b9649104f22f0dc43c7202d22cde5531cc303801a9c75cad3f2b6e413dcf7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/64/51/3e33a4df0ca66474e7f4e357328a5c7b35fb52cbc48b312c64d276d37da8/xgboost-1.6.0-py3-none-win_amd64.whl", hash = "sha256:e2f9baca0b7cbc208ad4fbafa4cd70b50b292717ee8ba817a3ba7a0fe49de958" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/71/abca2240b5d19aa3e90c8228cf307962fc9f598acc3c623fb49db83b4092/xgboost-1.6.0-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:5f7fd61024c41d0c424a8272dfd27797a0393a616b717c05c0f981a49a47b4fd", size = 1712537, upload-time = "2022-04-16T04:15:30.361Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/d0/85c9c40e7ca1a4bc05278c1e57a89c43ab846be4cb5227871ca7605921a6/xgboost-1.6.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:ad27c6a72f6abef6d20e67f957fb25553bb09a6d1c4eaf08cb8ee7efca288255", size = 1529734, upload-time = "2022-04-16T04:16:15.473Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/be/18970943eb7e9d9ded5e37e87c1dc02c8a961416f725f2734629f26d69d5/xgboost-1.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:b1d532b8d548dd3acb4bd5f56632339e48167d9e2ec0eda2d8d6b4cd772e03b4", size = 2472197, upload-time = "2022-04-16T04:12:15.75Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/64/c467a20848adc3d1c3f45d60df9c7cd0c40a548fd534a9f842a35114039d/xgboost-1.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:640b9649104f22f0dc43c7202d22cde5531cc303801a9c75cad3f2b6e413dcf7", size = 193735183, upload-time = "2022-04-16T04:09:35.821Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/51/3e33a4df0ca66474e7f4e357328a5c7b35fb52cbc48b312c64d276d37da8/xgboost-1.6.0-py3-none-win_amd64.whl", hash = "sha256:e2f9baca0b7cbc208ad4fbafa4cd70b50b292717ee8ba817a3ba7a0fe49de958", size = 126068123, upload-time = "2022-04-16T04:14:02.044Z" }, ] [[package]] name = "xlrd" version = "2.0.2" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/07/5a/377161c2d3538d1990d7af382c79f3b2372e880b65de21b01b1a2b78691e/xlrd-2.0.2.tar.gz", hash = "sha256:08b5e25de58f21ce71dc7db3b3b8106c1fa776f3024c54e45b45b374e89234c9" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/5a/377161c2d3538d1990d7af382c79f3b2372e880b65de21b01b1a2b78691e/xlrd-2.0.2.tar.gz", hash = "sha256:08b5e25de58f21ce71dc7db3b3b8106c1fa776f3024c54e45b45b374e89234c9", size = 100167, upload-time = "2025-06-14T08:46:39.039Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/1a/62/c8d562e7766786ba6587d09c5a8ba9f718ed3fa8af7f4553e8f91c36f302/xlrd-2.0.2-py2.py3-none-any.whl", hash = "sha256:ea762c3d29f4cca48d82df517b6d89fbce4db3107f9d78713e48cd321d5c9aa9" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/62/c8d562e7766786ba6587d09c5a8ba9f718ed3fa8af7f4553e8f91c36f302/xlrd-2.0.2-py2.py3-none-any.whl", hash = "sha256:ea762c3d29f4cca48d82df517b6d89fbce4db3107f9d78713e48cd321d5c9aa9", size = 96555, upload-time = "2025-06-14T08:46:37.766Z" }, ] [[package]] name = "xlsxwriter" version = "3.2.5" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a7/47/7704bac42ac6fe1710ae099b70e6a1e68ed173ef14792b647808c357da43/xlsxwriter-3.2.5.tar.gz", hash = "sha256:7e88469d607cdc920151c0ab3ce9cf1a83992d4b7bc730c5ffdd1a12115a7dbe" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/47/7704bac42ac6fe1710ae099b70e6a1e68ed173ef14792b647808c357da43/xlsxwriter-3.2.5.tar.gz", hash = "sha256:7e88469d607cdc920151c0ab3ce9cf1a83992d4b7bc730c5ffdd1a12115a7dbe", size = 213306, upload-time = "2025-06-17T08:59:14.619Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/fa/34/a22e6664211f0c8879521328000bdcae9bf6dbafa94a923e531f6d5b3f73/xlsxwriter-3.2.5-py3-none-any.whl", hash = "sha256:4f4824234e1eaf9d95df9a8fe974585ff91d0f5e3d3f12ace5b71e443c1c6abd" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/34/a22e6664211f0c8879521328000bdcae9bf6dbafa94a923e531f6d5b3f73/xlsxwriter-3.2.5-py3-none-any.whl", hash = "sha256:4f4824234e1eaf9d95df9a8fe974585ff91d0f5e3d3f12ace5b71e443c1c6abd", size = 172347, upload-time = "2025-06-17T08:59:13.453Z" }, ] [[package]] name = "xpinyin" version = "0.7.6" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/16/f2/d548d2f91106644b1b51df4cc59c1b3fabe9048954f18011775250c32d53/xpinyin-0.7.6.tar.gz", hash = "sha256:dec6aa0f4d9f9b6788d8131245293f1951180333a6d474b467b2d556221862fe" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/f2/d548d2f91106644b1b51df4cc59c1b3fabe9048954f18011775250c32d53/xpinyin-0.7.6.tar.gz", hash = "sha256:dec6aa0f4d9f9b6788d8131245293f1951180333a6d474b467b2d556221862fe", size = 131664, upload-time = "2020-12-21T07:58:32.453Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/a5/30/40e099a8da32105c8adf996abe92a5bbf5ecd338de2c4cc491b5718299ce/xpinyin-0.7.6-py3-none-any.whl", hash = "sha256:1d78eac9f612c20e155d7c3eb9dd7f9d3ec4e2667c52049e990b8bd036171a52" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/30/40e099a8da32105c8adf996abe92a5bbf5ecd338de2c4cc491b5718299ce/xpinyin-0.7.6-py3-none-any.whl", hash = "sha256:1d78eac9f612c20e155d7c3eb9dd7f9d3ec4e2667c52049e990b8bd036171a52", size = 129510, upload-time = "2020-12-21T07:58:30.32Z" }, ] [[package]] name = "xxhash" version = "3.5.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/bb/8a/0e9feca390d512d293afd844d31670e25608c4a901e10202aa98785eab09/xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212" }, - { url = "https://mirrors.aliyun.com/pypi/packages/16/e6/be5aa49580cd064a18200ab78e29b88b1127e1a8c7955eb8ecf81f2626eb/xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/ee/b8a99ebbc6d1113b3a3f09e747fa318c3cde5b04bd9c197688fadf0eeae8/xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680" }, - { url = "https://mirrors.aliyun.com/pypi/packages/58/62/15d10582ef159283a5c2b47f6d799fc3303fe3911d5bb0bcc820e1ef7ff4/xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/23/41/61202663ea9b1bd8e53673b8ec9e2619989353dba8cfb68e59a9cbd9ffe3/xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/07/d9a3059f702dec5b3b703737afb6dda32f304f6e9da181a229dafd052c29/xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/58/27caadf78226ecf1d62dbd0c01d152ed381c14c1ee4ad01f0d460fc40eac/xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/08/32d558ce23e1e068453c39aed7b3c1cdc690c177873ec0ca3a90d5808765/xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/d4/2b971e2d2b0a61045f842b622ef11e94096cf1f12cd448b6fd426e80e0e2/xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/ae/6a6438864a8c4c39915d7b65effd85392ebe22710412902487e51769146d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/7d/b3c27c27d1fc868094d02fe4498ccce8cec9fcc591825c01d6bcb0b4fc49/xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a1/05/918f9e7d2fbbd334b829997045d341d6239b563c44e683b9a7ef8fe50f5d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/29/dfe393805b2f86bfc47c290b275f0b7c189dc2f4e136fd4754f32eb18a8d/xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/d7/aa0b22c4ebb7c3ccb993d4c565132abc641cd11164f8952d89eb6a501909/xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/69/12/f969b81541ee91b55f1ce469d7ab55079593c80d04fd01691b550e535000/xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b8/c7/afed0f131fbda960ff15eee7f304fa0eeb2d58770fade99897984852ef23/xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8c/0c/7c3bc6d87e5235672fcc2fb42fd5ad79fe1033925f71bf549ee068c7d1ca/xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/04/9e/01067981d98069eec1c20201f8c145367698e9056f8bc295346e4ea32dd1/xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d4/09/d4996de4059c3ce5342b6e1e6a77c9d6c91acce31f6ed979891872dd162b/xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/f5/6d2dc9f8d55a7ce0f5e7bfef916e67536f01b85d32a9fbf137d4cadbee38/xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/72/9256303f10e41ab004799a4aa74b80b3c5977d6383ae4550548b24bd1971/xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/92/1a3a29acd08248a34b0e6a94f4e0ed9b8379a4ff471f1668e4dce7bdbaa8/xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88" }, - { url = "https://mirrors.aliyun.com/pypi/packages/53/ad/7fa1a109663366de42f724a1cdb8e796a260dbac45047bce153bc1e18abf/xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/35/02/137300e24203bf2b2a49b48ce898ecce6fd01789c0fcd9c686c0a002d129/xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/23/03/aeceb273933d7eee248c4322b98b8e971f06cc3880e5f7602c94e5578af5/xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/64/ed82ec09489474cbb35c716b189ddc1521d8b3de12b1b5ab41ce7f70253c/xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/71/43/6db4c02dcb488ad4e03bc86d70506c3d40a384ee73c9b5c93338eb1f3c23/xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/6d/db4abec29e7a567455344433d095fdb39c97db6955bb4a2c432e486b4d28/xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/1c/fa3b61c0cf03e1da4767213672efe186b1dfa4fc901a4a694fb184a513d1/xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6b/8e/9e6fc572acf6e1cc7ccb01973c213f895cb8668a9d4c2b58a99350da14b7/xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3" }, - { url = "https://mirrors.aliyun.com/pypi/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793" }, - { url = "https://mirrors.aliyun.com/pypi/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be" }, - { url = "https://mirrors.aliyun.com/pypi/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27" }, - { url = "https://mirrors.aliyun.com/pypi/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab" }, - { url = "https://mirrors.aliyun.com/pypi/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/9a/233606bada5bd6f50b2b72c45de3d9868ad551e83893d2ac86dc7bb8553a/xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/67/f75276ca39e2c6604e3bee6c84e9db8a56a4973fde9bf35989787cf6e8aa/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0f/f8/f6c61fd794229cc3848d144f73754a0c107854372d7261419dcbbd286299/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/d3/c029c99801526f859e6b38d34ab87c08993bf3dcea34b11275775001638a/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/e3/bef7b82c1997579c94de9ac5ea7626d01ae5858aa22bf4fcb38bf220cb3e/xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da" }, +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241, upload-time = "2024-08-17T09:20:38.972Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/8a/0e9feca390d512d293afd844d31670e25608c4a901e10202aa98785eab09/xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212", size = 31970, upload-time = "2024-08-17T09:17:35.675Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/e6/be5aa49580cd064a18200ab78e29b88b1127e1a8c7955eb8ecf81f2626eb/xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520", size = 30801, upload-time = "2024-08-17T09:17:37.353Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/ee/b8a99ebbc6d1113b3a3f09e747fa318c3cde5b04bd9c197688fadf0eeae8/xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680", size = 220927, upload-time = "2024-08-17T09:17:38.835Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/62/15d10582ef159283a5c2b47f6d799fc3303fe3911d5bb0bcc820e1ef7ff4/xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da", size = 200360, upload-time = "2024-08-17T09:17:40.851Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/41/61202663ea9b1bd8e53673b8ec9e2619989353dba8cfb68e59a9cbd9ffe3/xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23", size = 428528, upload-time = "2024-08-17T09:17:42.545Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/07/d9a3059f702dec5b3b703737afb6dda32f304f6e9da181a229dafd052c29/xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196", size = 194149, upload-time = "2024-08-17T09:17:44.361Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/58/27caadf78226ecf1d62dbd0c01d152ed381c14c1ee4ad01f0d460fc40eac/xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c", size = 207703, upload-time = "2024-08-17T09:17:46.656Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/08/32d558ce23e1e068453c39aed7b3c1cdc690c177873ec0ca3a90d5808765/xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482", size = 216255, upload-time = "2024-08-17T09:17:48.031Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/d4/2b971e2d2b0a61045f842b622ef11e94096cf1f12cd448b6fd426e80e0e2/xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296", size = 202744, upload-time = "2024-08-17T09:17:50.045Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/ae/6a6438864a8c4c39915d7b65effd85392ebe22710412902487e51769146d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415", size = 210115, upload-time = "2024-08-17T09:17:51.834Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/7d/b3c27c27d1fc868094d02fe4498ccce8cec9fcc591825c01d6bcb0b4fc49/xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198", size = 414247, upload-time = "2024-08-17T09:17:53.094Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/05/918f9e7d2fbbd334b829997045d341d6239b563c44e683b9a7ef8fe50f5d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442", size = 191419, upload-time = "2024-08-17T09:17:54.906Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/29/dfe393805b2f86bfc47c290b275f0b7c189dc2f4e136fd4754f32eb18a8d/xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da", size = 30114, upload-time = "2024-08-17T09:17:56.566Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/d7/aa0b22c4ebb7c3ccb993d4c565132abc641cd11164f8952d89eb6a501909/xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9", size = 30003, upload-time = "2024-08-17T09:17:57.596Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/12/f969b81541ee91b55f1ce469d7ab55079593c80d04fd01691b550e535000/xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6", size = 26773, upload-time = "2024-08-17T09:17:59.169Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b8/c7/afed0f131fbda960ff15eee7f304fa0eeb2d58770fade99897984852ef23/xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1", size = 31969, upload-time = "2024-08-17T09:18:00.852Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/0c/7c3bc6d87e5235672fcc2fb42fd5ad79fe1033925f71bf549ee068c7d1ca/xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8", size = 30800, upload-time = "2024-08-17T09:18:01.863Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/9e/01067981d98069eec1c20201f8c145367698e9056f8bc295346e4ea32dd1/xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166", size = 221566, upload-time = "2024-08-17T09:18:03.461Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/09/d4996de4059c3ce5342b6e1e6a77c9d6c91acce31f6ed979891872dd162b/xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7", size = 201214, upload-time = "2024-08-17T09:18:05.616Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/f5/6d2dc9f8d55a7ce0f5e7bfef916e67536f01b85d32a9fbf137d4cadbee38/xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623", size = 429433, upload-time = "2024-08-17T09:18:06.957Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/72/9256303f10e41ab004799a4aa74b80b3c5977d6383ae4550548b24bd1971/xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a", size = 194822, upload-time = "2024-08-17T09:18:08.331Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/92/1a3a29acd08248a34b0e6a94f4e0ed9b8379a4ff471f1668e4dce7bdbaa8/xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88", size = 208538, upload-time = "2024-08-17T09:18:10.332Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/53/ad/7fa1a109663366de42f724a1cdb8e796a260dbac45047bce153bc1e18abf/xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c", size = 216953, upload-time = "2024-08-17T09:18:11.707Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/02/137300e24203bf2b2a49b48ce898ecce6fd01789c0fcd9c686c0a002d129/xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2", size = 203594, upload-time = "2024-08-17T09:18:13.799Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/03/aeceb273933d7eee248c4322b98b8e971f06cc3880e5f7602c94e5578af5/xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084", size = 210971, upload-time = "2024-08-17T09:18:15.824Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/64/ed82ec09489474cbb35c716b189ddc1521d8b3de12b1b5ab41ce7f70253c/xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d", size = 415050, upload-time = "2024-08-17T09:18:17.142Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/43/6db4c02dcb488ad4e03bc86d70506c3d40a384ee73c9b5c93338eb1f3c23/xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839", size = 192216, upload-time = "2024-08-17T09:18:18.779Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/6d/db4abec29e7a567455344433d095fdb39c97db6955bb4a2c432e486b4d28/xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da", size = 30120, upload-time = "2024-08-17T09:18:20.009Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/1c/fa3b61c0cf03e1da4767213672efe186b1dfa4fc901a4a694fb184a513d1/xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58", size = 30003, upload-time = "2024-08-17T09:18:21.052Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/8e/9e6fc572acf6e1cc7ccb01973c213f895cb8668a9d4c2b58a99350da14b7/xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3", size = 26777, upload-time = "2024-08-17T09:18:22.809Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00", size = 31969, upload-time = "2024-08-17T09:18:24.025Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9", size = 30787, upload-time = "2024-08-17T09:18:25.318Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84", size = 220959, upload-time = "2024-08-17T09:18:26.518Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793", size = 200006, upload-time = "2024-08-17T09:18:27.905Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be", size = 428326, upload-time = "2024-08-17T09:18:29.335Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6", size = 194380, upload-time = "2024-08-17T09:18:30.706Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90", size = 207934, upload-time = "2024-08-17T09:18:32.133Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27", size = 216301, upload-time = "2024-08-17T09:18:33.474Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2", size = 203351, upload-time = "2024-08-17T09:18:34.889Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d", size = 210294, upload-time = "2024-08-17T09:18:36.355Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab", size = 414674, upload-time = "2024-08-17T09:18:38.536Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e", size = 192022, upload-time = "2024-08-17T09:18:40.138Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8", size = 30170, upload-time = "2024-08-17T09:18:42.163Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e", size = 30040, upload-time = "2024-08-17T09:18:43.699Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2", size = 26796, upload-time = "2024-08-17T09:18:45.29Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/9a/233606bada5bd6f50b2b72c45de3d9868ad551e83893d2ac86dc7bb8553a/xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c", size = 29732, upload-time = "2024-08-17T09:20:11.175Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/67/f75276ca39e2c6604e3bee6c84e9db8a56a4973fde9bf35989787cf6e8aa/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986", size = 36214, upload-time = "2024-08-17T09:20:12.335Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/f8/f6c61fd794229cc3848d144f73754a0c107854372d7261419dcbbd286299/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6", size = 32020, upload-time = "2024-08-17T09:20:13.537Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/d3/c029c99801526f859e6b38d34ab87c08993bf3dcea34b11275775001638a/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b", size = 40515, upload-time = "2024-08-17T09:20:14.669Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/e3/bef7b82c1997579c94de9ac5ea7626d01ae5858aa22bf4fcb38bf220cb3e/xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da", size = 30064, upload-time = "2024-08-17T09:20:15.925Z" }, ] [[package]] name = "yarl" version = "1.20.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73" }, - { url = "https://mirrors.aliyun.com/pypi/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23" }, - { url = "https://mirrors.aliyun.com/pypi/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70" }, - { url = "https://mirrors.aliyun.com/pypi/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24" }, - { url = "https://mirrors.aliyun.com/pypi/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf" }, - { url = "https://mirrors.aliyun.com/pypi/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819" }, - { url = "https://mirrors.aliyun.com/pypi/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16" }, - { url = "https://mirrors.aliyun.com/pypi/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6" }, - { url = "https://mirrors.aliyun.com/pypi/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f" }, - { url = "https://mirrors.aliyun.com/pypi/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004" }, - { url = "https://mirrors.aliyun.com/pypi/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698" }, - { url = "https://mirrors.aliyun.com/pypi/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4", size = 132910, upload-time = "2025-06-10T00:42:31.108Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a", size = 90644, upload-time = "2025-06-10T00:42:33.851Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed", size = 89322, upload-time = "2025-06-10T00:42:35.688Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e", size = 323786, upload-time = "2025-06-10T00:42:37.817Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73", size = 319627, upload-time = "2025-06-10T00:42:39.937Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e", size = 339149, upload-time = "2025-06-10T00:42:42.627Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8", size = 333327, upload-time = "2025-06-10T00:42:44.842Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23", size = 326054, upload-time = "2025-06-10T00:42:47.149Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70", size = 315035, upload-time = "2025-06-10T00:42:48.852Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb", size = 338962, upload-time = "2025-06-10T00:42:51.024Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2", size = 335399, upload-time = "2025-06-10T00:42:53.007Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30", size = 338649, upload-time = "2025-06-10T00:42:54.964Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309", size = 358563, upload-time = "2025-06-10T00:42:57.28Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24", size = 357609, upload-time = "2025-06-10T00:42:59.055Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13", size = 350224, upload-time = "2025-06-10T00:43:01.248Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8", size = 81753, upload-time = "2025-06-10T00:43:03.486Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16", size = 86817, upload-time = "2025-06-10T00:43:05.231Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833, upload-time = "2025-06-10T00:43:07.393Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070, upload-time = "2025-06-10T00:43:09.538Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818, upload-time = "2025-06-10T00:43:11.575Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003, upload-time = "2025-06-10T00:43:14.088Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537, upload-time = "2025-06-10T00:43:16.431Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358, upload-time = "2025-06-10T00:43:18.704Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362, upload-time = "2025-06-10T00:43:20.888Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979, upload-time = "2025-06-10T00:43:23.169Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274, upload-time = "2025-06-10T00:43:27.111Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294, upload-time = "2025-06-10T00:43:28.96Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169, upload-time = "2025-06-10T00:43:30.701Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776, upload-time = "2025-06-10T00:43:32.51Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341, upload-time = "2025-06-10T00:43:34.543Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988, upload-time = "2025-06-10T00:43:36.489Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113, upload-time = "2025-06-10T00:43:38.592Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485, upload-time = "2025-06-10T00:43:41.038Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686, upload-time = "2025-06-10T00:43:42.692Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667, upload-time = "2025-06-10T00:43:44.369Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025, upload-time = "2025-06-10T00:43:46.295Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709, upload-time = "2025-06-10T00:43:48.22Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287, upload-time = "2025-06-10T00:43:49.924Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429, upload-time = "2025-06-10T00:43:51.7Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429, upload-time = "2025-06-10T00:43:53.494Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862, upload-time = "2025-06-10T00:43:55.766Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616, upload-time = "2025-06-10T00:43:58.056Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954, upload-time = "2025-06-10T00:43:59.773Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575, upload-time = "2025-06-10T00:44:02.051Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061, upload-time = "2025-06-10T00:44:04.196Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142, upload-time = "2025-06-10T00:44:06.527Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894, upload-time = "2025-06-10T00:44:08.379Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378, upload-time = "2025-06-10T00:44:10.51Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069, upload-time = "2025-06-10T00:44:12.834Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249, upload-time = "2025-06-10T00:44:14.731Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710, upload-time = "2025-06-10T00:44:16.716Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" }, ] [[package]] name = "yfinance" version = "0.2.65" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "curl-cffi" }, @@ -7601,107 +7644,107 @@ dependencies = [ { name = "requests" }, { name = "websockets" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a3/c1/2ef5acda45a71297f4be22e205359e0f93b0171f2b6ebdd681362e725686/yfinance-0.2.65.tar.gz", hash = "sha256:3d465e58c49be9d61f9862829de3e00bef6b623809f32f4efb5197b62fc60485" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/c1/2ef5acda45a71297f4be22e205359e0f93b0171f2b6ebdd681362e725686/yfinance-0.2.65.tar.gz", hash = "sha256:3d465e58c49be9d61f9862829de3e00bef6b623809f32f4efb5197b62fc60485", size = 128666, upload-time = "2025-07-06T16:20:12.769Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/c9/1e/631c80e0f97aef46eb73549b9b0f60d94057294e040740f4cad0cb1f48e4/yfinance-0.2.65-py2.py3-none-any.whl", hash = "sha256:7be13abb0d80a17230bf798e9c6a324fa2bef0846684a6d4f7fa2abd21938963" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/1e/631c80e0f97aef46eb73549b9b0f60d94057294e040740f4cad0cb1f48e4/yfinance-0.2.65-py2.py3-none-any.whl", hash = "sha256:7be13abb0d80a17230bf798e9c6a324fa2bef0846684a6d4f7fa2abd21938963", size = 119438, upload-time = "2025-07-06T16:20:11.251Z" }, ] [[package]] name = "zhipuai" version = "2.0.1" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cachetools" }, { name = "httpx" }, { name = "pydantic" }, { name = "pyjwt" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/a4/90/299e3456ee7ee1e118593552e03b86da2e9adaa0d454e467aeb4b22032a4/zhipuai-2.0.1.tar.gz", hash = "sha256:297bbdbe9393da2d1dc8066c39cf39bb2342f170d86f2b7b7a13ba368c53d701" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/90/299e3456ee7ee1e118593552e03b86da2e9adaa0d454e467aeb4b22032a4/zhipuai-2.0.1.tar.gz", hash = "sha256:297bbdbe9393da2d1dc8066c39cf39bb2342f170d86f2b7b7a13ba368c53d701", size = 16760, upload-time = "2024-01-16T11:44:07.936Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/8f/05/c3d4556886b5c6cf8c0b96eb80448ee8154c0dcc87086df018e817779ed4/zhipuai-2.0.1-py3-none-any.whl", hash = "sha256:738033d95696c3d5117dc4487e37d924e3ebbcdfa0072812b3f63a08ff72274a" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/05/c3d4556886b5c6cf8c0b96eb80448ee8154c0dcc87086df018e817779ed4/zhipuai-2.0.1-py3-none-any.whl", hash = "sha256:738033d95696c3d5117dc4487e37d924e3ebbcdfa0072812b3f63a08ff72274a", size = 26386, upload-time = "2024-01-16T11:44:05.803Z" }, ] [[package]] name = "zipp" version = "3.23.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ] [[package]] name = "zlib-state" version = "0.1.9" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/84/ee/d4a461b3f96ae5ddb3ab89a294075ecb7c28a28ff56be1fe8bd09f9c50b1/zlib_state-0.1.9.tar.gz", hash = "sha256:8baef0cd0ab9f9d556a35df3f57b8d0f8b4a49c3f028189ab401672939cf435d" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/ee/d4a461b3f96ae5ddb3ab89a294075ecb7c28a28ff56be1fe8bd09f9c50b1/zlib_state-0.1.9.tar.gz", hash = "sha256:8baef0cd0ab9f9d556a35df3f57b8d0f8b4a49c3f028189ab401672939cf435d", size = 9473, upload-time = "2024-09-05T20:21:21.653Z" } wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/56/84/0619cadedd1ae873353fc2d15873bcd9be1a2a5d2f6c100006e7bc483124/zlib_state-0.1.9-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97f45d0f80e9d7070229ecb36112eea6a17dc40053449a9c613ef837d9cb66b4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/84/75833ca8df9307aa098acca7ee1f959790b4e4f811242d5363908bd8c713/zlib_state-0.1.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3564eaa130f2533b87b82d0e622cfb5c25acec123e7bfe38d39db9ce6349cb52" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ed/38/910748b5f788d311f43d2dabc11f0bddc173d600e0ede7f1660d78f4efb0/zlib_state-0.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:0e633bd3fb65cd8c8f0fc5870cdd40354f218f815cc7a53fb525410251f06ab9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/47/be/22a0657de2e7ad230aec984935f980e77bd21706688a90ae2572de06e1d8/zlib_state-0.1.9-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bbfd191c908be2ba04319e57b4d166f9c625204c1a8c85d2eb968d9d7d14dcb" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ad/48/14617cfa8ec97309e2e3cabc42b3e106e9354775940dc96443e9e5f1e55c/zlib_state-0.1.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66dd680ef5c0d21fe1e673a4c68173feeda20f7933e3468c22c44d5960ebf621" }, - { url = "https://mirrors.aliyun.com/pypi/packages/62/1a/a111ef96419b7195a3a50604668fe56cb5894fdecc79befcef964da5e103/zlib_state-0.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:cdb7cdf2515d8c70c6a99a331bf8c1486b3bda77371e951961272cc9888494e1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/6c/ae/739524e41a73cb77565bcb14d30478b322bc109f6e79fc583572eb75f637/zlib_state-0.1.9-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22bc6ea28d1cbb717e7ba8254b12da5cff0820309d7ff46dba083d2dc44fd69" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c6/f8/87cbda2338b5254db486804f8ce802a47a870b3f8572e757d37bd1f3d122/zlib_state-0.1.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06ed845442af6fc8ad885037b1393c02ff1554638cd43ff8718ca1fb8999b7c7" }, - { url = "https://mirrors.aliyun.com/pypi/packages/99/88/cb175ba96b1b72b424b789151341206389b913bba4de2abffc6f767cb8cb/zlib_state-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:862b120477db67df4ad8af8c135fe134ae4051693d6a6abf1c208d9d1170d7d8" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/84/0619cadedd1ae873353fc2d15873bcd9be1a2a5d2f6c100006e7bc483124/zlib_state-0.1.9-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97f45d0f80e9d7070229ecb36112eea6a17dc40053449a9c613ef837d9cb66b4", size = 20259, upload-time = "2024-09-05T20:24:59.394Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/84/75833ca8df9307aa098acca7ee1f959790b4e4f811242d5363908bd8c713/zlib_state-0.1.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3564eaa130f2533b87b82d0e622cfb5c25acec123e7bfe38d39db9ce6349cb52", size = 21783, upload-time = "2024-09-05T20:25:00.924Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/38/910748b5f788d311f43d2dabc11f0bddc173d600e0ede7f1660d78f4efb0/zlib_state-0.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:0e633bd3fb65cd8c8f0fc5870cdd40354f218f815cc7a53fb525410251f06ab9", size = 12703, upload-time = "2024-09-05T20:22:21.162Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/be/22a0657de2e7ad230aec984935f980e77bd21706688a90ae2572de06e1d8/zlib_state-0.1.9-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bbfd191c908be2ba04319e57b4d166f9c625204c1a8c85d2eb968d9d7d14dcb", size = 20324, upload-time = "2024-09-05T20:25:01.817Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/48/14617cfa8ec97309e2e3cabc42b3e106e9354775940dc96443e9e5f1e55c/zlib_state-0.1.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66dd680ef5c0d21fe1e673a4c68173feeda20f7933e3468c22c44d5960ebf621", size = 21816, upload-time = "2024-09-05T20:25:02.62Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/1a/a111ef96419b7195a3a50604668fe56cb5894fdecc79befcef964da5e103/zlib_state-0.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:cdb7cdf2515d8c70c6a99a331bf8c1486b3bda77371e951961272cc9888494e1", size = 12704, upload-time = "2024-09-05T20:22:21.432Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/ae/739524e41a73cb77565bcb14d30478b322bc109f6e79fc583572eb75f637/zlib_state-0.1.9-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22bc6ea28d1cbb717e7ba8254b12da5cff0820309d7ff46dba083d2dc44fd69", size = 20601, upload-time = "2024-09-05T20:25:03.428Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/f8/87cbda2338b5254db486804f8ce802a47a870b3f8572e757d37bd1f3d122/zlib_state-0.1.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06ed845442af6fc8ad885037b1393c02ff1554638cd43ff8718ca1fb8999b7c7", size = 22288, upload-time = "2024-09-05T20:25:04.449Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/99/88/cb175ba96b1b72b424b789151341206389b913bba4de2abffc6f767cb8cb/zlib_state-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:862b120477db67df4ad8af8c135fe134ae4051693d6a6abf1c208d9d1170d7d8", size = 12734, upload-time = "2024-09-05T20:22:33.219Z" }, ] [[package]] name = "zstandard" version = "0.23.0" -source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, ] -sdist = { url = "https://mirrors.aliyun.com/pypi/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09" } -wheels = [ - { url = "https://mirrors.aliyun.com/pypi/packages/2a/55/bd0487e86679db1823fc9ee0d8c9c78ae2413d34c0b461193b5f4c31d22f/zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e1/8a/ccb516b684f3ad987dfee27570d635822e3038645b1a950c5e8022df1145/zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880" }, - { url = "https://mirrors.aliyun.com/pypi/packages/12/89/75e633d0611c028e0d9af6df199423bf43f54bea5007e6718ab7132e234c/zstandard-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77da4c6bfa20dd5ea25cbf12c76f181a8e8cd7ea231c673828d0386b1740b8dc" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/7a/bd7f6a21802de358b63f1ee636ab823711c25ce043a3e9f043b4fcb5ba32/zstandard-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2170c7e0367dde86a2647ed5b6f57394ea7f53545746104c6b09fc1f4223573" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/3b/775f851a4a65013e88ca559c8ae42ac1352db6fcd96b028d0df4d7d1d7b4/zstandard-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c16842b846a8d2a145223f520b7e18b57c8f476924bda92aeee3a88d11cfc391" }, - { url = "https://mirrors.aliyun.com/pypi/packages/09/4f/0cc49570141dd72d4d95dd6fcf09328d1b702c47a6ec12fbed3b8aed18a5/zstandard-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:157e89ceb4054029a289fb504c98c6a9fe8010f1680de0201b3eb5dc20aa6d9e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/7c/aaa7cd27148bae2dc095191529c0570d16058c54c4597a7d118de4b21676/zstandard-0.23.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:203d236f4c94cd8379d1ea61db2fce20730b4c38d7f1c34506a31b34edc87bdd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ac/eb/4b58b5c071d177f7dc027129d20bd2a44161faca6592a67f8fcb0b88b3ae/zstandard-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dc5d1a49d3f8262be192589a4b72f0d03b72dcf46c51ad5852a4fdc67be7b9e4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/44/f9/21a5fb9bb7c9a274b05ad700a82ad22ce82f7ef0f485980a1e98ed6e8c5f/zstandard-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:752bf8a74412b9892f4e5b58f2f890a039f57037f52c89a740757ebd807f33ea" }, - { url = "https://mirrors.aliyun.com/pypi/packages/49/74/b7b3e61db3f88632776b78b1db597af3f44c91ce17d533e14a25ce6a2816/zstandard-0.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80080816b4f52a9d886e67f1f96912891074903238fe54f2de8b786f86baded2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4a/7f/d8eb1cb123d8e4c541d4465167080bec88481ab54cd0b31eb4013ba04b95/zstandard-0.23.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84433dddea68571a6d6bd4fbf8ff398236031149116a7fff6f777ff95cad3df9" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5e/05/f7dccdf3d121309b60342da454d3e706453a31073e2c4dac8e1581861e44/zstandard-0.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19a2d91963ed9e42b4e8d77cd847ae8381576585bad79dbd0a8837a9f6620a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/86/9d/3677a02e172dccd8dd3a941307621c0cbd7691d77cb435ac3c75ab6a3105/zstandard-0.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:59556bf80a7094d0cfb9f5e50bb2db27fefb75d5138bb16fb052b61b0e0eeeb0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/41/7e/0012a02458e74a7ba122cd9cafe491facc602c9a17f590367da369929498/zstandard-0.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27d3ef2252d2e62476389ca8f9b0cf2bbafb082a3b6bfe9d90cbcbb5529ecf7c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/65/3a/8f715b97bd7bcfc7342d8adcd99a026cb2fb550e44866a3b6c348e1b0f02/zstandard-0.23.0-cp310-cp310-win32.whl", hash = "sha256:5d41d5e025f1e0bccae4928981e71b2334c60f580bdc8345f824e7c0a4c2a813" }, - { url = "https://mirrors.aliyun.com/pypi/packages/19/b7/b2b9eca5e5a01111e4fe8a8ffb56bdcdf56b12448a24effe6cfe4a252034/zstandard-0.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:519fbf169dfac1222a76ba8861ef4ac7f0530c35dd79ba5727014613f91613d4" }, - { url = "https://mirrors.aliyun.com/pypi/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23" }, - { url = "https://mirrors.aliyun.com/pypi/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2" }, - { url = "https://mirrors.aliyun.com/pypi/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48" }, - { url = "https://mirrors.aliyun.com/pypi/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c" }, - { url = "https://mirrors.aliyun.com/pypi/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003" }, - { url = "https://mirrors.aliyun.com/pypi/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78" }, - { url = "https://mirrors.aliyun.com/pypi/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473" }, - { url = "https://mirrors.aliyun.com/pypi/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160" }, - { url = "https://mirrors.aliyun.com/pypi/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0" }, - { url = "https://mirrors.aliyun.com/pypi/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094" }, - { url = "https://mirrors.aliyun.com/pypi/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8" }, - { url = "https://mirrors.aliyun.com/pypi/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1" }, - { url = "https://mirrors.aliyun.com/pypi/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072" }, - { url = "https://mirrors.aliyun.com/pypi/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20" }, - { url = "https://mirrors.aliyun.com/pypi/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373" }, - { url = "https://mirrors.aliyun.com/pypi/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db" }, - { url = "https://mirrors.aliyun.com/pypi/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772" }, - { url = "https://mirrors.aliyun.com/pypi/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105" }, - { url = "https://mirrors.aliyun.com/pypi/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba" }, - { url = "https://mirrors.aliyun.com/pypi/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd" }, - { url = "https://mirrors.aliyun.com/pypi/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a" }, - { url = "https://mirrors.aliyun.com/pypi/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90" }, - { url = "https://mirrors.aliyun.com/pypi/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35" }, - { url = "https://mirrors.aliyun.com/pypi/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d" }, - { url = "https://mirrors.aliyun.com/pypi/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b" }, +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701, upload-time = "2024-07-15T00:18:06.141Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/55/bd0487e86679db1823fc9ee0d8c9c78ae2413d34c0b461193b5f4c31d22f/zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9", size = 788701, upload-time = "2024-07-15T00:13:27.351Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/8a/ccb516b684f3ad987dfee27570d635822e3038645b1a950c5e8022df1145/zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880", size = 633678, upload-time = "2024-07-15T00:13:30.24Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/89/75e633d0611c028e0d9af6df199423bf43f54bea5007e6718ab7132e234c/zstandard-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77da4c6bfa20dd5ea25cbf12c76f181a8e8cd7ea231c673828d0386b1740b8dc", size = 4941098, upload-time = "2024-07-15T00:13:32.526Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/7a/bd7f6a21802de358b63f1ee636ab823711c25ce043a3e9f043b4fcb5ba32/zstandard-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2170c7e0367dde86a2647ed5b6f57394ea7f53545746104c6b09fc1f4223573", size = 5308798, upload-time = "2024-07-15T00:13:34.925Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/3b/775f851a4a65013e88ca559c8ae42ac1352db6fcd96b028d0df4d7d1d7b4/zstandard-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c16842b846a8d2a145223f520b7e18b57c8f476924bda92aeee3a88d11cfc391", size = 5341840, upload-time = "2024-07-15T00:13:37.376Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/4f/0cc49570141dd72d4d95dd6fcf09328d1b702c47a6ec12fbed3b8aed18a5/zstandard-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:157e89ceb4054029a289fb504c98c6a9fe8010f1680de0201b3eb5dc20aa6d9e", size = 5440337, upload-time = "2024-07-15T00:13:39.772Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/7c/aaa7cd27148bae2dc095191529c0570d16058c54c4597a7d118de4b21676/zstandard-0.23.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:203d236f4c94cd8379d1ea61db2fce20730b4c38d7f1c34506a31b34edc87bdd", size = 4861182, upload-time = "2024-07-15T00:13:42.495Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/eb/4b58b5c071d177f7dc027129d20bd2a44161faca6592a67f8fcb0b88b3ae/zstandard-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dc5d1a49d3f8262be192589a4b72f0d03b72dcf46c51ad5852a4fdc67be7b9e4", size = 4932936, upload-time = "2024-07-15T00:13:44.234Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/f9/21a5fb9bb7c9a274b05ad700a82ad22ce82f7ef0f485980a1e98ed6e8c5f/zstandard-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:752bf8a74412b9892f4e5b58f2f890a039f57037f52c89a740757ebd807f33ea", size = 5464705, upload-time = "2024-07-15T00:13:46.822Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/74/b7b3e61db3f88632776b78b1db597af3f44c91ce17d533e14a25ce6a2816/zstandard-0.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80080816b4f52a9d886e67f1f96912891074903238fe54f2de8b786f86baded2", size = 4857882, upload-time = "2024-07-15T00:13:49.297Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4a/7f/d8eb1cb123d8e4c541d4465167080bec88481ab54cd0b31eb4013ba04b95/zstandard-0.23.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84433dddea68571a6d6bd4fbf8ff398236031149116a7fff6f777ff95cad3df9", size = 4697672, upload-time = "2024-07-15T00:13:51.447Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/05/f7dccdf3d121309b60342da454d3e706453a31073e2c4dac8e1581861e44/zstandard-0.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19a2d91963ed9e42b4e8d77cd847ae8381576585bad79dbd0a8837a9f6620a", size = 5206043, upload-time = "2024-07-15T00:13:53.587Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/9d/3677a02e172dccd8dd3a941307621c0cbd7691d77cb435ac3c75ab6a3105/zstandard-0.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:59556bf80a7094d0cfb9f5e50bb2db27fefb75d5138bb16fb052b61b0e0eeeb0", size = 5667390, upload-time = "2024-07-15T00:13:56.137Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/7e/0012a02458e74a7ba122cd9cafe491facc602c9a17f590367da369929498/zstandard-0.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27d3ef2252d2e62476389ca8f9b0cf2bbafb082a3b6bfe9d90cbcbb5529ecf7c", size = 5198901, upload-time = "2024-07-15T00:13:58.584Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/65/3a/8f715b97bd7bcfc7342d8adcd99a026cb2fb550e44866a3b6c348e1b0f02/zstandard-0.23.0-cp310-cp310-win32.whl", hash = "sha256:5d41d5e025f1e0bccae4928981e71b2334c60f580bdc8345f824e7c0a4c2a813", size = 430596, upload-time = "2024-07-15T00:14:00.693Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/b7/b2b9eca5e5a01111e4fe8a8ffb56bdcdf56b12448a24effe6cfe4a252034/zstandard-0.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:519fbf169dfac1222a76ba8861ef4ac7f0530c35dd79ba5727014613f91613d4", size = 495498, upload-time = "2024-07-15T00:14:02.741Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699, upload-time = "2024-07-15T00:14:04.909Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681, upload-time = "2024-07-15T00:14:13.99Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328, upload-time = "2024-07-15T00:14:16.588Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955, upload-time = "2024-07-15T00:14:19.389Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944, upload-time = "2024-07-15T00:14:22.173Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927, upload-time = "2024-07-15T00:14:24.825Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910, upload-time = "2024-07-15T00:14:26.982Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544, upload-time = "2024-07-15T00:14:29.582Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094, upload-time = "2024-07-15T00:14:40.126Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440, upload-time = "2024-07-15T00:14:42.786Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091, upload-time = "2024-07-15T00:14:45.184Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682, upload-time = "2024-07-15T00:14:47.407Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707, upload-time = "2024-07-15T00:15:03.529Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792, upload-time = "2024-07-15T00:15:28.372Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586, upload-time = "2024-07-15T00:15:32.26Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420, upload-time = "2024-07-15T00:15:34.004Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713, upload-time = "2024-07-15T00:15:35.815Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459, upload-time = "2024-07-15T00:15:37.995Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707, upload-time = "2024-07-15T00:15:39.872Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545, upload-time = "2024-07-15T00:15:41.75Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533, upload-time = "2024-07-15T00:15:44.114Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510, upload-time = "2024-07-15T00:15:46.509Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973, upload-time = "2024-07-15T00:15:49.939Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968, upload-time = "2024-07-15T00:15:52.025Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179, upload-time = "2024-07-15T00:15:54.971Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577, upload-time = "2024-07-15T00:15:57.634Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899, upload-time = "2024-07-15T00:16:00.811Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964, upload-time = "2024-07-15T00:16:03.669Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398, upload-time = "2024-07-15T00:16:06.694Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313, upload-time = "2024-07-15T00:16:09.758Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877, upload-time = "2024-07-15T00:16:11.758Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595, upload-time = "2024-07-15T00:16:13.731Z" }, ] From fc46d6bb87195086c021519cdc0c0f5a49e6d482 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 10 Oct 2025 13:10:18 +0800 Subject: [PATCH 0789/1187] Fix: Fixed the issue where the operator added by clicking the plus sign in the data flow would overlap with the original section #9886 (#10458) ### What problem does this PR solve? Fix: Fixed the issue where the operator added by clicking the plus sign in the data flow would overlap with the original section #9886 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/home-card.tsx | 2 +- web/src/pages/agent/hooks/use-add-node.ts | 1 + web/src/pages/data-flow/canvas/index.tsx | 8 +++- .../node/dropdown/next-step-dropdown.tsx | 46 ++++++++++--------- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/web/src/components/home-card.tsx b/web/src/components/home-card.tsx index ff355dae2a4..76b16a400d8 100644 --- a/web/src/components/home-card.tsx +++ b/web/src/components/home-card.tsx @@ -41,7 +41,7 @@ export function HomeCard({
    -
    +
    {data.name}
    {icon} diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index e679c417197..58f487921f0 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -116,6 +116,7 @@ export const useInitializeOperatorParams = () => { [Operator.UserFillUp]: initialUserFillUpValues, [Operator.StringTransform]: initialStringTransformValues, [Operator.TavilyExtract]: initialTavilyExtractValues, + [Operator.Placeholder]: {}, }; }, [llmId]); diff --git a/web/src/pages/data-flow/canvas/index.tsx b/web/src/pages/data-flow/canvas/index.tsx index edc998a699b..07410897c04 100644 --- a/web/src/pages/data-flow/canvas/index.tsx +++ b/web/src/pages/data-flow/canvas/index.tsx @@ -178,11 +178,17 @@ function DataFlowCanvas({ drawerVisible, hideDrawer, showLogSheet }: IProps) { }; const onConnectEnd: OnConnectEnd = (event, connectionState) => { + const target = event.target as HTMLElement; const nodeId = connectionState.fromNode?.id; + // Events triggered by Handle are directly interrupted - if (connectionState.toNode !== null || (nodeId && hasChildNode(nodeId))) { + if ( + target?.classList.contains('react-flow__handle') || + (nodeId && hasChildNode(nodeId)) + ) { return; } + if ('clientX' in event && 'clientY' in event) { const { clientX, clientY } = event; setDropdownPosition({ x: clientX, y: clientY }); diff --git a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx index 2e9c95f262e..6beadb7e810 100644 --- a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx @@ -52,30 +52,32 @@ function OperatorItemList({ const getNodeName = useGetNodeName(); const getNodeDescription = useGetNodeDescription(); - const handleClick = (operator: Operator) => { - const contextData = handleContext || { - nodeId: '', - id: '', - type: 'source' as const, - position: Position.Right, - isFromConnectionDrag: true, - }; + const handleClick = + (operator: Operator): React.MouseEventHandler => + (e) => { + const contextData = handleContext || { + nodeId: '', + id: '', + type: 'source' as const, + position: Position.Right, + isFromConnectionDrag: true, + }; - const mockEvent = mousePosition - ? { - clientX: mousePosition.x, - clientY: mousePosition.y, - } - : undefined; + const mockEvent = mousePosition + ? { + clientX: mousePosition.x, + clientY: mousePosition.y, + } + : e; - const newNodeId = addCanvasNode(operator, contextData)(mockEvent); + const newNodeId = addCanvasNode(operator, contextData)(mockEvent); - if (onNodeCreated && newNodeId) { - onNodeCreated(newNodeId); - } + if (onNodeCreated && newNodeId) { + onNodeCreated(newNodeId); + } - hideModal?.(); - }; + hideModal?.(); + }; const renderOperatorItem = (operator: Operator) => { const commonContent = ( @@ -89,12 +91,12 @@ function OperatorItemList({ {isCustomDropdown ? ( -
  • handleClick(operator)}>{commonContent}
  • +
  • {commonContent}
  • ) : ( handleClick(operator)} + onClick={handleClick(operator)} onSelect={() => hideModal?.()} > From f35c5ed11959977ac05978bafb98839dec0462f2 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 10 Oct 2025 16:30:13 +0800 Subject: [PATCH 0790/1187] Fix: Fixed an issue where parser configurations could be added infinitely #9869 (#10464) ### What problem does this PR solve? Fix: Fixed an issue where parser configurations could be added infinitely #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/home-card.tsx | 6 +-- web/src/layouts/next-header.tsx | 2 +- web/src/locales/en.ts | 4 +- web/src/locales/zh.ts | 2 + .../form/parser-form/common-form-fields.tsx | 17 ++++++-- .../data-flow/form/parser-form/index.tsx | 43 ++++++++++++------- .../data-flow/form/tokenizer-form/index.tsx | 1 + web/src/pages/data-flow/index.tsx | 4 +- .../data-flow/log-sheet/dataflow-timeline.tsx | 2 +- web/src/pages/data-flow/log-sheet/index.tsx | 42 +++++++++--------- web/src/pages/home/banner.tsx | 2 +- web/src/pages/home/index.tsx | 14 +++--- 12 files changed, 83 insertions(+), 56 deletions(-) diff --git a/web/src/components/home-card.tsx b/web/src/components/home-card.tsx index 76b16a400d8..91eecf17800 100644 --- a/web/src/components/home-card.tsx +++ b/web/src/components/home-card.tsx @@ -39,9 +39,9 @@ export function HomeCard({ />
    -
    -
    -
    +
    +
    +
    {data.name}
    {icon} diff --git a/web/src/layouts/next-header.tsx b/web/src/layouts/next-header.tsx index 26cce2ba02d..367449540f8 100644 --- a/web/src/layouts/next-header.tsx +++ b/web/src/layouts/next-header.tsx @@ -106,7 +106,7 @@ export function Header() { }, [navigate]); return ( -
    +
    >((pre, [key, value]) => { pre[key] = Object.values(value).map((v) => ({ - label: v === PdfOutputFormat.Json ? 'JSON' : upperFirst(v), + label: UppercaseFields.some((x) => x === v) + ? upperCase(v) + : upperFirst(v), value: v, })); return pre; diff --git a/web/src/pages/data-flow/form/parser-form/index.tsx b/web/src/pages/data-flow/form/parser-form/index.tsx index 836f5414e8a..cfb06e06dc5 100644 --- a/web/src/pages/data-flow/form/parser-form/index.tsx +++ b/web/src/pages/data-flow/form/parser-form/index.tsx @@ -1,4 +1,7 @@ -import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { + SelectWithSearch, + SelectWithSearchFlagOptionType, +} from '@/components/originui/select-with-search'; import { RAGFlowFormItem } from '@/components/ragflow-form'; import { BlockButton, Button } from '@/components/ui/button'; import { Form } from '@/components/ui/form'; @@ -49,6 +52,7 @@ type ParserItemProps = { index: number; fieldLength: number; remove: UseFieldArrayRemove; + fileFormatOptions: SelectWithSearchFlagOptionType[]; }; export const FormSchema = z.object({ @@ -67,7 +71,13 @@ export const FormSchema = z.object({ export type ParserFormSchemaType = z.infer; -function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) { +function ParserItem({ + name, + index, + fieldLength, + remove, + fileFormatOptions, +}: ParserItemProps) { const { t } = useTranslation(); const form = useFormContext(); const ref = useRef(null); @@ -79,23 +89,15 @@ function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) { const values = form.getValues(); const parserList = values.setups.slice(); // Adding, deleting, or modifying the parser array will not change the reference. - const FileFormatOptions = buildOptions( - FileType, - t, - 'dataflow.fileFormatOptions', - ).filter( - (x) => x.value !== FileType.Video, // Temporarily hide the video option - ); - const filteredFileFormatOptions = useMemo(() => { const otherFileFormatList = parserList .filter((_, idx) => idx !== index) .map((x) => x.fileFormat); - return FileFormatOptions.filter((x) => { + return fileFormatOptions.filter((x) => { return !otherFileFormatList.includes(x.value); }); - }, [FileFormatOptions, index, parserList]); + }, [fileFormatOptions, index, parserList]); const Widget = typeof fileFormat === 'string' && fileFormat in FileFormatWidgetMap @@ -158,6 +160,14 @@ const ParserForm = ({ node }: INextOperatorForm) => { const { t } = useTranslation(); const defaultValues = useFormValues(initialParserValues, node); + const FileFormatOptions = buildOptions( + FileType, + t, + 'dataflow.fileFormatOptions', + ).filter( + (x) => x.value !== FileType.Video, // Temporarily hide the video option + ); + const form = useForm>({ defaultValues, resolver: zodResolver(FormSchema), @@ -194,12 +204,15 @@ const ParserForm = ({ node }: INextOperatorForm) => { index={index} fieldLength={fields.length} remove={remove} + fileFormatOptions={FileFormatOptions} > ); })} - - {t('dataflow.addParser')} - + {fields.length < FileFormatOptions.length && ( + + {t('dataflow.addParser')} + + )}
    diff --git a/web/src/pages/data-flow/form/tokenizer-form/index.tsx b/web/src/pages/data-flow/form/tokenizer-form/index.tsx index 1b4975db96f..cc949d5cdc0 100644 --- a/web/src/pages/data-flow/form/tokenizer-form/index.tsx +++ b/web/src/pages/data-flow/form/tokenizer-form/index.tsx @@ -58,6 +58,7 @@ const TokenizerForm = ({ node }: INextOperatorForm) => { {(field) => ( - + {isParsing || running ? t('dataflow.running') : t('flow.run')} diff --git a/web/src/pages/data-flow/log-sheet/dataflow-timeline.tsx b/web/src/pages/data-flow/log-sheet/dataflow-timeline.tsx index 25787a2e4d7..e15465baf9a 100644 --- a/web/src/pages/data-flow/log-sheet/dataflow-timeline.tsx +++ b/web/src/pages/data-flow/log-sheet/dataflow-timeline.tsx @@ -78,7 +78,7 @@ export function DataflowTimeline({ traceList }: DataflowTimelineProps) {
    - {progress}% + {progress.toFixed(2)}%
    diff --git a/web/src/pages/data-flow/log-sheet/index.tsx b/web/src/pages/data-flow/log-sheet/index.tsx index a5bcb2787a9..546d2832872 100644 --- a/web/src/pages/data-flow/log-sheet/index.tsx +++ b/web/src/pages/data-flow/log-sheet/index.tsx @@ -55,10 +55,10 @@ export function LogSheet({ return ( e.preventDefault()} > - + {t('flow.log')} {isCompleted && ( @@ -82,30 +82,32 @@ export function LogSheet({ )} -
    +
    {isLogEmpty ? ( ) : ( )}
    - {isParsing ? ( - - ) : ( - - )} +
    + {isParsing ? ( + + ) : ( + + )} +
    ); diff --git a/web/src/pages/home/banner.tsx b/web/src/pages/home/banner.tsx index 3173a40ba92..973e417aabc 100644 --- a/web/src/pages/home/banner.tsx +++ b/web/src/pages/home/banner.tsx @@ -42,7 +42,7 @@ export function Banner() { export function NextBanner() { const { t } = useTranslation(); return ( -
    +
    {t('header.welcome')} RAGFlow diff --git a/web/src/pages/home/index.tsx b/web/src/pages/home/index.tsx index 54b2f83d52a..137f50ec502 100644 --- a/web/src/pages/home/index.tsx +++ b/web/src/pages/home/index.tsx @@ -4,15 +4,13 @@ import { Datasets } from './datasets'; const Home = () => { return ( -
    -
    - -
    - - -
    +
    + +
    + +
    -
    +
    ); }; From 5d167cd77275b670bdf72b99d9bf3136814339a4 Mon Sep 17 00:00:00 2001 From: buua436 <66937541+buua436@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:38:04 +0800 Subject: [PATCH 0791/1187] feat: support qwq reasoning models with non-stream output (#10468) ### What problem does this PR solve? issue: [#6193](https://github.com/infiniflow/ragflow/issues/6193) change: support qwq reasoning models with non-stream output ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- rag/llm/chat_model.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 81e1a345933..dd088c83b1a 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -142,6 +142,22 @@ def _clean_conf(self, gen_conf): def _chat(self, history, gen_conf, **kwargs): logging.info("[HISTORY]" + json.dumps(history, ensure_ascii=False, indent=2)) + if self.model_name.lower().find("qwq") >= 0: + logging.info(f"[INFO] {self.model_name} detected as reasoning model, using _chat_streamly") + + final_ans = "" + tol_token = 0 + for delta, tol in self._chat_streamly(history, gen_conf, with_reasoning=False, **kwargs): + if delta.startswith("") or delta.endswith(""): + continue + final_ans += delta + tol_token = tol + + if len(final_ans.strip()) == 0: + final_ans = "**ERROR**: Empty response from reasoning model" + + return final_ans.strip(), tol_token + if self.model_name.lower().find("qwen3") >= 0: kwargs["extra_body"] = {"enable_thinking": False} From 0d8791936ee19b884de6a727c17ab28da7040ce1 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Fri, 10 Oct 2025 17:07:55 +0800 Subject: [PATCH 0792/1187] Feat: TOC retrieval (#10456) ### What problem does this PR solve? #10436 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- agent/tools/retrieval.py | 6 ++ api/db/services/dialog_service.py | 4 + api/utils/api_utils.py | 6 +- deepdoc/parser/pdf_parser.py | 2 +- graphrag/utils.py | 11 +-- rag/flow/parser/parser.py | 6 +- rag/llm/chat_model.py | 3 +- rag/nlp/search.py | 62 +++++++++++++++ rag/prompts/assign_toc_levels.md | 32 ++++---- rag/prompts/generator.py | 113 +++++++++++++++++++++------- rag/prompts/toc_from_text_system.md | 62 ++++++++------- rag/svr/task_executor.py | 34 ++++++++- 12 files changed, 251 insertions(+), 90 deletions(-) diff --git a/agent/tools/retrieval.py b/agent/tools/retrieval.py index 07c16d97dbd..e2e24ea3599 100644 --- a/agent/tools/retrieval.py +++ b/agent/tools/retrieval.py @@ -57,6 +57,7 @@ def __init__(self): self.empty_response = "" self.use_kg = False self.cross_languages = [] + self.toc_enhance = False def check(self): self.check_decimal_float(self.similarity_threshold, "[Retrieval] Similarity threshold") @@ -134,6 +135,11 @@ def _invoke(self, **kwargs): rerank_mdl=rerank_mdl, rank_feature=label_question(query, kbs), ) + if self._param.toc_enhance: + chat_mdl = LLMBundle(self._canvas._tenant_id, LLMType.CHAT) + cks = settings.retriever.retrieval_by_toc(query, kbinfos["chunks"], [kb.tenant_id for kb in kbs], chat_mdl, self._param.top_n) + if cks: + kbinfos["chunks"] = cks if self._param.use_kg: ck = settings.kg_retriever.retrieval(query, [kb.tenant_id for kb in kbs], diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index a8ddf178d23..9bde6238d00 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -466,6 +466,10 @@ def chat(dialog, messages, stream=True, **kwargs): rerank_mdl=rerank_mdl, rank_feature=label_question(" ".join(questions), kbs), ) + if prompt_config.get("toc_enhance"): + cks = retriever.retrieval_by_toc(" ".join(questions), kbinfos["chunks"], tenant_ids, chat_mdl, dialog.top_n) + if cks: + kbinfos["chunks"] = cks if prompt_config.get("tavily_api_key"): tav = Tavily(prompt_config["tavily_api_key"]) tav_res = tav.retrieve_chunks(" ".join(questions)) diff --git a/api/utils/api_utils.py b/api/utils/api_utils.py index 8730dff4db4..1579e852bf7 100644 --- a/api/utils/api_utils.py +++ b/api/utils/api_utils.py @@ -51,9 +51,6 @@ from api.constants import REQUEST_MAX_WAIT_SEC, REQUEST_WAIT_SEC from api.db import ActiveEnum from api.db.db_models import APIToken -from api.db.services import UserService -from api.db.services.llm_service import LLMService -from api.db.services.tenant_llm_service import TenantLLMService from api.utils.json import CustomJSONEncoder, json_dumps from api.utils import get_uuid from rag.utils.mcp_tool_call_conn import MCPToolCallSession, close_multiple_mcp_toolcall_sessions @@ -239,6 +236,7 @@ def wrapper(*args, **kwargs): def active_required(f): @wraps(f) def wrapper(*args, **kwargs): + from api.db.services import UserService user_id = current_user.id usr = UserService.filter_by_id(user_id) # check is_active @@ -544,6 +542,8 @@ def check_duplicate_ids(ids, id_type="item"): def verify_embedding_availability(embd_id: str, tenant_id: str) -> tuple[bool, Response | None]: + from api.db.services.llm_service import LLMService + from api.db.services.tenant_llm_service import TenantLLMService """ Verifies availability of an embedding model for a specific tenant. diff --git a/deepdoc/parser/pdf_parser.py b/deepdoc/parser/pdf_parser.py index c73b610ade5..2cf14b88a27 100644 --- a/deepdoc/parser/pdf_parser.py +++ b/deepdoc/parser/pdf_parser.py @@ -1048,7 +1048,7 @@ def __call__(self, fnm, need_image=True, zoomin=3, return_html=False): def parse_into_bboxes(self, fnm, callback=None, zoomin=3): start = timer() - self.__images__(fnm, zoomin) + self.__images__(fnm, zoomin, callback=callback) if callback: callback(0.40, "OCR finished ({:.2f}s)".format(timer() - start)) diff --git a/graphrag/utils.py b/graphrag/utils.py index 5e64cdb11f7..877380d6a0a 100644 --- a/graphrag/utils.py +++ b/graphrag/utils.py @@ -92,10 +92,7 @@ def dict_has_keys_with_types(data: dict, expected_fields: list[tuple[str, type]] def get_llm_cache(llmnm, txt, history, genconf): hasher = xxhash.xxh64() - hasher.update(str(llmnm).encode("utf-8")) - hasher.update(str(txt).encode("utf-8")) - hasher.update(str(history).encode("utf-8")) - hasher.update(str(genconf).encode("utf-8")) + hasher.update((str(llmnm)+str(txt)+str(history)+str(genconf)).encode("utf-8")) k = hasher.hexdigest() bin = REDIS_CONN.get(k) @@ -106,11 +103,7 @@ def get_llm_cache(llmnm, txt, history, genconf): def set_llm_cache(llmnm, txt, v, history, genconf): hasher = xxhash.xxh64() - hasher.update(str(llmnm).encode("utf-8")) - hasher.update(str(txt).encode("utf-8")) - hasher.update(str(history).encode("utf-8")) - hasher.update(str(genconf).encode("utf-8")) - + hasher.update((str(llmnm)+str(txt)+str(history)+str(genconf)).encode("utf-8")) k = hasher.hexdigest() REDIS_CONN.set(k, v.encode("utf-8"), 24 * 3600) diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py index 424b2d6fa4a..e08629f3df4 100644 --- a/rag/flow/parser/parser.py +++ b/rag/flow/parser/parser.py @@ -366,6 +366,7 @@ def _email(self, name, blob): email_content = {} conf = self._param.setups["email"] + self.set_output("output_format", conf["output_format"]) target_fields = conf["fields"] _, ext = os.path.splitext(name) @@ -442,8 +443,9 @@ def _add_content(m, content_type): } # get body if "body" in target_fields: - email_content["text"] = msg.body # usually empty. try text_html instead - email_content["text_html"] = msg.htmlBody + email_content["text"] = msg.body[0] if isinstance(msg.body, list) and msg.body else msg.body + if not email_content["text"] and msg.htmlBody: + email_content["text"] = msg.htmlBody[0] if isinstance(msg.htmlBody, list) and msg.htmlBody else msg.htmlBody # get attachments if "attachments" in target_fields: attachments = [] diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index dd088c83b1a..2a77a45ebab 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -132,8 +132,7 @@ def _clean_conf(self, gen_conf): "tool_choice", "logprobs", "top_logprobs", - "extra_headers", - "enable_thinking" + "extra_headers" } gen_conf = {k: v for k, v in gen_conf.items() if k in allowed_conf} diff --git a/rag/nlp/search.py b/rag/nlp/search.py index db1423095aa..89c9d5bfca5 100644 --- a/rag/nlp/search.py +++ b/rag/nlp/search.py @@ -13,12 +13,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json import logging import re import math from collections import OrderedDict from dataclasses import dataclass +from rag.prompts.generator import relevant_chunks_with_toc from rag.settings import TAG_FLD, PAGERANK_FLD from rag.utils import rmSpace, get_float from rag.nlp import rag_tokenizer, query @@ -514,3 +516,63 @@ def tag_query(self, question: str, tenant_ids: str | list[str], kb_ids: list[str tag_fea = sorted([(a, round(0.1*(c + 1) / (cnt + S) / max(1e-6, all_tags.get(a, 0.0001)))) for a, c in aggs], key=lambda x: x[1] * -1)[:topn_tags] return {a.replace(".", "_"): max(1, c) for a, c in tag_fea} + + def retrieval_by_toc(self, query:str, chunks:list[dict], tenant_ids:list[str], chat_mdl, topn: int=6): + if not chunks: + return [] + idx_nms = [index_name(tid) for tid in tenant_ids] + ranks, doc_id2kb_id = {}, {} + for ck in chunks: + if ck["doc_id"] not in ranks: + ranks[ck["doc_id"]] = 0 + ranks[ck["doc_id"]] += ck["similarity"] + doc_id2kb_id[ck["doc_id"]] = ck["kb_id"] + doc_id = sorted(ranks.items(), key=lambda x: x[1]*-1.)[0][0] + kb_ids = [doc_id2kb_id[doc_id]] + es_res = self.dataStore.search(["content_with_weight"], [], {"doc_id": doc_id, "toc_kwd": "toc"}, [], OrderByExpr(), 0, 128, idx_nms, + kb_ids) + toc = [] + dict_chunks = self.dataStore.getFields(es_res, ["content_with_weight"]) + for _, doc in dict_chunks.items(): + try: + toc.extend(json.loads(doc["content_with_weight"])) + except Exception as e: + logging.exception(e) + if not toc: + return chunks + + ids = relevant_chunks_with_toc(query, toc, chat_mdl, topn*2) + if not ids: + return chunks + + vector_size = 1024 + id2idx = {ck["chunk_id"]: i for i, ck in enumerate(chunks)} + for cid, sim in ids: + if cid in id2idx: + chunks[id2idx[cid]]["similarity"] += sim + continue + chunk = self.dataStore.get(cid, idx_nms, kb_ids) + d = { + "chunk_id": cid, + "content_ltks": chunk["content_ltks"], + "content_with_weight": chunk["content_with_weight"], + "doc_id": doc_id, + "docnm_kwd": chunk.get("docnm_kwd", ""), + "kb_id": chunk["kb_id"], + "important_kwd": chunk.get("important_kwd", []), + "image_id": chunk.get("img_id", ""), + "similarity": sim, + "vector_similarity": sim, + "term_similarity": sim, + "vector": [0.0] * vector_size, + "positions": chunk.get("position_int", []), + "doc_type_kwd": chunk.get("doc_type_kwd", "") + } + for k in chunk.keys(): + if k[-4:] == "_vec": + d["vector"] = chunk[k] + vector_size = len(chunk[k]) + break + chunks.append(d) + + return sorted(chunks, key=lambda x:x["similarity"]*-1)[:topn] diff --git a/rag/prompts/assign_toc_levels.md b/rag/prompts/assign_toc_levels.md index fff0cd8b3de..d35dee7791f 100644 --- a/rag/prompts/assign_toc_levels.md +++ b/rag/prompts/assign_toc_levels.md @@ -1,4 +1,4 @@ -You are given a JSON array of TOC items. Each item has at least {"title": string} and may include an existing structure. +You are given a JSON array of TOC(tabel of content) items. Each item has at least {"title": string} and may include an existing title hierarchical level. Task - For each item, assign a depth label using Arabic numerals only: top-level = 1, second-level = 2, third-level = 3, etc. @@ -9,7 +9,7 @@ Task Output - Return a valid JSON array only (no extra text). -- Each element must be {"structure": "1|2|3", "title": }. +- Each element must be {"level": "1|2|3", "title": }. - title must be the original title string. Examples @@ -20,10 +20,10 @@ Input: Output: [ - {"structure":"1","title":"Chapter 1 Methods"}, - {"structure":"2","title":"Section 1 Definition"}, - {"structure":"2","title":"Section 2 Process"}, - {"structure":"1","title":"Chapter 2 Experiment"} + {"level":"1","title":"Chapter 1 Methods"}, + {"level":"2","title":"Section 1 Definition"}, + {"level":"2","title":"Section 2 Process"}, + {"level":"1","title":"Chapter 2 Experiment"} ] Example B (parts with chapters) @@ -32,11 +32,11 @@ Input: Output: [ - {"structure":"1","title":"Part I Theory"}, - {"structure":"2","title":"Chapter 1 Basics"}, - {"structure":"2","title":"Chapter 2 Methods"}, - {"structure":"1","title":"Part II Applications"}, - {"structure":"2","title":"Chapter 3 Case Studies"} + {"level":"1","title":"Part I Theory"}, + {"level":"2","title":"Chapter 1 Basics"}, + {"level":"2","title":"Chapter 2 Methods"}, + {"level":"1","title":"Part II Applications"}, + {"level":"2","title":"Chapter 3 Case Studies"} ] Example C (plain headings) @@ -45,9 +45,9 @@ Input: Output: [ - {"structure":"1","title":"Introduction"}, - {"structure":"2","title":"Background and Motivation"}, - {"structure":"2","title":"Related Work"}, - {"structure":"1","title":"Methodology"}, - {"structure":"1","title":"Evaluation"} + {"level":"1","title":"Introduction"}, + {"level":"2","title":"Background and Motivation"}, + {"level":"2","title":"Related Work"}, + {"level":"1","title":"Methodology"}, + {"level":"1","title":"Evaluation"} ] \ No newline at end of file diff --git a/rag/prompts/generator.py b/rag/prompts/generator.py index fa812c1fff9..73e1201f567 100644 --- a/rag/prompts/generator.py +++ b/rag/prompts/generator.py @@ -21,7 +21,9 @@ from typing import Tuple import jinja2 import json_repair +import trio from api.utils import hash_str2int +from rag.nlp import is_chinese from rag.prompts.template import load_prompt from rag.settings import TAG_FLD from rag.utils import encoder, num_tokens_from_string @@ -440,11 +442,17 @@ def gen_meta_filter(chat_mdl, meta_data:dict, query: str) -> list: def gen_json(system_prompt:str, user_prompt:str, chat_mdl, gen_conf = None): + from graphrag.utils import get_llm_cache, set_llm_cache + cached = get_llm_cache(chat_mdl.llm_name, system_prompt, user_prompt, gen_conf) + if cached: + return json_repair.loads(cached) _, msg = message_fit_in(form_message(system_prompt, user_prompt), chat_mdl.max_length) ans = chat_mdl.chat(msg[0]["content"], msg[1:],gen_conf=gen_conf) ans = re.sub(r"(^.*|```json\n|```\n*$)", "", ans, flags=re.DOTALL) try: - return json_repair.loads(ans) + res = json_repair.loads(ans) + set_llm_cache(chat_mdl.llm_name, system_prompt, ans, user_prompt, gen_conf) + return res except Exception: logging.exception(f"Loading json failure: {ans}") @@ -651,29 +659,31 @@ def clean_toc(arr): TOC_LEVELS = load_prompt("assign_toc_levels") def assign_toc_levels(toc_secs, chat_mdl, gen_conf = {"temperature": 0.2}): - print("\nBegin TOC level assignment...\n") - - ans = gen_json( + if not toc_secs: + return [] + return gen_json( PROMPT_JINJA_ENV.from_string(TOC_LEVELS).render(), str(toc_secs), chat_mdl, gen_conf ) - - return ans TOC_FROM_TEXT_SYSTEM = load_prompt("toc_from_text_system") TOC_FROM_TEXT_USER = load_prompt("toc_from_text_user") # Generate TOC from text chunks with text llms -def gen_toc_from_text(text, chat_mdl): - ans = gen_json( - PROMPT_JINJA_ENV.from_string(TOC_FROM_TEXT_SYSTEM).render(), - PROMPT_JINJA_ENV.from_string(TOC_FROM_TEXT_USER).render(text=text), - chat_mdl, - gen_conf={"temperature": 0.0, "top_p": 0.9, "enable_thinking": False, } - ) - return ans +async def gen_toc_from_text(txt_info: dict, chat_mdl): + try: + ans = gen_json( + PROMPT_JINJA_ENV.from_string(TOC_FROM_TEXT_SYSTEM).render(), + PROMPT_JINJA_ENV.from_string(TOC_FROM_TEXT_USER).render(text="\n".join([json.dumps(d, ensure_ascii=False) for d in txt_info["chunks"]])), + chat_mdl, + gen_conf={"temperature": 0.0, "top_p": 0.9} + ) + print(ans, "::::::::::::::::::::::::::::::::::::", flush=True) + txt_info["toc"] = ans if ans else [] + except Exception as e: + logging.exception(e) def split_chunks(chunks, max_length: int): @@ -690,44 +700,91 @@ def split_chunks(chunks, max_length: int): if batch_tokens + t > max_length: result.append(batch) batch, batch_tokens = [], 0 - batch.append({"id": idx, "text": chunk}) + batch.append({idx: chunk}) batch_tokens += t if batch: result.append(batch) return result -def run_toc_from_text(chunks, chat_mdl): +async def run_toc_from_text(chunks, chat_mdl): input_budget = int(chat_mdl.max_length * INPUT_UTILIZATION) - num_tokens_from_string( TOC_FROM_TEXT_USER + TOC_FROM_TEXT_SYSTEM ) - input_budget = 2000 if input_budget > 2000 else input_budget + input_budget = 1024 if input_budget > 1024 else input_budget chunk_sections = split_chunks(chunks, input_budget) res = [] - for chunk in chunk_sections: - ans = gen_toc_from_text(chunk, chat_mdl) - res.extend(ans) + chunks_res = [] + async with trio.open_nursery() as nursery: + for i, chunk in enumerate(chunk_sections): + if not chunk: + continue + chunks_res.append({"chunks": chunk}) + nursery.start_soon(gen_toc_from_text, chunks_res[-1], chat_mdl) + + for chunk in chunks_res: + res.extend(chunk.get("toc", [])) + + print(res, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") # Filter out entries with title == -1 - filtered = [x for x in res if x.get("title") and x.get("title") != "-1"] + filtered = [] + for x in res: + if not x.get("title") or x["title"] == "-1": + continue + if is_chinese(x["title"]) and len(x["title"]) > 12: + continue + if len(x["title"].split(" ")) > 12: + continue + if re.match(r"[0-9,.()/ -]+$", x["title"]): + continue + filtered.append(x) - print("\n\nFiltered TOC sections:\n", filtered) + logging.info(f"\n\nFiltered TOC sections:\n{filtered}") - # Generate initial structure (structure/title) - raw_structure = [{"structure": "0", "title": x.get("title", "")} for x in filtered] + # Generate initial level (level/title) + raw_structure = [x.get("title", "") for x in filtered] # Assign hierarchy levels using LLM - toc_with_levels = assign_toc_levels(raw_structure, chat_mdl, {"temperature": 0.0, "top_p": 0.9, "enable_thinking": False}) + toc_with_levels = assign_toc_levels(raw_structure, chat_mdl, {"temperature": 0.0, "top_p": 0.9}) # Merge structure and content (by index) merged = [] for _ , (toc_item, src_item) in enumerate(zip(toc_with_levels, filtered)): merged.append({ - "structure": toc_item.get("structure", "0"), + "level": toc_item.get("level", "0"), "title": toc_item.get("title", ""), - "content": src_item.get("content", ""), + "chunk_id": src_item.get("chunk_id", ""), }) - return merged \ No newline at end of file + return merged + + +TOC_RELEVANCE_SYSTEM = load_prompt("toc_relevance_system") +TOC_RELEVANCE_USER = load_prompt("toc_relevance_user") +def relevant_chunks_with_toc(query: str, toc:list[dict], chat_mdl, topn: int=6): + import numpy as np + try: + ans = gen_json( + PROMPT_JINJA_ENV.from_string(TOC_RELEVANCE_SYSTEM).render(), + PROMPT_JINJA_ENV.from_string(TOC_RELEVANCE_USER).render(query=query, toc_json="[\n%s\n]\n"%"\n".join([json.dumps({"level": d["level"], "title":d["title"]}, ensure_ascii=False) for d in toc])), + chat_mdl, + gen_conf={"temperature": 0.0, "top_p": 0.9} + ) + print(ans, "::::::::::::::::::::::::::::::::::::", flush=True) + id2score = {} + for ti, sc in zip(toc, ans): + if sc.get("score", -1) < 1: + continue + for id in ti.get("ids", []): + if id not in id2score: + id2score[id] = [] + id2score[id].append(sc["score"]/5.) + for id in id2score.keys(): + id2score[id] = np.mean(id2score[id]) + return [(id, sc) for id, sc in list(id2score.items()) if sc>=0.3][:topn] + except Exception as e: + logging.exception(e) + return [] diff --git a/rag/prompts/toc_from_text_system.md b/rag/prompts/toc_from_text_system.md index f982df47d68..7090f305825 100644 --- a/rag/prompts/toc_from_text_system.md +++ b/rag/prompts/toc_from_text_system.md @@ -1,25 +1,25 @@ You are a robust Table-of-Contents (TOC) extractor. GOAL -Given a dictionary of chunks {chunk_id: chunk_text}, extract TOC-like headings and return a strict JSON array of objects: +Given a dictionary of chunks {"": chunk_text}, extract TOC-like headings and return a strict JSON array of objects: [ - {"title": , "content": ""}, + {"title": "", "chunk_id": ""}, ... ] FIELDS - "title": the heading text (clean, no page numbers or leader dots). - If any part of a chunk has no valid heading, output that part as {"title":"-1", ...}. -- "content": the chunk_id (string). +- "chunk_id": the chunk ID (string). - One chunk can yield multiple JSON objects in order (unmatched text + one or more headings). RULES 1) Preserve input chunk order strictly. 2) If a chunk contains multiple headings, expand them in order: - - Pre-heading narrative → {"title":"-1","content":chunk_id} - - Then each heading → {"title":"...","content":chunk_id} -3) Do not merge outputs across chunks; each object refers to exactly one chunk_id. -4) "title" must be non-empty (or exactly "-1"). "content" must be a string (chunk_id). + - Pre-heading narrative → {"title":"-1","chunk_id":""} + - Then each heading → {"title":"...","chunk_id":""} +3) Do not merge outputs across chunks; each object refers to exactly one chunk ID. +4) "title" must be non-empty (or exactly "-1"). "chunk_id" must be a string (chunk ID). 5) When ambiguous, prefer "-1" unless the text strongly looks like a heading. HEADING DETECTION (cues, not hard rules) @@ -51,63 +51,69 @@ EXAMPLES Example 1 — No heading Input: -{0: "Copyright page · Publication info (ISBN 123-456). All rights reserved."} +[{"0": "Copyright page · Publication info (ISBN 123-456). All rights reserved."}, ...] Output: [ - {"title":"-1","content":"0"} + {"title":"-1","chunk_id":"0"}, + ... ] Example 2 — One heading Input: -{1: "Chapter 1: General Provisions This chapter defines the overall rules…"} +[{"1": "Chapter 1: General Provisions This chapter defines the overall rules…"}, ...] Output: [ - {"title":"Chapter 1: General Provisions","content":"1"} + {"title":"Chapter 1: General Provisions","chunk_id":"1"}, + ... ] Example 3 — Narrative + heading Input: -{2: "This paragraph introduces the background and goals. Section 2: Definitions Key terms are explained…"} +[{"2": "This paragraph introduces the background and goals. Section 2: Definitions Key terms are explained…"}, ...] Output: [ - {"title":"-1","content":"2"}, - {"title":"Section 2: Definitions","content":"2"} + {"title":"Section 2: Definitions","chunk_id":"2"}, + ... ] Example 4 — Multiple headings in one chunk Input: -{3: "Declarations and Commitments (I) Party B commits… (II) Party C commits… Appendix A Data Specification"} +[{"3": "Declarations and Commitments (I) Party B commits… (II) Party C commits… Appendix A Data Specification"}, ...] Output: [ - {"title":"Declarations and Commitments (I)","content":"3"}, - {"title":"(II)","content":"3"}, - {"title":"Appendix A","content":"3"} + {"title":"Declarations and Commitments","chunk_id":"3"}, + {"title":"(I) Party B commits","chunk_id":"3"}, + {"title":"(II) Party C commits","chunk_id":"3"}, + {"title":"Appendix A Data Specification","chunk_id":"3"}, + ... ] Example 5 — Numbering styles Input: -{4: "1. Scope: Defines boundaries. 2) Definitions: Terms used. III) Methods Overview."} +[{"4": "1. Scope: Defines boundaries. 2) Definitions: Terms used. III) Methods Overview."}, ...] Output: [ - {"title":"1. Scope","content":"4"}, - {"title":"2) Definitions","content":"4"}, - {"title":"III) Methods","content":"4"} + {"title":"1. Scope","chunk_id":"4"}, + {"title":"2) Definitions","chunk_id":"4"}, + {"title":"III) Methods Overview","chunk_id":"4"}, + ... ] Example 6 — Long list (NOT headings) Input: -{5: "Item list: apples, bananas, strawberries, blueberries, mangos, peaches"} +{"5": "Item list: apples, bananas, strawberries, blueberries, mangos, peaches"}, ...] Output: [ - {"title":"-1","content":"5"} + {"title":"-1","chunk_id":"5"}, + ... ] Example 7 — Mixed Chinese/English Input: -{6: "(出版信息略)This standard follows industry practices. Chapter 1: Overview 摘要… 第2节:术语与缩略语"} +{"6": "(出版信息略)This standard follows industry practices. Chapter 1: Overview 摘要… 第2节:术语与缩略语"}, ...] Output: [ - {"title":"-1","content":"6"}, - {"title":"Chapter 1: Overview","content":"6"}, - {"title":"第2节:术语与缩略语","content":"6"} + {"title":"Chapter 1: Overview","chunk_id":"6"}, + {"title":"第2节:术语与缩略语","chunk_id":"6"}, + ... ] diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index ae10214867e..08b232e49f6 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -32,7 +32,7 @@ from graphrag.general.index import run_graphrag_for_kb from graphrag.utils import get_llm_cache, set_llm_cache, get_tags_from_cache, set_tags_to_cache from rag.flow.pipeline import Pipeline -from rag.prompts.generator import keyword_extraction, question_proposal, content_tagging +from rag.prompts.generator import keyword_extraction, question_proposal, content_tagging, run_toc_from_text import logging import os from datetime import datetime @@ -370,6 +370,38 @@ async def doc_question_proposal(chat_mdl, d, topn): nursery.start_soon(doc_question_proposal, chat_mdl, d, task["parser_config"]["auto_questions"]) progress_callback(msg="Question generation {} chunks completed in {:.2f}s".format(len(docs), timer() - st)) + if task["parser_config"].get("toc_extraction", True): + progress_callback(msg="Start to generate table of content ...") + chat_mdl = LLMBundle(task["tenant_id"], LLMType.CHAT, llm_name=task["llm_id"], lang=task["language"]) + docs = sorted(docs, key=lambda d:( + d.get("page_num_int", 0)[0] if isinstance(d.get("page_num_int", 0), list) else d.get("page_num_int", 0), + d.get("top_int", 0)[0] if isinstance(d.get("top_int", 0), list) else d.get("top_int", 0) + )) + toc: list[dict] = await run_toc_from_text([d["content_with_weight"] for d in docs], chat_mdl) + logging.info("------------ T O C -------------\n"+json.dumps(toc, ensure_ascii=False, indent=' ')) + ii = 0 + while ii < len(toc): + try: + idx = int(toc[ii]["chunk_id"]) + del toc[ii]["chunk_id"] + toc[ii]["ids"] = [docs[idx]["id"]] + if ii == len(toc) -1: + break + for jj in range(idx+1, int(toc[ii+1]["chunk_id"])): + toc[ii]["ids"].append(docs[jj]["id"]) + except Exception as e: + logging.exception(e) + ii += 1 + + if toc: + d = copy.deepcopy(docs[-1]) + d["content_with_weight"] = json.dumps(toc, ensure_ascii=False) + d["toc_kwd"] = "toc" + d["available_int"] = 0 + d["page_num_int"] = 100000000 + d["id"] = xxhash.xxh64((d["content_with_weight"] + str(d["doc_id"])).encode("utf-8", "surrogatepass")).hexdigest() + docs.append(d) + if task["kb_parser_config"].get("tag_kb_ids", []): progress_callback(msg="Start to tag for every chunk ...") kb_ids = task["kb_parser_config"]["tag_kb_ids"] From deaf15a08bf37633693aff90d3faee268311240b Mon Sep 17 00:00:00 2001 From: Lynn Date: Fri, 10 Oct 2025 17:08:20 +0800 Subject: [PATCH 0793/1187] Fix: typo (#10469) ### What problem does this PR solve? Fix some typo in document. ### Type of change - [x] Documentation Update --- docs/guides/manage_users_and_services.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/manage_users_and_services.md b/docs/guides/manage_users_and_services.md index edbff0361bf..c032f3c7d5c 100644 --- a/docs/guides/manage_users_and_services.md +++ b/docs/guides/manage_users_and_services.md @@ -44,7 +44,7 @@ Commands are case-insensitive and must be terminated with a semicolon(;). `LIST SERVICES;` -- Lists all available services within the RAGFLow system. +- Lists all available services within the RAGFlow system. - [Example](#example-list-services) @@ -324,7 +324,7 @@ Listing all agents of user: lynn_inf@hotmail.com -- Show help infomation. +- Show help information. ``` admin> \help From a50ccf77f98776891df6c6f8096f02a343644d5b Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Fri, 10 Oct 2025 17:16:34 +0800 Subject: [PATCH 0794/1187] Fix: Fixed some discovered bugs #9869 (#10466) ### What problem does this PR solve? Fix: Bug fixes #9869 - Adjusted the breadcrumb display logic on the data flow results page - Added the default display of "Local Upload" to the Source field in the dataset overview table - Replaced the original Mindmap Task field with the GraphRAG Task field on the dataset settings page - Optimized the build button status check criteria and adjusted the progress information display logic - Introduced a Tooltip in the parsing status cell component and removed redundant Button components ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/interfaces/database/knowledge.ts | 2 + web/src/locales/en.ts | 5 ++- web/src/locales/zh.ts | 1 + web/src/pages/dataflow-result/index.tsx | 4 +- .../dataset-overview/overview-table.tsx | 4 +- .../pages/dataset/dataset-setting/index.tsx | 4 +- .../dataset/generate-button/generate.tsx | 12 ++++-- .../dataset/dataset/parsing-status-cell.tsx | 38 ++++++++++++++----- 8 files changed, 50 insertions(+), 20 deletions(-) diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index d768d8e5ecb..b6939d1c49b 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -33,6 +33,8 @@ export interface IKnowledge { raptor_task_id?: string; mindmap_task_finish_at?: string; mindmap_task_id?: string; + graphrag_task_finish_at: string; + graphrag_task_id: string; } export interface IKnowledgeResult { diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 76081af8470..549dd5dfbdf 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -96,17 +96,18 @@ export default { description: 'Which knowledge bases will you use today?', createKnowledgeBase: 'Create Dataset', name: 'Name', - namePlaceholder: 'Please input name!', + namePlaceholder: 'Please input name.', doc: 'Docs', searchKnowledgePlaceholder: 'Search', noMoreData: `That's all. Nothing more.`, }, knowledgeDetails: { + localUpload: 'Local Upload', fileSize: 'File Size', fileType: 'File Type', uploadedBy: 'Uploaded by', notGenerated: 'Not generated', - generatedOn: 'Generated on', + generatedOn: 'Generated on ', subbarFiles: 'Files', generateKnowledgeGraph: 'This will extract entities and relationships from all your documents in this dataset. The process may take a while to complete.', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 97291de81c2..d13f3db6cca 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -94,6 +94,7 @@ export default { noMoreData: '没有更多数据了', }, knowledgeDetails: { + localUpload: '本地上传', fileSize: '文件大小', fileType: '文件类型', uploadedBy: '创建者', diff --git a/web/src/pages/dataflow-result/index.tsx b/web/src/pages/dataflow-result/index.tsx index 9da4a910e09..a2d2fe9130a 100644 --- a/web/src/pages/dataflow-result/index.tsx +++ b/web/src/pages/dataflow-result/index.tsx @@ -187,7 +187,9 @@ const Chunk = () => { - {documentInfo?.name} + + {knowledgeId ? documentInfo?.name : t('dataflow.viewResult')} + diff --git a/web/src/pages/dataset/dataset-overview/overview-table.tsx b/web/src/pages/dataset/dataset-overview/overview-table.tsx index 993d39dbf25..677d3ea92eb 100644 --- a/web/src/pages/dataset/dataset-overview/overview-table.tsx +++ b/web/src/pages/dataset/dataset-overview/overview-table.tsx @@ -94,7 +94,9 @@ export const getFileLogsTableColumns = ( accessorKey: 'source_from', header: t('source'), cell: ({ row }) => ( -
    {row.original.source_from}
    +
    + {row.original.source_from || t('localUpload')} +
    ), }, { diff --git a/web/src/pages/dataset/dataset-setting/index.tsx b/web/src/pages/dataset/dataset-setting/index.tsx index c13fcb6b11a..25d8fd7ea33 100644 --- a/web/src/pages/dataset/dataset-setting/index.tsx +++ b/web/src/pages/dataset/dataset-setting/index.tsx @@ -95,8 +95,8 @@ export default function DatasetSettings() { }; setPipelineData(data); setGraphRagGenerateData({ - finish_at: knowledgeDetails.mindmap_task_finish_at, - task_id: knowledgeDetails.mindmap_task_id, + finish_at: knowledgeDetails.graphrag_task_finish_at, + task_id: knowledgeDetails.graphrag_task_id, } as IGenerateLogButtonProps); setRaptorGenerateData({ finish_at: knowledgeDetails.raptor_task_finish_at, diff --git a/web/src/pages/dataset/dataset/generate-button/generate.tsx b/web/src/pages/dataset/dataset/generate-button/generate.tsx index b9d16e01631..a488966cf16 100644 --- a/web/src/pages/dataset/dataset/generate-button/generate.tsx +++ b/web/src/pages/dataset/dataset/generate-button/generate.tsx @@ -116,7 +116,8 @@ const MenuItem: React.FC<{ /> {t(`knowledgeDetails.${lowerFirst(type)}`)}
    - {status === generateStatus.start && ( + {(status === generateStatus.start || + status === generateStatus.completed) && (
    {t(`knowledgeDetails.generate${type}`)}
    @@ -165,9 +166,12 @@ const MenuItem: React.FC<{ )}
    )} -
    - {replaceText(data?.progress_msg || '')} -
    + {status !== generateStatus.start && + status !== generateStatus.completed && ( +
    + {replaceText(data?.progress_msg || '')} +
    + )}
    ); diff --git a/web/src/pages/dataset/dataset/parsing-status-cell.tsx b/web/src/pages/dataset/dataset/parsing-status-cell.tsx index b029ceed7f6..102b6db546a 100644 --- a/web/src/pages/dataset/dataset/parsing-status-cell.tsx +++ b/web/src/pages/dataset/dataset/parsing-status-cell.tsx @@ -1,6 +1,5 @@ import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; import { IconFontFill } from '@/components/icon-font'; -import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, @@ -9,6 +8,11 @@ import { } from '@/components/ui/dropdown-menu'; import { Progress } from '@/components/ui/progress'; import { Separator } from '@/components/ui/separator'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; import { IDocumentInfo } from '@/interfaces/database/document'; import { CircleX } from 'lucide-react'; import { useCallback, useMemo } from 'react'; @@ -86,16 +90,29 @@ export function ParsingStatusCell({ }; return (
    -
    +
    - - + + + +
    + {pipeline_id + ? pipeline_name || pipeline_id + : parser_id === 'naive' + ? 'general' + : parser_id} +
    +
    + +

    + {pipeline_id + ? pipeline_name || pipeline_id + : parser_id === 'naive' + ? 'general' + : parser_id} +

    +
    +
    @@ -107,6 +124,7 @@ export function ParsingStatusCell({
    + {showParse && (
    From 198e52e990a8f6b49fcf1b97327df18b2d30f2a3 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 10 Oct 2025 18:35:43 +0800 Subject: [PATCH 0795/1187] Feat: Added toc enhance field to chat and retrieval operator configuration #10436 (#10470) ### What problem does this PR solve? Feat: Added toc enhance field to chat and retrieval operator configuration #10436 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/toc-enhance-form-field.tsx | 16 ++++++++++++++++ web/src/locales/en.ts | 6 ++++-- web/src/locales/zh.ts | 2 ++ web/src/pages/agent/constant.tsx | 1 + web/src/pages/agent/form/retrieval-form/next.tsx | 3 +++ .../form/tool-form/retrieval-form/index.tsx | 2 ++ .../form/parser-form/video-form-fields.tsx | 2 -- .../pages/data-flow/form/retrieval-form/next.tsx | 0 .../chat/app-settings/chat-basic-settings.tsx | 2 ++ .../chat/app-settings/chat-settings.tsx | 1 + .../app-settings/use-chat-setting-schema.tsx | 1 + .../pages/next-chats/hooks/use-rename-chat.ts | 1 + 12 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 web/src/components/toc-enhance-form-field.tsx delete mode 100644 web/src/pages/data-flow/form/retrieval-form/next.tsx diff --git a/web/src/components/toc-enhance-form-field.tsx b/web/src/components/toc-enhance-form-field.tsx new file mode 100644 index 00000000000..174d76a1d79 --- /dev/null +++ b/web/src/components/toc-enhance-form-field.tsx @@ -0,0 +1,16 @@ +import { useTranslation } from 'react-i18next'; +import { SwitchFormField } from './switch-fom-field'; + +type Props = { name: string }; + +export function TOCEnhanceFormField({ name }: Props) { + const { t } = useTranslation(); + + return ( + + ); +} diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 549dd5dfbdf..e0628433ad1 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -537,10 +537,10 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s setAnOpener: 'Opening greeting', setAnOpenerInitial: `Hi! I'm your assistant. What can I do for you?`, setAnOpenerTip: 'Set an opening greeting for users.', - knowledgeBases: 'Knowledge bases', + knowledgeBases: 'Datasets', knowledgeBasesMessage: 'Please select', knowledgeBasesTip: - 'Select the knowledge bases to associate with this chat assistant. An empty knowledge base will not appear in the dropdown list.', + 'Select the datasets to associate with this chat assistant. An empty knowledge base will not appear in the dropdown list.', system: 'System prompt', systemInitialValue: `You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence "The answer you are looking for is not found in the knowledge base!" Answers need to consider chat history. Here is the knowledge base: @@ -668,6 +668,8 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s }, cancel: 'Cancel', chatSetting: 'Chat setting', + tocEnhance: 'TOC enhance', + tocEnhanceTip: ` During the parsing of the document, table of contents information was generated (see the 'Enable Table of Contents Extraction' option in the General method). This allows the large model to return table of contents items relevant to the user's query, thereby using these items to retrieve related chunks and apply weighting to these chunks during the sorting process. This approach is derived from mimicking the behavioral logic of how humans search for knowledge in books.`, }, setting: { profile: 'Profile', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index d13f3db6cca..3c15e2df778 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -660,6 +660,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 chatSetting: '聊天设置', avatarHidden: '隐藏头像', locale: '地区', + tocEnhance: '目录增强', + tocEnhanceTip: `解析文档时生成了目录信息(见General方法的‘启用目录抽取’),让大模型返回和用户问题相关的目录项,从而利用目录项拿到相关chunk,对这些chunk在排序中进行加权。这种方法来源于模仿人类查询书本中知识的行为逻辑`, }, setting: { profile: '概要', diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index b40b42e178a..3079f3b1a29 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -254,6 +254,7 @@ export const initialRetrievalValues = { ...initialSimilarityThresholdValue, ...initialKeywordsSimilarityWeightValue, use_kg: false, + toc_enhance: false, cross_languages: [], outputs: { formalized_content: { diff --git a/web/src/pages/agent/form/retrieval-form/next.tsx b/web/src/pages/agent/form/retrieval-form/next.tsx index 69204d4da5a..67322b2b559 100644 --- a/web/src/pages/agent/form/retrieval-form/next.tsx +++ b/web/src/pages/agent/form/retrieval-form/next.tsx @@ -5,6 +5,7 @@ import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; import { RAGFlowFormItem } from '@/components/ragflow-form'; import { RerankFormFields } from '@/components/rerank'; import { SimilaritySliderFormField } from '@/components/similarity-slider'; +import { TOCEnhanceFormField } from '@/components/toc-enhance-form-field'; import { TopNFormField } from '@/components/top-n-item'; import { Form, @@ -39,6 +40,7 @@ export const RetrievalPartialSchema = { empty_response: z.string(), cross_languages: z.array(z.string()), use_kg: z.boolean(), + toc_enhance: z.boolean(), }; export const FormSchema = z.object({ @@ -119,6 +121,7 @@ function RetrievalForm({ node }: INextOperatorForm) { + diff --git a/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx b/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx index bf187a6bf71..c1031081ec3 100644 --- a/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx +++ b/web/src/pages/agent/form/tool-form/retrieval-form/index.tsx @@ -4,6 +4,7 @@ import { FormContainer } from '@/components/form-container'; import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; import { RerankFormFields } from '@/components/rerank'; import { SimilaritySliderFormField } from '@/components/similarity-slider'; +import { TOCEnhanceFormField } from '@/components/toc-enhance-form-field'; import { TopNFormField } from '@/components/top-n-item'; import { Form } from '@/components/ui/form'; import { UseKnowledgeGraphFormField } from '@/components/use-knowledge-graph-item'; @@ -53,6 +54,7 @@ const RetrievalForm = () => { + diff --git a/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx b/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx index d1ca8c68d5f..35295886719 100644 --- a/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx +++ b/web/src/pages/data-flow/form/parser-form/video-form-fields.tsx @@ -7,8 +7,6 @@ import { export function VideoFormFields({ prefix }: OutputFormatFormFieldProps) { const modelOptions = useComposeLlmOptionsByModelTypes([ - LlmModelType.Chat, - LlmModelType.Image2text, LlmModelType.Speech2text, ]); diff --git a/web/src/pages/data-flow/form/retrieval-form/next.tsx b/web/src/pages/data-flow/form/retrieval-form/next.tsx deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx b/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx index d945f784183..3f06e1818e7 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-basic-settings.tsx @@ -5,6 +5,7 @@ import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; import { MetadataFilter } from '@/components/metadata-filter'; import { SwitchFormField } from '@/components/switch-fom-field'; import { TavilyFormField } from '@/components/tavily-form-field'; +import { TOCEnhanceFormField } from '@/components/toc-enhance-form-field'; import { FormControl, FormField, @@ -109,6 +110,7 @@ export default function ChatBasicSetting() { label={t('tts')} tooltip={t('ttsTip')} > + diff --git a/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx b/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx index 50fe9dd850b..215285e5df7 100644 --- a/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx +++ b/web/src/pages/next-chats/chat/app-settings/chat-settings.tsx @@ -49,6 +49,7 @@ export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) { parameters: [], reasoning: false, cross_languages: [], + toc_enhance: false, }, top_n: 8, similarity_threshold: 0.2, diff --git a/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx b/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx index 105b880b08d..226400775a0 100644 --- a/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx +++ b/web/src/pages/next-chats/chat/app-settings/use-chat-setting-schema.tsx @@ -33,6 +33,7 @@ export function useChatSettingSchema() { tavily_api_key: z.string().optional(), reasoning: z.boolean().optional(), cross_languages: z.array(z.string()).optional(), + toc_enhance: z.boolean().optional(), }); const formSchema = z.object({ diff --git a/web/src/pages/next-chats/hooks/use-rename-chat.ts b/web/src/pages/next-chats/hooks/use-rename-chat.ts index 41f81232d81..bf8fc2fe6ca 100644 --- a/web/src/pages/next-chats/hooks/use-rename-chat.ts +++ b/web/src/pages/next-chats/hooks/use-rename-chat.ts @@ -34,6 +34,7 @@ export const useRenameChat = () => { use_kg: false, reasoning: false, parameters: [{ key: 'knowledge', optional: false }], + toc_enhance: false, }, llm_id: tenantInfo.data.llm_id, llm_setting: {}, From eb0b37d7ee40d1a6eda7c7b68a57da87196faffe Mon Sep 17 00:00:00 2001 From: f0 Date: Fri, 10 Oct 2025 18:36:00 +0800 Subject: [PATCH 0796/1187] typo: fix _entity_index_dilimiter_key to entity_index_delimiter_key (#10465) ### What problem does this PR solve? typo: fix _entity_index_dilimiter_key to entity_index_delimiter_key ### Type of change - [x] Refactoring --- graphrag/entity_resolution.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graphrag/entity_resolution.py b/graphrag/entity_resolution.py index 01478d760c4..11bacd15e01 100644 --- a/graphrag/entity_resolution.py +++ b/graphrag/entity_resolution.py @@ -60,7 +60,7 @@ def __init__( self._llm = llm_invoker self._resolution_prompt = ENTITY_RESOLUTION_PROMPT self._record_delimiter_key = "record_delimiter" - self._entity_index_dilimiter_key = "entity_index_delimiter" + self._entity_index_delimiter_key = "entity_index_delimiter" self._resolution_result_delimiter_key = "resolution_result_delimiter" self._input_text_key = "input_text" @@ -77,7 +77,7 @@ async def __call__(self, graph: nx.Graph, **prompt_variables, self._record_delimiter_key: prompt_variables.get(self._record_delimiter_key) or DEFAULT_RECORD_DELIMITER, - self._entity_index_dilimiter_key: prompt_variables.get(self._entity_index_dilimiter_key) + self._entity_index_delimiter_key: prompt_variables.get(self._entity_index_delimiter_key) or DEFAULT_ENTITY_INDEX_DELIMITER, self._resolution_result_delimiter_key: prompt_variables.get(self._resolution_result_delimiter_key) or DEFAULT_RESOLUTION_RESULT_DELIMITER, @@ -185,7 +185,7 @@ async def _resolve_candidate(self, candidate_resolution_i: tuple[str, list[tuple result = self._process_results(len(candidate_resolution_i[1]), response, self.prompt_variables.get(self._record_delimiter_key, DEFAULT_RECORD_DELIMITER), - self.prompt_variables.get(self._entity_index_dilimiter_key, + self.prompt_variables.get(self._entity_index_delimiter_key, DEFAULT_ENTITY_INDEX_DELIMITER), self.prompt_variables.get(self._resolution_result_delimiter_key, DEFAULT_RESOLUTION_RESULT_DELIMITER)) From 2cdba3d1e6c0a1f87fa52691f100f8bcdfad1a6c Mon Sep 17 00:00:00 2001 From: YngvarHuang <625452882@qq.com> Date: Fri, 10 Oct 2025 18:36:20 +0800 Subject: [PATCH 0797/1187] Fix: Fixed the issue where swagger apidocs could not be opened properly(#9522) (#10461) ### What problem does this PR solve? Fix: Fixed the issue where swagger apidocs could not be opened properly(#9522) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) Co-authored-by: virgilwong --- api/apps/sdk/dify_retrieval.py | 83 ++++++++++++++++++++++++++++++++++ api/apps/sdk/doc.py | 2 +- api/apps/sdk/files.py | 32 ++++++------- 3 files changed, 100 insertions(+), 17 deletions(-) diff --git a/api/apps/sdk/dify_retrieval.py b/api/apps/sdk/dify_retrieval.py index dc5476f3435..cbfb6e74816 100644 --- a/api/apps/sdk/dify_retrieval.py +++ b/api/apps/sdk/dify_retrieval.py @@ -31,6 +31,89 @@ @apikey_required @validate_request("knowledge_id", "query") def retrieval(tenant_id): + """ + Dify-compatible retrieval API + --- + tags: + - SDK + security: + - ApiKeyAuth: [] + parameters: + - in: body + name: body + required: true + schema: + type: object + required: + - knowledge_id + - query + properties: + knowledge_id: + type: string + description: Knowledge base ID + query: + type: string + description: Query text + use_kg: + type: boolean + description: Whether to use knowledge graph + default: false + retrieval_setting: + type: object + description: Retrieval configuration + properties: + score_threshold: + type: number + description: Similarity threshold + default: 0.0 + top_k: + type: integer + description: Number of results to return + default: 1024 + metadata_condition: + type: object + description: Metadata filter condition + properties: + conditions: + type: array + items: + type: object + properties: + name: + type: string + description: Field name + comparison_operator: + type: string + description: Comparison operator + value: + type: string + description: Field value + responses: + 200: + description: Retrieval succeeded + schema: + type: object + properties: + records: + type: array + items: + type: object + properties: + content: + type: string + description: Content text + score: + type: number + description: Similarity score + title: + type: string + description: Document title + metadata: + type: object + description: Metadata info + 404: + description: Knowledge base or document not found + """ req = request.json question = req["query"] kb_id = req["knowledge_id"] diff --git a/api/apps/sdk/doc.py b/api/apps/sdk/doc.py index 2cc2926df45..0c6405c1453 100644 --- a/api/apps/sdk/doc.py +++ b/api/apps/sdk/doc.py @@ -458,7 +458,7 @@ def list_docs(dataset_id, tenant_id): required: false default: true description: Order in descending. - - in: query + - in: query name: create_time_from type: integer required: false diff --git a/api/apps/sdk/files.py b/api/apps/sdk/files.py index 17af87dc00f..d2a3de21f26 100644 --- a/api/apps/sdk/files.py +++ b/api/apps/sdk/files.py @@ -62,22 +62,22 @@ def upload(tenant_id): type: object properties: data: - type: array - items: - type: object - properties: - id: - type: string - description: File ID - name: - type: string - description: File name - size: - type: integer - description: File size in bytes - type: - type: string - description: File type (e.g., document, folder) + type: array + items: + type: object + properties: + id: + type: string + description: File ID + name: + type: string + description: File name + size: + type: integer + description: File size in bytes + type: + type: string + description: File type (e.g., document, folder) """ pf_id = request.form.get("parent_id") From 0283e4098f842aa6fbd2d6ccb372af2c596449fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Lukas?= Date: Fri, 10 Oct 2025 13:18:24 +0200 Subject: [PATCH 0798/1187] Fix #10408 (#10471) ### What problem does this PR solve? Google Cloud model does not work correctly with gemini-2.5 models Close #10408 ### Type of change - [X] Bug Fix (non-breaking change which fixes an issue) --------- Co-authored-by: Kevin Hu --- api/db/db_models.py | 6 +++++- rag/llm/chat_model.py | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/api/db/db_models.py b/api/db/db_models.py index 7f2e35497b4..c1a5fd5ed1d 100644 --- a/api/db/db_models.py +++ b/api/db/db_models.py @@ -641,7 +641,7 @@ class TenantLLM(DataBaseModel): llm_factory = CharField(max_length=128, null=False, help_text="LLM factory name", index=True) model_type = CharField(max_length=128, null=True, help_text="LLM, Text Embedding, Image2Text, ASR", index=True) llm_name = CharField(max_length=128, null=True, help_text="LLM name", default="", index=True) - api_key = CharField(max_length=2048, null=True, help_text="API KEY", index=True) + api_key = TextField(null=True, help_text="API KEY") api_base = CharField(max_length=255, null=True, help_text="API Base") max_tokens = IntegerField(default=8192, index=True) used_tokens = IntegerField(default=0, index=True) @@ -1142,4 +1142,8 @@ def migrate_db(): migrate(migrator.add_column("knowledgebase", "mindmap_task_finish_at", CharField(null=True))) except Exception: pass + try: + migrate(migrator.alter_column_type("tenant_llm", "api_key", TextField(null=True, help_text="API KEY"))) + except Exception: + pass logging.disable(logging.NOTSET) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 2a77a45ebab..91507d1478a 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -145,7 +145,7 @@ def _chat(self, history, gen_conf, **kwargs): logging.info(f"[INFO] {self.model_name} detected as reasoning model, using _chat_streamly") final_ans = "" - tol_token = 0 + tol_token = 0 for delta, tol in self._chat_streamly(history, gen_conf, with_reasoning=False, **kwargs): if delta.startswith("") or delta.endswith(""): continue @@ -156,7 +156,7 @@ def _chat(self, history, gen_conf, **kwargs): final_ans = "**ERROR**: Empty response from reasoning model" return final_ans.strip(), tol_token - + if self.model_name.lower().find("qwen3") >= 0: kwargs["extra_body"] = {"enable_thinking": False} @@ -1182,6 +1182,7 @@ def _clean_conf(self, gen_conf): else: if "max_tokens" in gen_conf: gen_conf["max_output_tokens"] = gen_conf["max_tokens"] + del gen_conf["max_tokens"] for k in list(gen_conf.keys()): if k not in ["temperature", "top_p", "max_output_tokens"]: del gen_conf[k] @@ -1189,6 +1190,7 @@ def _clean_conf(self, gen_conf): def _chat(self, history, gen_conf={}, **kwargs): system = history[0]["content"] if history and history[0]["role"] == "system" else "" + gen_conf = self._clean_conf(gen_conf) if "claude" in self.model_name: response = self.client.messages.create( model=self.model_name, @@ -1250,9 +1252,12 @@ def chat_streamly(self, system, history, gen_conf={}, **kwargs): yield total_tokens else: + response = None + total_tokens = 0 self.client._system_instruction = system if "max_tokens" in gen_conf: gen_conf["max_output_tokens"] = gen_conf["max_tokens"] + del gen_conf["max_tokens"] for k in list(gen_conf.keys()): if k not in ["temperature", "top_p", "max_output_tokens"]: del gen_conf[k] @@ -1260,18 +1265,23 @@ def chat_streamly(self, system, history, gen_conf={}, **kwargs): if "role" in item and item["role"] == "assistant": item["role"] = "model" if "content" in item: - item["parts"] = item.pop("content") + item["parts"] = [ + { + "text": item.pop("content"), + } + ] ans = "" try: - response = self.model.generate_content(history, generation_config=gen_conf, stream=True) + response = self.client.generate_content(history, generation_config=gen_conf, stream=True) for resp in response: ans = resp.text + total_tokens += num_tokens_from_string(ans) yield ans except Exception as e: yield ans + "\n**ERROR**: " + str(e) - yield response._chunks[-1].usage_metadata.total_token_count + yield total_tokens class GPUStackChat(Base): From 390b2b8f261f8199a714151eec098e893a75f6a5 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 10 Oct 2025 19:52:08 +0800 Subject: [PATCH 0799/1187] Feat: Adjust the style of the delete button on the agent side #9869 (#10473) ### What problem does this PR solve? Feat: Adjust the style of the delete button on the agent side #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/canvas/edge/index.tsx | 2 +- web/tailwind.config.js | 68 ++--------------------- web/tailwind.css | 23 ++------ 3 files changed, 11 insertions(+), 82 deletions(-) diff --git a/web/src/pages/agent/canvas/edge/index.tsx b/web/src/pages/agent/canvas/edge/index.tsx index 5a3385c0126..725409aedda 100644 --- a/web/src/pages/agent/canvas/edge/index.tsx +++ b/web/src/pages/agent/canvas/edge/index.tsx @@ -116,7 +116,7 @@ function InnerButtonEdge({ > - - - ); -} - -export function SignInForm() { - const { t } = useTranslate('login'); - - const FormSchema = z.object({ - email: z.string().email({ - message: t('emailPlaceholder'), - }), - password: z.string({ required_error: t('passwordPlaceholder') }), - }); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { - email: '', - }, - }); - - function onSubmit(data: z.infer) { - console.log('🚀 ~ onSubmit ~ data:', data); - toast({ - title: 'You submitted the following values:', - description: ( -
    -          {JSON.stringify(data, null, 2)}
    -        
    - ), - }); - } - - return ( -
    - - ( - - {t('emailLabel')} - - - - - - )} - /> - ( - - {t('passwordLabel')} - - - - - - )} - /> -
    - - -
    - - - - ); -} - -export function VerifyEmailForm() { - const FormSchema = z.object({ - pin: z.string().min(6, { - message: 'Your one-time password must be 6 characters.', - }), - }); - - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { - pin: '', - }, - }); - - function onSubmit(data: z.infer) { - console.log('🚀 ~ onSubmit ~ data:', data); - toast({ - title: 'You submitted the following values:', - description: ( -
    -          {JSON.stringify(data, null, 2)}
    -        
    - ), - }); - } - - return ( -
    - - ( - - One-Time Password - - - - - - - - - - - - - - - )} - /> - - - - - ); -} diff --git a/web/src/pages/login-next/hooks.ts b/web/src/pages/login-next/hooks.ts deleted file mode 100644 index 2641a34b3de..00000000000 --- a/web/src/pages/login-next/hooks.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useCallback } from 'react'; -import { useSearchParams } from 'umi'; - -export enum Step { - SignIn, - SignUp, - ForgotPassword, - ResetPassword, - VerifyEmail, -} - -export const useSwitchStep = (step: Step) => { - const [_, setSearchParams] = useSearchParams(); - console.log('🚀 ~ useSwitchStep ~ _:', _); - const switchStep = useCallback(() => { - setSearchParams(new URLSearchParams({ step: step.toString() })); - }, [setSearchParams, step]); - - return { switchStep }; -}; diff --git a/web/src/pages/login-next/index.less b/web/src/pages/login-next/index.less new file mode 100644 index 00000000000..2322be352e7 --- /dev/null +++ b/web/src/pages/login-next/index.less @@ -0,0 +1,42 @@ +.animate-glow { + animation: glow 16s infinite linear; +} +.mask-path { + stroke-width: 8; + ::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + stroke-dasharray: 660; + stroke-dashoffset: 0; + stroke: #d11818; + stroke-width: 8; + fill: none; + } +} +@keyframes glow { + 0% { + stroke-dashoffset: 0; + } + 100% { + stroke-dashoffset: -650; + } +} + +@keyframes highlight-flow { + 0% { + stroke-dashoffset: 50; + } + 100% { + stroke-dashoffset: -600; + } /* 15+300-30=285 */ +} + +.animate-highlight { + animation: highlight-flow 16s linear infinite; +} + +////////////////////////////////////////////////////////////////////////// diff --git a/web/src/pages/login-next/index.tsx b/web/src/pages/login-next/index.tsx index a06d299874e..35e3e76b443 100644 --- a/web/src/pages/login-next/index.tsx +++ b/web/src/pages/login-next/index.tsx @@ -1,107 +1,305 @@ -import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Separator } from '@/components/ui/separator'; -import { useTranslate } from '@/hooks/common-hooks'; -import { DiscordLogoIcon, GitHubLogoIcon } from '@radix-ui/react-icons'; -import { useSearchParams } from 'umi'; -import { SignInForm, SignUpForm, VerifyEmailForm } from './form'; -import { Step, useSwitchStep } from './hooks'; +import SvgIcon from '@/components/svg-icon'; +import { useAuth } from '@/hooks/auth-hooks'; +import { + useLogin, + useLoginChannels, + useLoginWithChannel, + useRegister, +} from '@/hooks/login-hooks'; +import { useSystemConfig } from '@/hooks/system-hooks'; +import { rsaPsw } from '@/utils'; +import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'umi'; -function LoginFooter() { - return ( -
    - -

    or continue with

    -
    - - -
    -
    - ); -} +import Spotlight from '@/components/spotlight'; +import { Button, ButtonLoading } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { BgSvg } from './bg'; +import './index.less'; +import { SpotlightTopLeft, SpotlightTopRight } from './spotlight-top'; -export function SignUpCard() { - const { t } = useTranslate('login'); +const Login = () => { + const [title, setTitle] = useState('login'); + const navigate = useNavigate(); + const { login, loading: signLoading } = useLogin(); + const { register, loading: registerLoading } = useRegister(); + const { channels, loading: channelsLoading } = useLoginChannels(); + const { login: loginWithChannel, loading: loginWithChannelLoading } = + useLoginWithChannel(); + const { t } = useTranslation('translation', { keyPrefix: 'login' }); + const loading = + signLoading || + registerLoading || + channelsLoading || + loginWithChannelLoading; + const { config } = useSystemConfig(); + const registerEnabled = config?.registerEnabled !== 0; - const { switchStep } = useSwitchStep(Step.SignIn); + const { isLogin } = useAuth(); + useEffect(() => { + if (isLogin) { + navigate('/'); + } + }, [isLogin, navigate]); - return ( - - - {t('signUp')} - - - -
    - -
    - -
    -
    - ); -} + const handleLoginWithChannel = async (channel: string) => { + await loginWithChannel(channel); + }; -export function SignInCard() { - const { t } = useTranslate('login'); - const { switchStep } = useSwitchStep(Step.SignUp); + const changeTitle = () => { + if (title === 'login' && !registerEnabled) { + return; + } + setTitle((title) => (title === 'login' ? 'register' : 'login')); + }; - return ( - - - {t('login')} - - - - - - - ); -} + const FormSchema = z + .object({ + nickname: z.string().optional(), + email: z + .string() + .email() + .min(1, { message: t('emailPlaceholder') }), + password: z.string().min(1, { message: t('passwordPlaceholder') }), + remember: z.boolean().optional(), + }) + .superRefine((data, ctx) => { + if (title === 'register' && !data.nickname) { + ctx.addIssue({ + path: ['nickname'], + message: 'nicknamePlaceholder', + code: z.ZodIssueCode.custom, + }); + } + }); + const form = useForm({ + defaultValues: { + nickname: '', + email: '', + password: '', + confirmPassword: '', + remember: false, + }, + resolver: zodResolver(FormSchema), + }); + + const onCheck = async (params) => { + console.log('params', params); + try { + // const params = await form.validateFields(); + + const rsaPassWord = rsaPsw(params.password) as string; -export function VerifyEmailCard() { - // const { t } = useTranslate('login'); + if (title === 'login') { + const code = await login({ + email: `${params.email}`.trim(), + password: rsaPassWord, + }); + if (code === 0) { + navigate('/'); + } + } else { + const code = await register({ + nickname: params.nickname, + email: params.email, + password: rsaPassWord, + }); + if (code === 0) { + setTitle('login'); + } + } + } catch (errorInfo) { + console.log('Failed:', errorInfo); + } + }; return ( - - - Verify email - - -
    -
    -
    -

    - We’ve sent a 6-digit code to -

    -

    yifanwu92@gmail.com.

    -
    - +
    + + + + +
    +
    +
    + logo
    - -
    -
    -
    - ); -} + RAGFlow +
    +

    + A Leading RAG engine with Agent for superior LLM context. +

    +
    + Let's get started +
    +
    +
    + {/* Logo and Header */} -const Login = () => { - const [searchParams] = useSearchParams(); - const step = Number((searchParams.get('step') ?? Step.SignIn) as Step); + {/* Login Form */} +
    +

    + {title === 'login' + ? 'Sign in to Your Account' + : 'Create an Account'} +

    +
    +
    +
    + onCheck(data))} + > + ( + + {t('emailLabel')} + + + + + + )} + /> + {title === 'register' && ( + ( + + {t('nicknameLabel')} + + + + + + )} + /> + )} - return ( -
    -
    - {step === Step.SignIn && } - {step === Step.SignUp && } - {step === Step.VerifyEmail && } + ( + + {t('passwordLabel')} + + + + + + )} + /> + + {title === 'login' && ( + ( + + +
    + { + field.onChange(checked); + }} + /> + {t('rememberMe')} +
    +
    + +
    + )} + /> + )} + + {title === 'login' ? t('login') : t('continue')} + + {title === 'login' && channels && channels.length > 0 && ( +
    + {channels.map((item) => ( + + ))} +
    + )} + + + + {title === 'login' && registerEnabled && ( +
    +

    + {t('signInTip')} + +

    +
    + )} + {title === 'register' && ( +
    +

    + {t('signUpTip')} + +

    +
    + )} +
    ); diff --git a/web/src/pages/login-next/spotlight-top.tsx b/web/src/pages/login-next/spotlight-top.tsx new file mode 100644 index 00000000000..0615f09b5ff --- /dev/null +++ b/web/src/pages/login-next/spotlight-top.tsx @@ -0,0 +1,70 @@ +import { useIsDarkTheme } from '@/components/theme-provider'; +import React from 'react'; + +interface SpotlightProps { + className?: string; + opcity?: number; + coverage?: number; +} +/** + * + * @param opcity 0~1 default 0.5 + * @param coverage 0~100 default 60 + * @returns + */ +export const SpotlightTopLeft: React.FC = ({ + className, + opcity = 0.5, + coverage = 60, +}) => { + const isDark = useIsDarkTheme(); + const rgb = isDark ? '255, 255, 255' : '194, 221, 243'; + return ( +
    +
    +
    + ); +}; +/** + * + * @param opcity 0~1 default 0.5 + * @param coverage 0~100 default 60 + * @returns + */ +export const SpotlightTopRight: React.FC = ({ + className, + opcity = 0.5, + coverage = 60, +}) => { + const isDark = useIsDarkTheme(); + const rgb = isDark ? '255, 255, 255' : '194, 221, 243'; + return ( +
    +
    +
    + ); +}; From a0d5f8109826fb4f04fe818054fd95fc7d301a4a Mon Sep 17 00:00:00 2001 From: buua436 <66937541+buua436@users.noreply.github.com> Date: Sat, 11 Oct 2025 16:00:16 +0800 Subject: [PATCH 0805/1187] Feat: include author, journal name, volume, issue, page, and DOI in PubMed search results (#10481) ### What problem does this PR solve? issue: [#6571](https://github.com/infiniflow/ragflow/issues/6571) change: include author, journal name, volume, issue, page, and DOI in PubMed search results ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- agent/tools/pubmed.py | 53 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/agent/tools/pubmed.py b/agent/tools/pubmed.py index 6dce92a9be8..0920b3e2365 100644 --- a/agent/tools/pubmed.py +++ b/agent/tools/pubmed.py @@ -85,13 +85,7 @@ def _invoke(self, **kwargs): self._retrieve_chunks(pubmedcnt.findall("PubmedArticle"), get_title=lambda child: child.find("MedlineCitation").find("Article").find("ArticleTitle").text, get_url=lambda child: "https://pubmed.ncbi.nlm.nih.gov/" + child.find("MedlineCitation").find("PMID").text, - get_content=lambda child: child.find("MedlineCitation") \ - .find("Article") \ - .find("Abstract") \ - .find("AbstractText").text \ - if child.find("MedlineCitation")\ - .find("Article").find("Abstract") \ - else "No abstract available") + get_content=lambda child: self._format_pubmed_content(child),) return self.output("formalized_content") except Exception as e: last_e = e @@ -104,5 +98,50 @@ def _invoke(self, **kwargs): assert False, self.output() + def _format_pubmed_content(self, child): + """Extract structured reference info from PubMed XML""" + def safe_find(path): + node = child + for p in path.split("/"): + if node is None: + return None + node = node.find(p) + return node.text if node is not None and node.text else None + + title = safe_find("MedlineCitation/Article/ArticleTitle") or "No title" + abstract = safe_find("MedlineCitation/Article/Abstract/AbstractText") or "No abstract available" + journal = safe_find("MedlineCitation/Article/Journal/Title") or "Unknown Journal" + volume = safe_find("MedlineCitation/Article/Journal/JournalIssue/Volume") or "-" + issue = safe_find("MedlineCitation/Article/Journal/JournalIssue/Issue") or "-" + pages = safe_find("MedlineCitation/Article/Pagination/MedlinePgn") or "-" + + # Authors + authors = [] + for author in child.findall(".//AuthorList/Author"): + lastname = safe_find("LastName") or "" + forename = safe_find("ForeName") or "" + fullname = f"{forename} {lastname}".strip() + if fullname: + authors.append(fullname) + authors_str = ", ".join(authors) if authors else "Unknown Authors" + + # DOI + doi = None + for eid in child.findall(".//ArticleId"): + if eid.attrib.get("IdType") == "doi": + doi = eid.text + break + + return ( + f"Title: {title}\n" + f"Authors: {authors_str}\n" + f"Journal: {journal}\n" + f"Volume: {volume}\n" + f"Issue: {issue}\n" + f"Pages: {pages}\n" + f"DOI: {doi or '-'}\n" + f"Abstract: {abstract.strip()}" + ) + def thoughts(self) -> str: return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!")) From 7d2f65671f7ea6525fecb96c9804f7477f29ce72 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Sat, 11 Oct 2025 18:45:21 +0800 Subject: [PATCH 0806/1187] Feat: debugging toc part. (#10486) ### What problem does this PR solve? #10436 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- deepdoc/parser/pdf_parser.py | 2 +- rag/flow/splitter/schema.py | 2 +- rag/flow/tokenizer/tokenizer.py | 4 +++- rag/nlp/__init__.py | 12 ++++++------ rag/prompts/generator.py | 30 ++++++++++++++++++------------ rag/svr/task_executor.py | 6 +++--- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/deepdoc/parser/pdf_parser.py b/deepdoc/parser/pdf_parser.py index 2fb041e87ac..74e08e212f8 100644 --- a/deepdoc/parser/pdf_parser.py +++ b/deepdoc/parser/pdf_parser.py @@ -997,7 +997,7 @@ async def __img_ocr(i, id, img, chars, limiter): self.__ocr(i + 1, img, chars, zoomin, id) if callback and i % 6 == 5: - callback((i + 1) * 0.6 / len(self.page_images), msg="") + callback((i + 1) * 0.6 / len(self.page_images)) async def __img_ocr_launcher(): def __ocr_preprocess(): diff --git a/rag/flow/splitter/schema.py b/rag/flow/splitter/schema.py index 9144a4d9bec..9875d652caa 100644 --- a/rag/flow/splitter/schema.py +++ b/rag/flow/splitter/schema.py @@ -25,7 +25,7 @@ class SplitterFromUpstream(BaseModel): file: dict | None = Field(default=None) chunks: list[dict[str, Any]] | None = Field(default=None) - output_format: Literal["json", "markdown", "text", "html"] | None = Field(default=None) + output_format: Literal["json", "markdown", "text", "html", "chunks"] | None = Field(default=None) json_result: list[dict[str, Any]] | None = Field(default=None, alias="json") markdown_result: str | None = Field(default=None, alias="markdown") diff --git a/rag/flow/tokenizer/tokenizer.py b/rag/flow/tokenizer/tokenizer.py index 425e2039764..23e179496cc 100644 --- a/rag/flow/tokenizer/tokenizer.py +++ b/rag/flow/tokenizer/tokenizer.py @@ -126,7 +126,7 @@ async def _invoke(self, **kwargs): if ck.get("summary"): ck["content_ltks"] = rag_tokenizer.tokenize(str(ck["summary"])) ck["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(ck["content_ltks"]) - else: + elif ck.get("text"): ck["content_ltks"] = rag_tokenizer.tokenize(ck["text"]) ck["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(ck["content_ltks"]) if i % 100 == 99: @@ -155,6 +155,8 @@ async def _invoke(self, **kwargs): for i, ck in enumerate(chunks): ck["title_tks"] = rag_tokenizer.tokenize(re.sub(r"\.[a-zA-Z]+$", "", from_upstream.name)) ck["title_sm_tks"] = rag_tokenizer.fine_grained_tokenize(ck["title_tks"]) + if not ck.get("text"): + continue ck["content_ltks"] = rag_tokenizer.tokenize(ck["text"]) ck["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(ck["content_ltks"]) if i % 100 == 99: diff --git a/rag/nlp/__init__.py b/rag/nlp/__init__.py index 7362986791c..871190d268d 100644 --- a/rag/nlp/__init__.py +++ b/rag/nlp/__init__.py @@ -613,13 +613,13 @@ def add_chunk(t, pos): dels = get_delimiters(delimiter) for sec, pos in sections: if num_tokens_from_string(sec) < chunk_token_num: - add_chunk(sec, pos) + add_chunk("\n"+sec, pos) continue split_sec = re.split(r"(%s)" % dels, sec, flags=re.DOTALL) for sub_sec in split_sec: if re.match(f"^{dels}$", sub_sec): continue - add_chunk(sub_sec, pos) + add_chunk("\n"+sub_sec, pos) return cks @@ -669,13 +669,13 @@ def add_chunk(t, image, pos=""): for sub_sec in split_sec: if re.match(f"^{dels}$", sub_sec): continue - add_chunk(sub_sec, image, text_pos) + add_chunk("\n"+sub_sec, image, text_pos) else: split_sec = re.split(r"(%s)" % dels, text) for sub_sec in split_sec: if re.match(f"^{dels}$", sub_sec): continue - add_chunk(sub_sec, image) + add_chunk("\n"+sub_sec, image) return cks, result_images @@ -757,7 +757,7 @@ def add_chunk(t, image, pos=""): for sub_sec in split_sec: if re.match(f"^{dels}$", sub_sec): continue - add_chunk(sub_sec, image,"") + add_chunk("\n"+sub_sec, image,"") line = "" if line: @@ -765,7 +765,7 @@ def add_chunk(t, image, pos=""): for sub_sec in split_sec: if re.match(f"^{dels}$", sub_sec): continue - add_chunk(sub_sec, image,"") + add_chunk("\n"+sub_sec, image,"") return cks, images diff --git a/rag/prompts/generator.py b/rag/prompts/generator.py index 73e1201f567..eda3e89ec01 100644 --- a/rag/prompts/generator.py +++ b/rag/prompts/generator.py @@ -23,7 +23,7 @@ import json_repair import trio from api.utils import hash_str2int -from rag.nlp import is_chinese +from rag.nlp import rag_tokenizer from rag.prompts.template import load_prompt from rag.settings import TAG_FLD from rag.utils import encoder, num_tokens_from_string @@ -672,7 +672,7 @@ def assign_toc_levels(toc_secs, chat_mdl, gen_conf = {"temperature": 0.2}): TOC_FROM_TEXT_SYSTEM = load_prompt("toc_from_text_system") TOC_FROM_TEXT_USER = load_prompt("toc_from_text_user") # Generate TOC from text chunks with text llms -async def gen_toc_from_text(txt_info: dict, chat_mdl): +async def gen_toc_from_text(txt_info: dict, chat_mdl, callback=None): try: ans = gen_json( PROMPT_JINJA_ENV.from_string(TOC_FROM_TEXT_SYSTEM).render(), @@ -682,6 +682,8 @@ async def gen_toc_from_text(txt_info: dict, chat_mdl): ) print(ans, "::::::::::::::::::::::::::::::::::::", flush=True) txt_info["toc"] = ans if ans else [] + if callback: + callback(msg="") except Exception as e: logging.exception(e) @@ -707,14 +709,14 @@ def split_chunks(chunks, max_length: int): return result -async def run_toc_from_text(chunks, chat_mdl): +async def run_toc_from_text(chunks, chat_mdl, callback=None): input_budget = int(chat_mdl.max_length * INPUT_UTILIZATION) - num_tokens_from_string( TOC_FROM_TEXT_USER + TOC_FROM_TEXT_SYSTEM ) input_budget = 1024 if input_budget > 1024 else input_budget chunk_sections = split_chunks(chunks, input_budget) - res = [] + titles = [] chunks_res = [] async with trio.open_nursery() as nursery: @@ -722,21 +724,21 @@ async def run_toc_from_text(chunks, chat_mdl): if not chunk: continue chunks_res.append({"chunks": chunk}) - nursery.start_soon(gen_toc_from_text, chunks_res[-1], chat_mdl) + nursery.start_soon(gen_toc_from_text, chunks_res[-1], chat_mdl, callback) for chunk in chunks_res: - res.extend(chunk.get("toc", [])) + titles.extend(chunk.get("toc", [])) - print(res, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + print(titles, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") # Filter out entries with title == -1 + prune = len(titles) > 512 + max_len = 12 if prune else 22 filtered = [] - for x in res: + for x in titles: if not x.get("title") or x["title"] == "-1": continue - if is_chinese(x["title"]) and len(x["title"]) > 12: - continue - if len(x["title"].split(" ")) > 12: + if len(rag_tokenizer.tokenize(x["title"]).split(" ")) > max_len: continue if re.match(r"[0-9,.()/ -]+$", x["title"]): continue @@ -751,8 +753,12 @@ async def run_toc_from_text(chunks, chat_mdl): toc_with_levels = assign_toc_levels(raw_structure, chat_mdl, {"temperature": 0.0, "top_p": 0.9}) # Merge structure and content (by index) + prune = len(toc_with_levels) > 512 + max_lvl = sorted([t.get("level", "0") for t in toc_with_levels])[-1] merged = [] for _ , (toc_item, src_item) in enumerate(zip(toc_with_levels, filtered)): + if prune and toc_item.get("level", "0") >= max_lvl: + continue merged.append({ "level": toc_item.get("level", "0"), "title": toc_item.get("title", ""), @@ -776,7 +782,7 @@ def relevant_chunks_with_toc(query: str, toc:list[dict], chat_mdl, topn: int=6): print(ans, "::::::::::::::::::::::::::::::::::::", flush=True) id2score = {} for ti, sc in zip(toc, ans): - if sc.get("score", -1) < 1: + if not isinstance(sc, dict) or sc.get("score", -1) < 1: continue for id in ti.get("ids", []): if id not in id2score: diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index 08b232e49f6..c0b1d2c51e7 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -370,14 +370,14 @@ async def doc_question_proposal(chat_mdl, d, topn): nursery.start_soon(doc_question_proposal, chat_mdl, d, task["parser_config"]["auto_questions"]) progress_callback(msg="Question generation {} chunks completed in {:.2f}s".format(len(docs), timer() - st)) - if task["parser_config"].get("toc_extraction", True): + if task["parser_id"].lower() == "naive" and task["parser_config"].get("toc_extraction", False): progress_callback(msg="Start to generate table of content ...") chat_mdl = LLMBundle(task["tenant_id"], LLMType.CHAT, llm_name=task["llm_id"], lang=task["language"]) docs = sorted(docs, key=lambda d:( d.get("page_num_int", 0)[0] if isinstance(d.get("page_num_int", 0), list) else d.get("page_num_int", 0), d.get("top_int", 0)[0] if isinstance(d.get("top_int", 0), list) else d.get("top_int", 0) )) - toc: list[dict] = await run_toc_from_text([d["content_with_weight"] for d in docs], chat_mdl) + toc: list[dict] = await run_toc_from_text([d["content_with_weight"] for d in docs], chat_mdl, progress_callback) logging.info("------------ T O C -------------\n"+json.dumps(toc, ensure_ascii=False, indent=' ')) ii = 0 while ii < len(toc): @@ -387,7 +387,7 @@ async def doc_question_proposal(chat_mdl, d, topn): toc[ii]["ids"] = [docs[idx]["id"]] if ii == len(toc) -1: break - for jj in range(idx+1, int(toc[ii+1]["chunk_id"])): + for jj in range(idx+1, int(toc[ii+1]["chunk_id"])+1): toc[ii]["ids"].append(docs[jj]["id"]) except Exception as e: logging.exception(e) From 6a0f44841974bef96a062e920abacadaeecf5ee2 Mon Sep 17 00:00:00 2001 From: balibabu Date: Sat, 11 Oct 2025 18:45:38 +0800 Subject: [PATCH 0807/1187] Feat: Modify the default style of the agent node anchor #9869 (#10489) ### What problem does this PR solve? Feat: Modify the default style of the agent node anchor #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../canvas}/background.tsx | 0 web/src/locales/en.ts | 16 ++++---- web/src/pages/agent/canvas/edge/index.tsx | 2 +- web/src/pages/agent/canvas/index.tsx | 16 ++------ .../pages/agent/canvas/node/agent-node.tsx | 32 +++++++-------- .../agent/canvas/node/categorize-node.tsx | 12 +----- web/src/pages/agent/canvas/node/handle.tsx | 23 +++++++++-- web/src/pages/agent/canvas/node/index.tsx | 13 ++---- .../agent/canvas/node/iteration-node.tsx | 18 ++------ .../pages/agent/canvas/node/logic-node.tsx | 41 ------------------- .../pages/agent/canvas/node/message-node.tsx | 23 ++--------- .../pages/agent/canvas/node/node-wrapper.tsx | 2 +- .../agent/canvas/node/retrieval-node.tsx | 15 +------ .../pages/agent/canvas/node/switch-node.tsx | 12 ++---- web/src/pages/agent/canvas/node/tool-node.tsx | 1 + web/src/pages/agent/canvas/node/toolbar.tsx | 2 +- web/src/pages/agent/constant.tsx | 11 +---- .../agent/form-sheet/form-config-map.tsx | 3 -- web/src/pages/agent/hooks.tsx | 3 +- web/src/pages/agent/hooks/use-add-node.ts | 2 - web/src/pages/agent/version-dialog/index.tsx | 4 +- web/src/pages/data-flow/canvas/index.tsx | 4 +- .../pages/data-flow/components/background.tsx | 13 ------ .../pages/data-flow/version-dialog/index.tsx | 4 +- 24 files changed, 77 insertions(+), 195 deletions(-) rename web/src/{pages/agent/components => components/canvas}/background.tsx (100%) delete mode 100644 web/src/pages/agent/canvas/node/logic-node.tsx delete mode 100644 web/src/pages/data-flow/components/background.tsx diff --git a/web/src/pages/agent/components/background.tsx b/web/src/components/canvas/background.tsx similarity index 100% rename from web/src/pages/agent/components/background.tsx rename to web/src/components/canvas/background.tsx diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index e0628433ad1..7b29800a3d7 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -116,7 +116,7 @@ export default { generate: 'Generate', raptor: 'Raptor', processingType: 'Processing Type', - dataPipeline: 'Data Pipeline', + dataPipeline: 'Ingestion pipeline', operations: 'Operations', taskId: 'Task ID', duration: 'Duration', @@ -284,11 +284,11 @@ export default { fileFilter: 'File Filter', setDefaultTip: '', setDefault: 'Set as Default', - eidtLinkDataPipeline: 'Edit Data Pipeline', - linkPipelineSetTip: 'Manage data pipeline linkage with this dataset', + eidtLinkDataPipeline: 'Edit Ingestion pipeline', + linkPipelineSetTip: 'Manage Ingestion pipeline linkage with this dataset', default: 'Default', - dataPipeline: 'Data Pipeline', - linkDataPipeline: 'Link Data Pipeline', + dataPipeline: 'Ingestion pipeline', + linkDataPipeline: 'Link Ingestion pipeline', enableAutoGenerate: 'Enable Auto Generate', teamPlaceholder: 'Please select a team.', dataFlowPlaceholder: 'Please select a pipeline.', @@ -1595,7 +1595,7 @@ This delimiter is used to split the input text into several text pieces echo of createFromTemplate: 'Create from template', importJsonFile: 'Import JSON file', ceateAgent: 'Agent flow', - createPipeline: 'Data pipeline', + createPipeline: 'Ingestion pipeline', chooseAgentType: 'Choose Agent Type', }, llmTools: { @@ -1688,9 +1688,9 @@ This delimiter is used to split the input text into several text pieces echo of

    To keep them, please click Rerun to re-run the current stage.

    `, changeStepModalConfirmText: 'Switch Anyway', changeStepModalCancelText: 'Cancel', - unlinkPipelineModalTitle: 'Unlink data pipeline', + unlinkPipelineModalTitle: 'Unlink Ingestion pipeline', unlinkPipelineModalContent: ` -

    Once unlinked, this Dataset will no longer be connected to the current Data Pipeline.

    +

    Once unlinked, this Dataset will no longer be connected to the current Ingestion pipeline.

    Files that are already being parsed will continue until completion

    Files that are not yet parsed will no longer be processed


    Are you sure you want to proceed?

    `, diff --git a/web/src/pages/agent/canvas/edge/index.tsx b/web/src/pages/agent/canvas/edge/index.tsx index 3c39fbdac30..5b66e5be84e 100644 --- a/web/src/pages/agent/canvas/edge/index.tsx +++ b/web/src/pages/agent/canvas/edge/index.tsx @@ -102,7 +102,7 @@ function InnerButtonEdge({ ...showHighlight, ...placeholderHighlightStyle, }} - className={cn('text-text-secondary')} + className={cn('text-text-disabled')} /> diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index b36b9a6c534..e6fa087f328 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -1,4 +1,4 @@ -import { useIsDarkTheme, useTheme } from '@/components/theme-provider'; +import { useTheme } from '@/components/theme-provider'; import { Tooltip, TooltipContent, @@ -19,7 +19,6 @@ import { NotebookPen } from 'lucide-react'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ChatSheet } from '../chat/chat-sheet'; -import { AgentBackground } from '../components/background'; import { AgentChatContext, AgentChatLogContext, @@ -42,6 +41,7 @@ import { useMoveNote } from '../hooks/use-move-note'; import { usePlaceholderManager } from '../hooks/use-placeholder-manager'; import { useDropdownManager } from './context'; +import { AgentBackground } from '@/components/canvas/background'; import Spotlight from '@/components/spotlight'; import { useHideFormSheetOnNodeDeletion, @@ -61,7 +61,6 @@ import { GenerateNode } from './node/generate-node'; import { InvokeNode } from './node/invoke-node'; import { IterationNode, IterationStartNode } from './node/iteration-node'; import { KeywordNode } from './node/keyword-node'; -import { LogicNode } from './node/logic-node'; import { MessageNode } from './node/message-node'; import NoteNode from './node/note-node'; import { PlaceholderNode } from './node/placeholder-node'; @@ -78,7 +77,6 @@ export const nodeTypes: NodeTypes = { beginNode: BeginNode, placeholderNode: PlaceholderNode, relevantNode: RelevantNode, - logicNode: LogicNode, noteNode: NoteNode, switchNode: SwitchNode, generateNode: GenerateNode, @@ -173,8 +171,6 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { } }; - const isDarkTheme = useIsDarkTheme(); - useHideFormSheetOnNodeDeletion({ hideFormDrawer }); const { visible, hideModal, showModal } = useSetModalState(); @@ -243,7 +239,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { >
    { @@ -47,7 +48,7 @@ export function CommonHandle({ showModal(); }} > - + {visible && ( { @@ -62,3 +63,19 @@ export function CommonHandle({ ); } + +export function LeftEndHandle({ + isConnectable, + ...props +}: Omit) { + return ( + + ); +} diff --git a/web/src/pages/agent/canvas/node/index.tsx b/web/src/pages/agent/canvas/node/index.tsx index a1d48955b70..ccd00b9cab4 100644 --- a/web/src/pages/agent/canvas/node/index.tsx +++ b/web/src/pages/agent/canvas/node/index.tsx @@ -3,8 +3,8 @@ import { NodeProps, Position } from '@xyflow/react'; import { memo } from 'react'; import { NodeHandleId } from '../../constant'; import { needsSingleStepDebugging } from '../../utils'; -import { CommonHandle } from './handle'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import { CommonHandle, LeftEndHandle } from './handle'; +import { RightHandleStyle } from './handle-icon'; import NodeHeader from './node-header'; import { NodeWrapper } from './node-wrapper'; import { ToolBar } from './toolbar'; @@ -23,14 +23,7 @@ function InnerRagNode({ showRun={needsSingleStepDebugging(data.label)} > - +
    - + - ) { - return ( - - - - - - - - ); -} - -export const LogicNode = memo(InnerLogicNode); diff --git a/web/src/pages/agent/canvas/node/message-node.tsx b/web/src/pages/agent/canvas/node/message-node.tsx index 057845a63e8..cf680275741 100644 --- a/web/src/pages/agent/canvas/node/message-node.tsx +++ b/web/src/pages/agent/canvas/node/message-node.tsx @@ -1,35 +1,21 @@ import { IMessageNode } from '@/interfaces/database/flow'; -import { NodeProps, Position } from '@xyflow/react'; +import { NodeProps } from '@xyflow/react'; import { Flex } from 'antd'; import classNames from 'classnames'; import { get } from 'lodash'; import { memo } from 'react'; -import { NodeHandleId } from '../../constant'; -import { CommonHandle } from './handle'; -import { LeftHandleStyle } from './handle-icon'; +import { LeftEndHandle } from './handle'; import styles from './index.less'; import NodeHeader from './node-header'; import { NodeWrapper } from './node-wrapper'; import { ToolBar } from './toolbar'; -function InnerMessageNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { +function InnerMessageNode({ id, data, selected }: NodeProps) { const messages: string[] = get(data, 'form.messages', []); return ( - + {/* 0, })} > - {messages.map((message, idx) => { return ( diff --git a/web/src/pages/agent/canvas/node/node-wrapper.tsx b/web/src/pages/agent/canvas/node/node-wrapper.tsx index ab53466ff78..b26380d9b1f 100644 --- a/web/src/pages/agent/canvas/node/node-wrapper.tsx +++ b/web/src/pages/agent/canvas/node/node-wrapper.tsx @@ -7,7 +7,7 @@ export function NodeWrapper({ children, className, selected }: IProps) { return (
    - + diff --git a/web/src/pages/agent/canvas/node/switch-node.tsx b/web/src/pages/agent/canvas/node/switch-node.tsx index b62e45adb7d..ea4b1cc8305 100644 --- a/web/src/pages/agent/canvas/node/switch-node.tsx +++ b/web/src/pages/agent/canvas/node/switch-node.tsx @@ -2,10 +2,10 @@ import { Card, CardContent } from '@/components/ui/card'; import { ISwitchCondition, ISwitchNode } from '@/interfaces/database/flow'; import { NodeProps, Position } from '@xyflow/react'; import { memo, useCallback } from 'react'; -import { NodeHandleId, SwitchOperatorOptions } from '../../constant'; +import { SwitchOperatorOptions } from '../../constant'; import { LogicalOperatorIcon } from '../../form/switch-form'; import { useGetVariableLabelByValue } from '../../hooks/use-get-begin-query'; -import { CommonHandle } from './handle'; +import { CommonHandle, LeftEndHandle } from './handle'; import { RightHandleStyle } from './handle-icon'; import NodeHeader from './node-header'; import { NodeWrapper } from './node-wrapper'; @@ -66,13 +66,7 @@ function InnerSwitchNode({ id, data, selected }: NodeProps) { return ( - +
    {positions.map((position, idx) => { diff --git a/web/src/pages/agent/canvas/node/tool-node.tsx b/web/src/pages/agent/canvas/node/tool-node.tsx index 42c7bf3b566..b5212f3adf2 100644 --- a/web/src/pages/agent/canvas/node/tool-node.tsx +++ b/web/src/pages/agent/canvas/node/tool-node.tsx @@ -49,6 +49,7 @@ function InnerToolNode({ type="target" position={Position.Top} isConnectable={isConnectable} + className="!bg-accent-primary !size-2" >
      {tools.map((x) => ( diff --git a/web/src/pages/agent/canvas/node/toolbar.tsx b/web/src/pages/agent/canvas/node/toolbar.tsx index a8b5cd13cc7..f36cfe2afd1 100644 --- a/web/src/pages/agent/canvas/node/toolbar.tsx +++ b/web/src/pages/agent/canvas/node/toolbar.tsx @@ -66,7 +66,7 @@ export function ToolBar({ return ( - {children} + {children}
      diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 3079f3b1a29..35a67cae4b7 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -69,7 +69,6 @@ export enum Operator { AkShare = 'AkShare', YahooFinance = 'YahooFinance', Jin10 = 'Jin10', - Concentrator = 'Concentrator', TuShare = 'TuShare', Note = 'Note', Crawler = 'Crawler', @@ -102,7 +101,6 @@ export const AgentOperatorList = [ Operator.RewriteQuestion, Operator.KeywordExtract, Operator.Switch, - Operator.Concentrator, Operator.Iteration, Operator.WaitingDialogue, Operator.Note, @@ -129,9 +127,6 @@ export const componentMenuList = [ { name: Operator.Switch, }, - { - name: Operator.Concentrator, - }, { name: Operator.Iteration, }, @@ -544,8 +539,6 @@ export const initialJin10Values = { ...initialQueryBaseValues, }; -export const initialConcentratorValues = {}; - export const initialTuShareValues = { token: 'xxx', src: 'eastmoney', @@ -824,7 +817,6 @@ export const RestrictedUpstreamMap = { [Operator.AkShare]: [Operator.Begin], [Operator.YahooFinance]: [Operator.Begin], [Operator.Jin10]: [Operator.Begin], - [Operator.Concentrator]: [Operator.Begin], [Operator.TuShare]: [Operator.Begin], [Operator.Crawler]: [Operator.Begin], [Operator.Note]: [], @@ -840,6 +832,7 @@ export const RestrictedUpstreamMap = { [Operator.StringTransform]: [Operator.Begin], [Operator.UserFillUp]: [Operator.Begin], [Operator.Tool]: [Operator.Begin], + [Operator.Placeholder]: [Operator.Begin], }; export const NodeMap = { @@ -865,7 +858,6 @@ export const NodeMap = { [Operator.SearXNG]: 'ragNode', [Operator.ExeSQL]: 'ragNode', [Operator.Switch]: 'switchNode', - [Operator.Concentrator]: 'logicNode', [Operator.WenCai]: 'ragNode', [Operator.AkShare]: 'ragNode', [Operator.YahooFinance]: 'ragNode', @@ -908,7 +900,6 @@ export const BeginQueryTypeIconMap = { export const NoDebugOperatorsList = [ Operator.Begin, - Operator.Concentrator, Operator.Message, Operator.RewriteQuestion, Operator.Switch, diff --git a/web/src/pages/agent/form-sheet/form-config-map.tsx b/web/src/pages/agent/form-sheet/form-config-map.tsx index 0f0cbde7063..15ca5ffa344 100644 --- a/web/src/pages/agent/form-sheet/form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/form-config-map.tsx @@ -136,9 +136,6 @@ export const FormConfigMap = { [Operator.SearXNG]: { component: SearXNGForm, }, - [Operator.Concentrator]: { - component: () => <>, - }, [Operator.Note]: { component: () => <>, }, diff --git a/web/src/pages/agent/hooks.tsx b/web/src/pages/agent/hooks.tsx index 1cb972c2f15..77a53abb8b6 100644 --- a/web/src/pages/agent/hooks.tsx +++ b/web/src/pages/agent/hooks.tsx @@ -25,7 +25,6 @@ import { initialBingValues, initialCategorizeValues, initialCodeValues, - initialConcentratorValues, initialCrawlerValues, initialDeepLValues, initialDuckValues, @@ -124,7 +123,6 @@ export const useInitializeOperatorParams = () => { [Operator.AkShare]: initialAkShareValues, [Operator.YahooFinance]: initialYahooFinanceValues, [Operator.Jin10]: initialJin10Values, - [Operator.Concentrator]: initialConcentratorValues, [Operator.TuShare]: initialTuShareValues, [Operator.Note]: initialNoteValues, [Operator.Crawler]: initialCrawlerValues, @@ -140,6 +138,7 @@ export const useInitializeOperatorParams = () => { [Operator.Tool]: {}, [Operator.UserFillUp]: initialUserFillUpValues, [Operator.StringTransform]: initialStringTransformValues, + [Operator.Placeholder]: {}, }; }, [llmId]); diff --git a/web/src/pages/agent/hooks/use-add-node.ts b/web/src/pages/agent/hooks/use-add-node.ts index 58f487921f0..69a5b456b96 100644 --- a/web/src/pages/agent/hooks/use-add-node.ts +++ b/web/src/pages/agent/hooks/use-add-node.ts @@ -18,7 +18,6 @@ import { initialBingValues, initialCategorizeValues, initialCodeValues, - initialConcentratorValues, initialCrawlerValues, initialDeepLValues, initialDuckValues, @@ -100,7 +99,6 @@ export const useInitializeOperatorParams = () => { [Operator.AkShare]: initialAkShareValues, [Operator.YahooFinance]: initialYahooFinanceValues, [Operator.Jin10]: initialJin10Values, - [Operator.Concentrator]: initialConcentratorValues, [Operator.TuShare]: initialTuShareValues, [Operator.Note]: initialNoteValues, [Operator.Crawler]: initialCrawlerValues, diff --git a/web/src/pages/agent/version-dialog/index.tsx b/web/src/pages/agent/version-dialog/index.tsx index 9a0b9741ccb..1e61ee291e2 100644 --- a/web/src/pages/agent/version-dialog/index.tsx +++ b/web/src/pages/agent/version-dialog/index.tsx @@ -1,3 +1,5 @@ +import { AgentBackground } from '@/components/canvas/background'; +import Spotlight from '@/components/spotlight'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { @@ -22,7 +24,6 @@ import { ArrowDownToLine } from 'lucide-react'; import { ReactNode, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { nodeTypes } from '../canvas'; -import { AgentBackground } from '../components/background'; export function VersionDialog({ hideModal, @@ -121,6 +122,7 @@ export function VersionDialog({ minZoom={0.1} > +
      diff --git a/web/src/pages/data-flow/canvas/index.tsx b/web/src/pages/data-flow/canvas/index.tsx index 07410897c04..1023fe904aa 100644 --- a/web/src/pages/data-flow/canvas/index.tsx +++ b/web/src/pages/data-flow/canvas/index.tsx @@ -21,7 +21,6 @@ import '@xyflow/react/dist/style.css'; import { NotebookPen } from 'lucide-react'; import { memo, useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { AgentBackground } from '../components/background'; import { AgentInstanceContext, HandleContext } from '../context'; import FormSheet from '../form-sheet/next'; @@ -31,6 +30,8 @@ import { useBeforeDelete } from '../hooks/use-before-delete'; import { useMoveNote } from '../hooks/use-move-note'; import { useDropdownManager } from './context'; +import { AgentBackground } from '@/components/canvas/background'; +import Spotlight from '@/components/spotlight'; import { useRunDataflow } from '../hooks/use-run-dataflow'; import { useHideFormSheetOnNodeDeletion, @@ -263,6 +264,7 @@ function DataFlowCanvas({ drawerVisible, hideDrawer, showLogSheet }: IProps) { onBeforeDelete={handleBeforeDelete} > + diff --git a/web/src/pages/data-flow/components/background.tsx b/web/src/pages/data-flow/components/background.tsx deleted file mode 100644 index cf7b18f45c7..00000000000 --- a/web/src/pages/data-flow/components/background.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useIsDarkTheme } from '@/components/theme-provider'; -import { Background } from '@xyflow/react'; - -export function AgentBackground() { - const isDarkTheme = useIsDarkTheme(); - - return ( - - ); -} diff --git a/web/src/pages/data-flow/version-dialog/index.tsx b/web/src/pages/data-flow/version-dialog/index.tsx index 9a0b9741ccb..1e61ee291e2 100644 --- a/web/src/pages/data-flow/version-dialog/index.tsx +++ b/web/src/pages/data-flow/version-dialog/index.tsx @@ -1,3 +1,5 @@ +import { AgentBackground } from '@/components/canvas/background'; +import Spotlight from '@/components/spotlight'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { @@ -22,7 +24,6 @@ import { ArrowDownToLine } from 'lucide-react'; import { ReactNode, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { nodeTypes } from '../canvas'; -import { AgentBackground } from '../components/background'; export function VersionDialog({ hideModal, @@ -121,6 +122,7 @@ export function VersionDialog({ minZoom={0.1} > +
    From c21cea20387bfcdeef22928b0a5279ccabf7cce5 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Sat, 11 Oct 2025 18:45:55 +0800 Subject: [PATCH 0808/1187] Fix: Added table of contents extraction functionality and optimized form item layout #9869 (#10492) ### What problem does this PR solve? Fix: Added table of contents extraction functionality and optimized form item layout #9869 - Added `EnableTocToggle` component to toggle table of contents extraction on and off - Added multiple parser configuration components (such as naive, book, laws, etc.), displaying different parser components based on built-in slicing methods ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../components/chunk-method-dialog/index.tsx | 5 +- .../use-default-parser-values.ts | 1 + .../components/data-pipeline-select/index.tsx | 136 ++++++++++----- web/src/components/delimiter-form-field.tsx | 2 +- .../components/excel-to-html-form-field.tsx | 2 +- .../layout-recognize-form-field.tsx | 2 +- .../max-token-number-from-field.tsx | 2 +- .../components/slider-input-form-field.tsx | 2 +- web/src/components/ui/radio.tsx | 3 +- web/src/hooks/logic-hooks/navigate-hooks.ts | 3 +- web/src/locales/en.ts | 7 +- web/src/locales/zh.ts | 7 +- .../dataset-setting/chunk-method-form.tsx | 64 +++++++ .../dataset-setting/components/tag-item.tsx | 25 +-- .../dataset-setting/configuration/audio.tsx | 20 +++ .../dataset-setting/configuration/book.tsx | 28 +++ .../configuration/common-item.tsx | 61 ++++++- .../dataset-setting/configuration/email.tsx | 18 ++ .../configuration/knowledge-graph.tsx | 15 ++ .../dataset-setting/configuration/laws.tsx | 29 ++++ .../dataset-setting/configuration/manual.tsx | 27 +++ .../dataset-setting/configuration/naive.tsx | 33 ++++ .../dataset-setting/configuration/one.tsx | 21 +++ .../dataset-setting/configuration/paper.tsx | 28 +++ .../dataset-setting/configuration/picture.tsx | 18 ++ .../configuration/presentation.tsx | 29 ++++ .../dataset-setting/configuration/qa.tsx | 10 ++ .../dataset-setting/configuration/resume.tsx | 10 ++ .../dataset-setting/configuration/table.tsx | 12 ++ .../dataset-setting/configuration/tag.tsx | 5 + .../dataset/dataset-setting/form-schema.ts | 161 ++++++++++-------- .../pages/dataset/dataset-setting/index.tsx | 64 +++++-- .../dataset/dataset-setting/saving-button.tsx | 1 + web/src/pages/dataset/sidebar/index.tsx | 10 +- .../datasets/dataset-creating-dialog.tsx | 4 +- 35 files changed, 693 insertions(+), 172 deletions(-) create mode 100644 web/src/pages/dataset/dataset-setting/chunk-method-form.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/audio.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/book.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/email.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/knowledge-graph.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/laws.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/manual.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/naive.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/one.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/paper.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/picture.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/presentation.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/qa.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/resume.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/table.tsx create mode 100644 web/src/pages/dataset/dataset-setting/configuration/tag.tsx diff --git a/web/src/components/chunk-method-dialog/index.tsx b/web/src/components/chunk-method-dialog/index.tsx index 8c1bb855e49..f73680ad863 100644 --- a/web/src/components/chunk-method-dialog/index.tsx +++ b/web/src/components/chunk-method-dialog/index.tsx @@ -20,6 +20,7 @@ import { IParserConfig } from '@/interfaces/database/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { ChunkMethodItem, + EnableTocToggle, ParseTypeItem, } from '@/pages/dataset/dataset-setting/configuration/common-item'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -113,6 +114,7 @@ export function ChunkMethodDialog({ auto_keywords: z.coerce.number().optional(), auto_questions: z.coerce.number().optional(), html4excel: z.boolean().optional(), + toc_extraction: z.boolean().optional(), // raptor: z // .object({ // use_raptor: z.boolean().optional(), @@ -247,7 +249,7 @@ export function ChunkMethodDialog({ }, [parseType, form]); return ( - + {t('knowledgeDetails.chunkMethod')} @@ -338,6 +340,7 @@ export function ChunkMethodDialog({ show={showAutoKeywords(selectedTag) || showExcelToHtml} className="space-y-3" > + {showAutoKeywords(selectedTag) && ( <> diff --git a/web/src/components/chunk-method-dialog/use-default-parser-values.ts b/web/src/components/chunk-method-dialog/use-default-parser-values.ts index 829b98605e2..238047db6b4 100644 --- a/web/src/components/chunk-method-dialog/use-default-parser-values.ts +++ b/web/src/components/chunk-method-dialog/use-default-parser-values.ts @@ -15,6 +15,7 @@ export function useDefaultParserValues() { auto_keywords: 0, auto_questions: 0, html4excel: false, + toc_extraction: false, // raptor: { // use_raptor: false, // prompt: t('knowledgeConfiguration.promptText'), diff --git a/web/src/components/data-pipeline-select/index.tsx b/web/src/components/data-pipeline-select/index.tsx index 76dd15fa13b..18e246529b8 100644 --- a/web/src/components/data-pipeline-select/index.tsx +++ b/web/src/components/data-pipeline-select/index.tsx @@ -1,5 +1,7 @@ import { AgentCategory } from '@/constants/agent'; +import { FormLayout } from '@/constants/form'; import { useTranslate } from '@/hooks/common-hooks'; +import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useFetchAgentList } from '@/hooks/use-agent-request'; import { buildSelectOptions } from '@/utils/component-util'; import { ArrowUpRight } from 'lucide-react'; @@ -21,18 +23,27 @@ export interface IDataPipelineSelectNode { } interface IProps { - toDataPipeline?: () => void; + showToDataPipeline?: boolean; formFieldName: string; isMult?: boolean; setDataList?: (data: IDataPipelineSelectNode[]) => void; + layout?: FormLayout; } export function DataFlowSelect(props: IProps) { - const { toDataPipeline, formFieldName, isMult = false, setDataList } = props; + const { + showToDataPipeline, + formFieldName, + isMult = false, + setDataList, + layout = FormLayout.Vertical, + } = props; + const { t } = useTranslate('knowledgeConfiguration'); const form = useFormContext(); + const { navigateToAgents } = useNavigatePage(); const toDataPipLine = () => { - toDataPipeline?.(); + navigateToAgents(); }; const { data: dataPipelineOptions } = useFetchAgentList({ canvas_category: AgentCategory.DataflowCanvas, @@ -69,47 +80,92 @@ export function DataFlowSelect(props: IProps) { name={formFieldName} render={({ field }) => ( -
    -
    - - {t('dataPipeline')} - - {toDataPipeline && ( -
    +
    + - {t('buildItFromScratch')} - -
    - )} + {t('manualSetup')} + + {showToDataPipeline && ( +
    + {t('buildItFromScratch')} + +
    + )} +
    + +
    + + <> + {!isMult && ( + + )} + {isMult && ( + + )} + + +
    + )} + {layout === FormLayout.Horizontal && ( +
    +
    + + {t('manualSetup')} + +
    -
    - - <> - {!isMult && ( - - )} - {isMult && ( - - )} - - +
    + {showToDataPipeline && ( +
    + {t('buildItFromScratch')} + +
    + )} + + <> + {!isMult && ( + + )} + {isMult && ( + + )} + + +
    -
    + )}
    diff --git a/web/src/components/delimiter-form-field.tsx b/web/src/components/delimiter-form-field.tsx index ef23fec3b3b..271721021e6 100644 --- a/web/src/components/delimiter-form-field.tsx +++ b/web/src/components/delimiter-form-field.tsx @@ -61,7 +61,7 @@ export function DelimiterFormField() { {t('knowledgeDetails.delimiter')} diff --git a/web/src/components/excel-to-html-form-field.tsx b/web/src/components/excel-to-html-form-field.tsx index a5158740591..13ff8b821e4 100644 --- a/web/src/components/excel-to-html-form-field.tsx +++ b/web/src/components/excel-to-html-form-field.tsx @@ -28,7 +28,7 @@ export function ExcelToHtmlFormField() {
    {t('html4excel')} diff --git a/web/src/components/layout-recognize-form-field.tsx b/web/src/components/layout-recognize-form-field.tsx index 0e5b660bb0c..d0991c0dbef 100644 --- a/web/src/components/layout-recognize-form-field.tsx +++ b/web/src/components/layout-recognize-form-field.tsx @@ -79,7 +79,7 @@ export function LayoutRecognizeFormField({ > diff --git a/web/src/components/max-token-number-from-field.tsx b/web/src/components/max-token-number-from-field.tsx index c4e07da4f60..b01598d9341 100644 --- a/web/src/components/max-token-number-from-field.tsx +++ b/web/src/components/max-token-number-from-field.tsx @@ -17,7 +17,7 @@ export function MaxTokenNumberFormField({ max = 2048, initialValue }: IProps) { tooltip={t('chunkTokenNumberTip')} max={max} defaultValue={initialValue ?? 0} - layout={FormLayout.Vertical} + layout={FormLayout.Horizontal} > ); } diff --git a/web/src/components/slider-input-form-field.tsx b/web/src/components/slider-input-form-field.tsx index 7550e9653cd..8972222a8a8 100644 --- a/web/src/components/slider-input-form-field.tsx +++ b/web/src/components/slider-input-form-field.tsx @@ -36,7 +36,7 @@ export function SliderInputFormField({ tooltip, defaultValue, className, - layout = FormLayout.Vertical, + layout = FormLayout.Horizontal, }: SliderInputFormFieldProps) { const form = useFormContext(); diff --git a/web/src/components/ui/radio.tsx b/web/src/components/ui/radio.tsx index 5db0fca4365..eeea31201fe 100644 --- a/web/src/components/ui/radio.tsx +++ b/web/src/components/ui/radio.tsx @@ -1,5 +1,4 @@ import { cn } from '@/lib/utils'; -import { Radio as LucideRadio } from 'lucide-react'; import React, { useContext, useState } from 'react'; const RadioGroupContext = React.createContext<{ @@ -57,7 +56,7 @@ function Radio({ value, checked, disabled, onChange, children }: RadioProps) { onClick={handleClick} > {isChecked && ( - +
    )} {children && {children}} diff --git a/web/src/hooks/logic-hooks/navigate-hooks.ts b/web/src/hooks/logic-hooks/navigate-hooks.ts index 642b8c3c0ed..6f852ccfa78 100644 --- a/web/src/hooks/logic-hooks/navigate-hooks.ts +++ b/web/src/hooks/logic-hooks/navigate-hooks.ts @@ -19,7 +19,8 @@ export const useNavigatePage = () => { const navigateToDataset = useCallback( (id: string) => () => { - navigate(`${Routes.DatasetBase}${Routes.DataSetOverview}/${id}`); + // navigate(`${Routes.DatasetBase}${Routes.DataSetOverview}/${id}`); + navigate(`${Routes.Dataset}/${id}`); }, [navigate], ); diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 7b29800a3d7..d112d0891e8 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -126,8 +126,8 @@ export default { startDate: 'Start Date', source: 'Source', fileName: 'File Name', - datasetLogs: 'Dataset Logs', - fileLogs: 'File Logs', + datasetLogs: 'Dataset', + fileLogs: 'File', overview: 'Overview', success: 'Success', failed: 'Failed', @@ -270,6 +270,9 @@ export default { reRankModelWaring: 'Re-rank model is very time consuming.', }, knowledgeConfiguration: { + tocExtraction: 'toc toggle', + tocExtractionTip: + " For existing chunks, generate a hierarchical table of contents (one directory per file). During queries, when Directory Enhancement is activated, the system will use a large model to determine which directory items are relevant to the user's question, thereby identifying the relevant chunks.", deleteGenerateModalContent: `

    Deleting the generated {{type}} results will remove all derived entities and relationships from this dataset. diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index e9e0db3ce8c..f582099955d 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -114,8 +114,8 @@ export default { startDate: '开始时间', source: '来源', fileName: '文件名', - datasetLogs: '数据集日志', - fileLogs: '文件日志', + datasetLogs: '数据集', + fileLogs: '文件', overview: '概览', success: '成功', failed: '失败', @@ -255,6 +255,9 @@ export default { theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除', }, knowledgeConfiguration: { + tocExtraction: '目录提取', + tocExtractionTip: + '对于已有的chunk生成层级结构的目录信息(每个文件一个目录)。在查询时,激活`目录增强`后,系统会用大模型去判断用户问题和哪些目录项相关,从而找到相关的chunk。', deleteGenerateModalContent: `

    删除生成的 {{type}} 结果 将从此数据集中移除所有派生实体和关系。 diff --git a/web/src/pages/dataset/dataset-setting/chunk-method-form.tsx b/web/src/pages/dataset/dataset-setting/chunk-method-form.tsx new file mode 100644 index 00000000000..8d6debc165c --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/chunk-method-form.tsx @@ -0,0 +1,64 @@ +import { useFormContext, useWatch } from 'react-hook-form'; + +import { DocumentParserType } from '@/constants/knowledge'; +import { useMemo } from 'react'; +import { AudioConfiguration } from './configuration/audio'; +import { BookConfiguration } from './configuration/book'; +import { EmailConfiguration } from './configuration/email'; +import { KnowledgeGraphConfiguration } from './configuration/knowledge-graph'; +import { LawsConfiguration } from './configuration/laws'; +import { ManualConfiguration } from './configuration/manual'; +import { NaiveConfiguration } from './configuration/naive'; +import { OneConfiguration } from './configuration/one'; +import { PaperConfiguration } from './configuration/paper'; +import { PictureConfiguration } from './configuration/picture'; +import { PresentationConfiguration } from './configuration/presentation'; +import { QAConfiguration } from './configuration/qa'; +import { ResumeConfiguration } from './configuration/resume'; +import { TableConfiguration } from './configuration/table'; +import { TagConfiguration } from './configuration/tag'; + +const ConfigurationComponentMap = { + [DocumentParserType.Naive]: NaiveConfiguration, + [DocumentParserType.Qa]: QAConfiguration, + [DocumentParserType.Resume]: ResumeConfiguration, + [DocumentParserType.Manual]: ManualConfiguration, + [DocumentParserType.Table]: TableConfiguration, + [DocumentParserType.Paper]: PaperConfiguration, + [DocumentParserType.Book]: BookConfiguration, + [DocumentParserType.Laws]: LawsConfiguration, + [DocumentParserType.Presentation]: PresentationConfiguration, + [DocumentParserType.Picture]: PictureConfiguration, + [DocumentParserType.One]: OneConfiguration, + [DocumentParserType.Audio]: AudioConfiguration, + [DocumentParserType.Email]: EmailConfiguration, + [DocumentParserType.Tag]: TagConfiguration, + [DocumentParserType.KnowledgeGraph]: KnowledgeGraphConfiguration, +}; + +function EmptyComponent() { + return

    ; +} + +export function ChunkMethodForm() { + const form = useFormContext(); + + const finalParserId: DocumentParserType = useWatch({ + control: form.control, + name: 'parser_id', + }); + + const ConfigurationComponent = useMemo(() => { + return finalParserId + ? ConfigurationComponentMap[finalParserId] + : EmptyComponent; + }, [finalParserId]); + + return ( +
    +
    + +
    +
    + ); +} diff --git a/web/src/pages/dataset/dataset-setting/components/tag-item.tsx b/web/src/pages/dataset/dataset-setting/components/tag-item.tsx index c5ccecca47d..241fc240a2d 100644 --- a/web/src/pages/dataset/dataset-setting/components/tag-item.tsx +++ b/web/src/pages/dataset/dataset-setting/components/tag-item.tsx @@ -8,8 +8,9 @@ import { FormMessage, } from '@/components/ui/form'; import { MultiSelect } from '@/components/ui/multi-select'; +import { FormLayout } from '@/constants/form'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; -import { Flex, Form, InputNumber, Select, Slider, Space } from 'antd'; +import { Form, Select, Space } from 'antd'; import DOMPurify from 'dompurify'; import { useFormContext, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -44,7 +45,7 @@ export const TagSetItem = () => {
    { max={10} min={1} defaultValue={3} + layout={FormLayout.Horizontal} > ); - - return ( - - - - - - - - - - - - - ); }; export function TagItems() { diff --git a/web/src/pages/dataset/dataset-setting/configuration/audio.tsx b/web/src/pages/dataset/dataset-setting/configuration/audio.tsx new file mode 100644 index 00000000000..8cc6ceff817 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/audio.tsx @@ -0,0 +1,20 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { ConfigurationFormContainer } from '../configuration-form-container'; + +import { TagItems } from '../components/tag-item'; + +export function AudioConfiguration() { + return ( + + <> + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/book.tsx b/web/src/pages/dataset/dataset-setting/configuration/book.tsx new file mode 100644 index 00000000000..919c35efd15 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/book.tsx @@ -0,0 +1,28 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { TagItems } from '../components/tag-item'; +import { + ConfigurationFormContainer, + MainContainer, +} from '../configuration-form-container'; + +export function BookConfiguration() { + return ( + + + + + + + + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx b/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx index a92937e3e7b..031bce54742 100644 --- a/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx +++ b/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx @@ -42,7 +42,7 @@ export function ChunkMethodItem(props: IProps) { 'w-1/4 whitespace-pre-wrap': line === 1, })} > - {t('dataPipeline')} + {t('builtIn')}
    @@ -115,7 +115,7 @@ export function EmbeddingModelItem({ line = 1, isEdit = true }: IProps) { ); } -export function ParseTypeItem() { +export function ParseTypeItem({ line = 2 }: { line?: number }) { const { t } = useTranslate('knowledgeConfiguration'); const form = useFormContext(); @@ -125,17 +125,26 @@ export function ParseTypeItem() { name={'parseType'} render={({ field }) => ( -
    +
    {t('parseType')} -
    +
    -
    +
    {t('builtIn')} {t('manualSetup')}
    @@ -144,7 +153,7 @@ export function ParseTypeItem() {
    -
    +
    @@ -188,3 +197,39 @@ export function EnableAutoGenerateItem() { /> ); } + +export function EnableTocToggle() { + const { t } = useTranslate('knowledgeConfiguration'); + const form = useFormContext(); + + return ( + ( + +
    + + {t('tocExtraction')} + +
    + + + +
    +
    +
    +
    + +
    +
    + )} + /> + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/email.tsx b/web/src/pages/dataset/dataset-setting/configuration/email.tsx new file mode 100644 index 00000000000..7af07fcbeb7 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/email.tsx @@ -0,0 +1,18 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { TagItems } from '../components/tag-item'; +import { ConfigurationFormContainer } from '../configuration-form-container'; + +export function EmailConfiguration() { + return ( + + <> + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/knowledge-graph.tsx b/web/src/pages/dataset/dataset-setting/configuration/knowledge-graph.tsx new file mode 100644 index 00000000000..8912467faed --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/knowledge-graph.tsx @@ -0,0 +1,15 @@ +import { DelimiterFormField } from '@/components/delimiter-form-field'; +import { EntityTypesFormField } from '@/components/entity-types-form-field'; +import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field'; + +export function KnowledgeGraphConfiguration() { + return ( + <> + <> + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/laws.tsx b/web/src/pages/dataset/dataset-setting/configuration/laws.tsx new file mode 100644 index 00000000000..2d965863742 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/laws.tsx @@ -0,0 +1,29 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { TagItems } from '../components/tag-item'; +import { + ConfigurationFormContainer, + MainContainer, +} from '../configuration-form-container'; + +export function LawsConfiguration() { + return ( + + + + + + + + + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/manual.tsx b/web/src/pages/dataset/dataset-setting/configuration/manual.tsx new file mode 100644 index 00000000000..ccb1e99a54b --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/manual.tsx @@ -0,0 +1,27 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { TagItems } from '../components/tag-item'; +import { + ConfigurationFormContainer, + MainContainer, +} from '../configuration-form-container'; + +export function ManualConfiguration() { + return ( + + + + + + + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/naive.tsx b/web/src/pages/dataset/dataset-setting/configuration/naive.tsx new file mode 100644 index 00000000000..fd1b522df77 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/naive.tsx @@ -0,0 +1,33 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { DelimiterFormField } from '@/components/delimiter-form-field'; +import { ExcelToHtmlFormField } from '@/components/excel-to-html-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field'; +import { TagItems } from '../components/tag-item'; +import { + ConfigurationFormContainer, + MainContainer, +} from '../configuration-form-container'; +import { EnableTocToggle } from './common-item'; + +export function NaiveConfiguration() { + return ( + + + + + + + + + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/one.tsx b/web/src/pages/dataset/dataset-setting/configuration/one.tsx new file mode 100644 index 00000000000..d84b043eca8 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/one.tsx @@ -0,0 +1,21 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { TagItems } from '../components/tag-item'; +import { ConfigurationFormContainer } from '../configuration-form-container'; + +export function OneConfiguration() { + return ( + + + <> + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/paper.tsx b/web/src/pages/dataset/dataset-setting/configuration/paper.tsx new file mode 100644 index 00000000000..c24089bd38f --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/paper.tsx @@ -0,0 +1,28 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { TagItems } from '../components/tag-item'; +import { + ConfigurationFormContainer, + MainContainer, +} from '../configuration-form-container'; + +export function PaperConfiguration() { + return ( + + + + + + + + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/picture.tsx b/web/src/pages/dataset/dataset-setting/configuration/picture.tsx new file mode 100644 index 00000000000..6d4e6b557d7 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/picture.tsx @@ -0,0 +1,18 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { TagItems } from '../components/tag-item'; +import { ConfigurationFormContainer } from '../configuration-form-container'; + +export function PictureConfiguration() { + return ( + + <> + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/presentation.tsx b/web/src/pages/dataset/dataset-setting/configuration/presentation.tsx new file mode 100644 index 00000000000..ab4bc079614 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/presentation.tsx @@ -0,0 +1,29 @@ +import { + AutoKeywordsFormField, + AutoQuestionsFormField, +} from '@/components/auto-keywords-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { TagItems } from '../components/tag-item'; +import { + ConfigurationFormContainer, + MainContainer, +} from '../configuration-form-container'; + +export function PresentationConfiguration() { + return ( + + + + + + + + + + + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/qa.tsx b/web/src/pages/dataset/dataset-setting/configuration/qa.tsx new file mode 100644 index 00000000000..4d64b116af8 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/qa.tsx @@ -0,0 +1,10 @@ +import { TagItems } from '../components/tag-item'; +import { ConfigurationFormContainer } from '../configuration-form-container'; + +export function QAConfiguration() { + return ( + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/resume.tsx b/web/src/pages/dataset/dataset-setting/configuration/resume.tsx new file mode 100644 index 00000000000..bb05150647a --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/resume.tsx @@ -0,0 +1,10 @@ +import { TagItems } from '../components/tag-item'; +import { ConfigurationFormContainer } from '../configuration-form-container'; + +export function ResumeConfiguration() { + return ( + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/table.tsx b/web/src/pages/dataset/dataset-setting/configuration/table.tsx new file mode 100644 index 00000000000..ecf9fc7cc2e --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/table.tsx @@ -0,0 +1,12 @@ +import { ConfigurationFormContainer } from '../configuration-form-container'; + +export function TableConfiguration() { + return ( + + {/* + + + */} + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/configuration/tag.tsx b/web/src/pages/dataset/dataset-setting/configuration/tag.tsx new file mode 100644 index 00000000000..b068ec26f6a --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/configuration/tag.tsx @@ -0,0 +1,5 @@ +import { ConfigurationFormContainer } from '../configuration-form-container'; + +export function TagConfiguration() { + return ; +} diff --git a/web/src/pages/dataset/dataset-setting/form-schema.ts b/web/src/pages/dataset/dataset-setting/form-schema.ts index 3f10111213a..490eb5d567a 100644 --- a/web/src/pages/dataset/dataset-setting/form-schema.ts +++ b/web/src/pages/dataset/dataset-setting/form-schema.ts @@ -1,79 +1,92 @@ +import { t } from 'i18next'; import { z } from 'zod'; -export const formSchema = z.object({ - name: z.string().min(1, { - message: 'Username must be at least 2 characters.', - }), - description: z.string().min(2, { - message: 'Username must be at least 2 characters.', - }), - // avatar: z.instanceof(File), - avatar: z.any().nullish(), - permission: z.string().optional(), - parser_id: z.string(), - pipeline_id: z.string().optional(), - pipeline_name: z.string().optional(), - pipeline_avatar: z.string().optional(), - embd_id: z.string(), - parser_config: z - .object({ - layout_recognize: z.string(), - chunk_token_num: z.number(), - delimiter: z.string(), - auto_keywords: z.number().optional(), - auto_questions: z.number().optional(), - html4excel: z.boolean(), - tag_kb_ids: z.array(z.string()).nullish(), - topn_tags: z.number().optional(), - raptor: z - .object({ - use_raptor: z.boolean().optional(), - prompt: z.string().optional(), - max_token: z.number().optional(), - threshold: z.number().optional(), - max_cluster: z.number().optional(), - random_seed: z.number().optional(), - }) - .refine( - (data) => { - if (data.use_raptor && !data.prompt) { - return false; - } - return true; - }, - { - message: 'Prompt is required', - path: ['prompt'], - }, - ), - graphrag: z - .object({ - use_graphrag: z.boolean().optional(), - entity_types: z.array(z.string()).optional(), - method: z.string().optional(), - resolution: z.boolean().optional(), - community: z.boolean().optional(), - }) - .refine( - (data) => { - if ( - data.use_graphrag && - (!data.entity_types || data.entity_types.length === 0) - ) { - return false; - } - return true; - }, - { - message: 'Please enter Entity types', - path: ['entity_types'], - }, - ), - }) - .optional(), - pagerank: z.number(), - // icon: z.array(z.instanceof(File)), -}); +export const formSchema = z + .object({ + parseType: z.number(), + name: z.string().min(1, { + message: 'Username must be at least 2 characters.', + }), + description: z.string().min(2, { + message: 'Username must be at least 2 characters.', + }), + // avatar: z.instanceof(File), + avatar: z.any().nullish(), + permission: z.string().optional(), + parser_id: z.string(), + pipeline_id: z.string().optional(), + pipeline_name: z.string().optional(), + pipeline_avatar: z.string().optional(), + embd_id: z.string(), + parser_config: z + .object({ + layout_recognize: z.string(), + chunk_token_num: z.number(), + delimiter: z.string(), + auto_keywords: z.number().optional(), + auto_questions: z.number().optional(), + html4excel: z.boolean(), + tag_kb_ids: z.array(z.string()).nullish(), + topn_tags: z.number().optional(), + toc_extraction: z.boolean().optional(), + raptor: z + .object({ + use_raptor: z.boolean().optional(), + prompt: z.string().optional(), + max_token: z.number().optional(), + threshold: z.number().optional(), + max_cluster: z.number().optional(), + random_seed: z.number().optional(), + }) + .refine( + (data) => { + if (data.use_raptor && !data.prompt) { + return false; + } + return true; + }, + { + message: 'Prompt is required', + path: ['prompt'], + }, + ), + graphrag: z + .object({ + use_graphrag: z.boolean().optional(), + entity_types: z.array(z.string()).optional(), + method: z.string().optional(), + resolution: z.boolean().optional(), + community: z.boolean().optional(), + }) + .refine( + (data) => { + if ( + data.use_graphrag && + (!data.entity_types || data.entity_types.length === 0) + ) { + return false; + } + return true; + }, + { + message: 'Please enter Entity types', + path: ['entity_types'], + }, + ), + }) + .optional(), + pagerank: z.number(), + // icon: z.array(z.instanceof(File)), + }) + .superRefine((data, ctx) => { + if (data.parseType === 2 && !data.pipeline_id) { + ctx.addIssue({ + path: ['pipeline_id'], + message: t('common.pleaseSelect'), + code: 'custom', + }); + } + }); export const pipelineFormSchema = z.object({ pipeline_id: z.string().optional(), diff --git a/web/src/pages/dataset/dataset-setting/index.tsx b/web/src/pages/dataset/dataset-setting/index.tsx index 25d8fd7ea33..079b0457140 100644 --- a/web/src/pages/dataset/dataset-setting/index.tsx +++ b/web/src/pages/dataset/dataset-setting/index.tsx @@ -1,14 +1,18 @@ -import { IDataPipelineSelectNode } from '@/components/data-pipeline-select'; +import { + DataFlowSelect, + IDataPipelineSelectNode, +} from '@/components/data-pipeline-select'; import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields'; import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields'; import { Button } from '@/components/ui/button'; import Divider from '@/components/ui/divider'; import { Form } from '@/components/ui/form'; +import { FormLayout } from '@/constants/form'; import { DocumentParserType } from '@/constants/knowledge'; import { PermissionRole } from '@/constants/permission'; import { zodResolver } from '@hookform/resolvers/zod'; import { useEffect, useState } from 'react'; -import { useForm } from 'react-hook-form'; +import { useForm, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { TopTitle } from '../dataset-title'; @@ -16,10 +20,10 @@ import { GenerateType, IGenerateLogButtonProps, } from '../dataset/generate-button/generate'; -import LinkDataPipeline, { - IDataPipelineNodeProps, -} from './components/link-data-pipeline'; +import { ChunkMethodForm } from './chunk-method-form'; +import { IDataPipelineNodeProps } from './components/link-data-pipeline'; import { MainContainer } from './configuration-form-container'; +import { ChunkMethodItem, ParseTypeItem } from './configuration/common-item'; import { formSchema } from './form-schema'; import { GeneralForm } from './general-form'; import { useFetchKnowledgeConfigurationOnMount } from './hooks'; @@ -44,6 +48,7 @@ const enum MethodValue { export default function DatasetSettings() { const { t } = useTranslation(); + const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -58,6 +63,7 @@ export default function DatasetSettings() { auto_questions: 0, html4excel: false, topn_tags: 3, + toc_extraction: false, raptor: { use_raptor: true, max_token: 256, @@ -73,17 +79,17 @@ export default function DatasetSettings() { }, }, pipeline_id: '', + parseType: 1, pagerank: 0, }, }); - const knowledgeDetails = useFetchKnowledgeConfigurationOnMount(form); - const [pipelineData, setPipelineData] = useState(); const [graphRagGenerateData, setGraphRagGenerateData] = useState(); const [raptorGenerateData, setRaptorGenerateData] = useState(); + useEffect(() => { console.log('🚀 ~ DatasetSettings ~ knowledgeDetails:', knowledgeDetails); if (knowledgeDetails) { @@ -102,8 +108,10 @@ export default function DatasetSettings() { finish_at: knowledgeDetails.raptor_task_finish_at, task_id: knowledgeDetails.raptor_task_id, } as IGenerateLogButtonProps); + form.setValue('parseType', knowledgeDetails.pipeline_id ? 2 : 1); + form.setValue('pipeline_id', knowledgeDetails.pipeline_id || ''); } - }, [knowledgeDetails]); + }, [knowledgeDetails, form]); async function onSubmit(data: z.infer) { try { @@ -137,6 +145,22 @@ export default function DatasetSettings() { } as IGenerateLogButtonProps); } }; + + const parseType = useWatch({ + control: form.control, + name: 'parseType', + defaultValue: knowledgeDetails.pipeline_id ? 2 : 1, + }); + const selectedTag = useWatch({ + name: 'parser_id', + control: form.control, + }); + useEffect(() => { + if (parseType === 1) { + form.setValue('pipeline_id', ''); + } + console.log('parseType', parseType); + }, [parseType, form]); return (
    handleDeletePipelineTask(GenerateType.Raptor)} > - + {parseType === 1 && ( + + )} + {parseType === 2 && ( + + )} + + + {parseType === 1 && ( + + )} + + {/* + /> */}
    diff --git a/web/src/pages/dataset/dataset-setting/saving-button.tsx b/web/src/pages/dataset/dataset-setting/saving-button.tsx index 7a063081ac9..558150b4f01 100644 --- a/web/src/pages/dataset/dataset-setting/saving-button.tsx +++ b/web/src/pages/dataset/dataset-setting/saving-button.tsx @@ -62,6 +62,7 @@ export function SavingButton() { if (beValid) { form.handleSubmit(async (values) => { console.log('saveKnowledgeConfiguration: ', values); + delete values['parseType']; // delete values['avatar']; await saveKnowledgeConfiguration({ kb_id, diff --git a/web/src/pages/dataset/sidebar/index.tsx b/web/src/pages/dataset/sidebar/index.tsx index c612f205936..722f5996718 100644 --- a/web/src/pages/dataset/sidebar/index.tsx +++ b/web/src/pages/dataset/sidebar/index.tsx @@ -29,11 +29,6 @@ export function SideBar({ refreshCount }: PropType) { const items = useMemo(() => { const list = [ - { - icon: , - label: t(`knowledgeDetails.overview`), - key: Routes.DataSetOverview, - }, { icon: , label: t(`knowledgeDetails.subbarFiles`), @@ -44,6 +39,11 @@ export function SideBar({ refreshCount }: PropType) { label: t(`knowledgeDetails.testing`), key: Routes.DatasetTesting, }, + { + icon: , + label: t(`knowledgeDetails.overview`), + key: Routes.DataSetOverview, + }, { icon: , label: t(`knowledgeDetails.configuration`), diff --git a/web/src/pages/datasets/dataset-creating-dialog.tsx b/web/src/pages/datasets/dataset-creating-dialog.tsx index c2bbf3705e1..6b54de904ec 100644 --- a/web/src/pages/datasets/dataset-creating-dialog.tsx +++ b/web/src/pages/datasets/dataset-creating-dialog.tsx @@ -16,6 +16,7 @@ import { FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; +import { FormLayout } from '@/constants/form'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { IModalProps } from '@/interfaces/common'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -137,8 +138,9 @@ export function InputForm({ onOk }: IModalProps) { {parseType === 2 && ( )} From 5200711441d661c5bdefae4521c0082c13471077 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Sat, 11 Oct 2025 18:46:09 +0800 Subject: [PATCH 0809/1187] Feat: add support for multi-column PDF parsing (#10475) ### What problem does this PR solve? Add support for multi-columns PDF parsing. #9878, #9919. Two-column sample: image Three-column sample: image Single-column sample: image ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] New Feature (non-breaking change which adds functionality) --- deepdoc/parser/markdown_parser.py | 3 - deepdoc/parser/pdf_parser.py | 273 +++++++++++++++++++++--------- rag/app/naive.py | 3 +- 3 files changed, 195 insertions(+), 84 deletions(-) diff --git a/deepdoc/parser/markdown_parser.py b/deepdoc/parser/markdown_parser.py index 0f39b244396..8d0e6702209 100644 --- a/deepdoc/parser/markdown_parser.py +++ b/deepdoc/parser/markdown_parser.py @@ -17,7 +17,6 @@ import re -import mistune from markdown import markdown @@ -117,8 +116,6 @@ class MarkdownElementExtractor: def __init__(self, markdown_content): self.markdown_content = markdown_content self.lines = markdown_content.split("\n") - self.ast_parser = mistune.create_markdown(renderer="ast") - self.ast_nodes = self.ast_parser(markdown_content) def extract_elements(self): """Extract individual elements (headers, code blocks, lists, etc.)""" diff --git a/deepdoc/parser/pdf_parser.py b/deepdoc/parser/pdf_parser.py index 74e08e212f8..8cc56a88ce5 100644 --- a/deepdoc/parser/pdf_parser.py +++ b/deepdoc/parser/pdf_parser.py @@ -15,11 +15,13 @@ # import logging +import math import os import random import re import sys import threading +from collections import Counter, defaultdict from copy import deepcopy from io import BytesIO from timeit import default_timer as timer @@ -349,9 +351,78 @@ def _layouts_rec(self, ZM, drop=True): self.boxes[i]["top"] += self.page_cum_height[self.boxes[i]["page_number"] - 1] self.boxes[i]["bottom"] += self.page_cum_height[self.boxes[i]["page_number"] - 1] - def _text_merge(self): + def _assign_column(self, boxes, zoomin=3): + if not boxes: + return boxes + + if all("col_id" in b for b in boxes): + return boxes + + by_page = defaultdict(list) + for b in boxes: + by_page[b["page_number"]].append(b) + + page_info = {} # pg -> dict(page_w, left_edge, cand_cols) + counter = Counter() + + for pg, bxs in by_page.items(): + if not bxs: + page_info[pg] = {"page_w": 1.0, "left_edge": 0.0, "cand": 1} + counter[1] += 1 + continue + + if hasattr(self, "page_images") and self.page_images and len(self.page_images) >= pg: + page_w = self.page_images[pg - 1].size[0] / max(1, zoomin) + left_edge = 0.0 + else: + xs0 = [box["x0"] for box in bxs] + xs1 = [box["x1"] for box in bxs] + left_edge = float(min(xs0)) + page_w = max(1.0, float(max(xs1) - left_edge)) + + widths = [max(1.0, (box["x1"] - box["x0"])) for box in bxs] + median_w = float(np.median(widths)) if widths else 1.0 + + raw_cols = int(page_w / max(1.0, median_w)) + + # cand = raw_cols if (raw_cols >= 2 and median_w < page_w / raw_cols * 0.8) else 1 + cand = raw_cols + + page_info[pg] = {"page_w": page_w, "left_edge": left_edge, "cand": cand} + counter[cand] += 1 + + logging.info(f"[Page {pg}] median_w={median_w:.2f}, page_w={page_w:.2f}, raw_cols={raw_cols}, cand={cand}") + + global_cols = counter.most_common(1)[0][0] + logging.info(f"Global column_num decided by majority: {global_cols}") + + for pg, bxs in by_page.items(): + if not bxs: + continue + + page_w = page_info[pg]["page_w"] + left_edge = page_info[pg]["left_edge"] + + if global_cols == 1: + for box in bxs: + box["col_id"] = 0 + continue + + for box in bxs: + w = box["x1"] - box["x0"] + if w >= 0.8 * page_w: + box["col_id"] = 0 + continue + cx = 0.5 * (box["x0"] + box["x1"]) + norm_cx = (cx - left_edge) / page_w + norm_cx = max(0.0, min(norm_cx, 0.999999)) + box["col_id"] = int(min(global_cols - 1, norm_cx * global_cols)) + + return boxes + + def _text_merge(self, zoomin=3): # merge adjusted boxes - bxs = self.boxes + bxs = self._assign_column(self.boxes, zoomin) def end_with(b, txt): txt = txt.strip() @@ -367,9 +438,15 @@ def start_with(b, txts): while i < len(bxs) - 1: b = bxs[i] b_ = bxs[i + 1] + + if b["page_number"] != b_["page_number"] or b.get("col_id") != b_.get("col_id"): + i += 1 + continue + if b.get("layoutno", "0") != b_.get("layoutno", "1") or b.get("layout_type", "") in ["table", "figure", "equation"]: i += 1 continue + if abs(self._y_dis(b, b_)) < self.mean_height[bxs[i]["page_number"] - 1] / 3: # merge bxs[i]["x1"] = b_["x1"] @@ -379,83 +456,108 @@ def start_with(b, txts): bxs.pop(i + 1) continue i += 1 - continue + self.boxes = bxs - dis_thr = 1 - dis = b["x1"] - b_["x0"] - if b.get("layout_type", "") != "text" or b_.get("layout_type", "") != "text": - if end_with(b, ",") or start_with(b_, "(,"): - dis_thr = -8 - else: + def _naive_vertical_merge(self, zoomin=3): + bxs = self._assign_column(self.boxes, zoomin) + + grouped = defaultdict(list) + for b in bxs: + grouped[(b["page_number"], b.get("col_id", 0))].append(b) + + merged_boxes = [] + for (pg, col), bxs in grouped.items(): + bxs = sorted(bxs, key=lambda x: (x["top"], x["x0"])) + if not bxs: + continue + + mh = self.mean_height[pg - 1] if self.mean_height else np.median([b["bottom"] - b["top"] for b in bxs]) or 10 + + i = 0 + while i + 1 < len(bxs): + b = bxs[i] + b_ = bxs[i + 1] + + if b["page_number"] < b_["page_number"] and re.match(r"[0-9 •一—-]+$", b["text"]): + bxs.pop(i) + continue + + if not b["text"].strip(): + bxs.pop(i) + continue + + if not b["text"].strip() or b.get("layoutno") != b_.get("layoutno"): i += 1 continue - if abs(self._y_dis(b, b_)) < self.mean_height[bxs[i]["page_number"] - 1] / 5 and dis >= dis_thr and b["x1"] < b_["x1"]: - # merge - bxs[i]["x1"] = b_["x1"] - bxs[i]["top"] = (b["top"] + b_["top"]) / 2 - bxs[i]["bottom"] = (b["bottom"] + b_["bottom"]) / 2 - bxs[i]["text"] += b_["text"] + if b_["top"] - b["bottom"] > mh * 1.5: + i += 1 + continue + + overlap = max(0, min(b["x1"], b_["x1"]) - max(b["x0"], b_["x0"])) + if overlap / max(1, min(b["x1"] - b["x0"], b_["x1"] - b_["x0"])) < 0.3: + i += 1 + continue + + concatting_feats = [ + b["text"].strip()[-1] in ",;:'\",、‘“;:-", + len(b["text"].strip()) > 1 and b["text"].strip()[-2] in ",;:'\",‘“、;:", + b_["text"].strip() and b_["text"].strip()[0] in "。;?!?”)),,、:", + ] + # features for not concating + feats = [ + b.get("layoutno", 0) != b_.get("layoutno", 0), + b["text"].strip()[-1] in "。?!?", + self.is_english and b["text"].strip()[-1] in ".!?", + b["page_number"] == b_["page_number"] and b_["top"] - b["bottom"] > self.mean_height[b["page_number"] - 1] * 1.5, + b["page_number"] < b_["page_number"] and abs(b["x0"] - b_["x0"]) > self.mean_width[b["page_number"] - 1] * 4, + ] + # split features + detach_feats = [b["x1"] < b_["x0"], b["x0"] > b_["x1"]] + if (any(feats) and not any(concatting_feats)) or any(detach_feats): + logging.debug( + "{} {} {} {}".format( + b["text"], + b_["text"], + any(feats), + any(concatting_feats), + ) + ) + i += 1 + continue + + b["text"] = (b["text"].rstrip() + " " + b_["text"].lstrip()).strip() + b["bottom"] = b_["bottom"] + b["x0"] = min(b["x0"], b_["x0"]) + b["x1"] = max(b["x1"], b_["x1"]) bxs.pop(i + 1) - continue - i += 1 - self.boxes = bxs - def _naive_vertical_merge(self, zoomin=3): - import math - bxs = Recognizer.sort_Y_firstly(self.boxes, np.median(self.mean_height) / 3) + merged_boxes.extend(bxs) - column_width = np.median([b["x1"] - b["x0"] for b in self.boxes]) - if not column_width or math.isnan(column_width): - column_width = self.mean_width[0] - self.column_num = int(self.page_images[0].size[0] / zoomin / column_width) - if column_width < self.page_images[0].size[0] / zoomin / self.column_num: - logging.info("Multi-column................... {} {}".format(column_width, self.page_images[0].size[0] / zoomin / self.column_num)) - self.boxes = self.sort_X_by_page(self.boxes, column_width / self.column_num) + self.boxes = sorted(merged_boxes, key=lambda x: (x["page_number"], x.get("col_id", 0), x["top"])) - i = 0 - while i + 1 < len(bxs): - b = bxs[i] - b_ = bxs[i + 1] - if b["page_number"] < b_["page_number"] and re.match(r"[0-9 •一—-]+$", b["text"]): - bxs.pop(i) - continue - if not b["text"].strip(): - bxs.pop(i) - continue - concatting_feats = [ - b["text"].strip()[-1] in ",;:'\",、‘“;:-", - len(b["text"].strip()) > 1 and b["text"].strip()[-2] in ",;:'\",‘“、;:", - b_["text"].strip() and b_["text"].strip()[0] in "。;?!?”)),,、:", - ] - # features for not concating - feats = [ - b.get("layoutno", 0) != b_.get("layoutno", 0), - b["text"].strip()[-1] in "。?!?", - self.is_english and b["text"].strip()[-1] in ".!?", - b["page_number"] == b_["page_number"] and b_["top"] - b["bottom"] > self.mean_height[b["page_number"] - 1] * 1.5, - b["page_number"] < b_["page_number"] and abs(b["x0"] - b_["x0"]) > self.mean_width[b["page_number"] - 1] * 4, - ] - # split features - detach_feats = [b["x1"] < b_["x0"], b["x0"] > b_["x1"]] - if (any(feats) and not any(concatting_feats)) or any(detach_feats): - logging.debug( - "{} {} {} {}".format( - b["text"], - b_["text"], - any(feats), - any(concatting_feats), - ) - ) - i += 1 - continue - # merge up and down - b["bottom"] = b_["bottom"] - b["text"] += b_["text"] - b["x0"] = min(b["x0"], b_["x0"]) - b["x1"] = max(b["x1"], b_["x1"]) - bxs.pop(i + 1) - self.boxes = bxs + def _final_reading_order_merge(self, zoomin=3): + if not self.boxes: + return + + self.boxes = self._assign_column(self.boxes, zoomin=zoomin) + + pages = defaultdict(lambda: defaultdict(list)) + for b in self.boxes: + pg = b["page_number"] + col = b.get("col_id", 0) + pages[pg][col].append(b) + + for pg in pages: + for col in pages[pg]: + pages[pg][col].sort(key=lambda x: (x["top"], x["x0"])) + + new_boxes = [] + for pg in sorted(pages.keys()): + for col in sorted(pages[pg].keys()): + new_boxes.extend(pages[pg][col]) + + self.boxes = new_boxes def _concat_downward(self, concat_between_pages=True): self.boxes = Recognizer.sort_Y_firstly(self.boxes, 0) @@ -1074,7 +1176,6 @@ def parse_into_bboxes(self, fnm, callback=None, zoomin=3): def insert_table_figures(tbls_or_figs, layout_type): def min_rectangle_distance(rect1, rect2): - import math pn1, left1, right1, top1, bottom1 = rect1 pn2, left2, right2, top2, bottom2 = rect2 if right1 >= left2 and right2 >= left1 and bottom1 >= top2 and bottom2 >= top1: @@ -1091,27 +1192,39 @@ def min_rectangle_distance(rect1, rect2): dy = top1 - bottom2 else: dy = 0 - return math.sqrt(dx*dx + dy*dy)# + (pn2-pn1)*10000 + return math.sqrt(dx * dx + dy * dy) # + (pn2-pn1)*10000 for (img, txt), poss in tbls_or_figs: bboxes = [(i, (b["page_number"], b["x0"], b["x1"], b["top"], b["bottom"])) for i, b in enumerate(self.boxes)] - dists = [(min_rectangle_distance((pn, left, right, top+self.page_cum_height[pn], bott+self.page_cum_height[pn]), rect),i) for i, rect in bboxes for pn, left, right, top, bott in poss] + dists = [ + (min_rectangle_distance((pn, left, right, top + self.page_cum_height[pn], bott + self.page_cum_height[pn]), rect), i) for i, rect in bboxes for pn, left, right, top, bott in poss + ] min_i = np.argmin(dists, axis=0)[0] min_i, rect = bboxes[dists[min_i][-1]] if isinstance(txt, list): txt = "\n".join(txt) pn, left, right, top, bott = poss[0] - if self.boxes[min_i]["bottom"] < top+self.page_cum_height[pn]: + if self.boxes[min_i]["bottom"] < top + self.page_cum_height[pn]: min_i += 1 - self.boxes.insert(min_i, { - "page_number": pn+1, "x0": left, "x1": right, "top": top+self.page_cum_height[pn], "bottom": bott+self.page_cum_height[pn], "layout_type": layout_type, "text": txt, "image": img, - "positions": [[pn+1, int(left), int(right), int(top), int(bott)]] - }) + self.boxes.insert( + min_i, + { + "page_number": pn + 1, + "x0": left, + "x1": right, + "top": top + self.page_cum_height[pn], + "bottom": bott + self.page_cum_height[pn], + "layout_type": layout_type, + "text": txt, + "image": img, + "positions": [[pn + 1, int(left), int(right), int(top), int(bott)]], + }, + ) for b in self.boxes: b["position_tag"] = self._line_tag(b, zoomin) b["image"] = self.crop(b["position_tag"], zoomin) - b["positions"] = [[pos[0][-1]+1, *pos[1:]] for pos in RAGFlowPdfParser.extract_positions(b["position_tag"])] + b["positions"] = [[pos[0][-1] + 1, *pos[1:]] for pos in RAGFlowPdfParser.extract_positions(b["position_tag"])] insert_table_figures(tbls, "table") insert_table_figures(figs, "figure") diff --git a/rag/app/naive.py b/rag/app/naive.py index ca253efe15f..370b503adc1 100644 --- a/rag/app/naive.py +++ b/rag/app/naive.py @@ -328,7 +328,7 @@ def __call__(self, filename, binary=None, from_page=0, callback(0.65, "Table analysis ({:.2f}s)".format(timer() - start)) start = timer() - self._text_merge() + self._text_merge(zoomin=zoomin) callback(0.67, "Text merged ({:.2f}s)".format(timer() - start)) if separate_tables_figures: @@ -340,6 +340,7 @@ def __call__(self, filename, binary=None, from_page=0, tbls = self._extract_table_figure(True, zoomin, True, True) self._naive_vertical_merge() self._concat_downward() + self._final_reading_order_merge() # self._filter_forpages() logging.info("layouts cost: {}s".format(timer() - first_start)) return [(b["text"], self._line_tag(b, zoomin)) for b in self.boxes], tbls From 932781ea4e528d3ac9dadc7b0fdb6b172f61520e Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Sat, 11 Oct 2025 19:37:42 +0800 Subject: [PATCH 0810/1187] Fix: incorrect agent template #10393 (#10491) ### What problem does this PR solve? Fix: incorrect agent template #10493 ### Type of change - [X] Bug Fix (non-breaking change which fixes an issue) --- agent/templates/image_lingo.json | 515 ++++++++++++++++--------------- conf/llm_factories.json | 21 +- 2 files changed, 265 insertions(+), 271 deletions(-) diff --git a/agent/templates/image_lingo.json b/agent/templates/image_lingo.json index 0f874d75cda..88dfc6cde05 100644 --- a/agent/templates/image_lingo.json +++ b/agent/templates/image_lingo.json @@ -7,261 +7,262 @@ "en": "ImageLingo lets you snap any photo containing text—menus, signs, or documents—and instantly recognize and translate it into your language of choice using advanced AI-powered translation technology.", "zh": "多模态大模型允许您拍摄任何包含文本的照片——菜单、标志或文档——立即识别并转换成您选择的语言。"}, "canvas_type": "Consumer App", - "dsl": { - "components": { - "Agent:CoolPandasCrash": { - "downstream": [ - "Message:CurlyApplesRelate" - ], - "obj": { - "component_name": "Agent", - "params": { - "delay_after_error": 1, - "description": "", - "exception_comment": "", - "exception_goto": [], - "exception_method": null, - "frequencyPenaltyEnabled": false, - "frequency_penalty": 0.7, - "llm_filter": "image2text", - "llm_id": "qwen-vl-plus@Tongyi-Qianwen", - "maxTokensEnabled": false, - "max_retries": 3, - "max_rounds": 5, - "max_tokens": 256, - "mcp": [], - "message_history_window_size": 12, - "outputs": { - "content": { - "type": "string", - "value": "" - }, - "structured_output": {} - }, - "presencePenaltyEnabled": false, - "presence_penalty": 0.4, - "prompts": [ - { - "content": "The user query is {sys.query}\n\n\n\nThe input files are {sys.files}\n\n", - "role": "user" - } - ], - "sys_prompt": "You are a multilingual translation assistant that works from images. When given a photo of any text or scene, you should:\n\n\n\n1. Detect and extract all written text in the image, regardless of font, orientation, or style. \n\n2. Identify the source language of the extracted text. \n\n3. Determine the target language:\n\n - If the user explicitly specifies a language, use that.\n\n - If no language is specified, automatically detect the user\u2019s spoken language and use that as the target. \n\n4. Translate the content accurately into the target language, preserving meaning, tone, and formatting (e.g., line breaks, punctuation). \n\n5. If the image contains signage, menus, labels, or other contextual text, adapt the translation to be natural and context-appropriate for daily use. \n\n6. Return the translated text in plain, well-formatted paragraphs. If the user asks, also provide transliteration for non-Latin scripts. \n\n7. If the image is unclear or the target language cannot be determined, ask a clarifying follow-up question.\n\n\nExample:\n\nUser: \u201cTranslate this photo for me.\u201d\n\nAgent Input: [Image of a Japanese train schedule]\n\nAgent Output:\n\n\u201c7:30 AM \u2013 \u6771\u4eac\u99c5 (Tokyo Station) \n\n8:15 AM \u2013 \u65b0\u5927\u962a (Shin-Osaka)\u201d \n\n(Detected user language: English)```\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "tools": [], - "topPEnabled": false, - "top_p": 0.3, - "user_prompt": "", - "visual_files_var": "sys.files" - } - }, - "upstream": [ - "begin" - ] - }, - "Message:CurlyApplesRelate": { - "downstream": [], - "obj": { - "component_name": "Message", - "params": { - "content": [ - "{Agent:CoolPandasCrash@content}" - ] - } - }, - "upstream": [ - "Agent:CoolPandasCrash" - ] - }, - "begin": { - "downstream": [ - "Agent:CoolPandasCrash" - ], - "obj": { - "component_name": "Begin", - "params": { - "enablePrologue": true, - "inputs": {}, - "mode": "task", - "prologue": "Hi there! I\u2019m ImageLingo, your on-the-go image translation assistant\u2014just snap a photo, and I\u2019ll instantly translate and adapt it into your language." - } - }, - "upstream": [] - } - }, - "globals": { - "sys.conversation_turns": 0, - "sys.files": [], - "sys.query": "", - "sys.user_id": "" - }, - "graph": { - "edges": [ - { - "data": { - "isHovered": false - }, - "id": "xy-edge__beginstart-Agent:CoolPandasCrashend", - "source": "begin", - "sourceHandle": "start", - "target": "Agent:CoolPandasCrash", - "targetHandle": "end" - }, - { - "data": { - "isHovered": false - }, - "id": "xy-edge__Agent:CoolPandasCrashstart-Message:CurlyApplesRelateend", - "source": "Agent:CoolPandasCrash", - "sourceHandle": "start", - "target": "Message:CurlyApplesRelate", - "targetHandle": "end" - } - ], - "nodes": [ - { - "data": { - "form": { - "enablePrologue": true, - "inputs": {}, - "mode": "task", - "prologue": "Hi there! I\u2019m ImageLingo, your on-the-go image translation assistant\u2014just snap a photo, and I\u2019ll instantly translate and adapt it into your language." - }, - "label": "Begin", - "name": "begin" - }, - "id": "begin", - "measured": { - "height": 48, - "width": 200 - }, - "position": { - "x": 50, - "y": 200 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": { - "delay_after_error": 1, - "description": "", - "exception_comment": "", - "exception_goto": "", - "exception_method": null, - "frequencyPenaltyEnabled": false, - "frequency_penalty": 0.7, - "llm_filter": "image2text", - "llm_id": "qwen-vl-plus@Tongyi-Qianwen", - "maxTokensEnabled": false, - "max_retries": 3, - "max_rounds": 5, - "max_tokens": 256, - "mcp": [], - "message_history_window_size": 12, - "outputs": { - "content": { - "type": "string", - "value": "" - }, - "structured_output": {} - }, - "presencePenaltyEnabled": false, - "presence_penalty": 0.4, - "prompts": [ - { - "content": "The user query is {sys.query}\n\n\n\nThe input files are {sys.files}\n\n", - "role": "user" - } - ], - "sys_prompt": "You are a multilingual translation assistant that works from images. When given a photo of any text or scene, you should:\n\n\n\n1. Detect and extract all written text in the image, regardless of font, orientation, or style. \n\n2. Identify the source language of the extracted text. \n\n3. Determine the target language:\n\n - If the user explicitly specifies a language, use that.\n\n - If no language is specified, automatically detect the user\u2019s spoken language and use that as the target. \n\n4. Translate the content accurately into the target language, preserving meaning, tone, and formatting (e.g., line breaks, punctuation). \n\n5. If the image contains signage, menus, labels, or other contextual text, adapt the translation to be natural and context-appropriate for daily use. \n\n6. Return the translated text in plain, well-formatted paragraphs. If the user asks, also provide transliteration for non-Latin scripts. \n\n7. If the image is unclear or the target language cannot be determined, ask a clarifying follow-up question.\n\n\nExample:\n\nUser: \u201cTranslate this photo for me.\u201d\n\nAgent Input: [Image of a Japanese train schedule]\n\nAgent Output:\n\n\u201c7:30 AM \u2013 \u6771\u4eac\u99c5 (Tokyo Station) \n\n8:15 AM \u2013 \u65b0\u5927\u962a (Shin-Osaka)\u201d \n\n(Detected user language: English)```\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "tools": [], - "topPEnabled": false, - "top_p": 0.3, - "user_prompt": "", - "visual_files_var": "sys.files" - }, - "label": "Agent", - "name": "Translation Agent With Vision" - }, - "dragging": false, - "id": "Agent:CoolPandasCrash", - "measured": { - "height": 87, - "width": 200 - }, - "position": { - "x": 350.5, - "y": 200 - }, - "selected": true, - "sourcePosition": "right", - "targetPosition": "left", - "type": "agentNode" - }, - { - "data": { - "form": { - "content": [ - "{Agent:CoolPandasCrash@content}" - ] - }, - "label": "Message", - "name": "Message" - }, - "id": "Message:CurlyApplesRelate", - "measured": { - "height": 56, - "width": 200 - }, - "position": { - "x": 650, - "y": 200 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "messageNode" - }, - { - "data": { - "form": { - "text": "ImageLingo lets you snap any photo containing text\u2014menus, signs, or documents\u2014and instantly recognize and translate it into your language of choice using advanced OCR and AI-powered translation technology. With automatic source-language detection and context-aware adaptations, translations preserve formatting, tone, and intent. Your on-the-go language assistant. " - }, - "label": "Note", - "name": "Translation Agent" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 190, - "id": "Note:OpenCobrasMarry", - "measured": { - "height": 190, - "width": 376 - }, - "position": { - "x": 385.5, - "y": -42 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 376 - } - ] - }, - "history": [], - "messages": [], - "path": [], - "retrieval": [] - }, + "dsl": { + "components": { + "Agent:CoolPandasCrash": { + "downstream": [ + "Message:CurlyApplesRelate" + ], + "obj": { + "component_name": "Agent", + "params": { + "delay_after_error": 1, + "description": "", + "exception_comment": "", + "exception_goto": [], + "exception_method": null, + "frequency_penalty": 0.7, + "frequencyPenaltyEnabled": false, + "llm_filter": "image2text", + "llm_id": "qwen-vl-plus@Tongyi-Qianwen", + "max_retries": 3, + "max_rounds": 5, + "max_tokens": 256, + "maxTokensEnabled": false, + "mcp": [], + "message_history_window_size": 12, + "outputs": { + "content": { + "type": "string", + "value": "" + }, + "structured_output": {} + }, + "presence_penalty": 0.4, + "presencePenaltyEnabled": false, + "prompts": [ + { + "content": "The user query is {sys.query}\n\n\n", + "role": "user" + } + ], + "sys_prompt": "You are a multilingual translation assistant that works from images. When given a photo of any text or scene, you should:\n\n\n\n1. Detect and extract all written text in the image, regardless of font, orientation, or style. \n\n2. Identify the source language of the extracted text. \n\n3. Determine the target language:\n\n - If the user explicitly specifies a language, use that.\n\n - If no language is specified, automatically detect the user’s spoken language and use that as the target. \n\n4. Translate the content accurately into the target language, preserving meaning, tone, and formatting (e.g., line breaks, punctuation). \n\n5. If the image contains signage, menus, labels, or other contextual text, adapt the translation to be natural and context-appropriate for daily use. \n\n6. Return the translated text in plain, well-formatted paragraphs. If the user asks, also provide transliteration for non-Latin scripts. \n\n7. If the image is unclear or the target language cannot be determined, ask a clarifying follow-up question.\n\n\nExample:\n\nUser: “Translate this photo for me.”\n\nAgent Input: [Image of a Japanese train schedule]\n\nAgent Output:\n\n“7:30 AM – 東京駅 (Tokyo Station) \n\n8:15 AM – 新大阪 (Shin-Osaka)” \n\n(Detected user language: English)```\n\n", + "temperature": 0.1, + "temperatureEnabled": true, + "tools": [], + "top_p": 0.3, + "topPEnabled": false, + "user_prompt": "", + "visual_files_var": "sys.files" + } + }, + "upstream": [ + "begin" + ] + }, + "begin": { + "downstream": [ + "Agent:CoolPandasCrash" + ], + "obj": { + "component_name": "Begin", + "params": { + "enablePrologue": true, + "inputs": {}, + "mode": "conversational", + "prologue": "Hi there! I’m ImageLingo, your on-the-go image translation assistant—just snap a photo, and I’ll instantly translate and adapt it into your language." + } + }, + "upstream": [] + }, + "Message:CurlyApplesRelate": { + "downstream": [], + "obj": { + "component_name": "Message", + "params": { + "content": [ + "{Agent:CoolPandasCrash@content}" + ] + } + }, + "upstream": [ + "Agent:CoolPandasCrash" + ] + } + }, + "globals": { + "sys.conversation_turns": 0, + "sys.files": [], + "sys.query": "", + "sys.user_id": "" + }, + "graph": { + "edges": [ + { + "data": { + "isHovered": false + }, + "id": "xy-edge__beginstart-Agent:CoolPandasCrashend", + "source": "begin", + "sourceHandle": "start", + "target": "Agent:CoolPandasCrash", + "targetHandle": "end" + }, + { + "data": { + "isHovered": false + }, + "id": "xy-edge__Agent:CoolPandasCrashstart-Message:CurlyApplesRelateend", + "source": "Agent:CoolPandasCrash", + "sourceHandle": "start", + "target": "Message:CurlyApplesRelate", + "targetHandle": "end" + } + ], + "nodes": [ + { + "data": { + "form": { + "enablePrologue": true, + "inputs": {}, + "mode": "conversational", + "prologue": "Hi there! I’m ImageLingo, your on-the-go image translation assistant—just snap a photo, and I’ll instantly translate and adapt it into your language." + }, + "label": "Begin", + "name": "begin" + }, + "id": "begin", + "measured": { + "height": 48, + "width": 200 + }, + "position": { + "x": 50, + "y": 200 + }, + "selected": false, + "sourcePosition": "left", + "targetPosition": "right", + "type": "beginNode" + }, + { + "data": { + "form": { + "delay_after_error": 1, + "description": "", + "exception_comment": "", + "exception_goto": "", + "exception_method": null, + "frequency_penalty": 0.7, + "frequencyPenaltyEnabled": false, + "llm_filter": "image2text", + "llm_id": "qwen-vl-plus@Tongyi-Qianwen", + "max_retries": 3, + "max_rounds": 5, + "max_tokens": 256, + "maxTokensEnabled": false, + "mcp": [], + "message_history_window_size": 12, + "outputs": { + "content": { + "type": "string", + "value": "" + }, + "structured_output": {} + }, + "presence_penalty": 0.4, + "presencePenaltyEnabled": false, + "prompts": [ + { + "content": "The user query is {sys.query}\n\n\n", + "role": "user" + } + ], + "sys_prompt": "You are a multilingual translation assistant that works from images. When given a photo of any text or scene, you should:\n\n\n\n1. Detect and extract all written text in the image, regardless of font, orientation, or style. \n\n2. Identify the source language of the extracted text. \n\n3. Determine the target language:\n\n - If the user explicitly specifies a language, use that.\n\n - If no language is specified, automatically detect the user’s spoken language and use that as the target. \n\n4. Translate the content accurately into the target language, preserving meaning, tone, and formatting (e.g., line breaks, punctuation). \n\n5. If the image contains signage, menus, labels, or other contextual text, adapt the translation to be natural and context-appropriate for daily use. \n\n6. Return the translated text in plain, well-formatted paragraphs. If the user asks, also provide transliteration for non-Latin scripts. \n\n7. If the image is unclear or the target language cannot be determined, ask a clarifying follow-up question.\n\n\nExample:\n\nUser: “Translate this photo for me.”\n\nAgent Input: [Image of a Japanese train schedule]\n\nAgent Output:\n\n“7:30 AM – 東京駅 (Tokyo Station) \n\n8:15 AM – 新大阪 (Shin-Osaka)” \n\n(Detected user language: English)```\n\n", + "temperature": 0.1, + "temperatureEnabled": true, + "tools": [], + "top_p": 0.3, + "topPEnabled": false, + "user_prompt": "", + "visual_files_var": "sys.files" + }, + "label": "Agent", + "name": "Translation Agent With Vision" + }, + "dragging": false, + "id": "Agent:CoolPandasCrash", + "measured": { + "height": 85, + "width": 200 + }, + "position": { + "x": 350.5, + "y": 200 + }, + "selected": true, + "sourcePosition": "right", + "targetPosition": "left", + "type": "agentNode" + }, + { + "data": { + "form": { + "content": [ + "{Agent:CoolPandasCrash@content}" + ] + }, + "label": "Message", + "name": "Message" + }, + "id": "Message:CurlyApplesRelate", + "measured": { + "height": 56, + "width": 200 + }, + "position": { + "x": 650, + "y": 200 + }, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "messageNode" + }, + { + "data": { + "form": { + "text": "ImageLingo lets you snap any photo containing text—menus, signs, or documents—and instantly recognize and translate it into your language of choice using advanced OCR and AI-powered translation technology. With automatic source-language detection and context-aware adaptations, translations preserve formatting, tone, and intent. Your on-the-go language assistant. " + }, + "label": "Note", + "name": "Translation Agent" + }, + "dragging": false, + "dragHandle": ".note-drag-handle", + "height": 190, + "id": "Note:OpenCobrasMarry", + "measured": { + "height": 190, + "width": 376 + }, + "position": { + "x": 385.5, + "y": -42 + }, + "resizing": false, + "selected": false, + "sourcePosition": "right", + "targetPosition": "left", + "type": "noteNode", + "width": 376 + } + ] + }, + "history": [], + "messages": [], + "path": [], + "retrieval": [] + }, "avatar": "data:image/png;base64,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" -} \ No newline at end of file +} + diff --git a/conf/llm_factories.json b/conf/llm_factories.json index e6b82f46b17..73f83a5da4f 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -2816,6 +2816,13 @@ "tags": "LLM,TEXT EMBEDDING,TEXT RE-RANK,IMAGE2TEXT", "status": "1", "llm": [ + { + "llm_name":"THUDM/GLM-4.1V-9B-Thinking", + "tags":"LLM,CHAT,IMAGE2TEXT, 64k", + "max_tokens":64000, + "model_type":"chat", + "is_tools": false + }, { "llm_name": "Qwen/Qwen3-Embedding-8B", "tags": "TEXT EMBEDDING,TEXT RE-RANK,32k", @@ -3145,13 +3152,6 @@ "model_type": "chat", "is_tools": true }, - { - "llm_name": "Qwen/Qwen2-1.5B-Instruct", - "tags": "LLM,CHAT,32k", - "max_tokens": 32000, - "model_type": "chat", - "is_tools": true - }, { "llm_name": "Pro/Qwen/Qwen2.5-Coder-7B-Instruct", "tags": "LLM,CHAT,32k", @@ -3159,13 +3159,6 @@ "model_type": "chat", "is_tools": false }, - { - "llm_name": "Pro/Qwen/Qwen2-VL-7B-Instruct", - "tags": "LLM,CHAT,IMAGE2TEXT,32k", - "max_tokens": 32000, - "model_type": "image2text", - "is_tools": false - }, { "llm_name": "Pro/Qwen/Qwen2.5-7B-Instruct", "tags": "LLM,CHAT,32k", From 2828e321bcd6db8ee515a55731f56e912027d524 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Sat, 11 Oct 2025 19:38:07 +0800 Subject: [PATCH 0811/1187] Fix: remove lang for autio. (#10496) ### What problem does this PR solve? ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/flow/parser/parser.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py index 004d4bc91f7..86e039118c4 100644 --- a/rag/flow/parser/parser.py +++ b/rag/flow/parser/parser.py @@ -184,8 +184,6 @@ def check(self): audio_config = self.setups.get("audio", "") if audio_config: self.check_empty(audio_config.get("llm_id"), "Audio VLM") - audio_language = audio_config.get("lang", "") - self.check_empty(audio_language, "Language") email_config = self.setups.get("email", "") if email_config: @@ -348,15 +346,13 @@ def _audio(self, name, blob): conf = self._param.setups["audio"] self.set_output("output_format", conf["output_format"]) - - lang = conf["lang"] _, ext = os.path.splitext(name) with tempfile.NamedTemporaryFile(suffix=ext) as tmpf: tmpf.write(blob) tmpf.flush() tmp_path = os.path.abspath(tmpf.name) - seq2txt_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.SPEECH2TEXT, lang=lang) + seq2txt_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.SPEECH2TEXT) txt = seq2txt_mdl.transcription(tmp_path) self.set_output("text", txt) From ad56137a59f155150fac7e9f146438b8e4b3d9a4 Mon Sep 17 00:00:00 2001 From: pyyuhao Date: Sat, 11 Oct 2025 19:58:12 +0800 Subject: [PATCH 0812/1187] =?UTF-8?q?Feat:=20=E2=80=8B=E2=80=8BOpenSearch'?= =?UTF-8?q?s=20support=20for=20newly=20embedding=20models=E2=80=8B?= =?UTF-8?q?=E2=80=8B=20(#10494)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What problem does this PR solve? fix issues:https://github.com/infiniflow/ragflow/issues/10402 As the newly distributed embedding models support vector dimensions max to 4096, while current OpenSearch's max dimension support is 1536. As I tested, the 4096-dimensions vector will be treated as a float type which is unacceptable in OpenSearch. Besides, OpenSearch supports max to 16000 dimensions by defalut with the vector engine(Faiss). According to: https://docs.opensearch.org/2.19/field-types/supported-field-types/knn-methods-engines/ I added max to 10240 dimensions support for OpenSearch, as I think will be sufficient in the future. As I tested , it worked well on my own server (treated as knn_vector)by using qwen3-embedding:8b as the embedding model: image ### Type of change - [x] New Feature (non-breaking change which adds functionality) By the way, I will still focus on the stuff about Elasticsearch/Opensearch as search engines and vector databases. Co-authored-by: 张雨豪 --- conf/os_mapping.json | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/conf/os_mapping.json b/conf/os_mapping.json index a8663e069a3..47b7c24b0f5 100644 --- a/conf/os_mapping.json +++ b/conf/os_mapping.json @@ -200,6 +200,61 @@ } } }, + { + "knn_vector": { + "match": "*_2048_vec", + "mapping": { + "type": "knn_vector", + "index": true, + "space_type": "cosinesimil", + "dimension": 2048 + } + } + }, + { + "knn_vector": { + "match": "*_4096_vec", + "mapping": { + "type": "knn_vector", + "index": true, + "space_type": "cosinesimil", + "dimension": 4096 + } + } + }, + { + "knn_vector": { + "match": "*_6144_vec", + "mapping": { + "type": "knn_vector", + "index": true, + "space_type": "cosinesimil", + "dimension": 6144 + } + } + }, + { + "knn_vector": { + "match": "*_8192_vec", + "mapping": { + "type": "knn_vector", + "index": true, + "space_type": "cosinesimil", + "dimension": 8192 + } + } + }, + { + "knn_vector": { + "match": "*_10240_vec", + "mapping": { + "type": "knn_vector", + "index": true, + "space_type": "cosinesimil", + "dimension": 10240 + } + } + }, { "binary": { "match": "*_bin", From 58836d84fe0efdda354269cb239f7ba479a6993c Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Mon, 13 Oct 2025 09:34:44 +0800 Subject: [PATCH 0813/1187] Fix: Mcp reset error, #10497 (#10498) ### What problem does this PR solve? Fix #10497 ### Type of change - [X] Bug Fix (non-breaking change which fixes an issue) --- agent/component/agent_with_tools.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/agent/component/agent_with_tools.py b/agent/component/agent_with_tools.py index aa1ac296d2c..32458fc85b4 100644 --- a/agent/component/agent_with_tools.py +++ b/agent/component/agent_with_tools.py @@ -347,6 +347,10 @@ def get_useful_memory(self, goal: str, sub_goal:str, topn=3, user_defined_prompt return "Error occurred." def reset(self, temp=False): + """ + Reset all tools if they have a reset method. This avoids errors for tools like MCPToolCallSession. + """ for k, cpn in self.tools.items(): - cpn.reset() + if hasattr(cpn, "reset") and callable(cpn.reset): + cpn.reset() From acca3640f7dadd6023568ac06a9e75ddf158946b Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 13 Oct 2025 11:10:54 +0800 Subject: [PATCH 0814/1187] Feat: Modify the background color of the canvas #9869 (#10507) ### What problem does this PR solve? Feat: Modify the background color of the canvas #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/canvas/background.tsx | 8 +++----- web/src/pages/agent/canvas/index.tsx | 2 +- web/src/pages/agent/canvas/node/note-node/index.tsx | 4 ++-- web/src/pages/data-flow/canvas/index.tsx | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/web/src/components/canvas/background.tsx b/web/src/components/canvas/background.tsx index cf7b18f45c7..1ae99ac76ae 100644 --- a/web/src/components/canvas/background.tsx +++ b/web/src/components/canvas/background.tsx @@ -1,13 +1,11 @@ -import { useIsDarkTheme } from '@/components/theme-provider'; import { Background } from '@xyflow/react'; export function AgentBackground() { - const isDarkTheme = useIsDarkTheme(); - return ( ); } diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index e6fa087f328..0bf32d08f93 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -232,7 +232,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { ]); return ( -
    +
    ) { return ( -
    +
    diff --git a/web/src/pages/data-flow/canvas/index.tsx b/web/src/pages/data-flow/canvas/index.tsx index 1023fe904aa..fa1bb341ca6 100644 --- a/web/src/pages/data-flow/canvas/index.tsx +++ b/web/src/pages/data-flow/canvas/index.tsx @@ -205,7 +205,7 @@ function DataFlowCanvas({ drawerVisible, hideDrawer, showLogSheet }: IProps) { }; return ( -
    +
    Date: Mon, 13 Oct 2025 11:11:06 +0800 Subject: [PATCH 0815/1187] fix: decode before format to json (#10506) ### What problem does this PR solve? Decode bytes before format to json. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/flow/parser/parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py index 86e039118c4..b1a34c59bf8 100644 --- a/rag/flow/parser/parser.py +++ b/rag/flow/parser/parser.py @@ -411,7 +411,7 @@ def _add_content(m, content_type): dispositions = content_disposition.strip().split(";") if dispositions[0].lower() == "attachment": filename = part.get_filename() - payload = part.get_payload(decode=True) + payload = part.get_payload(decode=True).decode(part.get_content_charset()) attachments.append({ "filename": filename, "payload": payload, @@ -448,7 +448,7 @@ def _add_content(m, content_type): for t in msg.attachments: attachments.append({ "filename": t.name, - "payload": t.data # binary + "payload": t.data.decode("utf-8") }) email_content["attachments"] = attachments From 65c3f0406c101cd836570d8842e5c4ea978213a5 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Mon, 13 Oct 2025 11:53:48 +0800 Subject: [PATCH 0816/1187] Fix: maintain backward compatibility for KB tasks (#10508) ### What problem does this PR solve? Maintain backward compatibility for KB tasks ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/kb_app.py | 16 ++++++++++++---- rag/svr/task_executor.py | 41 +++++++++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/api/apps/kb_app.py b/api/apps/kb_app.py index bca28fb6f30..141f1f5d477 100644 --- a/api/apps/kb_app.py +++ b/api/apps/kb_app.py @@ -36,6 +36,7 @@ from rag.nlp import search from api.constants import DATASET_NAME_LIMIT from rag.settings import PAGERANK_FLD +from rag.utils.redis_conn import REDIS_CONN from rag.utils.storage_factory import STORAGE_IMPL @@ -760,18 +761,25 @@ def delete_kb_task(): match pipeline_task_type: case PipelineTaskType.GRAPH_RAG: settings.docStoreConn.delete({"knowledge_graph_kwd": ["graph", "subgraph", "entity", "relation"]}, search.index_name(kb.tenant_id), kb_id) - kb_task_id = "graphrag_task_id" + kb_task_id_field = "graphrag_task_id" + task_id = kb.graphrag_task_id kb_task_finish_at = "graphrag_task_finish_at" case PipelineTaskType.RAPTOR: - kb_task_id = "raptor_task_id" + kb_task_id_field = "raptor_task_id" + task_id = kb.raptor_task_id kb_task_finish_at = "raptor_task_finish_at" case PipelineTaskType.MINDMAP: - kb_task_id = "mindmap_task_id" + kb_task_id_field = "mindmap_task_id" + task_id = kb.mindmap_task_id kb_task_finish_at = "mindmap_task_finish_at" case _: return get_error_data_result(message="Internal Error: Invalid task type") - ok = KnowledgebaseService.update_by_id(kb_id, {kb_task_id: "", kb_task_finish_at: None}) + def cancel_task(task_id): + REDIS_CONN.set(f"{task_id}-cancel", "x") + cancel_task(task_id) + + ok = KnowledgebaseService.update_by_id(kb_id, {kb_task_id_field: "", kb_task_finish_at: None}) if not ok: return server_error_response(f"Internal error: cannot delete task {pipeline_task_type}") diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index c0b1d2c51e7..9801b53dd23 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -691,7 +691,7 @@ async def run_raptor_for_kb(row, kb_parser_config, chat_mdl, embd_mdl, vector_si raptor_config["threshold"], ) original_length = len(chunks) - chunks = await raptor(chunks, row["kb_parser_config"]["raptor"]["random_seed"], callback) + chunks = await raptor(chunks, kb_parser_config["raptor"]["random_seed"], callback) doc = { "doc_id": fake_doc_id, "kb_id": [str(row["kb_id"])], @@ -814,8 +814,22 @@ async def do_handle_task(task): kb_parser_config = kb.parser_config if not kb_parser_config.get("raptor", {}).get("use_raptor", False): - progress_callback(prog=-1.0, msg="Internal error: Invalid RAPTOR configuration") - return + kb_parser_config.update( + { + "raptor": { + "use_raptor": True, + "prompt": "Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following:\n {cluster_content}\nThe above is the content you need to summarize.", + "max_token": 256, + "threshold": 0.1, + "max_cluster": 64, + "random_seed": 0, + }, + } + ) + if not KnowledgebaseService.update_by_id(kb.id, {"parser_config":kb_parser_config}): + progress_callback(prog=-1.0, msg="Internal error: Invalid RAPTOR configuration") + return + # bind LLM for raptor chat_model = LLMBundle(task_tenant_id, LLMType.CHAT, llm_name=task_llm_id, lang=task_language) # run RAPTOR @@ -838,8 +852,25 @@ async def do_handle_task(task): kb_parser_config = kb.parser_config if not kb_parser_config.get("graphrag", {}).get("use_graphrag", False): - progress_callback(prog=-1.0, msg="Internal error: Invalid GraphRAG configuration") - return + kb_parser_config.update( + { + "graphrag": { + "use_graphrag": True, + "entity_types": [ + "organization", + "person", + "geo", + "event", + "category", + ], + "method": "light", + } + } + ) + if not KnowledgebaseService.update_by_id(kb.id, {"parser_config":kb_parser_config}): + progress_callback(prog=-1.0, msg="Internal error: Invalid GraphRAG configuration") + return + graphrag_conf = kb_parser_config.get("graphrag", {}) start_ts = timer() From 4e6b84bb41895ab7c8514bf54a18698dbec29e44 Mon Sep 17 00:00:00 2001 From: buua436 <66937541+buua436@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:57:40 +0800 Subject: [PATCH 0817/1187] Feat: add trino support (#10512) ### What problem does this PR solve? issue: [#10296](https://github.com/infiniflow/ragflow/issues/10296) change: - ExeSQL: support connecting to Trino. - Validation: password can be empty only when db_type === "trino"; all other database types keep the existing requirement (non-empty). ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- agent/tools/exesql.py | 44 ++++++++++++++++++- api/apps/canvas_app.py | 43 ++++++++++++++++++ .../agent/form/exesql-form/use-submit-form.ts | 23 +++++++--- web/src/pages/agent/options.ts | 1 + 4 files changed, 104 insertions(+), 7 deletions(-) diff --git a/agent/tools/exesql.py b/agent/tools/exesql.py index 2e1cc24bfd0..d9374532379 100644 --- a/agent/tools/exesql.py +++ b/agent/tools/exesql.py @@ -53,12 +53,13 @@ def __init__(self): self.max_records = 1024 def check(self): - self.check_valid_value(self.db_type, "Choose DB type", ['mysql', 'postgres', 'mariadb', 'mssql', 'IBM DB2']) + self.check_valid_value(self.db_type, "Choose DB type", ['mysql', 'postgres', 'mariadb', 'mssql', 'IBM DB2', 'trino']) self.check_empty(self.database, "Database name") self.check_empty(self.username, "database username") self.check_empty(self.host, "IP Address") self.check_positive_integer(self.port, "IP Port") - self.check_empty(self.password, "Database password") + if self.db_type != "trino": + self.check_empty(self.password, "Database password") self.check_positive_integer(self.max_records, "Maximum number of records") if self.database == "rag_flow": if self.host == "ragflow-mysql": @@ -123,6 +124,45 @@ def convert_decimals(obj): r'PWD=' + self._param.password ) db = pyodbc.connect(conn_str) + elif self._param.db_type == 'trino': + try: + import trino + from trino.auth import BasicAuthentication + except Exception: + raise Exception("Missing dependency 'trino'. Please install: pip install trino") + + def _parse_catalog_schema(db: str): + if not db: + return None, None + if "." in db: + c, s = db.split(".", 1) + elif "/" in db: + c, s = db.split("/", 1) + else: + c, s = db, "default" + return c, s + + catalog, schema = _parse_catalog_schema(self._param.database) + if not catalog: + raise Exception("For Trino, `database` must be 'catalog.schema' or at least 'catalog'.") + + http_scheme = "https" if os.environ.get("TRINO_USE_TLS", "0") == "1" else "http" + auth = None + if http_scheme == "https" and self._param.password: + auth = BasicAuthentication(self._param.username, self._param.password) + + try: + db = trino.dbapi.connect( + host=self._param.host, + port=int(self._param.port or 8080), + user=self._param.username or "ragflow", + catalog=catalog, + schema=schema or "default", + http_scheme=http_scheme, + auth=auth + ) + except Exception as e: + raise Exception("Database Connection Failed! \n" + str(e)) elif self._param.db_type == 'IBM DB2': import ibm_db conn_str = ( diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py index c3d4dd82499..7cdeff097da 100644 --- a/api/apps/canvas_app.py +++ b/api/apps/canvas_app.py @@ -409,6 +409,49 @@ def test_db_connect(): ibm_db.fetch_assoc(stmt) ibm_db.close(conn) return get_json_result(data="Database Connection Successful!") + elif req["db_type"] == 'trino': + def _parse_catalog_schema(db: str): + if not db: + return None, None + if "." in db: + c, s = db.split(".", 1) + elif "/" in db: + c, s = db.split("/", 1) + else: + c, s = db, "default" + return c, s + try: + import trino + import os + from trino.auth import BasicAuthentication + except Exception: + return server_error_response("Missing dependency 'trino'. Please install: pip install trino") + + catalog, schema = _parse_catalog_schema(req["database"]) + if not catalog: + return server_error_response("For Trino, 'database' must be 'catalog.schema' or at least 'catalog'.") + + http_scheme = "https" if os.environ.get("TRINO_USE_TLS", "0") == "1" else "http" + + auth = None + if http_scheme == "https" and req.get("password"): + auth = BasicAuthentication(req.get("username") or "ragflow", req["password"]) + + conn = trino.dbapi.connect( + host=req["host"], + port=int(req["port"] or 8080), + user=req["username"] or "ragflow", + catalog=catalog, + schema=schema or "default", + http_scheme=http_scheme, + auth=auth + ) + cur = conn.cursor() + cur.execute("SELECT 1") + cur.fetchall() + cur.close() + conn.close() + return get_json_result(data="Database Connection Successful!") else: return server_error_response("Unsupported database type.") if req["db_type"] != 'mssql': diff --git a/web/src/pages/agent/form/exesql-form/use-submit-form.ts b/web/src/pages/agent/form/exesql-form/use-submit-form.ts index 8be69c7b067..b7141a2cf92 100644 --- a/web/src/pages/agent/form/exesql-form/use-submit-form.ts +++ b/web/src/pages/agent/form/exesql-form/use-submit-form.ts @@ -8,14 +8,27 @@ export const ExeSQLFormSchema = { username: z.string().min(1), host: z.string().min(1), port: z.number(), - password: z.string().min(1), + password: z.string().optional().or(z.literal('')), max_records: z.number(), }; -export const FormSchema = z.object({ - sql: z.string().optional(), - ...ExeSQLFormSchema, -}); +export const FormSchema = z + .object({ + sql: z.string().optional(), + ...ExeSQLFormSchema, + }) + .superRefine((v, ctx) => { + if ( + v.db_type !== 'trino' && + !(v.password && v.password.trim().length > 0) + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['password'], + message: 'String must contain at least 1 character(s)', + }); + } + }); export function useSubmitForm() { const { testDbConnect, loading } = useTestDbConnect(); diff --git a/web/src/pages/agent/options.ts b/web/src/pages/agent/options.ts index f231ec450b2..9d68ec70bf4 100644 --- a/web/src/pages/agent/options.ts +++ b/web/src/pages/agent/options.ts @@ -2139,6 +2139,7 @@ export const ExeSQLOptions = [ 'mariadb', 'mssql', 'IBM DB2', + 'trino', ].map((x) => ({ label: upperFirst(x), value: x, From 24481f03329196cc0aa2816f72a8df814276b974 Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Mon, 13 Oct 2025 13:58:08 +0800 Subject: [PATCH 0818/1187] Fix: Update lm studio models support, refer to #8116 (#10509) ### What problem does this PR solve? Fix: Update lm studio models support, refer to #8116 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] Documentation Update --- docs/references/supported_models.mdx | 2 +- .../pages/user-setting/setting-model/ollama-modal/index.tsx | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/references/supported_models.mdx b/docs/references/supported_models.mdx index 7e704067b65..9c00840b4aa 100644 --- a/docs/references/supported_models.mdx +++ b/docs/references/supported_models.mdx @@ -33,7 +33,7 @@ A complete list of models supported by RAGFlow, which will continue to expand. | Jina | | :heavy_check_mark: | :heavy_check_mark: | | | | | LeptonAI | :heavy_check_mark: | | | | | | | LocalAI | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | | | -| LM-Studio | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | +| LM-Studio | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | | | | MiniMax | :heavy_check_mark: | | | | | | | Mistral | :heavy_check_mark: | :heavy_check_mark: | | | | | | ModelScope | :heavy_check_mark: | | | | | | diff --git a/web/src/pages/user-setting/setting-model/ollama-modal/index.tsx b/web/src/pages/user-setting/setting-model/ollama-modal/index.tsx index dd86b5796af..8ef4199d68e 100644 --- a/web/src/pages/user-setting/setting-model/ollama-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/ollama-modal/index.tsx @@ -107,6 +107,11 @@ const OllamaModal = ({ { value: 'chat', label: 'chat' }, { value: 'rerank', label: 'rerank' }, ], + [LLMFactory.LMStudio]: [ + { value: 'chat', label: 'chat' }, + { value: 'embedding', label: 'embedding' }, + { value: 'image2text', label: 'image2text' }, + ], [LLMFactory.Xinference]: [ { value: 'chat', label: 'chat' }, { value: 'embedding', label: 'embedding' }, From 9c53b3336adddd6c0b73e0a15e41135714983b97 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 13 Oct 2025 14:37:30 +0800 Subject: [PATCH 0819/1187] Fix: The Context Generator(Transformer) node can only be followed by a Tokenizer(Indexer) and a Context Generator(Transformer). #9869 (#10515) ### What problem does this PR solve? Fix: The Context Generator node can only be followed by a Tokenizer and a Context Generator. #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../components/file-upload-dialog/index.tsx | 2 +- web/src/locales/en.ts | 6 ++-- web/src/pages/data-flow/canvas/index.tsx | 1 + .../node/dropdown/next-step-dropdown.tsx | 28 +++++++++++++------ .../pages/data-flow/canvas/node/handle.tsx | 1 + web/src/pages/data-flow/constant.tsx | 4 ++- .../data-flow/form/extractor-form/index.tsx | 5 ++++ 7 files changed, 34 insertions(+), 13 deletions(-) diff --git a/web/src/components/file-upload-dialog/index.tsx b/web/src/components/file-upload-dialog/index.tsx index 162f6b8d111..aea32b472c0 100644 --- a/web/src/components/file-upload-dialog/index.tsx +++ b/web/src/components/file-upload-dialog/index.tsx @@ -98,7 +98,7 @@ export function FileUploadDialog({ return ( - + {t('fileManager.uploadFile')} diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index d112d0891e8..40c04928b4e 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1703,8 +1703,8 @@ This delimiter is used to split the input text into several text pieces echo of parser: 'Parser', parserDescription: 'Extracts raw text and structure from files for downstream processing.', - tokenizer: 'Tokenizer', - tokenizerRequired: 'Please add the Tokenizer node first', + tokenizer: 'Indexer', + tokenizerRequired: 'Please add the Indexer node first', tokenizerDescription: 'Transforms text into the required data structure (e.g., vector embeddings for Embedding Search) depending on the chosen search method.', splitter: 'Token Splitter', @@ -1713,7 +1713,7 @@ This delimiter is used to split the input text into several text pieces echo of hierarchicalMergerDescription: 'Split documents into sections by title hierarchy with regex rules for finer control.', hierarchicalMerger: 'Title Splitter', - extractor: 'Context Generator', + extractor: 'Transformer', extractorDescription: 'Use an LLM to extract structured insights from document chunks—such as summaries, classifications, etc.', outputFormat: 'Output format', diff --git a/web/src/pages/data-flow/canvas/index.tsx b/web/src/pages/data-flow/canvas/index.tsx index fa1bb341ca6..8672a2074c3 100644 --- a/web/src/pages/data-flow/canvas/index.tsx +++ b/web/src/pages/data-flow/canvas/index.tsx @@ -292,6 +292,7 @@ function DataFlowCanvas({ drawerVisible, hideDrawer, showLogSheet }: IProps) { clearActiveDropdown(); }} position={dropdownPosition} + nodeId={connectionStartRef.current?.nodeId || ''} > diff --git a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx index 6beadb7e810..2450de57917 100644 --- a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx @@ -131,16 +131,24 @@ function useRestrictSingleOperatorOnCanvas() { function AccordionOperators({ isCustomDropdown = false, mousePosition, + nodeId, }: { isCustomDropdown?: boolean; mousePosition?: { x: number; y: number }; + nodeId?: string; }) { const singleOperators = useRestrictSingleOperatorOnCanvas(); + const { getOperatorTypeFromId } = useGraphStore((state) => state); + const operators = useMemo(() => { - const list = [...singleOperators]; + let list = [...singleOperators]; + if (getOperatorTypeFromId(nodeId) === Operator.Extractor) { + const Splitters = [Operator.HierarchicalMerger, Operator.Splitter]; + list = list.filter((x) => !Splitters.includes(x)); // The Context Generator node can only be followed by a Tokenizer and a Context Generator. + } list.push(Operator.Extractor); return list; - }, [singleOperators]); + }, [getOperatorTypeFromId, nodeId, singleOperators]); return ( & { + position?: { x: number; y: number }; + onNodeCreated?: (newNodeId: string) => void; + nodeId?: string; + }; export function InnerNextStepDropdown({ children, hideModal, position, onNodeCreated, -}: PropsWithChildren & - IModalProps & { - position?: { x: number; y: number }; - onNodeCreated?: (newNodeId: string) => void; - }) { + nodeId, +}: NextStepDropdownProps) { const dropdownRef = useRef(null); useEffect(() => { @@ -200,6 +211,7 @@ export function InnerNextStepDropdown({ @@ -224,7 +236,7 @@ export function InnerNextStepDropdown({ > {t('flow.nextStep')} - + diff --git a/web/src/pages/data-flow/canvas/node/handle.tsx b/web/src/pages/data-flow/canvas/node/handle.tsx index da61e3b4c6e..17fe2839041 100644 --- a/web/src/pages/data-flow/canvas/node/handle.tsx +++ b/web/src/pages/data-flow/canvas/node/handle.tsx @@ -61,6 +61,7 @@ export function CommonHandle({ hideModal(); clearActiveDropdown(); }} + nodeId={nodeId} > diff --git a/web/src/pages/data-flow/constant.tsx b/web/src/pages/data-flow/constant.tsx index 600febef99e..8ebcc671c55 100644 --- a/web/src/pages/data-flow/constant.tsx +++ b/web/src/pages/data-flow/constant.tsx @@ -299,7 +299,9 @@ export const initialHierarchicalMergerValues = { export const initialExtractorValues = { ...initialLlmBaseValues, field_name: ContextGeneratorFieldName.Summary, - outputs: {}, + outputs: { + chunks: { type: 'Array', value: [] }, + }, }; export const CategorizeAnchorPointPositions = [ diff --git a/web/src/pages/data-flow/form/extractor-form/index.tsx b/web/src/pages/data-flow/form/extractor-form/index.tsx index cb0abc877cb..ddc0b8bdd10 100644 --- a/web/src/pages/data-flow/form/extractor-form/index.tsx +++ b/web/src/pages/data-flow/form/extractor-form/index.tsx @@ -19,7 +19,9 @@ import { useBuildNodeOutputOptions } from '../../hooks/use-build-options'; import { useFormValues } from '../../hooks/use-form-values'; import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; import { useSwitchPrompt } from './use-switch-prompt'; export const FormSchema = z.object({ @@ -31,6 +33,8 @@ export const FormSchema = z.object({ export type ExtractorFormSchemaType = z.infer; +const outputList = buildOutputList(initialExtractorValues.outputs); + const ExtractorForm = ({ node }: INextOperatorForm) => { const defaultValues = useFormValues(initialExtractorValues, node); const { t } = useTranslation(); @@ -85,6 +89,7 @@ const ExtractorForm = ({ node }: INextOperatorForm) => { baseOptions={promptOptions} > + {visible && ( Date: Mon, 13 Oct 2025 15:31:36 +0800 Subject: [PATCH 0820/1187] Fix: Optimized the login page and fixed some known issues. #9869 (#10514) ### What problem does this PR solve? Fix: Optimized the login page and fixed some known issues. #9869 - Added the FlipCard3D component to implement a 3D flip effect on the login/registration forms. - Adjusted the Spotlight component to support custom positioning and color configurations. - Updated the route to point to the new login page /login-next. - Added a cancel interface to the auto-generate function. - Fixed scroll bar issues in PDF preview. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/spotlight.tsx | 19 +- web/src/locales/en.ts | 17 +- web/src/locales/zh.ts | 13 +- .../components/document-preview/index.less | 2 +- .../components/document-preview/index.less | 2 +- .../components/document-preview/index.tsx | 3 +- .../pages/dataset/dataset-overview/index.tsx | 22 +- .../configuration/common-item.tsx | 7 +- .../dataset/dataset/generate-button/hook.ts | 47 +-- web/src/pages/dataset/sidebar/index.tsx | 4 +- .../datasets/dataset-creating-dialog.tsx | 4 +- web/src/pages/login-next/card.tsx | 39 ++ web/src/pages/login-next/index.less | 14 + web/src/pages/login-next/index.tsx | 337 ++++++++++-------- web/src/pages/login-next/spotlight-top.tsx | 70 ---- web/src/routes.ts | 4 +- web/src/utils/common-util.ts | 2 +- 17 files changed, 344 insertions(+), 262 deletions(-) create mode 100644 web/src/pages/login-next/card.tsx delete mode 100644 web/src/pages/login-next/spotlight-top.tsx diff --git a/web/src/components/spotlight.tsx b/web/src/components/spotlight.tsx index f5eacbceec6..50b4682f50d 100644 --- a/web/src/components/spotlight.tsx +++ b/web/src/components/spotlight.tsx @@ -1,10 +1,14 @@ import { useIsDarkTheme } from '@/components/theme-provider'; +import { parseColorToRGB } from '@/utils/common-util'; import React from 'react'; interface SpotlightProps { className?: string; opcity?: number; coverage?: number; + X?: string; + Y?: string; + color?: string; } /** * @@ -16,9 +20,20 @@ const Spotlight: React.FC = ({ className, opcity = 0.5, coverage = 60, + X = '50%', + Y = '190%', + color, }) => { const isDark = useIsDarkTheme(); - const rgb = isDark ? '255, 255, 255' : '194, 221, 243'; + let realColor: [number, number, number] | undefined = undefined; + if (color) { + realColor = parseColorToRGB(color); + } + const rgb = realColor + ? realColor.join(',') + : isDark + ? '255, 255, 255' + : '194, 221, 243'; return (
    = ({
    diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 40c04928b4e..fd5c0f016d7 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -57,6 +57,8 @@ export default { }, }, login: { + loginTitle: 'Sign in to Your Account', + signUpTitle: 'Create an Account', login: 'Sign in', signUp: 'Sign up', loginDescription: 'We’re so excited to see you again!', @@ -72,7 +74,8 @@ export default { nicknamePlaceholder: 'Please input nickname', register: 'Create an account', continue: 'Continue', - title: 'Start building your smart assistants.', + title: 'A leading RAG engine for LLM context', + start: "Let's get started", description: 'Sign up for free to explore top RAG technology. Create knowledge bases and AIs to empower your business.', review: 'from 500+ reviews', @@ -114,7 +117,7 @@ export default { generateRaptor: 'This will extract entities and relationships from all your documents in this dataset. The process may take a while to complete.', generate: 'Generate', - raptor: 'Raptor', + raptor: 'RAPTOR', processingType: 'Processing Type', dataPipeline: 'Ingestion pipeline', operations: 'Operations', @@ -128,7 +131,7 @@ export default { fileName: 'File Name', datasetLogs: 'Dataset', fileLogs: 'File', - overview: 'Overview', + overview: 'Logs', success: 'Success', failed: 'Failed', completed: 'Completed', @@ -270,7 +273,7 @@ export default { reRankModelWaring: 'Re-rank model is very time consuming.', }, knowledgeConfiguration: { - tocExtraction: 'toc toggle', + tocExtraction: 'TOC Enhance', tocExtractionTip: " For existing chunks, generate a hierarchical table of contents (one directory per file). During queries, when Directory Enhancement is activated, the system will use a large model to determine which directory items are relevant to the user's question, thereby identifying the relevant chunks.", deleteGenerateModalContent: ` @@ -1817,9 +1820,13 @@ Important structured information may include: names, dates, locations, events, k }, datasetOverview: { downloadTip: 'Files being downloaded from data sources. ', - processingTip: 'Files being processed by data flows.', + processingTip: 'Files being processed by data pipelines.', totalFiles: 'Total Files', downloading: 'Downloading', + downloadSuccessTip: 'Total successful downloads', + downloadFailedTip: 'Total failed downloads', + processingSuccessTip: 'Total successfully processed files', + processingFailedTip: 'Total failed processes', processing: 'Processing', }, }, diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index f582099955d..119aad5d673 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -49,6 +49,8 @@ export default { promptPlaceholder: '请输入或使用 / 快速插入变量。', }, login: { + loginTitle: '登录账户', + signUpTitle: '创建账户', login: '登录', signUp: '注册', loginDescription: '很高兴再次见到您!', @@ -64,7 +66,8 @@ export default { nicknamePlaceholder: '请输入名称', register: '创建账户', continue: '继续', - title: '开始构建您的智能助手', + title: 'A leading RAG engine for LLM context', + start: '立即开始', description: '免费注册以探索顶级 RAG 技术。 创建知识库和人工智能来增强您的业务', review: '来自 500 多条评论', @@ -116,7 +119,7 @@ export default { fileName: '文件名', datasetLogs: '数据集', fileLogs: '文件', - overview: '概览', + overview: '日志', success: '成功', failed: '失败', completed: '已完成', @@ -255,7 +258,7 @@ export default { theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除', }, knowledgeConfiguration: { - tocExtraction: '目录提取', + tocExtraction: '目录增强', tocExtractionTip: '对于已有的chunk生成层级结构的目录信息(每个文件一个目录)。在查询时,激活`目录增强`后,系统会用大模型去判断用户问题和哪些目录项相关,从而找到相关的chunk。', deleteGenerateModalContent: ` @@ -1713,6 +1716,10 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`, totalFiles: '文件总数', downloading: '正在下载', processing: '正在处理', + downloadSuccessTip: '下载成功总数', + downloadFailedTip: '下载失败总数', + processingSuccessTip: '处理成功的文件总数', + processingFailedTip: '处理失败的文件总数', }, }, }; diff --git a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/document-preview/index.less b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/document-preview/index.less index bbba51f0932..8f456af5a9b 100644 --- a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/document-preview/index.less +++ b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/components/document-preview/index.less @@ -1,7 +1,7 @@ .documentContainer { width: 100%; // height: calc(100vh - 284px); - height: calc(100vh - 170px); + height: calc(100vh - 180px); position: relative; :global(.PdfHighlighter) { overflow-x: hidden; diff --git a/web/src/pages/dataflow-result/components/document-preview/index.less b/web/src/pages/dataflow-result/components/document-preview/index.less index bbba51f0932..8f456af5a9b 100644 --- a/web/src/pages/dataflow-result/components/document-preview/index.less +++ b/web/src/pages/dataflow-result/components/document-preview/index.less @@ -1,7 +1,7 @@ .documentContainer { width: 100%; // height: calc(100vh - 284px); - height: calc(100vh - 170px); + height: calc(100vh - 180px); position: relative; :global(.PdfHighlighter) { overflow-x: hidden; diff --git a/web/src/pages/dataflow-result/components/document-preview/index.tsx b/web/src/pages/dataflow-result/components/document-preview/index.tsx index dd7b3534893..0a5cf08e81b 100644 --- a/web/src/pages/dataflow-result/components/document-preview/index.tsx +++ b/web/src/pages/dataflow-result/components/document-preview/index.tsx @@ -4,7 +4,6 @@ import CSVFileViewer from './csv-preview'; import { DocPreviewer } from './doc-preview'; import { ExcelCsvPreviewer } from './excel-preview'; import { ImagePreviewer } from './image-preview'; -import styles from './index.less'; import PdfPreviewer, { IProps } from './pdf-preview'; import { PptPreviewer } from './ppt-preview'; import { TxtPreviewer } from './txt-preview'; @@ -24,7 +23,7 @@ const Preview = ({ return ( <> {fileType === 'pdf' && highlights && setWidthAndHeight && ( -
    +
    = ({ @@ -56,7 +58,9 @@ const StatCard: FC = ({ const CardFooterProcess: FC = ({ success = 0, + successTip, failed = 0, + failedTip, }) => { const { t } = useTranslation(); return ( @@ -65,8 +69,13 @@ const CardFooterProcess: FC = ({
    -
    +
    {t('knowledgeDetails.success')} + {successTip && ( + + + + )}
    {success || 0}
    @@ -74,8 +83,13 @@ const CardFooterProcess: FC = ({
    -
    +
    {t('knowledgeDetails.failed')} + {failedTip && ( + + + + )}
    {failed || 0}
    @@ -259,7 +273,9 @@ const FileLogsPage: FC = () => { > { >
    diff --git a/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx b/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx index 031bce54742..e84900f6448 100644 --- a/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx +++ b/web/src/pages/dataset/dataset-setting/configuration/common-item.tsx @@ -144,7 +144,12 @@ export function ParseTypeItem({ line = 2 }: { line?: number }) { > -
    +
    {t('builtIn')} {t('manualSetup')}
    diff --git a/web/src/pages/dataset/dataset/generate-button/hook.ts b/web/src/pages/dataset/dataset/generate-button/hook.ts index 772c36ed7dc..e45a81e3826 100644 --- a/web/src/pages/dataset/dataset/generate-button/hook.ts +++ b/web/src/pages/dataset/dataset/generate-button/hook.ts @@ -6,7 +6,7 @@ import { t } from 'i18next'; import { useEffect, useState } from 'react'; import { useParams } from 'umi'; import { ProcessingType } from '../../dataset-overview/dataset-common'; -import { GenerateType } from './generate'; +import { GenerateType, GenerateTypeMap } from './generate'; export const generateStatus = { running: 'running', completed: 'completed', @@ -103,9 +103,28 @@ export const useTraceGenerate = ({ open }: { open: boolean }) => { raptorRunloading, }; }; + +export const useUnBindTask = () => { + const { id } = useParams(); + const { mutateAsync: handleUnbindTask } = useMutation({ + mutationKey: [DatasetKey.pauseGenerate], + mutationFn: async ({ type }: { type: ProcessingType }) => { + const { data } = await deletePipelineTask({ kb_id: id as string, type }); + if (data.code === 0) { + message.success(t('message.operated')); + // queryClient.invalidateQueries({ + // queryKey: [type], + // }); + } + return data; + }, + }); + return { handleUnbindTask }; +}; export const useDatasetGenerate = () => { const queryClient = useQueryClient(); const { id } = useParams(); + const { handleUnbindTask } = useUnBindTask(); const { data, isPending: loading, @@ -143,8 +162,12 @@ export const useDatasetGenerate = () => { type: GenerateType; }) => { const { data } = await agentService.cancelDataflow(task_id); - if (data.code === 0) { - message.success(t('message.operated')); + + const unbindData = await handleUnbindTask({ + type: GenerateTypeMap[type as GenerateType], + }); + if (data.code === 0 && unbindData.code === 0) { + // message.success(t('message.operated')); queryClient.invalidateQueries({ queryKey: [type], }); @@ -154,21 +177,3 @@ export const useDatasetGenerate = () => { }); return { runGenerate: mutateAsync, pauseGenerate, data, loading }; }; - -export const useUnBindTask = () => { - const { id } = useParams(); - const { mutateAsync: handleUnbindTask } = useMutation({ - mutationKey: [DatasetKey.pauseGenerate], - mutationFn: async ({ type }: { type: ProcessingType }) => { - const { data } = await deletePipelineTask({ kb_id: id as string, type }); - if (data.code === 0) { - message.success(t('message.operated')); - // queryClient.invalidateQueries({ - // queryKey: [type], - // }); - } - return data; - }, - }); - return { handleUnbindTask }; -}; diff --git a/web/src/pages/dataset/sidebar/index.tsx b/web/src/pages/dataset/sidebar/index.tsx index 722f5996718..d48019e510a 100644 --- a/web/src/pages/dataset/sidebar/index.tsx +++ b/web/src/pages/dataset/sidebar/index.tsx @@ -10,7 +10,7 @@ import { cn, formatBytes } from '@/lib/utils'; import { Routes } from '@/routes'; import { formatPureDate } from '@/utils/date'; import { isEmpty } from 'lodash'; -import { Banknote, DatabaseZap, FileSearch2, FolderOpen } from 'lucide-react'; +import { Banknote, FileSearch2, FolderOpen, Logs } from 'lucide-react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useHandleMenuClick } from './hooks'; @@ -40,7 +40,7 @@ export function SideBar({ refreshCount }: PropType) { key: Routes.DatasetTesting, }, { - icon: , + icon: , label: t(`knowledgeDetails.overview`), key: Routes.DataSetOverview, }, diff --git a/web/src/pages/datasets/dataset-creating-dialog.tsx b/web/src/pages/datasets/dataset-creating-dialog.tsx index 6b54de904ec..d372352c1e9 100644 --- a/web/src/pages/datasets/dataset-creating-dialog.tsx +++ b/web/src/pages/datasets/dataset-creating-dialog.tsx @@ -17,7 +17,6 @@ import { } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { FormLayout } from '@/constants/form'; -import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { IModalProps } from '@/interfaces/common'; import { zodResolver } from '@hookform/resolvers/zod'; import { useEffect } from 'react'; @@ -103,7 +102,6 @@ export function InputForm({ onOk }: IModalProps) { form.setValue('pipeline_id', ''); } }, [parseType, form]); - const { navigateToAgents } = useNavigatePage(); return (
    @@ -157,7 +155,7 @@ export function DatasetCreatingDialog({ return ( - + {t('knowledgeList.createKnowledgeBase')} diff --git a/web/src/pages/login-next/card.tsx b/web/src/pages/login-next/card.tsx new file mode 100644 index 00000000000..0908da6a1b0 --- /dev/null +++ b/web/src/pages/login-next/card.tsx @@ -0,0 +1,39 @@ +import React, { useEffect, useState } from 'react'; +import './index.less'; + +type IProps = { + children: React.ReactNode; + isLoginPage: boolean; +}; +const FlipCard3D = (props: IProps) => { + const { children, isLoginPage } = props; + const [isFlipped, setIsFlipped] = useState(false); + useEffect(() => { + console.log('title', isLoginPage); + if (isLoginPage) { + setIsFlipped(false); + } else { + setIsFlipped(true); + } + }, [isLoginPage]); + + return ( +
    +
    + {/* Front Face */} +
    + {children} +
    + + {/* Back Face */} +
    + {children} +
    +
    +
    + ); +}; + +export default FlipCard3D; diff --git a/web/src/pages/login-next/index.less b/web/src/pages/login-next/index.less index 2322be352e7..1ce641c32af 100644 --- a/web/src/pages/login-next/index.less +++ b/web/src/pages/login-next/index.less @@ -40,3 +40,17 @@ } ////////////////////////////////////////////////////////////////////////// +.perspective-1000 { + perspective: 1000px; +} +.transform-style-3d { + transform-style: preserve-3d; + transition-duration: 0.4s; +} +.backface-hidden { + backface-visibility: hidden; +} + +.rotate-y-180 { + transform: rotateY(180deg); +} diff --git a/web/src/pages/login-next/index.tsx b/web/src/pages/login-next/index.tsx index 35e3e76b443..8b8601889b6 100644 --- a/web/src/pages/login-next/index.tsx +++ b/web/src/pages/login-next/index.tsx @@ -25,11 +25,12 @@ import { } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { zodResolver } from '@hookform/resolvers/zod'; +import { Eye, EyeOff } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { BgSvg } from './bg'; +import FlipCard3D from './card'; import './index.less'; -import { SpotlightTopLeft, SpotlightTopRight } from './spotlight-top'; const Login = () => { const [title, setTitle] = useState('login'); @@ -40,6 +41,8 @@ const Login = () => { const { login: loginWithChannel, loading: loginWithChannelLoading } = useLoginWithChannel(); const { t } = useTranslation('translation', { keyPrefix: 'login' }); + const [isLoginPage, setIsLoginPage] = useState(true); + const [showPassword, setShowPassword] = useState(false); const loading = signLoading || registerLoading || @@ -60,10 +63,15 @@ const Login = () => { }; const changeTitle = () => { + setIsLoginPage(title !== 'login'); if (title === 'login' && !registerEnabled) { return; } - setTitle((title) => (title === 'login' ? 'register' : 'login')); + + setTimeout(() => { + setTitle(title === 'login' ? 'register' : 'login'); + }, 200); + // setTitle((title) => (title === 'login' ? 'register' : 'login')); }; const FormSchema = z @@ -129,177 +137,214 @@ const Login = () => { return (
    - - - + + + + + {/* */}
    -
    +
    logo
    - RAGFlow +
    RAGFlow
    -

    - A Leading RAG engine with Agent for superior LLM context. -

    +

    {t('title')}

    - Let's get started + {t('start')}
    {/* Logo and Header */} {/* Login Form */} -
    -

    - {title === 'login' - ? 'Sign in to Your Account' - : 'Create an Account'} -

    -
    -
    - - onCheck(data))} - > - ( - - {t('emailLabel')} - - - - - - )} - /> - {title === 'register' && ( - ( - - {t('nicknameLabel')} - - - - - + +
    +
    +

    + {title === 'login' ? t('loginTitle') : t('signUpTitle')} +

    +
    +
    + + onCheck(data))} + > + ( + + {t('emailLabel')} + + + + + + )} + /> + {title === 'register' && ( + ( + + {t('nicknameLabel')} + + + + + + )} + /> )} - /> - )} - ( - - {t('passwordLabel')} - - - - - - )} - /> + ( + + {t('passwordLabel')} + +
    + + +
    +
    + +
    + )} + /> - {title === 'login' && ( - ( - - -
    - { - field.onChange(checked); - }} - /> - {t('rememberMe')} -
    -
    - -
    + {title === 'login' && ( + ( + + +
    + { + field.onChange(checked); + }} + /> + {t('rememberMe')} +
    +
    + +
    + )} + /> )} - /> + + {title === 'login' ? t('login') : t('continue')} + + {title === 'login' && channels && channels.length > 0 && ( +
    + {channels.map((item) => ( + + ))} +
    + )} + + + + {title === 'login' && registerEnabled && ( +
    +

    + {t('signInTip')} + +

    +
    )} - - {title === 'login' ? t('login') : t('continue')} - - {title === 'login' && channels && channels.length > 0 && ( -
    - {channels.map((item) => ( + {title === 'register' && ( +
    +

    + {t('signUpTip')} - ))} +

    )} - - - - {title === 'login' && registerEnabled && ( -
    -

    - {t('signInTip')} - -

    -
    - )} - {title === 'register' && ( -
    -

    - {t('signUpTip')} - -

    - )} -
    +
    +
    ); diff --git a/web/src/pages/login-next/spotlight-top.tsx b/web/src/pages/login-next/spotlight-top.tsx deleted file mode 100644 index 0615f09b5ff..00000000000 --- a/web/src/pages/login-next/spotlight-top.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useIsDarkTheme } from '@/components/theme-provider'; -import React from 'react'; - -interface SpotlightProps { - className?: string; - opcity?: number; - coverage?: number; -} -/** - * - * @param opcity 0~1 default 0.5 - * @param coverage 0~100 default 60 - * @returns - */ -export const SpotlightTopLeft: React.FC = ({ - className, - opcity = 0.5, - coverage = 60, -}) => { - const isDark = useIsDarkTheme(); - const rgb = isDark ? '255, 255, 255' : '194, 221, 243'; - return ( -
    -
    -
    - ); -}; -/** - * - * @param opcity 0~1 default 0.5 - * @param coverage 0~100 default 60 - * @returns - */ -export const SpotlightTopRight: React.FC = ({ - className, - opcity = 0.5, - coverage = 60, -}) => { - const isDark = useIsDarkTheme(); - const rgb = isDark ? '255, 255, 255' : '194, 221, 243'; - return ( -
    -
    -
    - ); -}; diff --git a/web/src/routes.ts b/web/src/routes.ts index a1ee7dc75b9..e356095598e 100644 --- a/web/src/routes.ts +++ b/web/src/routes.ts @@ -1,6 +1,6 @@ export enum Routes { Root = '/', - Login = '/login', + Login = '/login-next', Logout = '/logout', Home = '/home', Datasets = '/datasets', @@ -52,7 +52,7 @@ export enum Routes { const routes = [ { path: '/login', - component: '@/pages/login', + component: '@/pages/login-next', layout: false, }, { diff --git a/web/src/utils/common-util.ts b/web/src/utils/common-util.ts index 879848a8f7e..b88e870fa15 100644 --- a/web/src/utils/common-util.ts +++ b/web/src/utils/common-util.ts @@ -220,7 +220,7 @@ export function parseColorToRGB(color: string): [number, number, number] { // Handling RGB colors (e.g., rgb(255, 87, 51)) if (colorStr.startsWith('rgb')) { - const rgbMatch = colorStr.match(/rgb$$(\d+),\s*(\d+),\s*(\d+)$$/); + const rgbMatch = colorStr.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); if (rgbMatch) { return [ parseInt(rgbMatch[1]), From cf5867b1464b5481cf273ff12f4d086a8950c243 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 13 Oct 2025 15:46:14 +0800 Subject: [PATCH 0821/1187] Feat: Merge title splitter and token splitter into chunker category #9869 (#10517) ### What problem does this PR solve? Feat: Merge title splitter and token splitter into chunker category #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/locales/en.ts | 4 +- .../node/dropdown/next-step-dropdown.tsx | 88 ++++++++++++++----- 2 files changed, 70 insertions(+), 22 deletions(-) diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index fd5c0f016d7..c990b8b8d3e 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1710,12 +1710,12 @@ This delimiter is used to split the input text into several text pieces echo of tokenizerRequired: 'Please add the Indexer node first', tokenizerDescription: 'Transforms text into the required data structure (e.g., vector embeddings for Embedding Search) depending on the chosen search method.', - splitter: 'Token Splitter', + splitter: 'Token', splitterDescription: 'Split text into chunks by token length with optional delimiters and overlap.', hierarchicalMergerDescription: 'Split documents into sections by title hierarchy with regex rules for finer control.', - hierarchicalMerger: 'Title Splitter', + hierarchicalMerger: 'Title', extractor: 'Transformer', extractorDescription: 'Use an LLM to extract structured insights from document chunks—such as summaries, classifications, etc.', diff --git a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx index 2450de57917..2e8c8c30f39 100644 --- a/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/data-flow/canvas/node/dropdown/next-step-dropdown.tsx @@ -1,3 +1,9 @@ +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; import { DropdownMenu, DropdownMenuContent, @@ -19,12 +25,13 @@ import { PropsWithChildren, createContext, memo, + useCallback, useContext, useEffect, useMemo, useRef, } from 'react'; -import { Operator, SingleOperators } from '../../../constant'; +import { Operator } from '../../../constant'; import { AgentInstanceContext, HandleContext } from '../../../context'; import OperatorIcon from '../../../operator-icon'; @@ -116,16 +123,22 @@ function OperatorItemList({ // Limit the number of operators of a certain type on the canvas to only one function useRestrictSingleOperatorOnCanvas() { - const list: Operator[] = []; const { findNodeByName } = useGraphStore((state) => state); - SingleOperators.forEach((operator) => { - if (!findNodeByName(operator)) { - list.push(operator); - } - }); + const restrictSingleOperatorOnCanvas = useCallback( + (singleOperators: Operator[]) => { + const list: Operator[] = []; + singleOperators.forEach((operator) => { + if (!findNodeByName(operator)) { + list.push(operator); + } + }); + return list; + }, + [findNodeByName], + ); - return list; + return restrictSingleOperatorOnCanvas; } function AccordionOperators({ @@ -137,25 +150,60 @@ function AccordionOperators({ mousePosition?: { x: number; y: number }; nodeId?: string; }) { - const singleOperators = useRestrictSingleOperatorOnCanvas(); + const restrictSingleOperatorOnCanvas = useRestrictSingleOperatorOnCanvas(); const { getOperatorTypeFromId } = useGraphStore((state) => state); const operators = useMemo(() => { - let list = [...singleOperators]; - if (getOperatorTypeFromId(nodeId) === Operator.Extractor) { - const Splitters = [Operator.HierarchicalMerger, Operator.Splitter]; - list = list.filter((x) => !Splitters.includes(x)); // The Context Generator node can only be followed by a Tokenizer and a Context Generator. - } + let list = [ + ...restrictSingleOperatorOnCanvas([Operator.Parser, Operator.Tokenizer]), + ]; list.push(Operator.Extractor); return list; - }, [getOperatorTypeFromId, nodeId, singleOperators]); + }, [restrictSingleOperatorOnCanvas]); + + const chunkerOperators = useMemo(() => { + return [ + ...restrictSingleOperatorOnCanvas([ + Operator.Splitter, + Operator.HierarchicalMerger, + ]), + ]; + }, [restrictSingleOperatorOnCanvas]); + + const showChunker = useMemo(() => { + return ( + getOperatorTypeFromId(nodeId) !== Operator.Extractor && + chunkerOperators.length > 0 + ); + }, [chunkerOperators.length, getOperatorTypeFromId, nodeId]); return ( - + <> + + {showChunker && ( + + + Chunker + + + + + + )} + ); } From ff4239c7cf66fe4e60dc36b3ffa35f81074a3dae Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:33:04 +0800 Subject: [PATCH 0822/1187] Docs: Updated descriptions on metadata filtering (#10518) ### What problem does this PR solve? ### Type of change - [x] Documentation Update --- docs/guides/dataset/set_metadata.md | 4 +++ docs/references/http_api_reference.md | 36 +++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/guides/dataset/set_metadata.md b/docs/guides/dataset/set_metadata.md index 904efaa9c3e..a3d239927c9 100644 --- a/docs/guides/dataset/set_metadata.md +++ b/docs/guides/dataset/set_metadata.md @@ -21,6 +21,10 @@ Ensure that your metadata is in JSON format; otherwise, your updates will not be ![Input metadata](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/input_metadata.jpg) +## Related APIs + +[Retrieve chunks](../../references/http_api_reference.md#retrieve-chunks) + ## Frequently asked questions ### Can I set metadata for multiple documents at once? diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index 12d20b2967e..286aaa5a058 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -1823,7 +1823,21 @@ curl --request POST \ { "question": "What is advantage of ragflow?", "dataset_ids": ["b2a62730759d11ef987d0242ac120004"], - "document_ids": ["77df9ef4759a11ef8bdd0242ac120004"] + "document_ids": ["77df9ef4759a11ef8bdd0242ac120004"], + "metadata_condition": { + "conditions": [ + { + "name": "author", + "comparison_operator": "=", + "value": "Toby" + }, + { + "name": "url", + "comparison_operator": "not contains", + "value": "amd" + } + ] + } }' ``` @@ -1858,7 +1872,25 @@ curl --request POST \ - `"cross_languages"`: (*Body parameter*) `list[string]` The languages that should be translated into, in order to achieve keywords retrievals in different languages. - `"metadata_condition"`: (*Body parameter*), `object` - The metadata condition for filtering chunks. + The metadata condition used for filtering chunks: + - `"conditions"`: (*Body parameter*), `array` + A list of metadata filter conditions. + - `"name"`: `string` - The metadata field name to filter by, e.g., `"author"`, `"company"`, `"url"`. Ensure this parameter before use. See [Set metadata](../guides/dataset/set_metadata.md) for details. + - `comparison_operator`: `string` - The comparison operator. Can be one of: + - `"contains"` + - `"not contains"` + - `"start with"` + - `"empty"` + - `"not empty"` + - `"="` + - `"≠"` + - `">"` + - `"<"` + - `"≥"` + - `"≤"` + - `"value"`: `string` - The value to compare. + + #### Response Success: From 8c75803b70e776eb60678e8ee02cc6152f10bed9 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 13 Oct 2025 19:04:25 +0800 Subject: [PATCH 0823/1187] Fix: XSS vulnerability in Ragflow's chat view (#10519) ### What problem does this PR solve? Fix: XSS vulnerability in Ragflow's chat view ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/next-markdown-content/index.tsx | 4 ++-- web/src/pages/chat/markdown-content/index.tsx | 4 ++-- web/src/pages/next-search/markdown-content/index.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/web/src/components/next-markdown-content/index.tsx b/web/src/components/next-markdown-content/index.tsx index 3555f02252a..24f67a995b5 100644 --- a/web/src/components/next-markdown-content/index.tsx +++ b/web/src/components/next-markdown-content/index.tsx @@ -54,8 +54,8 @@ function MarkdownContent({ const { setDocumentIds, data: fileThumbnails } = useFetchDocumentThumbnailsByIds(); const contentWithCursor = useMemo(() => { - // let text = DOMPurify.sanitize(content); - let text = content; + let text = DOMPurify.sanitize(content); + // let text = content; if (text === '') { text = t('chat.searching'); } diff --git a/web/src/pages/chat/markdown-content/index.tsx b/web/src/pages/chat/markdown-content/index.tsx index 2da6a577ffa..72d34fc2764 100644 --- a/web/src/pages/chat/markdown-content/index.tsx +++ b/web/src/pages/chat/markdown-content/index.tsx @@ -48,8 +48,8 @@ const MarkdownContent = ({ const { setDocumentIds, data: fileThumbnails } = useFetchDocumentThumbnailsByIds(); const contentWithCursor = useMemo(() => { - // let text = DOMPurify.sanitize(content); - let text = content; + let text = DOMPurify.sanitize(content); + // let text = content; if (text === '') { text = t('chat.searching'); } diff --git a/web/src/pages/next-search/markdown-content/index.tsx b/web/src/pages/next-search/markdown-content/index.tsx index 4fa62564886..36e82afe5fd 100644 --- a/web/src/pages/next-search/markdown-content/index.tsx +++ b/web/src/pages/next-search/markdown-content/index.tsx @@ -64,8 +64,8 @@ const MarkdownContent = ({ const { setDocumentIds, data: fileThumbnails } = useFetchDocumentThumbnailsByIds(); const contentWithCursor = useMemo(() => { - // let text = DOMPurify.sanitize(content); - let text = content; + let text = DOMPurify.sanitize(content); + // let text = content; if (text === '') { text = t('chat.searching'); } From 74ec734d69491c48a1f8a9d088a40d3c2d1b1d16 Mon Sep 17 00:00:00 2001 From: Lynn Date: Mon, 13 Oct 2025 19:05:54 +0800 Subject: [PATCH 0824/1187] Feat: add admin server to docker (#10522) ### What problem does this PR solve? Add admin server to docker. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- Dockerfile | 1 + admin/admin_client.py | 19 +++++++++++++++++-- docker/.env | 1 + docker/docker-compose.yml | 5 +++++ docker/entrypoint.sh | 13 +++++++++++++ docs/guides/manage_users_and_services.md | 8 +++++++- 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 67fd2645682..42798758a18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -191,6 +191,7 @@ ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" ENV PYTHONPATH=/ragflow/ COPY web web +COPY admin admin COPY api api COPY conf conf COPY deepdoc deepdoc diff --git a/admin/admin_client.py b/admin/admin_client.py index 399d173abce..dc368eb5e1e 100644 --- a/admin/admin_client.py +++ b/admin/admin_client.py @@ -267,10 +267,25 @@ def _print_table_simple(self, data): columns = list(data[0].keys()) col_widths = {} + def get_string_width(text): + half_width_chars = ( + " !\"#$%&'()*+,-./0123456789:;<=>?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" + "abcdefghijklmnopqrstuvwxyz{|}~" + "\t\n\r" + ) + width = 0 + for char in text: + if char in half_width_chars: + width += 1 + else: + width += 2 + return width + for col in columns: - max_width = len(str(col)) + max_width = get_string_width(str(col)) for item in data: - value_len = len(str(item.get(col, ''))) + value_len = get_string_width(str(item.get(col, ''))) if value_len > max_width: max_width = value_len col_widths[col] = max(2, max_width) diff --git a/docker/.env b/docker/.env index 1375f5564eb..63c12a4652b 100644 --- a/docker/.env +++ b/docker/.env @@ -91,6 +91,7 @@ REDIS_PASSWORD=infini_rag_flow # The port used to expose RAGFlow's HTTP API service to the host machine, # allowing EXTERNAL access to the service running inside the Docker container. SVR_HTTP_PORT=9380 +ADMIN_SVR_HTTP_PORT=9381 # The RAGFlow Docker image to download. # Defaults to the v0.20.5-slim edition, which is the RAGFlow Docker image without embedding models. diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3583afdf359..960f8506a96 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -22,9 +22,14 @@ services: # - --no-transport-sse-enabled # Disable legacy SSE endpoints (/sse and /messages/) # - --no-transport-streamable-http-enabled # Disable Streamable HTTP transport (/mcp endpoint) # - --no-json-response # Disable JSON response mode in Streamable HTTP transport (instead of SSE over HTTP) + + # Example configration to start Admin server: + # command: + # - --enable-adminserver container_name: ragflow-server ports: - ${SVR_HTTP_PORT}:9380 + - ${ADMIN_SVR_HTTP_PORT}:9381 - 80:80 - 443:443 - 5678:5678 diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 7570b73dd5c..5965acfefd8 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -11,6 +11,7 @@ function usage() { echo " --disable-webserver Disables the web server (nginx + ragflow_server)." echo " --disable-taskexecutor Disables task executor workers." echo " --enable-mcpserver Enables the MCP server." + echo " --enable-adminserver Enables the Admin server." echo " --consumer-no-beg= Start range for consumers (if using range-based)." echo " --consumer-no-end= End range for consumers (if using range-based)." echo " --workers= Number of task executors to run (if range is not used)." @@ -21,12 +22,14 @@ function usage() { echo " $0 --disable-webserver --consumer-no-beg=0 --consumer-no-end=5" echo " $0 --disable-webserver --workers=2 --host-id=myhost123" echo " $0 --enable-mcpserver" + echo " $0 --enable-adminserver" exit 1 } ENABLE_WEBSERVER=1 # Default to enable web server ENABLE_TASKEXECUTOR=1 # Default to enable task executor ENABLE_MCP_SERVER=0 +ENABLE_ADMIN_SERVER=0 # Default close admin server CONSUMER_NO_BEG=0 CONSUMER_NO_END=0 WORKERS=1 @@ -70,6 +73,10 @@ for arg in "$@"; do ENABLE_MCP_SERVER=1 shift ;; + --enable-adminserver) + ENABLE_ADMIN_SERVER=1 + shift + ;; --mcp-host=*) MCP_HOST="${arg#*=}" shift @@ -185,6 +192,12 @@ if [[ "${ENABLE_WEBSERVER}" -eq 1 ]]; then done & fi +if [[ "${ENABLE_ADMIN_SERVER}" -eq 1 ]]; then + echo "Starting admin_server..." + while true; do + "$PY" admin/admin_server.py + done & +fi if [[ "${ENABLE_MCP_SERVER}" -eq 1 ]]; then start_mcp_server diff --git a/docs/guides/manage_users_and_services.md b/docs/guides/manage_users_and_services.md index c032f3c7d5c..c076904f8be 100644 --- a/docs/guides/manage_users_and_services.md +++ b/docs/guides/manage_users_and_services.md @@ -1,3 +1,9 @@ +--- +sidebar_position: 6 +slug: /manage_users_and_services +--- + + # Admin CLI and Admin Service @@ -50,7 +56,7 @@ Commands are case-insensitive and must be terminated with a semicolon(;). `SHOW SERVICE ;` -- Shows detailed status information for the service identified by . +- Shows detailed status information for the service identified by **id**. - [Example](#example-show-service) ### User Management Commands From f11d8af9364707ccd3e56b41a8b42e5df95c7720 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Tue, 14 Oct 2025 09:30:46 +0800 Subject: [PATCH 0825/1187] Fix: wrong Knowledgebase tasks_finish_at (#10521) ### What problem does this PR solve? Wrong Knowledgebase tasks_finish_at. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/kb_app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/apps/kb_app.py b/api/apps/kb_app.py index 141f1f5d477..a5a5a026358 100644 --- a/api/apps/kb_app.py +++ b/api/apps/kb_app.py @@ -188,6 +188,9 @@ def detail(): return get_data_error_result( message="Can't find this knowledgebase!") kb["size"] = DocumentService.get_total_size_by_kb_id(kb_id=kb["id"],keywords="", run_status=[], types=[]) + for key in ["graphrag_task_finish_at", "raptor_task_finish_at", "mindmap_task_finish_at"]: + if finish_at := kb.get(key): + kb[key] = finish_at.strftime("%Y-%m-%d %H:%M:%S") return get_json_result(data=kb) except Exception as e: return server_error_response(e) From 68e47c81d4362828afa9235ad82c22a3f1e1b47c Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Tue, 14 Oct 2025 09:31:19 +0800 Subject: [PATCH 0826/1187] Feat: Add parse_document with feed back (#10523) ### What problem does this PR solve? Solved: Sync Parse Document API #5635 Feat: Add parse_document with feed back, user can view the status of each document after parsing finished. ### Type of change - [x] New Feature (non-breaking change which adds functionality) - [x] Documentation Update --- docs/references/python_api_reference.md | 52 +++++++++++++++++++++++ sdk/python/ragflow_sdk/modules/dataset.py | 41 +++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/docs/references/python_api_reference.md b/docs/references/python_api_reference.md index abd1c393e9d..ba29fa42533 100644 --- a/docs/references/python_api_reference.md +++ b/docs/references/python_api_reference.md @@ -698,6 +698,58 @@ print("Async bulk parsing initiated.") --- +### Parse documents (with document status) + +```python +DataSet.parse_documents(document_ids: list[str]) -> list[tuple[str, str, int, int]] +``` + +Parses documents **synchronously** in the current dataset. +This method wraps `async_parse_documents()` and automatically waits for all parsing tasks to complete. +It returns detailed parsing results, including the status and statistics for each document. +If interrupted by the user (e.g. `Ctrl+C`), all pending parsing jobs will be cancelled gracefully. + +#### Parameters + +##### document_ids: `list[str]`, *Required* + +The IDs of the documents to parse. + +#### Returns + +A list of tuples with detailed parsing results: +```python +[ + (document_id: str, status: str, chunk_count: int, token_count: int), + ... +] +``` +- **status** — Final parsing state (`success`, `failed`, `cancelled`, etc.) +- **chunk_count** — Number of content chunks created for the document. +- **token_count** — Total number of tokens processed. + +--- + +#### Example + +```python +rag_object = RAGFlow(api_key="", base_url="http://:9380") +dataset = rag_object.create_dataset(name="dataset_name") +documents = dataset.list_documents(keywords="test") +ids = [doc.id for doc in documents] + +try: + finished = dataset.parse_documents(ids) + for doc_id, status, chunk_count, token_count in finished: + print(f"Document {doc_id} parsing finished with status: {status}, chunks: {chunk_count}, tokens: {token_count}") +except KeyboardInterrupt: + print("\nParsing interrupted by user. All pending tasks have been cancelled.") +except Exception as e: + print(f"Parsing failed: {e}") +``` + +--- + ### Stop parsing documents ```python diff --git a/sdk/python/ragflow_sdk/modules/dataset.py b/sdk/python/ragflow_sdk/modules/dataset.py index b4367ac3b6e..d2d689da3b5 100644 --- a/sdk/python/ragflow_sdk/modules/dataset.py +++ b/sdk/python/ragflow_sdk/modules/dataset.py @@ -100,12 +100,51 @@ def delete_documents(self, ids: list[str] | None = None): res = res.json() if res.get("code") != 0: raise Exception(res["message"]) - + + + def _get_documents_status(self, document_ids): + import time + terminal_states = {"DONE", "FAIL", "CANCEL"} + interval_sec = 1 + pending = set(document_ids) + finished = [] + while pending: + for doc_id in list(pending): + def fetch_doc(doc_id: str) -> Document | None: + try: + docs = self.list_documents(id=doc_id) + return docs[0] if docs else None + except Exception: + return None + doc = fetch_doc(doc_id) + if doc is None: + continue + if isinstance(doc.run, str) and doc.run.upper() in terminal_states: + finished.append((doc_id, doc.run, doc.chunk_count, doc.token_count)) + pending.discard(doc_id) + elif float(doc.progress or 0.0) >= 1.0: + finished.append((doc_id, "DONE", doc.chunk_count, doc.token_count)) + pending.discard(doc_id) + if pending: + time.sleep(interval_sec) + return finished + def async_parse_documents(self, document_ids): res = self.post(f"/datasets/{self.id}/chunks", {"document_ids": document_ids}) res = res.json() if res.get("code") != 0: raise Exception(res.get("message")) + + + def parse_documents(self, document_ids): + try: + self.async_parse_documents(document_ids) + self._get_documents_status(document_ids) + except KeyboardInterrupt: + self.async_cancel_parse_documents(document_ids) + + return self._get_documents_status(document_ids) + def async_cancel_parse_documents(self, document_ids): res = self.rm(f"/datasets/{self.id}/chunks", {"document_ids": document_ids}) From 21a62130c8ebde8de3dacc581ce6c49b0d53d7b0 Mon Sep 17 00:00:00 2001 From: buua436 <66937541+buua436@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:32:13 +0800 Subject: [PATCH 0827/1187] Fix: empty references in agent conversation (#10528) ### What problem does this PR solve? issue: #10495 change: fix empty references in agent conversation ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- rag/prompts/generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rag/prompts/generator.py b/rag/prompts/generator.py index eda3e89ec01..d2762ec9166 100644 --- a/rag/prompts/generator.py +++ b/rag/prompts/generator.py @@ -124,7 +124,7 @@ def draw_node(k, line): knowledges = [] for i, ck in enumerate(kbinfos["chunks"][:chunks_num]): - cnt = "\nID: {}".format(i if not hash_id else hash_str2int(get_value(ck, "id", "chunk_id"), 100)) + cnt = "\nID: {}".format(i if not hash_id else hash_str2int(get_value(ck, "id", "chunk_id"), 500)) cnt += draw_node("Title", get_value(ck, "docnm_kwd", "document_name")) cnt += draw_node("URL", ck['url']) if "url" in ck else "" for k, v in docs.get(get_value(ck, "doc_id", "document_id"), {}).items(): From 9e73f799b22590ba15077d15e0e41a151380c3e3 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Tue, 14 Oct 2025 09:32:45 +0800 Subject: [PATCH 0828/1187] Feat: add Zhipu GLM-ASR model (#10529) ### What problem does this PR solve? Add Zhipu GLM-ASR model ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- conf/llm_factories.json | 8 +++++- rag/llm/sequence2txt_model.py | 48 ++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 73f83a5da4f..beb24f065e9 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -803,6 +803,12 @@ "tags": "TEXT EMBEDDING", "max_tokens": 512, "model_type": "embedding" + }, + { + "llm_name": "glm-asr", + "tags": "SPEECH2TEXT", + "max_tokens": 4096, + "model_type": "speech2text" } ] }, @@ -5140,4 +5146,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/rag/llm/sequence2txt_model.py b/rag/llm/sequence2txt_model.py index c43a0141a62..c66adada49a 100644 --- a/rag/llm/sequence2txt_model.py +++ b/rag/llm/sequence2txt_model.py @@ -234,8 +234,8 @@ def __init__(self, key, model_name, base_url="https://api.deepinfra.com/v1/opena self.client = OpenAI(api_key=key, base_url=base_url) self.model_name = model_name - - + + class CometAPISeq2txt(Base): _FACTORY_NAME = "CometAPI" @@ -244,7 +244,8 @@ def __init__(self, key, model_name="whisper-1", base_url="https://api.cometapi.c base_url = "https://api.cometapi.com/v1" self.client = OpenAI(api_key=key, base_url=base_url) self.model_name = model_name - + + class DeerAPISeq2txt(Base): _FACTORY_NAME = "DeerAPI" @@ -253,3 +254,44 @@ def __init__(self, key, model_name="whisper-1", base_url="https://api.deerapi.co base_url = "https://api.deerapi.com/v1" self.client = OpenAI(api_key=key, base_url=base_url) self.model_name = model_name + + +class ZhipuSeq2txt(Base): + _FACTORY_NAME = "ZHIPU-AI" + + def __init__(self, key, model_name="glm-asr", base_url="https://open.bigmodel.cn/api/paas/v4", **kwargs): + if not base_url: + base_url = "https://open.bigmodel.cn/api/paas/v4" + self.base_url = base_url + self.api_key = key + self.model_name = model_name + self.gen_conf = kwargs.get("gen_conf", {}) + self.stream = kwargs.get("stream", False) + + def transcription(self, audio_path): + payload = { + "model": self.model_name, + "temperature": str(self.gen_conf.get("temperature", 0.75)) or "0.75", + "stream": self.stream, + } + + headers = {"Authorization": f"Bearer {self.api_key}"} + with open(audio_path, "rb") as audio_file: + files = {"file": audio_file} + + try: + response = requests.post( + url=f"{self.base_url}/audio/transcriptions", + data=payload, + files=files, + headers=headers, + ) + body = response.json() + if response.status_code == 200: + full_content = body["text"] + return full_content, num_tokens_from_string(full_content) + else: + error = body["error"] + return f"**ERROR**: code: {error['code']}, message: {error['message']}", 0 + except Exception as e: + return "**ERROR**: " + str(e), 0 From aaae938f54145b0ebdd21ad9d6ba68c70dc7e57d Mon Sep 17 00:00:00 2001 From: YngvarHuang <625452882@qq.com> Date: Tue, 14 Oct 2025 09:38:47 +0800 Subject: [PATCH 0829/1187] Add kibana tool in the docker compose file(#10525) (#10526) ### What problem does this PR solve? add kibana tool in the docker compose file(#10525) ### Type of change - [x] New Feature (non-breaking change which adds functionality) Co-authored-by: virgilwong --- docker/.env | 7 +++++-- docker/docker-compose-base.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docker/.env b/docker/.env index 63c12a4652b..d2220d323c6 100644 --- a/docker/.env +++ b/docker/.env @@ -37,9 +37,12 @@ OPENSEARCH_PASSWORD=infini_rag_flow_OS_01 # The port used to expose the Kibana service to the host machine, # allowing EXTERNAL access to the service running inside the Docker container. +# To enable kibana, you need to: +# 1. Ensure that COMPOSE_PROFILES includes kibana, for example: COMPOSE_PROFILES=${DOC_ENGINE},kibana +# 2. Comment out or delete the following configurations of the es service in docker-compose-base.yml: xpack.security.enabled、xpack.security.http.ssl.enabled、xpack.security.transport.ssl.enabled (for details: https://www.elastic.co/docs/deploy-manage/security/self-auto-setup#stack-existing-settings-detected) +# 3. Adjust the es.hosts in conf/service_config.yaml or docker/service_conf.yaml.template to 'https://localhost:1200' +# 4. After the startup is successful, in the es container, execute the command to generate the kibana token: `bin/elasticsearch-create-enrollment-token -s kibana`, then you can use kibana normally KIBANA_PORT=6601 -KIBANA_USER=rag_flow -KIBANA_PASSWORD=infini_rag_flow # The maximum amount of the memory, in bytes, that a specific Docker container can use while running. # Update it according to the available memory in the host machine. diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index 44832fdfc7d..44c62d18ba9 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -207,6 +207,30 @@ services: start_period: 10s + kibana: + container_name: ragflow-kibana + profiles: + - kibana + image: kibana:${STACK_VERSION} + ports: + - ${KIBANA_PORT-5601}:5601 + env_file: .env + environment: + - TZ=${TIMEZONE} + volumes: + - kibana_data:/usr/share/kibana/data + depends_on: + es01: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5601/api/status"] + interval: 10s + timeout: 10s + retries: 120 + networks: + - ragflow + restart: on-failure + volumes: esdata01: @@ -221,6 +245,8 @@ volumes: driver: local redis_data: driver: local + kibana_data: + driver: local networks: ragflow: From 781d49cd0eeb5da18e848b8268cd048062399deb Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 14 Oct 2025 13:30:54 +0800 Subject: [PATCH 0830/1187] Feat: Display the configuration of data flow operators on the node #9869 (#10533) ### What problem does this PR solve? Feat: Display the configuration of data flow operators on the node #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/data-flow/canvas/node/card.tsx | 61 +++---------------- .../data-flow/canvas/node/extractor-node.tsx | 19 +++++- web/src/pages/data-flow/canvas/node/index.tsx | 7 ++- .../data-flow/canvas/node/parser-node.tsx | 19 +++++- .../data-flow/canvas/node/tokenizer-node.tsx | 19 +++++- .../data-flow/form/tokenizer-form/index.tsx | 4 +- 6 files changed, 68 insertions(+), 61 deletions(-) diff --git a/web/src/pages/data-flow/canvas/node/card.tsx b/web/src/pages/data-flow/canvas/node/card.tsx index 042ca45e06b..7b2f39ba7e2 100644 --- a/web/src/pages/data-flow/canvas/node/card.tsx +++ b/web/src/pages/data-flow/canvas/node/card.tsx @@ -1,57 +1,12 @@ -import { Button } from '@/components/ui/button'; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; +import { cn } from '@/lib/utils'; +import { PropsWithChildren } from 'react'; -export function CardWithForm() { +type LabelCardProps = { + className?: string; +} & PropsWithChildren; + +export function LabelCard({ children, className }: LabelCardProps) { return ( - - - Create project - Deploy your new project in one-click. - - -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    - - - - -
    +
    {children}
    ); } diff --git a/web/src/pages/data-flow/canvas/node/extractor-node.tsx b/web/src/pages/data-flow/canvas/node/extractor-node.tsx index b8746a163ec..8b2e034836a 100644 --- a/web/src/pages/data-flow/canvas/node/extractor-node.tsx +++ b/web/src/pages/data-flow/canvas/node/extractor-node.tsx @@ -1 +1,18 @@ -export { RagNode as ExtractorNode } from './index'; +import LLMLabel from '@/components/llm-select/llm-label'; +import { IRagNode } from '@/interfaces/database/agent'; +import { NodeProps } from '@xyflow/react'; +import { get } from 'lodash'; +import { LabelCard } from './card'; +import { RagNode } from './index'; + +export function ExtractorNode({ ...props }: NodeProps) { + const { data } = props; + + return ( + + + + + + ); +} diff --git a/web/src/pages/data-flow/canvas/node/index.tsx b/web/src/pages/data-flow/canvas/node/index.tsx index 7a26d4ecc19..4e004aa8317 100644 --- a/web/src/pages/data-flow/canvas/node/index.tsx +++ b/web/src/pages/data-flow/canvas/node/index.tsx @@ -1,6 +1,6 @@ import { IRagNode } from '@/interfaces/database/flow'; import { NodeProps, Position } from '@xyflow/react'; -import { memo, useMemo } from 'react'; +import { PropsWithChildren, memo, useMemo } from 'react'; import { NodeHandleId, SingleOperators } from '../../constant'; import useGraphStore from '../../store'; import { CommonHandle } from './handle'; @@ -9,12 +9,14 @@ import NodeHeader from './node-header'; import { NodeWrapper } from './node-wrapper'; import { ToolBar } from './toolbar'; +type RagNodeProps = NodeProps & PropsWithChildren; function InnerRagNode({ id, data, isConnectable = true, selected, -}: NodeProps) { + children, +}: RagNodeProps) { const getOperatorTypeFromId = useGraphStore( (state) => state.getOperatorTypeFromId, ); @@ -45,6 +47,7 @@ function InnerRagNode({ isConnectableEnd={false} > + {children} ); diff --git a/web/src/pages/data-flow/canvas/node/parser-node.tsx b/web/src/pages/data-flow/canvas/node/parser-node.tsx index 512632db642..9201b98db0f 100644 --- a/web/src/pages/data-flow/canvas/node/parser-node.tsx +++ b/web/src/pages/data-flow/canvas/node/parser-node.tsx @@ -1,7 +1,10 @@ -import { IRagNode } from '@/interfaces/database/flow'; +import { BaseNode } from '@/interfaces/database/agent'; import { NodeProps, Position } from '@xyflow/react'; import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { NodeHandleId } from '../../constant'; +import { ParserFormSchemaType } from '../../form/parser-form'; +import { LabelCard } from './card'; import { CommonHandle } from './handle'; import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; import NodeHeader from './node-header'; @@ -12,7 +15,8 @@ function ParserNode({ data, isConnectable = true, selected, -}: NodeProps) { +}: NodeProps>) { + const { t } = useTranslation(); return ( +
    + {data.form?.setups.map((x, idx) => ( + + Parser {idx + 1} + {t(`dataflow.fileFormatOptions.${x.fileFormat}`)} + + ))} +
    ); } diff --git a/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx b/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx index 710ddef1f2c..9ae2face269 100644 --- a/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx +++ b/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx @@ -1,7 +1,10 @@ -import { IRagNode } from '@/interfaces/database/flow'; +import { BaseNode } from '@/interfaces/database/agent'; import { NodeProps, Position } from '@xyflow/react'; import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { NodeHandleId } from '../../constant'; +import { TokenizerFormSchemaType } from '../../form/tokenizer-form'; +import { LabelCard } from './card'; import { CommonHandle } from './handle'; import { LeftHandleStyle } from './handle-icon'; import NodeHeader from './node-header'; @@ -13,7 +16,9 @@ function TokenizerNode({ data, isConnectable = true, selected, -}: NodeProps) { +}: NodeProps>) { + const { t } = useTranslation(); + return ( + + + {t('dataflow.searchMethod')} + +
      + {data.form?.search_method.map((x) => ( +
    • {t(`dataflow.tokenizerSearchMethodOptions.${x}`)}
    • + ))} +
    +
    ); diff --git a/web/src/pages/data-flow/form/tokenizer-form/index.tsx b/web/src/pages/data-flow/form/tokenizer-form/index.tsx index cc949d5cdc0..35855450815 100644 --- a/web/src/pages/data-flow/form/tokenizer-form/index.tsx +++ b/web/src/pages/data-flow/form/tokenizer-form/index.tsx @@ -29,6 +29,8 @@ export const FormSchema = z.object({ fields: z.string(), }); +export type TokenizerFormSchemaType = z.infer; + const TokenizerForm = ({ node }: INextOperatorForm) => { const { t } = useTranslation(); const defaultValues = useFormValues(initialTokenizerValues, node); @@ -44,7 +46,7 @@ const TokenizerForm = ({ node }: INextOperatorForm) => { 'dataflow.tokenizerFieldsOptions', ); - const form = useForm>({ + const form = useForm({ defaultValues, resolver: zodResolver(FormSchema), mode: 'onChange', From 66c69d10fe61a27504c65eff97a8b1514f56f1b7 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Tue, 14 Oct 2025 13:31:48 +0800 Subject: [PATCH 0831/1187] Fix: Update the parsing editor to support dynamic field names and optimize UI styles #9869 (#10535) ### What problem does this PR solve? Fix: Update the parsing editor to support dynamic field names and optimize UI styles #9869 -Modify the default background color of UI components such as Input and Select to 'bg bg base'` -Remove TagItems component reference from naive configuration page ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/components/delimiter-form-field.tsx | 2 + web/src/components/ui/input.tsx | 8 +- web/src/components/ui/select.tsx | 2 +- web/src/hooks/logic-hooks/navigate-hooks.ts | 7 + web/src/locales/en.ts | 1 + web/src/locales/zh.ts | 1 + .../components/parse-editer/interface.ts | 4 +- .../components/parse-editer/json-parser.tsx | 132 ++++++++++-------- web/src/pages/dataflow-result/hooks.ts | 48 +++++-- web/src/pages/dataflow-result/index.tsx | 10 +- web/src/pages/dataflow-result/interface.ts | 64 ++++++++- web/src/pages/dataflow-result/parser.tsx | 12 +- .../dataset-setting/configuration/naive.tsx | 3 +- 13 files changed, 207 insertions(+), 87 deletions(-) diff --git a/web/src/components/delimiter-form-field.tsx b/web/src/components/delimiter-form-field.tsx index 271721021e6..77fded5a354 100644 --- a/web/src/components/delimiter-form-field.tsx +++ b/web/src/components/delimiter-form-field.tsx @@ -1,3 +1,4 @@ +import { cn } from '@/lib/utils'; import { forwardRef } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -36,6 +37,7 @@ export const DelimiterInput = forwardRef( maxLength={maxLength} defaultValue={defaultValue} ref={ref} + className={cn('bg-bg-base', props.className)} {...props} > ); diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx index 303dc72e9a4..77f74681499 100644 --- a/web/src/components/ui/input.tsx +++ b/web/src/components/ui/input.tsx @@ -31,7 +31,7 @@ const Input = React.forwardRef( {label} diff --git a/web/src/hooks/logic-hooks/navigate-hooks.ts b/web/src/hooks/logic-hooks/navigate-hooks.ts index 6f852ccfa78..042489bac04 100644 --- a/web/src/hooks/logic-hooks/navigate-hooks.ts +++ b/web/src/hooks/logic-hooks/navigate-hooks.ts @@ -24,6 +24,12 @@ export const useNavigatePage = () => { }, [navigate], ); + const navigateToDatasetOverview = useCallback( + (id: string) => () => { + navigate(`${Routes.DatasetBase}${Routes.DataSetOverview}/${id}`); + }, + [navigate], + ); const navigateToDataFile = useCallback( (id: string) => () => { @@ -160,6 +166,7 @@ export const useNavigatePage = () => { return { navigateToDatasetList, navigateToDataset, + navigateToDatasetOverview, navigateToHome, navigateToProfile, navigateToChatList, diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index c990b8b8d3e..5fcdf297a11 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1672,6 +1672,7 @@ This delimiter is used to split the input text into several text pieces echo of page: '{{page}} /Page', }, dataflowParser: { + result: 'Result', parseSummary: 'Parse Summary', parseSummaryTip: 'Parser:deepdoc', rerunFromCurrentStep: 'Rerun From Current Step', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 119aad5d673..1cce50cc2e1 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1588,6 +1588,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 page: '{{page}}条/页', }, dataflowParser: { + result: '结果', parseSummary: '解析摘要', parseSummaryTip: '解析器: deepdoc', rerunFromCurrentStep: '从当前步骤重新运行', diff --git a/web/src/pages/dataflow-result/components/parse-editer/interface.ts b/web/src/pages/dataflow-result/components/parse-editer/interface.ts index e45d2097204..9da06fd9708 100644 --- a/web/src/pages/dataflow-result/components/parse-editer/interface.ts +++ b/web/src/pages/dataflow-result/components/parse-editer/interface.ts @@ -1,6 +1,6 @@ import { CheckedState } from '@radix-ui/react-checkbox'; import { ChunkTextMode } from '../../constant'; -import { IChunk } from '../../interface'; +import { ComponentParams, IChunk } from '../../interface'; import { parserKeyMap } from './json-parser'; export interface FormatPreserveEditorProps { @@ -28,6 +28,7 @@ export type IJsonContainerProps = { value: { [key: string]: string; }[]; + params: ComponentParams; }; isChunck?: boolean; handleCheck: (e: CheckedState, index: number) => void; @@ -52,6 +53,7 @@ export type IObjContainerProps = { key: string; type: string; value: string; + params: ComponentParams; }; isChunck?: boolean; handleCheck: (e: CheckedState, index: number) => void; diff --git a/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx b/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx index e86999b7875..7af1269b817 100644 --- a/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx +++ b/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx @@ -1,6 +1,7 @@ import { Checkbox } from '@/components/ui/checkbox'; import { cn } from '@/lib/utils'; -import { useCallback, useEffect } from 'react'; +import { isArray } from 'lodash'; +import { useCallback, useEffect, useMemo } from 'react'; import { ChunkTextMode } from '../../constant'; import styles from '../../index.less'; import { useParserInit } from './hook'; @@ -33,7 +34,13 @@ export const ArrayContainer = (props: IJsonContainerProps) => { editDivRef, } = useParserInit({ initialValue }); - const parserKey = parserKeyMap[content.key as keyof typeof parserKeyMap]; + const parserKey = useMemo(() => { + const key = + content.key === 'chunks' && content.params.field_name + ? content.params.field_name + : parserKeyMap[content.key as keyof typeof parserKeyMap]; + return key; + }, [content]); const handleEdit = useCallback( (e?: any, index?: number) => { @@ -73,67 +80,68 @@ export const ArrayContainer = (props: IJsonContainerProps) => { return ( <> - {content.value?.map((item, index) => { - if ( - item[parserKeyMap[content.key as keyof typeof parserKeyMap]] === '' - ) { - return null; - } - return ( -
    - {isChunck && !isReadonly && ( - { - handleCheck(e, index); - }} - checked={selectedChunkIds?.some( - (id) => id.toString() === index.toString(), - )} - > - )} - {activeEditIndex === index && ( -
    { + if ( + item[parserKeyMap[content.key as keyof typeof parserKeyMap]] === '' + ) { + return null; + } + return ( +
    + {isChunck && !isReadonly && ( + { + handleCheck(e, index); + }} + checked={selectedChunkIds?.some( + (id) => id.toString() === index.toString(), + )} + > + )} + {activeEditIndex === index && ( +
    - )} - {activeEditIndex !== index && ( -
    { - clickChunk(item); - if (!isReadonly) { - handleEdit(e, index); - } - }} - > - {item[parserKeyMap[content.key]]} -
    - )} -
    - ); - })} + className, + )} + >
    + )} + {activeEditIndex !== index && ( +
    { + clickChunk(item); + if (!isReadonly) { + handleEdit(e, index); + } + }} + > + {item[parserKey]} +
    + )} +
    + ); + })} ); }; diff --git a/web/src/pages/dataflow-result/hooks.ts b/web/src/pages/dataflow-result/hooks.ts index 8b984d8d858..cd782067ae3 100644 --- a/web/src/pages/dataflow-result/hooks.ts +++ b/web/src/pages/dataflow-result/hooks.ts @@ -218,18 +218,11 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => { const nodes: Array = []; console.log('time-->', data); const times = data?.dsl?.components; + const graphNodes = data?.dsl?.graph?.nodes; if (times) { - const getNode = ( - key: string, - index: number, - type: - | TimelineNodeType.begin - | TimelineNodeType.parser - | TimelineNodeType.tokenizer - | TimelineNodeType.characterSplitter - | TimelineNodeType.titleSplitter, - ) => { + const getNode = (key: string, index: number, type: TimelineNodeType) => { const node = times[key].obj; + const graphNode = graphNodes?.find((item) => item.id === key); const name = camelCase( node.component_name, ) as keyof typeof TimelineNodeObj; @@ -247,6 +240,7 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => { } const timeNode = { ...TimelineNodeObj[name], + title: graphNode?.data?.name, id: index, className: 'w-32', completed: false, @@ -255,6 +249,13 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => { ), type: tempType, detail: { value: times[key], key: key }, + } as ITimelineNodeObj & { + id: number | string; + className: string; + completed: boolean; + date: string; + type: TimelineNodeType; + detail: { value: IDslComponent; key: string }; }; console.log('timeNodetype-->', type); nodes.push(timeNode); @@ -329,3 +330,30 @@ export function useFetchPipelineResult({ return { pipelineResult }; } + +export const useSummaryInfo = ( + data: IPipelineFileLogDetail, + currentTimeNode: TimelineNode, +) => { + const summaryInfo = useMemo(() => { + if (currentTimeNode.type === TimelineNodeType.parser) { + const setups = + currentTimeNode.detail?.value?.obj?.params?.setups?.[ + data.document_suffix + ]; + if (setups) { + const { output_format, parse_method } = setups; + const res = []; + if (parse_method) { + res.push(`${t('dataflow.parserMethod')}: ${parse_method}`); + } + if (output_format) { + res.push(`${t('dataflow.outputFormat')}: ${output_format}`); + } + return res.join(' '); + } + } + return ''; + }, [data, currentTimeNode]); + return { summaryInfo }; +}; diff --git a/web/src/pages/dataflow-result/index.tsx b/web/src/pages/dataflow-result/index.tsx index a2d2fe9130a..00eddff14dd 100644 --- a/web/src/pages/dataflow-result/index.tsx +++ b/web/src/pages/dataflow-result/index.tsx @@ -9,6 +9,7 @@ import { useGetPipelineResultSearchParams, useHandleChunkCardClick, useRerunDataflow, + useSummaryInfo, useTimelineDataFlow, } from './hooks'; @@ -61,7 +62,7 @@ const Chunk = () => { ); const { - navigateToDataset, + navigateToDatasetOverview, navigateToDatasetList, navigateToAgents, navigateToDataflow, @@ -150,7 +151,7 @@ const Chunk = () => { ({} as TimelineNode) ); }, [activeStepId, timelineNodes]); - + const { summaryInfo } = useSummaryInfo(dataset, currentTimeNode); return ( <> @@ -175,7 +176,7 @@ const Chunk = () => { { if (knowledgeId) { - navigateToDataset(knowledgeId)(); + navigateToDatasetOverview(knowledgeId)(); } if (agentId) { navigateToDataflow(agentId)(); @@ -220,7 +221,7 @@ const Chunk = () => { >
    -
    +
    {/* {currentTimeNode?.type === TimelineNodeType.splitter && ( { key: string; } } + summaryInfo={summaryInfo} clickChunk={handleChunkCardClick} reRunFunc={handleReRunFunc} /> diff --git a/web/src/pages/dataflow-result/interface.ts b/web/src/pages/dataflow-result/interface.ts index 865044ce275..df3c758c1f2 100644 --- a/web/src/pages/dataflow-result/interface.ts +++ b/web/src/pages/dataflow-result/interface.ts @@ -1,6 +1,6 @@ import { PipelineResultSearchParams } from './constant'; -interface ComponentParams { +export interface ComponentParams { debug_inputs: Record; delay_after_error: number; description: string; @@ -8,6 +8,7 @@ interface ComponentParams { exception_goto: any; exception_method: any; inputs: Record; + field_name: string; max_retries: number; message_history_window_size: number; outputs: { @@ -30,6 +31,66 @@ export interface IDslComponent { obj: ComponentObject; upstream: Array; } + +interface NodeData { + label: string; + name: string; + form?: { + outputs?: Record< + string, + { + type: string; + value: string | Array> | number; + } + >; + setups?: Array>; + chunk_token_size?: number; + delimiters?: Array<{ + value: string; + }>; + overlapped_percent?: number; + }; +} + +interface EdgeData { + isHovered: boolean; +} + +interface Position { + x: number; + y: number; +} + +interface Measured { + height: number; + width: number; +} + +interface Node { + data: NodeData; + dragging: boolean; + id: string; + measured: Measured; + position: Position; + selected: boolean; + sourcePosition: string; + targetPosition: string; + type: string; +} + +interface Edge { + data: EdgeData; + id: string; + source: string; + sourceHandle: string; + target: string; + targetHandle: string; +} +interface GraphData { + edges: Edge[]; + nodes: Node[]; +} + export interface IPipelineFileLogDetail { avatar: string; create_date: string; @@ -42,6 +103,7 @@ export interface IPipelineFileLogDetail { components: { [key: string]: IDslComponent; }; + graph: GraphData; task_id: string; path: Array; }; diff --git a/web/src/pages/dataflow-result/parser.tsx b/web/src/pages/dataflow-result/parser.tsx index 41f4e2a3978..70d2b0253b0 100644 --- a/web/src/pages/dataflow-result/parser.tsx +++ b/web/src/pages/dataflow-result/parser.tsx @@ -19,6 +19,7 @@ interface IProps { data: { value: IDslComponent; key: string }; reRunLoading: boolean; clickChunk: (chunk: IChunk) => void; + summaryInfo: string; reRunFunc: (data: { value: IDslComponent; key: string }) => void; } const ParserContainer = (props: IProps) => { @@ -31,6 +32,7 @@ const ParserContainer = (props: IProps) => { reRunLoading, clickChunk, isReadonly, + summaryInfo, } = props; const { t } = useTranslation(); const [selectedChunkIds, setSelectedChunkIds] = useState([]); @@ -46,6 +48,7 @@ const ParserContainer = (props: IProps) => { key, type, value, + params: data?.value?.obj?.params, }; }, [data]); @@ -130,7 +133,7 @@ const ParserContainer = (props: IProps) => { const newText = [...initialText.value, { text: text || ' ' }]; setInitialText({ ...initialText, - value: newText, + value: newText as any, }); }, [initialText], @@ -156,15 +159,16 @@ const ParserContainer = (props: IProps) => { {t('dataflowParser.parseSummary')}
    - {t('dataflowParser.parseSummaryTip')} + {/* {t('dataflowParser.parseSummaryTip')} */} + {summaryInfo}
    )} {isChunck && (
    -

    {t('chunk.chunkResult')}

    +

    {t('dataflowParser.result')}

    - {t('chunk.chunkResultTip')} + {/* {t('chunk.chunkResultTip')} */}
    )} diff --git a/web/src/pages/dataset/dataset-setting/configuration/naive.tsx b/web/src/pages/dataset/dataset-setting/configuration/naive.tsx index fd1b522df77..d08e30aa807 100644 --- a/web/src/pages/dataset/dataset-setting/configuration/naive.tsx +++ b/web/src/pages/dataset/dataset-setting/configuration/naive.tsx @@ -6,7 +6,6 @@ import { DelimiterFormField } from '@/components/delimiter-form-field'; import { ExcelToHtmlFormField } from '@/components/excel-to-html-form-field'; import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; import { MaxTokenNumberFormField } from '@/components/max-token-number-from-field'; -import { TagItems } from '../components/tag-item'; import { ConfigurationFormContainer, MainContainer, @@ -26,7 +25,7 @@ export function NaiveConfiguration() { - + {/* */} ); From 113851a692cc47b2514104760e184e93e517f9a8 Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Tue, 14 Oct 2025 13:40:32 +0800 Subject: [PATCH 0832/1187] Add 'status' field when list services (#10538) ### What problem does this PR solve? ``` admin> list services; command: list services; Listing all services +-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+ | extra | host | id | name | port | service_type | status | +-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+ | {} | 0.0.0.0 | 0 | ragflow_0 | 9380 | ragflow_server | Timeout | | {'meta_type': 'mysql', 'password': 'infini_rag_flow', 'username': 'root'} | localhost | 1 | mysql | 5455 | meta_data | Alive | | {'password': 'infini_rag_flow', 'store_type': 'minio', 'user': 'rag_flow'} | localhost | 2 | minio | 9000 | file_store | Alive | | {'password': 'infini_rag_flow', 'retrieval_type': 'elasticsearch', 'username': 'elastic'} | localhost | 3 | elasticsearch | 1200 | retrieval | Alive | | {'db_name': 'default_db', 'retrieval_type': 'infinity'} | localhost | 4 | infinity | 23817 | retrieval | Timeout | | {'database': 1, 'mq_type': 'redis', 'password': 'infini_rag_flow'} | localhost | 5 | redis | 6379 | message_queue | Alive | +-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+ admin> Use '\q' to quit admin> ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai --- admin/services.py | 14 ++++++++++++-- docs/guides/manage_users_and_services.md | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/admin/services.py b/admin/services.py index 3aa738fd5a0..c3b7d300196 100644 --- a/admin/services.py +++ b/admin/services.py @@ -177,8 +177,18 @@ class ServiceMgr: def get_all_services(): result = [] configs = SERVICE_CONFIGS.configs - for config in configs: - result.append(config.to_dict()) + for service_id, config in enumerate(configs): + config_dict = config.to_dict() + service_detail = None + try: + service_detail = ServiceMgr.get_service_details(service_id) + if service_detail['alive']: + config_dict['status'] = 'Alive' + else: + config_dict['status'] = 'Timeout' + except Exception: + config_dict['status'] = 'Timeout' + result.append(config_dict) return result @staticmethod diff --git a/docs/guides/manage_users_and_services.md b/docs/guides/manage_users_and_services.md index c076904f8be..d36ed0ff547 100644 --- a/docs/guides/manage_users_and_services.md +++ b/docs/guides/manage_users_and_services.md @@ -121,16 +121,16 @@ Commands are case-insensitive and must be terminated with a semicolon(;). admin> list services; command: list services; Listing all services -+-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+ -| extra | host | id | name | port | service_type | -+-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+ -| {} | 0.0.0.0 | 0 | ragflow_0 | 9380 | ragflow_server | -| {'meta_type': 'mysql', 'password': 'infini_rag_flow', 'username': 'root'} | localhost | 1 | mysql | 5455 | meta_data | -| {'password': 'infini_rag_flow', 'store_type': 'minio', 'user': 'rag_flow'} | localhost | 2 | minio | 9000 | file_store | -| {'password': 'infini_rag_flow', 'retrieval_type': 'elasticsearch', 'username': 'elastic'} | localhost | 3 | elasticsearch | 1200 | retrieval | -| {'db_name': 'default_db', 'retrieval_type': 'infinity'} | localhost | 4 | infinity | 23817 | retrieval | -| {'database': 1, 'mq_type': 'redis', 'password': 'infini_rag_flow'} | localhost | 5 | redis | 6379 | message_queue | -+-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+ ++-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+ +| extra | host | id | name | port | service_type | status | ++-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+ +| {} | 0.0.0.0 | 0 | ragflow_0 | 9380 | ragflow_server | Timeout | +| {'meta_type': 'mysql', 'password': 'infini_rag_flow', 'username': 'root'} | localhost | 1 | mysql | 5455 | meta_data | Alive | +| {'password': 'infini_rag_flow', 'store_type': 'minio', 'user': 'rag_flow'} | localhost | 2 | minio | 9000 | file_store | Alive | +| {'password': 'infini_rag_flow', 'retrieval_type': 'elasticsearch', 'username': 'elastic'} | localhost | 3 | elasticsearch | 1200 | retrieval | Alive | +| {'db_name': 'default_db', 'retrieval_type': 'infinity'} | localhost | 4 | infinity | 23817 | retrieval | Timeout | +| {'database': 1, 'mq_type': 'redis', 'password': 'infini_rag_flow'} | localhost | 5 | redis | 6379 | message_queue | Alive | ++-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+ ``` From 6fd95080170a81ff126fe67e3b7ca457d7658d14 Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:40:56 +0800 Subject: [PATCH 0833/1187] Docs: Updated parse_documents (#10536) ### What problem does this PR solve? ### Type of change - [x] Documentation Update --- docs/references/python_api_reference.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/references/python_api_reference.md b/docs/references/python_api_reference.md index ba29fa42533..6b8e15485b3 100644 --- a/docs/references/python_api_reference.md +++ b/docs/references/python_api_reference.md @@ -704,10 +704,9 @@ print("Async bulk parsing initiated.") DataSet.parse_documents(document_ids: list[str]) -> list[tuple[str, str, int, int]] ``` -Parses documents **synchronously** in the current dataset. -This method wraps `async_parse_documents()` and automatically waits for all parsing tasks to complete. -It returns detailed parsing results, including the status and statistics for each document. -If interrupted by the user (e.g. `Ctrl+C`), all pending parsing jobs will be cancelled gracefully. +*Asynchronously* parses documents in the current dataset. + +This method encapsulates `async_parse_documents()`. It awaits the completion of all parsing tasks before returning detailed results, including the parsing status and statistics for each document. If a keyboard interruption occurs (e.g., `Ctrl+C`), all pending parsing tasks will be cancelled gracefully. #### Parameters @@ -717,16 +716,17 @@ The IDs of the documents to parse. #### Returns -A list of tuples with detailed parsing results: +A list of tuples with detailed parsing results: + ```python [ (document_id: str, status: str, chunk_count: int, token_count: int), ... ] ``` -- **status** — Final parsing state (`success`, `failed`, `cancelled`, etc.) -- **chunk_count** — Number of content chunks created for the document. -- **token_count** — Total number of tokens processed. +- `status`: The final parsing state (e.g., `success`, `failed`, `cancelled`). +- `chunk_count`: The number of content chunks created from the document. +- `token_count`: The total number of tokens processed. --- From 87659dcd3aff6dbd68a3dc007d73f8cf63dba88a Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Tue, 14 Oct 2025 14:13:10 +0800 Subject: [PATCH 0834/1187] Fix: unexpected Auth return code (#10539) ### What problem does this PR solve? Fix unexpected Auth return code. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/utils/api_utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/api/utils/api_utils.py b/api/utils/api_utils.py index 1579e852bf7..ad78067d287 100644 --- a/api/utils/api_utils.py +++ b/api/utils/api_utils.py @@ -151,10 +151,12 @@ def get_data_error_result(code=settings.RetCode.DATA_ERROR, message="Sorry! Data def server_error_response(e): logging.exception(e) try: - if e.code == 401: - return get_json_result(code=401, message=repr(e)) - except BaseException: - pass + msg = repr(e).lower() + if getattr(e, "code", None) == 401 or ("unauthorized" in msg) or ("401" in msg): + return get_json_result(code=settings.RetCode.UNAUTHORIZED, message=repr(e)) + except Exception as ex: + logging.warning(f"error checking authorization: {ex}") + if len(e.args) > 1: try: serialized_data = serialize_for_json(e.args[1]) From c4b8e4845c825d2d118ca2555ed8cf06494a80d3 Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:13:37 +0800 Subject: [PATCH 0835/1187] Docs: The full edition has only two built-in embedding models (#10540) ### What problem does this PR solve? _Briefly describe what this PR aims to solve. Include background context that will help reviewers understand the purpose of the PR._ ### Type of change - [x] Documentation Update --- docs/faq.mdx | 16 +++------------- docs/release_notes.md | 2 +- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/docs/faq.mdx b/docs/faq.mdx index a9bdf0cb17e..c4facad331b 100644 --- a/docs/faq.mdx +++ b/docs/faq.mdx @@ -40,19 +40,9 @@ Each RAGFlow release is available in two editions: RAGFlow offers two Docker image editions, `v0.20.5-slim` and `v0.20.5`: - `infiniflow/ragflow:v0.20.5-slim` (default): The RAGFlow Docker image without embedding models. -- `infiniflow/ragflow:v0.20.5`: The RAGFlow Docker image with embedding models including: - - Built-in embedding models: - - `BAAI/bge-large-zh-v1.5` - - `maidalun1020/bce-embedding-base_v1` - - Embedding models that will be downloaded once you select them in the RAGFlow UI: - - `BAAI/bge-base-en-v1.5` - - `BAAI/bge-large-en-v1.5` - - `BAAI/bge-small-en-v1.5` - - `BAAI/bge-small-zh-v1.5` - - `jinaai/jina-embeddings-v2-base-en` - - `jinaai/jina-embeddings-v2-small-en` - - `nomic-ai/nomic-embed-text-v1.5` - - `sentence-transformers/all-MiniLM-L6-v2` +- `infiniflow/ragflow:v0.20.5`: The RAGFlow Docker image with the following built-in embedding models: + - `BAAI/bge-large-zh-v1.5` + - `maidalun1020/bce-embedding-base_v1` --- diff --git a/docs/release_notes.md b/docs/release_notes.md index b19f9f720f5..4e9468bd5b8 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -580,7 +580,7 @@ Released on September 30, 2024. ### Compatibility changes -From this release onwards, RAGFlow offers slim editions of its Docker images to improve the experience for users with limited Internet access. A slim edition of RAGFlow's Docker image does not include built-in BGE/BCE embedding models and has a size of about 1GB; a full edition of RAGFlow is approximately 9GB and includes both built-in embedding models and embedding models that will be downloaded once you select them in the RAGFlow UI. +From this release onwards, RAGFlow offers slim editions of its Docker images to improve the experience for users with limited Internet access. A slim edition of RAGFlow's Docker image does not include built-in BGE/BCE embedding models and has a size of about 1GB; a full edition of RAGFlow is approximately 9GB and includes two built-in embedding models. The default Docker image edition is `nightly-slim`. The following list clarifies the differences between various editions: From f92a45dcc47718397fa862422a63fe52be52e630 Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Tue, 14 Oct 2025 14:14:52 +0800 Subject: [PATCH 0836/1187] Feat: let toc run asynchronizly... (#10513) ### What problem does this PR solve? #10436 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/db/services/knowledgebase_service.py | 3 +- .../hierarchical_merger.py | 4 +- rag/flow/parser/parser.py | 2 +- rag/flow/splitter/splitter.py | 4 +- rag/prompts/generator.py | 10 +-- rag/svr/task_executor.py | 88 +++++++++++-------- rag/utils/minio_conn.py | 10 +-- 7 files changed, 67 insertions(+), 54 deletions(-) diff --git a/api/db/services/knowledgebase_service.py b/api/db/services/knowledgebase_service.py index 42d8389fa8b..f8c10357fe0 100644 --- a/api/db/services/knowledgebase_service.py +++ b/api/db/services/knowledgebase_service.py @@ -397,9 +397,10 @@ def get_list(cls, joined_tenant_ids, user_id, else: kbs = kbs.order_by(cls.model.getter_by(orderby).asc()) + total = kbs.count() kbs = kbs.paginate(page_number, items_per_page) - return list(kbs.dicts()), kbs.count() + return list(kbs.dicts()), total @classmethod @DB.connection_context() diff --git a/rag/flow/hierarchical_merger/hierarchical_merger.py b/rag/flow/hierarchical_merger/hierarchical_merger.py index dda2bcfa776..e7b8b9defb9 100644 --- a/rag/flow/hierarchical_merger/hierarchical_merger.py +++ b/rag/flow/hierarchical_merger/hierarchical_merger.py @@ -166,7 +166,7 @@ def dfs(n, path, depth): img = None for i in path: txt += lines[i] + "\n" - concat_img(img, id2image(section_images[i], partial(STORAGE_IMPL.get))) + concat_img(img, id2image(section_images[i], partial(STORAGE_IMPL.get, tenant_id=self._canvas._tenant_id))) cks.append(txt) images.append(img) @@ -180,7 +180,7 @@ def dfs(n, path, depth): ] async with trio.open_nursery() as nursery: for d in cks: - nursery.start_soon(image2id, d, partial(STORAGE_IMPL.put), get_uuid()) + nursery.start_soon(image2id, d, partial(STORAGE_IMPL.put, tenant_id=self._canvas._tenant_id), get_uuid()) self.set_output("chunks", cks) self.callback(1, "Done.") diff --git a/rag/flow/parser/parser.py b/rag/flow/parser/parser.py index b1a34c59bf8..0784ce6fa7f 100644 --- a/rag/flow/parser/parser.py +++ b/rag/flow/parser/parser.py @@ -512,4 +512,4 @@ async def _invoke(self, **kwargs): outs = self.output() async with trio.open_nursery() as nursery: for d in outs.get("json", []): - nursery.start_soon(image2id, d, partial(STORAGE_IMPL.put), get_uuid()) + nursery.start_soon(image2id, d, partial(STORAGE_IMPL.put, tenant_id=self._canvas._tenant_id), get_uuid()) diff --git a/rag/flow/splitter/splitter.py b/rag/flow/splitter/splitter.py index 9c6eb7bfd3e..24f62b6f167 100644 --- a/rag/flow/splitter/splitter.py +++ b/rag/flow/splitter/splitter.py @@ -87,7 +87,7 @@ async def _invoke(self, **kwargs): sections, section_images = [], [] for o in from_upstream.json_result or []: sections.append((o.get("text", ""), o.get("position_tag", ""))) - section_images.append(id2image(o.get("img_id"), partial(STORAGE_IMPL.get))) + section_images.append(id2image(o.get("img_id"), partial(STORAGE_IMPL.get, tenant_id=self._canvas._tenant_id))) chunks, images = naive_merge_with_images( sections, @@ -106,6 +106,6 @@ async def _invoke(self, **kwargs): ] async with trio.open_nursery() as nursery: for d in cks: - nursery.start_soon(image2id, d, partial(STORAGE_IMPL.put), get_uuid()) + nursery.start_soon(image2id, d, partial(STORAGE_IMPL.put, tenant_id=self._canvas._tenant_id), get_uuid()) self.set_output("chunks", cks) self.callback(1, "Done.") diff --git a/rag/prompts/generator.py b/rag/prompts/generator.py index d2762ec9166..1b593bf6b3a 100644 --- a/rag/prompts/generator.py +++ b/rag/prompts/generator.py @@ -680,8 +680,7 @@ async def gen_toc_from_text(txt_info: dict, chat_mdl, callback=None): chat_mdl, gen_conf={"temperature": 0.0, "top_p": 0.9} ) - print(ans, "::::::::::::::::::::::::::::::::::::", flush=True) - txt_info["toc"] = ans if ans else [] + txt_info["toc"] = ans if ans and not isinstance(ans, str) else [] if callback: callback(msg="") except Exception as e: @@ -728,8 +727,6 @@ async def run_toc_from_text(chunks, chat_mdl, callback=None): for chunk in chunks_res: titles.extend(chunk.get("toc", [])) - - print(titles, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") # Filter out entries with title == -1 prune = len(titles) > 512 @@ -745,12 +742,16 @@ async def run_toc_from_text(chunks, chat_mdl, callback=None): filtered.append(x) logging.info(f"\n\nFiltered TOC sections:\n{filtered}") + if not filtered: + return [] # Generate initial level (level/title) raw_structure = [x.get("title", "") for x in filtered] # Assign hierarchy levels using LLM toc_with_levels = assign_toc_levels(raw_structure, chat_mdl, {"temperature": 0.0, "top_p": 0.9}) + if not toc_with_levels: + return [] # Merge structure and content (by index) prune = len(toc_with_levels) > 512 @@ -779,7 +780,6 @@ def relevant_chunks_with_toc(query: str, toc:list[dict], chat_mdl, topn: int=6): chat_mdl, gen_conf={"temperature": 0.0, "top_p": 0.9} ) - print(ans, "::::::::::::::::::::::::::::::::::::", flush=True) id2score = {} for ti, sc in zip(toc, ans): if not isinstance(sc, dict) or sc.get("score", -1) < 1: diff --git a/rag/svr/task_executor.py b/rag/svr/task_executor.py index 9801b53dd23..14a0b5cda7f 100644 --- a/rag/svr/task_executor.py +++ b/rag/svr/task_executor.py @@ -12,7 +12,7 @@ # 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 concurrent # from beartype import BeartypeConf # from beartype.claw import beartype_all # <-- you didn't sign up for this # beartype_all(conf=BeartypeConf(violation_type=UserWarning)) # <-- emit warnings from all code @@ -317,7 +317,7 @@ async def upload_to_minio(document, chunk): d["img_id"] = "" docs.append(d) return - await image2id(d, partial(STORAGE_IMPL.put), d["id"], task["kb_id"]) + await image2id(d, partial(STORAGE_IMPL.put, tenant_id=task["tenant_id"]), d["id"], task["kb_id"]) docs.append(d) except Exception: logging.exception( @@ -370,38 +370,6 @@ async def doc_question_proposal(chat_mdl, d, topn): nursery.start_soon(doc_question_proposal, chat_mdl, d, task["parser_config"]["auto_questions"]) progress_callback(msg="Question generation {} chunks completed in {:.2f}s".format(len(docs), timer() - st)) - if task["parser_id"].lower() == "naive" and task["parser_config"].get("toc_extraction", False): - progress_callback(msg="Start to generate table of content ...") - chat_mdl = LLMBundle(task["tenant_id"], LLMType.CHAT, llm_name=task["llm_id"], lang=task["language"]) - docs = sorted(docs, key=lambda d:( - d.get("page_num_int", 0)[0] if isinstance(d.get("page_num_int", 0), list) else d.get("page_num_int", 0), - d.get("top_int", 0)[0] if isinstance(d.get("top_int", 0), list) else d.get("top_int", 0) - )) - toc: list[dict] = await run_toc_from_text([d["content_with_weight"] for d in docs], chat_mdl, progress_callback) - logging.info("------------ T O C -------------\n"+json.dumps(toc, ensure_ascii=False, indent=' ')) - ii = 0 - while ii < len(toc): - try: - idx = int(toc[ii]["chunk_id"]) - del toc[ii]["chunk_id"] - toc[ii]["ids"] = [docs[idx]["id"]] - if ii == len(toc) -1: - break - for jj in range(idx+1, int(toc[ii+1]["chunk_id"])+1): - toc[ii]["ids"].append(docs[jj]["id"]) - except Exception as e: - logging.exception(e) - ii += 1 - - if toc: - d = copy.deepcopy(docs[-1]) - d["content_with_weight"] = json.dumps(toc, ensure_ascii=False) - d["toc_kwd"] = "toc" - d["available_int"] = 0 - d["page_num_int"] = 100000000 - d["id"] = xxhash.xxh64((d["content_with_weight"] + str(d["doc_id"])).encode("utf-8", "surrogatepass")).hexdigest() - docs.append(d) - if task["kb_parser_config"].get("tag_kb_ids", []): progress_callback(msg="Start to tag for every chunk ...") kb_ids = task["kb_parser_config"]["tag_kb_ids"] @@ -451,6 +419,39 @@ async def doc_content_tagging(chat_mdl, d, topn_tags): return docs +def build_TOC(task, docs, progress_callback): + progress_callback(msg="Start to generate table of content ...") + chat_mdl = LLMBundle(task["tenant_id"], LLMType.CHAT, llm_name=task["llm_id"], lang=task["language"]) + docs = sorted(docs, key=lambda d:( + d.get("page_num_int", 0)[0] if isinstance(d.get("page_num_int", 0), list) else d.get("page_num_int", 0), + d.get("top_int", 0)[0] if isinstance(d.get("top_int", 0), list) else d.get("top_int", 0) + )) + toc: list[dict] = trio.run(run_toc_from_text, [d["content_with_weight"] for d in docs], chat_mdl, progress_callback) + logging.info("------------ T O C -------------\n"+json.dumps(toc, ensure_ascii=False, indent=' ')) + ii = 0 + while ii < len(toc): + try: + idx = int(toc[ii]["chunk_id"]) + del toc[ii]["chunk_id"] + toc[ii]["ids"] = [docs[idx]["id"]] + if ii == len(toc) -1: + break + for jj in range(idx+1, int(toc[ii+1]["chunk_id"])+1): + toc[ii]["ids"].append(docs[jj]["id"]) + except Exception as e: + logging.exception(e) + ii += 1 + + if toc: + d = copy.deepcopy(docs[-1]) + d["content_with_weight"] = json.dumps(toc, ensure_ascii=False) + d["toc_kwd"] = "toc" + d["available_int"] = 0 + d["page_num_int"] = 100000000 + d["id"] = xxhash.xxh64((d["content_with_weight"] + str(d["doc_id"])).encode("utf-8", "surrogatepass")).hexdigest() + return d + + def init_kb(row, vector_size: int): idxnm = search.index_name(row["tenant_id"]) return settings.docStoreConn.createIdx(idxnm, row.get("kb_id", ""), vector_size) @@ -753,7 +754,7 @@ async def insert_es(task_id, task_tenant_id, task_dataset_id, chunks, progress_c return True -@timeout(60*60*2, 1) +@timeout(60*60*3, 1) async def do_handle_task(task): task_type = task.get("task_type", "") @@ -773,6 +774,8 @@ async def do_handle_task(task): task_document_name = task["name"] task_parser_config = task["parser_config"] task_start_ts = timer() + toc_thread = None + executor = concurrent.futures.ThreadPoolExecutor() # prepare the progress callback function progress_callback = partial(set_progress, task_id, task_from_page, task_to_page) @@ -905,8 +908,6 @@ async def do_handle_task(task): if not chunks: progress_callback(1., msg=f"No chunk built from {task_document_name}") return - # TODO: exception handler - ## set_progress(task["did"], -1, "ERROR: ") progress_callback(msg="Generate {} chunks".format(len(chunks))) start_ts = timer() try: @@ -920,6 +921,8 @@ async def do_handle_task(task): progress_message = "Embedding chunks ({:.2f}s)".format(timer() - start_ts) logging.info(progress_message) progress_callback(msg=progress_message) + if task["parser_id"].lower() == "naive" and task["parser_config"].get("toc_extraction", False): + toc_thread = executor.submit(build_TOC,task, chunks, progress_callback) chunk_count = len(set([chunk["id"] for chunk in chunks])) start_ts = timer() @@ -934,8 +937,17 @@ async def do_handle_task(task): DocumentService.increment_chunk_num(task_doc_id, task_dataset_id, token_count, chunk_count, 0) time_cost = timer() - start_ts + progress_callback(msg="Indexing done ({:.2f}s).".format(time_cost)) + if toc_thread: + d = toc_thread.result() + if d: + e = await insert_es(task_id, task_tenant_id, task_dataset_id, [d], progress_callback) + if not e: + return + DocumentService.increment_chunk_num(task_doc_id, task_dataset_id, 0, 1, 0) + task_time_cost = timer() - task_start_ts - progress_callback(prog=1.0, msg="Indexing done ({:.2f}s). Task done ({:.2f}s)".format(time_cost, task_time_cost)) + progress_callback(prog=1.0, msg="Task done ({:.2f}s)".format(task_time_cost)) logging.info( "Chunk doc({}), page({}-{}), chunks({}), token({}), elapsed:{:.2f}".format(task_document_name, task_from_page, task_to_page, len(chunks), diff --git a/rag/utils/minio_conn.py b/rag/utils/minio_conn.py index c26e5606d73..9d1aaccf2ed 100644 --- a/rag/utils/minio_conn.py +++ b/rag/utils/minio_conn.py @@ -60,7 +60,7 @@ def health(self): ) return r - def put(self, bucket, fnm, binary): + def put(self, bucket, fnm, binary, tenant_id=None): for _ in range(3): try: if not self.conn.bucket_exists(bucket): @@ -76,13 +76,13 @@ def put(self, bucket, fnm, binary): self.__open__() time.sleep(1) - def rm(self, bucket, fnm): + def rm(self, bucket, fnm, tenant_id=None): try: self.conn.remove_object(bucket, fnm) except Exception: logging.exception(f"Fail to remove {bucket}/{fnm}:") - def get(self, bucket, filename): + def get(self, bucket, filename, tenant_id=None): for _ in range(1): try: r = self.conn.get_object(bucket, filename) @@ -93,7 +93,7 @@ def get(self, bucket, filename): time.sleep(1) return - def obj_exist(self, bucket, filename): + def obj_exist(self, bucket, filename, tenant_id=None): try: if not self.conn.bucket_exists(bucket): return False @@ -121,7 +121,7 @@ def bucket_exists(self, bucket): logging.exception(f"bucket_exist {bucket} got exception") return False - def get_presigned_url(self, bucket, fnm, expires): + def get_presigned_url(self, bucket, fnm, expires, tenant_id=None): for _ in range(10): try: return self.conn.get_presigned_url("GET", bucket, fnm, expires) From 5b387b68ba71d14cc82a46daec870e8749ed325b Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Tue, 14 Oct 2025 14:53:00 +0800 Subject: [PATCH 0837/1187] The 'cmd' module is introduced to make the CLI easy to use. (#10542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …pdate comand ### What problem does this PR solve? To make the CLI easy to use. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai --- admin/admin_client.py | 70 +++++++++++++++++++++++++++++++------------ admin/services.py | 1 - 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/admin/admin_client.py b/admin/admin_client.py index dc368eb5e1e..5e9113725d9 100644 --- a/admin/admin_client.py +++ b/admin/admin_client.py @@ -16,14 +16,14 @@ import argparse import base64 +from cmd import Cmd from Cryptodome.PublicKey import RSA from Cryptodome.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 from typing import Dict, List, Any -from lark import Lark, Transformer, Tree +from lark import Lark, Transformer, Tree, Token import requests from requests.auth import HTTPBasicAuth -from api.common.base64 import encode_to_base64 GRAMMAR = r""" start: command @@ -100,7 +100,6 @@ %ignore WS """ - class AdminTransformer(Transformer): def start(self, items): @@ -183,7 +182,6 @@ def meta_command_name(self, items): def meta_args(self, items): return items - def encrypt(input_string): pub = '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB\n-----END PUBLIC KEY-----' pub_key = RSA.importKey(pub) @@ -191,13 +189,50 @@ def encrypt(input_string): cipher_text = cipher.encrypt(base64.b64encode(input_string.encode('utf-8'))) return base64.b64encode(cipher_text).decode("utf-8") +def encode_to_base64(input_string): + base64_encoded = base64.b64encode(input_string.encode('utf-8')) + return base64_encoded.decode('utf-8') -class AdminCommandParser: +class AdminCLI(Cmd): def __init__(self): + super().__init__() self.parser = Lark(GRAMMAR, start='start', parser='lalr', transformer=AdminTransformer()) self.command_history = [] + self.is_interactive = False + self.admin_account = "admin@ragflow.io" + self.admin_password: str = "admin" + self.host: str = "" + self.port: int = 0 + + intro = r"""Type "\h" for help.""" + prompt = "admin> " + + def onecmd(self, command: str) -> bool: + try: + print(f"command: {command}") + result = self.parse_command(command) + self.execute_command(result) + + if isinstance(result, Tree): + return False + + if result.get('type') == 'meta' and result.get('command') in ['q', 'quit', 'exit']: + return True + + except KeyboardInterrupt: + print("\nUse '\\q' to quit") + except EOFError: + print("\nGoodbye!") + return True + return False + + def emptyline(self) -> bool: + return False + + def default(self, line: str) -> bool: + return self.onecmd(line) - def parse_command(self, command_str: str) -> Dict[str, Any]: + def parse_command(self, command_str: str) -> dict[str, str] | Tree[Token]: if not command_str.strip(): return {'type': 'empty'} @@ -209,16 +244,6 @@ def parse_command(self, command_str: str) -> Dict[str, Any]: except Exception as e: return {'type': 'error', 'message': f'Parse error: {str(e)}'} - -class AdminCLI: - def __init__(self): - self.parser = AdminCommandParser() - self.is_interactive = False - self.admin_account = "admin@ragflow.io" - self.admin_password: str = "admin" - self.host: str = "" - self.port: int = 0 - def verify_admin(self, args): conn_info = self._parse_connection_args(args) @@ -323,7 +348,7 @@ def run_interactive(self): continue print(f"command: {command}") - result = self.parser.parse_command(command) + result = self.parse_command(command) self.execute_command(result) if isinstance(result, Tree): @@ -610,10 +635,17 @@ def main(): /_/ |_/_/ |_\____/_/ /_/\____/|__/|__/ /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ """) if cli.verify_admin(sys.argv): - cli.run_interactive() + cli.cmdloop() else: + print(r""" + ____ ___ ______________ ___ __ _ + / __ \/ | / ____/ ____/ /___ _ __ / | ____/ /___ ___ (_)___ + / /_/ / /| |/ / __/ /_ / / __ \ | /| / / / /| |/ __ / __ `__ \/ / __ \ + / _, _/ ___ / /_/ / __/ / / /_/ / |/ |/ / / ___ / /_/ / / / / / / / / / / + /_/ |_/_/ |_\____/_/ /_/\____/|__/|__/ /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ + """) if cli.verify_admin(sys.argv): - cli.run_interactive() + cli.cmdloop() # cli.run_single_command(sys.argv[1:]) diff --git a/admin/services.py b/admin/services.py index c3b7d300196..dd1a3c12bd6 100644 --- a/admin/services.py +++ b/admin/services.py @@ -179,7 +179,6 @@ def get_all_services(): configs = SERVICE_CONFIGS.configs for service_id, config in enumerate(configs): config_dict = config.to_dict() - service_detail = None try: service_detail = ServiceMgr.get_service_details(service_id) if service_detail['alive']: From d99d1e351860fb8e128a98cfe8544cf307fdf8a2 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 14 Oct 2025 14:55:47 +0800 Subject: [PATCH 0838/1187] Feat: Merge splitter and hierarchicalMerger into one node #9869 (#10543) ### What problem does this PR solve? Feat: Merge splitter and hierarchicalMerger into one node #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/locales/en.ts | 9 ++-- web/src/locales/zh.ts | 5 +- web/src/pages/data-flow/canvas/index.tsx | 2 - .../canvas/node/hierarchical-merger-node.tsx | 1 - .../data-flow/canvas/node/node-header.tsx | 4 +- .../data-flow/canvas/node/parser-node.tsx | 2 +- .../data-flow/canvas/node/splitter-node.tsx | 53 ++++++++++++++++++- .../data-flow/canvas/node/tokenizer-node.tsx | 4 +- web/src/pages/data-flow/constant.tsx | 2 +- web/src/pages/data-flow/form-sheet/next.tsx | 8 ++- .../data-flow/form/parser-form/index.tsx | 5 +- .../data-flow/form/splitter-form/index.tsx | 5 +- web/src/pages/data-flow/utils.ts | 1 + 13 files changed, 81 insertions(+), 20 deletions(-) delete mode 100644 web/src/pages/data-flow/canvas/node/hierarchical-merger-node.tsx diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 5fcdf297a11..3c6c5e406e7 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -1737,7 +1737,7 @@ This delimiter is used to split the input text into several text pieces echo of addParser: 'Add Parser', hierarchy: 'Hierarchy', regularExpressions: 'Regular Expressions', - overlappedPercent: 'Overlapped percent', + overlappedPercent: 'Overlapped percent (%)', searchMethod: 'Search method', searchMethodTip: `Defines how the content can be searched — by full-text, embedding, or both. The Tokenizer will store the content in the corresponding data structures for the selected methods.`, @@ -1749,11 +1749,11 @@ The Tokenizer will store the content in the corresponding data structures for th exportJson: 'Export JSON', viewResult: 'View result', running: 'Running', - summary: 'Augmented Context', + summary: 'Summary', keywords: 'Keywords', questions: 'Questions', metadata: 'Metadata', - fieldName: 'Result Destination', + fieldName: 'Result destination', prompts: { system: { keywords: `Role @@ -1818,6 +1818,9 @@ Important structured information may include: names, dates, locations, events, k imageParseMethodOptions: { ocr: 'OCR', }, + note: 'Note', + noteDescription: 'Note', + notePlaceholder: 'Please enter a note', }, datasetOverview: { downloadTip: 'Files being downloaded from data sources. ', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 1cce50cc2e1..bcfe07f9588 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -1642,7 +1642,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 addParser: '增加解析器', hierarchy: '层次结构', regularExpressions: '正则表达式', - overlappedPercent: '重叠百分比', + overlappedPercent: '重叠百分比(%)', searchMethod: '搜索方法', searchMethodTip: `决定该数据集启用的搜索方式,可选择全文、向量,或两者兼有。 Tokenizer 会根据所选方式将内容存储为对应的数据结构。`, @@ -1710,6 +1710,9 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`, cancel: '取消', filenameEmbeddingWeight: '文件名嵌入权重', switchPromptMessage: '提示词将发生变化,请确认是否放弃已有提示词?', + note: '注释', + noteDescription: '注释', + notePlaceholder: '请输入注释', }, datasetOverview: { downloadTip: '正在从数据源下载文件。', diff --git a/web/src/pages/data-flow/canvas/index.tsx b/web/src/pages/data-flow/canvas/index.tsx index 8672a2074c3..d7eb615f613 100644 --- a/web/src/pages/data-flow/canvas/index.tsx +++ b/web/src/pages/data-flow/canvas/index.tsx @@ -45,7 +45,6 @@ import { RagNode } from './node'; import { BeginNode } from './node/begin-node'; import { NextStepDropdown } from './node/dropdown/next-step-dropdown'; import { ExtractorNode } from './node/extractor-node'; -import { HierarchicalMergerNode } from './node/hierarchical-merger-node'; import NoteNode from './node/note-node'; import ParserNode from './node/parser-node'; import { SplitterNode } from './node/splitter-node'; @@ -58,7 +57,6 @@ export const nodeTypes: NodeTypes = { parserNode: ParserNode, tokenizerNode: TokenizerNode, splitterNode: SplitterNode, - hierarchicalMergerNode: HierarchicalMergerNode, contextNode: ExtractorNode, }; diff --git a/web/src/pages/data-flow/canvas/node/hierarchical-merger-node.tsx b/web/src/pages/data-flow/canvas/node/hierarchical-merger-node.tsx deleted file mode 100644 index ba47dccfb5c..00000000000 --- a/web/src/pages/data-flow/canvas/node/hierarchical-merger-node.tsx +++ /dev/null @@ -1 +0,0 @@ -export { RagNode as HierarchicalMergerNode } from './index'; diff --git a/web/src/pages/data-flow/canvas/node/node-header.tsx b/web/src/pages/data-flow/canvas/node/node-header.tsx index 9647af1ed56..a030e3a11d1 100644 --- a/web/src/pages/data-flow/canvas/node/node-header.tsx +++ b/web/src/pages/data-flow/canvas/node/node-header.tsx @@ -9,6 +9,7 @@ interface IProps { gap?: number; className?: string; wrapperClassName?: string; + icon?: React.ReactNode; } const InnerNodeHeader = ({ @@ -16,11 +17,12 @@ const InnerNodeHeader = ({ name, className, wrapperClassName, + icon, }: IProps) => { return (
    - + {icon || } {name} diff --git a/web/src/pages/data-flow/canvas/node/parser-node.tsx b/web/src/pages/data-flow/canvas/node/parser-node.tsx index 9201b98db0f..c3d67b8802c 100644 --- a/web/src/pages/data-flow/canvas/node/parser-node.tsx +++ b/web/src/pages/data-flow/canvas/node/parser-node.tsx @@ -41,7 +41,7 @@ function ParserNode({ {data.form?.setups.map((x, idx) => ( Parser {idx + 1} {t(`dataflow.fileFormatOptions.${x.fileFormat}`)} diff --git a/web/src/pages/data-flow/canvas/node/splitter-node.tsx b/web/src/pages/data-flow/canvas/node/splitter-node.tsx index c9948d3c6e5..c6a48671996 100644 --- a/web/src/pages/data-flow/canvas/node/splitter-node.tsx +++ b/web/src/pages/data-flow/canvas/node/splitter-node.tsx @@ -1 +1,52 @@ -export { RagNode as SplitterNode } from './index'; +import { IRagNode } from '@/interfaces/database/flow'; +import { NodeProps, Position } from '@xyflow/react'; +import { PropsWithChildren, memo } from 'react'; +import { NodeHandleId, Operator } from '../../constant'; +import OperatorIcon from '../../operator-icon'; +import { LabelCard } from './card'; +import { CommonHandle } from './handle'; +import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; + +type RagNodeProps = NodeProps & PropsWithChildren; +function InnerSplitterNode({ + id, + data, + isConnectable = true, + selected, +}: RagNodeProps) { + return ( + + + + + } + > + {data.name} + + + ); +} + +export const SplitterNode = memo(InnerSplitterNode); diff --git a/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx b/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx index 9ae2face269..20b261bf4b3 100644 --- a/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx +++ b/web/src/pages/data-flow/canvas/node/tokenizer-node.tsx @@ -37,11 +37,11 @@ function TokenizerNode({ nodeId={id} > - + {t('dataflow.searchMethod')} -
      +
        {data.form?.search_method.map((x) => (
      • {t(`dataflow.tokenizerSearchMethodOptions.${x}`)}
      • ))} diff --git a/web/src/pages/data-flow/constant.tsx b/web/src/pages/data-flow/constant.tsx index 8ebcc671c55..c7b386b0b1a 100644 --- a/web/src/pages/data-flow/constant.tsx +++ b/web/src/pages/data-flow/constant.tsx @@ -337,7 +337,7 @@ export const NodeMap = { [Operator.Parser]: 'parserNode', [Operator.Tokenizer]: 'tokenizerNode', [Operator.Splitter]: 'splitterNode', - [Operator.HierarchicalMerger]: 'hierarchicalMergerNode', + [Operator.HierarchicalMerger]: 'splitterNode', [Operator.Extractor]: 'contextNode', }; diff --git a/web/src/pages/data-flow/form-sheet/next.tsx b/web/src/pages/data-flow/form-sheet/next.tsx index 6e9ce057a51..a5c819a31fd 100644 --- a/web/src/pages/data-flow/form-sheet/next.tsx +++ b/web/src/pages/data-flow/form-sheet/next.tsx @@ -58,7 +58,13 @@ const FormSheet = ({
        - +
        {node?.id === BeginId ? ( diff --git a/web/src/pages/data-flow/form/parser-form/index.tsx b/web/src/pages/data-flow/form/parser-form/index.tsx index cfb06e06dc5..f3e82db0fbd 100644 --- a/web/src/pages/data-flow/form/parser-form/index.tsx +++ b/web/src/pages/data-flow/form/parser-form/index.tsx @@ -30,7 +30,6 @@ import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; import { buildOutputList } from '../../utils/build-output-list'; import { Output } from '../components/output'; -import { OutputFormatFormField } from './common-form-fields'; import { EmailFormFields } from './email-form-fields'; import { ImageFormFields } from './image-form-fields'; import { PdfFormFields } from './pdf-form-fields'; @@ -147,10 +146,10 @@ function ParserItem({ )} - + /> */} {index < fieldLength - 1 && }
        ); diff --git a/web/src/pages/data-flow/form/splitter-form/index.tsx b/web/src/pages/data-flow/form/splitter-form/index.tsx index 60676b5f6e7..5f66647c7ba 100644 --- a/web/src/pages/data-flow/form/splitter-form/index.tsx +++ b/web/src/pages/data-flow/form/splitter-form/index.tsx @@ -26,7 +26,7 @@ export const FormSchema = z.object({ value: z.string().optional(), }), ), - overlapped_percent: z.number(), // 0.0 - 0.3 + overlapped_percent: z.number(), // 0.0 - 0.3 , 0% - 30% }); export type SplitterFormSchemaType = z.infer; @@ -58,9 +58,8 @@ const SplitterForm = ({ node }: INextOperatorForm) => { >
        diff --git a/web/src/pages/data-flow/utils.ts b/web/src/pages/data-flow/utils.ts index e766e085117..8913f7f3eeb 100644 --- a/web/src/pages/data-flow/utils.ts +++ b/web/src/pages/data-flow/utils.ts @@ -131,6 +131,7 @@ function transformParserParams(params: ParserFormSchemaType) { function transformSplitterParams(params: SplitterFormSchemaType) { return { ...params, + overlapped_percent: Number(params.overlapped_percent) / 100, delimiters: transformObjectArrayToPureArray(params.delimiters, 'value'), }; } From 5fb3d2f55cf5bd7b564b5eea7fc8744dac37a3cf Mon Sep 17 00:00:00 2001 From: Kevin Hu Date: Tue, 14 Oct 2025 15:49:05 +0800 Subject: [PATCH 0839/1187] Fix: update parser id for change_parser. (#10545) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/apps/document_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/apps/document_app.py b/api/apps/document_app.py index b80d59b099f..a252db9ec2e 100644 --- a/api/apps/document_app.py +++ b/api/apps/document_app.py @@ -568,7 +568,7 @@ def change_parser(): def reset_doc(): nonlocal doc - e = DocumentService.update_by_id(doc.id, {"parser_id": req["parser_id"], "progress": 0, "progress_msg": "", "run": TaskStatus.UNSTART.value}) + e = DocumentService.update_by_id(doc.id, {"pipeline_id": req["pipeline_id"], "parser_id": req["parser_id"], "progress": 0, "progress_msg": "", "run": TaskStatus.UNSTART.value}) if not e: return get_data_error_result(message="Document not found!") if doc.token_num > 0: From 578ea34b3e2633e5d8b75bd63cf77658adb8c4a1 Mon Sep 17 00:00:00 2001 From: Lynn Date: Tue, 14 Oct 2025 16:28:43 +0800 Subject: [PATCH 0840/1187] Feat: build ragflow-cli (#10544) ### What problem does this PR solve? Build admin client. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .gitignore | 1 + admin/README.md | 56 ++++++++++++++++++------ admin/build_cli_release.sh | 47 ++++++++++++++++++++ admin/pyproject.toml | 24 ++++++++++ docs/guides/manage_users_and_services.md | 49 ++++++++++++++------- 5 files changed, 147 insertions(+), 30 deletions(-) create mode 100755 admin/build_cli_release.sh create mode 100644 admin/pyproject.toml diff --git a/.gitignore b/.gitignore index 956cd633ff2..16bd4e7ffd8 100644 --- a/.gitignore +++ b/.gitignore @@ -149,6 +149,7 @@ out # Nuxt.js build / generate output .nuxt dist +admin/release # Gatsby files .cache/ diff --git a/admin/README.md b/admin/README.md index c5e1c0b98a1..1200e81232e 100644 --- a/admin/README.md +++ b/admin/README.md @@ -15,22 +15,48 @@ It consists of a server-side Service and a command-line client (CLI), both imple - **Admin Service**: A backend service that interfaces with the RAGFlow system to execute administrative operations and monitor its status. - **Admin CLI**: A command-line interface that allows users to connect to the Admin Service and issue commands for system management. + + ### Starting the Admin Service -1. Before start Admin Service, please make sure RAGFlow system is already started. +#### Launching from source code + +1. Before start Admin Service, please make sure RAGFlow system is already started. + +2. Launch from source code: + + ```bash + python admin/admin_server.py + ``` + The service will start and listen for incoming connections from the CLI on the configured port. + +#### Using docker image + +1. Before startup, please configure the `docker_compose.yml` file to enable admin server: + + ```bash + command: + - --enable-adminserver + ``` + +2. Start the containers, the service will start and listen for incoming connections from the CLI on the configured port. + -2. Run the service script: - ```bash - python admin/admin_server.py - ``` - The service will start and listen for incoming connections from the CLI on the configured port. ### Using the Admin CLI 1. Ensure the Admin Service is running. -2. Launch the CLI client: +2. Install ragflow-cli. + ```bash + pip install ragflow-cli + ``` +3. Launch the CLI client: ```bash - python admin/admin_client.py -h 0.0.0.0 -p 9381 + ragflow-cli -h 0.0.0.0 -p 9381 + ``` + Enter superuser's password to login. Default password is `admin`. + + ## Supported Commands @@ -42,12 +68,7 @@ Commands are case-insensitive and must be terminated with a semicolon (`;`). - Lists all available services within the RAGFlow system. - `SHOW SERVICE ;` - Shows detailed status information for the service identified by ``. -- `STARTUP SERVICE ;` - - Attempts to start the service identified by ``. -- `SHUTDOWN SERVICE ;` - - Attempts to gracefully shut down the service identified by ``. -- `RESTART SERVICE ;` - - Attempts to restart the service identified by ``. + ### User Management Commands @@ -55,10 +76,17 @@ Commands are case-insensitive and must be terminated with a semicolon (`;`). - Lists all users known to the system. - `SHOW USER '';` - Shows details and permissions for the specified user. The username must be enclosed in single or double quotes. + +- `CREATE USER ;` + - Create user by username and password. The username and password must be enclosed in single or double quotes. + - `DROP USER '';` - Removes the specified user from the system. Use with caution. - `ALTER USER PASSWORD '' '';` - Changes the password for the specified user. +- `ALTER USER ACTIVE ;` + - Changes the user to active or inactive. + ### Data and Agent Commands diff --git a/admin/build_cli_release.sh b/admin/build_cli_release.sh new file mode 100755 index 00000000000..c9fd6d9d909 --- /dev/null +++ b/admin/build_cli_release.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +set -e + +echo "🚀 Start building..." +echo "================================" + +PROJECT_NAME="ragflow-cli" + +RELEASE_DIR="release" +BUILD_DIR="dist" +SOURCE_DIR="src" +PACKAGE_DIR="ragflow_cli" + +echo "🧹 Clean old build folder..." +rm -rf release/ + +echo "📁 Prepare source code..." +mkdir release/$PROJECT_NAME/$SOURCE_DIR -p +cp pyproject.toml release/$PROJECT_NAME/pyproject.toml +cp README.md release/$PROJECT_NAME/README.md + +mkdir release/$PROJECT_NAME/$SOURCE_DIR/$PACKAGE_DIR -p +cp admin_client.py release/$PROJECT_NAME/$SOURCE_DIR/$PACKAGE_DIR/admin_client.py + +if [ -d "release/$PROJECT_NAME/$SOURCE_DIR" ]; then + echo "✅ source dir: release/$PROJECT_NAME/$SOURCE_DIR" +else + echo "❌ source dir not exist: release/$PROJECT_NAME/$SOURCE_DIR" + exit 1 +fi + +echo "🔨 Make build file..." +cd release/$PROJECT_NAME +export PYTHONPATH=$(pwd) +python -m build + +echo "✅ check build result..." +if [ -d "$BUILD_DIR" ]; then + echo "📦 Package generated:" + ls -la $BUILD_DIR/ +else + echo "❌ Build Failed: $BUILD_DIR not exist." + exit 1 +fi + +echo "🎉 Build finished successfully!" \ No newline at end of file diff --git a/admin/pyproject.toml b/admin/pyproject.toml new file mode 100644 index 00000000000..825fedce4e5 --- /dev/null +++ b/admin/pyproject.toml @@ -0,0 +1,24 @@ +[project] +name = "ragflow-cli" +version = "0.21.0.dev2" +description = "Admin Service's client of [RAGFlow](https://github.com/infiniflow/ragflow). The Admin Service provides user management and system monitoring. " +authors = [{ name = "Lynn", email = "lynn_inf@hotmail.com" }] +license = { text = "Apache License, Version 2.0" } +readme = "README.md" +requires-python = ">=3.10,<3.13" +dependencies = [ + "requests>=2.30.0,<3.0.0", + "beartype>=0.18.5,<0.19.0", + "pycryptodomex>=3.10.0", + "lark>=1.1.0", +] + +[dependency-groups] +test = [ + "pytest>=8.3.5", + "requests>=2.32.3", + "requests-toolbelt>=1.0.0", +] + +[project.scripts] +ragflow-cli = "ragflow_cli.admin_client:main" diff --git a/docs/guides/manage_users_and_services.md b/docs/guides/manage_users_and_services.md index d36ed0ff547..faafbf1980d 100644 --- a/docs/guides/manage_users_and_services.md +++ b/docs/guides/manage_users_and_services.md @@ -12,33 +12,50 @@ The Admin CLI and Admin Service form a client-server architectural suite for RAG -## Starting the Admin Service +### Starting the Admin Service + +#### Launching from source code 1. Before start Admin Service, please make sure RAGFlow system is already started. -2. Switch to ragflow/ directory and run the service script: -```bash -source .venv/bin/activate -export PYTHONPATH=$(pwd) -python admin/admin_server.py -``` +2. Launch from source code: + + ```bash + python admin/admin_server.py + ``` + + The service will start and listen for incoming connections from the CLI on the configured port. + +#### Using docker image + +1. Before startup, please configure the `docker_compose.yml` file to enable admin server: -The service will start and listen for incoming connections from the CLI on the configured port. Default port is 9381. + ```bash + command: + - --enable-adminserver + ``` +2. Start the containers, the service will start and listen for incoming connections from the CLI on the configured port. -## Using the Admin CLI + +### Using the Admin CLI 1. Ensure the Admin Service is running. -2. Launch the CLI client: -```bash -source .venv/bin/activate -export PYTHONPATH=$(pwd) -python admin/admin_client.py -h 0.0.0.0 -p 9381 -``` +2. Install ragflow-cli. + + ```bash + pip install ragflow-cli + ``` + +3. Launch the CLI client: + + ```bash + ragflow-cli -h 0.0.0.0 -p 9381 + ``` -Enter superuser's password to login. Default password is `admin`. +​ Enter superuser's password to login. Default password is `admin`. From 1f5167f1cabcd8a4cc3afbb141a81e844aac4f75 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 14 Oct 2025 17:15:26 +0800 Subject: [PATCH 0841/1187] Feat: Adjust the style of note nodes #9869 (#10547) ### What problem does this PR solve? Feat: Adjust the style of note nodes #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../pages/agent/canvas/node/node-header.tsx | 2 +- .../pages/agent/canvas/node/node-wrapper.tsx | 2 +- .../agent/canvas/node/note-node/index.tsx | 27 +++-- .../pages/agent/canvas/node/resize-icon.tsx | 2 +- .../data-flow/canvas/node/note-node/index.tsx | 103 ++---------------- .../canvas/node/note-node/use-watch-change.ts | 2 +- 6 files changed, 31 insertions(+), 107 deletions(-) diff --git a/web/src/pages/agent/canvas/node/node-header.tsx b/web/src/pages/agent/canvas/node/node-header.tsx index 9647af1ed56..cada3deee47 100644 --- a/web/src/pages/agent/canvas/node/node-header.tsx +++ b/web/src/pages/agent/canvas/node/node-header.tsx @@ -18,7 +18,7 @@ const InnerNodeHeader = ({ wrapperClassName, }: IProps) => { return ( -
        +
        diff --git a/web/src/pages/agent/canvas/node/node-wrapper.tsx b/web/src/pages/agent/canvas/node/node-wrapper.tsx index b26380d9b1f..b7d160673da 100644 --- a/web/src/pages/agent/canvas/node/node-wrapper.tsx +++ b/web/src/pages/agent/canvas/node/node-wrapper.tsx @@ -7,7 +7,7 @@ export function NodeWrapper({ children, className, selected }: IProps) { return (
        ) { +type NoteNodeProps = NodeProps & { + useWatchNoteFormChange?: typeof useWatchFormChange; + useWatchNoteNameFormChange?: typeof useWatchNameFormChange; +}; + +function NoteNode({ + data, + id, + selected, + useWatchNoteFormChange, + useWatchNoteNameFormChange, +}: NoteNodeProps) { const { t } = useTranslation(); const form = useForm>({ @@ -41,19 +52,19 @@ function NoteNode({ data, id, selected }: NodeProps) { defaultValues: { name: data.name }, }); - useWatchFormChange(id, form); + (useWatchNoteFormChange || useWatchFormChange)(id, form); - useWatchNameFormChange(id, nameForm); + (useWatchNoteNameFormChange || useWatchNameFormChange)(id, nameForm); return ( -
        +
        @@ -67,7 +78,7 @@ function NoteNode({ data, id, selected }: NodeProps) { placeholder={t('flow.notePlaceholder')} {...field} type="text" - className="bg-transparent border-none focus-visible:outline focus-visible:outline-text-sub-title" + className="bg-transparent border-none focus-visible:outline focus-visible:outline-text-sub-title p-1" /> @@ -78,7 +89,7 @@ function NoteNode({ data, id, selected }: NodeProps) {
        - + ) { - - - - )} - /> - ), - [BeginQueryType.Options]: ( - ( - - {props.label} - - ({ - label: x, - value: x as string, - })) ?? [] - } - {...field} - > - - - - )} - /> - ), - [BeginQueryType.File]: ( - - ( -
        - - {t('assistantAvatar')} - - - - - -
        - )} - /> -
        - ), - [BeginQueryType.Integer]: ( - ( - - {props.label} - - - - - - )} - /> - ), - [BeginQueryType.Boolean]: ( - ( - - {props.label} - - - - - - )} - /> - ), - }; - - return ( - BeginQueryTypeMap[q.type as BeginQueryType] ?? - BeginQueryTypeMap[BeginQueryType.Paragraph] - ); - }, - [form, t], - ); - - const onSubmit = useCallback( - (values: z.infer) => { - const nextValues = Object.entries(values).map(([key, value]) => { - const item = parameters[Number(key)]; - return { ...item, value }; - }); - - ok(nextValues); - }, - [formSchemaValues, ok, parameters], - ); - return ( - <> -
        - {message?.data?.tips &&
        {message.data.tips}
        } - - - {parameters.map((x, idx) => { - return
        {renderWidget(x, idx.toString())}
        ; - })} -
        - - {btnText || t(isNext ? 'common.next' : 'flow.run')} - -
        - - -
        - - ); -}; - -export default DebugContent; diff --git a/web/src/pages/data-flow/debug-content/popover-form.tsx b/web/src/pages/data-flow/debug-content/popover-form.tsx deleted file mode 100644 index 9465d903b46..00000000000 --- a/web/src/pages/data-flow/debug-content/popover-form.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { - Form, - FormControl, - FormField, - FormItem, - FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { Popover, PopoverContent } from '@/components/ui/popover'; -import { useParseDocument } from '@/hooks/document-hooks'; -import { IModalProps } from '@/interfaces/common'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { PropsWithChildren } from 'react'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; - -const reg = - /^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/; - -const FormSchema = z.object({ - url: z.string(), - result: z.any(), -}); - -const values = { - url: '', - result: null, -}; - -export const PopoverForm = ({ - children, - visible, - switchVisible, -}: PropsWithChildren>) => { - const form = useForm({ - defaultValues: values, - resolver: zodResolver(FormSchema), - }); - const { parseDocument, loading } = useParseDocument(); - const { t } = useTranslation(); - - // useResetFormOnCloseModal({ - // form, - // visible, - // }); - - async function onSubmit(values: z.infer) { - const val = values.url; - - if (reg.test(val)) { - const ret = await parseDocument(val); - if (ret?.data?.code === 0) { - form.setValue('result', ret?.data?.data); - } - } - } - - const content = ( -
        - - ( - - - e.preventDefault()} - placeholder={t('flow.pasteFileLink')} - // suffix={ - // - // } - /> - - - - )} - /> - <>} - /> - - - ); - - return ( - - {children} - {content} - - ); -}; diff --git a/web/src/pages/data-flow/debug-content/uploader.tsx b/web/src/pages/data-flow/debug-content/uploader.tsx deleted file mode 100644 index e11a6f41d31..00000000000 --- a/web/src/pages/data-flow/debug-content/uploader.tsx +++ /dev/null @@ -1,116 +0,0 @@ -'use client'; - -import { - FileUpload, - FileUploadDropzone, - FileUploadItem, - FileUploadItemDelete, - FileUploadItemMetadata, - FileUploadItemPreview, - FileUploadItemProgress, - FileUploadList, - FileUploadTrigger, - type FileUploadProps, -} from '@/components/file-upload'; -import { Button } from '@/components/ui/button'; -import { useUploadCanvasFile } from '@/hooks/use-agent-request'; -import { Upload, X } from 'lucide-react'; -import * as React from 'react'; -import { toast } from 'sonner'; - -type FileUploadDirectUploadProps = { - value: Record; - onChange(value: Record): void; -}; - -export function FileUploadDirectUpload({ - onChange, -}: FileUploadDirectUploadProps) { - const [files, setFiles] = React.useState([]); - - const { uploadCanvasFile } = useUploadCanvasFile(); - - const onUpload: NonNullable = React.useCallback( - async (files, { onSuccess, onError }) => { - try { - const uploadPromises = files.map(async (file) => { - const handleError = (error?: any) => { - onError( - file, - error instanceof Error ? error : new Error('Upload failed'), - ); - }; - try { - const ret = await uploadCanvasFile([file]); - if (ret.code === 0) { - onSuccess(file); - onChange(ret.data); - } else { - handleError(); - } - } catch (error) { - handleError(error); - } - }); - - // Wait for all uploads to complete - await Promise.all(uploadPromises); - } catch (error) { - // This handles any error that might occur outside the individual upload processes - console.error('Unexpected error during upload:', error); - } - }, - [onChange, uploadCanvasFile], - ); - - const onFileReject = React.useCallback((file: File, message: string) => { - toast(message, { - description: `"${file.name.length > 20 ? `${file.name.slice(0, 20)}...` : file.name}" has been rejected`, - }); - }, []); - - return ( - - -
        -
        - -
        -

        Drag & drop files here

        -

        - Or click to browse (max 2 files) -

        -
        - - - -
        - - {files.map((file, index) => ( - -
        - - - - - -
        - -
        - ))} -
        -
        - ); -} diff --git a/web/src/pages/data-flow/form-hooks.ts b/web/src/pages/data-flow/form-hooks.ts deleted file mode 100644 index fef7d37b973..00000000000 --- a/web/src/pages/data-flow/form-hooks.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useTranslate } from '@/hooks/common-hooks'; -import { useCallback, useMemo } from 'react'; -import { Operator, RestrictedUpstreamMap } from './constant'; -import useGraphStore from './store'; - -export const useBuildFormSelectOptions = ( - operatorName: Operator, - selfId?: string, // exclude the current node -) => { - const nodes = useGraphStore((state) => state.nodes); - - const buildCategorizeToOptions = useCallback( - (toList: string[]) => { - const excludedNodes: Operator[] = [ - Operator.Note, - ...(RestrictedUpstreamMap[operatorName] ?? []), - ]; - return nodes - .filter( - (x) => - excludedNodes.every((y) => y !== x.data.label) && - x.id !== selfId && - !toList.some((y) => y === x.id), // filter out selected values ​​in other to fields from the current drop-down box options - ) - .map((x) => ({ label: x.data.name, value: x.id })); - }, - [nodes, operatorName, selfId], - ); - - return buildCategorizeToOptions; -}; - -export const useBuildSortOptions = () => { - const { t } = useTranslate('flow'); - - const options = useMemo(() => { - return ['data', 'relevance'].map((x) => ({ - value: x, - label: t(x), - })); - }, [t]); - return options; -}; diff --git a/web/src/pages/data-flow/hooks/use-open-document.ts b/web/src/pages/data-flow/hooks/use-open-document.ts deleted file mode 100644 index 384529c1597..00000000000 --- a/web/src/pages/data-flow/hooks/use-open-document.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useCallback } from 'react'; - -export function useOpenDocument() { - const openDocument = useCallback(() => { - window.open( - 'https://ragflow.io/docs/dev/category/agent-components', - '_blank', - ); - }, []); - - return openDocument; -} diff --git a/web/src/pages/data-flow/hooks/use-show-dialog.ts b/web/src/pages/data-flow/hooks/use-show-dialog.ts deleted file mode 100644 index 6178e3fbc69..00000000000 --- a/web/src/pages/data-flow/hooks/use-show-dialog.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { useFetchTokenListBeforeOtherStep } from '@/components/embed-dialog/use-show-embed-dialog'; -import { SharedFrom } from '@/constants/chat'; -import { useShowDeleteConfirm } from '@/hooks/common-hooks'; -import { - useCreateSystemToken, - useFetchSystemTokenList, - useRemoveSystemToken, -} from '@/hooks/user-setting-hooks'; -import { IStats } from '@/interfaces/database/chat'; -import { useQueryClient } from '@tanstack/react-query'; -import { useCallback } from 'react'; - -export const useOperateApiKey = (idKey: string, dialogId?: string) => { - const { removeToken } = useRemoveSystemToken(); - const { createToken, loading: creatingLoading } = useCreateSystemToken(); - const { data: tokenList, loading: listLoading } = useFetchSystemTokenList(); - - const showDeleteConfirm = useShowDeleteConfirm(); - - const onRemoveToken = (token: string) => { - showDeleteConfirm({ - onOk: () => removeToken(token), - }); - }; - - const onCreateToken = useCallback(() => { - createToken({ [idKey]: dialogId }); - }, [createToken, idKey, dialogId]); - - return { - removeToken: onRemoveToken, - createToken: onCreateToken, - tokenList, - creatingLoading, - listLoading, - }; -}; - -type ChartStatsType = { - [k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>; -}; - -export const useSelectChartStatsList = (): ChartStatsType => { - const queryClient = useQueryClient(); - const data = queryClient.getQueriesData({ queryKey: ['fetchStats'] }); - const stats: IStats = (data.length > 0 ? data[0][1] : {}) as IStats; - - return Object.keys(stats).reduce((pre, cur) => { - const item = stats[cur as keyof IStats]; - if (item.length > 0) { - pre[cur as keyof IStats] = item.map((x) => ({ - xAxis: x[0] as string, - yAxis: x[1] as number, - })); - } - return pre; - }, {} as ChartStatsType); -}; - -const getUrlWithToken = (token: string, from: string = 'chat') => { - const { protocol, host } = window.location; - return `${protocol}//${host}/chat/share?shared_id=${token}&from=${from}`; -}; - -export const usePreviewChat = (idKey: string) => { - const { handleOperate } = useFetchTokenListBeforeOtherStep(); - - const open = useCallback( - (t: string) => { - window.open( - getUrlWithToken( - t, - idKey === 'canvasId' ? SharedFrom.Agent : SharedFrom.Chat, - ), - '_blank', - ); - }, - [idKey], - ); - - const handlePreview = useCallback(async () => { - const token = await handleOperate(); - if (token) { - open(token); - } - }, [handleOperate, open]); - - return { - handlePreview, - }; -}; diff --git a/web/src/pages/data-flow/hooks/use-show-drawer.tsx b/web/src/pages/data-flow/hooks/use-show-drawer.tsx index 82a9bec8538..0fb8596f58f 100644 --- a/web/src/pages/data-flow/hooks/use-show-drawer.tsx +++ b/web/src/pages/data-flow/hooks/use-show-drawer.tsx @@ -4,7 +4,6 @@ import get from 'lodash/get'; import React, { useCallback, useEffect } from 'react'; import { BeginId, Operator } from '../constant'; import useGraphStore from '../store'; -import { useCacheChatLog } from './use-cache-chat-log'; import { useSaveGraph } from './use-save-graph'; export const useShowFormDrawer = () => { @@ -135,26 +134,6 @@ export function useShowDrawer({ }; } -export function useShowLogSheet({ - setCurrentMessageId, -}: Pick, 'setCurrentMessageId'>) { - const { visible, showModal, hideModal } = useSetModalState(); - - const handleShow = useCallback( - (messageId: string) => { - setCurrentMessageId(messageId); - showModal(); - }, - [setCurrentMessageId, showModal], - ); - - return { - logSheetVisible: visible, - hideLogSheet: hideModal, - showLogSheet: handleShow, - }; -} - export function useHideFormSheetOnNodeDeletion({ hideFormDrawer, }: Pick, 'hideFormDrawer'>) { diff --git a/web/src/pages/data-flow/options.ts b/web/src/pages/data-flow/options.ts deleted file mode 100644 index e69de29bb2d..00000000000 From f50b2461cb2b17af13e5876d4341ba75239420bf Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Fri, 17 Oct 2025 15:39:45 +0800 Subject: [PATCH 0892/1187] Fix: Restore the sidebar description of DP slicing method #9869 (#10633) ### What problem does this PR solve? Fix: Restore the sidebar description of DP slicing method #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- .../layout-recognize-form-field.tsx | 10 +- web/src/components/ui/modal/modal.tsx | 5 +- .../dataset-setting/category-panel.tsx | 72 +++++ .../chunk-method-learn-more.tsx | 39 +++ .../pages/dataset/dataset-setting/index.tsx | 9 +- .../dataset-setting/tag-table/index.tsx | 305 ++++++++++++++++++ .../tag-table/rename-dialog/index.tsx | 40 +++ .../tag-table/rename-dialog/rename-form.tsx | 83 +++++ .../dataset/dataset-setting/tag-tabs.tsx | 40 +++ .../dataset-setting/tag-word-cloud.tsx | 62 ++++ .../pages/dataset/dataset-setting/utils.ts | 20 ++ 11 files changed, 677 insertions(+), 8 deletions(-) create mode 100644 web/src/pages/dataset/dataset-setting/category-panel.tsx create mode 100644 web/src/pages/dataset/dataset-setting/chunk-method-learn-more.tsx create mode 100644 web/src/pages/dataset/dataset-setting/tag-table/index.tsx create mode 100644 web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/index.tsx create mode 100644 web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/rename-form.tsx create mode 100644 web/src/pages/dataset/dataset-setting/tag-tabs.tsx create mode 100644 web/src/pages/dataset/dataset-setting/tag-word-cloud.tsx create mode 100644 web/src/pages/dataset/dataset-setting/utils.ts diff --git a/web/src/components/layout-recognize-form-field.tsx b/web/src/components/layout-recognize-form-field.tsx index d0991c0dbef..2a51a1bff4a 100644 --- a/web/src/components/layout-recognize-form-field.tsx +++ b/web/src/components/layout-recognize-form-field.tsx @@ -17,6 +17,7 @@ import { export const enum ParseDocumentType { DeepDOC = 'DeepDOC', PlainText = 'Plain Text', + MinerU = 'MinerU', } export function LayoutRecognizeFormField({ @@ -38,9 +39,12 @@ export function LayoutRecognizeFormField({ const options = useMemo(() => { const list = optionsWithoutLLM ? optionsWithoutLLM - : [ParseDocumentType.DeepDOC, ParseDocumentType.PlainText].map((x) => ({ - label: - x === ParseDocumentType.PlainText ? t(camelCase(x)) : 'DeepDoc', + : [ + ParseDocumentType.DeepDOC, + ParseDocumentType.PlainText, + ParseDocumentType.MinerU, + ].map((x) => ({ + label: x === ParseDocumentType.PlainText ? t(camelCase(x)) : x, value: x, })); diff --git a/web/src/components/ui/modal/modal.tsx b/web/src/components/ui/modal/modal.tsx index 64fb176a104..2e9e004f99c 100644 --- a/web/src/components/ui/modal/modal.tsx +++ b/web/src/components/ui/modal/modal.tsx @@ -116,7 +116,10 @@ const Modal: ModalType = ({ type="button" disabled={confirmLoading || disabled} onClick={() => handleOk()} - className="px-2 py-1 bg-primary text-primary-foreground rounded-md hover:bg-primary/90" + className={cn( + 'px-2 py-1 bg-primary text-primary-foreground rounded-md hover:bg-primary/90', + { 'cursor-not-allowed': disabled }, + )} > {confirmLoading && ( diff --git a/web/src/pages/dataset/dataset-setting/category-panel.tsx b/web/src/pages/dataset/dataset-setting/category-panel.tsx new file mode 100644 index 00000000000..06b2af743a9 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/category-panel.tsx @@ -0,0 +1,72 @@ +import SvgIcon from '@/components/svg-icon'; +import { useTranslate } from '@/hooks/common-hooks'; +import { useSelectParserList } from '@/hooks/user-setting-hooks'; +import { Col, Divider, Empty, Row, Typography } from 'antd'; +import DOMPurify from 'dompurify'; +import camelCase from 'lodash/camelCase'; +import { useMemo } from 'react'; +import { TagTabs } from './tag-tabs'; +import { ImageMap } from './utils'; + +const { Text } = Typography; + +const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => { + const parserList = useSelectParserList(); + const { t } = useTranslate('knowledgeConfiguration'); + + const item = useMemo(() => { + const item = parserList.find((x) => x.value === chunkMethod); + if (item) { + return { + title: item.label, + description: t(camelCase(item.value)), + }; + } + return { title: '', description: '' }; + }, [parserList, chunkMethod, t]); + + const imageList = useMemo(() => { + if (chunkMethod in ImageMap) { + return ImageMap[chunkMethod as keyof typeof ImageMap]; + } + return []; + }, [chunkMethod]); + + return ( +
        + {imageList.length > 0 ? ( + <> +
        + {`"${item.title}" ${t('methodTitle')}`} +
        +

        +
        {`"${item.title}" ${t('methodExamples')}`}
        + {t('methodExamplesDescription')} + + {imageList.map((x) => ( + + + + ))} + +
        + {item.title} {t('dialogueExamplesTitle')} +
        + + + ) : ( + +

        {t('methodEmpty')}

        + +
        + )} + {chunkMethod === 'tag' && } +
        + ); +}; + +export default CategoryPanel; diff --git a/web/src/pages/dataset/dataset-setting/chunk-method-learn-more.tsx b/web/src/pages/dataset/dataset-setting/chunk-method-learn-more.tsx new file mode 100644 index 00000000000..6894825d5fc --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/chunk-method-learn-more.tsx @@ -0,0 +1,39 @@ +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; +import { t } from 'i18next'; +import { X } from 'lucide-react'; +import { useState } from 'react'; +import CategoryPanel from './category-panel'; + +export default ({ parserId }: { parserId: string }) => { + const [visible, setVisible] = useState(false); + + return ( +
        +
        + +
        +
        + +
        { + setVisible(false); + }} + > + +
        +
        +
        + ); +}; diff --git a/web/src/pages/dataset/dataset-setting/index.tsx b/web/src/pages/dataset/dataset-setting/index.tsx index 079b0457140..c677204b8ae 100644 --- a/web/src/pages/dataset/dataset-setting/index.tsx +++ b/web/src/pages/dataset/dataset-setting/index.tsx @@ -21,6 +21,7 @@ import { IGenerateLogButtonProps, } from '../dataset/generate-button/generate'; import { ChunkMethodForm } from './chunk-method-form'; +import ChunkMethodLearnMore from './chunk-method-learn-more'; import { IDataPipelineNodeProps } from './components/link-data-pipeline'; import { MainContainer } from './configuration-form-container'; import { ChunkMethodItem, ParseTypeItem } from './configuration/common-item'; @@ -169,10 +170,7 @@ export default function DatasetSettings() { >
        - +
        @@ -231,6 +229,9 @@ export default function DatasetSettings() {
        +
        + {parseType === 1 && } +
        ); diff --git a/web/src/pages/dataset/dataset-setting/tag-table/index.tsx b/web/src/pages/dataset/dataset-setting/tag-table/index.tsx new file mode 100644 index 00000000000..a2e38018ced --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/tag-table/index.tsx @@ -0,0 +1,305 @@ +'use client'; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react'; +import * as React from 'react'; + +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Input } from '@/components/ui/input'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { useDeleteTag, useFetchTagList } from '@/hooks/knowledge-hooks'; +import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useRenameKnowledgeTag } from '../hooks'; +import { RenameDialog } from './rename-dialog'; + +export type ITag = { + tag: string; + frequency: number; +}; + +export function TagTable() { + const { t } = useTranslation(); + const { list } = useFetchTagList(); + const [tagList, setTagList] = useState([]); + + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [], + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = useState({}); + + const { deleteTag } = useDeleteTag(); + + useEffect(() => { + setTagList(list.map((x) => ({ tag: x[0], frequency: x[1] }))); + }, [list]); + + const handleDeleteTag = useCallback( + (tags: string[]) => () => { + deleteTag(tags); + }, + [deleteTag], + ); + + const { + showTagRenameModal, + hideTagRenameModal, + tagRenameVisible, + initialName, + } = useRenameKnowledgeTag(); + + const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: 'tag', + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const value: string = row.getValue('tag'); + return
        {value}
        ; + }, + }, + { + accessorKey: 'frequency', + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => ( +
        {row.getValue('frequency')}
        + ), + }, + { + id: 'actions', + enableHiding: false, + header: t('common.action'), + cell: ({ row }) => { + return ( +
        + + + + + + + +

        {t('common.delete')}

        +
        +
        + + + + + +

        {t('common.rename')}

        +
        +
        +
        + ); + }, + }, + ]; + + const table = useReactTable({ + data: tagList, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + }); + + const selectedRowLength = table.getFilteredSelectedRowModel().rows.length; + + return ( + +
        +
        + + table.getColumn('tag')?.setFilterValue(event.target.value) + } + className="w-1/2" + /> + {selectedRowLength > 0 && ( + x.original.tag), + )} + > + + + )} +
        + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
        +
        +
        +
        + {selectedRowLength} of {table.getFilteredRowModel().rows.length}{' '} + row(s) selected. +
        +
        + + +
        +
        + {tagRenameVisible && ( + + )} +
        + ); +} diff --git a/web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/index.tsx b/web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/index.tsx new file mode 100644 index 00000000000..b95907f9243 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/index.tsx @@ -0,0 +1,40 @@ +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { LoadingButton } from '@/components/ui/loading-button'; +import { useTagIsRenaming } from '@/hooks/knowledge-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { TagRenameId } from '@/pages/add-knowledge/constant'; +import { useTranslation } from 'react-i18next'; +import { RenameForm } from './rename-form'; + +export function RenameDialog({ + hideModal, + initialName, +}: IModalProps & { initialName: string }) { + const { t } = useTranslation(); + const loading = useTagIsRenaming(); + + return ( + + + + {t('common.rename')} + + + + + {t('common.save')} + + + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/rename-form.tsx b/web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/rename-form.tsx new file mode 100644 index 00000000000..9c8f1cf7e4b --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/tag-table/rename-dialog/rename-form.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; + +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { useRenameTag } from '@/hooks/knowledge-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { TagRenameId } from '@/pages/add-knowledge/constant'; +import { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; + +export function RenameForm({ + initialName, + hideModal, +}: IModalProps & { initialName: string }) { + const { t } = useTranslation(); + const FormSchema = z.object({ + name: z + .string() + .min(1, { + message: t('common.namePlaceholder'), + }) + .trim(), + }); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + name: '', + }, + }); + + const { renameTag } = useRenameTag(); + + async function onSubmit(data: z.infer) { + const ret = await renameTag({ fromTag: initialName, toTag: data.name }); + if (ret) { + hideModal?.(); + } + } + + useEffect(() => { + form.setValue('name', initialName); + }, [form, initialName]); + + return ( +
        + + ( + + {t('common.name')} + + + + + + )} + /> + + + ); +} diff --git a/web/src/pages/dataset/dataset-setting/tag-tabs.tsx b/web/src/pages/dataset/dataset-setting/tag-tabs.tsx new file mode 100644 index 00000000000..abcd3f673c0 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/tag-tabs.tsx @@ -0,0 +1,40 @@ +import { Segmented } from 'antd'; +import { SegmentedLabeledOption } from 'antd/es/segmented'; +import { upperFirst } from 'lodash'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { TagTable } from './tag-table'; +import { TagWordCloud } from './tag-word-cloud'; + +enum TagType { + Cloud = 'cloud', + Table = 'table', +} + +const TagContentMap = { + [TagType.Cloud]: , + [TagType.Table]: , +}; + +export function TagTabs() { + const [value, setValue] = useState(TagType.Cloud); + const { t } = useTranslation(); + + const options: SegmentedLabeledOption[] = [TagType.Cloud, TagType.Table].map( + (x) => ({ + label: t(`knowledgeConfiguration.tag${upperFirst(x)}`), + value: x, + }), + ); + + return ( +
        + setValue(val as TagType)} + /> + {TagContentMap[value]} +
        + ); +} diff --git a/web/src/pages/dataset/dataset-setting/tag-word-cloud.tsx b/web/src/pages/dataset/dataset-setting/tag-word-cloud.tsx new file mode 100644 index 00000000000..b71ed69afb1 --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/tag-word-cloud.tsx @@ -0,0 +1,62 @@ +import { useFetchTagList } from '@/hooks/knowledge-hooks'; +import { Chart } from '@antv/g2'; +import { sumBy } from 'lodash'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; + +export function TagWordCloud() { + const domRef = useRef(null); + let chartRef = useRef(); + const { list } = useFetchTagList(); + + const { list: tagList } = useMemo(() => { + const nextList = list.sort((a, b) => b[1] - a[1]).slice(0, 256); + + return { + list: nextList.map((x) => ({ text: x[0], value: x[1], name: x[0] })), + sumValue: sumBy(nextList, (x: [string, number]) => x[1]), + length: nextList.length, + }; + }, [list]); + + const renderWordCloud = useCallback(() => { + if (domRef.current) { + chartRef.current = new Chart({ container: domRef.current }); + + chartRef.current.options({ + type: 'wordCloud', + autoFit: true, + layout: { + fontSize: [10, 50], + // fontSize: (d: any) => { + // if (d.value) { + // return (d.value / sumValue) * 100 * (length / 10); + // } + // return 0; + // }, + }, + data: { + type: 'inline', + value: tagList, + }, + encode: { color: 'text' }, + legend: false, + tooltip: { + title: 'name', // title + items: ['value'], // data item + }, + }); + + chartRef.current.render(); + } + }, [tagList]); + + useEffect(() => { + renderWordCloud(); + + return () => { + chartRef.current?.destroy(); + }; + }, [renderWordCloud]); + + return
        ; +} diff --git a/web/src/pages/dataset/dataset-setting/utils.ts b/web/src/pages/dataset/dataset-setting/utils.ts new file mode 100644 index 00000000000..4c5666467cf --- /dev/null +++ b/web/src/pages/dataset/dataset-setting/utils.ts @@ -0,0 +1,20 @@ +const getImageName = (prefix: string, length: number) => + new Array(length) + .fill(0) + .map((x, idx) => `chunk-method/${prefix}-0${idx + 1}`); + +export const ImageMap = { + book: getImageName('book', 4), + laws: getImageName('law', 2), + manual: getImageName('manual', 4), + picture: getImageName('media', 2), + naive: getImageName('naive', 2), + paper: getImageName('paper', 2), + presentation: getImageName('presentation', 2), + qa: getImageName('qa', 2), + resume: getImageName('resume', 2), + table: getImageName('table', 2), + one: getImageName('one', 2), + knowledge_graph: getImageName('knowledge-graph', 2), + tag: getImageName('tag', 2), +}; From 8ee0b6ea54e1ec70c36f0c7a0146adb28d18b92b Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Fri, 17 Oct 2025 18:46:47 +0800 Subject: [PATCH 0893/1187] File: Now parsing support all types of embedded documents, solved #10059 (#10635) ### What problem does this PR solve? File: Now parsing support all types of embedded documents, solved #10059 Fix: Incomplete words in chat #10530 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- api/db/services/llm_service.py | 2 +- api/db/services/task_service.py | 4 +- api/utils/file_utils.py | 149 ++++++++++++++++ pyproject.toml | 1 + rag/app/naive.py | 36 +++- uv.lock | 301 ++++++++++++++++++++++++++++++++ 6 files changed, 486 insertions(+), 7 deletions(-) diff --git a/api/db/services/llm_service.py b/api/db/services/llm_service.py index 76c4cb40f6c..120de2114ff 100644 --- a/api/db/services/llm_service.py +++ b/api/db/services/llm_service.py @@ -266,7 +266,7 @@ def chat_streamly(self, system: str, history: list, gen_conf: dict = {}, **kwarg break if txt.endswith(""): - ans = ans.rstrip("") + ans = ans[: -len("")] if not self.verbose_tool_use: txt = re.sub(r".*?", "", txt, flags=re.DOTALL) diff --git a/api/db/services/task_service.py b/api/db/services/task_service.py index 41641733976..b0fc2a119db 100644 --- a/api/db/services/task_service.py +++ b/api/db/services/task_service.py @@ -351,7 +351,7 @@ def new_task(): "progress": 0.0, "from_page": 0, "to_page": 100000000, - "begin_at": datetime.now(), + "begin_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), } parse_task_array = [] @@ -503,7 +503,7 @@ def queue_dataflow(tenant_id:str, flow_id:str, task_id:str, doc_id:str=CANVAS_DE to_page=100000000, task_type="dataflow" if not rerun else "dataflow_rerun", priority=priority, - begin_at=datetime.now(), + begin_at= datetime.now().strftime("%Y-%m-%d %H:%M:%S"), ) if doc_id not in [CANVAS_DEBUG_DOC_ID, GRAPH_RAPTOR_FAKE_DOC_ID]: TaskService.model.delete().where(TaskService.model.doc_id == doc_id).execute() diff --git a/api/utils/file_utils.py b/api/utils/file_utils.py index 63e96fb78cf..71389c01a97 100644 --- a/api/utils/file_utils.py +++ b/api/utils/file_utils.py @@ -13,7 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # + + +# Standard library imports import base64 +import hashlib +import io import json import os import re @@ -22,13 +27,21 @@ import sys import tempfile import threading +import zipfile from io import BytesIO +# Typing +from typing import List, Union, Tuple + +# Third-party imports +import olefile +import fitz import pdfplumber from cachetools import LRUCache, cached from PIL import Image from ruamel.yaml import YAML +# Local imports from api.constants import IMG_BASE64_PREFIX from api.db import FileType @@ -284,3 +297,139 @@ def try_open(blob): return repaired return blob + + +def _is_zip(h: bytes) -> bool: + return h.startswith(b"PK\x03\x04") or h.startswith(b"PK\x05\x06") or h.startswith(b"PK\x07\x08") + +def _is_pdf(h: bytes) -> bool: + return h.startswith(b"%PDF-") + +def _is_ole(h: bytes) -> bool: + return h.startswith(b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1") + +def _sha10(b: bytes) -> str: + return hashlib.sha256(b).hexdigest()[:10] + +def _guess_ext(b: bytes) -> str: + h = b[:8] + if _is_zip(h): + try: + with zipfile.ZipFile(io.BytesIO(b), "r") as z: + names = [n.lower() for n in z.namelist()] + if any(n.startswith("word/") for n in names): + return ".docx" + if any(n.startswith("ppt/") for n in names): + return ".pptx" + if any(n.startswith("xl/") for n in names): + return ".xlsx" + except Exception: + pass + return ".zip" + if _is_pdf(h): + return ".pdf" + if _is_ole(h): + return ".doc" + return ".bin" + +# Try to extract the real embedded payload from OLE's Ole10Native +def _extract_ole10native_payload(data: bytes) -> bytes: + try: + pos = 0 + if len(data) < 4: + return data + _ = int.from_bytes(data[pos:pos+4], "little") + pos += 4 + for _ in range(3): # filename/src/tmp (NUL-terminated ANSI) + z = data.index(b"\x00", pos) + pos = z + 1 + pos += 4 + if pos + 4 > len(data): + return data + size = int.from_bytes(data[pos:pos+4], "little") + pos += 4 + if pos + size <= len(data): + return data[pos:pos+size] + except Exception: + pass + return data + +def extract_embed_file(target: Union[bytes, bytearray]) -> List[Tuple[str, bytes]]: + """ + Only extract the "first layer" of embedding, returning raw (filename, bytes). + These bytes can be directly used for io.BytesIO and then passed to zipfile/fitz/olefile and other libraries for further parsing. + """ + top = bytes(target) + + head = top[:8] + out: List[Tuple[str, bytes]] = [] + seen = set() + + def push(b: bytes, name_hint: str = ""): + h10 = _sha10(b) + if h10 in seen: + return + seen.add(h10) + ext = _guess_ext(b) + # If name_hint does not have a clear extension, use the extension guessed from the content + if "." in name_hint: + fname = name_hint.split("/")[-1] + else: + fname = f"{h10}{ext}" + out.append((fname, b)) + + # OOXML/ZIP container (docx/xlsx/pptx) + if _is_zip(head): + try: + with zipfile.ZipFile(io.BytesIO(top), "r") as z: + embed_dirs = ("word/embeddings/", "word/objects/", "word/activex/", + "xl/embeddings/", "ppt/embeddings/") + for name in z.namelist(): + low = name.lower() + if any(low.startswith(d) for d in embed_dirs): + try: + b = z.read(name) + push(b, name) + except Exception: + pass + except Exception: + pass + return out + + # PDF attachments + if _is_pdf(head): + try: + doc = fitz.open(stream=top, filetype="pdf") + count = getattr(doc, "embfile_count", 0) + for i in range(count): + try: + data = doc.embfile_get(i) + if data: + push(bytes(data), f"EmbeddedFiles[{i}]") + except Exception: + pass + doc.close() + except Exception: + pass + return out + + # Legacy OLE (.doc/.xls/.ppt) + if _is_ole(head): + try: + with olefile.OleFileIO(io.BytesIO(top)) as ole: + for entry in ole.listdir(): + p = "/".join(entry) + try: + data = ole.openstream(entry).read() + except Exception: + continue + if not data: + continue + if "Ole10Native" in p or "ole10native" in p.lower(): + data = _extract_ole10native_payload(data) + push(data, p) + except Exception: + pass + return out + + return out \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 486de7b4072..4237e692d65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -137,6 +137,7 @@ dependencies = [ "mammoth>=1.11.0", "markdownify>=1.2.0", "captcha>=0.7.1", + "fitz>=0.0.1.dev2", ] [project.optional-dependencies] diff --git a/rag/app/naive.py b/rag/app/naive.py index e32b8be186c..d91018eadc4 100644 --- a/rag/app/naive.py +++ b/rag/app/naive.py @@ -15,12 +15,11 @@ # import logging -import os import re +import os from functools import reduce from io import BytesIO from timeit import default_timer as timer - from docx import Document from docx.image.exceptions import InvalidImageStreamError, UnexpectedEndOfFileError, UnrecognizedImageError from docx.opc.pkgreader import _SerializedRelationships, _SerializedRelationship @@ -31,10 +30,11 @@ from api.db import LLMType from api.db.services.llm_service import LLMBundle +from api.utils.file_utils import extract_embed_file from deepdoc.parser import DocxParser, ExcelParser, HtmlParser, JsonParser, MarkdownElementExtractor, MarkdownParser, PdfParser, TxtParser from deepdoc.parser.figure_parser import VisionFigureParser, vision_figure_parser_figure_data_wrapper -from deepdoc.parser.mineru_parser import MinerUParser from deepdoc.parser.pdf_parser import PlainParser, VisionParser +from deepdoc.parser.mineru_parser import MinerUParser from rag.nlp import concat_img, find_codec, naive_merge, naive_merge_with_images, naive_merge_docx, rag_tokenizer, tokenize_chunks, tokenize_chunks_with_images, tokenize_table @@ -437,6 +437,7 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, Successive text will be sliced into pieces using 'delimiter'. Next, these successive pieces are merge into chunks whose token number is no more than 'Max token number'. """ + is_english = lang.lower() == "english" # is_english(cks) parser_config = kwargs.get( @@ -450,6 +451,27 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, res = [] pdf_parser = None section_images = None + + is_root = kwargs.get("is_root", True) + embed_res = [] + if is_root: + # Only extract embedded files at the root call + embeds = [] + if binary is not None: + embeds = extract_embed_file(binary) + else: + raise Exception("Embedding extraction from file path is not supported.") + + # Recursively chunk each embedded file and collect results + for embed_filename, embed_bytes in embeds: + try: + sub_res = chunk(embed_filename, binary=embed_bytes, lang=lang, callback=callback, is_root=False, **kwargs) or [] + embed_res.extend(sub_res) + except Exception as e: + if callback: + callback(0.05, f"Failed to chunk embed {embed_filename}: {e}") + continue + if re.search(r"\.docx$", filename, re.IGNORECASE): callback(0.1, "Start to parse.") @@ -483,10 +505,12 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, "delimiter", "\n!?。;!?")) if kwargs.get("section_only", False): + chunks.extend(embed_res) return chunks res.extend(tokenize_chunks_with_images(chunks, doc, is_english, images)) logging.info("naive_merge({}): {}".format(filename, timer() - st)) + res.extend(embed_res) return res elif re.search(r"\.pdf$", filename, re.IGNORECASE): @@ -519,6 +543,7 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, res = tokenize_table(tables, doc, is_english) callback(0.8, "Finish parsing.") + elif layout_recognizer == "MinerU": mineru_executable = os.environ.get("MINERU_EXECUTABLE", "mineru") pdf_parser = MinerUParser(mineru_path=mineru_executable) @@ -621,7 +646,6 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, callback(0.8, f"tika.parser got empty content from {filename}.") logging.warning(f"tika.parser got empty content from {filename}.") return [] - else: raise NotImplementedError( "file type not supported yet(pdf, xlsx, doc, docx, txt supported)") @@ -638,6 +662,7 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, "chunk_token_num", 128)), parser_config.get( "delimiter", "\n!?。;!?")) if kwargs.get("section_only", False): + chunks.extend(embed_res) return chunks res.extend(tokenize_chunks_with_images(chunks, doc, is_english, images)) @@ -647,11 +672,14 @@ def chunk(filename, binary=None, from_page=0, to_page=100000, "chunk_token_num", 128)), parser_config.get( "delimiter", "\n!?。;!?")) if kwargs.get("section_only", False): + chunks.extend(embed_res) return chunks res.extend(tokenize_chunks(chunks, doc, is_english, pdf_parser)) logging.info("naive_merge({}): {}".format(filename, timer() - st)) + if embed_res: + res.extend(embed_res) return res diff --git a/uv.lock b/uv.lock index 1099579b1fb..db5e2883257 100644 --- a/uv.lock +++ b/uv.lock @@ -31,6 +31,15 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/1c/a17fb513aeb684fb83bef5f395910f53103ab30308bbdd77fd66d6698c46/accelerate-1.9.0-py3-none-any.whl", hash = "sha256:c24739a97ade1d54af4549a65f8b6b046adc87e2b3e4d6c66516e32c53d5a8f1", size = 367073, upload-time = "2025-07-16T16:24:52.957Z" }, ] +[[package]] +name = "acres" +version = "0.5.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/ba/94b63a9af588fbf7bde25ce44d55456199654a92fb7b2337767198a824b0/acres-0.5.0.tar.gz", hash = "sha256:128b6447bf5df3b6210264feccbfa018b4ac5bd337358319aec6563f99db8f3a", size = 57750, upload-time = "2025-06-04T12:40:30.329Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/e8/806475fe4cdfd8635535d3fa11bd61d19b7cc94b61b9147ebdd2ab4cbbee/acres-0.5.0-py3-none-any.whl", hash = "sha256:fcc32b974b510897de0f041609b4234f9ff03e2e960aea088f63973fb106c772", size = 12703, upload-time = "2025-06-04T12:40:28.745Z" }, +] + [[package]] name = "aiofiles" version = "24.1.0" @@ -818,6 +827,15 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] +[[package]] +name = "ci-info" +version = "0.3.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/27/938d6ef93df09c686dcee1c7334578274320e98e7bf912a6409cf2c8c3e5/ci-info-0.3.0.tar.gz", hash = "sha256:1fd50cbd401f29adffeeb18b0489e232d16ac1a7458ac6bc316deab6ae535fb0", size = 25169, upload-time = "2022-07-27T17:22:49.365Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/c3/8ac768b389d5b6dda1c3ce7992b3acd2b46401f9b71439123858b17b1a2c/ci_info-0.3.0-py3-none-any.whl", hash = "sha256:e9e05d262a6c48aa03cd904475de5ce8c4da8a5435e516631c795d0487dc9e07", size = 7764, upload-time = "2022-07-27T17:22:47.196Z" }, +] + [[package]] name = "click" version = "8.2.1" @@ -912,6 +930,24 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/07/1d/62f5bf92e12335eb63517f42671ed78512d48bbc69e02a942dd7b90f03f0/compressed_rtf-1.0.7-py3-none-any.whl", hash = "sha256:b7904921d78c67a0a4b7fff9fb361a00ae2b447b6edca010ce321cd98fa0fcc0", size = 7968, upload-time = "2025-03-24T23:03:57.433Z" }, ] +[[package]] +name = "configobj" +version = "5.0.9" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/c4/c7f9e41bc2e5f8eeae4a08a01c91b2aea3dfab40a3e14b25e87e7db8d501/configobj-5.0.9.tar.gz", hash = "sha256:03c881bbf23aa07bccf1b837005975993c4ab4427ba57f959afdd9d1a2386848", size = 101518, upload-time = "2024-09-21T12:47:46.315Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a6/c4/0679472c60052c27efa612b4cd3ddd2a23e885dcdc73461781d2c802d39e/configobj-5.0.9-py2.py3-none-any.whl", hash = "sha256:1ba10c5b6ee16229c79a05047aeda2b55eb4e80d7c7d8ecf17ec1ca600c79882", size = 35615, upload-time = "2024-11-26T14:03:32.972Z" }, +] + +[[package]] +name = "configparser" +version = "7.2.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/ac/ea19242153b5e8be412a726a70e82c7b5c1537c83f61b20995b2eda3dcd7/configparser-7.2.0.tar.gz", hash = "sha256:b629cc8ae916e3afbd36d1b3d093f34193d851e11998920fdcfc4552218b7b70", size = 51273, upload-time = "2025-03-08T16:04:09.339Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/fe/f61e7129e9e689d9e40bbf8a36fb90f04eceb477f4617c02c6a18463e81f/configparser-7.2.0-py3-none-any.whl", hash = "sha256:fee5e1f3db4156dcd0ed95bc4edfa3580475537711f67a819c966b389d09ce62", size = 17232, upload-time = "2025-03-08T16:04:07.743Z" }, +] + [[package]] name = "contourpy" version = "1.3.2" @@ -1471,6 +1507,19 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, ] +[[package]] +name = "etelemetry" +version = "0.3.1" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "ci-info" }, + { name = "packaging" }, + { name = "requests" }, +] +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/27/f997c9da0e179986fadd6c8474d16743f1b3697c129c2fcd1e739cd038c2/etelemetry-0.3.1-py3-none-any.whl", hash = "sha256:a64f09bcd55cbfa5684e4d9fb6d1d6a018ab99d2ea28e638435c4c26e6814a6b" }, +] + [[package]] name = "events" version = "0.5" @@ -1659,6 +1708,25 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/f0/48285f0262fe47103a4a45972ed2f9b93e4c80b8fd609fa98da78b2a5706/filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7", size = 16159, upload-time = "2024-06-22T15:59:12.695Z" }, ] +[[package]] +name = "fitz" +version = "0.0.1.dev2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "configobj" }, + { name = "configparser" }, + { name = "httplib2" }, + { name = "nibabel" }, + { name = "nipype" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyxnat" }, + { name = "scipy" }, +] +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/28/27f27d66eb82f24e6595deb26c0a875e62431878c416e38eac515023abb2/fitz-0.0.1.dev2-py2.py3-none-any.whl", hash = "sha256:3b75083d58068d9bd51695eb2f78c9c92094cd6c8dada839e93edcddf18c0c5c", size = 20003, upload-time = "2017-02-25T23:29:54.403Z" }, +] + [[package]] name = "flagembedding" version = "1.2.10" @@ -2693,6 +2761,15 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, ] +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + [[package]] name = "infinity-emb" version = "0.0.66" @@ -3075,6 +3152,15 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, ] +[[package]] +name = "looseversion" +version = "1.3.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/64/7e/f13dc08e0712cc2eac8e56c7909ce2ac280dbffef2ffd87bd5277ce9d58b/looseversion-1.3.0.tar.gz", hash = "sha256:ebde65f3f6bb9531a81016c6fef3eb95a61181adc47b7f949e9c0ea47911669e", size = 8799, upload-time = "2023-07-05T16:07:51.173Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/74/d5405b9b3b12e9176dff223576d7090bc161092878f533fd0dc23dd6ae1d/looseversion-1.3.0-py2.py3-none-any.whl", hash = "sha256:781ef477b45946fc03dd4c84ea87734b21137ecda0e1e122bcb3c8d16d2a56e0", size = 8237, upload-time = "2023-07-05T16:07:49.782Z" }, +] + [[package]] name = "lxml" version = "5.3.0" @@ -3681,6 +3767,50 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, ] +[[package]] +name = "nibabel" +version = "5.3.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "importlib-resources", marker = "python_full_version < '3.12'" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/61/33036cb89f1ec1fedbc4039602345d830b27cbd8a5c7bf28c2e5b5de3ea2/nibabel-5.3.2.tar.gz", hash = "sha256:0bdca6503b1c784b446c745a4542367de7756cfba0d72143b91f9ffb78be569b", size = 4504842, upload-time = "2024-10-23T14:19:55.866Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/b2/dc384197be44e2a640bb43311850e23c2c30f3b82ce7c8cdabbf0e53045e/nibabel-5.3.2-py3-none-any.whl", hash = "sha256:52970a5a8a53b1b55249cba4d9bcfaa8cc57e3e5af35a29d7352237e8680a6f8", size = 3293839, upload-time = "2024-10-23T14:19:52.65Z" }, +] + +[[package]] +name = "nipype" +version = "1.10.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "acres" }, + { name = "click" }, + { name = "etelemetry" }, + { name = "filelock" }, + { name = "looseversion" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.5", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nibabel" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "prov" }, + { name = "puremagic" }, + { name = "pydot" }, + { name = "python-dateutil" }, + { name = "rdflib" }, + { name = "scipy" }, + { name = "simplejson" }, + { name = "traits" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/1a/7ff53f5802d37085a55d7c6df7c6ebebbc8a044930628ca21f7e661c1983/nipype-1.10.0.tar.gz", hash = "sha256:19e5d6cefa70997198f78bc665ef4d3d3cb53325b5b98a72e51aefadaf6b3e0e", size = 2919807, upload-time = "2025-03-19T23:30:07.473Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/53/c5ad0140e2e4c4d92ae45558587e26b2ebc62e39eafa30b74cb052d9375b/nipype-1.10.0-py3-none-any.whl", hash = "sha256:56ced3272e77952e330f13e28328a8fe2e8a69587ca89bc34234f7d06f8319bb", size = 3200685, upload-time = "2025-03-19T23:30:05.357Z" }, +] + [[package]] name = "nltk" version = "3.9.1" @@ -4362,6 +4492,15 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475, upload-time = "2023-03-27T02:01:09.31Z" }, ] +[[package]] +name = "pathlib" +version = "1.0.1" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ac/aa/9b065a76b9af472437a0059f77e8f962fe350438b927cb80184c32f075eb/pathlib-1.0.1.tar.gz", hash = "sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f", size = 49298, upload-time = "2014-09-03T15:41:57.18Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/f9/690a8600b93c332de3ab4a344a4ac34f00c8f104917061f779db6a918ed6/pathlib-1.0.1-py3-none-any.whl", hash = "sha256:f35f95ab8b0f59e6d354090350b44a80a80635d22efdedfa84c7ad1cf0a74147", size = 14363, upload-time = "2022-05-04T13:37:20.585Z" }, +] + [[package]] name = "patsy" version = "1.0.1" @@ -4697,6 +4836,21 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3a/fa/4c3ac5527ed2e5f3577167ecd5f8180ffcdc8bdd59c9f143409c19706456/protobuf-5.27.2-py3-none-any.whl", hash = "sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470", size = 164772, upload-time = "2024-06-25T20:54:52.196Z" }, ] +[[package]] +name = "prov" +version = "2.1.1" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.5", source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pydot" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/bb/442f2e478061543c9c229f48c2d3a43cb0a77642584edecac126bc1ade99/prov-2.1.1.tar.gz", hash = "sha256:7d012b164f5bbb42e118ed9d25788ab012d09082b722bc9dd4e811a309ea57f5", size = 136802, upload-time = "2025-06-24T22:01:50.767Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/17/5703ad2380e57ecceb2700e30646ba0d856d9b90c9f33b01c68a3e298e3a/prov-2.1.1-py3-none-any.whl", hash = "sha256:04f74f9151b68f0bda68c943e111b1275207b19e197689043644a1b355a9d035", size = 425860, upload-time = "2025-06-24T22:01:49.485Z" }, +] + [[package]] name = "psutil" version = "7.0.0" @@ -4756,6 +4910,15 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/08/9c66c269b0d417a0af9fb969535f0371b8c538633535a7a6a5ca3f9231e2/psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab", size = 1163864, upload-time = "2023-10-28T09:37:28.155Z" }, ] +[[package]] +name = "puremagic" +version = "1.30" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/7f/9998706bc516bdd664ccf929a1da6c6e5ee06e48f723ce45aae7cf3ff36e/puremagic-1.30.tar.gz", hash = "sha256:f9ff7ac157d54e9cf3bff1addfd97233548e75e685282d84ae11e7ffee1614c9", size = 314785, upload-time = "2025-07-04T18:48:36.061Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/ed/1e347d85d05b37a8b9a039ca832e5747e1e5248d0bd66042783ef48b4a37/puremagic-1.30-py3-none-any.whl", hash = "sha256:5eeeb2dd86f335b9cfe8e205346612197af3500c6872dffebf26929f56e9d3c1", size = 43304, upload-time = "2025-07-04T18:48:34.801Z" }, +] + [[package]] name = "py" version = "1.11.0" @@ -5012,6 +5175,18 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ca/8f/86d7931c62013a5a7ebf4e1642a87d4a6050c0f570e714f61b0df1984c62/pydivert-2.1.0-py2.py3-none-any.whl", hash = "sha256:382db488e3c37c03ec9ec94e061a0b24334d78dbaeebb7d4e4d32ce4355d9da1", size = 104718, upload-time = "2017-10-20T21:36:56.726Z" }, ] +[[package]] +name = "pydot" +version = "4.0.1" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/35/b17cb89ff865484c6a20ef46bf9d95a5f07328292578de0b295f4a6beec2/pydot-4.0.1.tar.gz", hash = "sha256:c2148f681c4a33e08bf0e26a9e5f8e4099a82e0e2a068098f32ce86577364ad5", size = 162594, upload-time = "2025-06-17T20:09:56.454Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl", hash = "sha256:869c0efadd2708c0be1f916eb669f3d664ca684bc57ffb7ecc08e70d5e93fee6", size = 37087, upload-time = "2025-06-17T20:09:55.25Z" }, +] + [[package]] name = "pyee" version = "13.0.0" @@ -5062,6 +5237,21 @@ crypto = [ { name = "cryptography" }, ] +[[package]] +name = "pymupdf" +version = "1.26.5" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/9a/e0a4e92a85fc17be7c54afdbb113f0ade2a8bca49856d510e28bd249e462/pymupdf-1.26.5.tar.gz", hash = "sha256:8ef335e07f648492df240f2247854d0e7c0467afb9c4dc2376ec30978ec158c3", size = 84319274, upload-time = "2025-10-10T14:04:51.826Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dd/3f/7fc927fd66922ce838d4c974ff9a685c5f5aba108a5d94914dc05c9371f5/pymupdf-1.26.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bfb58f07ad631e5f71ad0bd6f1ff52700f7ba7ebb4973130e81e75b721beae1", size = 23065601, upload-time = "2025-10-10T13:58:43.98Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/e2/e87e62284ba98d59f1fd4fc7542ef2ed0002525754a485fa4077b3bbddae/pymupdf-1.26.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d58599479bc471d3ae56c3d68d9160d0b7de8a3bd40221ddc3a4eaae2d281b86", size = 22412612, upload-time = "2025-10-10T13:59:04.846Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/c2/af93c6367f79e9b5435f803bde51c1dc8225f054f8238162dda80b44986d/pymupdf-1.26.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7dfea81fdd73437a6a6ce83e1fcf556faee9327a6540571e58bf04fa362bb0cd", size = 23457410, upload-time = "2025-10-10T22:45:26.355Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5b/5a/1292a0df4ff71fbc00dfa8c08759d17c97e1e8ea9277eb5bc5f079ca188d/pymupdf-1.26.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:caad0ffeb63dcc4a29ca40f3c68d7b78d32a932e834b0056b529cc0bdbaaffc9", size = 24064941, upload-time = "2025-10-10T13:59:48.544Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/90/87b7fdfc9cd6991a3eb69a5752f6343374c34f258c511c242f4d60791eea/pymupdf-1.26.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e24e7a7d696bd398543cc5c147869edb2026d5d5a21b7f8e35db2f20170b389e", size = 24268203, upload-time = "2025-10-10T14:00:28.791Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/99/9d4b36485538e29df0a013fb02bbf6b5b0743a428fa07515e36631c43363/pymupdf-1.26.5-cp39-abi3-win32.whl", hash = "sha256:a2a42f5911d153a47bf5c3e162a0bfe8745eb9bec3e59fbaf87617b4003d8270", size = 17130722, upload-time = "2025-10-10T14:00:51.377Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/96/fd59c1532891762ea4815e73956c532053d5e26d56969e1e5d1e4ca4b207/pymupdf-1.26.5-cp39-abi3-win_amd64.whl", hash = "sha256:39a6fb58182b27b51ea8150a0cd2e4ee7e0cf71e9d6723978f28699b42ee61ae", size = 18747258, upload-time = "2025-10-10T14:01:37.346Z" }, +] + [[package]] name = "pymysql" version = "1.1.1" @@ -5404,6 +5594,20 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, ] +[[package]] +name = "pyxnat" +version = "1.6.3" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "lxml" }, + { name = "pathlib" }, + { name = "requests" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/24/c8737985e65d8adbbf51970b2a75cf54b5376d68d251159d9b7c5c9673b6/pyxnat-1.6.3.tar.gz", hash = "sha256:ddd074f35f7b35b5dccb6f713b20cf083c79d6e0d3d9cafbcaabb7c661b0cc68", size = 82466, upload-time = "2025-02-04T19:03:53.801Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/df/257c0f0af8e624daa924a3899f88e6465f162d72ada3fb0b96df9e61a2d6/pyxnat-1.6.3-py3-none-any.whl", hash = "sha256:a6d84dd24486eab9731a5de5df4fb486021b095665083c2fb1d33ac1e719d3c5", size = 95408, upload-time = "2025-02-04T19:03:51.707Z" }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -5502,6 +5706,7 @@ dependencies = [ { name = "elasticsearch-dsl" }, { name = "extract-msg" }, { name = "filelock" }, + { name = "fitz" }, { name = "flasgger" }, { name = "flask" }, { name = "flask-cors" }, @@ -5554,6 +5759,7 @@ dependencies = [ { name = "pyclipper" }, { name = "pycryptodomex" }, { name = "pyicu" }, + { name = "pymupdf" }, { name = "pymysql" }, { name = "pyodbc" }, { name = "pypdf" }, @@ -5662,6 +5868,7 @@ requires-dist = [ { name = "fastembed", marker = "(platform_machine != 'x86_64' and extra == 'full') or (sys_platform == 'darwin' and extra == 'full')", specifier = ">=0.3.6,<0.4.0" }, { name = "fastembed-gpu", marker = "platform_machine == 'x86_64' and sys_platform != 'darwin' and extra == 'full'", specifier = ">=0.3.6,<0.4.0" }, { name = "filelock", specifier = "==3.15.4" }, + { name = "fitz", specifier = ">=0.0.1.dev2" }, { name = "flagembedding", marker = "extra == 'full'", specifier = "==1.2.10" }, { name = "flasgger", specifier = ">=0.9.7.1,<0.10.0" }, { name = "flask", specifier = "==3.0.3" }, @@ -5715,6 +5922,7 @@ requires-dist = [ { name = "pyclipper", specifier = "==1.3.0.post5" }, { name = "pycryptodomex", specifier = "==3.20.0" }, { name = "pyicu", specifier = ">=2.15.3,<3.0.0" }, + { name = "pymupdf", specifier = ">=1.26.5" }, { name = "pymysql", specifier = ">=1.1.1,<2.0.0" }, { name = "pyodbc", specifier = ">=5.2.0,<6.0.0" }, { name = "pypdf", specifier = "==6.0.0" }, @@ -5815,6 +6023,19 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/30/53f41b7b728a48da8974075f56c57200d7b11e4e9fa93be3cabf8218dc0c/ranx-0.3.20-py3-none-any.whl", hash = "sha256:e056e4d5981b0328b045868cc7064fc57a545f36009fbe9bb602295ec33335de", size = 99318, upload-time = "2024-07-01T17:40:27.095Z" }, ] +[[package]] +name = "rdflib" +version = "7.2.1" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +dependencies = [ + { name = "isodate", marker = "python_full_version < '3.11'" }, + { name = "pyparsing" }, +] +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/99/d2fec85e5f6bdfe4367dea143119cb4469bf48710487939df0abf7e22003/rdflib-7.2.1.tar.gz", hash = "sha256:cf9b7fa25234e8925da8b1fb09700f8349b5f0f100e785fb4260e737308292ac", size = 4873802, upload-time = "2025-09-19T02:33:36.492Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/98/7fa830bb4b9da21905683a5352aa0a01a1f3082328ae976aad341e980c23/rdflib-7.2.1-py3-none-any.whl", hash = "sha256:1a175bc1386a167a42fbfaba003bfa05c164a2a3ca3cb9c0c97f9c9638ca6ac2", size = 565423, upload-time = "2025-09-19T02:33:30.889Z" }, +] + [[package]] name = "readability-lxml" version = "0.8.1" @@ -6412,6 +6633,54 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] +[[package]] +name = "simplejson" +version = "3.20.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/41/f4/a1ac5ed32f7ed9a088d62a59d410d4c204b3b3815722e2ccfb491fa8251b/simplejson-3.20.2.tar.gz", hash = "sha256:5fe7a6ce14d1c300d80d08695b7f7e633de6cd72c80644021874d985b3393649", size = 85784, upload-time = "2025-09-26T16:29:36.64Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/09/2bf3761de89ea2d91bdce6cf107dcd858892d0adc22c995684878826cc6b/simplejson-3.20.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6d7286dc11af60a2f76eafb0c2acde2d997e87890e37e24590bb513bec9f1bc5", size = 94039, upload-time = "2025-09-26T16:27:29.283Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/33/c3277db8931f0ae9e54b9292668863365672d90fb0f632f4cf9829cb7d68/simplejson-3.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c01379b4861c3b0aa40cba8d44f2b448f5743999aa68aaa5d3ef7049d4a28a2d", size = 75894, upload-time = "2025-09-26T16:27:30.378Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/ea/ae47b04d03c7c8a7b7b1a8b39a6e27c3bd424e52f4988d70aca6293ff5e5/simplejson-3.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a16b029ca25645b3bc44e84a4f941efa51bf93c180b31bd704ce6349d1fc77c1", size = 76116, upload-time = "2025-09-26T16:27:31.42Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/42/6c9af551e5a8d0f171d6dce3d9d1260068927f7b80f1f09834e07887c8c4/simplejson-3.20.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e22a5fb7b1437ffb057e02e1936a3bfb19084ae9d221ec5e9f4cf85f69946b6", size = 138827, upload-time = "2025-09-26T16:27:32.486Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/22/5e268bbcbe9f75577491e406ec0a5536f5b2fa91a3b52031fea51cd83e1d/simplejson-3.20.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b6ff02fc7b8555c906c24735908854819b0d0dc85883d453e23ca4c0445d01", size = 146772, upload-time = "2025-09-26T16:27:34.036Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/b4/800f14728e2ad666f420dfdb57697ca128aeae7f991b35759c09356b829a/simplejson-3.20.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bfc1c396ad972ba4431130b42307b2321dba14d988580c1ac421ec6a6b7cee3", size = 134497, upload-time = "2025-09-26T16:27:35.211Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/b9/c54eef4226c6ac8e9a389bbe5b21fef116768f97a2dc1a683c716ffe66ef/simplejson-3.20.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a97249ee1aee005d891b5a211faf58092a309f3d9d440bc269043b08f662eda", size = 138172, upload-time = "2025-09-26T16:27:36.44Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/36/4e282f5211b34620f1b2e4b51d9ddaab5af82219b9b7b78360a33f7e5387/simplejson-3.20.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f1036be00b5edaddbddbb89c0f80ed229714a941cfd21e51386dc69c237201c2", size = 140272, upload-time = "2025-09-26T16:27:37.605Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/b0/94ad2cf32f477c449e1f63c863d8a513e2408d651c4e58fe4b6a7434e168/simplejson-3.20.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5d6f5bacb8cdee64946b45f2680afa3f54cd38e62471ceda89f777693aeca4e4", size = 140468, upload-time = "2025-09-26T16:27:39.015Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/46/827731e4163be3f987cb8ee90f5d444161db8f540b5e735355faa098d9bc/simplejson-3.20.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8db6841fb796ec5af632f677abf21c6425a1ebea0d9ac3ef1a340b8dc69f52b8", size = 148700, upload-time = "2025-09-26T16:27:40.171Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/28/c32121064b1ec2fb7b5d872d9a1abda62df064d35e0160eddfa907118343/simplejson-3.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0a341f7cc2aae82ee2b31f8a827fd2e51d09626f8b3accc441a6907c88aedb7", size = 141323, upload-time = "2025-09-26T16:27:41.324Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/46/b6/c897c54326fe86dd12d101981171a49361949f4728294f418c3b86a1af77/simplejson-3.20.2-cp310-cp310-win32.whl", hash = "sha256:27f9c01a6bc581d32ab026f515226864576da05ef322d7fc141cd8a15a95ce53", size = 74377, upload-time = "2025-09-26T16:27:42.533Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/87/a6e03d4d80cca99c1fee4e960f3440e2f21be9470e537970f960ca5547f1/simplejson-3.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0a63ec98a4547ff366871bf832a7367ee43d047bcec0b07b66c794e2137b476", size = 76081, upload-time = "2025-09-26T16:27:43.945Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/3e/96898c6c66d9dca3f9bd14d7487bf783b4acc77471b42f979babbb68d4ca/simplejson-3.20.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:06190b33cd7849efc413a5738d3da00b90e4a5382fd3d584c841ac20fb828c6f", size = 92633, upload-time = "2025-09-26T16:27:45.028Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/a2/cd2e10b880368305d89dd540685b8bdcc136df2b3c76b5ddd72596254539/simplejson-3.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4ad4eac7d858947a30d2c404e61f16b84d16be79eb6fb316341885bdde864fa8", size = 75309, upload-time = "2025-09-26T16:27:46.142Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/02/290f7282eaa6ebe945d35c47e6534348af97472446951dce0d144e013f4c/simplejson-3.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b392e11c6165d4a0fde41754a0e13e1d88a5ad782b245a973dd4b2bdb4e5076a", size = 75308, upload-time = "2025-09-26T16:27:47.542Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/91/43695f17b69e70c4b0b03247aa47fb3989d338a70c4b726bbdc2da184160/simplejson-3.20.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51eccc4e353eed3c50e0ea2326173acdc05e58f0c110405920b989d481287e51", size = 143733, upload-time = "2025-09-26T16:27:48.673Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9b/4b/fdcaf444ac1c3cbf1c52bf00320c499e1cf05d373a58a3731ae627ba5e2d/simplejson-3.20.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:306e83d7c331ad833d2d43c76a67f476c4b80c4a13334f6e34bb110e6105b3bd", size = 153397, upload-time = "2025-09-26T16:27:49.89Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/83/21550f81a50cd03599f048a2d588ffb7f4c4d8064ae091511e8e5848eeaa/simplejson-3.20.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f820a6ac2ef0bc338ae4963f4f82ccebdb0824fe9caf6d660670c578abe01013", size = 141654, upload-time = "2025-09-26T16:27:51.168Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cf/54/d76c0e72ad02450a3e723b65b04f49001d0e73218ef6a220b158a64639cb/simplejson-3.20.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e7a066528a5451433eb3418184f05682ea0493d14e9aae690499b7e1eb6b81", size = 144913, upload-time = "2025-09-26T16:27:52.331Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/49/976f59b42a6956d4aeb075ada16ad64448a985704bc69cd427a2245ce835/simplejson-3.20.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:438680ddde57ea87161a4824e8de04387b328ad51cfdf1eaf723623a3014b7aa", size = 144568, upload-time = "2025-09-26T16:27:53.41Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/60/c7/30bae30424ace8cd791ca660fed454ed9479233810fe25c3f3eab3d9dc7b/simplejson-3.20.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cac78470ae68b8d8c41b6fca97f5bf8e024ca80d5878c7724e024540f5cdaadb", size = 146239, upload-time = "2025-09-26T16:27:54.502Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/79/3e/7f3b7b97351c53746e7b996fcd106986cda1954ab556fd665314756618d2/simplejson-3.20.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7524e19c2da5ef281860a3d74668050c6986be15c9dd99966034ba47c68828c2", size = 154497, upload-time = "2025-09-26T16:27:55.885Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1d/48/7241daa91d0bf19126589f6a8dcbe8287f4ed3d734e76fd4a092708947be/simplejson-3.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e9b6d845a603b2eef3394eb5e21edb8626cd9ae9a8361d14e267eb969dbe413", size = 148069, upload-time = "2025-09-26T16:27:57.039Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/f4/ef18d2962fe53e7be5123d3784e623859eec7ed97060c9c8536c69d34836/simplejson-3.20.2-cp311-cp311-win32.whl", hash = "sha256:47d8927e5ac927fdd34c99cc617938abb3624b06ff86e8e219740a86507eb961", size = 74158, upload-time = "2025-09-26T16:27:58.265Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/fd/3d1158ecdc573fdad81bf3cc78df04522bf3959758bba6597ba4c956c74d/simplejson-3.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:ba4edf3be8e97e4713d06c3d302cba1ff5c49d16e9d24c209884ac1b8455520c", size = 75911, upload-time = "2025-09-26T16:27:59.292Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/9e/1a91e7614db0416885eab4136d49b7303de20528860ffdd798ce04d054db/simplejson-3.20.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4376d5acae0d1e91e78baeba4ee3cf22fbf6509d81539d01b94e0951d28ec2b6", size = 93523, upload-time = "2025-09-26T16:28:00.356Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5e/2b/d2413f5218fc25608739e3d63fe321dfa85c5f097aa6648dbe72513a5f12/simplejson-3.20.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f8fe6de652fcddae6dec8f281cc1e77e4e8f3575249e1800090aab48f73b4259", size = 75844, upload-time = "2025-09-26T16:28:01.756Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/f1/efd09efcc1e26629e120fef59be059ce7841cc6e1f949a4db94f1ae8a918/simplejson-3.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25ca2663d99328d51e5a138f22018e54c9162438d831e26cfc3458688616eca8", size = 75655, upload-time = "2025-09-26T16:28:03.037Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/97/ec/5c6db08e42f380f005d03944be1af1a6bd501cc641175429a1cbe7fb23b9/simplejson-3.20.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12a6b2816b6cab6c3fd273d43b1948bc9acf708272074c8858f579c394f4cbc9", size = 150335, upload-time = "2025-09-26T16:28:05.027Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/f5/808a907485876a9242ec67054da7cbebefe0ee1522ef1c0be3bfc90f96f6/simplejson-3.20.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac20dc3fcdfc7b8415bfc3d7d51beccd8695c3f4acb7f74e3a3b538e76672868", size = 158519, upload-time = "2025-09-26T16:28:06.5Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/af/b8a158246834645ea890c36136584b0cc1c0e4b83a73b11ebd9c2a12877c/simplejson-3.20.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db0804d04564e70862ef807f3e1ace2cc212ef0e22deb1b3d6f80c45e5882c6b", size = 148571, upload-time = "2025-09-26T16:28:07.715Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/05/ed9b2571bbf38f1a2425391f18e3ac11cb1e91482c22d644a1640dea9da7/simplejson-3.20.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:979ce23ea663895ae39106946ef3d78527822d918a136dbc77b9e2b7f006237e", size = 152367, upload-time = "2025-09-26T16:28:08.921Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/81/2c/bad68b05dd43e93f77994b920505634d31ed239418eb6a88997d06599983/simplejson-3.20.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a2ba921b047bb029805726800819675249ef25d2f65fd0edb90639c5b1c3033c", size = 150205, upload-time = "2025-09-26T16:28:10.086Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/69/46/90c7fc878061adafcf298ce60cecdee17a027486e9dce507e87396d68255/simplejson-3.20.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:12d3d4dc33770069b780cc8f5abef909fe4a3f071f18f55f6d896a370fd0f970", size = 151823, upload-time = "2025-09-26T16:28:11.329Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/27/b85b03349f825ae0f5d4f780cdde0bbccd4f06c3d8433f6a3882df887481/simplejson-3.20.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aff032a59a201b3683a34be1169e71ddda683d9c3b43b261599c12055349251e", size = 158997, upload-time = "2025-09-26T16:28:12.917Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/ad/d7f3c331fb930638420ac6d236db68e9f4c28dab9c03164c3cd0e7967e15/simplejson-3.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30e590e133b06773f0dc9c3f82e567463df40598b660b5adf53eb1c488202544", size = 154367, upload-time = "2025-09-26T16:28:14.393Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/46/5c67324addd40fa2966f6e886cacbbe0407c03a500db94fb8bb40333fcdf/simplejson-3.20.2-cp312-cp312-win32.whl", hash = "sha256:8d7be7c99939cc58e7c5bcf6bb52a842a58e6c65e1e9cdd2a94b697b24cddb54", size = 74285, upload-time = "2025-09-26T16:28:15.931Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/c9/5cc2189f4acd3a6e30ffa9775bf09b354302dbebab713ca914d7134d0f29/simplejson-3.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:2c0b4a67e75b945489052af6590e7dca0ed473ead5d0f3aad61fa584afe814ab", size = 75969, upload-time = "2025-09-26T16:28:17.017Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/05/5b/83e1ff87eb60ca706972f7e02e15c0b33396e7bdbd080069a5d1b53cf0d8/simplejson-3.20.2-py3-none-any.whl", hash = "sha256:3b6bb7fb96efd673eac2e4235200bfffdc2353ad12c54117e1e4e2fc485ac017", size = 57309, upload-time = "2025-09-26T16:29:35.312Z" }, +] + [[package]] name = "six" version = "1.16.0" @@ -7017,6 +7286,38 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] +[[package]] +name = "traits" +version = "7.0.2" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/ba/33e199bfae748e802f68a857035fb003089c176897bf43e2cf38ff167740/traits-7.0.2.tar.gz", hash = "sha256:a563515809cb3911975de5a54209855f0b6fdb7ca6912a5e81de26529f70428c", size = 9534785, upload-time = "2025-01-24T20:52:59.954Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/5c/6aa6aef1472a79accd4c077cc8eccf3c3a2acc4b42ece2c48f5651f2f915/traits-7.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb59a033260dfa3aacfe484307a91f318a1fa801f5e8c8293fe22834fa4b30a7", size = 5034452, upload-time = "2025-01-24T20:55:25.02Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/0a/8387ff6f32898c334b2a96b465a8790633cec3c2270893210946d43de0d3/traits-7.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5c18d5f4aea2988b15bc10e2ac9f4eb49531d1ec380857f3046a7ba14509e4b", size = 5034825, upload-time = "2025-01-24T20:56:04.238Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8f/15/a04a5e1cd0c2e2979365e1ac3a674ec0f16a5af36d19809c869985e63f7a/traits-7.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11950d519b113e9a34d5a99fca112866d8c36aa8fce85edadf52995ad03de07e", size = 5110401, upload-time = "2025-01-24T20:57:19.172Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b3/da/58d58c3495b2bfee03975d95799d5a8ac771a2f510d579935122c02d26dc/traits-7.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d50b42061cb8f34119b6b7abe703982c6fa157a2fe4e10a5b9ab9f93c340d5e3", size = 5121856, upload-time = "2025-01-24T20:57:20.949Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/74/66ed1b2511c0a457f716f6c718abf807db58c76292cbd69ecf4390519fea/traits-7.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:53fbd8a0adf42d235e6a73bd3fbb3f7190a28302d151c9a25967ff6f12b918cd", size = 5109296, upload-time = "2025-01-24T20:57:23.835Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/30/60efe8a3fe454fd7b939695d556cdee7943b1ced19fc40f9b4f2a240211c/traits-7.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0b48be9fb0b9e5a733e9fa5a542b0751237822e20b52fac80b5796cc606af509", size = 5117788, upload-time = "2025-01-24T20:57:27.096Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/ef/e884bd2c05d52415acb0344ed3847f1c3835d1651a4189a17e06fa2363fa/traits-7.0.2-cp310-cp310-win32.whl", hash = "sha256:5b98600b9f40e980e0cc5b1f0ade5fb1c1f1c19d25afc2b33ea30773015eb3e5", size = 5033760, upload-time = "2025-01-24T21:01:04.683Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/71/a630ee815843e3d87484c9a0368f81eb993e862aa4cb9c20822deee7e9a3/traits-7.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:def3ab01e7d636aceda9dc6ca2abf71f2a992f9ec993c7ea200157c1ca983ae7", size = 5036225, upload-time = "2025-01-24T21:01:07.817Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/db/da628e34564a89f68d6b3ff5caee8a0a932858a4a3e1bf0d077d9f6d053c/traits-7.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33fd20c3bc29fbb1f51ddb23f63173bf59a2fdafd300e5f4790352d76e4cf68e", size = 5034488, upload-time = "2025-01-24T20:55:26.853Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/4e/d64ad9fb725ff1b943432c5df32c64abb28ad17f66e976d6ce6aaa1b54d5/traits-7.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:018d4f7cbd5e18cb34bafc915134c29aa8568bccd35d9aa9102e2af9ef66cb80", size = 5034832, upload-time = "2025-01-24T20:56:06.125Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3f/80/f32ade6b131c69d2a3451edfa5c9f23056c3c9889b1d7918890ff6dad273/traits-7.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa323b634abd9c7f049892d86296bc1c46bad6ad80121fefeaf12039002d58ff", size = 5119215, upload-time = "2025-01-24T20:57:31.594Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/be/d6/0c7c2c12a53698906e86a0076d13ee3d529a5c0a44468e89cb8a91186f22/traits-7.0.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:209bfb95c094cd315f77fc610ae65af86ec0de075be2d84e6e6290ff2f860715", size = 5130753, upload-time = "2025-01-24T20:57:34.737Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/09/070aef46f818eaab7afdada8647b303facb14d4d5f931c1fb560cfc24e1b/traits-7.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4f38eee0b94f9fbab2f086200e35f835ad1563ba7e078a044cb268ce50542565", size = 5117762, upload-time = "2025-01-24T20:57:36.764Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/85/99/fb239d5fe1ac2931c284496995998abc72f6af0ca32cfdb70095b883fab9/traits-7.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:135dc11da393f5dec1ecaf6981f0608976354435f7be53b9e9175a9c8a118127", size = 5126325, upload-time = "2025-01-24T20:57:38.638Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/48/6c1484be7d5b322c57415c9b6d39c7419ad4ee1eb52b288ddfa3893caf31/traits-7.0.2-cp311-cp311-win32.whl", hash = "sha256:c588571d981d1254d9abf8bd2f8e449f82f31ebe8f951853290910ae2f03dc84", size = 5033773, upload-time = "2025-01-24T21:01:09.598Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/f4/d8cb863aaacfe1633d2b636647bcc70b1cd2e258e4a83e71eae995a34ed4/traits-7.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:98a880b6adab40d66ce0eda1c6f4fdcf178bb182d28d0fb71d3755c36065dd39", size = 5036235, upload-time = "2025-01-24T21:01:12.296Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/6c/9b3be8e459627267de56029a0c91e9a9c9a082353cd5b9ec1edd2f4738a5/traits-7.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bccfafbda22346f0278f010458e819f0a58a95f242f91e14014b055580a15cd8", size = 5035260, upload-time = "2025-01-24T20:55:28.536Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/35/0c/990486e972614dd0173ea647b80c30c30d3ad4819befa9ec94f4a8a421b6/traits-7.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9899ee203fd379fb0e07aebc178940d62d5790dc311263d5c3a577f3baf7dfa", size = 5035240, upload-time = "2025-01-24T20:56:08.856Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/7c/458041d4b345ddd351451303353acbc72a36cbc47649eedb29863a37f119/traits-7.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2938cccfea2da2fdce6cc7ec1e605c923e66610df1b223cf24a4b23ba97375de", size = 5121555, upload-time = "2025-01-24T20:57:41.688Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/f3/7736bf1bee46c6fd1c488e180236067c91490cf2aea235ed851bcf2151e2/traits-7.0.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f696c4d4d03b333e8f8beec206d80d4998ce6b4801deb74c258dbc4415f92345", size = 5135379, upload-time = "2025-01-24T20:57:45.797Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f0/07/e80f6663d460f80f09b443175cb8118b74ca3b7bd164f1ec5c44e1da2047/traits-7.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c49384b12ecaf39b9ab156e1c7d31960206e15071a9917596ab3c265d7bb99aa", size = 5120513, upload-time = "2025-01-24T20:57:49.354Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/8b/0716f7b8f34e1b57b39f81472460f4e02491dde02fbc114bac42cf0acd85/traits-7.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6932e5a784000368aa3948890bf55c4aba10494d4a45e9bb6c2b228644f2e67c", size = 5130509, upload-time = "2025-01-24T20:57:51.933Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c5/bf/e0135ce54d5604c57caad8866ac56a05265943a1b3a438277fb6ee10b0f6/traits-7.0.2-cp312-cp312-win32.whl", hash = "sha256:f434da460be8b3eb9f9f35143af116622cd313fa346c0df37b026d318c88ad29", size = 5034118, upload-time = "2025-01-24T21:01:14.04Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/2b/49423d5b269dfc095e09ecbb41b987b224f4154716d91da063cebaf963a0/traits-7.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:497463a437cb8cd4bb2ed27ae4e4491a8ed3d4d8515803476c94ce952a17af54", size = 5036464, upload-time = "2025-01-24T21:01:16.256Z" }, +] + [[package]] name = "transformers" version = "4.36.2" From c9e56d20cf79cf1969f623051721b07894dafcf5 Mon Sep 17 00:00:00 2001 From: writinwaters <93570324+writinwaters@users.noreply.github.com> Date: Fri, 17 Oct 2025 18:47:09 +0800 Subject: [PATCH 0894/1187] Doc: miscellaneous (#10641) ### What problem does this PR solve? ### Type of change - [x] Documentation Update --- .../agent/agent_component_reference/agent.mdx | 13 +-- .../agent_component_reference/categorize.mdx | 6 +- .../indexer.md | 2 +- .../agent_component_reference/retrieval.mdx | 4 +- .../agent_component_reference/transformer.md | 80 +++++++++++++++++++ docs/guides/chat/start_chat.md | 12 +-- docs/guides/dataset/run_retrieval_test.md | 4 +- 7 files changed, 102 insertions(+), 19 deletions(-) rename docs/guides/agent/{ => agent_component_reference}/indexer.md (97%) create mode 100644 docs/guides/agent/agent_component_reference/transformer.md diff --git a/docs/guides/agent/agent_component_reference/agent.mdx b/docs/guides/agent/agent_component_reference/agent.mdx index 26caff88dc5..dd358f062a2 100644 --- a/docs/guides/agent/agent_component_reference/agent.mdx +++ b/docs/guides/agent/agent_component_reference/agent.mdx @@ -9,7 +9,7 @@ The component equipped with reasoning, tool usage, and multi-agent collaboration --- -An **Agent** component fine-tunes the LLM and sets its prompt. From v0.21.0 onwards, an **Agent** component is able to work independently and with the following capabilities: +An **Agent** component fine-tunes the LLM and sets its prompt. From v0.20.5 onwards, an **Agent** component is able to work independently and with the following capabilities: - Autonomous reasoning with reflection and adjustment based on environmental feedback. - Use of tools or subagents to complete tasks. @@ -24,7 +24,7 @@ An **Agent** component is essential when you need the LLM to assist with summari ![Set default models](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/set_default_models.jpg) -2. If your Agent involves dataset retrieval, ensure you [have properly configured your target knowledge base(s)](../../dataset/configure_knowledge_base.md). +2. If your Agent involves dataset retrieval, ensure you [have properly configured your target dataset(s)](../../dataset/configure_knowledge_base.md). ## Quickstart @@ -113,7 +113,7 @@ Click the dropdown menu of **Model** to show the model configuration window. - **Model**: The chat model to use. - Ensure you set the chat model correctly on the **Model providers** page. - You can use different models for different components to increase flexibility or improve overall performance. -- **Freedom**: A shortcut to **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty** settings, indicating the freedom level of the model. From **Improvise**, **Precise**, to **Balance**, each preset configuration corresponds to a unique combination of **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**. +- **Creavity**: A shortcut to **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty** settings, indicating the freedom level of the model. From **Improvise**, **Precise**, to **Balance**, each preset configuration corresponds to a unique combination of **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**. This parameter has three options: - **Improvise**: Produces more creative responses. - **Precise**: (Default) Produces more conservative responses. @@ -132,11 +132,12 @@ Click the dropdown menu of **Model** to show the model configuration window. - **Frequency penalty**: Discourages the model from repeating the same words or phrases too frequently in the generated text. - A higher **frequency penalty** value results in the model being more conservative in its use of repeated tokens. - Defaults to 0.7. -- **Max tokens**: +- **Max tokens**: + This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). It is disabled by default, allowing the model to determine the number of tokens in its responses. :::tip NOTE - It is not necessary to stick with the same model for all components. If a specific model is not performing well for a particular task, consider using a different one. -- If you are uncertain about the mechanism behind **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**, simply choose one of the three options of **Preset configurations**. +- If you are uncertain about the mechanism behind **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**, simply choose one of the three options of **Creavity**. ::: ### System prompt @@ -147,7 +148,7 @@ An **Agent** component relies on keys (variables) to specify its data inputs. It #### Advanced usage -From v0.21.0 onwards, four framework-level prompt blocks are available in the **System prompt** field, enabling you to customize and *override* prompts at the framework level. Type `/` or click **(x)** to view them; they appear under the **Framework** entry in the dropdown menu. +From v0.20.5 onwards, four framework-level prompt blocks are available in the **System prompt** field, enabling you to customize and *override* prompts at the framework level. Type `/` or click **(x)** to view them; they appear under the **Framework** entry in the dropdown menu. - `task_analysis` prompt block - This block is responsible for analyzing tasks — either a user task or a task assigned by the lead Agent when the **Agent** component is acting as a Sub-Agent. diff --git a/docs/guides/agent/agent_component_reference/categorize.mdx b/docs/guides/agent/agent_component_reference/categorize.mdx index 4ad03661845..39cc7c4b2fc 100644 --- a/docs/guides/agent/agent_component_reference/categorize.mdx +++ b/docs/guides/agent/agent_component_reference/categorize.mdx @@ -42,7 +42,7 @@ Click the dropdown menu of **Model** to show the model configuration window. - **Model**: The chat model to use. - Ensure you set the chat model correctly on the **Model providers** page. - You can use different models for different components to increase flexibility or improve overall performance. -- **Freedom**: A shortcut to **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty** settings, indicating the freedom level of the model. From **Improvise**, **Precise**, to **Balance**, each preset configuration corresponds to a unique combination of **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**. +- **Creavity**: A shortcut to **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty** settings, indicating the freedom level of the model. From **Improvise**, **Precise**, to **Balance**, each preset configuration corresponds to a unique combination of **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**. This parameter has three options: - **Improvise**: Produces more creative responses. - **Precise**: (Default) Produces more conservative responses. @@ -61,10 +61,12 @@ Click the dropdown menu of **Model** to show the model configuration window. - **Frequency penalty**: Discourages the model from repeating the same words or phrases too frequently in the generated text. - A higher **frequency penalty** value results in the model being more conservative in its use of repeated tokens. - Defaults to 0.7. +- **Max tokens**: + This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). It is disabled by default, allowing the model to determine the number of tokens in its responses. :::tip NOTE - It is not necessary to stick with the same model for all components. If a specific model is not performing well for a particular task, consider using a different one. -- If you are uncertain about the mechanism behind **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**, simply choose one of the three options of **Preset configurations**. +- If you are uncertain about the mechanism behind **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**, simply choose one of the three options of **Creavity**. ::: ### Message window size diff --git a/docs/guides/agent/indexer.md b/docs/guides/agent/agent_component_reference/indexer.md similarity index 97% rename from docs/guides/agent/indexer.md rename to docs/guides/agent/agent_component_reference/indexer.md index f96d9df16e0..5bc2d925e10 100644 --- a/docs/guides/agent/indexer.md +++ b/docs/guides/agent/agent_component_reference/indexer.md @@ -1,5 +1,5 @@ --- -sidebar_position: 30 +sidebar_position: 40 slug: /indexer_component --- diff --git a/docs/guides/agent/agent_component_reference/retrieval.mdx b/docs/guides/agent/agent_component_reference/retrieval.mdx index 5807eab5c97..a2215a2f716 100644 --- a/docs/guides/agent/agent_component_reference/retrieval.mdx +++ b/docs/guides/agent/agent_component_reference/retrieval.mdx @@ -87,9 +87,9 @@ RAGFlow employs a combination of weighted keyword similarity and weighted vector Defaults to 0.2. -### Keyword similarity weight +### Vector similarity weight -This parameter sets the weight of keyword similarity in the combined similarity score. The total of the two weights must equal 1.0. Its default value is 0.7, which means the weight of vector similarity in the combined search is 1 - 0.7 = 0.3. +This parameter sets the weight of vector similarity in the composite similarity score. The total of the two weights must equal 1.0. Its default value is 0.3, which means the weight of keyword similarity in a combined search is 1 - 0.3 = 0.7. ### Top N diff --git a/docs/guides/agent/agent_component_reference/transformer.md b/docs/guides/agent/agent_component_reference/transformer.md new file mode 100644 index 00000000000..ab21f60dba7 --- /dev/null +++ b/docs/guides/agent/agent_component_reference/transformer.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 37 +slug: /transformer_component +--- + +# Transformer component + +A component that uses an LLM to extract insights from the chunks. + +--- + +A **Transformer** component indexes chunks and configures their storage formats in the document engine. It *typically* precedes the **Indexer** in the ingestion pipeline, but you can also chain multiple **Transformer** components in sequence. + +## Scenario + +A **Transformer** component is essential when you need the LLM to extract new information, such as keywords, questions, metadata, and summaries, from the original chunks. + +## Configurations + +### Model + +Click the dropdown menu of **Model** to show the model configuration window. + +- **Model**: The chat model to use. + - Ensure you set the chat model correctly on the **Model providers** page. + - You can use different models for different components to increase flexibility or improve overall performance. +- **Creavity**: A shortcut to **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty** settings, indicating the freedom level of the model. From **Improvise**, **Precise**, to **Balance**, each preset configuration corresponds to a unique combination of **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**. + This parameter has three options: + - **Improvise**: Produces more creative responses. + - **Precise**: (Default) Produces more conservative responses. + - **Balance**: A middle ground between **Improvise** and **Precise**. +- **Temperature**: The randomness level of the model's output. + Defaults to 0.1. + - Lower values lead to more deterministic and predictable outputs. + - Higher values lead to more creative and varied outputs. + - A temperature of zero results in the same output for the same prompt. +- **Top P**: Nucleus sampling. + - Reduces the likelihood of generating repetitive or unnatural text by setting a threshold *P* and restricting the sampling to tokens with a cumulative probability exceeding *P*. + - Defaults to 0.3. +- **Presence penalty**: Encourages the model to include a more diverse range of tokens in the response. + - A higher **presence penalty** value results in the model being more likely to generate tokens not yet been included in the generated text. + - Defaults to 0.4. +- **Frequency penalty**: Discourages the model from repeating the same words or phrases too frequently in the generated text. + - A higher **frequency penalty** value results in the model being more conservative in its use of repeated tokens. + - Defaults to 0.7. +- **Max tokens**: + This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). It is disabled by default, allowing the model to determine the number of tokens in its responses. + +:::tip NOTE +- It is not necessary to stick with the same model for all components. If a specific model is not performing well for a particular task, consider using a different one. +- If you are uncertain about the mechanism behind **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**, simply choose one of the three options of **Creativity**. +::: + +### Result destination + +Select the type of output to be generated by the LLM: + +- Summary +- Keywords +- Questions +- Metadata + +### System prompt + +Typically, you use the system prompt to describe the task for the LLM, specify how it should respond, and outline other miscellaneous requirements. We do not plan to elaborate on this topic, as it can be as extensive as prompt engineering. + +:::tip NOTE +The system prompt here automatically updates to match your selected **Result destination**. +::: + +### User prompt + +The user-defined prompt. For example, you can type `/` or click **(x)** to insert variables of preceding components in the ingestion pipeline as the LLM's input. + +### Output + +The global variable name for the output of the **Transformer** component, which can be referenced by subsequent **Transformer** components in the ingestion pipeline. + +- Default: `chunks` +- Type: `Array` \ No newline at end of file diff --git a/docs/guides/chat/start_chat.md b/docs/guides/chat/start_chat.md index 5f41f3a0c25..99b0cf6ac1e 100644 --- a/docs/guides/chat/start_chat.md +++ b/docs/guides/chat/start_chat.md @@ -19,7 +19,7 @@ You start an AI conversation by creating an assistant. > RAGFlow offers you the flexibility of choosing a different chat model for each dialogue, while allowing you to set the default models in **System Model Settings**. -2. Update **Assistant settings**: +2. Update Assistant-specific settings: - **Assistant name** is the name of your chat assistant. Each assistant corresponds to a dialogue with a unique combination of datasets, prompts, hybrid search configurations, and large model settings. - **Empty response**: @@ -28,12 +28,12 @@ You start an AI conversation by creating an assistant. - **Show quote**: This is a key feature of RAGFlow and enabled by default. RAGFlow does not work like a black box. Instead, it clearly shows the sources of information that its responses are based on. - Select the corresponding datasets. You can select one or multiple datasets, but ensure that they use the same embedding model, otherwise an error would occur. -3. Update **Prompt engine**: +3. Update Prompt-specific settings: - In **System**, you fill in the prompts for your LLM, you can also leave the default prompt as-is for the beginning. - **Similarity threshold** sets the similarity "bar" for each chunk of text. The default is 0.2. Text chunks with lower similarity scores are filtered out of the final response. - - **Keyword similarity weight** is set to 0.7 by default. RAGFlow uses a hybrid score system to evaluate the relevance of different text chunks. This value sets the weight assigned to the keyword similarity component in the hybrid score. - - If **Rerank model** is left empty, the hybrid score system uses keyword similarity and vector similarity, and the default weight assigned to the vector similarity component is 1-0.7=0.3. + - **Vector similarity weight** is set to 0.3 by default. RAGFlow uses a hybrid score system to evaluate the relevance of different text chunks. This value sets the weight assigned to the vector similarity component in the hybrid score. + - If **Rerank model** is left empty, the hybrid score system uses keyword similarity and vector similarity, and the default weight assigned to the keyword similarity component is 1-0.3=0.7. - If **Rerank model** is selected, the hybrid score system uses keyword similarity and reranker score, and the default weight assigned to the reranker score is 1-0.7=0.3. - **Top N** determines the *maximum* number of chunks to feed to the LLM. In other words, even if more chunks are retrieved, only the top N chunks are provided as input. - **Multi-turn optimization** enhances user queries using existing context in a multi-round conversation. It is enabled by default. When enabled, it will consume additional LLM tokens and significantly increase the time to generate answers. @@ -52,10 +52,10 @@ You start an AI conversation by creating an assistant. - HTTP method [Converse with chat assistant](../../references/http_api_reference.md#converse-with-chat-assistant), or - Python method [Converse with chat assistant](../../references/python_api_reference.md#converse-with-chat-assistant). -4. Update **Model Setting**: +4. Update Model-specific Settings: - In **Model**: you select the chat model. Though you have selected the default chat model in **System Model Settings**, RAGFlow allows you to choose an alternative chat model for your dialogue. - - **Freedom**: A shortcut to **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty** settings, indicating the freedom level of the model. From **Improvise**, **Precise**, to **Balance**, each preset configuration corresponds to a unique combination of **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**. + - **Creavity**: A shortcut to **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty** settings, indicating the freedom level of the model. From **Improvise**, **Precise**, to **Balance**, each preset configuration corresponds to a unique combination of **Temperature**, **Top P**, **Presence penalty**, and **Frequency penalty**. This parameter has three options: - **Improvise**: Produces more creative responses. - **Precise**: (Default) Produces more conservative responses. diff --git a/docs/guides/dataset/run_retrieval_test.md b/docs/guides/dataset/run_retrieval_test.md index 08ef999cd57..87bd29835c5 100644 --- a/docs/guides/dataset/run_retrieval_test.md +++ b/docs/guides/dataset/run_retrieval_test.md @@ -29,9 +29,9 @@ In contrast, chunks created from [knowledge graph construction](./construct_know This sets the bar for retrieving chunks: chunks with similarities below the threshold will be filtered out. By default, the threshold is set to 0.2. This means that only chunks with hybrid similarity score of 20 or higher will be retrieved. -### Keyword similarity weight +### Vector similarity weight -This sets the weight of keyword similarity in the combined similarity score, whether used with vector cosine similarity or a reranking score. By default, it is set to 0.7, making the weight of the other component 0.3 (1 - 0.7). +This sets the weight of vector similarity in the composite similarity score, whether used with vector cosine similarity or a reranking score. By default, it is set to 0.3, making the weight of the other component 0.7 (1 - 0.3). ### Rerank model From 685114d2539b04c935dd7ee5560a1283b4bff963 Mon Sep 17 00:00:00 2001 From: balibabu Date: Fri, 17 Oct 2025 18:47:33 +0800 Subject: [PATCH 0895/1187] Feat: Display the pipeline on the agent canvas #9869 (#10638) ### What problem does this PR solve? Feat: Display the pipeline on the agent canvas #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/constants/agent.ts | 56 ++++ web/src/hooks/logic-hooks/navigate-hooks.ts | 5 +- web/src/pages/agent/canvas/index.tsx | 18 +- web/src/pages/agent/canvas/node/card.tsx | 13 + .../node/dropdown/accordion-operators.tsx | 196 +++++++++++++ .../node/dropdown/next-step-dropdown.tsx | 237 ++------------- .../node/dropdown/operator-item-list.tsx | 100 +++++++ .../agent/canvas/node/extractor-node.tsx | 18 ++ web/src/pages/agent/canvas/node/file-node.tsx | 62 ++++ web/src/pages/agent/canvas/node/handle.tsx | 7 +- web/src/pages/agent/canvas/node/index.tsx | 3 +- .../pages/agent/canvas/node/node-header.tsx | 4 +- .../pages/agent/canvas/node/parser-node.tsx | 57 ++++ .../pages/agent/canvas/node/splitter-node.tsx | 58 ++++ .../agent/canvas/node/tokenizer-node.tsx | 55 ++++ web/src/pages/agent/canvas/node/toolbar.tsx | 12 +- .../{constant.tsx => constant/index.tsx} | 75 ++--- web/src/pages/agent/constant/pipeline.tsx | 272 ++++++++++++++++++ web/src/pages/agent/context.ts | 10 + .../agent/form-sheet/form-config-map.tsx | 27 ++ .../pages/agent/form/extractor-form/index.tsx | 107 +++++++ .../form/extractor-form/use-switch-prompt.ts | 69 +++++ .../form/hierarchical-merger-form/index.tsx | 191 ++++++++++++ .../form/parser-form/common-form-fields.tsx | 106 +++++++ .../form/parser-form/email-form-fields.tsx | 30 ++ .../form/parser-form/image-form-fields.tsx | 60 ++++ .../pages/agent/form/parser-form/index.tsx | 226 +++++++++++++++ .../pages/agent/form/parser-form/interface.ts | 3 + .../form/parser-form/pdf-form-fields.tsx | 44 +++ .../parser-form/use-set-initial-language.ts | 29 ++ web/src/pages/agent/form/parser-form/utils.ts | 3 + .../form/parser-form/video-form-fields.tsx | 22 ++ .../pages/agent/form/splitter-form/index.tsx | 101 +++++++ .../pages/agent/form/tokenizer-form/index.tsx | 91 ++++++ web/src/pages/agent/hooks/use-add-node.ts | 16 ++ .../pages/agent/hooks/use-build-options.tsx | 19 ++ .../pages/agent/hooks/use-cancel-dataflow.ts | 21 ++ .../pages/agent/hooks/use-connection-drag.ts | 1 + .../pages/agent/hooks/use-download-output.ts | 38 +++ .../agent/hooks/use-fetch-pipeline-log.ts | 56 ++++ web/src/pages/agent/hooks/use-is-pipeline.ts | 10 + web/src/pages/agent/index.tsx | 139 +++++++-- .../pipeline-log-sheet/dataflow-timeline.tsx | 137 +++++++++ .../pages/agent/pipeline-log-sheet/index.tsx | 114 ++++++++ web/src/pages/agent/utils.ts | 11 +- web/src/pages/agents/agent-card.tsx | 9 +- .../data-flow/canvas/node/begin-node.tsx | 28 +- web/src/pages/data-flow/constant.tsx | 93 ------ 48 files changed, 2636 insertions(+), 423 deletions(-) create mode 100644 web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx create mode 100644 web/src/pages/agent/canvas/node/dropdown/operator-item-list.tsx create mode 100644 web/src/pages/agent/canvas/node/extractor-node.tsx create mode 100644 web/src/pages/agent/canvas/node/file-node.tsx create mode 100644 web/src/pages/agent/canvas/node/parser-node.tsx create mode 100644 web/src/pages/agent/canvas/node/splitter-node.tsx create mode 100644 web/src/pages/agent/canvas/node/tokenizer-node.tsx rename web/src/pages/agent/{constant.tsx => constant/index.tsx} (94%) create mode 100644 web/src/pages/agent/constant/pipeline.tsx create mode 100644 web/src/pages/agent/form/extractor-form/index.tsx create mode 100644 web/src/pages/agent/form/extractor-form/use-switch-prompt.ts create mode 100644 web/src/pages/agent/form/hierarchical-merger-form/index.tsx create mode 100644 web/src/pages/agent/form/parser-form/common-form-fields.tsx create mode 100644 web/src/pages/agent/form/parser-form/email-form-fields.tsx create mode 100644 web/src/pages/agent/form/parser-form/image-form-fields.tsx create mode 100644 web/src/pages/agent/form/parser-form/index.tsx create mode 100644 web/src/pages/agent/form/parser-form/interface.ts create mode 100644 web/src/pages/agent/form/parser-form/pdf-form-fields.tsx create mode 100644 web/src/pages/agent/form/parser-form/use-set-initial-language.ts create mode 100644 web/src/pages/agent/form/parser-form/utils.ts create mode 100644 web/src/pages/agent/form/parser-form/video-form-fields.tsx create mode 100644 web/src/pages/agent/form/splitter-form/index.tsx create mode 100644 web/src/pages/agent/form/tokenizer-form/index.tsx create mode 100644 web/src/pages/agent/hooks/use-build-options.tsx create mode 100644 web/src/pages/agent/hooks/use-cancel-dataflow.ts create mode 100644 web/src/pages/agent/hooks/use-download-output.ts create mode 100644 web/src/pages/agent/hooks/use-fetch-pipeline-log.ts create mode 100644 web/src/pages/agent/hooks/use-is-pipeline.ts create mode 100644 web/src/pages/agent/pipeline-log-sheet/dataflow-timeline.tsx create mode 100644 web/src/pages/agent/pipeline-log-sheet/index.tsx diff --git a/web/src/constants/agent.ts b/web/src/constants/agent.ts index 6e388d7a0d9..f6af4f57c65 100644 --- a/web/src/constants/agent.ts +++ b/web/src/constants/agent.ts @@ -53,6 +53,10 @@ export enum AgentCategory { DataflowCanvas = 'dataflow_canvas', } +export enum AgentQuery { + Category = 'category', +} + export enum DataflowOperator { Begin = 'File', Note = 'Note', @@ -62,3 +66,55 @@ export enum DataflowOperator { HierarchicalMerger = 'HierarchicalMerger', Extractor = 'Extractor', } + +export enum Operator { + Begin = 'Begin', + Retrieval = 'Retrieval', + Categorize = 'Categorize', + Message = 'Message', + Relevant = 'Relevant', + RewriteQuestion = 'RewriteQuestion', + KeywordExtract = 'KeywordExtract', + Baidu = 'Baidu', + DuckDuckGo = 'DuckDuckGo', + Wikipedia = 'Wikipedia', + PubMed = 'PubMed', + ArXiv = 'ArXiv', + Google = 'Google', + Bing = 'Bing', + GoogleScholar = 'GoogleScholar', + DeepL = 'DeepL', + GitHub = 'GitHub', + BaiduFanyi = 'BaiduFanyi', + QWeather = 'QWeather', + ExeSQL = 'ExeSQL', + Switch = 'Switch', + WenCai = 'WenCai', + AkShare = 'AkShare', + YahooFinance = 'YahooFinance', + Jin10 = 'Jin10', + TuShare = 'TuShare', + Note = 'Note', + Crawler = 'Crawler', + Invoke = 'Invoke', + Email = 'Email', + Iteration = 'Iteration', + IterationStart = 'IterationItem', + Code = 'CodeExec', + WaitingDialogue = 'WaitingDialogue', + Agent = 'Agent', + Tool = 'Tool', + TavilySearch = 'TavilySearch', + TavilyExtract = 'TavilyExtract', + UserFillUp = 'UserFillUp', + StringTransform = 'StringTransform', + SearXNG = 'SearXNG', + Placeholder = 'Placeholder', + File = 'File', // pipeline + Parser = 'Parser', + Tokenizer = 'Tokenizer', + Splitter = 'Splitter', + HierarchicalMerger = 'HierarchicalMerger', + Extractor = 'Extractor', + Generate = 'Generate', +} diff --git a/web/src/hooks/logic-hooks/navigate-hooks.ts b/web/src/hooks/logic-hooks/navigate-hooks.ts index 042489bac04..92f573010ff 100644 --- a/web/src/hooks/logic-hooks/navigate-hooks.ts +++ b/web/src/hooks/logic-hooks/navigate-hooks.ts @@ -1,3 +1,4 @@ +import { AgentCategory, AgentQuery } from '@/constants/agent'; import { NavigateToDataflowResultProps } from '@/pages/dataflow-result/interface'; import { Routes } from '@/routes'; import { useCallback } from 'react'; @@ -70,8 +71,8 @@ export const useNavigatePage = () => { }, [navigate]); const navigateToAgent = useCallback( - (id: string) => () => { - navigate(`${Routes.Agent}/${id}`); + (id: string, category?: AgentCategory) => () => { + navigate(`${Routes.Agent}/${id}?${AgentQuery.Category}=${category}`); }, [navigate], ); diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index 0bf32d08f93..ec81bd9e64b 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -56,19 +56,24 @@ import { RagNode } from './node'; import { AgentNode } from './node/agent-node'; import { BeginNode } from './node/begin-node'; import { CategorizeNode } from './node/categorize-node'; -import { InnerNextStepDropdown } from './node/dropdown/next-step-dropdown'; +import { NextStepDropdown } from './node/dropdown/next-step-dropdown'; +import { ExtractorNode } from './node/extractor-node'; +import { FileNode } from './node/file-node'; import { GenerateNode } from './node/generate-node'; import { InvokeNode } from './node/invoke-node'; import { IterationNode, IterationStartNode } from './node/iteration-node'; import { KeywordNode } from './node/keyword-node'; import { MessageNode } from './node/message-node'; import NoteNode from './node/note-node'; +import ParserNode from './node/parser-node'; import { PlaceholderNode } from './node/placeholder-node'; import { RelevantNode } from './node/relevant-node'; import { RetrievalNode } from './node/retrieval-node'; import { RewriteNode } from './node/rewrite-node'; +import { SplitterNode } from './node/splitter-node'; import { SwitchNode } from './node/switch-node'; import { TemplateNode } from './node/template-node'; +import TokenizerNode from './node/tokenizer-node'; import { ToolNode } from './node/tool-node'; export const nodeTypes: NodeTypes = { @@ -91,6 +96,11 @@ export const nodeTypes: NodeTypes = { iterationStartNode: IterationStartNode, agentNode: AgentNode, toolNode: ToolNode, + fileNode: FileNode, + parserNode: ParserNode, + tokenizerNode: TokenizerNode, + splitterNode: SplitterNode, + contextNode: ExtractorNode, }; const edgeTypes = { @@ -194,6 +204,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { getConnectionStartContext, shouldPreventClose, onMove, + nodeId, } = useConnectionDrag( reactFlowInstance, originalOnConnect, @@ -312,7 +323,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { } } > - { removePlaceholderNode(); hideModal(); @@ -320,9 +331,10 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { }} position={dropdownPosition} onNodeCreated={onNodeCreated} + nodeId={nodeId} > - + )} diff --git a/web/src/pages/agent/canvas/node/card.tsx b/web/src/pages/agent/canvas/node/card.tsx index 042ca45e06b..cd5e1ae0993 100644 --- a/web/src/pages/agent/canvas/node/card.tsx +++ b/web/src/pages/agent/canvas/node/card.tsx @@ -17,6 +17,9 @@ import { SelectValue, } from '@/components/ui/select'; +import { cn } from '@/lib/utils'; +import { PropsWithChildren } from 'react'; + export function CardWithForm() { return ( @@ -55,3 +58,13 @@ export function CardWithForm() { ); } + +type LabelCardProps = { + className?: string; +} & PropsWithChildren; + +export function LabelCard({ children, className }: LabelCardProps) { + return ( +
        {children}
        + ); +} diff --git a/web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx b/web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx new file mode 100644 index 00000000000..3ea40f9864e --- /dev/null +++ b/web/src/pages/agent/canvas/node/dropdown/accordion-operators.tsx @@ -0,0 +1,196 @@ +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Operator } from '@/constants/agent'; +import useGraphStore from '@/pages/agent/store'; +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { OperatorItemList } from './operator-item-list'; + +export function AccordionOperators({ + isCustomDropdown = false, + mousePosition, +}: { + isCustomDropdown?: boolean; + mousePosition?: { x: number; y: number }; +}) { + const { t } = useTranslation(); + + return ( + + + + {t('flow.foundation')} + + + + + + + + {t('flow.dialog')} + + + + + + + + {t('flow.flow')} + + + + + + + + {t('flow.dataManipulation')} + + + + + + + + {t('flow.tools')} + + + + + + + ); +} + +// Limit the number of operators of a certain type on the canvas to only one +function useRestrictSingleOperatorOnCanvas() { + const { findNodeByName } = useGraphStore((state) => state); + + const restrictSingleOperatorOnCanvas = useCallback( + (singleOperators: Operator[]) => { + const list: Operator[] = []; + singleOperators.forEach((operator) => { + if (!findNodeByName(operator)) { + list.push(operator); + } + }); + return list; + }, + [findNodeByName], + ); + + return restrictSingleOperatorOnCanvas; +} + +export function PipelineAccordionOperators({ + isCustomDropdown = false, + mousePosition, + nodeId, +}: { + isCustomDropdown?: boolean; + mousePosition?: { x: number; y: number }; + nodeId?: string; +}) { + const restrictSingleOperatorOnCanvas = useRestrictSingleOperatorOnCanvas(); + const { getOperatorTypeFromId } = useGraphStore((state) => state); + + const operators = useMemo(() => { + let list = [ + ...restrictSingleOperatorOnCanvas([Operator.Parser, Operator.Tokenizer]), + ]; + list.push(Operator.Extractor); + return list; + }, [restrictSingleOperatorOnCanvas]); + + const chunkerOperators = useMemo(() => { + return [ + ...restrictSingleOperatorOnCanvas([ + Operator.Splitter, + Operator.HierarchicalMerger, + ]), + ]; + }, [restrictSingleOperatorOnCanvas]); + + const showChunker = useMemo(() => { + return ( + getOperatorTypeFromId(nodeId) !== Operator.Extractor && + chunkerOperators.length > 0 + ); + }, [chunkerOperators.length, getOperatorTypeFromId, nodeId]); + + return ( + <> + + {showChunker && ( + + + Chunker + + + + + + )} + + ); +} diff --git a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx index bc7bf457755..dd2945b779e 100644 --- a/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx +++ b/web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx @@ -1,230 +1,33 @@ -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from '@/components/ui/accordion'; import { DropdownMenu, DropdownMenuContent, - DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/components/ui/tooltip'; import { IModalProps } from '@/interfaces/common'; -import { Operator } from '@/pages/agent/constant'; -import { AgentInstanceContext, HandleContext } from '@/pages/agent/context'; -import OperatorIcon from '@/pages/agent/operator-icon'; -import { Position } from '@xyflow/react'; +import { useIsPipeline } from '@/pages/agent/hooks/use-is-pipeline'; import { t } from 'i18next'; -import { lowerFirst } from 'lodash'; +import { PropsWithChildren, memo, useEffect, useRef } from 'react'; import { - PropsWithChildren, - createContext, - memo, - useContext, - useEffect, - useRef, -} from 'react'; -import { useTranslation } from 'react-i18next'; - -type OperatorItemProps = { - operators: Operator[]; - isCustomDropdown?: boolean; - mousePosition?: { x: number; y: number }; -}; - -const HideModalContext = createContext['showModal']>(() => {}); -const OnNodeCreatedContext = createContext< - ((newNodeId: string) => void) | undefined ->(undefined); - -function OperatorItemList({ - operators, - isCustomDropdown = false, - mousePosition, -}: OperatorItemProps) { - const { addCanvasNode } = useContext(AgentInstanceContext); - const handleContext = useContext(HandleContext); - const hideModal = useContext(HideModalContext); - const onNodeCreated = useContext(OnNodeCreatedContext); - const { t } = useTranslation(); - - const handleClick = - (operator: Operator): React.MouseEventHandler => - (e) => { - const contextData = handleContext || { - nodeId: '', - id: '', - type: 'source' as const, - position: Position.Right, - isFromConnectionDrag: true, - }; - - const mockEvent = mousePosition - ? { - clientX: mousePosition.x, - clientY: mousePosition.y, - } - : e; - - const newNodeId = addCanvasNode(operator, contextData)(mockEvent); - - if (onNodeCreated && newNodeId) { - onNodeCreated(newNodeId); - } - - hideModal?.(); - }; - - const renderOperatorItem = (operator: Operator) => { - const commonContent = ( -
        - - {t(`flow.${lowerFirst(operator)}`)} -
        - ); - - return ( - - - {isCustomDropdown ? ( -
      • {commonContent}
      • - ) : ( - hideModal?.()} - > - - {t(`flow.${lowerFirst(operator)}`)} - - )} -
        - -

        {t(`flow.${lowerFirst(operator)}Description`)}

        -
        -
        - ); - }; - - return
          {operators.map(renderOperatorItem)}
        ; -} - -function AccordionOperators({ - isCustomDropdown = false, - mousePosition, -}: { - isCustomDropdown?: boolean; - mousePosition?: { x: number; y: number }; -}) { - return ( - - - - {t('flow.foundation')} - - - - - - - - {t('flow.dialog')} - - - - - - - - {t('flow.flow')} - - - - - - - - {t('flow.dataManipulation')} - - - - - - - - {t('flow.tools')} - - - - - - - ); -} + AccordionOperators, + PipelineAccordionOperators, +} from './accordion-operators'; +import { HideModalContext, OnNodeCreatedContext } from './operator-item-list'; export function InnerNextStepDropdown({ children, hideModal, position, onNodeCreated, + nodeId, }: PropsWithChildren & IModalProps & { position?: { x: number; y: number }; onNodeCreated?: (newNodeId: string) => void; + nodeId?: string; }) { const dropdownRef = useRef(null); + const isPipeline = useIsPipeline(); useEffect(() => { if (position && hideModal) { @@ -260,10 +63,18 @@ export function InnerNextStepDropdown({ - + {isPipeline ? ( + + ) : ( + + )} @@ -287,7 +98,11 @@ export function InnerNextStepDropdown({ > {t('flow.nextStep')} - + {isPipeline ? ( + + ) : ( + + )} diff --git a/web/src/pages/agent/canvas/node/dropdown/operator-item-list.tsx b/web/src/pages/agent/canvas/node/dropdown/operator-item-list.tsx new file mode 100644 index 00000000000..30480bd5031 --- /dev/null +++ b/web/src/pages/agent/canvas/node/dropdown/operator-item-list.tsx @@ -0,0 +1,100 @@ +import { DropdownMenuItem } from '@/components/ui/dropdown-menu'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { Operator } from '@/constants/agent'; +import { IModalProps } from '@/interfaces/common'; +import { AgentInstanceContext, HandleContext } from '@/pages/agent/context'; +import OperatorIcon from '@/pages/agent/operator-icon'; +import { Position } from '@xyflow/react'; +import { lowerFirst } from 'lodash'; +import { createContext, useContext } from 'react'; +import { useTranslation } from 'react-i18next'; + +export type OperatorItemProps = { + operators: Operator[]; + isCustomDropdown?: boolean; + mousePosition?: { x: number; y: number }; +}; + +export const HideModalContext = createContext['showModal']>( + () => {}, +); +export const OnNodeCreatedContext = createContext< + ((newNodeId: string) => void) | undefined +>(undefined); + +export function OperatorItemList({ + operators, + isCustomDropdown = false, + mousePosition, +}: OperatorItemProps) { + const { addCanvasNode } = useContext(AgentInstanceContext); + const handleContext = useContext(HandleContext); + const hideModal = useContext(HideModalContext); + const onNodeCreated = useContext(OnNodeCreatedContext); + const { t } = useTranslation(); + + const handleClick = + (operator: Operator): React.MouseEventHandler => + (e) => { + const contextData = handleContext || { + nodeId: '', + id: '', + type: 'source' as const, + position: Position.Right, + isFromConnectionDrag: true, + }; + + const mockEvent = mousePosition + ? { + clientX: mousePosition.x, + clientY: mousePosition.y, + } + : e; + + const newNodeId = addCanvasNode(operator, contextData)(mockEvent); + + if (onNodeCreated && newNodeId) { + onNodeCreated(newNodeId); + } + + hideModal?.(); + }; + + const renderOperatorItem = (operator: Operator) => { + const commonContent = ( +
        + + {t(`flow.${lowerFirst(operator)}`)} +
        + ); + + return ( + + + {isCustomDropdown ? ( +
      • {commonContent}
      • + ) : ( + hideModal?.()} + > + + {t(`flow.${lowerFirst(operator)}`)} + + )} +
        + +

        {t(`flow.${lowerFirst(operator)}Description`)}

        +
        +
        + ); + }; + + return
          {operators.map(renderOperatorItem)}
        ; +} diff --git a/web/src/pages/agent/canvas/node/extractor-node.tsx b/web/src/pages/agent/canvas/node/extractor-node.tsx new file mode 100644 index 00000000000..8b2e034836a --- /dev/null +++ b/web/src/pages/agent/canvas/node/extractor-node.tsx @@ -0,0 +1,18 @@ +import LLMLabel from '@/components/llm-select/llm-label'; +import { IRagNode } from '@/interfaces/database/agent'; +import { NodeProps } from '@xyflow/react'; +import { get } from 'lodash'; +import { LabelCard } from './card'; +import { RagNode } from './index'; + +export function ExtractorNode({ ...props }: NodeProps) { + const { data } = props; + + return ( + + + + + + ); +} diff --git a/web/src/pages/agent/canvas/node/file-node.tsx b/web/src/pages/agent/canvas/node/file-node.tsx new file mode 100644 index 00000000000..41e0b250718 --- /dev/null +++ b/web/src/pages/agent/canvas/node/file-node.tsx @@ -0,0 +1,62 @@ +import { IBeginNode } from '@/interfaces/database/flow'; +import { cn } from '@/lib/utils'; +import { NodeProps, Position } from '@xyflow/react'; +import get from 'lodash/get'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + BeginQueryType, + BeginQueryTypeIconMap, + NodeHandleId, + Operator, +} from '../../constant'; +import { BeginQuery } from '../../interface'; +import OperatorIcon from '../../operator-icon'; +import { CommonHandle } from './handle'; +import { RightHandleStyle } from './handle-icon'; +import styles from './index.less'; +import { NodeWrapper } from './node-wrapper'; + +// TODO: do not allow other nodes to connect to this node +function InnerFileNode({ data, id, selected }: NodeProps) { + const { t } = useTranslation(); + const inputs: Record = get(data, 'form.inputs', {}); + + return ( + + + +
        + +
        + {t(`dataflow.begin`)} +
        +
        +
        + {Object.entries(inputs).map(([key, val], idx) => { + const Icon = BeginQueryTypeIconMap[val.type as BeginQueryType]; + return ( +
        + + + {val.name} + {val.optional ? 'Yes' : 'No'} +
        + ); + })} +
        +
        + ); +} + +export const FileNode = memo(InnerFileNode); diff --git a/web/src/pages/agent/canvas/node/handle.tsx b/web/src/pages/agent/canvas/node/handle.tsx index 43ee94bc8c7..2f7530fd64d 100644 --- a/web/src/pages/agent/canvas/node/handle.tsx +++ b/web/src/pages/agent/canvas/node/handle.tsx @@ -6,7 +6,7 @@ import { useMemo } from 'react'; import { NodeHandleId } from '../../constant'; import { HandleContext } from '../../context'; import { useDropdownManager } from '../context'; -import { InnerNextStepDropdown } from './dropdown/next-step-dropdown'; +import { NextStepDropdown } from './dropdown/next-step-dropdown'; export function CommonHandle({ className, @@ -50,14 +50,15 @@ export function CommonHandle({ > {visible && ( - { hideModal(); clearActiveDropdown(); }} > - + )} diff --git a/web/src/pages/agent/canvas/node/index.tsx b/web/src/pages/agent/canvas/node/index.tsx index ccd00b9cab4..953bb6daaf5 100644 --- a/web/src/pages/agent/canvas/node/index.tsx +++ b/web/src/pages/agent/canvas/node/index.tsx @@ -2,7 +2,7 @@ import { IRagNode } from '@/interfaces/database/flow'; import { NodeProps, Position } from '@xyflow/react'; import { memo } from 'react'; import { NodeHandleId } from '../../constant'; -import { needsSingleStepDebugging } from '../../utils'; +import { needsSingleStepDebugging, showCopyIcon } from '../../utils'; import { CommonHandle, LeftEndHandle } from './handle'; import { RightHandleStyle } from './handle-icon'; import NodeHeader from './node-header'; @@ -21,6 +21,7 @@ function InnerRagNode({ id={id} label={data.label} showRun={needsSingleStepDebugging(data.label)} + showCopy={showCopyIcon(data.label)} > diff --git a/web/src/pages/agent/canvas/node/node-header.tsx b/web/src/pages/agent/canvas/node/node-header.tsx index cada3deee47..de5b222edef 100644 --- a/web/src/pages/agent/canvas/node/node-header.tsx +++ b/web/src/pages/agent/canvas/node/node-header.tsx @@ -9,6 +9,7 @@ interface IProps { gap?: number; className?: string; wrapperClassName?: string; + icon?: React.ReactNode; } const InnerNodeHeader = ({ @@ -16,11 +17,12 @@ const InnerNodeHeader = ({ name, className, wrapperClassName, + icon, }: IProps) => { return (
        - + {icon || } {name} diff --git a/web/src/pages/agent/canvas/node/parser-node.tsx b/web/src/pages/agent/canvas/node/parser-node.tsx new file mode 100644 index 00000000000..15539d0b889 --- /dev/null +++ b/web/src/pages/agent/canvas/node/parser-node.tsx @@ -0,0 +1,57 @@ +import { NodeCollapsible } from '@/components/collapse'; +import { BaseNode } from '@/interfaces/database/agent'; +import { NodeProps, Position } from '@xyflow/react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { NodeHandleId } from '../../constant'; +import { ParserFormSchemaType } from '../../form/parser-form'; +import { LabelCard } from './card'; +import { CommonHandle } from './handle'; +import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; + +function ParserNode({ + id, + data, + isConnectable = true, + selected, +}: NodeProps>) { + const { t } = useTranslation(); + return ( + + + + + + + {(x, idx) => ( + + Parser {idx + 1} + {t(`dataflow.fileFormatOptions.${x.fileFormat}`)} + + )} + + + ); +} + +export default memo(ParserNode); diff --git a/web/src/pages/agent/canvas/node/splitter-node.tsx b/web/src/pages/agent/canvas/node/splitter-node.tsx new file mode 100644 index 00000000000..5797bd5e3b1 --- /dev/null +++ b/web/src/pages/agent/canvas/node/splitter-node.tsx @@ -0,0 +1,58 @@ +import { IRagNode } from '@/interfaces/database/flow'; +import { NodeProps, Position } from '@xyflow/react'; +import { PropsWithChildren, memo } from 'react'; +import { NodeHandleId, Operator } from '../../constant'; +import OperatorIcon from '../../operator-icon'; +import { LabelCard } from './card'; +import { CommonHandle } from './handle'; +import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; +import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; + +type RagNodeProps = NodeProps & PropsWithChildren; +function InnerSplitterNode({ + id, + data, + isConnectable = true, + selected, +}: RagNodeProps) { + return ( + + + + + } + > + {data.name} + + + ); +} + +export const SplitterNode = memo(InnerSplitterNode); diff --git a/web/src/pages/agent/canvas/node/tokenizer-node.tsx b/web/src/pages/agent/canvas/node/tokenizer-node.tsx new file mode 100644 index 00000000000..20b261bf4b3 --- /dev/null +++ b/web/src/pages/agent/canvas/node/tokenizer-node.tsx @@ -0,0 +1,55 @@ +import { BaseNode } from '@/interfaces/database/agent'; +import { NodeProps, Position } from '@xyflow/react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { NodeHandleId } from '../../constant'; +import { TokenizerFormSchemaType } from '../../form/tokenizer-form'; +import { LabelCard } from './card'; +import { CommonHandle } from './handle'; +import { LeftHandleStyle } from './handle-icon'; +import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; + +function TokenizerNode({ + id, + data, + isConnectable = true, + selected, +}: NodeProps>) { + const { t } = useTranslation(); + + return ( + + + + + + + {t('dataflow.searchMethod')} + +
          + {data.form?.search_method.map((x) => ( +
        • {t(`dataflow.tokenizerSearchMethodOptions.${x}`)}
        • + ))} +
        +
        +
        +
        + ); +} + +export default memo(TokenizerNode); diff --git a/web/src/pages/agent/canvas/node/toolbar.tsx b/web/src/pages/agent/canvas/node/toolbar.tsx index f36cfe2afd1..565378575ac 100644 --- a/web/src/pages/agent/canvas/node/toolbar.tsx +++ b/web/src/pages/agent/canvas/node/toolbar.tsx @@ -28,6 +28,7 @@ type ToolBarProps = { label: string; id: string; showRun?: boolean; + showCopy?: boolean; } & PropsWithChildren; export function ToolBar({ @@ -36,6 +37,7 @@ export function ToolBar({ label, id, showRun = true, + showCopy = true, }: ToolBarProps) { const deleteNodeById = useGraphStore((store) => store.deleteNodeById); const deleteIterationNodeById = useGraphStore( @@ -74,10 +76,12 @@ export function ToolBar({ - )}{' '} - - - + )} + {showCopy && ( + + + + )} diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant/index.tsx similarity index 94% rename from web/src/pages/agent/constant.tsx rename to web/src/pages/agent/constant/index.tsx index 35a67cae4b7..c444b830e4a 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant/index.tsx @@ -6,9 +6,13 @@ import { AgentGlobals, AgentGlobalsSysQueryWithBrace, CodeTemplateStrMap, + Operator, ProgrammingLanguage, initialLlmBaseValues, } from '@/constants/agent'; +export { Operator } from '@/constants/agent'; + +export * from './pipeline'; export enum AgentDialogueMode { Conversational = 'conversational', @@ -43,51 +47,6 @@ import { export const BeginId = 'begin'; -export enum Operator { - Begin = 'Begin', - Retrieval = 'Retrieval', - Categorize = 'Categorize', - Message = 'Message', - Relevant = 'Relevant', - RewriteQuestion = 'RewriteQuestion', - KeywordExtract = 'KeywordExtract', - Baidu = 'Baidu', - DuckDuckGo = 'DuckDuckGo', - Wikipedia = 'Wikipedia', - PubMed = 'PubMed', - ArXiv = 'ArXiv', - Google = 'Google', - Bing = 'Bing', - GoogleScholar = 'GoogleScholar', - DeepL = 'DeepL', - GitHub = 'GitHub', - BaiduFanyi = 'BaiduFanyi', - QWeather = 'QWeather', - ExeSQL = 'ExeSQL', - Switch = 'Switch', - WenCai = 'WenCai', - AkShare = 'AkShare', - YahooFinance = 'YahooFinance', - Jin10 = 'Jin10', - TuShare = 'TuShare', - Note = 'Note', - Crawler = 'Crawler', - Invoke = 'Invoke', - Email = 'Email', - Iteration = 'Iteration', - IterationStart = 'IterationItem', - Code = 'CodeExec', - WaitingDialogue = 'WaitingDialogue', - Agent = 'Agent', - Tool = 'Tool', - TavilySearch = 'TavilySearch', - TavilyExtract = 'TavilyExtract', - UserFillUp = 'UserFillUp', - StringTransform = 'StringTransform', - SearXNG = 'SearXNG', - Placeholder = 'Placeholder', -} - export const SwitchLogicOperatorOptions = ['and', 'or']; export const CommonOperatorList = Object.values(Operator).filter( @@ -833,6 +792,11 @@ export const RestrictedUpstreamMap = { [Operator.UserFillUp]: [Operator.Begin], [Operator.Tool]: [Operator.Begin], [Operator.Placeholder]: [Operator.Begin], + [Operator.Parser]: [Operator.Begin], + [Operator.Splitter]: [Operator.Begin], + [Operator.HierarchicalMerger]: [Operator.Begin], + [Operator.Tokenizer]: [Operator.Begin], + [Operator.Extractor]: [Operator.Begin], }; export const NodeMap = { @@ -878,6 +842,12 @@ export const NodeMap = { [Operator.StringTransform]: 'ragNode', [Operator.TavilyExtract]: 'ragNode', [Operator.Placeholder]: 'placeholderNode', + [Operator.File]: 'fileNode', + [Operator.Parser]: 'parserNode', + [Operator.Tokenizer]: 'tokenizerNode', + [Operator.Splitter]: 'splitterNode', + [Operator.HierarchicalMerger]: 'splitterNode', + [Operator.Extractor]: 'contextNode', }; export enum BeginQueryType { @@ -906,6 +876,21 @@ export const NoDebugOperatorsList = [ Operator.Iteration, Operator.UserFillUp, Operator.IterationStart, + Operator.File, + Operator.Parser, + Operator.Tokenizer, + Operator.Splitter, + Operator.HierarchicalMerger, + Operator.Extractor, +]; + +export const NoCopyOperatorsList = [ + Operator.File, + Operator.Parser, + Operator.Tokenizer, + Operator.Splitter, + Operator.HierarchicalMerger, + Operator.Extractor, ]; export enum NodeHandleId { diff --git a/web/src/pages/agent/constant/pipeline.tsx b/web/src/pages/agent/constant/pipeline.tsx new file mode 100644 index 00000000000..78d4b3ac996 --- /dev/null +++ b/web/src/pages/agent/constant/pipeline.tsx @@ -0,0 +1,272 @@ +import { ParseDocumentType } from '@/components/layout-recognize-form-field'; +import { + initialLlmBaseValues, + DataflowOperator as Operator, +} from '@/constants/agent'; + +export enum FileType { + PDF = 'pdf', + Spreadsheet = 'spreadsheet', + Image = 'image', + Email = 'email', + TextMarkdown = 'text&markdown', + Docx = 'word', + PowerPoint = 'slides', + Video = 'video', + Audio = 'audio', +} + +export enum PdfOutputFormat { + Json = 'json', + Markdown = 'markdown', +} + +export enum SpreadsheetOutputFormat { + Json = 'json', + Html = 'html', +} + +export enum ImageOutputFormat { + Text = 'text', +} + +export enum EmailOutputFormat { + Json = 'json', + Text = 'text', +} + +export enum TextMarkdownOutputFormat { + Text = 'text', +} + +export enum DocxOutputFormat { + Markdown = 'markdown', + Json = 'json', +} + +export enum PptOutputFormat { + Json = 'json', +} + +export enum VideoOutputFormat { + Json = 'json', +} + +export enum AudioOutputFormat { + Text = 'text', +} + +export const OutputFormatMap = { + [FileType.PDF]: PdfOutputFormat, + [FileType.Spreadsheet]: SpreadsheetOutputFormat, + [FileType.Image]: ImageOutputFormat, + [FileType.Email]: EmailOutputFormat, + [FileType.TextMarkdown]: TextMarkdownOutputFormat, + [FileType.Docx]: DocxOutputFormat, + [FileType.PowerPoint]: PptOutputFormat, + [FileType.Video]: VideoOutputFormat, + [FileType.Audio]: AudioOutputFormat, +}; + +export const InitialOutputFormatMap = { + [FileType.PDF]: PdfOutputFormat.Json, + [FileType.Spreadsheet]: SpreadsheetOutputFormat.Html, + [FileType.Image]: ImageOutputFormat.Text, + [FileType.Email]: EmailOutputFormat.Text, + [FileType.TextMarkdown]: TextMarkdownOutputFormat.Text, + [FileType.Docx]: DocxOutputFormat.Json, + [FileType.PowerPoint]: PptOutputFormat.Json, + [FileType.Video]: VideoOutputFormat.Json, + [FileType.Audio]: AudioOutputFormat.Text, +}; + +export enum ContextGeneratorFieldName { + Summary = 'summary', + Keywords = 'keywords', + Questions = 'questions', + Metadata = 'metadata', +} + +export const FileId = 'File'; // BeginId + +export enum TokenizerSearchMethod { + Embedding = 'embedding', + FullText = 'full_text', +} + +export enum ImageParseMethod { + OCR = 'ocr', +} + +export enum TokenizerFields { + Text = 'text', + Questions = 'questions', + Summary = 'summary', +} + +export enum ParserFields { + From = 'from', + To = 'to', + Cc = 'cc', + Bcc = 'bcc', + Date = 'date', + Subject = 'subject', + Body = 'body', + Attachments = 'attachments', +} + +// initialBeginValues +export const initialFileValues = { + outputs: { + name: { + type: 'string', + value: '', + }, + file: { + type: 'Object', + value: {}, + }, + }, +}; + +export const initialTokenizerValues = { + search_method: [ + TokenizerSearchMethod.Embedding, + TokenizerSearchMethod.FullText, + ], + filename_embd_weight: 0.1, + fields: TokenizerFields.Text, + outputs: {}, +}; + +export enum StringTransformMethod { + Merge = 'merge', + Split = 'split', +} + +export enum StringTransformDelimiter { + Comma = ',', + Semicolon = ';', + Period = '.', + LineBreak = '\n', + Tab = '\t', + Space = ' ', +} + +export const initialParserValues = { + outputs: { + markdown: { type: 'string', value: '' }, + text: { type: 'string', value: '' }, + html: { type: 'string', value: '' }, + json: { type: 'Array', value: [] }, + }, + setups: [ + { + fileFormat: FileType.PDF, + output_format: PdfOutputFormat.Json, + parse_method: ParseDocumentType.DeepDOC, + }, + { + fileFormat: FileType.Spreadsheet, + output_format: SpreadsheetOutputFormat.Html, + }, + { + fileFormat: FileType.Image, + output_format: ImageOutputFormat.Text, + parse_method: ImageParseMethod.OCR, + system_prompt: '', + }, + { + fileFormat: FileType.Email, + fields: Object.values(ParserFields), + output_format: EmailOutputFormat.Text, + }, + { + fileFormat: FileType.TextMarkdown, + output_format: TextMarkdownOutputFormat.Text, + }, + { + fileFormat: FileType.Docx, + output_format: DocxOutputFormat.Json, + }, + { + fileFormat: FileType.PowerPoint, + output_format: PptOutputFormat.Json, + }, + ], +}; + +export const initialSplitterValues = { + outputs: { + chunks: { type: 'Array', value: [] }, + }, + chunk_token_size: 512, + overlapped_percent: 0, + delimiters: [{ value: '\n' }], +}; + +export enum Hierarchy { + H1 = '1', + H2 = '2', + H3 = '3', + H4 = '4', + H5 = '5', +} + +export const initialHierarchicalMergerValues = { + outputs: { + chunks: { type: 'Array', value: [] }, + }, + hierarchy: Hierarchy.H3, + levels: [ + { expressions: [{ expression: '^#[^#]' }] }, + { expressions: [{ expression: '^##[^#]' }] }, + { expressions: [{ expression: '^###[^#]' }] }, + { expressions: [{ expression: '^####[^#]' }] }, + ], +}; + +export const initialExtractorValues = { + ...initialLlmBaseValues, + field_name: ContextGeneratorFieldName.Summary, + outputs: { + chunks: { type: 'Array', value: [] }, + }, +}; + +export const NoDebugOperatorsList = [Operator.Begin]; + +export const FileTypeSuffixMap = { + [FileType.PDF]: ['pdf'], + [FileType.Spreadsheet]: ['xls', 'xlsx', 'csv'], + [FileType.Image]: ['jpg', 'jpeg', 'png', 'gif'], + [FileType.Email]: ['eml', 'msg'], + [FileType.TextMarkdown]: ['md', 'markdown', 'mdx', 'txt'], + [FileType.Docx]: ['doc', 'docx'], + [FileType.PowerPoint]: ['pptx'], + [FileType.Video]: [], + [FileType.Audio]: [ + 'da', + 'wave', + 'wav', + 'mp3', + 'aac', + 'flac', + 'ogg', + 'aiff', + 'au', + 'midi', + 'wma', + 'realaudio', + 'vqf', + 'oggvorbis', + 'ape', + ], +}; + +export const SingleOperators = [ + Operator.Tokenizer, + Operator.Splitter, + Operator.HierarchicalMerger, + Operator.Parser, +]; diff --git a/web/src/pages/agent/context.ts b/web/src/pages/agent/context.ts index 6839554d3e1..9d575cc3c35 100644 --- a/web/src/pages/agent/context.ts +++ b/web/src/pages/agent/context.ts @@ -48,3 +48,13 @@ export type HandleContextType = { export const HandleContext = createContext( {} as HandleContextType, ); + +export type PipelineLogContextType = { + messageId: string; + setMessageId: (messageId: string) => void; + setUploadedFileData: (data: Record) => void; +}; + +export const PipelineLogContext = createContext( + {} as PipelineLogContextType, +); diff --git a/web/src/pages/agent/form-sheet/form-config-map.tsx b/web/src/pages/agent/form-sheet/form-config-map.tsx index 15ca5ffa344..f99e2eed78a 100644 --- a/web/src/pages/agent/form-sheet/form-config-map.tsx +++ b/web/src/pages/agent/form-sheet/form-config-map.tsx @@ -13,25 +13,30 @@ import DeepLForm from '../form/deepl-form'; import DuckDuckGoForm from '../form/duckduckgo-form'; import EmailForm from '../form/email-form'; import ExeSQLForm from '../form/exesql-form'; +import ExtractorForm from '../form/extractor-form'; import GithubForm from '../form/github-form'; import GoogleForm from '../form/google-form'; import GoogleScholarForm from '../form/google-scholar-form'; +import HierarchicalMergerForm from '../form/hierarchical-merger-form'; import InvokeForm from '../form/invoke-form'; import IterationForm from '../form/iteration-form'; import IterationStartForm from '../form/iteration-start-from'; import Jin10Form from '../form/jin10-form'; import KeywordExtractForm from '../form/keyword-extract-form'; import MessageForm from '../form/message-form'; +import ParserForm from '../form/parser-form'; import PubMedForm from '../form/pubmed-form'; import QWeatherForm from '../form/qweather-form'; import RelevantForm from '../form/relevant-form'; import RetrievalForm from '../form/retrieval-form/next'; import RewriteQuestionForm from '../form/rewrite-question-form'; import SearXNGForm from '../form/searxng-form'; +import SplitterForm from '../form/splitter-form'; import StringTransformForm from '../form/string-transform-form'; import SwitchForm from '../form/switch-form'; import TavilyExtractForm from '../form/tavily-extract-form'; import TavilyForm from '../form/tavily-form'; +import TokenizerForm from '../form/tokenizer-form'; import ToolForm from '../form/tool-form'; import TuShareForm from '../form/tushare-form'; import UserFillUpForm from '../form/user-fill-up-form'; @@ -163,4 +168,26 @@ export const FormConfigMap = { [Operator.TavilyExtract]: { component: TavilyExtractForm, }, + [Operator.Placeholder]: { + component: () => <>, + }, + // pipeline + [Operator.File]: { + component: () => <>, + }, + [Operator.Parser]: { + component: ParserForm, + }, + [Operator.Tokenizer]: { + component: TokenizerForm, + }, + [Operator.Splitter]: { + component: SplitterForm, + }, + [Operator.HierarchicalMerger]: { + component: HierarchicalMergerForm, + }, + [Operator.Extractor]: { + component: ExtractorForm, + }, }; diff --git a/web/src/pages/agent/form/extractor-form/index.tsx b/web/src/pages/agent/form/extractor-form/index.tsx new file mode 100644 index 00000000000..c178e8a465b --- /dev/null +++ b/web/src/pages/agent/form/extractor-form/index.tsx @@ -0,0 +1,107 @@ +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import { LargeModelFormField } from '@/components/large-model-form-field'; +import { LlmSettingSchema } from '@/components/llm-setting-items/next'; +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Form } from '@/components/ui/form'; +import { PromptEditor } from '@/pages/agent/form/components/prompt-editor'; +import { buildOptions } from '@/utils/form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { memo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { + ContextGeneratorFieldName, + initialExtractorValues, +} from '../../constant/pipeline'; +import { useBuildNodeOutputOptions } from '../../hooks/use-build-options'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; +import { useSwitchPrompt } from './use-switch-prompt'; + +export const FormSchema = z.object({ + field_name: z.string(), + sys_prompt: z.string(), + prompts: z.string().optional(), + ...LlmSettingSchema, +}); + +export type ExtractorFormSchemaType = z.infer; + +const outputList = buildOutputList(initialExtractorValues.outputs); + +const ExtractorForm = ({ node }: INextOperatorForm) => { + const defaultValues = useFormValues(initialExtractorValues, node); + const { t } = useTranslation(); + + const form = useForm({ + defaultValues, + resolver: zodResolver(FormSchema), + // mode: 'onChange', + }); + + const promptOptions = useBuildNodeOutputOptions(node?.id); + + const options = buildOptions(ContextGeneratorFieldName, t, 'dataflow'); + + const { + handleFieldNameChange, + confirmSwitch, + hideModal, + visible, + cancelSwitch, + } = useSwitchPrompt(form); + + useWatchFormChange(node?.id, form); + + return ( +
        + + + + {(field) => ( + { + field.onChange(value); + handleFieldNameChange(value); + }} + value={field.value} + placeholder={t('dataFlowPlaceholder')} + options={options} + > + )} + + + + + + + + + + {visible && ( + + )} +
        + ); +}; + +export default memo(ExtractorForm); diff --git a/web/src/pages/agent/form/extractor-form/use-switch-prompt.ts b/web/src/pages/agent/form/extractor-form/use-switch-prompt.ts new file mode 100644 index 00000000000..4efb2c4727e --- /dev/null +++ b/web/src/pages/agent/form/extractor-form/use-switch-prompt.ts @@ -0,0 +1,69 @@ +import { LlmSettingSchema } from '@/components/llm-setting-items/next'; +import { useSetModalState } from '@/hooks/common-hooks'; +import { useCallback, useRef } from 'react'; +import { UseFormReturn } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +export const FormSchema = z.object({ + field_name: z.string(), + sys_prompt: z.string(), + prompts: z.string().optional(), + ...LlmSettingSchema, +}); + +export type ExtractorFormSchemaType = z.infer; + +export function useSwitchPrompt(form: UseFormReturn) { + const { visible, showModal, hideModal } = useSetModalState(); + const { t } = useTranslation(); + const previousFieldNames = useRef([form.getValues('field_name')]); + + const setPromptValue = useCallback( + (field: keyof ExtractorFormSchemaType, key: string, value: string) => { + form.setValue(field, t(`dataflow.prompts.${key}.${value}`), { + shouldDirty: true, + shouldValidate: true, + }); + }, + [form, t], + ); + + const handleFieldNameChange = useCallback( + (value: string) => { + if (value) { + const names = previousFieldNames.current; + if (names.length > 1) { + names.shift(); + } + names.push(value); + showModal(); + } + }, + [showModal], + ); + + const confirmSwitch = useCallback(() => { + const value = form.getValues('field_name'); + setPromptValue('sys_prompt', 'system', value); + setPromptValue('prompts', 'user', value); + }, [form, setPromptValue]); + + const cancelSwitch = useCallback(() => { + const previousValue = previousFieldNames.current.at(-2); + if (previousValue) { + form.setValue('field_name', previousValue, { + shouldDirty: true, + shouldValidate: true, + }); + } + }, [form]); + + return { + handleFieldNameChange, + confirmSwitch, + hideModal, + visible, + cancelSwitch, + }; +} diff --git a/web/src/pages/agent/form/hierarchical-merger-form/index.tsx b/web/src/pages/agent/form/hierarchical-merger-form/index.tsx new file mode 100644 index 00000000000..6235307922a --- /dev/null +++ b/web/src/pages/agent/form/hierarchical-merger-form/index.tsx @@ -0,0 +1,191 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { BlockButton, Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader } from '@/components/ui/card'; +import { Form, FormLabel } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Plus, Trash2 } from 'lucide-react'; +import { memo } from 'react'; +import { useFieldArray, useForm, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { + Hierarchy, + initialHierarchicalMergerValues, +} from '../../constant/pipeline'; +import { useFormValues } from '../../hooks/use-form-values'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; +import { INextOperatorForm } from '../../interface'; +import { buildOutputList } from '../../utils/build-output-list'; +import { FormWrapper } from '../components/form-wrapper'; +import { Output } from '../components/output'; + +const outputList = buildOutputList(initialHierarchicalMergerValues.outputs); + +const HierarchyOptions = [ + { label: 'H1', value: Hierarchy.H1 }, + { label: 'H2', value: Hierarchy.H2 }, + { label: 'H3', value: Hierarchy.H3 }, + { label: 'H4', value: Hierarchy.H4 }, + { label: 'H5', value: Hierarchy.H5 }, +]; + +export const FormSchema = z.object({ + hierarchy: z.string(), + levels: z.array( + z.object({ + expressions: z.array( + z.object({ + expression: z.string().refine( + (val) => { + try { + // Try converting the string to a RegExp + new RegExp(val); + return true; + } catch { + return false; + } + }, + { + message: 'Must be a valid regular expression string', + }, + ), + }), + ), + }), + ), +}); + +export type HierarchicalMergerFormSchemaType = z.infer; + +type RegularExpressionsProps = { + index: number; + parentName: string; + removeParent: (index: number) => void; + isLatest: boolean; +}; + +export function RegularExpressions({ + index, + parentName, + isLatest, + removeParent, +}: RegularExpressionsProps) { + const { t } = useTranslation(); + const form = useFormContext(); + + const name = `${parentName}.${index}.expressions`; + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( + + + H{index + 1} + {isLatest && ( + + )} + + + + {t('dataflow.regularExpressions')} + +
        + {fields.map((field, index) => ( +
        +
        + + + +
        + {index === 0 ? ( + + ) : ( + + )} +
        + ))} +
        +
        +
        + ); +} + +const HierarchicalMergerForm = ({ node }: INextOperatorForm) => { + const { t } = useTranslation(); + const defaultValues = useFormValues(initialHierarchicalMergerValues, node); + + const form = useForm({ + defaultValues, + resolver: zodResolver(FormSchema), + mode: 'onChange', + }); + + const name = 'levels'; + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + useWatchFormChange(node?.id, form); + + return ( +
        + + + + + {fields.map((field, index) => ( +
        +
        + +
        +
        + ))} + {fields.length < 5 && ( + append({ expressions: [{ expression: '' }] })} + > + {t('common.add')} + + )} +
        +
        + +
        +
        + ); +}; + +export default memo(HierarchicalMergerForm); diff --git a/web/src/pages/agent/form/parser-form/common-form-fields.tsx b/web/src/pages/agent/form/parser-form/common-form-fields.tsx new file mode 100644 index 00000000000..a44b22a836b --- /dev/null +++ b/web/src/pages/agent/form/parser-form/common-form-fields.tsx @@ -0,0 +1,106 @@ +import { crossLanguageOptions } from '@/components/cross-language-form-field'; +import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; +import { + LLMFormField, + LLMFormFieldProps, +} from '@/components/llm-setting-items/llm-form-field'; +import { + SelectWithSearch, + SelectWithSearchFlagOptionType, +} from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { upperCase, upperFirst } from 'lodash'; +import { useTranslation } from 'react-i18next'; +import { + FileType, + OutputFormatMap, + SpreadsheetOutputFormat, +} from '../../constant/pipeline'; +import { CommonProps } from './interface'; +import { buildFieldNameWithPrefix } from './utils'; + +const UppercaseFields = [ + SpreadsheetOutputFormat.Html, + SpreadsheetOutputFormat.Json, +]; + +function buildOutputOptionsFormatMap() { + return Object.entries(OutputFormatMap).reduce< + Record + >((pre, [key, value]) => { + pre[key] = Object.values(value).map((v) => ({ + label: UppercaseFields.some((x) => x === v) + ? upperCase(v) + : upperFirst(v), + value: v, + })); + return pre; + }, {}); +} + +export type OutputFormatFormFieldProps = CommonProps & { + fileType: FileType; +}; + +export function OutputFormatFormField({ + prefix, + fileType, +}: OutputFormatFormFieldProps) { + const { t } = useTranslation(); + return ( + + + + ); +} + +export function ParserMethodFormField({ + prefix, + optionsWithoutLLM, +}: CommonProps & { optionsWithoutLLM?: { value: string; label: string }[] }) { + const { t } = useTranslation(); + return ( + + ); +} + +export function LargeModelFormField({ + prefix, + options, +}: CommonProps & Pick) { + return ( + + ); +} + +export function LanguageFormField({ prefix }: CommonProps) { + const { t } = useTranslation(); + + return ( + + {(field) => ( + + )} + + ); +} diff --git a/web/src/pages/agent/form/parser-form/email-form-fields.tsx b/web/src/pages/agent/form/parser-form/email-form-fields.tsx new file mode 100644 index 00000000000..fef3f1c52d3 --- /dev/null +++ b/web/src/pages/agent/form/parser-form/email-form-fields.tsx @@ -0,0 +1,30 @@ +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { MultiSelect } from '@/components/ui/multi-select'; +import { buildOptions } from '@/utils/form'; +import { useTranslation } from 'react-i18next'; +import { ParserFields } from '../../constant/pipeline'; +import { CommonProps } from './interface'; +import { buildFieldNameWithPrefix } from './utils'; + +const options = buildOptions(ParserFields); + +export function EmailFormFields({ prefix }: CommonProps) { + const { t } = useTranslation(); + return ( + <> + + {(field) => ( + + )} + + + ); +} diff --git a/web/src/pages/agent/form/parser-form/image-form-fields.tsx b/web/src/pages/agent/form/parser-form/image-form-fields.tsx new file mode 100644 index 00000000000..c5dc83a70e0 --- /dev/null +++ b/web/src/pages/agent/form/parser-form/image-form-fields.tsx @@ -0,0 +1,60 @@ +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Textarea } from '@/components/ui/textarea'; +import { buildOptions } from '@/utils/form'; +import { isEmpty } from 'lodash'; +import { useEffect, useMemo } from 'react'; +import { useFormContext, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { ImageParseMethod } from '../../constant/pipeline'; +import { LanguageFormField, ParserMethodFormField } from './common-form-fields'; +import { CommonProps } from './interface'; +import { useSetInitialLanguage } from './use-set-initial-language'; +import { buildFieldNameWithPrefix } from './utils'; + +export function ImageFormFields({ prefix }: CommonProps) { + const { t } = useTranslation(); + const form = useFormContext(); + const options = buildOptions( + ImageParseMethod, + t, + 'dataflow.imageParseMethodOptions', + ); + const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix); + + const parseMethod = useWatch({ + name: parseMethodName, + }); + + const languageShown = useMemo(() => { + return !isEmpty(parseMethod) && parseMethod !== ImageParseMethod.OCR; + }, [parseMethod]); + + useEffect(() => { + if (isEmpty(form.getValues(parseMethodName))) { + form.setValue(parseMethodName, ImageParseMethod.OCR, { + shouldValidate: true, + shouldDirty: true, + }); + } + }, [form, parseMethodName]); + + useSetInitialLanguage({ prefix, languageShown }); + + return ( + <> + + {languageShown && } + {languageShown && ( + + @@ -112,8 +111,9 @@ export default function TestingForm({ disabled={!!!trim(question)} loading={loading} > - {!loading && } + {/* {!loading && } */} {t('knowledgeDetails.testingLabel')} + diff --git a/web/src/pages/next-search/search-setting.tsx b/web/src/pages/next-search/search-setting.tsx index 8a9e136cb66..665d7e49b3c 100644 --- a/web/src/pages/next-search/search-setting.tsx +++ b/web/src/pages/next-search/search-setting.tsx @@ -5,6 +5,7 @@ import { MetadataFilter, MetadataFilterSchema, } from '@/components/metadata-filter'; +import { SimilaritySliderFormField } from '@/components/similarity-slider'; import { Button } from '@/components/ui/button'; import { SingleFormSlider } from '@/components/ui/dual-range-slider'; import { @@ -392,86 +393,11 @@ const SearchSetting: React.FC = ({ )} /> - ( - - - {t('knowledgeDetails.similarityThreshold')} - -
        - - - - - - -
        - -
        - )} - /> - {/* Keyword Similarity Weight */} - ( - - - * - {t('knowledgeDetails.vectorSimilarityWeight')} - -
        - - - - - - -
        - -
        - )} - /> + {/* Rerank Model */} Date: Tue, 28 Oct 2025 19:38:06 +0800 Subject: [PATCH 0989/1187] Add readme in web (#10855) ### What problem does this PR solve? As title ### Type of change - [x] Documentation Update Signed-off-by: Jin Hai --- web/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 web/README.md diff --git a/web/README.md b/web/README.md new file mode 100644 index 00000000000..b1afaed3daf --- /dev/null +++ b/web/README.md @@ -0,0 +1,23 @@ +## Install front-end dependencies + + ```bash + npm install + ``` + +## Launch front-end + + ```bash + npm run dev + ``` + + _The following output confirms a successful launch of the system:_ + + ![](https://github.com/user-attachments/assets/0daf462c-a24d-4496-a66f-92533534e187) + +## Shutdown front-end + + Ctrl + C or + + ```bash + kill -f "umi dev" + ``` \ No newline at end of file From f93be47f51d54f81480a91fb16b489eabba1af6a Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Tue, 28 Oct 2025 19:40:58 +0800 Subject: [PATCH 0990/1187] Remove 'DID YOU KNOW', when start front-end (#10853) ### What problem does this PR solve? Remove 'DID YOU KNOW', when start front-end ### Type of change - [x] Refactoring Signed-off-by: Jin Hai --- web/.env | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/.env b/web/.env index 26f65746c70..a6cbd9ccd3b 100644 --- a/web/.env +++ b/web/.env @@ -1 +1,2 @@ -PORT=9222 \ No newline at end of file +PORT=9222 +DID_YOU_KNOW=none \ No newline at end of file From c3b0ab43e7d6b2bb836daf0ca4241e558099b3a9 Mon Sep 17 00:00:00 2001 From: Zhichang Yu Date: Tue, 28 Oct 2025 21:29:48 +0800 Subject: [PATCH 0991/1187] Fix release.yml --- .github/workflows/release.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52b9c9ab1a2..c34945aff41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,14 +75,6 @@ jobs: # The body field does not support environment variable substitution directly. body_path: release_body.md - - name: Build and push image - run: | - echo ${{ secrets.DOCKERHUB_TOKEN }} | sudo docker login --username infiniflow --password-stdin - sudo docker build --build-arg NEED_MIRROR=1 -t infiniflow/ragflow:${RELEASE_TAG} -f Dockerfile . - sudo docker tag infiniflow/ragflow:${RELEASE_TAG} infiniflow/ragflow:latest - sudo docker push infiniflow/ragflow:${RELEASE_TAG} - sudo docker push infiniflow/ragflow:latest - - name: Build and push ragflow-sdk if: startsWith(github.ref, 'refs/tags/v') run: | @@ -94,3 +86,11 @@ jobs: run: | cd admin/client && uv build twine upload admin/client/dist/* -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} + + - name: Build and push image + run: | + echo ${{ secrets.DOCKERHUB_TOKEN }} | sudo docker login --username infiniflow --password-stdin + sudo docker build --build-arg NEED_MIRROR=1 -t infiniflow/ragflow:${RELEASE_TAG} -f Dockerfile . + sudo docker tag infiniflow/ragflow:${RELEASE_TAG} infiniflow/ragflow:latest + sudo docker push infiniflow/ragflow:${RELEASE_TAG} + sudo docker push infiniflow/ragflow:latest From 2c0035dcea71c89446b7d717f811a8360dd79d8b Mon Sep 17 00:00:00 2001 From: UN1C0DE Date: Tue, 28 Oct 2025 22:25:43 +0800 Subject: [PATCH 0992/1187] Feat: Admin UI (#10857) ### What problem does this PR solve? Add admin UI for RAGFlow ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/.umirc.ts | 7 + web/src/components/ui/scroll-area.tsx | 3 +- web/src/locales/en.ts | 132 +++- web/src/pages/404.jsx | 14 +- .../admin/components/enterprise-feature.tsx | 13 + .../pages/admin/components/theme-switch.tsx | 47 ++ .../admin/forms/change-password-form.tsx | 150 ++++ web/src/pages/admin/forms/email-form.tsx | 102 +++ .../pages/admin/forms/import-excel-form.tsx | 155 ++++ web/src/pages/admin/forms/role-form.tsx | 207 ++++++ web/src/pages/admin/forms/user-form.tsx | 215 ++++++ web/src/pages/admin/index.tsx | 265 +++++++ web/src/pages/admin/layout.tsx | 133 ++++ web/src/pages/admin/monitoring.tsx | 13 + web/src/pages/admin/roles.tsx | 229 ++++++ web/src/pages/admin/service-detail.tsx | 86 +++ web/src/pages/admin/service-status.tsx | 460 ++++++++++++ web/src/pages/admin/user-detail.tsx | 433 +++++++++++ web/src/pages/admin/users.tsx | 691 ++++++++++++++++++ web/src/pages/admin/utils.tsx | 85 +++ web/src/pages/admin/whitelist.tsx | 522 +++++++++++++ web/src/routes.ts | 58 ++ web/src/services/admin-service.ts | 379 ++++++++++ web/src/utils/api.ts | 43 ++ web/src/wrappers/authAdmin.tsx | 9 + 25 files changed, 4442 insertions(+), 9 deletions(-) create mode 100644 web/src/pages/admin/components/enterprise-feature.tsx create mode 100644 web/src/pages/admin/components/theme-switch.tsx create mode 100644 web/src/pages/admin/forms/change-password-form.tsx create mode 100644 web/src/pages/admin/forms/email-form.tsx create mode 100644 web/src/pages/admin/forms/import-excel-form.tsx create mode 100644 web/src/pages/admin/forms/role-form.tsx create mode 100644 web/src/pages/admin/forms/user-form.tsx create mode 100644 web/src/pages/admin/index.tsx create mode 100644 web/src/pages/admin/layout.tsx create mode 100644 web/src/pages/admin/monitoring.tsx create mode 100644 web/src/pages/admin/roles.tsx create mode 100644 web/src/pages/admin/service-detail.tsx create mode 100644 web/src/pages/admin/service-status.tsx create mode 100644 web/src/pages/admin/user-detail.tsx create mode 100644 web/src/pages/admin/users.tsx create mode 100644 web/src/pages/admin/utils.tsx create mode 100644 web/src/pages/admin/whitelist.tsx create mode 100644 web/src/services/admin-service.ts create mode 100644 web/src/wrappers/authAdmin.tsx diff --git a/web/.umirc.ts b/web/.umirc.ts index 7cf7f16b8c6..044ea303672 100644 --- a/web/.umirc.ts +++ b/web/.umirc.ts @@ -38,6 +38,13 @@ export default defineConfig({ { from: 'node_modules/monaco-editor/min/vs/', to: 'dist/vs/' }, ], proxy: [ + { + context: ['/api/v1/admin'], + target: 'http://127.0.0.1:9381/', + changeOrigin: true, + ws: true, + logger: console, + }, { context: ['/api', '/v1'], target: 'http://127.0.0.1:9380/', diff --git a/web/src/components/ui/scroll-area.tsx b/web/src/components/ui/scroll-area.tsx index 141463d83b8..e8f989ee260 100644 --- a/web/src/components/ui/scroll-area.tsx +++ b/web/src/components/ui/scroll-area.tsx @@ -39,7 +39,8 @@ const ScrollArea = React.forwardRef< {children} - + + )); diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 88c17b1e16a..718a867be99 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -278,8 +278,8 @@ export default { tocExtractionTip: " For existing chunks, generate a hierarchical table of contents (one directory per file). During queries, when Directory Enhancement is activated, the system will use a large model to determine which directory items are relevant to the user's question, thereby identifying the relevant chunks.", deleteGenerateModalContent: ` -

        Deleting the generated {{type}} results - will remove all derived entities and relationships from this dataset. +

        Deleting the generated {{type}} results + will remove all derived entities and relationships from this dataset. Your original files will remain intact.


        Do you want to continue? @@ -1813,15 +1813,15 @@ Important structured information may include: names, dates, locations, events, k `, changeStepModalTitle: 'Step Switch Warning', changeStepModalContent: ` -

        You are currently editing the results of this stage.

        -

        If you switch to a later stage, your changes will be lost.

        +

        You are currently editing the results of this stage.

        +

        If you switch to a later stage, your changes will be lost.

        To keep them, please click Rerun to re-run the current stage.

        `, changeStepModalConfirmText: 'Switch Anyway', changeStepModalCancelText: 'Cancel', unlinkPipelineModalTitle: 'Unlink Ingestion pipeline', unlinkPipelineModalContent: ` -

        Once unlinked, this Dataset will no longer be connected to the current Ingestion pipeline.

        -

        Files that are already being parsed will continue until completion

        +

        Once unlinked, this Dataset will no longer be connected to the current Ingestion pipeline.

        +

        Files that are already being parsed will continue until completion

        Files that are not yet parsed will no longer be processed


        Are you sure you want to proceed?

        `, unlinkPipelineModalConfirmText: 'Unlink', @@ -1837,5 +1837,125 @@ Important structured information may include: names, dates, locations, events, k processingFailedTip: 'Total failed processes', processing: 'Processing', }, + admin: { + loginTitle: 'RAGFlow ADMIN', + title: 'RAGFlow admin', + confirm: 'Confirm', + close: 'Close', + yes: 'Yes', + no: 'No', + delete: 'Delete', + cancel: 'Cancel', + reset: 'Reset', + import: 'Import', + description: 'Description', + noDescription: 'No description', + + resourceType: { + dataset: 'Dataset', + chat: 'Chat', + agent: 'Agent', + search: 'Search', + file: 'File', + team: 'Team', + memory: 'Memory', + }, + + permissionType: { + enable: 'Enable', + read: 'Read', + write: 'Write', + share: 'Share', + }, + + serviceStatus: 'Service status', + userManagement: 'User management', + registrationWhitelist: 'Registration whitelist', + roles: 'Roles', + monitoring: 'Monitoring', + + active: 'Active', + inactive: 'Inactive', + enable: 'Enable', + disable: 'Disable', + all: 'All', + actions: 'Actions', + newUser: 'New User', + email: 'Email', + name: 'Name', + nickname: 'Nickname', + status: 'Status', + id: 'ID', + serviceType: 'Service type', + host: 'Host', + port: 'Port', + + role: 'Role', + user: 'User', + superuser: 'Superuser', + + createTime: 'Create time', + lastLoginTime: 'Last login time', + lastUpdateTime: 'Last update time', + + isAnonymous: 'Is Anonymous', + + deleteUser: 'Delete user', + deleteUserConfirmation: 'Are you sure you want to delete this user?', + + createNewUser: 'Create new user', + changePassword: 'Change password', + newPassword: 'New password', + confirmNewPassword: 'Confirm new password', + password: 'Password', + confirmPassword: 'Confirm password', + + invalidEmail: 'Please input a valid email address!', + passwordRequired: 'Please input your password!', + passwordMinLength: 'Password must be more than 8 characters.', + confirmPasswordRequired: 'Please confirm your password!', + confirmPasswordDoNotMatch: 'The password that you entered do not match!', + + read: 'Read', + write: 'Write', + share: 'Share', + create: 'Create', + + extraInfo: 'Extra information', + serviceDetail: `Service {{name}} detail`, + + whitelistManagement: 'Whitelist management', + exportAsExcel: 'Export Excel', + importFromExcel: 'Import Excel', + createEmail: 'Create email', + deleteEmail: 'Delete email', + editEmail: 'Edit email', + deleteWhitelistEmailConfirmation: + 'Are you sure you want to delete this email from whitelist? This action cannot be undone.', + + importWhitelist: 'Import whitelist (excel)', + importSelectExcelFile: 'Excel file (.xlsx)', + importOverwriteExistingEmails: 'Overwrite existing emails', + importInvalidExcelFile: 'Please select a valid Excel file', + importFileRequired: 'Please select a file to import', + importFileTips: + 'File must contain a single header column named email.', + + chunkNum: 'Chunks', + docNum: 'Documents', + tokenNum: 'Tokens used', + language: 'Language', + createDate: 'Create date', + updateDate: 'Update date', + permission: 'Permission', + + agentTitle: 'Agent title', + canvasCategory: 'Canvas category', + + newRole: 'New Role', + addNewRole: 'Add new role', + roleName: 'Role name', + resources: 'Resources', + }, }, }; diff --git a/web/src/pages/404.jsx b/web/src/pages/404.jsx index db9ee69c8e5..7ca998ed785 100644 --- a/web/src/pages/404.jsx +++ b/web/src/pages/404.jsx @@ -1,14 +1,24 @@ +import { Routes } from '@/routes'; import { Button, Result } from 'antd'; -import { history } from 'umi'; +import { history, useLocation } from 'umi'; const NoFoundPage = () => { + const location = useLocation(); + return ( history.push('/')}> + } diff --git a/web/src/pages/admin/components/enterprise-feature.tsx b/web/src/pages/admin/components/enterprise-feature.tsx new file mode 100644 index 00000000000..6b92d2281df --- /dev/null +++ b/web/src/pages/admin/components/enterprise-feature.tsx @@ -0,0 +1,13 @@ +import { IS_ENTERPRISE } from '../utils'; + +export default function EnterpriseFeature({ + children, +}: { + children: () => React.ReactNode; +}) { + return IS_ENTERPRISE + ? typeof children === 'function' + ? children() + : children + : null; +} diff --git a/web/src/pages/admin/components/theme-switch.tsx b/web/src/pages/admin/components/theme-switch.tsx new file mode 100644 index 00000000000..6ba97e3f90a --- /dev/null +++ b/web/src/pages/admin/components/theme-switch.tsx @@ -0,0 +1,47 @@ +import { useIsDarkTheme, useTheme } from '@/components/theme-provider'; +import { ThemeEnum } from '@/constants/common'; +import { cn } from '@/lib/utils'; +import { Root, Thumb } from '@radix-ui/react-switch'; +import { LucideMoon, LucideSun } from 'lucide-react'; +import { forwardRef } from 'react'; + +const ThemeSwitch = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { setTheme } = useTheme(); + const isDark = useIsDarkTheme(); + + return ( + + setTheme(value ? ThemeEnum.Dark : ThemeEnum.Light) + } + > +
        +
        + + +
        +
        + + +
        + ); +}); + +export default ThemeSwitch; diff --git a/web/src/pages/admin/forms/change-password-form.tsx b/web/src/pages/admin/forms/change-password-form.tsx new file mode 100644 index 00000000000..a0627c916dd --- /dev/null +++ b/web/src/pages/admin/forms/change-password-form.tsx @@ -0,0 +1,150 @@ +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useCallback, useId, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +interface ChangePasswordFormData { + newPassword: string; + confirmPassword: string; +} + +interface ChangePasswordFormProps { + id: string; + form: ReturnType>; + email?: string; + onSubmit?: (data: ChangePasswordFormData) => void; +} + +export const ChangePasswordForm = ({ + id, + form, + email, + onSubmit = () => {}, +}: ChangePasswordFormProps) => { + const { t } = useTranslation(); + + return ( +
        + + {/* Email field (readonly) */} +
        + + {t('admin.email')} + + +
        + + {/* New password field */} + ( + + + {t('admin.newPassword')} + + + + + + + + )} + /> + + {/* Confirm password field */} + ( + + + {t('admin.confirmNewPassword')} + + + + + + + )} + /> + + + ); +}; + +// Export the form validation state for parent component +function useChangePasswordForm() { + const { t } = useTranslation(); + const id = useId(); + + const schema = useMemo(() => { + return z + .object({ + newPassword: z + .string() + .min(8, { message: t('admin.passwordMinLength') }), + confirmPassword: z + .string() + .min(8, { message: t('admin.confirmPasswordRequired') }), + }) + .refine((data) => data.newPassword === data.confirmPassword, { + message: t('admin.confirmPasswordDoNotMatch'), + path: ['confirmPassword'], + }); + }, [t]); + + const form = useForm({ + defaultValues: { + newPassword: '', + confirmPassword: '', + }, + resolver: zodResolver(schema), + }); + + const FormComponent = useCallback( + (props: Partial) => ( + + ), + [id, form], + ); + + return { + schema, + id, + form, + FormComponent, + }; +} + +export default useChangePasswordForm; diff --git a/web/src/pages/admin/forms/email-form.tsx b/web/src/pages/admin/forms/email-form.tsx new file mode 100644 index 00000000000..e5fc2636a56 --- /dev/null +++ b/web/src/pages/admin/forms/email-form.tsx @@ -0,0 +1,102 @@ +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useCallback, useId, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +interface CreateEmailFormData { + email: string; +} + +interface CreateEmailFormProps { + id: string; + form: ReturnType>; + onSubmit?: (data: CreateEmailFormData) => void; +} + +export const CreateEmailForm = ({ + id, + form, + onSubmit = () => {}, +}: CreateEmailFormProps) => { + const { t } = useTranslation(); + + return ( +
        + + {/* Email field */} + ( + + + {t('admin.email')} + + + + + + + )} + /> + + + ); +}; + +// Export the form validation state for parent component +function useCreateEmailForm(props?: { + defaultValues: Partial; +}) { + const { t } = useTranslation(); + const id = useId(); + + const schema = useMemo(() => { + return z.object({ + email: z.string().email({ message: t('admin.invalidEmail') }), + }); + }, [t]); + + const form = useForm({ + defaultValues: { + email: '', + ...(props?.defaultValues ?? {}), + }, + resolver: zodResolver(schema), + }); + + const FormComponent = useCallback( + (props: Partial) => ( + + ), + [id, form], + ); + + return { + schema, + id, + form, + FormComponent, + }; +} + +export default useCreateEmailForm; diff --git a/web/src/pages/admin/forms/import-excel-form.tsx b/web/src/pages/admin/forms/import-excel-form.tsx new file mode 100644 index 00000000000..67c1b9033b3 --- /dev/null +++ b/web/src/pages/admin/forms/import-excel-form.tsx @@ -0,0 +1,155 @@ +import { Checkbox } from '@/components/ui/checkbox'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useCallback, useId, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { Trans, useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +interface ImportExcelFormData { + file: FileList; + overwriteExisting: boolean; +} + +interface ImportExcelFormProps { + id: string; + form: ReturnType>; + onSubmit?: (data: ImportExcelFormData) => void; +} + +export const ImportExcelForm = ({ + id, + form, + onSubmit = () => {}, +}: ImportExcelFormProps) => { + const { t } = useTranslation(); + + return ( +
        + + {/* File input field */} + ( + + + {t('admin.importSelectExcelFile')} + + + + { + const files = e.target.files; + onChange(files); + }} + {...field} + /> + + + + )} + /> + + {/* Overwrite checkbox */} + ( + + + + + + + {t('admin.importOverwriteExistingEmails')} + + + )} + /> + +

        + }} + /> +

        + + + ); +}; + +// Export the form validation state for parent component +function useImportExcelForm() { + const { t } = useTranslation(); + const id = useId(); + + const schema = useMemo(() => { + return z.object({ + file: z + .any() + .refine((files) => files && files.length > 0, { + message: t('admin.importFileRequired'), + }) + .refine( + (files) => { + if (!files || files.length === 0) return false; + const [file] = files; + return ( + file.type === + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || + // || file.type === 'application/vnd.ms-excel' + file.name.endsWith('.xlsx') + ); + // || file.name.endsWith('.xls'); + }, + { + message: t('admin.invalidExcelFile'), + }, + ), + overwriteExisting: z.boolean().optional(), + }); + }, [t]); + + const form = useForm({ + defaultValues: { + file: undefined, + overwriteExisting: false, + }, + resolver: zodResolver(schema), + }); + + const FormComponent = useCallback( + (props: Partial) => ( + + ), + [id, form], + ); + + return { + schema, + id, + form, + FormComponent, + }; +} + +export default useImportExcelForm; diff --git a/web/src/pages/admin/forms/role-form.tsx b/web/src/pages/admin/forms/role-form.tsx new file mode 100644 index 00000000000..dd236bc3198 --- /dev/null +++ b/web/src/pages/admin/forms/role-form.tsx @@ -0,0 +1,207 @@ +import { Card, CardContent } from '@/components/ui/card'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Switch } from '@/components/ui/switch'; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from '@/components/ui/tabs-underlined'; +import { AdminService, listResources } from '@/services/admin-service'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useQuery } from '@tanstack/react-query'; +import { useCallback, useId, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +interface CreateRoleFormData { + name: string; + description: string; + permissions: Record; +} + +interface CreateRoleFormProps { + id: string; + form: ReturnType>; + onSubmit?: (data: CreateRoleFormData) => void; +} + +const PERMISSION_TYPES = ['enable', 'read', 'write', 'share'] as const; + +export const CreateRoleForm = ({ + id, + form, + onSubmit = () => {}, +}: CreateRoleFormProps) => { + const { t } = useTranslation(); + + const { data: resourceTypes } = useQuery({ + queryKey: ['admin/resourceTypes'], + queryFn: async () => (await listResources()).data.data.resource_types, + }); + + return ( +
        + + {/* Role name field */} + ( + + + {t('admin.roleName')} + + + + + + + )} + /> + + {/* Role description field */} + ( + + + {t('admin.description')} + + + + + + )} + /> + + {/* Permissions section */} +
        + + + + + {resourceTypes?.map((resourceType) => ( + + {t(`admin.resourceType.${resourceType}`)} + + ))} + + + {resourceTypes?.map((resourceType) => ( + + + +
        + {PERMISSION_TYPES.map((permissionType) => ( + ( + + + {t(`admin.permissionType.${permissionType}`)} + + + + + + )} + /> + ))} +
        +
        +
        +
        + ))} +
        +
        + + + ); +}; + +// Export the form validation state for parent component +function useCreateRoleForm(props?: { + defaultValues: Partial; +}) { + const { t } = useTranslation(); + const id = useId(); + + const schema = useMemo(() => { + return z.object({ + name: z.string().min(1, { message: 'Role name is required' }), + description: z.string().optional(), + permissions: z.record( + z.string(), + z.object({ + enable: z.boolean(), + read: z.boolean(), + write: z.boolean(), + share: z.boolean(), + }), + ), + }); + }, [t]); + + const form = useForm({ + defaultValues: { + name: '', + description: '', + permissions: {}, + ...(props?.defaultValues ?? {}), + }, + resolver: zodResolver(schema), + }); + + const FormComponent = useCallback( + (props: Partial) => ( + + ), + [form], + ); + + return { + schema, + id, + form, + FormComponent, + }; +} + +export default useCreateRoleForm; diff --git a/web/src/pages/admin/forms/user-form.tsx b/web/src/pages/admin/forms/user-form.tsx new file mode 100644 index 00000000000..2ffe16a2613 --- /dev/null +++ b/web/src/pages/admin/forms/user-form.tsx @@ -0,0 +1,215 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { useQuery } from '@tanstack/react-query'; +import { useCallback, useId, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; + +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { listRoles } from '@/services/admin-service'; +import EnterpriseFeature from '../components/enterprise-feature'; + +interface CreateUserFormData { + email: string; + password: string; + confirmPassword: string; + role?: string; +} + +interface CreateUserFormProps { + id: string; + form: ReturnType>; + onSubmit?: (data: CreateUserFormData) => void; +} + +export const CreateUserForm = ({ + id, + form, + onSubmit = () => {}, +}: CreateUserFormProps) => { + const { t } = useTranslation(); + + const { data: roleList } = useQuery({ + queryKey: ['admin/listRoles'], + queryFn: async () => (await listRoles()).data.data.roles, + }); + + return ( +
        + + {/* Email field (editable) */} + ( + + + {t('admin.email')} + + + + + + + )} + /> + + {/* Password field */} + ( + + + {t('admin.password')} + + + + + + + )} + /> + + {/* Confirm password field */} + ( + + + {t('admin.confirmPassword')} + + + + + + + )} + /> + + + {/* Role field */} + {() => ( + ( + + + {t('admin.role')} + + + + + + )} + /> + )} + + + + ); +}; + +// Export the form validation state for parent component +function useCreateUserForm(props?: { + defaultValues: Partial; +}) { + const { t } = useTranslation(); + const id = useId(); + + const schema = useMemo(() => { + return z + .object({ + email: z.string().email({ message: t('admin.invalidEmail') }), + password: z.string().min(6, { message: t('admin.passwordMinLength') }), + confirmPassword: z + .string() + .min(1, { message: t('admin.confirmPasswordRequired') }), + role: z.string().optional(), + }) + .refine((data) => data.password === data.confirmPassword, { + message: t('admin.confirmPasswordDoNotMatch'), + path: ['confirmPassword'], + }); + }, [t]); + + const form = useForm({ + defaultValues: { + email: '', + password: '', + confirmPassword: '', + ...(props?.defaultValues ?? {}), + }, + resolver: zodResolver(schema), + }); + + const FormComponent = useCallback( + (props: Partial) => ( + + ), + [id, form], + ); + + return { + schema, + id, + form, + FormComponent, + }; +} + +export default useCreateUserForm; diff --git a/web/src/pages/admin/index.tsx b/web/src/pages/admin/index.tsx new file mode 100644 index 00000000000..9009de635de --- /dev/null +++ b/web/src/pages/admin/index.tsx @@ -0,0 +1,265 @@ +import Spotlight from '@/components/spotlight'; +import { ButtonLoading } from '@/components/ui/button'; +import { Card, CardContent, CardFooter } from '@/components/ui/card'; +import { Checkbox } from '@/components/ui/checkbox'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Authorization } from '@/constants/authorization'; +import { useAuth } from '@/hooks/auth-hooks'; +import { cn } from '@/lib/utils'; +import { Routes } from '@/routes'; +import adminService from '@/services/admin-service'; +import { rsaPsw } from '@/utils'; +import authorizationUtil from '@/utils/authorization-util'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useMutation } from '@tanstack/react-query'; +import { AxiosResponseHeaders } from 'axios'; +import { LucideEye, LucideEyeOff } from 'lucide-react'; +import { useEffect, useId, useState } from 'react'; +import { SubmitHandler, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'umi'; +import { z } from 'zod'; +import { BgSvg } from '../login-next/bg'; +import ThemeSwitch from './components/theme-switch'; + +function AdminLogin() { + const navigate = useNavigate(); + const { t } = useTranslation('translation', { keyPrefix: 'login' }); + const { isLogin } = useAuth(); + + const [showPassword, setShowPassword] = useState(false); + + const { isPending: signLoading, mutateAsync: login } = useMutation({ + mutationKey: ['adminLogin'], + mutationFn: async (params: { email: string; password: string }) => { + const request = await adminService.login(params); + const { data: req, headers } = request; + + if (req.code === 0) { + const authorization = (headers as AxiosResponseHeaders)?.get( + Authorization, + ); + const token = req.data.access_token; + + const userInfo = { + avatar: req.data.avatar, + name: req.data.nickname, + email: req.data.email, + }; + + authorizationUtil.setItems({ + Authorization: authorization as string, + Token: token, + userInfo: JSON.stringify(userInfo), + }); + } + + return req; + }, + }); + + const loading = signLoading; + + useEffect(() => { + if (isLogin) { + navigate(Routes.AdminServices); + } + }, [isLogin, navigate]); + + const FormSchema = z.object({ + email: z + .string() + .email() + .min(1, { message: t('emailPlaceholder') }), + password: z.string().min(1, { message: t('passwordPlaceholder') }), + remember: z.boolean().optional(), + }); + + const formId = useId(); + const form = useForm({ + defaultValues: { + email: '', + password: '', + remember: false, + }, + resolver: zodResolver(FormSchema), + }); + + const onCheck: SubmitHandler> = async (params) => { + try { + const rsaPassWord = rsaPsw(params.password) as string; + + const { code } = await login({ + email: `${params.email}`.trim(), + password: rsaPassWord, + }); + + if (code === 0) { + navigate('/admin/services'); + } + } catch (errorInfo) { + console.log('Failed:', errorInfo); + } + }; + + return ( +
        + + + + + + +
        +
        + logo + RAGFlow +
        + +

        + {t('loginTitle', { keyPrefix: 'admin' })} +

        +
        + +
        +
        + + +
        + + ( + + {t('emailLabel')} + + + + + + + + )} + /> + + ( + + {t('passwordLabel')} + + +
        + + +
        +
        + + +
        + )} + /> + + ( + + + + + + + {t('rememberMe')} + + + )} + /> + + +
        + + + + {t('login')} + + +
        + +
        + +
        +
        +
        +
        + ); +} + +export default AdminLogin; diff --git a/web/src/pages/admin/layout.tsx b/web/src/pages/admin/layout.tsx new file mode 100644 index 00000000000..5ee2cf89599 --- /dev/null +++ b/web/src/pages/admin/layout.tsx @@ -0,0 +1,133 @@ +import { Button } from '@/components/ui/button'; +import message from '@/components/ui/message'; +import { cn } from '@/lib/utils'; +import { Routes } from '@/routes'; +import adminService from '@/services/admin-service'; +import authorizationUtil from '@/utils/authorization-util'; +import { useMutation } from '@tanstack/react-query'; +import { + LucideMonitor, + LucideServerCrash, + LucideSquareUserRound, + LucideUserCog, + LucideUserStar, +} from 'lucide-react'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { NavLink, Outlet, useLocation, useNavigate } from 'umi'; +import ThemeSwitch from './components/theme-switch'; +import { IS_ENTERPRISE } from './utils'; + +const AdminLayout = () => { + const { t } = useTranslation(); + const { pathname } = useLocation(); + const navigate = useNavigate(); + + const navItems = useMemo( + () => [ + { + path: Routes.AdminServices, + name: t('admin.serviceStatus'), + icon: , + }, + { + path: Routes.AdminUserManagement, + name: t('admin.userManagement'), + icon: , + }, + ...(IS_ENTERPRISE + ? [ + { + path: Routes.AdminWhitelist, + name: t('admin.registrationWhitelist'), + icon: , + }, + { + path: Routes.AdminRoles, + name: t('admin.roles'), + icon: , + }, + { + path: Routes.AdminMonitoring, + name: t('admin.monitoring'), + icon: , + }, + ] + : []), + ], + [t], + ); + + const { + data, + isPending, + mutateAsync: logout, + } = useMutation({ + mutationKey: ['adminLogout'], + mutationFn: async () => { + await adminService.logout(); + + message.success(t('message.logout')); + authorizationUtil.removeAll(); + navigate(Routes.Admin); + }, + }); + + return ( +
        + + +
        + +
        +
        + ); +}; + +export default AdminLayout; diff --git a/web/src/pages/admin/monitoring.tsx b/web/src/pages/admin/monitoring.tsx new file mode 100644 index 00000000000..60af64545aa --- /dev/null +++ b/web/src/pages/admin/monitoring.tsx @@ -0,0 +1,13 @@ +import { Card, CardContent } from '@/components/ui/card'; + +function AdminMonitoring() { + return ( + + +