Merge branch 'main' of https://github.com/infiniflow/ragflow into feature/fault_tolerant_mechanism_raptor
This commit is contained in:
commit
1787b094bd
22 changed files with 3410 additions and 3415 deletions
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"id": 27,
|
||||
"title": {
|
||||
"en": "Interacting with the Agent",
|
||||
"zh": "用户与 Agent 交互"
|
||||
"en": "Interactive Agent",
|
||||
"zh": "可交互的 Agent"
|
||||
},
|
||||
"description": {
|
||||
"en": "During the Agent’s execution, users can actively intervene and interact with the Agent to adjust or guide its output, ensuring the final result aligns with their intentions.",
|
||||
|
|
|
|||
|
|
@ -236,13 +236,13 @@ class Connector2KbService(CommonService):
|
|||
conn_id = conn["id"]
|
||||
connector_ids.append(conn_id)
|
||||
if conn_id in old_conn_ids:
|
||||
cls.update_by_id(conn_id, {"auto_parse": conn.get("auto_parse", "1")})
|
||||
cls.filter_update([cls.model.connector_id==conn_id, cls.model.kb_id==kb_id], {"auto_parse": conn.get("auto_parse", "1")})
|
||||
continue
|
||||
cls.save(**{
|
||||
"id": get_uuid(),
|
||||
"connector_id": conn_id,
|
||||
"kb_id": kb_id,
|
||||
"auto_parse": conn.get("auto_parse", "1")
|
||||
"auto_parse": conn.get("auto_parse", "1")
|
||||
})
|
||||
SyncLogsService.schedule(conn_id, kb_id, reindex=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ services:
|
|||
infinity:
|
||||
profiles:
|
||||
- infinity
|
||||
image: infiniflow/infinity:v0.6.4
|
||||
image: infiniflow/infinity:v0.6.5
|
||||
volumes:
|
||||
- infinity_data:/var/infinity
|
||||
- ./infinity_conf.toml:/infinity_conf.toml
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[general]
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
time_zone = "utc-8"
|
||||
|
||||
[network]
|
||||
|
|
|
|||
|
|
@ -97,14 +97,7 @@ RAGFlow utilizes MinIO as its object storage solution, leveraging its scalabilit
|
|||
- `SVR_HTTP_PORT`
|
||||
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. Defaults to `9380`.
|
||||
- `RAGFLOW-IMAGE`
|
||||
The Docker image edition. Available editions:
|
||||
|
||||
- `infiniflow/ragflow:v0.21.1-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.21.1`: The RAGFlow Docker image with embedding models including:
|
||||
- Built-in embedding models:
|
||||
- `BAAI/bge-large-zh-v1.5`
|
||||
- `maidalun1020/bce-embedding-base_v1`
|
||||
|
||||
The Docker image edition. Defaults to `infiniflow/ragflow:v0.21.1` (the RAGFlow Docker image without embedding models).
|
||||
|
||||
:::tip NOTE
|
||||
If you cannot download the RAGFlow Docker image, try the following mirrors.
|
||||
|
|
|
|||
|
|
@ -42,11 +42,7 @@ cd ragflow/
|
|||
```
|
||||
|
||||
2. Install Python dependencies:
|
||||
- slim:
|
||||
```bash
|
||||
uv sync --python 3.10 # install RAGFlow dependent python modules
|
||||
```
|
||||
- full:
|
||||
|
||||
```bash
|
||||
uv sync --python 3.10 # install RAGFlow dependent python modules
|
||||
```
|
||||
|
|
|
|||
25
docs/faq.mdx
25
docs/faq.mdx
|
|
@ -26,27 +26,9 @@ The "garbage in garbage out" status quo remains unchanged despite the fact that
|
|||
|
||||
---
|
||||
|
||||
### Differences between RAGFlow full edition and RAGFlow slim edition?
|
||||
|
||||
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.21.1-slim`
|
||||
- **Full edition**: includes built-in embedding models and has no suffix added to the version name. Example: `infiniflow/ragflow:v0.21.1`
|
||||
|
||||
Note: Starting with `v0.22.0`, we ship only the slim edition and no longer append the **-slim** suffix to the image tag.
|
||||
|
||||
---
|
||||
|
||||
### Which embedding models can be deployed locally?
|
||||
|
||||
RAGFlow offers two Docker image editions, `v0.21.1-slim` and `v0.21.1`:
|
||||
|
||||
- `infiniflow/ragflow:v0.21.1-slim` (default): The RAGFlow Docker image without embedding models.
|
||||
- `infiniflow/ragflow:v0.21.1`: The RAGFlow Docker image with the following built-in embedding models:
|
||||
- `BAAI/bge-large-zh-v1.5`
|
||||
- `maidalun1020/bce-embedding-base_v1`
|
||||
|
||||
Note: Starting with `v0.22.0`, we ship only the slim edition and no longer append the **-slim** suffix to the image tag.
|
||||
Starting from `v0.22.0`, we ship only the slim edition and no longer append the **-slim** suffix to the image tag.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -65,7 +47,7 @@ If you build RAGFlow from source, the version number is also in the system log:
|
|||
/ _, _// ___ |/ /_/ // __/ / // /_/ /| |/ |/ /
|
||||
/_/ |_|/_/ |_|\____//_/ /_/ \____/ |__/|__/
|
||||
|
||||
2025-02-18 10:10:43,835 INFO 1445658 RAGFlow version: v0.15.0-50-g6daae7f2 full
|
||||
2025-02-18 10:10:43,835 INFO 1445658 RAGFlow version: v0.15.0-50-g6daae7f2
|
||||
```
|
||||
|
||||
Where:
|
||||
|
|
@ -73,9 +55,6 @@ Where:
|
|||
- `v0.15.0`: The officially published release.
|
||||
- `50`: The number of git commits since the official release.
|
||||
- `g6daae7f2`: `g` is the prefix, and `6daae7f2` is the first seven characters of the current commit ID.
|
||||
- `full`/`slim`: The RAGFlow edition.
|
||||
- `full`: The full RAGFlow edition.
|
||||
- `slim`: The RAGFlow edition without embedding models and Python packages.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -189,10 +189,6 @@ This section provides instructions on setting up the RAGFlow server on Linux. If
|
|||
|
||||
3. Use the pre-built Docker images and start up the server:
|
||||
|
||||
:::tip NOTE
|
||||
The command below downloads the `v0.21.1-slim` edition of the RAGFlow Docker image. Refer to the following table for descriptions of different RAGFlow editions. To download a RAGFlow edition different from `v0.21.1-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.21.1` for the full edition `v0.21.1`.
|
||||
:::
|
||||
|
||||
```bash
|
||||
# Use CPU for embedding and DeepDoc tasks:
|
||||
$ docker compose -f docker-compose.yml up -d
|
||||
|
|
@ -202,11 +198,10 @@ This section provides instructions on setting up the RAGFlow server on Linux. If
|
|||
<APITable>
|
||||
```
|
||||
|
||||
| RAGFlow image tag | Image size (GB) | Has embedding models and Python packages? | Stable? |
|
||||
| ------------------- | --------------- | ----------------------------------------- | ------------------------ |
|
||||
| v0.21.1 | ≈9 | ✔️ | Stable release |
|
||||
| v0.21.1-slim | ≈2 | ❌ | Stable release |
|
||||
| nightly | ≈2 | ❌ | _Unstable_ nightly build |
|
||||
| RAGFlow image tag | Image size (GB) | Stable? |
|
||||
| ------------------- | --------------- | ------------------------ |
|
||||
| v0.21.1 | ≈2 | Stable release |
|
||||
| nightly | ≈2 | _Unstable_ nightly build |
|
||||
|
||||
```mdx-code-block
|
||||
</APITable>
|
||||
|
|
@ -222,7 +217,7 @@ These two embedding models are optimized specifically for English and Chinese, s
|
|||
:::
|
||||
|
||||
:::tip NOTE
|
||||
The image size shown refers to the size of the *downloaded* Docker image, which is compressed. When Docker runs the image, it unpacks it, resulting in significantly greater disk usage. For example, a slim edition image will expand to around 7 GB once unpacked.
|
||||
The image size shown refers to the size of the *downloaded* Docker image, which is compressed. When Docker runs the image, it unpacks it, resulting in significantly greater disk usage. A Docker image will expand to around 7 GB once unpacked.
|
||||
:::
|
||||
|
||||
4. Check the server status after having the server up and running:
|
||||
|
|
|
|||
|
|
@ -7,21 +7,42 @@ slug: /release_notes
|
|||
|
||||
Key features, improvements and bug fixes in the latest releases.
|
||||
|
||||
:::info
|
||||
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.21.1-slim`
|
||||
- **Full edition**: includes built-in embedding models and has no suffix added to the version name. Example: `infiniflow/ragflow:v0.21.1`
|
||||
:::
|
||||
## v0.22.0
|
||||
|
||||
Released on November 12, 2025.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
:::danger IMPORTANT
|
||||
The embedding models included in a full edition are:
|
||||
|
||||
- BAAI/bge-large-zh-v1.5
|
||||
- maidalun1020/bce-embedding-base_v1
|
||||
|
||||
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.
|
||||
From this release onwards, we ship only the slim edition (without embedding models) Docker image and no longer append the `-slim` suffix to the image tag.
|
||||
:::
|
||||
|
||||
### New Features
|
||||
|
||||
- Dataset:
|
||||
- Supports data synchronization from five online sources (AWS S3, Google Drive, Notion, Confluence, and Discord).
|
||||
- RAPTOR can be built across an entire dataset or on individual documents.
|
||||
- Ingestion pipeline: Supports [Docling document parsing](https://github.com/docling-project/docling) in the **Parser** component.
|
||||
- Launches a new administrative Web UI dashboard for graphical user management and service status monitoring.
|
||||
- Agent:
|
||||
- Supports structured output.
|
||||
- Supports metadata filtering in the **Retrieval** component.
|
||||
- Introduces a **Variable aggregator** component with data operation and session variable definition capabilities.
|
||||
|
||||
### Improvements
|
||||
|
||||
- Agent: Supports visualizing previous components' outputs in the **Await Response** component.
|
||||
- Revamps the model provider page.
|
||||
- Upgrades RAGFlow's document engine Infinity to v0.6.5.
|
||||
|
||||
### Added Models
|
||||
|
||||
- Kimi-K2-Thinking
|
||||
|
||||
### New agent templates
|
||||
|
||||
- Interactive Agent, incorporates real-time user feedback to dynamically optimize Agent output.
|
||||
|
||||
## v0.21.1
|
||||
|
||||
Released on October 23, 2025.
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ ragflow:
|
|||
infinity:
|
||||
image:
|
||||
repository: infiniflow/infinity
|
||||
tag: v0.6.4
|
||||
tag: v0.6.5
|
||||
pullPolicy: IfNotPresent
|
||||
pullSecrets: []
|
||||
storage:
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ dependencies = [
|
|||
"html-text==0.6.2",
|
||||
"httpx[socks]>=0.28.1,<0.29.0",
|
||||
"huggingface-hub>=0.25.0,<0.26.0",
|
||||
"infinity-sdk==0.6.4",
|
||||
"infinity-sdk==0.6.5",
|
||||
"infinity-emb>=0.0.66,<0.0.67",
|
||||
"itsdangerous==2.1.2",
|
||||
"json-repair==0.35.0",
|
||||
|
|
|
|||
|
|
@ -156,7 +156,6 @@ class InfinityConnection(DocStoreConnection):
|
|||
msg = f"Infinity {infinity_uri} is unhealthy in 120s."
|
||||
logger.error(msg)
|
||||
raise Exception(msg)
|
||||
self.column_vec_patt = re.compile(r"q_(?P<vector_size>\d+)_vec")
|
||||
logger.info(f"Infinity {infinity_uri} is healthy.")
|
||||
|
||||
def _migrate_db(self, inf_conn):
|
||||
|
|
@ -324,8 +323,7 @@ class InfinityConnection(DocStoreConnection):
|
|||
output.append(score_func)
|
||||
if PAGERANK_FLD not in output:
|
||||
output.append(PAGERANK_FLD)
|
||||
output = [f for f in output if f != "_score" and self.column_vec_patt.match(f) is None]
|
||||
logger.info(f"infinity output fields: {output}")
|
||||
output = [f for f in output if f != "_score"]
|
||||
if limit <= 0:
|
||||
# ElasticSearch default limit is 10000
|
||||
limit = 10000
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ const PREDEFINED_COLORS = [
|
|||
];
|
||||
|
||||
const getStringHash = (str: string): number => {
|
||||
if (typeof str !== 'string') return 0;
|
||||
|
||||
const normalized = str.trim().toLowerCase();
|
||||
let hash = 104729;
|
||||
const seed = 0x9747b28c;
|
||||
|
|
@ -41,8 +43,8 @@ export const RAGFlowAvatar = memo(
|
|||
>(({ name, avatar, isPerson = false, className, ...props }, ref) => {
|
||||
// Generate initial letter logic
|
||||
const getInitials = (name?: string) => {
|
||||
if (!name) return '';
|
||||
const parts = name.trim().split(/\s+/);
|
||||
if (typeof name !== 'string' || !name) return '';
|
||||
const parts = name?.trim().split(/\s+/);
|
||||
if (parts.length === 1) {
|
||||
return parts[0][0].toUpperCase();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -694,6 +694,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
|||
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: {
|
||||
selectModelPlaceholder: 'Select model',
|
||||
configureModelTitle: 'Configure model',
|
||||
confluenceIsCloudTip:
|
||||
'Check if this is a Confluence Cloud instance, uncheck for Confluence Server/Data Center',
|
||||
|
|
@ -729,7 +730,7 @@ Example: general/v2/`,
|
|||
google_driveSharedFoldersTip:
|
||||
'Comma-separated Google Drive folder links to crawl.',
|
||||
availableSourcesDescription: 'Select a data source to add',
|
||||
availableSources: 'Available Sources',
|
||||
availableSources: 'Available sources',
|
||||
datasourceDescription: 'Manage your data source and connections',
|
||||
save: 'Save',
|
||||
search: 'Search',
|
||||
|
|
@ -748,7 +749,7 @@ Example: general/v2/`,
|
|||
'Please enter your current password to change your password.',
|
||||
model: 'Model providers',
|
||||
systemModelDescription: 'Please complete these settings before beginning',
|
||||
dataSources: 'Data Sources',
|
||||
dataSources: 'Data sources',
|
||||
team: 'Team',
|
||||
system: 'System',
|
||||
logout: 'Log out',
|
||||
|
|
@ -924,8 +925,8 @@ Example: general/v2/`,
|
|||
invite: 'Invite Member',
|
||||
agree: 'Accept',
|
||||
refuse: 'Decline',
|
||||
teamMembers: 'Team Members',
|
||||
joinedTeams: 'Joined Teams',
|
||||
teamMembers: 'Team members',
|
||||
joinedTeams: 'Joined teams',
|
||||
sureDelete: 'Are you sure to remove this member?',
|
||||
quit: 'Quit',
|
||||
sureQuit: 'Are you sure you want to quit the team you joined?',
|
||||
|
|
@ -1825,7 +1826,7 @@ Important structured information may include: names, dates, locations, events, k
|
|||
addMCP: 'Add MCP',
|
||||
editMCP: 'Edit MCP',
|
||||
toolsAvailable: 'tools available',
|
||||
mcpServers: 'MCP Servers',
|
||||
mcpServers: 'MCP servers',
|
||||
customizeTheListOfMcpServers: 'Customize the list of MCP servers',
|
||||
cachedTools: 'cached tools',
|
||||
bulkManage: 'Bulk manage',
|
||||
|
|
|
|||
|
|
@ -684,6 +684,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||
tocEnhanceTip: `解析文档时生成了目录信息(见General方法的‘启用目录抽取’),让大模型返回和用户问题相关的目录项,从而利用目录项拿到相关chunk,对这些chunk在排序中进行加权。这种方法来源于模仿人类查询书本中知识的行为逻辑`,
|
||||
},
|
||||
setting: {
|
||||
selectModelPlaceholder: '请选择模型',
|
||||
configureModelTitle: '配置模型',
|
||||
confluenceIsCloudTip:
|
||||
'检查这是否是 Confluence Cloud 实例,如果是 Confluence 服务/数据中心,则取消选中。',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ const aspectRatio = {
|
|||
bottom: 704,
|
||||
};
|
||||
|
||||
export const BgSvg = () => {
|
||||
export const BgSvg = ({ isPaused = false }: { isPaused?: boolean }) => {
|
||||
const animationClass = isPaused ? 'paused' : '';
|
||||
|
||||
const def = (
|
||||
path: string,
|
||||
id: number | string = '',
|
||||
|
|
@ -124,7 +126,7 @@ export const BgSvg = () => {
|
|||
<div className="absolute inset-0 overflow-hidden pointer-events-none ">
|
||||
<div className="absolute top-0 left-0 right-0 w-full">
|
||||
<div
|
||||
className={`w-full ml-10`}
|
||||
className={`w-full ml-10 ${animationClass}`}
|
||||
style={{ height: aspectRatio['top'] + 'px' }}
|
||||
>
|
||||
{def(
|
||||
|
|
@ -134,7 +136,7 @@ export const BgSvg = () => {
|
|||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`w-full -mt-48`}
|
||||
className={`w-full -mt-48 ${animationClass}`}
|
||||
style={{ height: aspectRatio['middle'] + 'px' }}
|
||||
>
|
||||
{def(
|
||||
|
|
@ -144,7 +146,7 @@ export const BgSvg = () => {
|
|||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`w-full -mt-72`}
|
||||
className={`w-full -mt-72 ${animationClass}`}
|
||||
style={{ height: aspectRatio['bottom'] + 'px' }}
|
||||
>
|
||||
{def(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
.animate-glow {
|
||||
animation: glow 16s infinite linear;
|
||||
}
|
||||
.mask-path {
|
||||
stroke-width: 8;
|
||||
::after {
|
||||
|
|
@ -34,11 +31,20 @@
|
|||
stroke-dashoffset: -600;
|
||||
} /* 15+300-30=285 */
|
||||
}
|
||||
|
||||
.animate-glow {
|
||||
animation: glow 16s infinite linear;
|
||||
}
|
||||
.animate-highlight {
|
||||
animation: highlight-flow 16s linear infinite;
|
||||
}
|
||||
|
||||
.paused {
|
||||
.animate-glow,
|
||||
.animate-highlight {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
.perspective-1000 {
|
||||
perspective: 1000px;
|
||||
|
|
@ -47,11 +53,13 @@
|
|||
min-height: 680px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
will-change: transform;
|
||||
}
|
||||
.transform-style-3d {
|
||||
transform-style: preserve-3d;
|
||||
-webkit-transform-style: preserve-3d;
|
||||
transition-duration: 0.4s;
|
||||
will-change: transform;
|
||||
}
|
||||
.backface-hidden {
|
||||
backface-visibility: hidden;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ const Login = () => {
|
|||
useLoginWithChannel();
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'login' });
|
||||
const [isLoginPage, setIsLoginPage] = useState(true);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const [isUserInteracting, setIsUserInteracting] = useState(true);
|
||||
|
||||
const loading =
|
||||
signLoading ||
|
||||
registerLoading ||
|
||||
|
|
@ -152,9 +154,8 @@ const Login = () => {
|
|||
color={'rgb(128, 255, 248)'}
|
||||
/>
|
||||
<div className=" h-[inherit] relative overflow-auto">
|
||||
<BgSvg />
|
||||
<BgSvg isPaused={isUserInteracting} />
|
||||
|
||||
{/* <SpotlightTopRight opcity={0.7} coverage={10} /> */}
|
||||
<div className="absolute top-3 flex flex-col items-center mb-12 w-full text-text-primary">
|
||||
<div className="flex items-center mb-4 w-full pl-10 pt-10 ">
|
||||
<div className="w-12 h-12 p-2 rounded-lg flex items-center justify-center mr-3">
|
||||
|
|
@ -237,7 +238,7 @@ const Login = () => {
|
|||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
type={'password'}
|
||||
placeholder={t('passwordPlaceholder')}
|
||||
autoComplete={
|
||||
title === 'login'
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ export const DataSourceFormFields = {
|
|||
name: 'config.credentials.google_tokens',
|
||||
type: FormFieldType.Textarea,
|
||||
required: true,
|
||||
render: (fieldProps) => (
|
||||
render: (fieldProps: any) => (
|
||||
<GoogleDriveTokenField
|
||||
value={fieldProps.value}
|
||||
onChange={fieldProps.onChange}
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ const SystemSetting = ({ onOk, loading }: IProps) => {
|
|||
value={value}
|
||||
options={options}
|
||||
onChange={(value) => handleFieldChange(id, value)}
|
||||
placeholder={tcommon('selectPlaceholder')}
|
||||
placeholder={t('selectModelPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -123,16 +123,14 @@ export const AvailableModels: FC<{
|
|||
<div
|
||||
key={model.name}
|
||||
className=" border border-border-button rounded-lg p-3 hover:bg-bg-input transition-colors group"
|
||||
onClick={() => handleAddModel(model.name)}
|
||||
>
|
||||
<div className="flex items-center space-x-3 mb-3">
|
||||
<LlmIcon name={model.name} imgClass="h-8 w-8 text-text-primary" />
|
||||
<div className="flex-1">
|
||||
<h3 className="font-medium truncate">{model.name}</h3>
|
||||
</div>
|
||||
<Button
|
||||
className=" px-2 items-center gap-0 text-xs h-6 rounded-md transition-colors hidden group-hover:flex"
|
||||
onClick={() => handleAddModel(model.name)}
|
||||
>
|
||||
<Button className=" px-2 items-center gap-0 text-xs h-6 rounded-md transition-colors hidden group-hover:flex">
|
||||
<Plus size={12} />
|
||||
{t('addTheModel')}
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue