Compare commits

...

17 Commits

Author SHA1 Message Date
9de3ecc4a8 Fix: rm field not allow check (#12240)
### What problem does this PR solve?

Remove not allowed field check.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-12-26 16:43:46 +08:00
c4a66204f0 Fix: Memory-related bug fixes (#12238)
### What problem does this PR solve?

Fix: Memory-related bug fixes
- Forget memory button text
- Adjust memory storage interface
### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-12-26 15:56:41 +08:00
3558a6c170 Fix: allow update memory type (#12237)
### What problem does this PR solve?

Allow update memory_type.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-12-26 15:26:56 +08:00
595fc4ccec Feat: Display the selected list of memories in the retrieval node. #4213 (#12235)
### What problem does this PR solve?

Feat: Display the selected list of memories in the retrieval node. #4213

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
2025-12-26 15:20:40 +08:00
3ad147d349 Update deploy_local_llm.mdx with vllm guide support (#12222)
### What problem does this PR solve?

vllm guide support

### Type of change

- [x] Documentation Update
2025-12-26 15:14:25 +08:00
d285d8cd97 Fix: memory (#12230)
### What problem does this PR solve?

Judge has attr memory_ids

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-12-26 14:42:47 +08:00
5714895291 Fix message duration (#12233)
### What problem does this PR solve?

As title

### Type of change

- [x] Refactoring

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2025-12-26 14:40:46 +08:00
a33936e8ff Fix small issues on UI (#12231)
### What problem does this PR solve?

As title

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2025-12-26 14:21:59 +08:00
9f8161d13e Fix memory config: user prompt text box (#12229)
### What problem does this PR solve?

As title

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2025-12-26 14:05:58 +08:00
a599a0f4bf Fix forget policy (#12228)
### What problem does this PR solve?

As title

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2025-12-26 13:54:15 +08:00
7498bc63a3 Fix: judge retrieval from (#12223)
### What problem does this PR solve?

Judge retrieval from in retrieval component, and fix bug in message
component

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-12-26 13:01:46 +08:00
894bf995bb Fix: Memory-related bug fixes (#12226)
### What problem does this PR solve?

Fix: bugs fix
- table -> Table
- memory delete fail
- memory copywriting modified

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-12-26 12:24:05 +08:00
52dbacc506 Feat: Preview the image at the bottom of the message #12076 (#12225)
### What problem does this PR solve?

Feat: Preview the image at the bottom of the message #12076

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
2025-12-26 12:11:19 +08:00
cbcbbc41af Feat: The agent can only retrieve content from the knowledge base or memory. #4213 (#12224)
### What problem does this PR solve?

Feat: The agent can only retrieve content from the knowledge base or
memory. #4213

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
2025-12-26 12:10:13 +08:00
6044314811 Fix text issue (#12221)
### What problem does this PR solve?

Fix several text issues.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2025-12-26 11:18:08 +08:00
5fb38ecc2a Fix: Can not select LLM in memory page (#12219)
### What problem does this PR solve?

Fix: Can not select LLM in memory page

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-12-26 11:00:11 +08:00
73db759558 refactor: improve memory service date time consistency (#12144)
### What problem does this PR solve?

 improve memory service date time consistency

### Type of change

- [x] Refactoring
2025-12-26 09:54:38 +08:00
50 changed files with 674 additions and 220 deletions

View File

@ -427,7 +427,7 @@ class Message(ComponentBase):
logging.error(f"Error converting content to {self._param.output_format}: {e}")
async def _save_to_memory(self, content):
if not self._param.memory_ids:
if not hasattr(self._param, "memory_ids") or not self._param.memory_ids:
return True, "No memory selected."
message_dict = {

View File

@ -282,7 +282,11 @@ class Retrieval(ToolBase, ABC):
self.set_output("formalized_content", self._param.empty_response)
return
if self._param.kb_ids:
if hasattr(self._param, "retrieval_from") and self._param.retrieval_from == "dataset":
return await self._retrieve_kb(kwargs["query"])
elif hasattr(self._param, "retrieval_from") and self._param.retrieval_from == "memory":
return await self._retrieve_memory(kwargs["query"])
elif self._param.kb_ids:
return await self._retrieve_kb(kwargs["query"])
elif hasattr(self._param, "memory_ids") and self._param.memory_ids:
return await self._retrieve_memory(kwargs["query"])

View File

@ -21,11 +21,12 @@ from api.db import TenantPermission
from api.db.services.memory_service import MemoryService
from api.db.services.user_service import UserTenantService
from api.db.services.canvas_service import UserCanvasService
from api.utils.api_utils import validate_request, get_request_json, get_error_argument_result, get_json_result, \
not_allowed_parameters
from api.db.joint_services.memory_message_service import get_memory_size_cache, judge_system_prompt_is_default
from api.utils.api_utils import validate_request, get_request_json, get_error_argument_result, get_json_result
from api.utils.memory_utils import format_ret_data_from_memory, get_memory_type_human
from api.constants import MEMORY_NAME_LIMIT, MEMORY_SIZE_LIMIT
from memory.services.messages import MessageService
from memory.utils.prompt_util import PromptAssembler
from common.constants import MemoryType, RetCode, ForgettingPolicy
@ -68,7 +69,6 @@ async def create_memory():
@manager.route("/<memory_id>", methods=["PUT"]) # noqa: F821
@login_required
@not_allowed_parameters("id", "tenant_id", "memory_type", "storage_type", "embd_id")
async def update_memory(memory_id):
req = await get_request_json()
update_dict = {}
@ -88,6 +88,14 @@ async def update_memory(memory_id):
update_dict["permissions"] = req["permissions"]
if req.get("llm_id"):
update_dict["llm_id"] = req["llm_id"]
if req.get("embd_id"):
update_dict["embd_id"] = req["embd_id"]
if req.get("memory_type"):
memory_type = set(req["memory_type"])
invalid_type = memory_type - {e.name.lower() for e in MemoryType}
if invalid_type:
return get_error_argument_result(f"Memory type '{invalid_type}' is not supported.")
update_dict["memory_type"] = list(memory_type)
# check memory_size valid
if req.get("memory_size"):
if not 0 < int(req["memory_size"]) <= MEMORY_SIZE_LIMIT:
@ -123,6 +131,15 @@ async def update_memory(memory_id):
if not to_update:
return get_json_result(message=True, data=memory_dict)
# check memory empty when update embd_id, memory_type
memory_size = get_memory_size_cache(memory_id, current_memory.tenant_id)
not_allowed_update = [f for f in ["embd_id", "memory_type"] if f in to_update and memory_size > 0]
if not_allowed_update:
return get_error_argument_result(f"Can't update {not_allowed_update} when memory isn't empty.")
if "memory_type" in to_update:
if "system_prompt" not in to_update and judge_system_prompt_is_default(current_memory.system_prompt, current_memory.memory_type):
# update old default prompt, assemble a new one
to_update["system_prompt"] = PromptAssembler.assemble_system_prompt({"memory_type": to_update["memory_type"]})
try:
MemoryService.update_memory(current_memory.tenant_id, memory_id, to_update)

View File

@ -1189,7 +1189,7 @@ class Memory(DataBaseModel):
permissions = CharField(max_length=16, null=False, index=True, help_text="me|team", default="me")
description = TextField(null=True, help_text="description")
memory_size = IntegerField(default=5242880, null=False, index=False)
forgetting_policy = CharField(max_length=32, null=False, default="fifo", index=False, help_text="lru|fifo")
forgetting_policy = CharField(max_length=32, null=False, default="FIFO", index=False, help_text="LRU|FIFO")
temperature = FloatField(default=0.5, index=False)
system_prompt = TextField(null=True, help_text="system prompt", index=False)
user_prompt = TextField(null=True, help_text="user prompt", index=False)

View File

@ -96,7 +96,7 @@ async def save_to_memory(memory_id: str, message_dict: dict):
current_memory_size = get_memory_size_cache(memory_id, tenant_id)
if new_msg_size + current_memory_size > memory.memory_size:
size_to_delete = current_memory_size + new_msg_size - memory.memory_size
if memory.forgetting_policy == "fifo":
if memory.forgetting_policy == "FIFO":
message_ids_to_delete, delete_size = MessageService.pick_messages_to_delete_by_fifo(memory_id, tenant_id, size_to_delete)
MessageService.delete_message({"message_id": message_ids_to_delete}, tenant_id, memory_id)
decrease_memory_size_cache(memory_id, delete_size)
@ -231,3 +231,8 @@ def init_memory_size_cache():
memory_size = memory_size_map.get(memory.id, 0)
set_memory_size_cache(memory.id, memory_size)
logging.info("Memory size cache init done.")
def judge_system_prompt_is_default(system_prompt: str, memory_type: int|list[str]):
memory_type_list = memory_type if isinstance(memory_type, list) else get_memory_type_human(memory_type)
return system_prompt == PromptAssembler.assemble_system_prompt({"memory_type": memory_type_list})

View File

@ -117,6 +117,8 @@ class MemoryService(CommonService):
if len(memory_name) > MEMORY_NAME_LIMIT:
return False, f"Memory name {memory_name} exceeds limit of {MEMORY_NAME_LIMIT}."
timestamp = current_timestamp()
format_time = get_format_time()
# build create dict
memory_info = {
"id": get_uuid(),
@ -126,10 +128,10 @@ class MemoryService(CommonService):
"embd_id": embd_id,
"llm_id": llm_id,
"system_prompt": PromptAssembler.assemble_system_prompt({"memory_type": memory_type}),
"create_time": current_timestamp(),
"create_date": get_format_time(),
"update_time": current_timestamp(),
"update_date": get_format_time(),
"create_time": timestamp,
"create_date": format_time,
"update_time": timestamp,
"update_date": format_time,
}
obj = cls.model(**memory_info).save(force_insert=True)
@ -147,6 +149,8 @@ class MemoryService(CommonService):
return 0
if "temperature" in update_dict and isinstance(update_dict["temperature"], str):
update_dict["temperature"] = float(update_dict["temperature"])
if "memory_type" in update_dict and isinstance(update_dict["memory_type"], list):
update_dict["memory_type"] = calculate_memory_type(update_dict["memory_type"])
if "name" in update_dict:
update_dict["name"] = duplicate_name(
cls.query,

View File

@ -171,7 +171,7 @@ class MemoryStorageType(StrEnum):
class ForgettingPolicy(StrEnum):
FIFO = "fifo"
FIFO = "FIFO"
# environment

View File

@ -7,7 +7,7 @@ slug: /deploy_local_llm
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Deploy and run local models using Ollama, Xinference, or other frameworks.
Deploy and run local models using Ollama, Xinference, VLLM SGLANG or other frameworks.
---
@ -314,3 +314,41 @@ To enable IPEX-LLM accelerated Ollama in RAGFlow, you must also complete the con
3. [Update System Model Settings](#6-update-system-model-settings)
4. [Update Chat Configuration](#7-update-chat-configuration)
### 5. Deploy VLLM
ubuntu 22.04/24.04
```bash
pip install vllm
```
### 5.1 RUN VLLM WITH BEST PRACTISE
```bash
nohup vllm serve /data/Qwen3-8B --served-model-name Qwen3-8B-FP8 --dtype auto --port 1025 --gpu-memory-utilization 0.90 --tool-call-parser hermes --enable-auto-tool-choice > /var/log/vllm_startup1.log 2>&1 &
```
you can get log info
```bash
tail -f -n 100 /var/log/vllm_startup1.log
```
when see the follow ,it means vllm engine is ready for access
```bash
Starting vLLM API server 0 on http://0.0.0.0:1025
Started server process [19177]
Application startup complete.
```
### 5.2 INTERGRATEING RAGFLOW WITH VLLM CHAT/EM/RERANK LLM WITH WEBUI
setting->model providers->search->vllm->add ,configure as follow:<br>
![add vllm](https://github.com/user-attachments/assets/6f1d9f1a-3507-465b-87a3-4427254fff86)
select vllm chat model as default llm model as follow:<br>
![chat](https://github.com/user-attachments/assets/05efbd4b-2c18-4c6b-8d1c-52bae712372d)
### 5.3 chat with vllm chat model
create chat->create conversations-chat as follow:<br>
![chat](https://github.com/user-attachments/assets/dc1885f6-23a9-48f1-8850-d5f59b5e8f67)

26
web/package-lock.json generated
View File

@ -90,6 +90,7 @@
"react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1",
"react-pdf-highlighter": "^6.1.0",
"react-photo-view": "^1.2.7",
"react-resizable-panels": "^3.0.6",
"react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0",
@ -30404,6 +30405,16 @@
}
}
},
"node_modules/react-photo-view": {
"version": "1.2.7",
"resolved": "https://registry.npmmirror.com/react-photo-view/-/react-photo-view-1.2.7.tgz",
"integrity": "sha512-MfOWVPxuibncRLaycZUNxqYU8D9IA+rbGDDaq6GM8RIoGJal592hEJoRAyRSI7ZxyyJNJTLMUWWL3UIXHJJOpw==",
"license": "Apache-2.0",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.0.tgz",
@ -36191,6 +36202,21 @@
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tinyrainbow": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz",

View File

@ -103,6 +103,7 @@
"react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1",
"react-pdf-highlighter": "^6.1.0",
"react-photo-view": "^1.2.7",
"react-resizable-panels": "^3.0.6",
"react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0",

View File

@ -2,6 +2,7 @@ import { Toaster as Sonner } from '@/components/ui/sonner';
import { Toaster } from '@/components/ui/toaster';
import i18n from '@/locales/config';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { configResponsive } from 'ahooks';
import { App, ConfigProvider, ConfigProviderProps, theme } from 'antd';
import pt_BR from 'antd/lib/locale/pt_BR';
import deDE from 'antd/locale/de_DE';
@ -24,7 +25,7 @@ import { TooltipProvider } from './components/ui/tooltip';
import { ThemeEnum } from './constants/common';
import storage from './utils/authorization-util';
import { configResponsive } from 'ahooks';
import 'react-photo-view/dist/react-photo-view.css';
configResponsive({
sm: 640,

View File

@ -91,13 +91,13 @@ export function ConfirmDeleteDialog({
</AlertDialogHeader>
<AlertDialogFooter className="px-5 flex items-center gap-2">
<AlertDialogCancel onClick={onCancel}>
{okButtonText || t('common.cancel')}
{cancelButtonText || t('common.cancel')}
</AlertDialogCancel>
<AlertDialogAction
className="bg-state-error text-text-primary hover:text-text-primary hover:bg-state-error"
onClick={onOk}
>
{cancelButtonText || t('common.delete')}
{okButtonText || t('common.delete')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>

View File

@ -7,8 +7,11 @@ import {
CarouselPrevious,
} from '@/components/ui/carousel';
import { IReferenceChunk } from '@/interfaces/database/chat';
import { api_host } from '@/utils/api';
import { isPlainObject } from 'lodash';
import { RotateCw, ZoomIn, ZoomOut } from 'lucide-react';
import { useMemo } from 'react';
import { PhotoProvider, PhotoView } from 'react-photo-view';
import { extractNumbersFromMessageContent } from './utils';
type IProps = {
@ -36,35 +39,60 @@ function ImageCarousel({ images }: { images: ImageItem[] }) {
const buttonVisibilityClass = getButtonVisibilityClass(images.length);
return (
<Carousel
className="w-full"
opts={{
align: 'start',
<PhotoProvider
// className="[&_.PhotoView-Slider__toolbarIcon]:hidden"
toolbarRender={({ rotate, onRotate, scale, onScale }) => {
return (
<>
<RotateCw
className="mr-4 cursor-pointer text-text-disabled hover:text-text-primary"
onClick={() => onRotate(rotate + 90)}
/>
<ZoomIn
className="mr-4 cursor-pointer text-text-disabled hover:text-text-primary"
onClick={() => onScale(scale + 1)}
/>
<ZoomOut
className="cursor-pointer text-text-disabled hover:text-text-primary"
onClick={() => onScale(scale - 1)}
/>
{/* <X className="cursor-pointer text-text-disabled hover:text-text-primary" /> */}
</>
);
}}
>
<CarouselContent>
{images.map(({ id, index }) => (
<CarouselItem
key={index}
className="
basis-full
@sm:basis-1/2
@md:basis-1/3
@lg:basis-1/4
@2xl:basis-1/6
"
>
<Image
id={id}
className="h-40 w-full"
label={`Fig. ${(index + 1).toString()}`}
/>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className={buttonVisibilityClass} />
<CarouselNext className={buttonVisibilityClass} />
</Carousel>
<Carousel
className="w-full"
opts={{
align: 'start',
}}
>
<CarouselContent>
{images.map(({ id, index }) => (
<CarouselItem
key={index}
className="
basis-full
@sm:basis-1/2
@md:basis-1/3
@lg:basis-1/4
@2xl:basis-1/6
"
>
<PhotoView src={`${api_host}/document/image/${id}`}>
<Image
id={id}
className="h-40 w-full"
label={`Fig. ${(index + 1).toString()}`}
/>
</PhotoView>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className={buttonVisibilityClass} />
<CarouselNext className={buttonVisibilityClass} />
</Carousel>
</PhotoProvider>
);
}

View File

@ -1,6 +1,6 @@
import { toast } from 'sonner';
const duration = { duration: 1500 };
const duration = { duration: 2500 };
const message = {
success: (msg: string) => {

View File

@ -31,6 +31,7 @@ export interface ModalProps {
cancelButtonClassName?: string;
disabled?: boolean;
style?: React.CSSProperties;
zIndex?: number;
}
export interface ModalType extends FC<ModalProps> {
show: typeof modalIns.show;
@ -63,6 +64,7 @@ const Modal: ModalType = ({
cancelButtonClassName,
disabled = false,
style,
zIndex = 50,
}) => {
const sizeClasses = {
small: 'max-w-md',
@ -172,6 +174,7 @@ const Modal: ModalType = ({
<DialogPrimitive.Overlay
className="fixed inset-0 z-[1000] bg-bg-card backdrop-blur-[1px] flex items-center justify-center p-4"
onClick={() => maskClosable && onOpenChange?.(false)}
style={{ zIndex: zIndex }}
>
<DialogPrimitive.Content
className={cn(

View File

@ -102,6 +102,7 @@ export const useShowDeleteConfirm = () => {
style: {
width: '450px',
},
zIndex: 1000,
okButtonClassName:
'bg-state-error text-white hover:bg-state-error hover:text-white',
onOk: async () => {

View File

@ -266,20 +266,19 @@ export const useRunDocument = () => {
mutationFn: async ({
documentIds,
run,
shouldDelete,
option,
}: {
documentIds: string[];
run: number;
shouldDelete: boolean;
option?: { delete: boolean; apply_kb: boolean };
}) => {
queryClient.invalidateQueries({
queryKey: [DocumentApiAction.FetchDocumentList],
});
const ret = await kbService.document_run({
doc_ids: documentIds,
run,
delete: shouldDelete,
...option,
});
const code = get(ret, 'data.code');
if (code === 0) {

View File

@ -79,7 +79,7 @@ export function Header() {
{ path: Routes.Chats, name: t('header.chat'), icon: MessageSquareText },
{ path: Routes.Searches, name: t('header.search'), icon: Search },
{ path: Routes.Agents, name: t('header.flow'), icon: Cpu },
{ path: Routes.Memories, name: t('header.Memories'), icon: Cpu },
{ path: Routes.Memories, name: t('header.memories'), icon: Cpu },
{ path: Routes.Files, name: t('header.fileManager'), icon: File },
],
[t],

View File

@ -417,7 +417,7 @@ Prozedurales Gedächtnis: Erlernte Fähigkeiten, Gewohnheiten und automatisierte
fileFilter: 'Dateifilter',
setDefaultTip: '',
setDefault: 'Als Standard festlegen',
eidtLinkDataPipeline: 'Ingestion-Pipeline bearbeiten',
editLinkDataPipeline: 'Ingestion-Pipeline bearbeiten',
linkPipelineSetTip:
'Verknüpfung der Ingestion-Pipeline mit diesem Datensatz verwalten',
default: 'Standard',
@ -900,7 +900,7 @@ Beispiel: general/v2/`,
Beispiel: https://fsn1.your-objectstorage.com`,
S3CompatibleAddressingStyleTip: `Erforderlich für S3-kompatible Storage Box. Geben Sie den S3-kompatiblen Adressierungsstil an.
Beispiel: Virtual Hosted Style`,
addDataSourceModalTital: 'Erstellen Sie Ihren {{name}} Connector',
addDataSourceModalTitle: 'Erstellen Sie Ihren {{name}} Connector',
deleteSourceModalTitle: 'Datenquelle löschen',
deleteSourceModalContent: `
<p>Sind Sie sicher, dass Sie diese Datenquellenverknüpfung löschen möchten?</p>`,
@ -1357,12 +1357,12 @@ Beispiel: Virtual Hosted Style`,
search: 'Suchen',
communication: 'Kommunikation',
developer: 'Entwickler',
typeCommandOrsearch: 'Geben Sie einen Befehl ein oder suchen Sie...',
typeCommandORsearch: 'Geben Sie einen Befehl ein oder suchen Sie...',
builtIn: 'Eingebaut',
ExceptionDefaultValue: 'Ausnahme-Standardwert',
exceptionMethod: 'Ausnahmemethode',
maxRounds: 'Maximale Reflexionsrunden',
delayEfterError: 'Verzögerung nach Fehler',
delayAfterError: 'Verzögerung nach Fehler',
maxRetries: 'Maximale Wiederholungsrunden',
advancedSettings: 'Erweiterte Einstellungen',
addTools: 'Tools hinzufügen',
@ -1882,7 +1882,7 @@ Beispiel: Virtual Hosted Style`,
}`,
datatype: 'MIME-Typ der HTTP-Anfrage',
insertVariableTip: 'Eingabe / Variablen einfügen',
historyversion: 'Versionsverlauf',
historyVersion: 'Versionsverlauf',
version: {
created: 'Erstellt',
details: 'Versionsdetails',

View File

@ -99,7 +99,7 @@ export default {
search: 'Search',
welcome: 'Welcome to',
dataset: 'Dataset',
Memories: 'Memory',
memories: 'Memory',
},
memories: {
llmTooltip:
@ -112,6 +112,10 @@ export default {
Semantic Memory: General knowledge and facts about the user and world.
Episodic Memory: Time-stamped records of specific events and experiences.
Procedural Memory: Learned skills, habits, and automated procedures.`,
raw: 'raw',
semantic: 'semantic',
episodic: 'episodic',
procedural: 'procedural',
editName: 'Edit name',
memory: 'Memory',
createMemory: 'Create memory',
@ -125,8 +129,9 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
},
memory: {
messages: {
forgetMessageTip: 'Are you sure you want to forget?',
messageDescription:
'Memory retrieval is configured with Similarity threshold, Keyword similarity weight, and Top N from Advanced Settings.',
'Memory extract is configured with Prompts and Temperature from Advanced Settings.',
copied: 'Copied!',
contentEmbed: 'Content embed',
content: 'Content',
@ -145,17 +150,17 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
avatar: 'Avatar',
description: 'Description',
memorySize: 'Memory size',
advancedSettings: 'Advanced Settings',
advancedSettings: 'Advanced settings',
permission: 'Permission',
onlyMe: 'Only Me',
onlyMe: 'Only me',
team: 'Team',
storageType: 'Storage Type',
storageType: 'Storage type',
storageTypePlaceholder: 'Please select storage type',
forgetPolicy: 'Forget Policy',
forgetPolicy: 'Forget policy',
temperature: 'Temperature',
systemPrompt: 'System Prompt',
systemPrompt: 'System prompt',
systemPromptPlaceholder: 'Please enter system prompt',
userPrompt: 'User Prompt',
userPrompt: 'User prompt',
userPromptPlaceholder: 'Please enter user prompt',
},
sideBar: {
@ -166,7 +171,7 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
knowledgeList: {
welcome: 'Welcome back',
description: 'Which knowledge bases will you use today?',
createKnowledgeBase: 'Create Dataset',
createKnowledgeBase: 'Create dataset',
name: 'Name',
namePlaceholder: 'Please input name.',
doc: 'Docs',
@ -216,6 +221,10 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
deleteSettingFieldWarn: `This field will be deleted; existing metadata won't be affected.`,
deleteSettingValueWarn: `This value will be deleted; existing metadata won't be affected.`,
},
redoAll: 'Clear existing chunks',
applyAutoMetadataSettings: 'Apply global auto-metadata settings',
parseFileTip: 'Are you sure to parse?',
parseFile: 'Parse file',
emptyMetadata: 'No metadata',
metadataField: 'Metadata field',
systemAttribute: 'System attribute',
@ -364,11 +373,11 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
autoQuestions: 'Auto-question',
autoQuestionsTip: `Automatically extract N questions for each chunk to increase their ranking for queries containing those questions. You can check or update the added questions for a chunk from the chunk list. This feature will not disrupt the chunking process if an error occurs, except that it may add an empty result to the original chunk. Be aware that extra tokens will be consumed by the indexing model specified in 'Configuration'. For details, see https://ragflow.io/docs/dev/autokeyword_autoquestion.`,
redo: 'Do you want to clear the existing {{chunkNum}} chunks?',
setMetaData: 'Set meta data',
setMetaData: 'Set metadata',
pleaseInputJson: 'Please enter JSON',
documentMetaTips: `<p>The meta data is in Json format(it's not searchable). It will be added into prompt for LLM if any chunks of this document are included in the prompt.</p>
documentMetaTips: `<p>The metadata is in Json format(it's not searchable). It will be added into prompt for LLM if any chunks of this document are included in the prompt.</p>
<p>Examples:</p>
<b>The meta data is:</b><br>
<b>The metadata is:</b><br>
<code>
{
"Author": "Alex Dowson",
@ -401,14 +410,14 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
imageTableContextWindowTip:
'Captures N tokens of text above and below the image & table to provide richer background context.',
autoMetadata: 'Auto metadata',
mineruOptions: 'MinerU Options',
mineruParseMethod: 'Parse Method',
mineruOptions: 'MinerU options',
mineruParseMethod: 'Parse method',
mineruParseMethodTip:
'Method for parsing PDF: auto (automatic detection), txt (text extraction), ocr (optical character recognition)',
mineruFormulaEnable: 'Formula Recognition',
mineruFormulaEnable: 'Formula recognition',
mineruFormulaEnableTip:
'Enable formula recognition. Note: This may not work correctly for Cyrillic documents.',
mineruTableEnable: 'Table Recognition',
mineruTableEnable: 'Table recognition',
mineruTableEnableTip: 'Enable table recognition and extraction.',
overlappedPercent: 'Overlapped percent(%)',
generationScopeTip:
@ -441,11 +450,11 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
fileFilter: 'File filter',
setDefaultTip: '',
setDefault: 'Set as default',
eidtLinkDataPipeline: 'Edit Ingestion pipeline',
linkPipelineSetTip: 'Manage Ingestion pipeline linkage with this dataset',
editLinkDataPipeline: 'Edit ingestion pipeline',
linkPipelineSetTip: 'Manage ingestion pipeline linkage with this dataset',
default: 'Default',
dataPipeline: 'Ingestion pipeline',
linkDataPipeline: 'Link Ingestion pipeline',
linkDataPipeline: 'Link ingestion pipeline',
enableAutoGenerate: 'Enable auto generate',
teamPlaceholder: 'Please select a team.',
dataFlowPlaceholder: 'Please select a pipeline.',
@ -455,7 +464,7 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
manualSetup: 'Choose pipeline',
builtIn: 'Built-in',
titleDescription:
'Update your knowledge base configuration here, particularly the chunking method.',
'Update your memory configuration here, particularly the LLM and prompts.',
name: 'Knowledge base name',
photo: 'Knowledge base photo',
photoTip: 'You can upload a file with 4 MB',
@ -756,8 +765,8 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
maxTokens: 'Max tokens',
maxTokensMessage: 'Max tokens is required',
maxTokensTip: `This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). Defaults to 512. If disabled, you lift the maximum token limit, allowing the model to determine the number of tokens in its responses.`,
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
maxTokensInvalidMessage: 'Please enter a valid number for Max tokens.',
maxTokensMinMessage: 'Max tokens cannot be less than 0.',
quote: 'Show quote',
quoteTip: 'Whether to display the original text as a reference.',
selfRag: 'Self-RAG',
@ -878,7 +887,7 @@ Example: general/v2/`,
Example: https://fsn1.your-objectstorage.com`,
S3CompatibleAddressingStyleTip: `Required for S3 compatible Storage Box. Specify the S3-compatible addressing style.
Example: Virtual Hosted Style`,
addDataSourceModalTital: 'Create your {{name}} connector',
addDataSourceModalTitle: 'Create your {{name}} connector',
deleteSourceModalTitle: 'Delete data source',
deleteSourceModalContent: `
<p>Are you sure you want to delete this data source link?</p>`,
@ -970,10 +979,10 @@ Example: Virtual Hosted Style`,
avatarTip: 'This will be displayed on your profile.',
profileDescription: 'Update your photo and personal details here.',
maxTokens: 'Max tokens',
maxTokensMessage: 'Max Tokens is required',
maxTokensMessage: 'Max tokens is required',
maxTokensTip: `This sets the maximum length of the model's output, measured in the number of tokens (words or pieces of words). Defaults to 512. If disabled, you lift the maximum token limit, allowing the model to determine the number of tokens in its responses.`,
maxTokensInvalidMessage: 'Please enter a valid number for Max Tokens.',
maxTokensMinMessage: 'Max Tokens cannot be less than 0.',
maxTokensInvalidMessage: 'Please enter a valid number for Max tokens.',
maxTokensMinMessage: 'Max tokens cannot be less than 0.',
password: 'Password',
passwordDescription:
'Please enter your current password to change your password.',
@ -1322,12 +1331,12 @@ Example: Virtual Hosted Style`,
search: 'Search',
communication: 'Communication',
developer: 'Developer',
typeCommandOrsearch: 'Type a command or search...',
typeCommandORsearch: 'Type a command or search...',
builtIn: 'Built-in',
ExceptionDefaultValue: 'Exception default value',
exceptionMethod: 'Exception method',
maxRounds: 'Max reflection rounds',
delayEfterError: 'Delay after error',
delayAfterError: 'Delay after error',
maxRetries: 'Max retry rounds',
advancedSettings: 'Advanced settings',
addTools: 'Add tools',
@ -1834,7 +1843,7 @@ This delimiter is used to split the input text into several text pieces echo of
}`,
datatype: 'MINE type of the HTTP request',
insertVariableTip: `Enter / Insert variables`,
historyversion: 'Version history',
historyVersion: 'Version history',
version: {
created: 'Created',
details: 'Version details',
@ -2169,7 +2178,7 @@ Important structured information may include: names, dates, locations, events, k
agentStatus: 'Agent status:',
},
saveToMemory: 'Save to memory',
memory: 'Memory',
retrievalFrom: 'Retrieval from',
},
llmTools: {
bad_calculator: {
@ -2247,7 +2256,7 @@ Important structured information may include: names, dates, locations, events, k
dataflowParser: {
result: 'Result',
parseSummary: 'Parse summary',
parseSummaryTip: 'Parserdeepdoc',
parseSummaryTip: 'ParserDeepDoc',
parserMethod: 'Parser method',
outputFormat: 'Output format',
rerunFromCurrentStep: 'Rerun from current step',
@ -2270,10 +2279,10 @@ Important structured information may include: names, dates, locations, events, k
<p>To keep them, please click Rerun to re-run the current stage.</p> `,
changeStepModalConfirmText: 'Switch Anyway',
changeStepModalCancelText: 'Cancel',
unlinkPipelineModalTitle: 'Unlink Ingestion pipeline',
unlinkPipelineModalTitle: 'Unlink ingestion pipeline',
unlinkPipelineModalConfirmText: 'Unlink',
unlinkPipelineModalContent: `
<p>Once unlinked, this Dataset will no longer be connected to the current Ingestion pipeline.</p>
<p>Once unlinked, this Dataset will no longer be connected to the current ingestion pipeline.</p>
<p>Files that are already being parsed will continue until completion</p>
<p>Files that are not yet parsed will no longer be processed</p> <br/>
<p>Are you sure you want to proceed?</p> `,
@ -2284,7 +2293,7 @@ Important structured information may include: names, dates, locations, events, k
},
datasetOverview: {
downloadTip: 'Files being downloaded from data sources. ',
processingTip: 'Files being processed by Ingestion pipeline.',
processingTip: 'Files being processed by ingestion pipeline.',
totalFiles: 'Total files',
downloading: 'Downloading',
downloadSuccessTip: 'Total successful downloads',

View File

@ -1191,7 +1191,7 @@ export default {
}`,
datatype: 'Type MIME de la requête HTTP',
insertVariableTip: `Entrer / Insérer des variables`,
historyversion: 'Historique des versions',
historyVersion: 'Historique des versions',
version: {
created: 'Créé',
details: 'Détails de la version',

View File

@ -325,7 +325,7 @@ export default {
fileFilter: 'Filtro file',
setDefaultTip: '',
setDefault: 'Imposta come predefinito',
eidtLinkDataPipeline: 'Modifica pipeline di ingestione',
editLinkDataPipeline: 'Modifica pipeline di ingestione',
linkPipelineSetTip:
'Gestisci il collegamento della pipeline di ingestione con questo dataset',
default: 'Predefinito',

View File

@ -311,7 +311,7 @@ export default {
fileFilter: 'Фильтр файлов',
setDefaultTip: '',
setDefault: 'Установить по умолчанию',
eidtLinkDataPipeline: 'Редактировать пайплайн обработки',
editLinkDataPipeline: 'Редактировать пайплайн обработки',
linkPipelineSetTip:
'Управление связью пайплайна обработки с этим набором данных',
default: 'По умолчанию',
@ -722,7 +722,7 @@ export default {
Пример: https://fsn1.your-objectstorage.com`,
S3CompatibleAddressingStyleTip: `Требуется для S3 совместимого Storage Box. Укажите стиль адресации, совместимый с S3.
Пример: Virtual Hosted Style`,
addDataSourceModalTital: 'Создайте ваш коннектор {{name}}',
addDataSourceModalTitle: 'Создайте ваш коннектор {{name}}',
deleteSourceModalTitle: 'Удалить источник данных',
deleteSourceModalContent: `
<p>Вы уверены, что хотите удалить эту ссылку на источник данных?</p>`,
@ -1133,12 +1133,12 @@ export default {
search: 'Поиск',
communication: 'Коммуникация',
developer: 'Разработчик',
typeCommandOrsearch: 'Введите команду или поиск...',
typeCommandORsearch: 'Введите команду или поиск...',
builtIn: 'Встроенный',
ExceptionDefaultValue: 'Значение по умолчанию при исключении',
exceptionMethod: 'Метод обработки исключений',
maxRounds: 'Макс. раундов рефлексии',
delayEfterError: 'Задержка после ошибки',
delayAfterError: 'Задержка после ошибки',
maxRetries: 'Макс. попыток повтора',
advancedSettings: 'Расширенные настройки',
addTools: 'Добавить инструменты',
@ -1637,7 +1637,7 @@ export default {
}`,
datatype: 'MIME тип HTTP запроса',
insertVariableTip: `Введите / Вставьте переменные`,
historyversion: 'История версий',
historyVersion: 'История версий',
version: {
created: 'Создано',
details: 'Детали версии',

View File

@ -93,17 +93,21 @@ export default {
search: '搜索',
welcome: '欢迎来到',
dataset: '知识库',
Memories: '记忆',
memories: '记忆',
},
memories: {
llmTooltip: '分析对话内容,提取关键信息,并生成结构化的记忆摘要。',
embeddingModelTooltip:
'将文本转换为数值向量,用于语义相似度搜索和记忆检索。',
embeddingModelError: '记忆类型为必填项,且"原始"类型不可删除。',
embeddingModelError: '记忆类型为必填项,且"row"类型不可删除。',
memoryTypeTooltip: `原始: 用户与代理之间的原始对话内容(默认必需)。
语义记忆: 关于用户和世界的通用知识和事实。
情景记忆: 带时间戳的特定事件和经历记录。
程序记忆: 学习的技能、习惯和自动化程序。`,
raw: '原始',
semantic: '语义',
episodic: '情景',
procedural: '程序',
editName: '编辑名称',
memory: '记忆',
createMemory: '创建记忆',
@ -117,8 +121,8 @@ export default {
},
memory: {
messages: {
messageDescription:
'记忆检索已在高级设置中配置相似度阈值、关键词相似度权重和前N个结果。',
forgetMessageTip: '确定遗忘吗?',
messageDescription: '记忆提取使用高级设置中的提示词和温度值进行配置。',
copied: '已复制!',
contentEmbed: '内容嵌入',
content: '内容',
@ -201,6 +205,10 @@ export default {
deleteSettingFieldWarn: `此字段将被删除;现有元数据不会受到影响。`,
deleteSettingValueWarn: `此值将被删除;现有元数据不会受到影响。`,
},
redoAll: '清除现有分块',
applyAutoMetadataSettings: '应用全局自动元数据设置',
parseFileTip: '您确定要解析吗?',
parseFile: '解析文件',
emptyMetadata: '无元数据',
localUpload: '本地上传',
fileSize: '文件大小',
@ -405,7 +413,7 @@ export default {
fileFilter: '正则匹配表达式',
setDefaultTip: '',
setDefault: '设置默认',
eidtLinkDataPipeline: '编辑pipeline',
editLinkDataPipeline: '编辑pipeline',
linkPipelineSetTip: '管理与此数据集的数据管道链接',
default: '默认',
dataPipeline: 'Ingestion pipeline',
@ -418,7 +426,7 @@ export default {
parseType: '解析方法',
manualSetup: '选择pipeline',
builtIn: '内置',
titleDescription: '在这里更新您的知识库详细信息,尤其是切片方法。',
titleDescription: '在这里更新您的记忆配置,特别是大语言模型和提示词。',
name: '知识库名称',
photo: '知识库图片',
photoTip: '你可以上传4MB的文件',
@ -820,7 +828,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
'可选指定空间键以限制同步到特定空间。留空则同步所有可访问的空间。多个空间请用逗号分隔例如DEV,DOCS,HR',
s3PrefixTip: `指定 S3 存储桶内的文件夹路径,用于读取文件。
示例general/v2/`,
addDataSourceModalTital: '创建你的 {{name}} 链接',
addDataSourceModalTitle: '创建你的 {{name}} 链接',
deleteSourceModalTitle: '删除数据源链接',
deleteSourceModalContent: `
<p>您确定要删除此数据源链接吗?</p>`,
@ -1202,14 +1210,14 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
search: '搜索',
communication: '通信',
developer: '开发者',
typeCommandOrsearch: '输入命令或或搜索...',
typeCommandORsearch: '输入命令或或搜索...',
builtIn: '内置',
goto: '异常分支',
comment: '默认值',
ExceptionDefaultValue: '异常处理默认值',
exceptionMethod: '异常处理方法',
maxRounds: '最大反思轮数',
delayEfterError: '错误后延迟',
delayAfterError: '错误后延迟',
maxRetries: '最大重试轮数',
advancedSettings: '高级设置',
addTools: '添加工具',
@ -1246,7 +1254,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
dialog: '对话',
flow: '工作流',
noMoreData: '没有更多数据了',
historyversion: '历史版本',
historyVersion: '历史版本',
version: {
details: '版本详情',
download: '下载',
@ -1970,7 +1978,7 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
agentStatus: '智能体状态:',
},
saveToMemory: '保存到Memory',
memory: 'Memory',
retrievalFrom: '检索来源',
},
footer: {
profile: 'All rights reserved @ React',

View File

@ -1,13 +1,16 @@
import { NodeCollapsible } from '@/components/collapse';
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import { useFetchKnowledgeList } from '@/hooks/use-knowledge-request';
import { IRetrievalNode } from '@/interfaces/database/flow';
import { useFetchAllMemoryList } from '@/hooks/use-memory-request';
import { BaseNode } from '@/interfaces/database/flow';
import { NodeProps, Position } from '@xyflow/react';
import classNames from 'classnames';
import { get } from 'lodash';
import { memo } from 'react';
import { NodeHandleId } from '../../constant';
import { NodeHandleId, RetrievalFrom } from '../../constant';
import { RetrievalFormSchemaType } from '../../form/retrieval-form/next';
import { useGetVariableLabelOrTypeByValue } from '../../hooks/use-get-begin-query';
import { LabelCard } from './card';
import { CommonHandle, LeftEndHandle } from './handle';
import styles from './index.less';
import NodeHeader from './node-header';
@ -19,12 +22,17 @@ function InnerRetrievalNode({
data,
isConnectable = true,
selected,
}: NodeProps<IRetrievalNode>) {
}: NodeProps<BaseNode<RetrievalFormSchemaType>>) {
const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []);
const memoryIds: string[] = get(data, 'form.memory_ids', []);
const { list: knowledgeList } = useFetchKnowledgeList(true);
const { getLabel } = useGetVariableLabelOrTypeByValue({ nodeId: id });
const isMemory = data.form?.retrieval_from === RetrievalFrom.Memory;
const memoryList = useFetchAllMemoryList();
return (
<ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper selected={selected} id={id}>
@ -45,8 +53,22 @@ function InnerRetrievalNode({
[styles.nodeHeader]: knowledgeBaseIds.length > 0,
})}
></NodeHeader>
<NodeCollapsible items={knowledgeBaseIds}>
<NodeCollapsible items={isMemory ? memoryIds : knowledgeBaseIds}>
{(id) => {
if (isMemory) {
const item = memoryList.data?.find((y) => id === y.id);
return (
<LabelCard key={id} className="flex items-center gap-1.5">
<RAGFlowAvatar
className="size-6 rounded-lg"
avatar={item?.avatar ?? ''}
name={item ? item?.name : id}
/>
<span className="flex-1 truncate"> {item?.name}</span>
</LabelCard>
);
}
const item = knowledgeList.find((y) => id === y.id);
const label = getLabel(id);

View File

@ -79,6 +79,11 @@ export const DataOperationsOperatorOptions = [
export const SwitchElseTo = 'end_cpn_ids';
export enum RetrievalFrom {
Dataset = 'dataset',
Memory = 'memory',
}
export const initialRetrievalValues = {
query: AgentGlobalsSysQueryWithBrace,
top_n: 8,
@ -91,6 +96,7 @@ export const initialRetrievalValues = {
use_kg: false,
toc_enhance: false,
cross_languages: [],
retrieval_from: RetrievalFrom.Dataset,
outputs: {
formalized_content: {
type: 'string',

View File

@ -242,7 +242,7 @@ function AgentForm({ node }: INextOperatorForm) {
name={`delay_after_error`}
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>{t('flow.delayEfterError')}</FormLabel>
<FormLabel>{t('flow.delayAfterError')}</FormLabel>
<FormControl>
<NumberInput {...field} max={5} step={0.1}></NumberInput>
</FormControl>

View File

@ -115,7 +115,7 @@ export function ToolCommand({ value, onChange }: ToolCommandProps) {
return (
<Command>
<CommandInput placeholder={t('flow.typeCommandOrsearch')} />
<CommandInput placeholder={t('flow.typeCommandORsearch')} />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{Menus.map((x) => (

View File

@ -20,14 +20,20 @@ import {
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Radio } from '@/components/ui/radio';
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 {
UseFormReturn,
useForm,
useFormContext,
useWatch,
} from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { initialRetrievalValues } from '../../constant';
import { RetrievalFrom, initialRetrievalValues } from '../../constant';
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
import { INextOperatorForm } from '../../interface';
import { FormWrapper } from '../components/form-wrapper';
@ -48,6 +54,7 @@ export const RetrievalPartialSchema = {
toc_enhance: z.boolean(),
...MetadataFilterSchema,
memory_ids: z.array(z.string()).optional(),
retrieval_from: z.string(),
};
export const FormSchema = z.object({
@ -55,6 +62,44 @@ export const FormSchema = z.object({
...RetrievalPartialSchema,
});
export type RetrievalFormSchemaType = z.infer<typeof FormSchema>;
export function MemoryDatasetForm() {
const { t } = useTranslation();
const form = useFormContext();
const retrievalFrom = useWatch({
control: form.control,
name: 'retrieval_from',
});
return (
<>
<RAGFlowFormItem name="retrieval_from" label={t('flow.retrievalFrom')}>
<Radio.Group>
<Radio value={RetrievalFrom.Dataset}>
{t('knowledgeDetails.dataset')}
</Radio>
<Radio value={RetrievalFrom.Memory}>{t('header.memories')}</Radio>
</Radio.Group>
</RAGFlowFormItem>
{retrievalFrom === RetrievalFrom.Memory ? (
<MemoriesFormField label={t('header.memories')}></MemoriesFormField>
) : (
<KnowledgeBaseFormField showVariable></KnowledgeBaseFormField>
)}
</>
);
}
export function useHideKnowledgeGraphField(form: UseFormReturn<any>) {
const retrievalFrom = useWatch({
control: form.control,
name: 'retrieval_from',
});
return retrievalFrom === RetrievalFrom.Memory;
}
export function EmptyResponseField() {
const { t } = useTranslation();
const form = useFormContext();
@ -106,6 +151,8 @@ function RetrievalForm({ node }: INextOperatorForm) {
resolver: zodResolver(FormSchema),
});
const hideKnowledgeGraphField = useHideKnowledgeGraphField(form);
useWatchFormChange(node?.id, form);
return (
@ -114,8 +161,7 @@ function RetrievalForm({ node }: INextOperatorForm) {
<RAGFlowFormItem name="query" label={t('flow.query')}>
<PromptEditor></PromptEditor>
</RAGFlowFormItem>
<KnowledgeBaseFormField showVariable></KnowledgeBaseFormField>
<MemoriesFormField label={t('flow.memory')}></MemoriesFormField>
<MemoryDatasetForm></MemoryDatasetForm>
<Collapse title={<div>{t('flow.advancedSettings')}</div>}>
<FormContainer>
<SimilaritySliderFormField
@ -123,12 +169,20 @@ function RetrievalForm({ node }: INextOperatorForm) {
isTooltipShown
></SimilaritySliderFormField>
<TopNFormField></TopNFormField>
<RerankFormFields></RerankFormFields>
<MetadataFilter canReference></MetadataFilter>
{hideKnowledgeGraphField || (
<>
<RerankFormFields></RerankFormFields>
<MetadataFilter canReference></MetadataFilter>
</>
)}
<EmptyResponseField></EmptyResponseField>
<CrossLanguageFormField name="cross_languages"></CrossLanguageFormField>
<UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField>
<TOCEnhanceFormField name="toc_enhance"></TOCEnhanceFormField>
{hideKnowledgeGraphField || (
<>
<CrossLanguageFormField name="cross_languages"></CrossLanguageFormField>
<UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField>
<TOCEnhanceFormField name="toc_enhance"></TOCEnhanceFormField>
</>
)}
</FormContainer>
</Collapse>
<Output list={outputList}></Output>

View File

@ -1,8 +1,6 @@
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 { MemoriesFormField } from '@/components/memories-form-field';
import { MetadataFilter } from '@/components/metadata-filter';
import { RerankFormFields } from '@/components/rerank';
import { SimilaritySliderFormField } from '@/components/similarity-slider';
@ -18,7 +16,9 @@ import { DescriptionField } from '../../components/description-field';
import { FormWrapper } from '../../components/form-wrapper';
import {
EmptyResponseField,
MemoryDatasetForm,
RetrievalPartialSchema,
useHideKnowledgeGraphField,
} from '../../retrieval-form/next';
import { useValues } from '../use-values';
import { useWatchFormChange } from '../use-watch-change';
@ -36,14 +36,15 @@ const RetrievalForm = () => {
resolver: zodResolver(FormSchema),
});
const hideKnowledgeGraphField = useHideKnowledgeGraphField(form);
useWatchFormChange(form);
return (
<Form {...form}>
<FormWrapper>
<DescriptionField></DescriptionField>
<KnowledgeBaseFormField showVariable></KnowledgeBaseFormField>
<MemoriesFormField label={t('flow.memory')}></MemoriesFormField>
<MemoryDatasetForm></MemoryDatasetForm>
<Collapse title={<div>{t('flow.advancedSettings')}</div>}>
<FormContainer>
<SimilaritySliderFormField
@ -51,12 +52,21 @@ const RetrievalForm = () => {
isTooltipShown
></SimilaritySliderFormField>
<TopNFormField></TopNFormField>
<RerankFormFields></RerankFormFields>
<MetadataFilter canReference></MetadataFilter>
{hideKnowledgeGraphField || (
<>
<RerankFormFields></RerankFormFields>
<MetadataFilter canReference></MetadataFilter>
</>
)}
<EmptyResponseField></EmptyResponseField>
<CrossLanguageFormField name="cross_languages"></CrossLanguageFormField>
<UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField>
<TOCEnhanceFormField name="toc_enhance"></TOCEnhanceFormField>
{hideKnowledgeGraphField || (
<>
<CrossLanguageFormField name="cross_languages"></CrossLanguageFormField>
<UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField>
<TOCEnhanceFormField name="toc_enhance"></TOCEnhanceFormField>
</>
)}
</FormContainer>
</Collapse>
</FormWrapper>

View File

@ -246,7 +246,7 @@ export default function Agent() {
</Button>
<Button variant={'secondary'} onClick={showVersionDialog}>
<History />
{t('flow.historyversion')}
{t('flow.historyVersion')}
</Button>
{isPipeline || (
<Button

View File

@ -61,7 +61,7 @@ export function VersionDialog({
<DialogContent className="max-w-[60vw]">
<DialogHeader>
<DialogTitle className="text-base">
{t('flow.historyversion')}
{t('flow.historyVersion')}
</DialogTitle>
</DialogHeader>
<section className="flex gap-8 relative">

View File

@ -52,7 +52,7 @@ const LinkDataPipelineModal = ({
title={
!isEdit
? t('knowledgeConfiguration.linkDataPipeline')
: t('knowledgeConfiguration.eidtLinkDataPipeline')
: t('knowledgeConfiguration.editLinkDataPipeline')
}
open={open}
onOpenChange={setOpen}

View File

@ -23,6 +23,7 @@ import {
import { ManageMetadataModal } from '../components/metedata/manage-modal';
import { DatasetTable } from './dataset-table';
import Generate from './generate-button/generate';
import { ReparseDialog } from './reparse-dialog';
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
import { useCreateEmptyDocument } from './use-create-empty-document';
import { useSelectDatasetFilters } from './use-select-filters';
@ -77,7 +78,12 @@ export default function Dataset() {
const { rowSelection, rowSelectionIsEmpty, setRowSelection, selectedCount } =
useRowSelection();
const { list } = useBulkOperateDataset({
const {
list,
visible: reparseDialogVisible,
hideModal: hideReparseDialogModal,
handleRunClick: handleOperationIconClick,
} = useBulkOperateDataset({
documents,
rowSelection,
setRowSelection,
@ -207,6 +213,16 @@ export default function Dataset() {
otherData={metadataConfig.record}
/>
)}
{reparseDialogVisible && (
<ReparseDialog
// hidden={isZeroChunk || isRunning}
hidden={false}
handleOperationIconClick={handleOperationIconClick}
chunk_num={0}
visible={reparseDialogVisible}
hideModal={hideReparseDialogModal}
></ReparseDialog>
)}
</section>
</>
);

View File

@ -1,4 +1,3 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import { IconFontFill } from '@/components/icon-font';
import {
DropdownMenu,
@ -19,6 +18,7 @@ import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { DocumentType, RunningStatus } from './constant';
import { ParsingCard } from './parsing-card';
import { ReparseDialog } from './reparse-dialog';
import { UseChangeDocumentParserShowType } from './use-change-document-parser';
import { useHandleRunDocumentByIds } from './use-run-document';
import { UseSaveMetaShowType } from './use-save-meta';
@ -63,15 +63,21 @@ export function ParsingStatusCell({
} = record;
const operationIcon = IconMap[run];
const p = Number((progress * 100).toFixed(2));
const { handleRunDocumentByIds } = useHandleRunDocumentByIds(id);
const {
handleRunDocumentByIds,
visible: reparseDialogVisible,
showModal: showReparseDialogModal,
hideModal: hideReparseDialogModal,
} = useHandleRunDocumentByIds(id);
const isRunning = isParserRunning(run);
const isZeroChunk = chunk_num === 0;
const handleOperationIconClick =
(shouldDelete: boolean = false) =>
() => {
handleRunDocumentByIds(record.id, isRunning, shouldDelete);
};
const handleOperationIconClick = (option: {
delete: boolean;
apply_kb: boolean;
}) => {
handleRunDocumentByIds(record.id, isRunning, option);
};
const handleShowChangeParserModal = useCallback(() => {
showChangeParserModal(record);
@ -129,23 +135,20 @@ export function ParsingStatusCell({
<div className="flex items-center gap-3">
<Separator orientation="vertical" className="h-2.5" />
{!isParserRunning(run) && (
<ConfirmDeleteDialog
title={t(`knowledgeDetails.redo`, { chunkNum: chunk_num })}
hidden={isZeroChunk || isRunning}
onOk={handleOperationIconClick(true)}
onCancel={handleOperationIconClick(false)}
// <ReparseDialog
// hidden={isZeroChunk || isRunning}
// handleOperationIconClick={handleOperationIconClick}
// chunk_num={chunk_num}
// >
<div
className="cursor-pointer flex items-center gap-3"
onClick={() => {
showReparseDialogModal();
}}
>
<div
className="cursor-pointer flex items-center gap-3"
onClick={
isZeroChunk || isRunning
? handleOperationIconClick(false)
: () => {}
}
>
{!isParserRunning(run) && operationIcon}
</div>
</ConfirmDeleteDialog>
{!isParserRunning(run) && operationIcon}
</div>
// {/* </ReparseDialog> */}
)}
{isParserRunning(run) ? (
<>
@ -158,11 +161,14 @@ export function ParsingStatusCell({
</div>
<div
className="cursor-pointer flex items-center gap-3"
onClick={
isZeroChunk || isRunning
? handleOperationIconClick(false)
: () => {}
}
onClick={() => {
showReparseDialogModal();
}}
// onClick={
// isZeroChunk || isRunning
// ? handleOperationIconClick(false)
// : () => {}
// }
>
{operationIcon}
</div>
@ -175,6 +181,16 @@ export function ParsingStatusCell({
)}
</div>
)}
{reparseDialogVisible && (
<ReparseDialog
// hidden={isZeroChunk || isRunning}
hidden={false}
handleOperationIconClick={handleOperationIconClick}
chunk_num={chunk_num}
visible={reparseDialogVisible}
hideModal={hideReparseDialogModal}
></ReparseDialog>
)}
</section>
);
}

View File

@ -0,0 +1,154 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
DynamicForm,
DynamicFormRef,
FormFieldType,
} from '@/components/dynamic-form';
import { Checkbox } from '@/components/ui/checkbox';
import { DialogProps } from '@radix-ui/react-dialog';
import { t } from 'i18next';
import { useCallback, useState } from 'react';
export const ReparseDialog = ({
handleOperationIconClick,
chunk_num,
hidden = false,
visible = true,
hideModal,
children,
}: DialogProps & {
chunk_num: number;
handleOperationIconClick: (options: {
delete: boolean;
apply_kb: boolean;
}) => void;
visible: boolean;
hideModal: () => void;
hidden?: boolean;
}) => {
const [formInstance, setFormInstance] = useState<DynamicFormRef | null>(null);
const formCallbackRef = useCallback((node: DynamicFormRef | null) => {
if (node) {
setFormInstance(node);
console.log('Form instance assigned:', node);
} else {
console.log('Form instance removed');
}
}, []);
const handleCancel = useCallback(() => {
// handleOperationIconClick(false);
hideModal?.();
formInstance?.reset();
}, [formInstance]);
const handleSave = useCallback(async () => {
const instance = formInstance;
if (!instance) {
console.error('Form instance is null');
return;
}
const check = await instance.trigger();
if (check) {
instance.submit();
const formValues = instance.getValues();
console.log(formValues);
handleOperationIconClick({
delete: formValues.delete,
apply_kb: formValues.apply_kb,
});
}
}, [formInstance, handleOperationIconClick]);
// useEffect(() => {
// if (!hidden) {
// const timer = setTimeout(() => {
// if (!formInstance) {
// console.warn(
// 'Form ref is still null after component should be mounted',
// );
// } else {
// console.log('Form ref is properly set');
// }
// }, 1000);
// return () => clearTimeout(timer);
// }
// }, [hidden, formInstance]);
return (
<ConfirmDeleteDialog
title={t(`knowledgeDetails.parseFile`)}
onOk={() => handleSave()}
onCancel={() => handleCancel()}
hidden={hidden}
open={visible}
content={{
title: t(`knowledgeDetails.parseFileTip`),
node: (
<div>
<DynamicForm.Root
onSubmit={(data) => {
console.log('submit', data);
}}
ref={formCallbackRef}
fields={[
{
name: 'delete',
label: '',
type: FormFieldType.Checkbox,
render: (fieldProps) => (
<div className="flex items-center text-text-secondary p-5 border border-border-button rounded-lg">
<Checkbox
{...fieldProps}
onCheckedChange={(checked: boolean) => {
fieldProps.onChange(checked);
}}
/>
<span className="ml-2">
{chunk_num > 0
? t(`knowledgeDetails.redo`, { chunkNum: chunk_num })
: t('knowledgeDetails.redoAll')}
</span>
</div>
),
},
{
name: 'apply_kb',
label: '',
type: FormFieldType.Checkbox,
render: (fieldProps) => (
<div className="flex items-center text-text-secondary p-5 border border-border-button rounded-lg">
<Checkbox
{...fieldProps}
onCheckedChange={(checked: boolean) => {
fieldProps.onChange(checked);
}}
/>
<span className="ml-2">
{t('knowledgeDetails.applyAutoMetadataSettings')}
</span>
</div>
),
},
]}
>
{/* <DynamicForm.CancelButton
handleCancel={() => handleOperationIconClick(false)}
cancelText={t('common.cancel')}
/>
<DynamicForm.SavingButton
buttonText={t('common.confirm')}
submitFunc={handleSave}
/> */}
</DynamicForm.Root>
</div>
),
}}
>
{/* {children} */}
</ConfirmDeleteDialog>
);
};

View File

@ -1,3 +1,4 @@
import { useSetModalState } from '@/hooks/common-hooks';
import {
UseRowSelectionType,
useSelectedIds,
@ -30,9 +31,9 @@ export function useBulkOperateDataset({
const { runDocumentByIds } = useRunDocument();
const { setDocumentStatus } = useSetDocumentStatus();
const { removeDocument } = useRemoveDocument();
const { visible, showModal, hideModal } = useSetModalState();
const runDocument = useCallback(
(run: number) => {
async (run: number, option?: { delete: boolean; apply_kb: boolean }) => {
const nonVirtualKeys = selectedRowKeys.filter(
(x) =>
!documents.some((y) => x === y.id && y.type === DocumentType.Virtual),
@ -42,18 +43,22 @@ export function useBulkOperateDataset({
toast.error(t('Please select a non-empty file list'));
return;
}
runDocumentByIds({
await runDocumentByIds({
documentIds: nonVirtualKeys,
run,
shouldDelete: false,
option,
});
hideModal();
},
[documents, runDocumentByIds, selectedRowKeys, t],
[documents, runDocumentByIds, selectedRowKeys, hideModal, t],
);
const handleRunClick = useCallback(() => {
runDocument(1);
}, [runDocument]);
const handleRunClick = useCallback(
(option: { delete: boolean; apply_kb: boolean }) => {
runDocument(1, option);
},
[runDocument],
);
const handleCancelClick = useCallback(() => {
runDocument(2);
@ -106,7 +111,7 @@ export function useBulkOperateDataset({
id: 'run',
label: t('knowledgeDetails.run'),
icon: <Play />,
onClick: handleRunClick,
onClick: () => showModal(),
},
{
id: 'cancel',
@ -127,5 +132,5 @@ export function useBulkOperateDataset({
},
];
return { list };
return { list, visible, hideModal, showModal, handleRunClick };
}

View File

@ -1,3 +1,4 @@
import { useSetModalState } from '@/hooks/common-hooks';
import { useRunDocument } from '@/hooks/use-document-request';
import { useState } from 'react';
@ -5,11 +6,11 @@ export const useHandleRunDocumentByIds = (id: string) => {
const { runDocumentByIds, loading } = useRunDocument();
const [currentId, setCurrentId] = useState<string>('');
const isLoading = loading && currentId !== '' && currentId === id;
const { visible, showModal, hideModal } = useSetModalState();
const handleRunDocumentByIds = async (
documentId: string,
isRunning: boolean,
shouldDelete: boolean = false,
option: { delete: boolean; apply_kb: boolean },
) => {
if (isLoading) {
return;
@ -19,16 +20,20 @@ export const useHandleRunDocumentByIds = (id: string) => {
await runDocumentByIds({
documentIds: [documentId],
run: isRunning ? 2 : 1,
shouldDelete,
option,
});
setCurrentId('');
} catch (error) {
setCurrentId('');
}
hideModal();
};
return {
handleRunDocumentByIds,
loading: isLoading,
visible,
showModal,
hideModal,
};
};

View File

@ -10,6 +10,12 @@ export enum MemoryType {
Episodic = 'episodic',
Procedural = 'procedural',
}
export const MemoryOptions = (t: TFunction) => [
{ label: t('memories.raw'), value: MemoryType.Raw },
{ label: t('memories.semantic'), value: MemoryType.Semantic },
{ label: t('memories.episodic'), value: MemoryType.Episodic },
{ label: t('memories.procedural'), value: MemoryType.Procedural },
];
export const createMemoryFields = (t: TFunction) =>
[
{
@ -24,12 +30,7 @@ export const createMemoryFields = (t: TFunction) =>
type: FormFieldType.MultiSelect,
placeholder: t('memories.descriptionPlaceholder'),
tooltip: t('memories.memoryTypeTooltip'),
options: [
{ label: 'Raw', value: MemoryType.Raw },
{ label: 'Semantic', value: MemoryType.Semantic },
{ label: 'Episodic', value: MemoryType.Episodic },
{ label: 'Procedural', value: MemoryType.Procedural },
],
options: MemoryOptions(t),
required: true,
customValidate: (value) => {
if (!value.includes(MemoryType.Raw) || !value.length) {

View File

@ -16,7 +16,7 @@ export interface MemoryListParams {
export type MemoryType = 'raw' | 'semantic' | 'episodic' | 'procedural';
export type StorageType = 'table' | 'graph';
export type Permissions = 'me' | 'team';
export type ForgettingPolicy = 'fifo' | 'lru';
export type ForgettingPolicy = 'FIFO' | 'LRU';
export interface ICreateMemoryProps {
name: string;
memory_type: MemoryType[];

View File

@ -39,7 +39,7 @@ export default function MemoryMessage() {
messages={data?.messages?.message_list ?? []}
pagination={pagination}
setPagination={setPagination}
total={data?.messages?.total ?? 0}
total={data?.messages?.total_count ?? 0}
// rowSelection={rowSelection}
// setRowSelection={setRowSelection}
// loading={loading}

View File

@ -14,7 +14,7 @@ export interface IMessageInfo {
}
export interface IMessageTableProps {
messages: { message_list: Array<IMessageInfo>; total: number };
messages: { message_list: Array<IMessageInfo>; total_count: number };
storage_type: string;
}

View File

@ -251,7 +251,9 @@ export function MemoryTable({
title={t('memory.messages.forgetMessage')}
open={showDeleteDialog}
onOpenChange={setShowDeleteDialog}
okButtonText={t('common.confirm')}
content={{
title: t('memory.messages.forgetMessageTip'),
node: (
<ConfirmDeleteDialogNode
// avatar={{ avatar: selectedMessage.avatar, name: selectedMessage.name }}

View File

@ -12,7 +12,7 @@ import { z } from 'zod';
export const advancedSettingsFormSchema = {
permissions: z.string().optional(),
storage_type: z.enum(['table', 'graph']).optional(),
forgetting_policy: z.enum(['lru', 'fifo']).optional(),
forgetting_policy: z.enum(['LRU', 'FIFO']).optional(),
temperature: z.number().optional(),
system_prompt: z.string().optional(),
user_prompt: z.string().optional(),
@ -20,7 +20,7 @@ export const advancedSettingsFormSchema = {
export const defaultAdvancedSettingsForm = {
permissions: 'me',
storage_type: 'table',
forgetting_policy: 'fifo',
forgetting_policy: 'FIFO',
temperature: 0.7,
system_prompt: '',
user_prompt: '',
@ -80,8 +80,8 @@ export const AdvancedSettingsForm = () => {
horizontal: true,
placeholder: t('memory.config.storageTypePlaceholder'),
options: [
{ label: 'table', value: 'table' },
// { label: 'graph', value: 'graph' },
{ label: 'Table', value: 'table' },
// { label: 'Graph', value: 'graph' },
],
required: false,
}}
@ -94,8 +94,8 @@ export const AdvancedSettingsForm = () => {
horizontal: true,
// placeholder: t('memory.config.storageTypePlaceholder'),
options: [
// { label: 'lru', value: 'lru' },
{ label: 'fifo', value: 'fifo' },
// { label: 'LRU', value: 'LRU' },
{ label: 'FIFO', value: 'fifo' },
],
required: false,
}}
@ -146,7 +146,7 @@ export const AdvancedSettingsForm = () => {
field={{
name: 'user_prompt',
label: t('memory.config.userPrompt'),
type: FormFieldType.Text,
type: FormFieldType.Textarea,
horizontal: true,
placeholder: t('memory.config.userPromptPlaceholder'),
required: false,

View File

@ -15,9 +15,9 @@ export const useUpdateMemoryConfig = () => {
try {
const params = omit(data, [
'id',
'memory_type',
'embd_id',
'storage_type',
// 'memory_type',
// 'embd_id',
// 'storage_type',
]);
res = await updateMemory({
// ...memoryDataTemp,

View File

@ -6,9 +6,9 @@ import { MainContainer } from '@/pages/dataset/dataset-setting/configuration-for
import { TopTitle } from '@/pages/dataset/dataset-title';
import { IMemory } from '@/pages/memories/interface';
import { zodResolver } from '@hookform/resolvers/zod';
import { t } from 'i18next';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { useFetchMemoryBaseConfiguration } from '../hooks/use-memory-setting';
import {
@ -24,14 +24,15 @@ import {
memoryModelFormSchema,
} from './memory-model-form';
const MemoryMessageSchema = z.object({
id: z.string(),
...basicInfoSchema,
...memoryModelFormSchema,
...advancedSettingsFormSchema,
});
// type MemoryMessageForm = z.infer<typeof MemoryMessageSchema>;
export default function MemoryMessage() {
const { t } = useTranslation();
const MemoryMessageSchema = z.object({
id: z.string(),
...basicInfoSchema,
...memoryModelFormSchema(t),
...advancedSettingsFormSchema,
});
const form = useForm<IMemory>({
resolver: zodResolver(MemoryMessageSchema),
defaultValues: {
@ -57,12 +58,13 @@ export default function MemoryMessage() {
temperature: data?.temperature,
system_prompt: data?.system_prompt || '',
user_prompt: data?.user_prompt || '',
forgetting_policy: data?.forgetting_policy || 'fifo',
storage_type: data?.storage_type || 'table',
forgetting_policy: data?.forgetting_policy || 'FIFO',
storage_type: data?.storage_type || 'Table',
permissions: data?.permissions || 'me',
});
}, [data, form]);
const onSubmit = (data: IMemory) => {
console.log('data', data);
onMemoryRenameOk(data);
};
return (
@ -74,7 +76,7 @@ export default function MemoryMessage() {
<div className="flex gap-14 flex-1 min-h-0">
<Form {...form}>
<form onSubmit={form.handleSubmit(() => {})} className="space-y-6 ">
<div className="w-[768px] h-[calc(100vh-300px)] pr-1 overflow-y-auto scrollbar-auto">
<div className="w-[768px] h-[calc(100vh-300px)] pr-1 overflow-y-auto scrollbar-auto pb-4">
<MainContainer className="text-text-secondary !space-y-10">
<div className="text-base font-medium text-text-primary">
{t('knowledgeConfiguration.baseInfo')}

View File

@ -1,25 +1,36 @@
import { FormFieldType, RenderField } from '@/components/dynamic-form';
import { useModelOptions } from '@/components/llm-setting-items/llm-form-field';
import { EmbeddingSelect } from '@/pages/dataset/dataset-setting/configuration/common-item';
import { MemoryType } from '@/pages/memories/constants';
import { MemoryOptions, MemoryType } from '@/pages/memories/constants';
import { TFunction } from 'i18next';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { useFetchMemoryMessageList } from '../memory-message/hook';
export const memoryModelFormSchema = {
export const memoryModelFormSchema = (t: TFunction) => ({
embd_id: z.string(),
llm_id: z.string(),
memory_type: z.array(z.string()).optional(),
memory_type: z.array(z.string()).superRefine((data, ctx) => {
if (!data.includes(MemoryType.Raw) || !data.length) {
ctx.addIssue({
// path: ['memory_type'],
message: t('memories.embeddingModelError'),
code: 'custom',
});
}
}),
memory_size: z.number().optional(),
};
});
export const defaultMemoryModelForm = {
embd_id: '',
llm_id: '',
memory_type: [MemoryType.Raw],
memory_type: [],
memory_size: 0,
};
export const MemoryModelForm = () => {
const { modelOptions } = useModelOptions();
const { t } = useTranslation();
const { data } = useFetchMemoryMessageList();
return (
<>
<RenderField
@ -33,7 +44,11 @@ export const MemoryModelForm = () => {
type: FormFieldType.Custom,
disabled: true,
render: (field) => (
<EmbeddingSelect field={field} isEdit={false} disabled={true} />
<EmbeddingSelect
field={field}
isEdit={false}
disabled={data?.messages?.total_count > 0}
/>
),
tooltip: t('memories.embeddingModelTooltip'),
@ -47,6 +62,7 @@ export const MemoryModelForm = () => {
required: true,
horizontal: true,
type: FormFieldType.Select,
disabled: data?.messages?.total_count > 0,
options: modelOptions as { value: string; label: string }[],
tooltip: t('memories.llmTooltip'),
}}
@ -59,13 +75,14 @@ export const MemoryModelForm = () => {
horizontal: true,
placeholder: t('memories.memoryTypePlaceholder'),
tooltip: t('memories.memoryTypeTooltip'),
disabled: true,
options: [
{ label: 'Raw', value: 'raw' },
{ label: 'Semantic', value: 'semantic' },
{ label: 'Episodic', value: 'episodic' },
{ label: 'Procedural', value: 'procedural' },
],
disabled: data?.messages?.total_count > 0,
options: MemoryOptions(t),
customValidate: (value) => {
if (!value.includes(MemoryType.Raw) || !value.length) {
return t('memories.embeddingModelError');
}
return true;
},
required: true,
}}
/>

View File

@ -42,7 +42,7 @@ const AddDataSourceModal = ({
title={
<div className="flex flex-col gap-4">
<div className="size-6">{sourceData?.icon}</div>
{t('setting.addDataSourceModalTital', { name: sourceData?.name })}
{t('setting.addDataSourceModalTitle', { name: sourceData?.name })}
</div>
}
open={visible || false}

View File

@ -34,7 +34,7 @@ const methods = {
} as const;
const memoryService = registerNextServer<keyof typeof methods>(methods);
export const updateMemoryById = (id: string, data: any) => {
return request.put(updateMemorySetting(id), { data });
return request.put(updateMemorySetting(id), { ...data });
};
export const getMemoryDetailById = (id: string, data: any) => {
return request.get(getMemoryDetail(id), { params: data });

View File

@ -239,7 +239,7 @@ export default {
createMemory: `${api_host}/memories`,
getMemoryList: `${api_host}/memories`,
getMemoryConfig: (id: string) => `${api_host}/memories/${id}/config`,
deleteMemory: (id: string) => `${api_host}/memory/rm/${id}`,
deleteMemory: (id: string) => `${api_host}/memories/${id}`,
getMemoryDetail: (id: string) => `${api_host}/memories/${id}`,
updateMemorySetting: (id: string) => `${api_host}/memories/${id}`,
deleteMemoryMessage: (data: { memory_id: string; message_id: string }) =>