Merge branch 'main' of https://github.com/KevinHuSh/ragflow
This commit is contained in:
commit
3f652088e8
16 changed files with 57 additions and 58 deletions
|
|
@ -30,7 +30,7 @@ from api.db.services.mcp_server_service import MCPServerService
|
||||||
from common.connection_utils import timeout
|
from common.connection_utils import timeout
|
||||||
from rag.prompts.generator import next_step, COMPLETE_TASK, analyze_task, \
|
from rag.prompts.generator import next_step, COMPLETE_TASK, analyze_task, \
|
||||||
citation_prompt, reflect, rank_memories, kb_prompt, citation_plus, full_question, message_fit_in
|
citation_prompt, reflect, rank_memories, kb_prompt, citation_plus, full_question, message_fit_in
|
||||||
from rag.utils.mcp_tool_call_conn import MCPToolCallSession, mcp_tool_metadata_to_openai_tool
|
from common.mcp_tool_call_conn import MCPToolCallSession, mcp_tool_metadata_to_openai_tool
|
||||||
from agent.component.llm import LLMParam, LLM
|
from agent.component.llm import LLMParam, LLM
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,8 @@ from functools import partial
|
||||||
from typing import TypedDict, List, Any
|
from typing import TypedDict, List, Any
|
||||||
from agent.component.base import ComponentParamBase, ComponentBase
|
from agent.component.base import ComponentParamBase, ComponentBase
|
||||||
from common.misc_utils import hash_str2int
|
from common.misc_utils import hash_str2int
|
||||||
from rag.llm.chat_model import ToolCallSession
|
|
||||||
from rag.prompts.generator import kb_prompt
|
from rag.prompts.generator import kb_prompt
|
||||||
from rag.utils.mcp_tool_call_conn import MCPToolCallSession
|
from common.mcp_tool_call_conn import MCPToolCallSession, ToolCallSession
|
||||||
from timeit import default_timer as timer
|
from timeit import default_timer as timer
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ from common.misc_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.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request, \
|
||||||
get_mcp_tools
|
get_mcp_tools
|
||||||
from api.utils.web_utils import get_float, safe_json_parse
|
from api.utils.web_utils import get_float, safe_json_parse
|
||||||
from rag.utils.mcp_tool_call_conn import MCPToolCallSession, close_multiple_mcp_toolcall_sessions
|
from common.mcp_tool_call_conn import MCPToolCallSession, close_multiple_mcp_toolcall_sessions
|
||||||
|
|
||||||
|
|
||||||
@manager.route("/list", methods=["POST"]) # noqa: F821
|
@manager.route("/list", methods=["POST"]) # noqa: F821
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ def list_agents(tenant_id):
|
||||||
return get_error_data_result("The agent doesn't exist.")
|
return get_error_data_result("The agent doesn't exist.")
|
||||||
page_number = int(request.args.get("page", 1))
|
page_number = int(request.args.get("page", 1))
|
||||||
items_per_page = int(request.args.get("page_size", 30))
|
items_per_page = int(request.args.get("page_size", 30))
|
||||||
orderby = request.args.get("orderby", "update_time")
|
order_by = request.args.get("orderby", "update_time")
|
||||||
if request.args.get("desc") == "False" or request.args.get("desc") == "false":
|
if request.args.get("desc") == "False" or request.args.get("desc") == "false":
|
||||||
desc = False
|
desc = False
|
||||||
else:
|
else:
|
||||||
desc = True
|
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, order_by, desc, id, title)
|
||||||
return get_result(data=canvas)
|
return get_result(data=canvas)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -420,10 +420,10 @@ class KnowledgebaseService(CommonService):
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"created_by": tenant_id,
|
"created_by": tenant_id,
|
||||||
"parser_id": (parser_id or "naive"),
|
"parser_id": (parser_id or "naive"),
|
||||||
**kwargs
|
**kwargs # Includes optional fields such as description, language, permission, avatar, parser_config, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
# Default parser_config (align with kb_app.create) — do not accept external overrides
|
# Update parser_config (always override with validated default/merged config)
|
||||||
payload["parser_config"] = get_parser_config(parser_id, kwargs.get("parser_config"))
|
payload["parser_config"] = get_parser_config(parser_id, kwargs.get("parser_config"))
|
||||||
|
|
||||||
return True, payload
|
return True, payload
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import re
|
||||||
from common.token_utils import num_tokens_from_string
|
from common.token_utils import num_tokens_from_string
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
from common.constants import LLMType
|
||||||
from api.db.db_models import LLM
|
from api.db.db_models import LLM
|
||||||
from api.db.services.common_service import CommonService
|
from api.db.services.common_service import CommonService
|
||||||
from api.db.services.tenant_llm_service import LLM4Tenant, TenantLLMService
|
from api.db.services.tenant_llm_service import LLM4Tenant, TenantLLMService
|
||||||
|
|
@ -32,6 +33,14 @@ def get_init_tenant_llm(user_id):
|
||||||
from common import settings
|
from common import settings
|
||||||
tenant_llm = []
|
tenant_llm = []
|
||||||
|
|
||||||
|
model_configs = {
|
||||||
|
LLMType.CHAT: settings.CHAT_CFG,
|
||||||
|
LLMType.EMBEDDING: settings.EMBEDDING_CFG,
|
||||||
|
LLMType.SPEECH2TEXT: settings.ASR_CFG,
|
||||||
|
LLMType.IMAGE2TEXT: settings.IMAGE2TEXT_CFG,
|
||||||
|
LLMType.RERANK: settings.RERANK_CFG,
|
||||||
|
}
|
||||||
|
|
||||||
seen = set()
|
seen = set()
|
||||||
factory_configs = []
|
factory_configs = []
|
||||||
for factory_config in [
|
for factory_config in [
|
||||||
|
|
@ -54,8 +63,8 @@ def get_init_tenant_llm(user_id):
|
||||||
"llm_factory": factory_config["factory"],
|
"llm_factory": factory_config["factory"],
|
||||||
"llm_name": llm.llm_name,
|
"llm_name": llm.llm_name,
|
||||||
"model_type": llm.model_type,
|
"model_type": llm.model_type,
|
||||||
"api_key": factory_config["api_key"],
|
"api_key": model_configs.get(llm.model_type, {}).get("api_key", factory_config["api_key"]),
|
||||||
"api_base": factory_config["base_url"],
|
"api_base": model_configs.get(llm.model_type, {}).get("base_url", factory_config["base_url"]),
|
||||||
"max_tokens": llm.max_tokens if llm.max_tokens else 8192,
|
"max_tokens": llm.max_tokens if llm.max_tokens else 8192,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -80,8 +89,8 @@ class LLMBundle(LLM4Tenant):
|
||||||
|
|
||||||
def encode(self, texts: list):
|
def encode(self, texts: list):
|
||||||
if self.langfuse:
|
if self.langfuse:
|
||||||
generation = self.langfuse.start_generation(trace_context=self.trace_context, name="encode", model=self.llm_name, input={"texts": texts})
|
generation = self.langfuse.start_generation(trace_context=self.trace_context, name="encode", model=self.llm_name, input={"texts": texts})
|
||||||
|
|
||||||
safe_texts = []
|
safe_texts = []
|
||||||
for text in texts:
|
for text in texts:
|
||||||
token_size = num_tokens_from_string(text)
|
token_size = num_tokens_from_string(text)
|
||||||
|
|
@ -90,7 +99,7 @@ class LLMBundle(LLM4Tenant):
|
||||||
safe_texts.append(text[:target_len])
|
safe_texts.append(text[:target_len])
|
||||||
else:
|
else:
|
||||||
safe_texts.append(text)
|
safe_texts.append(text)
|
||||||
|
|
||||||
embeddings, used_tokens = self.mdl.encode(safe_texts)
|
embeddings, used_tokens = self.mdl.encode(safe_texts)
|
||||||
|
|
||||||
llm_name = getattr(self, "llm_name", None)
|
llm_name = getattr(self, "llm_name", None)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ from api.db.db_models import init_database_tables as init_web_db
|
||||||
from api.db.init_data import init_web_data
|
from api.db.init_data import init_web_data
|
||||||
from common.versions import get_ragflow_version
|
from common.versions import get_ragflow_version
|
||||||
from common.config_utils import show_configs
|
from common.config_utils import show_configs
|
||||||
from rag.utils.mcp_tool_call_conn import shutdown_all_mcp_sessions
|
from common.mcp_tool_call_conn import shutdown_all_mcp_sessions
|
||||||
from rag.utils.redis_conn import RedisDistributedLock
|
from rag.utils.redis_conn import RedisDistributedLock
|
||||||
|
|
||||||
stop_event = threading.Event()
|
stop_event = threading.Event()
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ from peewee import OperationalError
|
||||||
from common.constants import ActiveEnum
|
from common.constants import ActiveEnum
|
||||||
from api.db.db_models import APIToken
|
from api.db.db_models import APIToken
|
||||||
from api.utils.json_encode import CustomJSONEncoder
|
from api.utils.json_encode import CustomJSONEncoder
|
||||||
from rag.utils.mcp_tool_call_conn import MCPToolCallSession, close_multiple_mcp_toolcall_sessions
|
from common.mcp_tool_call_conn import MCPToolCallSession, close_multiple_mcp_toolcall_sessions
|
||||||
from api.db.services.tenant_llm_service import LLMFactoriesService
|
from api.db.services.tenant_llm_service import LLMFactoriesService
|
||||||
from common.connection_utils import timeout
|
from common.connection_utils import timeout
|
||||||
from common.constants import RetCode
|
from common.constants import RetCode
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ class SlimConnectorWithPermSync(ABC):
|
||||||
|
|
||||||
|
|
||||||
class CheckpointedConnectorWithPermSync(ABC):
|
class CheckpointedConnectorWithPermSync(ABC):
|
||||||
"""Checkpointed connector interface (with permission sync)"""
|
"""Checkpoint connector interface (with permission sync)"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def load_from_checkpoint(
|
def load_from_checkpoint(
|
||||||
|
|
@ -143,7 +143,7 @@ class CredentialsProviderInterface(abc.ABC, Generic[T]):
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def is_dynamic(self) -> bool:
|
def is_dynamic(self) -> bool:
|
||||||
"""If dynamic, the credentials may change during usage ... maening the client
|
"""If dynamic, the credentials may change during usage ... meaning the client
|
||||||
needs to use the locking features of the credentials provider to operate
|
needs to use the locking features of the credentials provider to operate
|
||||||
correctly.
|
correctly.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import weakref
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from concurrent.futures import TimeoutError as FuturesTimeoutError
|
from concurrent.futures import TimeoutError as FuturesTimeoutError
|
||||||
from string import Template
|
from string import Template
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal, Protocol
|
||||||
|
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
|
|
@ -30,12 +30,15 @@ from mcp.client.session import ClientSession
|
||||||
from mcp.client.sse import sse_client
|
from mcp.client.sse import sse_client
|
||||||
from mcp.client.streamable_http import streamablehttp_client
|
from mcp.client.streamable_http import streamablehttp_client
|
||||||
from mcp.types import CallToolResult, ListToolsResult, TextContent, Tool
|
from mcp.types import CallToolResult, ListToolsResult, TextContent, Tool
|
||||||
from rag.llm.chat_model import ToolCallSession
|
|
||||||
|
|
||||||
MCPTaskType = Literal["list_tools", "tool_call"]
|
MCPTaskType = Literal["list_tools", "tool_call"]
|
||||||
MCPTask = tuple[MCPTaskType, dict[str, Any], asyncio.Queue[Any]]
|
MCPTask = tuple[MCPTaskType, dict[str, Any], asyncio.Queue[Any]]
|
||||||
|
|
||||||
|
|
||||||
|
class ToolCallSession(Protocol):
|
||||||
|
def tool_call(self, name: str, arguments: dict[str, Any]) -> str: ...
|
||||||
|
|
||||||
|
|
||||||
class MCPToolCallSession(ToolCallSession):
|
class MCPToolCallSession(ToolCallSession):
|
||||||
_ALL_INSTANCES: weakref.WeakSet["MCPToolCallSession"] = weakref.WeakSet()
|
_ALL_INSTANCES: weakref.WeakSet["MCPToolCallSession"] = weakref.WeakSet()
|
||||||
|
|
||||||
|
|
@ -106,7 +109,8 @@ class MCPToolCallSession(ToolCallSession):
|
||||||
await self._process_mcp_tasks(None, msg)
|
await self._process_mcp_tasks(None, msg)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
await self._process_mcp_tasks(None, 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, error_message: str | None = None) -> None:
|
async def _process_mcp_tasks(self, client_session: ClientSession | None, error_message: str | None = None) -> None:
|
||||||
while not self._close:
|
while not self._close:
|
||||||
|
|
@ -164,7 +168,8 @@ class MCPToolCallSession(ToolCallSession):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def _call_mcp_tool(self, name: str, arguments: dict[str, Any], timeout: float | int = 10) -> str:
|
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)
|
result: CallToolResult = await self._call_mcp_server("tool_call", name=name, arguments=arguments,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
if result.isError:
|
if result.isError:
|
||||||
return f"MCP server error: {result.content}"
|
return f"MCP server error: {result.content}"
|
||||||
|
|
@ -283,7 +288,8 @@ def close_multiple_mcp_toolcall_sessions(sessions: list[MCPToolCallSession]) ->
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Exception during MCP session cleanup thread management")
|
logging.exception("Exception during MCP session cleanup thread management")
|
||||||
|
|
||||||
logging.info(f"{len(sessions)} MCP sessions has been cleaned up. {len(list(MCPToolCallSession._ALL_INSTANCES))} in global context.")
|
logging.info(
|
||||||
|
f"{len(sessions)} MCP sessions has been cleaned up. {len(list(MCPToolCallSession._ALL_INSTANCES))} in global context.")
|
||||||
|
|
||||||
|
|
||||||
def shutdown_all_mcp_sessions():
|
def shutdown_all_mcp_sessions():
|
||||||
|
|
@ -298,7 +304,7 @@ def shutdown_all_mcp_sessions():
|
||||||
logging.info("All MCPToolCallSession instances have been closed.")
|
logging.info("All MCPToolCallSession instances have been closed.")
|
||||||
|
|
||||||
|
|
||||||
def mcp_tool_metadata_to_openai_tool(mcp_tool: Tool|dict) -> dict[str, Any]:
|
def mcp_tool_metadata_to_openai_tool(mcp_tool: Tool | dict) -> dict[str, Any]:
|
||||||
if isinstance(mcp_tool, dict):
|
if isinstance(mcp_tool, dict):
|
||||||
return {
|
return {
|
||||||
"type": "function",
|
"type": "function",
|
||||||
|
|
@ -434,7 +434,7 @@ class MinerUParser(RAGFlowPdfParser):
|
||||||
if not section.strip():
|
if not section.strip():
|
||||||
section = "FAILED TO PARSE TABLE"
|
section = "FAILED TO PARSE TABLE"
|
||||||
case MinerUContentType.IMAGE:
|
case MinerUContentType.IMAGE:
|
||||||
section = "".join(output.get(["image_caption"],[])) + "\n" + "".join(output.get(["image_footnote"],[]))
|
section = "".join(output.get("image_caption", [])) + "\n" + "".join(output.get("image_footnote", []))
|
||||||
case MinerUContentType.EQUATION:
|
case MinerUContentType.EQUATION:
|
||||||
section = output["text"]
|
section = output["text"]
|
||||||
case MinerUContentType.CODE:
|
case MinerUContentType.CODE:
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ To access the RAGFlow admin UI, append `/admin` to the web UI's address, e.g. `h
|
||||||
### Default Credentials
|
### Default Credentials
|
||||||
| Username | Password |
|
| Username | Password |
|
||||||
|----------|----------|
|
|----------|----------|
|
||||||
| admin@ragflow.io | admin |
|
| `admin@ragflow.io` | `admin` |
|
||||||
|
|
||||||
## Admin UI Overview
|
## Admin UI Overview
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import re
|
||||||
import time
|
import time
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Any, Protocol
|
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
import json_repair
|
import json_repair
|
||||||
|
|
@ -65,10 +64,6 @@ LENGTH_NOTIFICATION_CN = "······\n由于大模型的上下文窗口大小
|
||||||
LENGTH_NOTIFICATION_EN = "...\nThe answer is truncated by your chosen LLM due to its limitation on context length."
|
LENGTH_NOTIFICATION_EN = "...\nThe answer is truncated by your chosen LLM due to its limitation on context length."
|
||||||
|
|
||||||
|
|
||||||
class ToolCallSession(Protocol):
|
|
||||||
def tool_call(self, name: str, arguments: dict[str, Any]) -> str: ...
|
|
||||||
|
|
||||||
|
|
||||||
class Base(ABC):
|
class Base(ABC):
|
||||||
def __init__(self, key, model_name, base_url, **kwargs):
|
def __init__(self, key, model_name, base_url, **kwargs):
|
||||||
timeout = int(os.environ.get("LM_TIMEOUT_SECONDS", 600))
|
timeout = int(os.environ.get("LM_TIMEOUT_SECONDS", 600))
|
||||||
|
|
|
||||||
|
|
@ -155,13 +155,13 @@ def qbullets_category(sections):
|
||||||
if re.match(pro, sec) and not not_bullet(sec):
|
if re.match(pro, sec) and not not_bullet(sec):
|
||||||
hits[i] += 1
|
hits[i] += 1
|
||||||
break
|
break
|
||||||
maxium = 0
|
maximum = 0
|
||||||
res = -1
|
res = -1
|
||||||
for i, h in enumerate(hits):
|
for i, h in enumerate(hits):
|
||||||
if h <= maxium:
|
if h <= maximum:
|
||||||
continue
|
continue
|
||||||
res = i
|
res = i
|
||||||
maxium = h
|
maximum = h
|
||||||
return res, QUESTION_PATTERN[res]
|
return res, QUESTION_PATTERN[res]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -222,13 +222,13 @@ def bullets_category(sections):
|
||||||
if re.match(p, sec) and not not_bullet(sec):
|
if re.match(p, sec) and not not_bullet(sec):
|
||||||
hits[i] += 1
|
hits[i] += 1
|
||||||
break
|
break
|
||||||
maxium = 0
|
maximum = 0
|
||||||
res = -1
|
res = -1
|
||||||
for i, h in enumerate(hits):
|
for i, h in enumerate(hits):
|
||||||
if h <= maxium:
|
if h <= maximum:
|
||||||
continue
|
continue
|
||||||
res = i
|
res = i
|
||||||
maxium = h
|
maximum = h
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,9 @@ const Modal: ModalType = ({
|
||||||
onOk?.();
|
onOk?.();
|
||||||
}, [onOk, onOpenChange]);
|
}, [onOk, onOpenChange]);
|
||||||
const handleChange = (open: boolean) => {
|
const handleChange = (open: boolean) => {
|
||||||
|
if (!open && !maskClosable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
onOpenChange?.(open);
|
onOpenChange?.(open);
|
||||||
console.log('open', open, onOpenChange);
|
console.log('open', open, onOpenChange);
|
||||||
if (open && !disabled) {
|
if (open && !disabled) {
|
||||||
|
|
@ -185,6 +188,7 @@ const Modal: ModalType = ({
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted focus-visible:outline-none"
|
className="flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted focus-visible:outline-none"
|
||||||
|
onClick={handleCancel}
|
||||||
>
|
>
|
||||||
{closeIcon}
|
{closeIcon}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,7 @@ import {
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Modal } from '@/components/ui/modal/modal';
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
import {
|
import { RAGFlowSelect } from '@/components/ui/select';
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@/components/ui/select';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { TimezoneList } from '@/pages/user-setting/constants';
|
import { TimezoneList } from '@/pages/user-setting/constants';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
@ -230,6 +224,7 @@ const ProfilePage: FC = () => {
|
||||||
title={modalTitle[editType]}
|
title={modalTitle[editType]}
|
||||||
open={isEditing}
|
open={isEditing}
|
||||||
showfooter={false}
|
showfooter={false}
|
||||||
|
maskClosable={false}
|
||||||
titleClassName="text-base"
|
titleClassName="text-base"
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
|
|
@ -281,23 +276,14 @@ const ProfilePage: FC = () => {
|
||||||
<FormLabel className="text-sm text-text-secondary whitespace-nowrap">
|
<FormLabel className="text-sm text-text-secondary whitespace-nowrap">
|
||||||
{t('timezone')}
|
{t('timezone')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Select
|
<RAGFlowSelect
|
||||||
|
options={TimezoneList.map((timeStr) => {
|
||||||
|
return { value: timeStr, label: timeStr };
|
||||||
|
})}
|
||||||
|
placeholder="Select a timeZone"
|
||||||
onValueChange={field.onChange}
|
onValueChange={field.onChange}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
>
|
/>
|
||||||
<FormControl className="w-full bg-bg-input border-border-default">
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select a timeZone" />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{TimezoneList.map((timeStr) => (
|
|
||||||
<SelectItem key={timeStr} value={timeStr}>
|
|
||||||
{timeStr}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full pt-1">
|
<div className="flex w-full pt-1">
|
||||||
<div className="w-1/4"></div>
|
<div className="w-1/4"></div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue