diff --git a/.github/code_spell_ignore.txt b/.github/code_spell_ignore.txt
index 4566d4f3a2..3c59d07a31 100644
--- a/.github/code_spell_ignore.txt
+++ b/.github/code_spell_ignore.txt
@@ -1,2 +1,3 @@
 ModelIn
 modelin
+pressEnter
\ No newline at end of file
diff --git a/EdgeCraftRAG/Dockerfile b/EdgeCraftRAG/Dockerfile
index fffb8d8970..c7100d1600 100644
--- a/EdgeCraftRAG/Dockerfile
+++ b/EdgeCraftRAG/Dockerfile
@@ -6,4 +6,4 @@ FROM opea/comps-base:$BASE_TAG
 
 COPY ./chatqna.py $HOME/chatqna.py
 
-ENTRYPOINT ["python", "chatqna.py"]
+ENTRYPOINT ["python", "chatqna.py"]
\ No newline at end of file
diff --git a/EdgeCraftRAG/Dockerfile.server b/EdgeCraftRAG/Dockerfile.server
index ab4060de80..6a701f72dd 100644
--- a/EdgeCraftRAG/Dockerfile.server
+++ b/EdgeCraftRAG/Dockerfile.server
@@ -10,7 +10,7 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missin
     poppler-utils \
     tesseract-ocr
 
-RUN apt-get update && apt-get install -y gnupg wget 
+RUN apt-get update && apt-get install -y gnupg wget git
 RUN wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | \
     gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg
 RUN echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy client" | \
@@ -33,15 +33,17 @@ RUN chown -R user /templates/default_prompt.txt
 
 COPY ./edgecraftrag /home/user/edgecraftrag
 
-RUN mkdir -p /home/user/gradio_cache 
-ENV GRADIO_TEMP_DIR=/home/user/gradio_cache
+RUN mkdir -p /home/user/ui_cache 
+ENV UI_UPLOAD_PATH=/home/user/ui_cache
 
 WORKDIR /home/user/edgecraftrag
 RUN pip install --no-cache-dir --upgrade pip setuptools==70.0.0 && \
     pip install --no-cache-dir -r requirements.txt
 
 WORKDIR /home/user/
+RUN git clone https://github.com/openvinotoolkit/openvino.genai.git genai
+ENV PYTHONPATH="$PYTHONPATH:/home/user/genai/tools/llm_bench"
 
 USER user
 
-ENTRYPOINT ["python", "-m", "edgecraftrag.server"]
+ENTRYPOINT ["python", "-m", "edgecraftrag.server"]
\ No newline at end of file
diff --git a/EdgeCraftRAG/README.md b/EdgeCraftRAG/README.md
old mode 100644
new mode 100755
index e2479c07a8..34975d3747
--- a/EdgeCraftRAG/README.md
+++ b/EdgeCraftRAG/README.md
@@ -7,10 +7,10 @@ quality and performance.
 
 ## What's New in this release?
 
-- Support image/url data retrieval and display in EC-RAG
-- Support display of document source used by LLM in UI
-- Support pipeline remove operation in RESTful API and UI
-- Support RAG pipeline performance benchmark and display in UI
+- A sleek new UI with enhanced user experience, built on Vue and Ant Design
+- Support concurrent multi-requests handling on vLLM inference backend
+- Support pipeline configuration through json file
+- Support system prompt modification through API
 - Fixed known issues in EC-RAG UI and server
 
 ## Quick Start Guide
@@ -36,7 +36,7 @@ You can select "local" type in generation field which is the default approach to
 #### vLLM with OpenVINO for Intel Arc GPU
 
 You can also select "vLLM" as generation type, to enable this type, you'll need to build the vLLM image for Intel Arc GPU before service bootstrap.
-Please follow this link [vLLM with OpenVINO](https://github.com/opea-project/GenAIComps/tree/main/comps/llms/text-generation/vllm/langchain#build-docker-image) to build the vLLM image.
+Please follow this link [vLLM with OpenVINO](https://github.com/opea-project/GenAIComps/tree/main/comps/third_parties/vllm#23-vllm-with-openvino-on-intel-gpu-and-cpu) to build the vLLM image.
 
 ### Start Edge Craft RAG Services with Docker Compose
 
@@ -45,12 +45,12 @@ cd GenAIExamples/EdgeCraftRAG/docker_compose/intel/gpu/arc
 
 export MODEL_PATH="your model path for all your models"
 export DOC_PATH="your doc path for uploading a dir of files"
-export GRADIO_PATH="your gradio cache path for transferring files"
+export UI_TMPFILE_PATH="your UI cache path for transferring files"
 # If you have a specific prompt template, please uncomment the following line
 # export PROMPT_PATH="your prompt path for prompt templates"
 
 # Make sure all 3 folders have 1000:1000 permission, otherwise
-# chown 1000:1000 ${MODEL_PATH} ${DOC_PATH} ${GRADIO_PATH}
+# chown 1000:1000 ${MODEL_PATH} ${DOC_PATH} ${UI_TMPFILE_PATH}
 # In addition, also make sure the .cache folder has 1000:1000 permission, otherwise
 # chown 1000:1000 $HOME/.cache
 
@@ -189,6 +189,12 @@ After the pipeline creation, you can upload your data in the `Chatbot` page.
 Then, you can submit messages in the chat box.
 ![chat_with_rag](assets/img/chat_with_rag.png)
 
+If you want to try Gradio UI, please launch service through compose_gradio.yaml, then access http://${HOST_IP}:8082 on your browser:
+
+```bash
+docker compose -f compose_gradio.yaml up -d
+```
+
 ## Advanced User Guide
 
 ### Pipeline Management
@@ -226,8 +232,26 @@ curl -X PATCH http://${HOST_IP}:16010/v1/settings/pipelines/rag_test_local_llm -
 curl -X DELETE http://${HOST_IP}:16010/v1/settings/pipelines/rag_test_local_llm -H "Content-Type: application/json" | jq '.'
 ```
 
+#### Get pipeline json
+
+```bash
+curl -X GET http://${HOST_IP}:16010/v1/settings/pipelines/{name}/json -H "Content-Type: application/json" | jq '.'
+```
+
+#### Import pipeline from a json file
+
+```bash
+curl -X POST http://${HOST_IP}:16010/v1/settings/pipelines/import -H "Content-Type: multipart/form-data" -F "file=@your_test_pipeline_json_file.txt"| jq '.'
+```
+
 #### Enable and check benchmark for pipelines
 
+##### ⚠️ NOTICE ⚠️
+
+Benchmarking activities may significantly reduce system performance.
+
+**DO NOT** perform benchmarking in a production environment.
+
 ```bash
 # Set ENABLE_BENCHMARK as true before launch services
 export ENABLE_BENCHMARK="true"
@@ -308,3 +332,11 @@ curl -X DELETE http://${HOST_IP}:16010/v1/data/files/test2.docx -H "Content-Type
 ```bash
 curl -X PATCH http://${HOST_IP}:16010/v1/data/files/test.pdf -H "Content-Type: application/json" -d '{"local_path":"docs/#REPLACE WITH YOUR FILE WITHIN MOUNTED DOC PATH#"}' | jq '.'
 ```
+
+### System Prompt Management
+
+#### Use custom system prompt
+
+```bash
+curl -X POST http://${HOST_IP}:16010/v1/chatqna/prompt -H "Content-Type: multipart/form-data" -F "file=@your_prompt_file.txt"
+```
diff --git a/EdgeCraftRAG/assets/img/chat_with_rag.png b/EdgeCraftRAG/assets/img/chat_with_rag.png
old mode 100644
new mode 100755
index 04000ef374..16262fdeac
Binary files a/EdgeCraftRAG/assets/img/chat_with_rag.png and b/EdgeCraftRAG/assets/img/chat_with_rag.png differ
diff --git a/EdgeCraftRAG/assets/img/create_pipeline.png b/EdgeCraftRAG/assets/img/create_pipeline.png
old mode 100644
new mode 100755
index 53331b2b7d..c443fafc59
Binary files a/EdgeCraftRAG/assets/img/create_pipeline.png and b/EdgeCraftRAG/assets/img/create_pipeline.png differ
diff --git a/EdgeCraftRAG/assets/img/upload_data.png b/EdgeCraftRAG/assets/img/upload_data.png
old mode 100644
new mode 100755
index 8fff43e689..c1890658b1
Binary files a/EdgeCraftRAG/assets/img/upload_data.png and b/EdgeCraftRAG/assets/img/upload_data.png differ
diff --git a/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose.yaml b/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose.yaml
index ce8fa09dc6..281dc16132 100644
--- a/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose.yaml
+++ b/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose.yaml
@@ -15,7 +15,7 @@ services:
     volumes:
       - ${MODEL_PATH:-${PWD}}:/home/user/models
       - ${DOC_PATH:-${PWD}}:/home/user/docs
-      - ${GRADIO_PATH:-${PWD}}:/home/user/gradio_cache
+      - ${UI_TMPFILE_PATH:-${PWD}}:/home/user/ui_cache
       - ${HF_CACHE:-${HOME}/.cache}:/home/user/.cache
       - ${PROMPT_PATH:-${PWD}}:/templates/custom
     ports:
@@ -54,7 +54,7 @@ services:
       UI_SERVICE_PORT: ${UI_SERVICE_PORT:-8082}
       UI_SERVICE_HOST_IP: ${UI_SERVICE_HOST_IP:-0.0.0.0}
     volumes:
-      - ${GRADIO_PATH:-${PWD}}:/home/user/gradio_cache
+      - ${UI_TMPFILE_PATH:-${PWD}}:/home/user/ui_cache
     ports:
       - ${UI_SERVICE_PORT:-8082}:${UI_SERVICE_PORT:-8082}
     restart: always
diff --git a/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose_gradio.yaml b/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose_gradio.yaml
new file mode 100644
index 0000000000..9204351fd4
--- /dev/null
+++ b/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose_gradio.yaml
@@ -0,0 +1,94 @@
+# Copyright (C) 2024 Intel Corporation
+# SPDX-License-Identifier: Apache-2.0
+
+services:
+  server:
+    image: ${REGISTRY:-opea}/edgecraftrag-server:${TAG:-latest}
+    container_name: edgecraftrag-server
+    environment:
+      no_proxy: ${no_proxy}
+      http_proxy: ${http_proxy}
+      https_proxy: ${https_proxy}
+      HF_ENDPOINT: ${HF_ENDPOINT}
+      vLLM_ENDPOINT: ${vLLM_ENDPOINT}
+      ENABLE_BENCHMARK: ${ENABLE_BENCHMARK:-false}
+    volumes:
+      - ${MODEL_PATH:-${PWD}}:/home/user/models
+      - ${DOC_PATH:-${PWD}}:/home/user/docs
+      - ${UI_TMPFILE_PATH:-${PWD}}:/home/user/ui_cache
+      - ${HF_CACHE:-${HOME}/.cache}:/home/user/.cache
+      - ${PROMPT_PATH:-${PWD}}:/templates/custom
+    ports:
+      - ${PIPELINE_SERVICE_PORT:-16010}:${PIPELINE_SERVICE_PORT:-16010}
+    devices:
+      - /dev/dri:/dev/dri
+    group_add:
+      - ${VIDEOGROUPID:-44}
+      - ${RENDERGROUPID:-109}
+  ecrag:
+    image: ${REGISTRY:-opea}/edgecraftrag:${TAG:-latest}
+    container_name: edgecraftrag
+    environment:
+      no_proxy: ${no_proxy}
+      http_proxy: ${http_proxy}
+      https_proxy: ${https_proxy}
+      MEGA_SERVICE_PORT: ${MEGA_SERVICE_PORT:-16011}
+      MEGA_SERVICE_HOST_IP: ${MEGA_SERVICE_HOST_IP:-${HOST_IP}}
+      PIPELINE_SERVICE_PORT: ${PIPELINE_SERVICE_PORT:-16010}
+      PIPELINE_SERVICE_HOST_IP: ${PIPELINE_SERVICE_HOST_IP:-${HOST_IP}}
+    ports:
+      - ${MEGA_SERVICE_PORT:-16011}:${MEGA_SERVICE_PORT:-16011}
+    depends_on:
+      - server
+  ui:
+    image: ${REGISTRY:-opea}/edgecraftrag-ui-gradio:${TAG:-latest}
+    container_name: edgecraftrag-ui
+    environment:
+      no_proxy: ${no_proxy}
+      http_proxy: ${http_proxy}
+      https_proxy: ${https_proxy}
+      MEGA_SERVICE_PORT: ${MEGA_SERVICE_PORT:-16011}
+      MEGA_SERVICE_HOST_IP: ${MEGA_SERVICE_HOST_IP:-${HOST_IP}}
+      PIPELINE_SERVICE_PORT: ${PIPELINE_SERVICE_PORT:-16010}
+      PIPELINE_SERVICE_HOST_IP: ${PIPELINE_SERVICE_HOST_IP:-${HOST_IP}}
+      UI_SERVICE_PORT: ${UI_SERVICE_PORT:-8082}
+      UI_SERVICE_HOST_IP: ${UI_SERVICE_HOST_IP:-0.0.0.0}
+    volumes:
+      - ${UI_TMPFILE_PATH:-${PWD}}:/home/user/ui_cache
+    ports:
+      - ${UI_SERVICE_PORT:-8082}:${UI_SERVICE_PORT:-8082}
+    restart: always
+    depends_on:
+      - server
+      - ecrag
+  # vllm-openvino-server:
+  #   container_name: vllm-openvino-server
+  #   image: opea/vllm-arc:latest
+  #   ports:
+  #     - ${VLLM_SERVICE_PORT:-8008}:80
+  #   environment:
+  #     HTTPS_PROXY: ${https_proxy}
+  #     HTTP_PROXY: ${https_proxy}
+  #     VLLM_OPENVINO_DEVICE: GPU
+  #     HF_ENDPOINT: ${HF_ENDPOINT}
+  #     HF_TOKEN: ${HUGGINGFACEHUB_API_TOKEN}
+  #   volumes:
+  #     - /dev/dri/by-path:/dev/dri/by-path
+  #     - $HOME/.cache/huggingface:/root/.cache/huggingface
+  #   devices:
+  #     - /dev/dri
+  #   group_add:
+  #     - ${VIDEOGROUPID:-44}
+  #     - ${RENDERGROUPID:-109}
+  #   entrypoint: /bin/bash -c "\
+  #     cd / && \
+  #     export VLLM_CPU_KVCACHE_SPACE=50 && \
+  #     export VLLM_OPENVINO_ENABLE_QUANTIZED_WEIGHTS=ON && \
+  #     python3 -m vllm.entrypoints.openai.api_server \
+  #       --model '${LLM_MODEL}' \
+  #       --max_model_len=1024 \
+  #       --host 0.0.0.0 \
+  #       --port 80"
+networks:
+  default:
+    driver: bridge
diff --git a/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose_vllm.yaml b/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose_vllm.yaml
index 4b7ff08f3b..cfa14d968c 100644
--- a/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose_vllm.yaml
+++ b/EdgeCraftRAG/docker_compose/intel/gpu/arc/compose_vllm.yaml
@@ -15,7 +15,7 @@ services:
     volumes:
       - ${MODEL_PATH:-${PWD}}:/home/user/models
       - ${DOC_PATH:-${PWD}}:/home/user/docs
-      - ${GRADIO_PATH:-${PWD}}:/home/user/gradio_cache
+      - ${UI_TMPFILE_PATH:-${PWD}}:/home/user/ui_cache
       - ${HF_CACHE:-${HOME}/.cache}:/home/user/.cache
       - ${PROMPT_PATH:-${PWD}}:/templates/custom
     ports:
@@ -54,7 +54,7 @@ services:
       UI_SERVICE_PORT: ${UI_SERVICE_PORT:-8082}
       UI_SERVICE_HOST_IP: ${UI_SERVICE_HOST_IP:-0.0.0.0}
     volumes:
-      - ${GRADIO_PATH:-${PWD}}:/home/user/gradio_cache
+      - ${UI_TMPFILE_PATH:-${PWD}}:/home/user/ui_cache
     ports:
       - ${UI_SERVICE_PORT:-8082}:${UI_SERVICE_PORT:-8082}
     restart: always
diff --git a/EdgeCraftRAG/docker_image_build/build.yaml b/EdgeCraftRAG/docker_image_build/build.yaml
index 39718a289c..c4f2189deb 100644
--- a/EdgeCraftRAG/docker_image_build/build.yaml
+++ b/EdgeCraftRAG/docker_image_build/build.yaml
@@ -18,6 +18,14 @@ services:
         https_proxy: ${https_proxy}
       dockerfile: ./ui/docker/Dockerfile.ui
     image: ${REGISTRY:-opea}/edgecraftrag-ui:${TAG:-latest}
+  edgecraftrag-ui-gradio:
+    build:
+      context: ..
+      args:
+        http_proxy: ${http_proxy}
+        https_proxy: ${https_proxy}
+      dockerfile: ./ui/docker/Dockerfile.gradio
+    image: ${REGISTRY:-opea}/edgecraftrag-ui-gradio:${TAG:-latest}
   edgecraftrag:
     build:
       context: ..
diff --git a/EdgeCraftRAG/edgecraftrag/api/v1/chatqna.py b/EdgeCraftRAG/edgecraftrag/api/v1/chatqna.py
index 91aeb8adce..d0236c82e8 100644
--- a/EdgeCraftRAG/edgecraftrag/api/v1/chatqna.py
+++ b/EdgeCraftRAG/edgecraftrag/api/v1/chatqna.py
@@ -5,7 +5,7 @@
 from comps.cores.proto.api_protocol import ChatCompletionRequest
 from edgecraftrag.api_schema import RagOut
 from edgecraftrag.context import ctx
-from fastapi import FastAPI
+from fastapi import FastAPI, File, HTTPException, UploadFile, status
 from fastapi.responses import StreamingResponse
 
 chatqna_app = FastAPI()
@@ -22,37 +22,69 @@ async def retrieval(request: ChatCompletionRequest):
             ret.append((n.node.node_id, n.node.text, n.score))
         return ret
 
-    return "Not found"
+    return None
 
 
 # ChatQnA
 @chatqna_app.post(path="/v1/chatqna")
 async def chatqna(request: ChatCompletionRequest):
-    generator = ctx.get_pipeline_mgr().get_active_pipeline().generator
-    if generator:
-        request.model = generator.model_id
-    if request.stream:
-        ret, retri_res = ctx.get_pipeline_mgr().run_pipeline(chat_request=request)
-        return ret
-    else:
-        ret, retri_res = ctx.get_pipeline_mgr().run_pipeline(chat_request=request)
-        return str(ret)
+    try:
+        generator = ctx.get_pipeline_mgr().get_active_pipeline().generator
+        if generator:
+            request.model = generator.model_id
+        if request.stream:
+            ret, retri_res = ctx.get_pipeline_mgr().run_pipeline(chat_request=request)
+            return ret
+        else:
+            ret, retri_res = ctx.get_pipeline_mgr().run_pipeline(chat_request=request)
+            return str(ret)
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
 
 
 # RAGQnA
 @chatqna_app.post(path="/v1/ragqna")
 async def ragqna(request: ChatCompletionRequest):
-    res, retri_res = ctx.get_pipeline_mgr().run_pipeline(chat_request=request)
-    if isinstance(res, GeneratedDoc):
-        res = res.text
-    elif isinstance(res, StreamingResponse):
-        collected_data = []
-        async for chunk in res.body_iterator:
-            collected_data.append(chunk)
-        res = "".join(collected_data)
-
-    ragout = RagOut(query=request.messages, contexts=[], response=str(res))
-    for n in retri_res:
-        origin_text = n.node.get_text()
-        ragout.contexts.append(origin_text.strip())
-    return ragout
+    try:
+        res, retri_res = ctx.get_pipeline_mgr().run_pipeline(chat_request=request)
+        if isinstance(res, GeneratedDoc):
+            res = res.text
+        elif isinstance(res, StreamingResponse):
+            collected_data = []
+            async for chunk in res.body_iterator:
+                collected_data.append(chunk)
+            res = "".join(collected_data)
+
+        ragout = RagOut(query=request.messages, contexts=[], response=str(res))
+        for n in retri_res:
+            origin_text = n.node.get_text()
+            ragout.contexts.append(origin_text.strip())
+        return ragout
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+
+
+# Upload prompt file for LLM ChatQnA
+@chatqna_app.post(path="/v1/chatqna/prompt")
+async def load_prompt(file: UploadFile = File(...)):
+    try:
+        generator = ctx.get_pipeline_mgr().get_active_pipeline().generator
+        if generator:
+            content = await file.read()
+            prompt_str = content.decode("utf-8")
+            generator.set_prompt(prompt_str)
+            return "Set LLM Prompt Successfully"
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+
+
+# Reset prompt for LLM ChatQnA
+@chatqna_app.post(path="/v1/chatqna/prompt/reset")
+async def reset_prompt():
+    try:
+        generator = ctx.get_pipeline_mgr().get_active_pipeline().generator
+        if generator:
+            generator.reset_prompt()
+            return "Reset LLM Prompt Successfully"
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
diff --git a/EdgeCraftRAG/edgecraftrag/api/v1/data.py b/EdgeCraftRAG/edgecraftrag/api/v1/data.py
old mode 100644
new mode 100755
index 29934b9c97..4a584de425
--- a/EdgeCraftRAG/edgecraftrag/api/v1/data.py
+++ b/EdgeCraftRAG/edgecraftrag/api/v1/data.py
@@ -1,9 +1,11 @@
 # Copyright (C) 2024 Intel Corporation
 # SPDX-License-Identifier: Apache-2.0
 
+import os
+
 from edgecraftrag.api_schema import DataIn, FilesIn
 from edgecraftrag.context import ctx
-from fastapi import FastAPI
+from fastapi import FastAPI, File, HTTPException, UploadFile, status
 
 data_app = FastAPI()
 
@@ -11,8 +13,6 @@
 # Upload a text or files
 @data_app.post(path="/v1/data")
 async def add_data(request: DataIn):
-    nodelist = None
-
     docs = []
     if request.text is not None:
         docs.extend(ctx.get_file_mgr().add_text(text=request.text))
@@ -20,28 +20,41 @@ async def add_data(request: DataIn):
         docs.extend(ctx.get_file_mgr().add_files(docs=request.local_path))
 
     nodelist = ctx.get_pipeline_mgr().run_data_prepare(docs=docs)
-    if nodelist is None:
-        return "Error"
+    if nodelist is None or len(nodelist) == 0:
+        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="File not found")
     pl = ctx.get_pipeline_mgr().get_active_pipeline()
-    # TODO: Need bug fix, when node_parser is None
     ctx.get_node_mgr().add_nodes(pl.node_parser.idx, nodelist)
     return "Done"
 
 
+# Reindex all files
+@data_app.post(path="/v1/data/reindex")
+async def redindex_data():
+    pl = ctx.get_pipeline_mgr().get_active_pipeline()
+
+    ctx.get_node_mgr().del_nodes_by_np_idx(pl.node_parser.idx)
+    pl.indexer.reinitialize_indexer()
+    pl.update_indexer_to_retriever()
+
+    all_docs = ctx.get_file_mgr().get_all_docs()
+    nodelist = ctx.get_pipeline_mgr().run_data_prepare(docs=all_docs)
+    if nodelist is not None and len(nodelist) > 0:
+        ctx.get_node_mgr().add_nodes(pl.node_parser.idx, nodelist)
+
+    return "Done"
+
+
 # Upload files by a list of file_path
 @data_app.post(path="/v1/data/files")
 async def add_files(request: FilesIn):
-    nodelist = None
-
     docs = []
     if request.local_paths is not None:
         docs.extend(ctx.get_file_mgr().add_files(docs=request.local_paths))
 
     nodelist = ctx.get_pipeline_mgr().run_data_prepare(docs=docs)
-    if nodelist is None:
-        return "Error"
+    if nodelist is None or len(nodelist) == 0:
+        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="File not found")
     pl = ctx.get_pipeline_mgr().get_active_pipeline()
-    # TODO: Need bug fix, when node_parser is None
     ctx.get_node_mgr().add_nodes(pl.node_parser.idx, nodelist)
     return "Done"
 
@@ -62,41 +75,41 @@ async def get_file_docs(name):
 @data_app.delete(path="/v1/data/files/{name}")
 async def delete_file(name):
     if ctx.get_file_mgr().del_file(name):
+        pl = ctx.get_pipeline_mgr().get_active_pipeline()
+
+        # Current solution: reindexing all docs after deleting one file
         # TODO: delete the nodes related to the file
+        ctx.get_node_mgr().del_nodes_by_np_idx(pl.node_parser.idx)
+        pl.indexer.reinitialize_indexer()
+        pl.update_indexer_to_retriever()
+
         all_docs = ctx.get_file_mgr().get_all_docs()
+        nodelist = ctx.get_pipeline_mgr().run_data_prepare(docs=all_docs)
+        if nodelist is not None and len(nodelist) > 0:
+            ctx.get_node_mgr().add_nodes(pl.node_parser.idx, nodelist)
 
-        nodelist = ctx.get_pipeline_mgr().run_data_update(docs=all_docs)
-        if nodelist is None:
-            return "Error"
-        pl = ctx.get_pipeline_mgr().get_active_pipeline()
-        ctx.get_node_mgr().del_nodes_by_np_idx(pl.node_parser.idx)
-        ctx.get_node_mgr().add_nodes(pl.node_parser.idx, nodelist)
         return f"File {name} is deleted"
     else:
         return f"File {name} not found"
 
 
-# UPDATE a file
-@data_app.patch(path="/v1/data/files/{name}")
-async def update_file(name, request: DataIn):
-    # 1. Delete
-    if ctx.get_file_mgr().del_file(name):
-        # 2. Add
-        docs = []
-        if request.text is not None:
-            docs.extend(ctx.get_file_mgr().add_text(text=request.text))
-        if request.local_path is not None:
-            docs.extend(ctx.get_file_mgr().add_files(docs=request.local_path))
-
-        # 3. Re-run the pipeline
-        # TODO: update the nodes related to the file
-        all_docs = ctx.get_file_mgr().get_all_docs()
-        nodelist = ctx.get_pipeline_mgr().run_data_update(docs=all_docs)
-        if nodelist is None:
-            return "Error"
-        pl = ctx.get_pipeline_mgr().get_active_pipeline()
-        ctx.get_node_mgr().del_nodes_by_np_idx(pl.node_parser.idx)
-        ctx.get_node_mgr().add_nodes(pl.node_parser.idx, nodelist)
-        return f"File {name} is updated"
-    else:
-        return f"File {name} not found"
+# Upload & save a file from UI
+@data_app.post(path="/v1/data/file")
+async def upload_file(file: UploadFile = File(...)):
+    if ctx.get_pipeline_mgr().get_active_pipeline() is None:
+        raise HTTPException(
+            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Please activate pipeline and upload the file"
+        )
+    try:
+        # DIR for server to save files uploaded by UI
+        UI_DIRECTORY = os.getenv("UI_TMPFILE_PATH", "/home/user/ui_cache")
+        UPLOAD_DIRECTORY = os.path.join(UI_DIRECTORY, "documents")
+        os.makedirs(UPLOAD_DIRECTORY, exist_ok=True)
+        file_path = os.path.join(UPLOAD_DIRECTORY, file.filename)
+        with open(file_path, "wb") as buffer:
+            buffer.write(await file.read())
+        return file_path
+    except Exception as e:
+        raise HTTPException(
+            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to upload file: {str(e)}"
+        )
diff --git a/EdgeCraftRAG/edgecraftrag/api/v1/model.py b/EdgeCraftRAG/edgecraftrag/api/v1/model.py
index 17044ae91f..3a08e20c42 100644
--- a/EdgeCraftRAG/edgecraftrag/api/v1/model.py
+++ b/EdgeCraftRAG/edgecraftrag/api/v1/model.py
@@ -2,13 +2,35 @@
 # SPDX-License-Identifier: Apache-2.0
 
 import gc
+import os
 
 from edgecraftrag.api_schema import ModelIn
 from edgecraftrag.context import ctx
-from fastapi import FastAPI
+from fastapi import FastAPI, HTTPException, status
 
 model_app = FastAPI()
 
+# Model path in container is set to '/home/user/models'
+CONTAINER_MODEL_PATH = "/home/user/models/"
+
+
+# Search available model weight
+@model_app.get(path="/v1/settings/weight/{model_id:path}")
+async def get_model_weight(model_id):
+    try:
+        return get_available_weights(CONTAINER_MODEL_PATH + model_id)
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=" GET model weight failed")
+
+
+# Search available model id
+@model_app.get(path="/v1/settings/avail-models/{model_type}")
+async def get_model_id(model_type):
+    try:
+        return get_available_models(model_type)
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=" GET model failed")
+
 
 # GET Models
 @model_app.get(path="/v1/settings/models")
@@ -42,13 +64,16 @@ async def update_model(model_id, request: ModelIn):
     active_pl = ctx.get_pipeline_mgr().get_active_pipeline()
     modelmgr = ctx.get_model_mgr()
     if active_pl and active_pl.model_existed(model_id):
-        return "Model is being used by active pipeline, unable to update model"
+        raise HTTPException(
+            status_code=status.HTTP_423_LOCKED, detail="Model is being used by active pipeline, unable to update"
+        )
     else:
         async with modelmgr._lock:
             if modelmgr.get_model_by_name(model_id) is None:
                 # Need to make sure original model still exists before updating model
                 # to prevent memory leak in concurrent requests situation
-                return "Model " + model_id + " not exists"
+                err_msg = "Model " + model_id + " not exists"
+                raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=err_msg)
             model = modelmgr.search_model(request)
             if model is None:
                 modelmgr.del_model_by_name(model_id)
@@ -65,7 +90,9 @@ async def update_model(model_id, request: ModelIn):
 async def delete_model(model_id):
     active_pl = ctx.get_pipeline_mgr().get_active_pipeline()
     if active_pl and active_pl.model_existed(model_id):
-        return "Model is being used by active pipeline, unable to remove"
+        raise HTTPException(
+            status_code=status.HTTP_423_LOCKED, detail="Model is being used by active pipeline, unable to remove"
+        )
     else:
         modelmgr = ctx.get_model_mgr()
         # Currently use asyncio.Lock() to deal with multi-requests
@@ -74,3 +101,37 @@ async def delete_model(model_id):
             # Clean up memory occupation
             gc.collect()
         return response
+
+
+def get_available_weights(model_path):
+    avail_weights_compression = []
+    for _, dirs, _ in os.walk(model_path):
+        for dir_name in dirs:
+            if "INT4" in dir_name:
+                avail_weights_compression.append("INT4")
+            if "INT8" in dir_name:
+                avail_weights_compression.append("INT8")
+            if "FP16" in dir_name:
+                avail_weights_compression.append("FP16")
+    return avail_weights_compression
+
+
+def get_available_models(model_type):
+    avail_models = []
+    if model_type == "LLM":
+        items = os.listdir(CONTAINER_MODEL_PATH)
+        for item in items:
+            if item == "BAAI":
+                continue
+            sub_paths = os.listdir(os.path.join(CONTAINER_MODEL_PATH, item))
+            if sub_paths and "INT4" not in sub_paths[0] and "INT8" not in sub_paths[0] and "FP16" not in sub_paths[0]:
+                for sub_path in sub_paths:
+                    avail_models.append(item + "/" + sub_path)
+            else:
+                avail_models.append(item)
+    else:
+        for item in os.listdir(CONTAINER_MODEL_PATH + "BAAI"):
+            if (model_type == "reranker" and "rerank" in item) or (model_type == "embedding" and "rerank" not in item):
+                avail_models.append("BAAI/" + item)
+
+    return avail_models
diff --git a/EdgeCraftRAG/edgecraftrag/api/v1/pipeline.py b/EdgeCraftRAG/edgecraftrag/api/v1/pipeline.py
index b160df45ca..536d0759f3 100644
--- a/EdgeCraftRAG/edgecraftrag/api/v1/pipeline.py
+++ b/EdgeCraftRAG/edgecraftrag/api/v1/pipeline.py
@@ -1,6 +1,7 @@
 # Copyright (C) 2024 Intel Corporation
 # SPDX-License-Identifier: Apache-2.0
 
+import json
 import weakref
 
 from edgecraftrag.api_schema import PipelineCreateIn
@@ -17,7 +18,7 @@
 from edgecraftrag.components.postprocessor import MetadataReplaceProcessor, RerankProcessor
 from edgecraftrag.components.retriever import AutoMergeRetriever, SimpleBM25Retriever, VectorSimRetriever
 from edgecraftrag.context import ctx
-from fastapi import FastAPI
+from fastapi import FastAPI, File, HTTPException, UploadFile, status
 
 pipeline_app = FastAPI()
 
@@ -34,6 +35,16 @@ async def get_pipeline(name):
     return ctx.get_pipeline_mgr().get_pipeline_by_name_or_id(name)
 
 
+# GET Pipeline Json Data
+@pipeline_app.get(path="/v1/settings/pipelines/{name}/json")
+async def get_pipeline_json(name):
+    pl = ctx.get_pipeline_mgr().get_pipeline_by_name_or_id(name)
+    if pl:
+        return pl.get_pipeline_json
+    else:
+        return None
+
+
 # GET Pipeline benchmark
 @pipeline_app.get(path="/v1/settings/pipelines/{name}/benchmark")
 async def get_pipeline_benchmark(name):
@@ -45,21 +56,7 @@ async def get_pipeline_benchmark(name):
 # POST Pipeline
 @pipeline_app.post(path="/v1/settings/pipelines")
 async def add_pipeline(request: PipelineCreateIn):
-    pl = ctx.get_pipeline_mgr().get_pipeline_by_name_or_id(request.name)
-    if pl is None:
-        pl = ctx.get_pipeline_mgr().create_pipeline(request.name)
-    active_pl = ctx.get_pipeline_mgr().get_active_pipeline()
-    if pl == active_pl:
-        if not request.active:
-            pass
-        else:
-            return "Unable to patch an active pipeline..."
-    try:
-        update_pipeline_handler(pl, request)
-    except ValueError as e:
-        ctx.get_pipeline_mgr().remove_pipeline_by_name_or_id(request.name)
-        return str(e)
-    return pl
+    return load_pipeline(request)
 
 
 # PATCH Pipeline
@@ -67,25 +64,54 @@ async def add_pipeline(request: PipelineCreateIn):
 async def update_pipeline(name, request: PipelineCreateIn):
     pl = ctx.get_pipeline_mgr().get_pipeline_by_name_or_id(name)
     if pl is None:
-        return "Pipeline not exists"
+        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pipeline not found")
     active_pl = ctx.get_pipeline_mgr().get_active_pipeline()
     if pl == active_pl:
-        if not request.active:
-            pass
-        else:
-            return "Unable to patch an active pipeline..."
+        if request.active:
+            raise HTTPException(status_code=status.HTTP_423_LOCKED, detail="Unable to patch an active pipeline...")
     async with ctx.get_pipeline_mgr()._lock:
         try:
             update_pipeline_handler(pl, request)
-        except ValueError as e:
-            return str(e)
+            pipeline_dict = request.dict()
+            pl.update_pipeline_json(pipeline_dict)
+        except (ValueError, Exception) as e:
+            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
     return pl
 
 
 # REMOVE Pipeline
 @pipeline_app.delete(path="/v1/settings/pipelines/{name}")
 async def remove_pipeline(name):
-    return ctx.get_pipeline_mgr().remove_pipeline_by_name_or_id(name)
+    try:
+        res = ctx.get_pipeline_mgr().remove_pipeline_by_name_or_id(name)
+        return res
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+
+
+# IMPORT pipeline json from a file
+@pipeline_app.post(path="/v1/settings/pipelines/import")
+async def upload_file(file: UploadFile = File(...)):
+    content = await file.read()
+    request = json.loads(content)
+    pipeline_req = PipelineCreateIn(**request)
+    return load_pipeline(pipeline_req)
+
+
+def load_pipeline(request):
+    pl = ctx.get_pipeline_mgr().get_pipeline_by_name_or_id(request.name)
+    if pl is None:
+        pipeline_json = request.model_dump_json()
+        pl = ctx.get_pipeline_mgr().create_pipeline(request.name, pipeline_json)
+    active_pl = ctx.get_pipeline_mgr().get_active_pipeline()
+    if pl == active_pl and request.active:
+        raise HTTPException(status_code=status.HTTP_423_LOCKED, detail="Unable to patch an active pipeline...")
+    try:
+        update_pipeline_handler(pl, request)
+    except (ValueError, Exception) as e:
+        ctx.get_pipeline_mgr().remove_pipeline_by_name_or_id(request.name)
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+    return pl
 
 
 def update_pipeline_handler(pl, req):
@@ -117,6 +143,12 @@ def update_pipeline_handler(pl, req):
                     pl.node_parser = UnstructedNodeParser(chunk_size=np.chunk_size, chunk_overlap=np.chunk_overlap)
             ctx.get_node_parser_mgr().add(pl.node_parser)
 
+            all_docs = ctx.get_file_mgr().get_all_docs()
+            nodelist = pl.node_parser.run(docs=all_docs)
+            if nodelist is not None and len(nodelist) > 0:
+                ctx.get_node_mgr().add_nodes(pl.node_parser.idx, nodelist)
+            pl._node_changed = True
+
     if req.indexer is not None:
         ind = req.indexer
         found_indexer = ctx.get_indexer_mgr().search_indexer(ind)
@@ -138,6 +170,10 @@ def update_pipeline_handler(pl, req):
                 case _:
                     pass
             ctx.get_indexer_mgr().add(pl.indexer)
+            pl._index_changed = True
+            pl._index_to_retriever_updated = False
+            # As indexer changed, nodes are cleared in indexer's db
+            pl._node_changed = True
 
     if req.retriever is not None:
         retr = req.retriever
@@ -146,20 +182,22 @@ def update_pipeline_handler(pl, req):
                 if pl.indexer is not None:
                     pl.retriever = VectorSimRetriever(pl.indexer, similarity_top_k=retr.retrieve_topk)
                 else:
-                    return "No indexer"
+                    raise Exception("No indexer")
             case RetrieverType.AUTOMERGE:
                 # AutoMergeRetriever looks at a set of leaf nodes and recursively "merges" subsets of leaf nodes that reference a parent node
                 if pl.indexer is not None:
                     pl.retriever = AutoMergeRetriever(pl.indexer, similarity_top_k=retr.retrieve_topk)
                 else:
-                    return "No indexer"
+                    return Exception("No indexer")
             case RetrieverType.BM25:
                 if pl.indexer is not None:
                     pl.retriever = SimpleBM25Retriever(pl.indexer, similarity_top_k=retr.retrieve_topk)
                 else:
-                    return "No indexer"
+                    return Exception("No indexer")
             case _:
                 pass
+        # Index is updated to retriever
+        pl._index_to_retriever_updated = True
 
     if req.postprocessor is not None:
         pp = req.postprocessor
@@ -177,7 +215,7 @@ def update_pipeline_handler(pl, req):
                         postprocessor = RerankProcessor(reranker_model, processor.top_n)
                         pl.postprocessor.append(postprocessor)
                     else:
-                        return "No reranker model"
+                        raise Exception("No reranker model")
                 case PostProcessorType.METADATAREPLACE:
                     postprocessor = MetadataReplaceProcessor(target_metadata_key="window")
                     pl.postprocessor.append(postprocessor)
@@ -185,7 +223,7 @@ def update_pipeline_handler(pl, req):
     if req.generator:
         gen = req.generator
         if gen.model is None:
-            return "No ChatQnA Model"
+            raise Exception("No ChatQnA Model")
         if gen.inference_type:
             model = ctx.get_model_mgr().search_model(gen.model)
             if model is None:
@@ -193,15 +231,22 @@ def update_pipeline_handler(pl, req):
                     gen.model.model_type = ModelType.VLLM
                 else:
                     gen.model.model_type = ModelType.LLM
-                model = ctx.get_model_mgr().load_model(gen.model)
+                if pl.enable_benchmark:
+                    model, tokenizer, bench_hook = ctx.get_model_mgr().load_model_ben(gen.model)
+                else:
+                    model = ctx.get_model_mgr().load_model(gen.model)
                 ctx.get_model_mgr().add(model)
             # Use weakref to achieve model deletion and memory release
             model_ref = weakref.ref(model)
             pl.generator = QnAGenerator(model_ref, gen.prompt_path, gen.inference_type)
-
-            pl.benchmark = Benchmark(pl.enable_benchmark, gen.inference_type)
+            if pl.enable_benchmark:
+                if "tokenizer" not in locals() or tokenizer is None:
+                    _, tokenizer, bench_hook = ctx.get_model_mgr().load_model_ben(gen.model)
+                pl.benchmark = Benchmark(pl.enable_benchmark, gen.inference_type, tokenizer, bench_hook)
+            else:
+                pl.benchmark = Benchmark(pl.enable_benchmark, gen.inference_type)
         else:
-            return "Inference Type Not Supported"
+            raise Exception("Inference Type Not Supported")
 
     if pl.status.active != req.active:
         ctx.get_pipeline_mgr().activate_pipeline(pl.name, req.active, ctx.get_node_mgr())
diff --git a/EdgeCraftRAG/edgecraftrag/api/v1/system.py b/EdgeCraftRAG/edgecraftrag/api/v1/system.py
new file mode 100644
index 0000000000..25def7f78a
--- /dev/null
+++ b/EdgeCraftRAG/edgecraftrag/api/v1/system.py
@@ -0,0 +1,71 @@
+# Copyright (C) 2024 Intel Corporation
+# SPDX-License-Identifier: Apache-2.0
+
+import platform
+from datetime import datetime
+
+import cpuinfo
+import distro
+import openvino.runtime as ov
+import psutil
+from fastapi import FastAPI, HTTPException, status
+
+
+def get_available_devices():
+    core = ov.Core()
+    avail_devices = core.available_devices + ["AUTO"]
+    if "NPU" in avail_devices:
+        avail_devices.remove("NPU")
+    return avail_devices
+
+
+def get_system_status():
+    cpu_usage = psutil.cpu_percent(interval=1)
+    memory_info = psutil.virtual_memory()
+    memory_usage = memory_info.percent
+    memory_total_gb = memory_info.total / (1024**3)
+    memory_used_gb = memory_info.used / (1024**3)
+    # uptime_seconds = time.time() - psutil.boot_time()
+    # uptime_hours, uptime_minutes = divmod(uptime_seconds // 60, 60)
+    disk_usage = psutil.disk_usage("/").percent
+    # net_io = psutil.net_io_counters()
+    os_info = platform.uname()
+    kernel_version = os_info.release
+    processor = cpuinfo.get_cpu_info()["brand_raw"]
+    dist_name = distro.name(pretty=True)
+
+    now = datetime.now()
+    current_time_str = now.strftime("%Y-%m-%d %H:%M")
+    status = {
+        "cpuUsage": cpu_usage,
+        "memoryUsage": memory_usage,
+        "memoryUsed": memory_used_gb,
+        "memoryTotal": memory_total_gb,
+        "diskUsage": disk_usage,
+        "kernel": kernel_version,
+        "processor": processor,
+        "os": dist_name,
+        "currentTime": current_time_str,
+    }
+    return status
+
+
+system_app = FastAPI()
+
+
+# GET Syetem info
+@system_app.get(path="/v1/system/info")
+async def get_system_info():
+    try:
+        return get_system_status()
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+
+
+# GET available devices info
+@system_app.get(path="/v1/system/device")
+async def get_device_info():
+    try:
+        return get_available_devices()
+    except Exception as e:
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
diff --git a/EdgeCraftRAG/edgecraftrag/api_schema.py b/EdgeCraftRAG/edgecraftrag/api_schema.py
index 07dfa78836..7a8a493b1e 100644
--- a/EdgeCraftRAG/edgecraftrag/api_schema.py
+++ b/EdgeCraftRAG/edgecraftrag/api_schema.py
@@ -10,7 +10,7 @@ class ModelIn(BaseModel):
     model_type: Optional[str] = "LLM"
     model_id: Optional[str]
     model_path: Optional[str] = "./"
-    weight: Optional[str]
+    weight: Optional[str] = "INT4"
     device: Optional[str] = "cpu"
 
 
diff --git a/EdgeCraftRAG/edgecraftrag/base.py b/EdgeCraftRAG/edgecraftrag/base.py
index f9b52da275..9eabf857ea 100644
--- a/EdgeCraftRAG/edgecraftrag/base.py
+++ b/EdgeCraftRAG/edgecraftrag/base.py
@@ -79,7 +79,6 @@ class InferenceType(str, Enum):
 class CallbackType(str, Enum):
 
     DATAPREP = "dataprep"
-    DATAUPDATE = "dataupdate"
     RETRIEVE = "retrieve"
     PIPELINE = "pipeline"
 
diff --git a/EdgeCraftRAG/edgecraftrag/components/benchmark.py b/EdgeCraftRAG/edgecraftrag/components/benchmark.py
index eac92cf9ac..fc3801b5d3 100644
--- a/EdgeCraftRAG/edgecraftrag/components/benchmark.py
+++ b/EdgeCraftRAG/edgecraftrag/components/benchmark.py
@@ -2,6 +2,7 @@
 # SPDX-License-Identifier: Apache-2.0
 
 import os
+import threading
 from typing import Any, List, Optional
 
 import requests
@@ -12,15 +13,20 @@
 
 class Benchmark(BaseComponent):
 
-    def __init__(self, enable_benchmark, inference_type):
+    def __init__(self, enable_benchmark, inference_type, tokenizer=None, bench_hook=None):
         super().__init__()
         self.enabled = enable_benchmark
         self.is_vllm = True if inference_type == InferenceType.VLLM else False
 
+        self.tokenizer = tokenizer
+        self.bench_hook = bench_hook
+
         self.benchmark_data_list = {}
         self.llm_data_list = {}
 
+        self._idx_lock = threading.Lock()
         self.last_idx = 0
+        self.dict_idx = 0
 
     def is_enabled(self):
         return self.enabled
@@ -31,48 +37,75 @@ def enable(self):
     def disable(self):
         self.enabled = False
 
+    def cal_input_token_size(self, input_text_list):
+        tokenizer = self.tokenizer
+        if tokenizer:
+            input_data = tokenizer(input_text_list, return_tensors="pt")
+            input_data.pop("token_type_ids", None)
+            input_tokens = input_data["input_ids"] if "input_ids" in input_data else input_data
+            input_token_size = input_tokens[0].numel()
+        else:
+            input_token_size = -1
+        return input_token_size
+
     def init_benchmark_data(self):
         pipeline_comp = [CompType.RETRIEVER, CompType.POSTPROCESSOR, CompType.GENERATOR]
         if self.is_enabled():
-            self.last_idx += 1
-            idx = self.last_idx
+            with self._idx_lock:
+                self.last_idx += 1
+                idx = self.last_idx
             data = {}
             data["idx"] = idx
             for comp in pipeline_comp:
                 data[comp] = ""
-            self.benchmark_data_list[idx] = data
-            return idx
+            return idx, data
 
     def update_benchmark_data(self, idx, comp_type, start, end):
         if self.is_enabled() and idx in self.benchmark_data_list and comp_type in self.benchmark_data_list[idx]:
             self.benchmark_data_list[idx][comp_type] = end - start
 
-    def insert_llm_data(self, idx):
+    def insert_benchmark_data(self, benchmark_data):
+        idx = benchmark_data["idx"]
+        self.benchmark_data_list[idx] = benchmark_data
+        self.dict_idx = idx
+
+    def insert_llm_data(self, idx, input_token_size):
         if self.is_enabled():
             if self.is_vllm:
-                metrics = get_vllm_metrics()
+                metrics = {}
+                if input_token_size != -1:
+                    metrics["input_token_size"] = input_token_size
+                metrics = get_vllm_metrics(metrics)
             else:
-                metrics = None
+                bench_hook = self.bench_hook
+                if bench_hook:
+                    metrics = {}
+                    tm_list = bench_hook.get_time_list()
+                    tm_infer_list = bench_hook.get_time_infer_list()
+                    metrics["input_token_size"] = input_token_size
+                    metrics["output_token_size"] = len(tm_list)
+                    metrics["generation_time"] = sum(tm_list)
+                    metrics["first_token_latency"] = tm_list[0] if len(tm_list) > 0 else ""
+                    metrics["other_tokens_avg_latency"] = (
+                        sum(tm_list[1:]) / len(tm_list[1:]) if len(tm_list) > 1 else ""
+                    )
+                    bench_hook.clear_time_list()
+                    bench_hook.clear_time_infer_list()
+                else:
+                    metrics = None
+
             self.llm_data_list[idx] = metrics
 
     @model_serializer
     def ser_model(self):
         if self.enabled:
-            if self.is_vllm:
-                set = {
-                    "Benchmark enabled": self.enabled,
-                    "last_benchmark_data": (
-                        self.benchmark_data_list[self.last_idx] if self.last_idx in self.benchmark_data_list else None
-                    ),
-                    "vllm_metrics": self.llm_data_list[self.last_idx] if self.last_idx in self.llm_data_list else None,
-                }
-            else:
-                set = {
-                    "Benchmark enabled": self.enabled,
-                    "last_benchmark_data": (
-                        self.benchmark_data_list[self.last_idx] if self.last_idx in self.benchmark_data_list else None
-                    ),
-                }
+            set = {
+                "Benchmark enabled": self.enabled,
+                "last_benchmark_data": (
+                    self.benchmark_data_list[self.dict_idx] if self.dict_idx in self.benchmark_data_list else None
+                ),
+                "llm_metrics": self.llm_data_list[self.dict_idx] if self.dict_idx in self.llm_data_list else None,
+            }
         else:
             set = {
                 "Benchmark enabled": self.enabled,
@@ -83,7 +116,7 @@ def run(self, **kwargs) -> Any:
         pass
 
 
-def get_vllm_metrics():
+def get_vllm_metrics(metrics):
 
     llm_endpoint = os.getenv("vLLM_ENDPOINT", "http://localhost:8008")
     response = requests.get(f"{llm_endpoint}/metrics", headers={"Content-Type": "application/json"})
@@ -107,7 +140,7 @@ def get_vllm_metrics():
         "vllm:e2e_request_latency_seconds_sum",
         "vllm:e2e_request_latency_seconds_count",
     ]
-    metrics = {}
+
     for metric in vllm_metrics:
         if metric in parsed_metrics:
             metrics[metric] = parsed_metrics[metric].value
diff --git a/EdgeCraftRAG/edgecraftrag/components/generator.py b/EdgeCraftRAG/edgecraftrag/components/generator.py
index 4ab6096286..d5a3e73ccd 100644
--- a/EdgeCraftRAG/edgecraftrag/components/generator.py
+++ b/EdgeCraftRAG/edgecraftrag/components/generator.py
@@ -5,31 +5,108 @@
 import dataclasses
 import json
 import os
+import urllib.request
+from urllib.parse import urlparse
 
 from comps import GeneratedDoc
-from edgecraftrag.base import BaseComponent, CompType, GeneratorType
+from edgecraftrag.base import BaseComponent, CompType, GeneratorType, NodeParserType
 from fastapi.responses import StreamingResponse
 from langchain_core.prompts import PromptTemplate
 from llama_index.llms.openai_like import OpenAILike
 from pydantic import model_serializer
 from unstructured.staging.base import elements_from_base64_gzipped_json
 
+DEFAULT_TEMPLATE = """
+<|im_start|>System: You are an AI assistant. Your task is to learn from the following context. Then answer the user's question based on what you learned from the context but not your own knowledge.<|im_end|>
 
-async def stream_generator(llm, prompt_str, retrieved_nodes=[], text_gen_context=""):
+<|im_start|>{context}<|im_end|>
+
+<|im_start|>System: Pay attention to your formatting of response. If you need to reference content from context, try to keep the formatting.<|im_end|>
+<|im_start|>System: Try to summarize from the context, do some reasoning before response, then response. Make sure your response is logically sound and self-consistent.<|im_end|>
+
+<|im_start|>{input}
+"""
+
+
+def extract_urls(text):
+    urls = []
+    words = text.split()
+    for word in words:
+        parsed_url = urlparse(word)
+        if parsed_url.scheme and parsed_url.netloc:
+            url = parsed_url.geturl()
+            try:
+                response = urllib.request.urlopen(url)
+                if response.status == 200:
+                    urls.append(url)
+            except (urllib.error.URLError, urllib.error.HTTPError, Exception):
+                pass
+    return urls
+
+
+def extract_unstructured_eles(retrieved_nodes=[], text_gen_context=""):
+    IMAGE_NUMBER = 2
+    image_count = 0
+    link_urls = []
+    image_paths = []
+    reference_docs = set()
+    for node in retrieved_nodes:
+        if node.score < 0.5:
+            continue
+        metadata = node.node.metadata
+        # extract referenced docs
+        if "filename" in metadata:
+            reference_doc = (
+                metadata["filename"]
+                if "page_number" not in metadata
+                else metadata["filename"] + " --page" + str(metadata["page_number"])
+            )
+            reference_docs.add(reference_doc)
+        # extract hyperlinks in chunk
+        if "link_urls" in metadata:
+            if isinstance(metadata["link_urls"], str):
+                try:
+                    url_list = json.loads(metadata["link_urls"])
+                    link_urls.extend(url_list)
+                except json.JSONDecodeError:
+                    print("link_urls is not a valid JSON string.")
+        # extract images in chunk
+        if image_count < IMAGE_NUMBER and "orig_elements" in metadata:
+            elements = elements_from_base64_gzipped_json(metadata["orig_elements"])
+            for element in elements:
+                if element.metadata.image_path:
+                    image_paths.append(element.metadata.image_path)
+                    image_count += 1
+        # extract hyperlinks in chunk
+        link_urls.extend(extract_urls(text_gen_context))
+    unstructured_str = ""
+    if image_paths:
+        unstructured_str += "\n\n参考图片:\n\n"
+        for image_path in image_paths:
+            unstructured_str += f"![]({image_path})"
+    if link_urls:
+        unstructured_str += "\n\n相关链接:\n\n"
+        for link in link_urls:
+            unstructured_str += f"[{link}]({link})\n\n"
+    if reference_docs:
+        unstructured_str += "\n\n内容来源:\n\n"
+        for reference_doc in reference_docs:
+            unstructured_str += f"{reference_doc}\n\n"
+    return unstructured_str
+
+
+async def stream_generator(llm, prompt_str, unstructured_str):
     response = llm.stream_complete(prompt_str)
     for r in response:
-        yield json.dumps({"llm_res": r.delta})
-        await asyncio.sleep(0)
-    for node in retrieved_nodes:
-        node.node.metadata["score"] = float(node.score)
-        yield json.dumps(node.node.metadata)
+        yield r.delta
         await asyncio.sleep(0)
-    yield json.dumps({"retrieved_text": text_gen_context})
+    if unstructured_str:
+        yield unstructured_str
 
 
 class QnAGenerator(BaseComponent):
 
-    def __init__(self, llm_model, prompt_template, inference_type, **kwargs):
+    def __init__(self, llm_model, prompt_template_file, inference_type, **kwargs):
         BaseComponent.__init__(
             self,
             comp_type=CompType.GENERATOR,
@@ -40,19 +117,35 @@ def __init__(self, llm_model, prompt_template, inference_type, **kwargs):
             ("\n\n", "\n"),
             ("\t\n", "\n"),
         )
-        safe_root = "/templates"
-        template = os.path.normpath(os.path.join(safe_root, prompt_template))
-        if not template.startswith(safe_root):
-            raise ValueError("Invalid template path")
-        if not os.path.exists(template):
-            raise ValueError("Template file not exists")
-        self.prompt = DocumentedContextRagPromptTemplate.from_file(template)
+
+        if prompt_template_file is None:
+            print("There is no template file, using the default template.")
+            self.prompt = DocumentedContextRagPromptTemplate.from_template(DEFAULT_TEMPLATE)
+        else:
+            safe_root = "/templates"
+            template_path = os.path.normpath(os.path.join(safe_root, prompt_template_file))
+            if not template_path.startswith(safe_root):
+                raise ValueError("Invalid template path")
+            if not os.path.exists(template_path):
+                raise ValueError("Template file not exists")
+            self.prompt = DocumentedContextRagPromptTemplate.from_file(template_path)
+
         self.llm = llm_model
         if isinstance(llm_model, str):
             self.model_id = llm_model
         else:
             self.model_id = llm_model().model_id
 
+    def set_prompt(self, prompt):
+        if "{context}" not in prompt:
+            prompt += "\n<|im_start|>{context}<|im_end|>"
+        if "{input}" not in prompt:
+            prompt += "\n<|im_start|>{input}"
+        self.prompt = prompt
+
+    def reset_prompt(self):
+        self.prompt = DocumentedContextRagPromptTemplate.from_template(DEFAULT_TEMPLATE)
+
     def clean_string(self, string):
         ret = string
         for p in self._REPLACE_PAIRS:
@@ -72,10 +165,10 @@ def query_transform(self, chat_request, retrieved_nodes):
         prompt_str = self.prompt.format(input=query, context=text_gen_context)
         return text_gen_context, prompt_str
 
-    def run(self, chat_request, retrieved_nodes, **kwargs):
+    def run(self, chat_request, retrieved_nodes, node_parser_type, **kwargs):
         if self.llm() is None:
             # This could happen when User delete all LLMs through RESTful API
-            return "No LLM available, please load LLM"
+            raise ValueError("No LLM available, please load LLM")
         # query transformation
         text_gen_context, prompt_str = self.query_transform(chat_request, retrieved_nodes)
         generate_kwargs = dict(
@@ -88,21 +181,22 @@ def run(self, chat_request, retrieved_nodes, **kwargs):
         )
         self.llm().generate_kwargs = generate_kwargs
         self.llm().max_new_tokens = chat_request.max_tokens
+        unstructured_str = ""
+        if node_parser_type == NodeParserType.UNSTRUCTURED:
+            unstructured_str = extract_unstructured_eles(retrieved_nodes, text_gen_context)
         if chat_request.stream:
             return StreamingResponse(
-                stream_generator(self.llm(), prompt_str, retrieved_nodes, text_gen_context),
+                stream_generator(self.llm(), prompt_str, unstructured_str),
                 media_type="text/event-stream",
             )
         else:
             return self.llm().complete(prompt_str)
 
-    def run_vllm(self, chat_request, retrieved_nodes, **kwargs):
-        if self.llm is None:
-            return "No LLM provided, please provide model_id_or_path"
+    def run_vllm(self, chat_request, retrieved_nodes, node_parser_type, **kwargs):
         # query transformation
         text_gen_context, prompt_str = self.query_transform(chat_request, retrieved_nodes)
         llm_endpoint = os.getenv("vLLM_ENDPOINT", "http://localhost:8008")
-        model_name = self.llm().model_id
+        model_name = os.getenv("LLM_MODEL", self.model_id)
         llm = OpenAILike(
             api_key="fake",
             api_base=llm_endpoint + "/v1",
@@ -114,10 +208,12 @@ def run_vllm(self, chat_request, retrieved_nodes, **kwargs):
             streaming=chat_request.stream,
             repetition_penalty=chat_request.repetition_penalty,
         )
-
+        unstructured_str = ""
+        if node_parser_type == NodeParserType.UNSTRUCTURED:
+            unstructured_str = extract_unstructured_eles(retrieved_nodes, text_gen_context)
         if chat_request.stream:
             return StreamingResponse(
-                stream_generator(llm, prompt_str, retrieved_nodes, text_gen_context), media_type="text/event-stream"
+                stream_generator(llm, prompt_str, unstructured_str), media_type="text/event-stream"
             )
         else:
             response = llm.complete(prompt_str)
diff --git a/EdgeCraftRAG/edgecraftrag/components/indexer.py b/EdgeCraftRAG/edgecraftrag/components/indexer.py
index 71613b1124..abbf67b0c8 100644
--- a/EdgeCraftRAG/edgecraftrag/components/indexer.py
+++ b/EdgeCraftRAG/edgecraftrag/components/indexer.py
@@ -40,8 +40,7 @@ def _initialize_indexer(self, embed_model, vector_type):
                 VectorStoreIndex.__init__(self, embed_model=embed_model, nodes=[], storage_context=faiss_store)
 
     def reinitialize_indexer(self):
-        if self.comp_subtype == IndexerType.FAISS_VECTOR:
-            self._initialize_indexer(self.model, IndexerType.FAISS_VECTOR)
+        self._initialize_indexer(self.model, self.comp_subtype)
 
     def run(self, **kwargs) -> Any:
         pass
diff --git a/EdgeCraftRAG/edgecraftrag/components/model.py b/EdgeCraftRAG/edgecraftrag/components/model.py
index e0c1904619..a41b4a7f01 100644
--- a/EdgeCraftRAG/edgecraftrag/components/model.py
+++ b/EdgeCraftRAG/edgecraftrag/components/model.py
@@ -77,10 +77,11 @@ def __init__(self, model_id, model_path, device, weight):
 
 class OpenVINOLLMModel(BaseModelComponent, OpenVINOLLM):
 
-    def __init__(self, model_id, model_path, device, weight):
+    def __init__(self, model_id, model_path, device, weight, model=None):
         OpenVINOLLM.__init__(
             self,
             model_id_or_path=model_path,
+            model=model,
             device_map=device,
         )
         self.comp_type = CompType.MODEL
diff --git a/EdgeCraftRAG/edgecraftrag/components/node_parser.py b/EdgeCraftRAG/edgecraftrag/components/node_parser.py
index ac2157dc78..0f386bc61f 100644
--- a/EdgeCraftRAG/edgecraftrag/components/node_parser.py
+++ b/EdgeCraftRAG/edgecraftrag/components/node_parser.py
@@ -57,7 +57,7 @@ def ser_model(self):
         set = {
             "idx": self.idx,
             "parser_type": self.comp_subtype,
-            "chunk_size": self.chunk_sizes,
+            "chunk_sizes": self.chunk_sizes,
             "chunk_overlap": None,
         }
         return set
diff --git a/EdgeCraftRAG/edgecraftrag/components/pipeline.py b/EdgeCraftRAG/edgecraftrag/components/pipeline.py
index b09ae86fc2..e0a01eba96 100644
--- a/EdgeCraftRAG/edgecraftrag/components/pipeline.py
+++ b/EdgeCraftRAG/edgecraftrag/components/pipeline.py
@@ -1,13 +1,15 @@
 # Copyright (C) 2024 Intel Corporation
 # SPDX-License-Identifier: Apache-2.0
 
+import json
 import os
 import time
 from typing import Any, Callable, List, Optional
 
 from comps.cores.proto.api_protocol import ChatCompletionRequest
-from edgecraftrag.base import BaseComponent, CallbackType, CompType, InferenceType
+from edgecraftrag.base import BaseComponent, CallbackType, CompType, InferenceType, RetrieverType
 from edgecraftrag.components.postprocessor import RerankProcessor
+from edgecraftrag.components.retriever import AutoMergeRetriever, SimpleBM25Retriever, VectorSimRetriever
 from fastapi.responses import StreamingResponse
 from llama_index.core.schema import Document, QueryBundle
 from pydantic import BaseModel, Field, model_serializer
@@ -29,11 +31,11 @@ class Pipeline(BaseComponent):
     run_pipeline_cb: Optional[Callable[..., Any]] = Field(default=None)
     run_retriever_cb: Optional[Callable[..., Any]] = Field(default=None)
     run_data_prepare_cb: Optional[Callable[..., Any]] = Field(default=None)
-    run_data_update_cb: Optional[Callable[..., Any]] = Field(default=None)
 
     def __init__(
         self,
         name,
+        origin_json=None,
     ):
         super().__init__(name=name, comp_type=CompType.PIPELINE)
         if self.name == "" or self.name is None:
@@ -42,25 +44,77 @@ def __init__(
         self.run_pipeline_cb = run_test_generator_ben if self.enable_benchmark else run_test_generator
         self.run_retriever_cb = run_test_retrieve
         self.run_data_prepare_cb = run_simple_doc
-        self.run_data_update_cb = run_update_doc
-        self._node_changed = True
+
+        self._node_changed = False
+        self._index_changed = False
+        self._index_to_retriever_updated = True
+        self._origin_json = origin_json
 
     # TODO: consider race condition
+    @property
+    def get_pipeline_json(self) -> str:
+        return self._origin_json
+
+    def update_pipeline_json(self, pipeline_dict):
+        origin_json = json.loads(self._origin_json)
+        for k, v in pipeline_dict.items():
+            if v is not None:
+                origin_json[k] = v
+        self._origin_json = json.dumps(origin_json)
+
     @property
     def node_changed(self) -> bool:
         return self._node_changed
 
+    def reset_node_status(self) -> bool:
+        self._node_changed = False
+        self._index_changed = False
+        self._index_to_retriever_updated = True
+
+    def check_active(self, nodelist):
+        if self._node_changed:
+            if not self._index_changed:
+                print("Reinitializing indexer ...")
+                self.indexer.reinitialize_indexer()
+                self._index_changed = True
+                self._index_to_retriever_updated = False
+
+            if nodelist is not None and len(nodelist) > 0:
+                self.update_nodes(nodelist)
+            self._node_changed = False
+
+        # Due to limitation, need to update retriever's db after reinitialize_indexer()
+        if self._index_changed and not self._index_to_retriever_updated:
+            self.update_indexer_to_retriever()
+            self._index_changed = False
+            self._index_to_retriever_updated = True
+
+        self.reset_node_status()
+
     # TODO: update doc changes
     # TODO: more operations needed, add, del, modify
     def update_nodes(self, nodes):
-        print("updating nodes ", nodes)
+        print(f"Updating {len(nodes)} nodes ...")
         if self.indexer is not None:
             self.indexer.insert_nodes(nodes)
 
-    # TODO: check more conditions
-    def check_active(self, nodelist):
-        if self._node_changed and nodelist is not None:
-            self.update_nodes(nodelist)
+    def update_indexer_to_retriever(self):
+        print("Updating indexer to retriever ...")
+        if self.indexer is not None and self.retriever is not None:
+            old_retriever = self.retriever
+            retriever_type = old_retriever.comp_subtype
+            similarity_top_k = old_retriever.topk
+            match retriever_type:
+                case RetrieverType.VECTORSIMILARITY:
+                    new_retriever = VectorSimRetriever(self.indexer, similarity_top_k=similarity_top_k)
+                case RetrieverType.AUTOMERGE:
+                    new_retriever = AutoMergeRetriever(self.indexer, similarity_top_k=similarity_top_k)
+                case RetrieverType.BM25:
+                    new_retriever = SimpleBM25Retriever(self.indexer, similarity_top_k=similarity_top_k)
+                case _:
+                    new_retriever = old_retriever
+
+            self.retriever = new_retriever
 
     # Implement abstract run function
     # callback dispatcher
@@ -70,9 +124,6 @@ def run(self, **kwargs) -> Any:
             if kwargs["cbtype"] == CallbackType.DATAPREP:
                 if "docs" in kwargs:
                     return self.run_data_prepare_cb(self, docs=kwargs["docs"])
-            if kwargs["cbtype"] == CallbackType.DATAUPDATE:
-                if "docs" in kwargs:
-                    return self.run_data_update_cb(self, docs=kwargs["docs"])
             if kwargs["cbtype"] == CallbackType.RETRIEVE:
                 if "chat_request" in kwargs:
                     return self.run_retriever_cb(self, chat_request=kwargs["chat_request"])
@@ -151,27 +202,16 @@ def run_simple_doc(pl: Pipeline, docs: List[Document]) -> Any:
     return n
 
 
-def run_update_doc(pl: Pipeline, docs: List[Document]) -> Any:
-    n = pl.node_parser.run(docs=docs)
-    if pl.indexer is not None:
-        pl.indexer.reinitialize_indexer()
-        pl.retriever._vector_store = pl.indexer.vector_store
-        pl.retriever._docstore = pl.indexer.docstore
-
-        pl.indexer.insert_nodes(n)
-    print(pl.indexer._index_struct)
-    return n
-
-
-def benchmark_response(ret, benchmark, benchmark_index, start):
+def benchmark_response(ret, benchmark, benchmark_index, benchmark_data, input_token_size, start):
     if isinstance(ret, StreamingResponse):
         original_body_iterator = ret.body_iterator
 
         async def timing_wrapper():
             async for chunk in original_body_iterator:
                 yield chunk
-            benchmark.update_benchmark_data(benchmark_index, CompType.GENERATOR, start, time.perf_counter())
-            benchmark.insert_llm_data(benchmark_index)
+            benchmark_data[CompType.GENERATOR] = time.perf_counter() - start
+            benchmark.insert_llm_data(benchmark_index, input_token_size)
+            benchmark.insert_benchmark_data(benchmark_data)
 
         ret.body_iterator = timing_wrapper()
         return ret
@@ -180,12 +220,12 @@ async def timing_wrapper():
 
 
 def run_test_generator_ben(pl: Pipeline, chat_request: ChatCompletionRequest) -> Any:
-    benchmark_index = pl.benchmark.init_benchmark_data()
+    benchmark_index, benchmark_data = pl.benchmark.init_benchmark_data()
     start = time.perf_counter()
     query = chat_request.messages
     retri_res = pl.retriever.run(query=query)
     query_bundle = QueryBundle(query)
-    pl.benchmark.update_benchmark_data(benchmark_index, CompType.RETRIEVER, start, time.perf_counter())
+    benchmark_data[CompType.RETRIEVER] = time.perf_counter() - start
 
     start = time.perf_counter()
     if pl.postprocessor:
@@ -196,24 +236,30 @@ def run_test_generator_ben(pl: Pipeline, chat_request: ChatCompletionRequest) ->
             ):
                 processor.top_n = chat_request.top_n
             retri_res = processor.run(retri_res=retri_res, query_bundle=query_bundle)
-    pl.benchmark.update_benchmark_data(benchmark_index, CompType.POSTPROCESSOR, start, time.perf_counter())
+    benchmark_data[CompType.POSTPROCESSOR] = time.perf_counter() - start
 
-    start = time.perf_counter()
     if pl.generator is None:
-        ret = "No Generator Specified"
+        raise ValueError("No Generator Specified")
+
+    text_gen_context, prompt_str = pl.generator.query_transform(chat_request, retri_res)
+    input_token_size = pl.benchmark.cal_input_token_size(prompt_str)
+
+    np_type = pl.node_parser.comp_subtype
+    start = time.perf_counter()
     if pl.generator.inference_type == InferenceType.LOCAL:
-        ret = pl.generator.run(chat_request, retri_res)
+        ret = pl.generator.run(chat_request, retri_res, np_type)
     elif pl.generator.inference_type == InferenceType.VLLM:
-        ret = pl.generator.run_vllm(chat_request, retri_res)
+        ret = pl.generator.run_vllm(chat_request, retri_res, np_type)
     else:
-        ret = "LLM inference_type not supported"
+        raise ValueError("LLM inference_type not supported")
     end = time.perf_counter()
 
     if isinstance(ret, StreamingResponse):
-        ret = benchmark_response(ret, pl.benchmark, benchmark_index, start)
+        ret = benchmark_response(ret, pl.benchmark, benchmark_index, benchmark_data, input_token_size, start)
     else:
-        pl.benchmark.update_benchmark_data(benchmark_index, CompType.GENERATOR, start, end)
-        pl.benchmark.insert_llm_data(benchmark_index)
+        benchmark_data[CompType.GENERATOR] = end - start
+        pl.benchmark.insert_llm_data(benchmark_index, input_token_size)
+        pl.benchmark.insert_benchmark_data(benchmark_data)
     return ret, retri_res
 
 
@@ -232,11 +278,12 @@ def run_test_generator(pl: Pipeline, chat_request: ChatCompletionRequest) -> Any
             retri_res = processor.run(retri_res=retri_res, query_bundle=query_bundle)
 
     if pl.generator is None:
-        ret = "No Generator Specified"
+        raise ValueError("No Generator Specified")
+    np_type = pl.node_parser.comp_subtype
     if pl.generator.inference_type == InferenceType.LOCAL:
-        ret = pl.generator.run(chat_request, retri_res)
+        ret = pl.generator.run(chat_request, retri_res, np_type)
     elif pl.generator.inference_type == InferenceType.VLLM:
-        ret = pl.generator.run_vllm(chat_request, retri_res)
+        ret = pl.generator.run_vllm(chat_request, retri_res, np_type)
     else:
-        ret = "LLM inference_type not supported"
+        raise ValueError("LLM inference_type not supported")
     return ret, retri_res
diff --git a/EdgeCraftRAG/edgecraftrag/components/postprocessor.py b/EdgeCraftRAG/edgecraftrag/components/postprocessor.py
index 672826bdbb..bb59cc3d21 100644
--- a/EdgeCraftRAG/edgecraftrag/components/postprocessor.py
+++ b/EdgeCraftRAG/edgecraftrag/components/postprocessor.py
@@ -33,7 +33,7 @@ def run(self, **kwargs) -> Any:
 
     @model_serializer
     def ser_model(self):
-        set = {"idx": self.idx, "postprocessor_type": self.comp_subtype, "model": self.model, "top_n": self.top_n}
+        set = {"idx": self.idx, "processor_type": self.comp_subtype, "model": self.model, "top_n": self.top_n}
         return set
 
 
@@ -60,5 +60,5 @@ def run(self, **kwargs) -> Any:
 
     @model_serializer
     def ser_model(self):
-        set = {"idx": self.idx, "postprocessor_type": self.comp_subtype, "model": None, "top_n": None}
+        set = {"idx": self.idx, "processor_type": self.comp_subtype, "model": None, "top_n": None}
         return set
diff --git a/EdgeCraftRAG/edgecraftrag/components/retriever.py b/EdgeCraftRAG/edgecraftrag/components/retriever.py
index c5aa18c28a..fa8553346a 100644
--- a/EdgeCraftRAG/edgecraftrag/components/retriever.py
+++ b/EdgeCraftRAG/edgecraftrag/components/retriever.py
@@ -19,6 +19,8 @@ def __init__(self, indexer, **kwargs):
             comp_type=CompType.RETRIEVER,
             comp_subtype=RetrieverType.VECTORSIMILARITY,
         )
+        self.topk = kwargs["similarity_top_k"]
+
         VectorIndexRetriever.__init__(
             self,
             index=indexer,
@@ -79,6 +81,15 @@ def run(self, **kwargs) -> Any:
 
         return None
 
+    @model_serializer
+    def ser_model(self):
+        set = {
+            "idx": self.idx,
+            "retriever_type": self.comp_subtype,
+            "retrieve_topk": self.topk,
+        }
+        return set
+
 
 class SimpleBM25Retriever(BaseComponent):
     # The nodes parameter in BM25Retriever is not from index,
@@ -103,3 +114,12 @@ def run(self, **kwargs) -> Any:
                 return bm25_retr.retrieve(v)
 
         return None
+
+    @model_serializer
+    def ser_model(self):
+        set = {
+            "idx": self.idx,
+            "retriever_type": self.comp_subtype,
+            "retrieve_topk": self.topk,
+        }
+        return set
diff --git a/EdgeCraftRAG/edgecraftrag/controllers/modelmgr.py b/EdgeCraftRAG/edgecraftrag/controllers/modelmgr.py
index 6d0166bc56..b2715c27fa 100644
--- a/EdgeCraftRAG/edgecraftrag/controllers/modelmgr.py
+++ b/EdgeCraftRAG/edgecraftrag/controllers/modelmgr.py
@@ -105,3 +105,38 @@ def load_model(model_para: ModelIn):
                 model.comp_subtype = ModelType.VLLM
                 model.model_id_or_path = model_para.model_id
         return model
+
+    @staticmethod
+    def load_model_ben(model_para: ModelIn):
+        model = None
+        tokenizer = None
+        bench_hook = None
+        match model_para.model_type:
+            case ModelType.LLM:
+                from optimum.intel import OVModelForCausalLM
+
+                ov_model = OVModelForCausalLM.from_pretrained(
+                    model_para.model_path,
+                    device=model_para.device,
+                    weight=model_para.weight,
+                )
+                from llm_bench_utils.hook_common import get_bench_hook
+
+                num_beams = 1
+                bench_hook = get_bench_hook(num_beams, ov_model)
+                model = OpenVINOLLMModel(
+                    model_id=model_para.model_id,
+                    model_path=model_para.model_path,
+                    device=model_para.device,
+                    weight=model_para.weight,
+                    model=ov_model,
+                )
+                from transformers import AutoTokenizer
+
+                tokenizer = AutoTokenizer.from_pretrained(model_para.model_path, trust_remote_code=True)
+            case ModelType.VLLM:
+                model = BaseModelComponent(model_id=model_para.model_id, model_path="", device="", weight="")
+                model.comp_type = CompType.MODEL
+                model.comp_subtype = ModelType.VLLM
+                model.model_id_or_path = model_para.model_id
+        return model, tokenizer, bench_hook
diff --git a/EdgeCraftRAG/edgecraftrag/controllers/nodemgr.py b/EdgeCraftRAG/edgecraftrag/controllers/nodemgr.py
index 12101010c5..40b825af5b 100644
--- a/EdgeCraftRAG/edgecraftrag/controllers/nodemgr.py
+++ b/EdgeCraftRAG/edgecraftrag/controllers/nodemgr.py
@@ -16,7 +16,7 @@ def __init__(self):
     # idx: index of node_parser
     def add_nodes(self, np_idx, nodes):
         if np_idx in self.nodes:
-            self.nodes[np_idx].append(nodes)
+            self.nodes[np_idx].extend(nodes)
         else:
             self.nodes[np_idx] = nodes
 
diff --git a/EdgeCraftRAG/edgecraftrag/controllers/pipelinemgr.py b/EdgeCraftRAG/edgecraftrag/controllers/pipelinemgr.py
index c3a0fd5ecc..c03598f54b 100644
--- a/EdgeCraftRAG/edgecraftrag/controllers/pipelinemgr.py
+++ b/EdgeCraftRAG/edgecraftrag/controllers/pipelinemgr.py
@@ -19,8 +19,8 @@ def __init__(self):
         self._lock = asyncio.Lock()
         super().__init__()
 
-    def create_pipeline(self, name: str):
-        pl = Pipeline(name)
+    def create_pipeline(self, name: str, origin_json: str):
+        pl = Pipeline(name, origin_json)
         self.add(pl)
         return pl
 
@@ -33,9 +33,9 @@ def get_pipeline_by_name_or_id(self, name: str):
     def remove_pipeline_by_name_or_id(self, name: str):
         pl = self.get_pipeline_by_name_or_id(name)
         if pl is None:
-            return "Pipeline not found..."
+            raise Exception("Pipeline not found...")
         if pl.status.active:
-            return "Unable to remove an active pipeline..."
+            raise Exception("Unable to remove an active pipeline...")
         pl.node_parser = None
         pl.indexer = None
         pl.retriever = None
@@ -46,7 +46,6 @@ def remove_pipeline_by_name_or_id(self, name: str):
         pl.run_pipeline_cb = None
         pl.run_retriever_cb = None
         pl.run_data_prepare_cb = None
-        pl.run_data_update_cb = None
         pl._node_changed = None
         self.remove(pl.idx)
         del pl
@@ -58,18 +57,22 @@ def get_pipelines(self):
 
     def activate_pipeline(self, name: str, active: bool, nm: NodeMgr):
         pl = self.get_pipeline_by_name_or_id(name)
+        if pl is None:
+            return
+
+        if not active:
+            pl.status.active = False
+            self._active_pipeline = None
+            return
+
         nodelist = None
-        if pl is not None:
-            if not active:
-                pl.status.active = False
-                self._active_pipeline = None
-                return
-            if pl.node_changed:
-                nodelist = nm.get_nodes(pl.node_parser.idx)
+        if pl.node_changed:
+            nodelist = nm.get_nodes(pl.node_parser.idx)
         pl.check_active(nodelist)
         prevactive = self._active_pipeline
         if prevactive:
             prevactive.status.active = False
+            prevactive.update_pipeline_json({"active": prevactive.status.active})
         pl.status.active = True
         self._active_pipeline = pl
 
@@ -101,9 +104,3 @@ def run_data_prepare(self, docs: List[Document]) -> Any:
         if ap is not None:
             return ap.run(cbtype=CallbackType.DATAPREP, docs=docs)
         return -1
-
-    def run_data_update(self, docs: List[Document]) -> Any:
-        ap = self.get_active_pipeline()
-        if ap is not None:
-            return ap.run(cbtype=CallbackType.DATAUPDATE, docs=docs)
-        return -1
diff --git a/EdgeCraftRAG/edgecraftrag/server.py b/EdgeCraftRAG/edgecraftrag/server.py
index 705c3f07ba..cd8a7f8eab 100644
--- a/EdgeCraftRAG/edgecraftrag/server.py
+++ b/EdgeCraftRAG/edgecraftrag/server.py
@@ -8,12 +8,25 @@
 from edgecraftrag.api.v1.data import data_app
 from edgecraftrag.api.v1.model import model_app
 from edgecraftrag.api.v1.pipeline import pipeline_app
+from edgecraftrag.api.v1.system import system_app
+from edgecraftrag.utils import UI_DIRECTORY
 from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.staticfiles import StaticFiles
 from llama_index.core.settings import Settings
 
 app = FastAPI()
+app.mount(UI_DIRECTORY, StaticFiles(directory=UI_DIRECTORY), name=UI_DIRECTORY)
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=["*"],
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
 
-sub_apps = [data_app, model_app, pipeline_app, chatqna_app]
+
+sub_apps = [data_app, model_app, pipeline_app, chatqna_app, system_app]
 for sub_app in sub_apps:
     for route in sub_app.routes:
         app.router.routes.append(route)
diff --git a/EdgeCraftRAG/edgecraftrag/utils.py b/EdgeCraftRAG/edgecraftrag/utils.py
index d954e75860..be83f47135 100644
--- a/EdgeCraftRAG/edgecraftrag/utils.py
+++ b/EdgeCraftRAG/edgecraftrag/utils.py
@@ -10,8 +10,8 @@
 from unstructured.documents.elements import ElementMetadata, Image
 from unstructured.partition.docx import DocxPartitionerOptions
 
-GRADIO_TEMP_DIR = os.getenv("GRADIO_TEMP_DIR", "/home/user/gradio_cache")
-IMG_OUTPUT_DIR = os.path.join(GRADIO_TEMP_DIR, "pic")
+UI_DIRECTORY = os.getenv("UI_TMPFILE_PATH", "/home/user/ui_cache")
+IMG_OUTPUT_DIR = os.path.join(UI_DIRECTORY, "pic")
 os.makedirs(IMG_OUTPUT_DIR, exist_ok=True)
 
 
diff --git a/EdgeCraftRAG/tests/test_compose_on_arc.sh b/EdgeCraftRAG/tests/test_compose_on_arc.sh
index 0a0674dc69..4d41cdac1a 100755
--- a/EdgeCraftRAG/tests/test_compose_on_arc.sh
+++ b/EdgeCraftRAG/tests/test_compose_on_arc.sh
@@ -24,7 +24,7 @@ EC_RAG_SERVICE_PORT=16010
 MODEL_PATH="/home/media/models"
 # MODEL_PATH="$WORKPATH/models"
 DOC_PATH="$WORKPATH/tests"
-GRADIO_PATH="$WORKPATH/tests"
+UI_UPLOAD_PATH="$WORKPATH/tests"
 
 HF_ENDPOINT=https://hf-mirror.com
 
@@ -40,7 +40,7 @@ function build_docker_images() {
 function start_services() {
     export MODEL_PATH=${MODEL_PATH}
     export DOC_PATH=${DOC_PATH}
-    export GRADIO_PATH=${GRADIO_PATH}
+    export UI_UPLOAD_PATH=${UI_UPLOAD_PATH}
     export HOST_IP=${HOST_IP}
     export LLM_MODEL=${LLM_MODEL}
     export HF_ENDPOINT=${HF_ENDPOINT}
diff --git a/EdgeCraftRAG/tests/test_compose_vllm_on_arc.sh b/EdgeCraftRAG/tests/test_compose_vllm_on_arc.sh
index 2522e41273..feeba14fb5 100755
--- a/EdgeCraftRAG/tests/test_compose_vllm_on_arc.sh
+++ b/EdgeCraftRAG/tests/test_compose_vllm_on_arc.sh
@@ -24,7 +24,7 @@ EC_RAG_SERVICE_PORT=16010
 MODEL_PATH="/home/media/models"
 # MODEL_PATH="$WORKPATH/models"
 DOC_PATH="$WORKPATH/tests"
-GRADIO_PATH="$WORKPATH/tests"
+UI_TMPFILE_PATH="$WORKPATH/tests"
 
 #HF_ENDPOINT=https://hf-mirror.com
 LLM_MODEL="Qwen/Qwen2-7B-Instruct"
@@ -48,7 +48,7 @@ function build_docker_images() {
 function start_services() {
     export MODEL_PATH=${MODEL_PATH}
     export DOC_PATH=${DOC_PATH}
-    export GRADIO_PATH=${GRADIO_PATH}
+    export UI_TMPFILE_PATH=${UI_TMPFILE_PATH}
     export HOST_IP=${HOST_IP}
     export LLM_MODEL=${LLM_MODEL}
     export HF_ENDPOINT=${HF_ENDPOINT}
diff --git a/EdgeCraftRAG/ui/docker/Dockerfile.gradio b/EdgeCraftRAG/ui/docker/Dockerfile.gradio
new file mode 100644
index 0000000000..08880b4679
--- /dev/null
+++ b/EdgeCraftRAG/ui/docker/Dockerfile.gradio
@@ -0,0 +1,25 @@
+FROM python:3.11-slim
+
+RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \
+    libgl1-mesa-glx \
+    libjemalloc-dev 
+
+RUN useradd -m -s /bin/bash user && \
+    mkdir -p /home/user && \
+    chown -R user /home/user/
+
+COPY ./ui/gradio /home/user/ui
+COPY ./edgecraftrag /home/user/edgecraftrag
+
+RUN mkdir -p /home/user/ui_cache/
+ENV GRADIO_TEMP_DIR=/home/user/ui_cache
+
+WORKDIR /home/user/ui
+RUN pip install --no-cache-dir --upgrade pip setuptools==70.0.0 && \
+    pip install --no-cache-dir -r requirements.txt
+
+USER user
+
+RUN echo 'ulimit -S -n 999999' >> ~/.bashrc
+
+ENTRYPOINT ["python", "ecragui.py"]
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/docker/Dockerfile.ui b/EdgeCraftRAG/ui/docker/Dockerfile.ui
index 8f8b9b0fb6..9bca70ff3b 100644
--- a/EdgeCraftRAG/ui/docker/Dockerfile.ui
+++ b/EdgeCraftRAG/ui/docker/Dockerfile.ui
@@ -1,25 +1,29 @@
-FROM python:3.11-slim
-
-RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \
-    libgl1-mesa-glx \
-    libjemalloc-dev 
-
+FROM node:20.18.1 AS vue-app
+ 
+RUN apt-get update -y && apt-get install -y git
+ 
 RUN useradd -m -s /bin/bash user && \
     mkdir -p /home/user && \
     chown -R user /home/user/
-
-COPY ./ui/gradio /home/user/ui
-COPY ./edgecraftrag /home/user/edgecraftrag
-
-RUN mkdir -p /home/user/gradio_cache 
-ENV GRADIO_TEMP_DIR=/home/user/gradio_cache
-
+ 
+COPY ./ui/vue /home/user/ui
+ 
+RUN mkdir -p /home/user/ui
+ENV UI_TMPFILE_PATH=/home/user/ui
+ 
 WORKDIR /home/user/ui
-RUN pip install --no-cache-dir --upgrade pip setuptools==70.0.0 && \
-    pip install --no-cache-dir -r requirements.txt
+ 
+RUN npm install
+ 
+RUN npm run build
 
-USER user
+FROM nginx:stable-alpine
 
-RUN echo 'ulimit -S -n 999999' >> ~/.bashrc
+COPY --from=vue-app home/user/ui/dist /usr/share/nginx/html
 
-ENTRYPOINT ["python", "ecragui.py"]
+COPY ./ui/vue/nginx.conf /etc/nginx/nginx.conf
+ 
+EXPOSE 8082
+ 
+CMD ["nginx", "-g", "daemon off;"]
+ 
diff --git a/EdgeCraftRAG/ui/gradio/ecragui.py b/EdgeCraftRAG/ui/gradio/ecragui.py
index 8e46dee03c..7aa03763d0 100644
--- a/EdgeCraftRAG/ui/gradio/ecragui.py
+++ b/EdgeCraftRAG/ui/gradio/ecragui.py
@@ -3,14 +3,12 @@
 
 import argparse
 import base64
-import json
 import os
 import platform
+import re
 import time
-import urllib.request
 from datetime import datetime
 from threading import Timer
-from urllib.parse import urlparse
 
 import cpuinfo
 import distro  # if running Python 3.8 or above
@@ -29,7 +27,6 @@
     get_available_weights,
     get_local_available_models,
 )
-from unstructured.staging.base import elements_from_base64_gzipped_json
 
 pipeline_df = []
 
@@ -53,22 +50,6 @@ def get_image_base64(image_path):
     return image_base64
 
 
-def extract_urls(text):
-    urls = []
-    words = text.split()
-    for word in words:
-        parsed_url = urlparse(word)
-        if parsed_url.scheme and parsed_url.netloc:
-            url = parsed_url.geturl()
-            try:
-                response = urllib.request.urlopen(url)
-                if response.status == 200:
-                    urls.append(url)
-            except (urllib.error.URLError, urllib.error.HTTPError, Exception):
-                pass
-    return urls
-
-
 def get_system_status():
     cpu_usage = psutil.cpu_percent(interval=1)
     memory_info = psutil.virtual_memory()
@@ -177,63 +158,20 @@ async def bot(
 
         # Async for stream response
         partial_text = ""
-        link_urls = []
-        image_paths = []
-        reference_docs = set()
-        IMAGE_NUMBER = 2
         async with httpx.AsyncClient() as client:
             async with client.stream("POST", f"{server_addr}/v1/chatqna", json=new_req, timeout=None) as response:
-                image_count = 0
                 async for chunk in response.aiter_text():
-                    if chunk.strip():
-                        try:
-                            data = json.loads(chunk)
-                            if "llm_res" in data:
-                                partial_text = partial_text + data["llm_res"]
-                            elif "score" in data:
-                                # show referenced docs
-                                if "filename" in data:
-                                    reference_doc = (
-                                        data["filename"]
-                                        if "page_number" not in data
-                                        else data["filename"] + " --page" + str(data["page_number"])
-                                    )
-                                    reference_docs.add(reference_doc)
-                                # show hyperlinks in chunk
-                                if data["score"] > 0.5 and "link_urls" in data:
-                                    if isinstance(data["link_urls"], str):
-                                        try:
-                                            url_list = json.loads(data["link_urls"])
-                                            link_urls.extend(url_list)
-                                        except json.JSONDecodeError:
-                                            print("link_urls is not a valid JSON string.")
-                                # show images in chunk
-                                if image_count < IMAGE_NUMBER and "orig_elements" in data:
-                                    elements = elements_from_base64_gzipped_json(data["orig_elements"])
-                                    for element in elements:
-                                        if element.metadata.image_path:
-                                            image_paths.append(element.metadata.image_path)
-                                            image_count += 1
-                            elif "retrieved_text" in data:
-                                link_urls.extend(extract_urls(data["retrieved_text"]))
-                        except json.JSONDecodeError:
-                            print(f"Received non-JSON chunk: {chunk}")
+                    wrap_text = chunk
+                    if "参考图片" in chunk:
+                        image_paths = re.compile(r"!\[\]\((.*?)\)").findall(chunk)
+                        for image_path in image_paths:
+                            image_base64 = get_image_base64(image_path)
+                            wrap_text = chunk.replace(
+                                f"![]({image_path})", f'<img src="data:image/png;base64,{image_base64}">'
+                            )
+                    partial_text = partial_text + wrap_text
                     history[-1][1] = partial_text
                     yield history
-        if image_paths:
-            history[-1][1] += "\n参考图片:\n"
-            for image_path in image_paths:
-                image_base64 = get_image_base64(image_path)
-                history[-1][1] += f'<img src="data:image/png;base64,{image_base64}">'
-        if link_urls:
-            history[-1][1] += "\n相关链接:\n"
-            for link in link_urls:
-                history[-1][1] += f"{link}\n"
-        if reference_docs:
-            history[-1][1] += "\n内容来源:\n"
-            for reference_doc in reference_docs:
-                history[-1][1] += f"{reference_doc}\n"
-        yield history
 
     avail_llms = get_local_available_models("llm")
     avail_embed_models = get_local_available_models("embed")
@@ -520,7 +458,7 @@ def show_pipeline_detail(evt: gr.SelectData):
                 pl["indexer"]["indexer_type"],
                 pl["retriever"]["retriever_type"],
                 pl["retriever"]["retrieve_topk"],
-                pl["postprocessor"][0]["postprocessor_type"],
+                pl["postprocessor"][0]["processor_type"],
                 pl["generator"]["generator_type"],
                 pl["generator"]["inference_type"],
                 pl["generator"]["model"]["model_id"],
@@ -697,6 +635,7 @@ def get_files():
             return cli.get_files()
 
         def create_vectordb(docs, spliter):
+
             res = cli.create_vectordb(docs, spliter)
             return gr.update(value=get_files()), res, None
 
diff --git a/EdgeCraftRAG/ui/vue/.env.development b/EdgeCraftRAG/ui/vue/.env.development
new file mode 100644
index 0000000000..c2237f95f5
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/.env.development
@@ -0,0 +1,6 @@
+# Local
+ENV = development
+
+# Local Api
+VITE_API_URL = http://10.67.106.236:16010/
+VITE_CHATBOT_URL = http://10.67.106.236:16011/
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/.env.production b/EdgeCraftRAG/ui/vue/.env.production
new file mode 100644
index 0000000000..f276429632
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/.env.production
@@ -0,0 +1,6 @@
+# Online
+ENV = production
+
+# Online Api
+VITE_API_URL = /
+VITE_CHATBOT_URL = /
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/.gitignore b/EdgeCraftRAG/ui/vue/.gitignore
new file mode 100644
index 0000000000..a547bf36d8
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/EdgeCraftRAG/ui/vue/README.md b/EdgeCraftRAG/ui/vue/README.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/EdgeCraftRAG/ui/vue/auto-imports.d.ts b/EdgeCraftRAG/ui/vue/auto-imports.d.ts
new file mode 100644
index 0000000000..d6fd8da012
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/auto-imports.d.ts
@@ -0,0 +1,106 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+// biome-ignore lint: disable
+export {}
+declare global {
+  const EffectScope: (typeof import("vue"))["EffectScope"];
+  const acceptHMRUpdate: (typeof import("pinia"))["acceptHMRUpdate"];
+  const computed: (typeof import("vue"))["computed"];
+  const createApp: (typeof import("vue"))["createApp"];
+  const createPinia: (typeof import("pinia"))["createPinia"];
+  const customRef: (typeof import("vue"))["customRef"];
+  const defineAsyncComponent: (typeof import("vue"))["defineAsyncComponent"];
+  const defineComponent: (typeof import("vue"))["defineComponent"];
+  const defineStore: (typeof import("pinia"))["defineStore"];
+  const effectScope: (typeof import("vue"))["effectScope"];
+  const getActivePinia: (typeof import("pinia"))["getActivePinia"];
+  const getCurrentInstance: (typeof import("vue"))["getCurrentInstance"];
+  const getCurrentScope: (typeof import("vue"))["getCurrentScope"];
+  const h: (typeof import("vue"))["h"];
+  const inject: (typeof import("vue"))["inject"];
+  const isProxy: (typeof import("vue"))["isProxy"];
+  const isReactive: (typeof import("vue"))["isReactive"];
+  const isReadonly: (typeof import("vue"))["isReadonly"];
+  const isRef: (typeof import("vue"))["isRef"];
+  const mapActions: (typeof import("pinia"))["mapActions"];
+  const mapGetters: (typeof import("pinia"))["mapGetters"];
+  const mapState: (typeof import("pinia"))["mapState"];
+  const mapStores: (typeof import("pinia"))["mapStores"];
+  const mapWritableState: (typeof import("pinia"))["mapWritableState"];
+  const markRaw: (typeof import("vue"))["markRaw"];
+  const nextTick: (typeof import("vue"))["nextTick"];
+  const onActivated: (typeof import("vue"))["onActivated"];
+  const onBeforeMount: (typeof import("vue"))["onBeforeMount"];
+  const onBeforeRouteLeave: (typeof import("vue-router"))["onBeforeRouteLeave"];
+  const onBeforeRouteUpdate: (typeof import("vue-router"))["onBeforeRouteUpdate"];
+  const onBeforeUnmount: (typeof import("vue"))["onBeforeUnmount"];
+  const onBeforeUpdate: (typeof import("vue"))["onBeforeUpdate"];
+  const onDeactivated: (typeof import("vue"))["onDeactivated"];
+  const onErrorCaptured: (typeof import("vue"))["onErrorCaptured"];
+  const onMounted: (typeof import("vue"))["onMounted"];
+  const onRenderTracked: (typeof import("vue"))["onRenderTracked"];
+  const onRenderTriggered: (typeof import("vue"))["onRenderTriggered"];
+  const onScopeDispose: (typeof import("vue"))["onScopeDispose"];
+  const onServerPrefetch: (typeof import("vue"))["onServerPrefetch"];
+  const onUnmounted: (typeof import("vue"))["onUnmounted"];
+  const onUpdated: (typeof import("vue"))["onUpdated"];
+  const onWatcherCleanup: (typeof import("vue"))["onWatcherCleanup"];
+  const provide: (typeof import("vue"))["provide"];
+  const reactive: (typeof import("vue"))["reactive"];
+  const readonly: (typeof import("vue"))["readonly"];
+  const ref: (typeof import("vue"))["ref"];
+  const resolveComponent: (typeof import("vue"))["resolveComponent"];
+  const setActivePinia: (typeof import("pinia"))["setActivePinia"];
+  const setMapStoreSuffix: (typeof import("pinia"))["setMapStoreSuffix"];
+  const shallowReactive: (typeof import("vue"))["shallowReactive"];
+  const shallowReadonly: (typeof import("vue"))["shallowReadonly"];
+  const shallowRef: (typeof import("vue"))["shallowRef"];
+  const storeToRefs: (typeof import("pinia"))["storeToRefs"];
+  const toRaw: (typeof import("vue"))["toRaw"];
+  const toRef: (typeof import("vue"))["toRef"];
+  const toRefs: (typeof import("vue"))["toRefs"];
+  const toValue: (typeof import("vue"))["toValue"];
+  const triggerRef: (typeof import("vue"))["triggerRef"];
+  const unref: (typeof import("vue"))["unref"];
+  const useAttrs: (typeof import("vue"))["useAttrs"];
+  const useCssModule: (typeof import("vue"))["useCssModule"];
+  const useCssVars: (typeof import("vue"))["useCssVars"];
+  const useId: (typeof import("vue"))["useId"];
+  const useLink: (typeof import("vue-router"))["useLink"];
+  const useModel: (typeof import("vue"))["useModel"];
+  const useRoute: (typeof import("vue-router"))["useRoute"];
+  const useRouter: (typeof import("vue-router"))["useRouter"];
+  const useSlots: (typeof import("vue"))["useSlots"];
+  const useTemplateRef: (typeof import("vue"))["useTemplateRef"];
+  const watch: (typeof import("vue"))["watch"];
+  const watchEffect: (typeof import("vue"))["watchEffect"];
+  const watchPostEffect: (typeof import("vue"))["watchPostEffect"];
+  const watchSyncEffect: (typeof import("vue"))["watchSyncEffect"];
+}
+// for type re-export
+declare global {
+  // @ts-ignore
+  export type {
+    Component,
+    ComponentPublicInstance,
+    ComputedRef,
+    DirectiveBinding,
+    ExtractDefaultPropTypes,
+    ExtractPropTypes,
+    ExtractPublicPropTypes,
+    InjectionKey,
+    PropType,
+    Ref,
+    MaybeRef,
+    MaybeRefOrGetter,
+    VNode,
+    WritableComputedRef,
+  } from "vue";
+  import("vue");
+}
diff --git a/EdgeCraftRAG/ui/vue/components.d.ts b/EdgeCraftRAG/ui/vue/components.d.ts
new file mode 100644
index 0000000000..64ca06335b
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/components.d.ts
@@ -0,0 +1,52 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+/* eslint-disable */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+export {};
+
+/* prettier-ignore */
+declare module 'vue' {
+  export interface GlobalComponents {
+    AAffix: typeof import('ant-design-vue/es')['Affix']
+    AButton: typeof import('ant-design-vue/es')['Button']
+    ACol: typeof import('ant-design-vue/es')['Col']
+    ACollapse: typeof import('ant-design-vue/es')['Collapse']
+    ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
+    AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
+    ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
+    ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem']
+    ADivider: typeof import('ant-design-vue/es')['Divider']
+    ADrawer: typeof import('ant-design-vue/es')['Drawer']
+    AEmpty: typeof import('ant-design-vue/es')['Empty']
+    AForm: typeof import('ant-design-vue/es')['Form']
+    AFormItem: typeof import('ant-design-vue/es')['FormItem']
+    AImage: typeof import('ant-design-vue/es')['Image']
+    AInput: typeof import('ant-design-vue/es')['Input']
+    AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
+    ALayout: typeof import('ant-design-vue/es')['Layout']
+    ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
+    ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']
+    AModal: typeof import('ant-design-vue/es')['Modal']
+    APagination: typeof import('ant-design-vue/es')['Pagination']
+    APopover: typeof import('ant-design-vue/es')['Popover']
+    ARadio: typeof import('ant-design-vue/es')['Radio']
+    ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
+    ARow: typeof import('ant-design-vue/es')['Row']
+    ASelect: typeof import('ant-design-vue/es')['Select']
+    ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
+    ASlider: typeof import('ant-design-vue/es')['Slider']
+    ASpace: typeof import('ant-design-vue/es')['Space']
+    ASteps: typeof import('ant-design-vue/es')['Steps']
+    ATable: typeof import('ant-design-vue/es')['Table']
+    ATag: typeof import('ant-design-vue/es')['Tag']
+    ATooltip: typeof import('ant-design-vue/es')['Tooltip']
+    AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
+    FormTooltip: typeof import('./src/components/FormTooltip.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    SvgIcon: typeof import('./src/components/SvgIcon.vue')['default']
+  }
+}
diff --git a/EdgeCraftRAG/ui/vue/index.html b/EdgeCraftRAG/ui/vue/index.html
new file mode 100644
index 0000000000..df137679ef
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/index.html
@@ -0,0 +1,20 @@
+<!--
+  Copyright (C) 2025 Intel Corporation
+  SPDX-License-Identifier: Apache-2.0
+-->
+
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Edge Craft RAG based Q&A Chatbot</title>
+  </head>
+
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>
diff --git a/EdgeCraftRAG/ui/vue/nginx.conf b/EdgeCraftRAG/ui/vue/nginx.conf
new file mode 100644
index 0000000000..e71fbbc109
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/nginx.conf
@@ -0,0 +1,56 @@
+
+worker_processes  1;
+
+events {
+    worker_connections  1024;
+}
+
+http {
+    include /etc/nginx/mime.types;
+    default_type  application/octet-stream;
+    client_max_body_size 50M;
+
+    sendfile on;
+    keepalive_timeout  65;
+
+    server {
+        listen 8082;
+        server_name _; 
+
+        location / {
+            root /usr/share/nginx/html;
+            index index.html index.htm;
+            add_header Cache-Control "no-cache";
+            try_files $uri $uri/ /index.html;
+        }
+
+        location /v1/ {
+            proxy_pass http://server:16010;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_http_version 1.1;
+            proxy_set_header Connection "";
+        }
+
+        location /home/user/ {
+            proxy_pass http://server:16010;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_http_version 1.1;
+            proxy_set_header Connection "";
+        }
+        
+        location /v1/chatqna {
+            proxy_pass http://ecrag:16011;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_http_version 1.1;
+            proxy_set_header Connection "";
+            proxy_buffering off;
+            proxy_cache off;
+            proxy_read_timeout 600s;
+            gzip off;
+            proxy_set_header Host ecrag:16011; 
+            proxy_set_header X-Accel-Buffering no;
+            proxy_set_header X-Real-IP $remote_addr;
+            chunked_transfer_encoding on; 
+        }
+    }
+}
diff --git a/EdgeCraftRAG/ui/vue/package.json b/EdgeCraftRAG/ui/vue/package.json
new file mode 100644
index 0000000000..0c3e32bc11
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/package.json
@@ -0,0 +1,51 @@
+{
+  "name": "ec-rag-ui",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@vueuse/i18n": "^4.0.0-beta.12",
+    "ant-design-vue": "^4.0.0-rc.6",
+    "axios": "^1.7.9",
+    "echarts": "^5.5.1",
+    "event-source-polyfill": "^1.0.31",
+    "http": "^0.0.1-security",
+    "js-cookie": "^3.0.5",
+    "lodash": "^4.17.21",
+    "marked": "^15.0.6",
+    "pinia": "^2.3.0",
+    "pinia-plugin-persistedstate": "^4.2.0",
+    "qs": "^6.13.1",
+    "socket.io-client": "^4.8.1",
+    "vite-plugin-compression": "^0.5.1",
+    "vue": "^3.5.13",
+    "vue-echarts": "^7.0.3",
+    "vue-i18n": "^10.0.5",
+    "vue-json-pretty": "^2.4.0",
+    "vue-router": "^4.5.0",
+    "ws": "^8.18.0"
+  },
+  "devDependencies": {
+    "@types/eventsource": "^1.1.15",
+    "@types/js-cookie": "^3.0.6",
+    "@types/node": "^22.10.2",
+    "@vitejs/plugin-vue": "^5.2.1",
+    "less": "^4.2.1",
+    "less-loader": "^12.2.0",
+    "typescript": "~5.6.2",
+    "unplugin-auto-import": "^0.19.0",
+    "unplugin-vue-components": "^0.27.5",
+    "vite": "^6.0.1",
+    "vue-tsc": "^2.1.10"
+  },
+  "description": "This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.",
+  "main": "index.js",
+  "keywords": [],
+  "author": "",
+  "license": "ISC"
+}
diff --git a/EdgeCraftRAG/ui/vue/public/favicon.ico b/EdgeCraftRAG/ui/vue/public/favicon.ico
new file mode 100644
index 0000000000..2d62a78f50
Binary files /dev/null and b/EdgeCraftRAG/ui/vue/public/favicon.ico differ
diff --git a/EdgeCraftRAG/ui/vue/public/favicon1.ico b/EdgeCraftRAG/ui/vue/public/favicon1.ico
new file mode 100644
index 0000000000..100180c12d
Binary files /dev/null and b/EdgeCraftRAG/ui/vue/public/favicon1.ico differ
diff --git a/EdgeCraftRAG/ui/vue/src/App.vue b/EdgeCraftRAG/ui/vue/src/App.vue
new file mode 100644
index 0000000000..c55f04225a
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/App.vue
@@ -0,0 +1,28 @@
+<template>
+  <a-config-provider
+    :locale="getGlobalI18n"
+    :theme="currentTheme"
+    prefixCls="intel"
+    autoInsertSpaceInButton
+  >
+    <router-view />
+  </a-config-provider>
+</template>
+<script setup lang="ts">
+import { themeAppStore } from "@/store/theme";
+import { antTheme } from "@/utils/antTheme";
+import { computed } from "vue";
+import { useI18n } from "vue-i18n";
+
+const themeStore = themeAppStore();
+
+const currentTheme = computed(() => {
+  return antTheme[themeStore.theme as keyof typeof antTheme];
+});
+
+const { messages, locale } = useI18n();
+const getGlobalI18n = computed(() => {
+  return messages.value[locale.value];
+});
+</script>
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/api/chatbot/index.ts b/EdgeCraftRAG/ui/vue/src/api/chatbot/index.ts
new file mode 100644
index 0000000000..17bd6c27e7
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/api/chatbot/index.ts
@@ -0,0 +1,52 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import request from "../request";
+
+export const getFilleList = () => {
+  return request({
+    url: "/v1/data/files",
+    method: "get",
+  });
+};
+
+export const requestChatbotConfig = (data: Object) => {
+  return request({
+    url: "/v1/settings/pipelines",
+    method: "post",
+    data,
+    showLoading: true,
+    showSuccessMsg: true,
+    successMsg: "Configuration update successful !",
+  });
+};
+
+export const requestFileDelete = (name: String) => {
+  return request({
+    url: `/v1/data/files/${name}`,
+    method: "delete",
+    showLoading: true,
+    showSuccessMsg: true,
+    successMsg: "File deleted successfully !",
+  });
+};
+
+export const getBenchmark = (name: String) => {
+  return request({
+    url: `/v1/settings/pipelines/${name}/benchmark`,
+    method: "get",
+  });
+};
+
+export const requestParsingFiles = (data: Object) => {
+  return request({
+    url: `/v1/data`,
+    method: "post",
+    data,
+    showLoading: true,
+    showSuccessMsg: true,
+    successMsg: "Document uploaded and parsed successfully !",
+  });
+};
+
+export const uploadFileUrl = `${import.meta.env.VITE_API_URL}v1/data/file`;
diff --git a/EdgeCraftRAG/ui/vue/src/api/pipeline/index.ts b/EdgeCraftRAG/ui/vue/src/api/pipeline/index.ts
new file mode 100644
index 0000000000..959caa5676
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/api/pipeline/index.ts
@@ -0,0 +1,92 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import request from "../request";
+
+export const getSystemStatus = () => {
+  return request({
+    url: "/v1/system/info",
+    method: "get",
+  });
+};
+
+export const getPipelineList = () => {
+  return request({
+    url: "/v1/settings/pipelines",
+    method: "get",
+    showLoading: true,
+  });
+};
+
+export const getPipelineDetialByName = (name: String) => {
+  return request({
+    url: `/v1/settings/pipelines/${name}/json`,
+    method: "get",
+  });
+};
+
+export const requestPipelineCreate = (data: Object) => {
+  return request({
+    url: "/v1/settings/pipelines",
+    method: "post",
+    data,
+    showLoading: true,
+    showSuccessMsg: true,
+    successMsg: "Pipeline created successfully !",
+  });
+};
+
+export const requestPipelineUpdate = (name: String, data: Object) => {
+  return request({
+    url: `/v1/settings/pipelines/${name}`,
+    method: "patch",
+    data,
+    showLoading: true,
+    showSuccessMsg: true,
+    successMsg: "Pipeline update successfully !",
+  });
+};
+
+export const requestPipelineDelete = (name: String) => {
+  return request({
+    url: `/v1/settings/pipelines/${name}`,
+    method: "delete",
+    showLoading: true,
+    showSuccessMsg: true,
+    successMsg: "Pipeline deleted successfully !",
+  });
+};
+
+export const requestPipelineSwitchState = (name: String, data: Object) => {
+  return request({
+    url: `/v1/settings/pipelines/${name}`,
+    method: "patch",
+    data,
+    showLoading: true,
+    showSuccessMsg: true,
+    successMsg: "Pipeline state switch successful !",
+  });
+};
+
+export const getRunDevice = () => {
+  return request({
+    url: "/v1/system/device",
+    method: "get",
+  });
+};
+
+export const getModelList = (type: string) => {
+  return request({
+    url: `/v1/settings/avail-models/${type}`,
+    method: "get",
+  });
+};
+
+export const getModelWeight = (model_id: string) => {
+  return request({
+    url: `/v1/settings/weight/${model_id}`,
+    method: "get",
+  });
+};
+
+export const importUrl = `${import.meta.env.VITE_API_URL}v1/settings/pipelines/import`;
diff --git a/EdgeCraftRAG/ui/vue/src/api/request.ts b/EdgeCraftRAG/ui/vue/src/api/request.ts
new file mode 100644
index 0000000000..95382588fe
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/api/request.ts
@@ -0,0 +1,62 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { NextLoading } from "@/utils/loading";
+import serviceManager from "@/utils/serviceManager";
+import axios, { AxiosInstance } from "axios";
+import qs from "qs";
+
+const antNotification = serviceManager.getService("antNotification");
+
+const service: AxiosInstance = axios.create({
+  baseURL: import.meta.env.VITE_API_URL,
+  timeout: 50000,
+  headers: { "Content-Type": "application/json" },
+});
+
+// request interceptor
+service.interceptors.request.use(
+  (config) => {
+    if (config.type === "formData") {
+      config.data = qs.stringify(config.data);
+    } else if (config.type === "files") {
+      config.headers!["Content-Type"] = "multipart/form-data";
+    }
+
+    if (config.showLoading) NextLoading.start();
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
+  },
+);
+
+// response interceptor
+service.interceptors.response.use(
+  (response) => {
+    const { config } = response;
+    if (NextLoading) NextLoading.done();
+    const res = response.data;
+    if (config.showSuccessMsg) {
+      if (antNotification) antNotification("success", "Success", config.successMsg);
+    }
+    return Promise.resolve(res);
+  },
+  (error) => {
+    if (NextLoading) NextLoading.done();
+    let errorMessage = "";
+    const { detail = "" } = error.response?.data || {};
+    if (error.message.indexOf("timeout") != -1) {
+      errorMessage = "Timeout";
+    } else if (detail) {
+      errorMessage = detail;
+    } else {
+      errorMessage = error.message;
+    }
+    if (antNotification) antNotification("error", "Error", errorMessage);
+
+    return Promise.reject(error);
+  },
+);
+
+export default service;
diff --git a/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.css b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.css
new file mode 100644
index 0000000000..0fd282ff5e
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.css
@@ -0,0 +1,99 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 4784207 */
+  src:
+    url("iconfont.woff2?t=1739238081968") format("woff2"),
+    url("iconfont.woff?t=1739238081968") format("woff"),
+    url("iconfont.ttf?t=1739238081968") format("truetype");
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-stop:before {
+  content: "\e904";
+}
+
+.icon-time-expand:before {
+  content: "\e616";
+}
+
+.icon-time-retract:before {
+  content: "\e615";
+}
+
+.icon-search-time:before {
+  content: "\ea85";
+}
+
+.icon-handle-time:before {
+  content: "\e7a2";
+}
+
+.icon-generation:before {
+  content: "\e6b9";
+}
+
+.icon-generator:before {
+  content: "\e67e";
+}
+
+.icon-user:before {
+  content: "\e730";
+}
+
+.icon-send:before {
+  content: "\e7db";
+}
+
+.icon-uploaded:before {
+  content: "\e614";
+}
+
+.icon-notice-board:before {
+  content: "\e674";
+}
+
+.icon-setting:before {
+  content: "\e64c";
+}
+
+.icon-chatbot:before {
+  content: "\e669";
+}
+
+.icon-cloudupload-fill:before {
+  content: "\e7da";
+}
+
+.icon-basic:before {
+  content: "\e613";
+}
+
+.icon-indexer:before {
+  content: "\e612";
+}
+
+.icon-retriever:before {
+  content: "\e694";
+}
+
+.icon-node-parser:before {
+  content: "\e65d";
+}
+
+.icon-generator1:before {
+  content: "\e656";
+}
+
+.icon-post-processor:before {
+  content: "\e718";
+}
+
+.icon-active:before {
+  content: "\e795";
+}
diff --git a/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.js b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.js
new file mode 100644
index 0000000000..5c567a1196
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.js
@@ -0,0 +1,68 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+(window._iconfont_svg_string_4784207 =
+  '<svg><symbol id="icon-stop" viewBox="0 0 1024 1024"><path d="M512.08028937 991.94294281c-261.60871406-1.17313312-479.27825625-214.79003344-480.02479499-479.91814687-0.74653969-265.07479031 214.89668156-480.02479594 479.97147093-480.02479594a480.02479594 480.02479594 0 0 1 479.97147188 480.02479594c0 265.12811437-242.94523125 480.98463187-479.91814782 479.91814687z m164.98519501-599.89768406a45.00565781 45.00565781 0 0 0-45.00565782-45.00565687h-239.95907343a45.05898188 45.05898188 0 0 0-45.00565782 45.00565687v239.95907344c0 24.84909563 20.20988625 45.00565781 45.00565781 45.00565781h239.95907344a45.00565781 45.00565781 0 0 0 45.00565781-45.00565781v-239.95907344z" fill="#666666" ></path></symbol><symbol id="icon-time-expand" viewBox="0 0 1024 1024"><path d="M851.36 172.64L915.2 108.8l-63.78 63.78L915.2 108.8q22.5-20.58 48.72-9.36 26.28 9.36 28.14 41.28V347q-3.78 41.28-45 45h-206.28q-31.8-1.86-41.22-28.14-11.28-26.22 9.36-48.72l58.14-58.14Q665.72 155.72 512 152q-153.78 3.78-255 105Q155.72 358.28 152 512q3.78 153.78 105 255Q358.28 868.28 512 872q116.28-1.86 206.28-63.78 20.58-15 45-11.22 22.44 5.64 37.44 26.28 15 20.58 11.28 43.08-5.64 24.36-26.28 39.36Q667.7 990.2 512 992q-135-1.86-241.86-65.64-108.78-63.72-172.5-172.5Q33.8 647 32 512q1.86-135 65.64-241.86 63.72-108.78 172.5-172.5Q377 33.8 512 32q99.36 0 187.5 37.5 86.28 37.5 151.86 103.14zM512 272q41.28 3.78 45 45V512q0 18.78-13.14 31.86l-135 135q-31.86 26.28-63.72 0-26.28-31.86 0-63.72L467 493.28V317Q470.78 275.72 512 272z" fill="#6B7280" ></path></symbol><symbol id="icon-time-retract" viewBox="0 0 1024 1024"><path d="M169.646195 189.84683188L107.1651575 127.42451656l62.42231531 62.42231532L107.1651575 127.42451656Q85.20283812 107.1651575 59.59970656 118.26376344 33.76168344 127.42451656 32 158.60631313v201.88891593q3.69953531 40.34254969 44.042085 44.04208407h201.88891594q31.12307344-1.82040656 40.34254968-27.59970563 11.03988281-25.60313156-9.16075406-47.624175l-56.90237344-56.96109562Q351.334475 173.34573031 501.78223625 169.646195q150.50648437 3.69953531 249.57181312 102.76486406 99.12405187 99.12405187 102.76486407 249.57181313-3.69953531 150.50648437-102.76486407 249.57181312-99.12405187 99.12405187-249.57181312 102.76486406-113.80474687-1.76168344-201.88891594-62.36359125-20.14191375-14.680695-44.042085-10.98115968-21.96231937 5.51994094-36.64301437 25.66185468-14.680695 20.20063594-11.03988188 42.22167844 5.51994094 23.84144812 25.72057688 38.52214313Q349.39662312 989.88597969 501.78223625 991.76510844q132.12625406-1.87912875 236.71152469-64.24272 106.46439937-62.42231438 168.82799062-168.82799156Q969.80278906 654.10912625 971.5644725 521.98287219q-1.82040656-132.12625406-64.24272094-236.71152375-62.36359219-106.46439937-168.82799062-168.82799156Q633.90849031 54.0210425 501.78223625 52.20063594q-97.24492312 0-183.50868562 36.7017375-84.44335687 36.7017375-148.62735563 100.94445844zM501.78223625 287.09175406q-40.40127187 3.64081219-44.042085 44.042085v190.84903313q0 18.32150719 12.86028937 31.18179656l132.12625407 132.12625406q31.18179562 25.72057781 62.36359125 0 25.72057781-31.18179562 0-62.42231531L545.82432125 503.72008813V331.07511594q-3.69953531-40.40127187-44.042085-44.04208407z" fill="#6B7280" ></path></symbol><symbol id="icon-search-time" viewBox="0 0 1024 1024"><path d="M640.96 321.92l-182.08 181.44-59.2-59.2c-13.76-15.04-37.44-16-52.48-2.24s-16 37.44-2.24 52.48c0.64 0.96 1.28 1.6 2.24 2.24l86.08 85.12c7.04 7.04 16.32 10.88 26.24 10.88 9.92-0.32 19.2-4.16 26.56-10.88l208-207.36c13.44-15.68 11.84-39.36-3.84-52.8a39.04 39.04 0 0 0-49.28 0.32z"  ></path><path d="M971.2 942.08l-196.8-196.8c4.8-4.48 9.92-8.64 14.72-13.44 80.32-80.32 125.44-189.44 125.44-303.04 0-236.8-192-428.8-428.8-428.8S56.96 192 56.96 428.8s192 428.8 428.8 428.8c74.56 0.32 146.88-19.52 210.56-55.36l207.68 207.68c8.96 8.64 20.8 14.08 34.24 14.08 26.24 0 47.68-21.44 47.68-47.68 0-13.44-5.76-25.6-14.72-34.24z m-485.12-179.84c-184 0-333.44-149.76-333.44-333.44S302.08 95.36 486.08 95.36 819.52 244.8 819.52 428.8s-149.76 333.44-333.44 333.44z"  ></path></symbol><symbol id="icon-handle-time" viewBox="0 0 1024 1024"><path d="M858.112 877.568h-38.4c0-153.6-92.16-241.152-153.6-307.2-23.04-22.016-53.76-44.032-53.76-58.368 0-7.168 23.04-22.016 38.4-29.184 61.44-44.032 168.96-124.416 168.96-336.384h38.4c23.04 0 38.4-14.848 38.4-36.352s-15.872-36.352-38.4-36.352h-691.2c-23.04 0-38.4 14.848-38.4 36.352s15.36 36.352 38.4 36.352H204.8C204.8 358.4 312.32 438.784 373.76 482.816c15.872 7.168 38.4 22.016 38.4 29.184 0 14.848-30.72 36.352-53.76 66.048-61.44 58.368-153.6 146.432-153.6 300.032h-38.4c-23.04 0-38.4 14.848-38.4 36.352 0 22.016 15.36 36.352 38.4 36.352h691.2c23.04 0 38.4-14.848 38.4-36.352 0.512-22.016-14.848-36.864-37.888-36.864z m-186.88-48.64H348.16v-45.056c0-25.6 12.8-51.2 33.28-64l100.864-82.944c19.968-12.8 47.104-12.8 59.904 0l93.696 82.944c20.48 18.944 33.792 44.544 33.792 64v45.056h1.536z"  ></path></symbol><symbol id="icon-generation" viewBox="0 0 1024 1024"><path d="M736.37789287 63.83251348h-26.82651283c-21.88478675 0-39.53380832 17.76668319-39.53380832 39.53380841v78.24399411c0 21.88478675 17.76668319 39.53380832 39.53380832 39.53380837h26.82651283c21.88478675 0 39.53380832-17.76668319 39.53380831-39.53380837v-78.24399411c0.11766159-21.76712511-17.64902155-39.53380832-39.53380831-39.53380841zM315.50756123 63.83251348h-26.82651289c-21.88478675 0-39.53380832 17.76668319-39.53380829 39.53380841v78.24399411c0 21.88478675 17.76668319 39.53380832 39.53380829 39.53380837h26.82651289c21.88478675 0 39.53380832-17.76668319 39.53380834-39.53380837v-78.24399411c0-21.76712511-17.64902155-39.53380832-39.53380834-39.53380841z"  ></path><path d="M882.04115391 169.96196174c-28.35609614 0-51.41748141 23.06138967-51.41748575 51.41748143v14.00156007c0 22.47308593-18.2373208 40.5927496-40.5927497 40.59274965h-130.83807841c-22.47308593 0-40.5927496-18.2373208-40.59274972-40.59274965v-24.82629185c0-22.47308593-18.2373208-40.5927496-40.59274963-40.59274965h-130.83807855c-22.47308593 0-40.5927496 18.2373208-40.59274963 40.59274965v24.82629185c0 22.47308593-18.2373208 40.5927496-40.59274959 40.59274965h-130.83808294c-22.47308593 0-40.5927496-18.2373208-40.59274966-40.59274965V221.37944317c0-28.35609614-23.06138967-51.41748141-51.41748132-51.41748143-30.0033367 0-54.59430532 24.59096853-54.59430532 54.59430531v684.31139968c0 28.35609614 23.06138967 51.41748141 51.41748143 51.4174814h742.20018603c28.35609614 0 51.41748141-23.06138967 51.41748575-51.4174814v-687.60588077c3.05916228-28.82673377-22.59074761-51.29982423-51.53514299-51.29982422z m-82.00912175 325.91860006l-302.85721027 302.85721027-6.70662822 6.70662825c-18.82562451 18.82562451-49.53492204 18.82562451-68.24288492 0l-3.41214268-3.4121471-161.54737613-161.5473761c-18.82562451-18.82562451-18.82562451-49.53492204 0-68.24288492l6.70662829-6.70662818c18.82562451-18.82562451 49.53492204-18.82562451 68.24288051 0l124.01379307 124.01379311 268.73577008-268.7357701c20.59052665-20.59052665 54.35898642-20.59052665 74.9495087 0 20.70818385 20.70818385 20.70818385 54.47664804 0.11766157 75.06717477z"  ></path></symbol><symbol id="icon-generator" viewBox="0 0 1024 1024"><path d="M960 128v128h64V128c0-70.4-57.6-128-128-128h-128v64h128c32 0 64 32 64 64zM64 896v-128H0v128c0 70.4 57.6 128 128 128h128v-64H128c-32 0-64-32-64-64z m896 0c0 25.6-25.6 64-64 64h-128v64h128c70.4 0 128-57.6 128-134.4V768h-64v128zM64 128c0-32 32-64 64-64h128V0H128C57.6 6.4 0 64 0 128v128h64V128z m755.2 0H204.8C166.4 128 128 166.4 128 204.8v608c0 51.2 38.4 83.2 76.8 83.2h608c44.8 0 76.8-32 76.8-76.8V204.8c6.4-38.4-25.6-76.8-70.4-76.8zM576 704L512 576H384l-64 128H256l128-384h128l128 384H576z m192 0h-64V320h64v384zM512 512L448 320 384 512h128z" fill="#999999" ></path></symbol><symbol id="icon-user" viewBox="0 0 1024 1024"><path d="M856.339597 799.428571q0 62.285714-35.714286 106.857143t-86 44.571429H246.625311q-50.285714 0-86-44.571429T124.911026 799.428571q0-48.571429 4.857143-91.714285t18-86.857143 33.428571-74.857143 53.714286-50.857143T311.768169 475.428571q74.857143 73.142857 178.857142 73.142858t178.857143-73.142858q43.428571 0 76.857143 19.714286t53.714286 50.857143 33.428571 74.857143 18 86.857143 4.857143 91.714285z m-146.285714-506.857142q0 90.857143-64.285714 155.142857T490.625311 512 335.482454 447.714286 271.19674 292.571429t64.285714-155.142858T490.625311 73.142857t155.142858 64.285714T710.053883 292.571429z"  ></path></symbol><symbol id="icon-send" viewBox="0 0 1024 1024"><path d="M893.18 191.719c20.24-7.106 41.16 8.769 39.753 30.164l-38.276 581.744c-1.35 20.511-22.53 33.572-41.477 25.576L536.091 695.376 431.374 859.151c-15.935 24.922-54.329 14.003-55.095-15.206l-0.012-0.89V630.433L109.654 522.213c-25.266-10.256-24.662-46.049 0.57-55.616l0.772-0.282zM664.153 453.57L436.08 633.345l-0.001 107.426 58.26-91.114a29.816 29.816 0 0 1 7.794-8.22 30.49 30.49 0 0 1 1.665-2.109l160.356-185.757z" fill="#333333" ></path></symbol><symbol id="icon-uploaded" viewBox="0 0 1024 1024"><path d="M692.312 956.978c-59.656 0-112.963-25.778-148.221-66.23H214.286V133.034h400.115v187.818h197.413v310.98c42.357 31.932 70.176 80.457 72.544 135.214v-510.52L674.346 64.223H141.742v895.335h742.615V782.244c-4.202 97.174-88.568 174.734-192.045 174.734z" fill="#666666" ></path><path d="M301.326 418.477h437.601v67.091H301.326zM301.326 578.493h219.957v67.091H301.326zM301.326 738.509h138.34V805.6h-138.34z" fill="#666666" ></path><path d="M811.814 631.832c-32.811-24.735-74.334-39.519-119.502-39.519-106.162 0-192.224 81.633-192.224 182.333 0 44.113 16.517 84.567 44.003 116.103 35.258 40.452 88.565 66.23 148.221 66.23 103.477 0 187.843-77.56 192.045-174.734 0.109-2.521 0.179-5.052 0.179-7.599s-0.07-5.078-0.179-7.599c-2.368-54.759-30.186-103.283-72.543-135.215zM660.869 888.809l-38.472-36.492 0.92-0.872-69.159-65.6 38.472-36.492 69.159 65.6 130.205-123.505 38.472 36.492-169.597 160.869z" fill="#50A135" ></path><path d="M791.994 691.447L661.789 814.952l-69.159-65.6-38.472 36.492 69.159 65.6-0.92 0.873 38.472 36.492L830.466 727.94z" fill="#FFFFFF" ></path></symbol><symbol id="icon-notice-board" viewBox="0 0 1024 1024"><path d="M464 736H192a80 80 0 0 1-80-80v-448A80 80 0 0 1 192 128h640a80 80 0 0 1 80 80v448A80 80 0 0 1 832 736h-272v64H768a32 32 0 0 1 32 32v32a32 32 0 0 1-32 32H256a32 32 0 0 1-32-32v-32a32 32 0 0 1 32-32h208v-64z m352-512h-608v416h608V224z m-448 96a32 32 0 0 1 32 32v208a32 32 0 0 1-32 32h-32a32 32 0 0 1-32-32V352a32 32 0 0 1 32-32h32z m320 48a32 32 0 0 1 32 32v160a32 32 0 0 1-32 32h-32a32 32 0 0 1-32-32v-160a32 32 0 0 1 32-32h32z m-160-96a32 32 0 0 1 32 32v256a32 32 0 0 1-32 32h-32a32 32 0 0 1-32-32v-256a32 32 0 0 1 32-32h32z" fill="#333333" ></path></symbol><symbol id="icon-setting" viewBox="0 0 1024 1024"><path d="M950.857143 402.285714h-51.748572a403.2 403.2 0 0 0-36.571428-86.308571l36.571428-36.571429a73.142857 73.142857 0 0 0 0-103.314285l-51.017142-51.931429a73.142857 73.142857 0 0 0-103.314286 0l-36.571429 36.571429A393.691429 393.691429 0 0 0 621.714286 125.074286V73.142857a73.142857 73.142857 0 0 0-73.142857-73.142857h-73.142858a73.142857 73.142857 0 0 0-73.142857 73.142857v51.931429a393.691429 393.691429 0 0 0-86.308571 35.657143l-36.571429-36.571429a73.142857 73.142857 0 0 0-103.314285 0L124.16 175.908571a73.142857 73.142857 0 0 0 0 103.314286l36.571429 36.571429a403.2 403.2 0 0 0-36.571429 86.308571H73.142857a73.142857 73.142857 0 0 0-73.142857 73.142857v73.142857a73.142857 73.142857 0 0 0 73.142857 73.142858h51.748572a403.2 403.2 0 0 0 36.571428 86.308571l-36.571428 36.571429a73.142857 73.142857 0 0 0 0 103.314285l51.748571 51.748572a73.142857 73.142857 0 0 0 103.314286 0l36.571428-36.571429A393.691429 393.691429 0 0 0 402.285714 898.925714V950.857143a73.142857 73.142857 0 0 0 73.142857 73.142857h73.142858a73.142857 73.142857 0 0 0 73.142857-73.142857v-51.931429a393.691429 393.691429 0 0 0 86.308571-35.657143l36.571429 36.571429a73.142857 73.142857 0 0 0 103.314285 0l51.748572-51.748571a73.142857 73.142857 0 0 0 0-103.314286l-36.571429-36.571429a403.2 403.2 0 0 0 36.571429-86.308571H950.857143a73.142857 73.142857 0 0 0 73.142857-73.142857V475.428571a73.142857 73.142857 0 0 0-73.142857-73.142857zM617.142857 613.668571a146.285714 146.285714 0 1 1-3.474286-206.811428 146.285714 146.285714 0 0 1 3.474286 206.811428z" fill="#131313" ></path></symbol><symbol id="icon-chatbot" viewBox="0 0 1024 1024"><path d="M282.4192 271.2064H742.4a79.9744 79.9744 0 0 1 79.9744 79.9744v400.0256a79.9744 79.9744 0 0 1-79.9744 79.9744H282.368a79.9744 79.9744 0 0 1-79.9744-79.9744V351.1808a79.9744 79.9744 0 0 1 80.0256-79.9744z m404.6336 445.952a24.0128 24.0128 0 0 0-16.9472-40.96H354.0992a24.0128 24.0128 0 0 0 0 48.0256h316.0064c6.3488 0 12.4928-2.56 16.9472-7.0656zM372.0192 604.672h280.0128a109.9776 109.9776 0 0 0 0-220.0064h-280.064a109.9776 109.9776 0 1 0 0 220.0064z m19.968-50.0224a60.0064 60.0064 0 1 0 0-120.0128 60.0064 60.0064 0 0 0 0 120.0128z m240.0256 0a60.0064 60.0064 0 1 0 0-120.0128 60.0064 60.0064 0 0 0 0 120.0128z" fill="#131313" ></path><path d="M953.344 160.6656a49.9712 49.9712 0 0 0-43.008 75.4688l-64 107.6736v333.4144h55.9616a24.0128 24.0128 0 0 0 24.0128-24.064v-192a24.0128 24.0128 0 0 0-24.064-23.9616h-55.552l104.8576-176.5376h1.7408a49.9712 49.9712 0 1 0 0-99.9936zM113.9712 236.1344l64 107.6736v333.3632H122.0096a24.0128 24.0128 0 0 1-24.0128-23.9616v-192a24.0128 24.0128 0 0 1 24.0128-24.064H177.664L72.704 260.7104h-1.7408a50.0224 50.0224 0 1 1 43.008-24.576z" fill="#131313" fill-opacity=".64" ></path></symbol><symbol id="icon-cloudupload-fill" viewBox="0 0 1024 1024"><path d="M1024 640.192C1024 782.912 919.872 896 787.648 896h-512C123.904 896 0 761.6 0 597.504 0 451.968 94.656 331.52 226.432 302.976 284.16 195.456 391.808 128 512 128c152.32 0 282.112 108.416 323.392 261.12C941.888 413.44 1024 519.04 1024 640.192z m-341.312-139.84L512 314.24 341.312 500.48h341.376z m-213.376 0v256h85.376v-256H469.312z" fill="#00377C" ></path></symbol><symbol id="icon-basic" viewBox="0 0 1024 1024"><path d="M116.4921875 776.19921875q0 21.60461426 13.89221192 35.54626465 13.89221191 13.84277344 35.54626464 13.84277344h84.98474122q13.89221191 34.01367188 44.79125975 54.13513183 29.36645508 20.07202148 67.97790528 20.07202148t67.97790528-20.07202148q30.89904786-20.12145997 44.79125976-54.08569336H858.06933594q21.65405273 0 35.54626465-13.89221192 13.89221191-13.94165039 13.89221191-35.59570311 0-21.60461426-13.89221191-35.49682617-13.89221191-13.89221191-35.54626465-13.89221192H476.45373535Q462.56152345 692.74707031 431.66247558 672.67504883q-29.36645508-20.07202148-67.97790527-20.07202149t-67.97790528 20.07202149q-30.89904786 20.07202148-44.79125975 54.08569336H165.93066406q-21.65405273 0-35.54626465 13.89221192-13.89221191 13.89221191-13.89221191 35.54626464z m197.75390625 0q0-21.65405273 13.89221192-35.54626465 13.94165039-13.89221191 35.54626464-13.89221191 21.65405273 0 35.54626464 13.89221191 13.89221191 13.89221191 13.89221193 35.54626465 0 21.60461426-13.89221193 35.54626465-13.94165039 13.84277344-35.54626464 13.84277344-21.65405273 0-35.54626465-13.84277344Q314.24609375 797.75439453 314.24609375 776.1003418z m296.63085938-247.19238281q0-21.65405273 13.8922119-35.54626467 13.89221191-13.89221191 35.54626466-13.8922119 21.65405273 0 35.54626464 13.8922119 13.89221191 13.89221191 13.89221192 35.54626467 0 21.60461426-13.89221192 35.54626465-13.89221191 13.84277344-35.54626464 13.84277344-21.65405273 0-35.54626466-13.84277344Q610.87695313 550.56201172 610.87695313 528.90795898z m49.43847656-123.59619141q-38.6114502 0-67.97790528 20.07202149-30.89904786 20.07202148-44.79125976 54.08569336H165.93066406q-21.65405273 0-35.54626465 13.89221191Q116.4921875 507.3527832 116.4921875 529.00683594q0 21.60461426 13.89221192 35.54626465 13.89221191 13.84277344 35.54626464 13.84277344h381.61560059q13.89221191 34.01367188 44.79125976 54.13513183 29.36645508 20.07202148 67.97790528 20.07202148t67.97790528-20.07202148q30.89904786-20.12145997 44.79125975-54.08569336H858.06933594q21.65405273 0 35.54626465-13.89221192 13.89221191-13.94165039 13.89221191-35.59570311 0-21.60461426-13.89221191-35.49682618-13.89221191-13.89221191-35.54626465-13.89221191h-84.98474122Q759.19238281 445.5546875 728.29333497 425.48266602q-29.36645508-20.07202148-67.97790528-20.07202149z m-247.19238282-74.15771484q-21.65405273 0-35.54626465-13.89221192Q363.68457031 303.36962891 363.68457031 281.76501463q0-21.60461426 13.89221192-35.49682615 13.94165039-13.89221191 35.54626465-13.89221191 21.65405273 0 35.54626464 13.84277342 13.89221191 13.94165039 13.89221193 35.59570313 0 21.60461426-13.89221193 35.54626466-13.94165039 13.84277344-35.54626464 13.84277344z m112.76916504-98.87695314Q512 198.36230469 481.10095214 178.2902832 451.73449707 158.21826172 413.12304687 158.21826172t-67.97790527 20.07202148Q314.24609375 198.36230469 300.35388183 232.37597657H165.93066406q-21.65405273 0-35.54626465 13.84277344Q116.4921875 260.20983886 116.4921875 281.86389159q0 21.60461426 13.89221192 35.54626466 13.89221191 13.84277344 35.54626464 13.84277344h134.42321777q13.89221191 34.01367188 44.79125977 54.13513184 29.36645508 20.07202148 67.97790528 20.07202148t67.97790527-20.07202148Q512 365.16772461 525.89221191 331.25292969H858.06933594q21.65405273 0 35.54626465-13.89221192 13.89221191-13.94165039 13.89221191-35.59570312 0-21.60461426-13.89221191-35.49682617-13.89221191-13.89221191-35.54626465-13.89221193H525.89221191z" fill="#595959" ></path></symbol><symbol id="icon-indexer" viewBox="0 0 1024 1024"><path d="M328.63338477 905.06522041q-18.38740957 18.33730752-44.0897001 18.33730752t-44.08970098-18.33730752l-161.62883525-176.35880303q-16.53363809-20.19107901-14.67986573-42.23592861 1.80366943-23.84851992 18.33730752-42.23592949 20.24118106-16.53363809 42.23592862-14.6798666 23.89862198 1.85377148 42.28603154 18.33730751l58.7695667 66.13455147V159.34805527q0-25.70229141 16.53363809-42.23592949 16.53363809-16.53363809 42.23592949-16.53363721 25.70229141 0 42.23592861 16.53363721 16.53363809 16.53363809 16.53363809 42.23592949v554.67849639l58.7695667-66.13455146q18.38740957-16.53363809 42.23592949-18.33730752 22.04485049-1.85377148 42.23592949 14.67986659 16.53363809 18.38740957 18.38740869 40.43225919 1.85377148 23.84851992-14.67986572 44.08970097l-161.62883525 176.30870098z m249.75813427-275.56062978q0-25.65218935 16.53363809-42.18582657 16.53363809-16.53363809 42.23592949-16.53363809h235.12836973q38.57848857 1.80366943 55.11212578 36.72471622 12.82609512 34.92104678-12.87619716 64.28077998l-135.92654297 134.07277148h93.69061435q25.70229141 0 42.23592862 16.53363809 16.53363809 16.53363809 16.53363808 42.23592949 0 25.75239346-16.53363808 42.28603067-16.53363809 16.53363809-42.23592862 16.53363808h-235.12836973q-38.57848857-1.85377148-55.11212665-36.77481826-12.82609512-34.87094473 12.87619716-64.28077998l135.92654297-134.07277148h-93.69061348q-25.70229141 0-42.23592949-16.53363809-16.53363809-16.53363809-16.53363809-42.23592949zM754.75032208 100.57848857q34.87094473 1.80366943 53.25835428 33.0672753l117.53913429 235.07826767 29.40983437 58.76956671q9.16865332 22.04485049 1.80366943 44.08970097-7.31488184 22.04485049-29.35973232 34.92104677-22.04485049 9.16865332-44.08970098 1.80367032-22.04485049-7.31488184-34.92104677-29.35973321l-12.82609513-25.7022914h-161.62883437l-12.87619717 25.7022914q-12.82609512 22.04485049-34.87094472 29.35973321-22.04485049 7.36498388-44.08970098-1.80367032-22.04485049-12.87619628-29.40983438-34.92104677-7.31488184-22.04485049 1.85377149-44.08970097l29.35973232-58.76956671 117.58923633-235.07826767q18.33730752-31.26360586 53.2583543-33.0672753z m-36.72471622 264.48810206h73.4494333-73.4494333 73.4494333l-36.72471709-73.49953535-36.72471621 73.49953534z" fill="#595959" ></path></symbol><symbol id="icon-retriever" viewBox="0 0 1024 1024"><path d="M934.966272 879.950848 709.541888 654.526464c47.665152-59.81696 76.166144-135.581696 76.166144-218.012672 0-193.36704-156.752896-350.124032-350.12096-350.124032S85.464064 243.145728 85.464064 436.513792s156.756992 350.124032 350.124032 350.124032c79.875072 0 153.486336-26.766336 212.39808-71.79264l226.04288 226.04288c8.416256 8.416256 19.441664 12.62592 30.470144 12.62592 11.025408 0 22.050816-4.209664 30.466048-12.62592C951.798784 924.055552 951.798784 896.782336 934.966272 879.950848zM165.953536 436.513792c0-148.912128 120.722432-269.630464 269.63456-269.630464S705.21856 287.60064 705.21856 436.513792 584.500224 706.148352 435.588096 706.148352 165.953536 585.42592 165.953536 436.513792z" fill="#595959" ></path><path d="M291.135488 302.036992c-8.997888-4.864-20.23424-1.511424-25.097216 7.486464-20.256768 37.476352-32.600064 78.152704-36.688896 120.899584-0.973824 10.181632 6.491136 19.224576 16.672768 20.1984 0.59904 0.057344 1.195008 0.084992 1.784832 0.084992 9.433088 0 17.497088-7.175168 18.413568-16.75776 3.613696-37.7856 14.5152-73.72288 32.401408-106.814464C303.485952 318.138368 300.1344 306.900992 291.135488 302.036992z" fill="#595959" ></path></symbol><symbol id="icon-node-parser" viewBox="0 0 1024 1024"><path d="M946.28571406 992h-137.14285687a45.71428594 45.71428594 0 0 1-45.71428594-45.71428594v-137.14285687a45.71428594 45.71428594 0 0 1 45.71428594-45.71428594h45.71428594v-137.14285688h-320.00000063v137.14285688h45.71428594a45.71428594 45.71428594 0 0 1 45.71428594 45.71428594v137.14285687a45.71428594 45.71428594 0 0 1-45.71428594 45.71428594H443.42857156a45.71428594 45.71428594 0 0 1-45.71428593-45.71428594v-137.14285687a45.71428594 45.71428594 0 0 1 45.71428593-45.71428594h45.71428594v-137.14285688H169.14285688v137.14285688h45.71428593a45.71428594 45.71428594 0 0 1 45.71428594 45.71428594v137.14285687a45.71428594 45.71428594 0 0 1-45.71428594 45.71428594H77.71428594a45.71428594 45.71428594 0 0 1-45.71428594-45.71428594v-137.14285687a45.71428594 45.71428594 0 0 1 45.71428594-45.71428594h45.71428593v-182.85714281h365.71428563V443.42857156H397.71428563a91.42857188 91.42857188 0 0 1-91.42857094-91.42857187V123.42857188a91.42857188 91.42857188 0 0 1 91.42857093-91.42857188h228.57142875a91.42857188 91.42857188 0 0 1 91.42857094 91.42857188v228.57142781a91.42857188 91.42857188 0 0 1-91.42857093 91.42857187h-91.42857188v137.14285688h365.71428563v182.85714281h45.71428593a45.71428594 45.71428594 0 0 1 45.71428594 45.71428594v137.14285687a45.71428594 45.71428594 0 0 1-45.71428594 45.71428594zM123.42857188 854.85714312v45.71428501h45.714285v-45.714285H123.42857188zM580.57142844 351.99999969a45.71428594 45.71428594 0 0 0 45.71428593-45.714285V169.14285688a45.71428594 45.71428594 0 0 0-45.71428593-45.71428501H443.42857156a45.71428594 45.71428594 0 0 0-45.71428593 45.714285v137.14285782a45.71428594 45.71428594 0 0 0 45.71428593 45.714285h137.14285688z m-91.42857094 502.85714344v45.71428499h45.714285v-45.714285h-45.714285z m411.42857063-1e-8h-45.71428501v45.71428501h45.714285v-45.71428501z" fill="#595959" ></path></symbol><symbol id="icon-generator1" viewBox="0 0 1024 1024"><path d="M532.48 531.953l418.377-248.159V775.9L531.017 1024l1.463-492.047zM512 492.047L95.086 245.292 512 0l416.914 245.292L512 492.047z m-23.406 36.922l-1.463 490.584-413.988-243.83V283.795l415.451 245.35z" fill="#595959" ></path></symbol><symbol id="icon-post-processor" viewBox="0 0 1024 1024"><path d="M329.9555552 329.9555552h364.0888896v364.0888896H329.9555552z" fill="#595959" ></path><path d="M967.11111147 420.97777813V329.9555552h-91.02222294V193.42222187c0-27.30666667-18.2044448-45.51111147-45.5111104-45.5111104h-136.53333333V56.88888853h-91.02222293v91.02222294h-182.04444374V56.88888853H329.9555552v91.02222294H193.42222187c-27.30666667 0-45.51111147 18.2044448-45.5111104 45.5111104v136.53333333H56.88888853v91.02222293h91.02222294v182.04444374H56.88888853v91.02222293h91.02222294v136.53333333c0 27.30666667 18.2044448 45.51111147 45.5111104 45.5111104h136.53333333v91.02222294h91.02222293v-91.02222294h182.04444374v91.02222294h91.02222293v-91.02222294h136.53333333c27.30666667 0 45.51111147-18.2044448 45.5111104-45.5111104v-136.53333333h91.02222294v-91.02222293h-91.02222294v-182.04444374h91.02222294z m-182.0444448 364.08888854H238.93333333V238.93333333h546.13333334v546.13333334z" fill="#595959" ></path></symbol><symbol id="icon-active" viewBox="0 0 1024 1024"><path d="M513.44 62A450.16000031 450.16000031 0 0 1 962 512a450.16000031 450.16000031 0 0 1-448.56 450H512A450.24 450.24 0 0 1 62 512 450.24 450.24 0 0 1 512 62h1.44zM411.99999969 318.39999969a19.99999969 19.99999969 0 0 0-19.99999969 20.00000062v347.19999938a19.99999969 19.99999969 0 0 0 30.72 16.8l272.80000031-173.59999969a19.99999969 19.99999969 0 0 0 0-33.6l-272.80000031-173.59999969a19.99999969 19.99999969 0 0 0-10.72000031-3.20000062z" fill="#595959" ></path></symbol></svg>'),
+  ((l) => {
+    var a = (t = (t = document.getElementsByTagName("script"))[t.length - 1]).getAttribute("data-injectcss"),
+      t = t.getAttribute("data-disable-injectsvg");
+    if (!t) {
+      var h,
+        i,
+        o,
+        e,
+        c,
+        v = function (a, t) {
+          t.parentNode.insertBefore(a, t);
+        };
+      if (a && !l.__iconfont__svg__cssinject__) {
+        l.__iconfont__svg__cssinject__ = !0;
+        try {
+          document.write(
+            "<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>",
+          );
+        } catch (a) {
+          console && console.log(a);
+        }
+      }
+      (h = function () {
+        var a,
+          t = document.createElement("div");
+        (t.innerHTML = l._iconfont_svg_string_4784207),
+          (t = t.getElementsByTagName("svg")[0]) &&
+            (t.setAttribute("aria-hidden", "true"),
+            (t.style.position = "absolute"),
+            (t.style.width = 0),
+            (t.style.height = 0),
+            (t.style.overflow = "hidden"),
+            (t = t),
+            (a = document.body).firstChild ? v(t, a.firstChild) : a.appendChild(t));
+      }),
+        document.addEventListener
+          ? ~["complete", "loaded", "interactive"].indexOf(document.readyState)
+            ? setTimeout(h, 0)
+            : ((i = function () {
+                document.removeEventListener("DOMContentLoaded", i, !1), h();
+              }),
+              document.addEventListener("DOMContentLoaded", i, !1))
+          : document.attachEvent &&
+            ((o = h),
+            (e = l.document),
+            (c = !1),
+            d(),
+            (e.onreadystatechange = function () {
+              "complete" == e.readyState && ((e.onreadystatechange = null), n());
+            }));
+    }
+    function n() {
+      c || ((c = !0), o());
+    }
+    function d() {
+      try {
+        e.documentElement.doScroll("left");
+      } catch (a) {
+        return void setTimeout(d, 50);
+      }
+      n();
+    }
+  })(window);
diff --git a/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.json b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.json
new file mode 100644
index 0000000000..f00702b38d
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.json
@@ -0,0 +1,156 @@
+{
+  "id": "4784207",
+  "name": "intel-ECRAG",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "42853460",
+      "name": "stop",
+      "font_class": "stop",
+      "unicode": "e904",
+      "unicode_decimal": 59652
+    },
+    {
+      "icon_id": "43170667",
+      "name": "time-expand",
+      "font_class": "time-expand",
+      "unicode": "e616",
+      "unicode_decimal": 58902
+    },
+    {
+      "icon_id": "43169873",
+      "name": "time-retract",
+      "font_class": "time-retract",
+      "unicode": "e615",
+      "unicode_decimal": 58901
+    },
+    {
+      "icon_id": "30339163",
+      "name": "search-time",
+      "font_class": "search-time",
+      "unicode": "ea85",
+      "unicode_decimal": 60037
+    },
+    {
+      "icon_id": "21506860",
+      "name": "handle-time",
+      "font_class": "handle-time",
+      "unicode": "e7a2",
+      "unicode_decimal": 59298
+    },
+    {
+      "icon_id": "7199746",
+      "name": "generation",
+      "font_class": "generation",
+      "unicode": "e6b9",
+      "unicode_decimal": 59065
+    },
+    {
+      "icon_id": "25198413",
+      "name": "generator",
+      "font_class": "generator",
+      "unicode": "e67e",
+      "unicode_decimal": 59006
+    },
+    {
+      "icon_id": "1262340",
+      "name": "user",
+      "font_class": "user",
+      "unicode": "e730",
+      "unicode_decimal": 59184
+    },
+    {
+      "icon_id": "43165836",
+      "name": "send",
+      "font_class": "send",
+      "unicode": "e7db",
+      "unicode_decimal": 59355
+    },
+    {
+      "icon_id": "1973584",
+      "name": "uploaded",
+      "font_class": "uploaded",
+      "unicode": "e614",
+      "unicode_decimal": 58900
+    },
+    {
+      "icon_id": "33661191",
+      "name": "notice board",
+      "font_class": "notice-board",
+      "unicode": "e674",
+      "unicode_decimal": 58996
+    },
+    {
+      "icon_id": "8094211",
+      "name": "setting",
+      "font_class": "setting",
+      "unicode": "e64c",
+      "unicode_decimal": 58956
+    },
+    {
+      "icon_id": "13990803",
+      "name": "chatbot",
+      "font_class": "chatbot",
+      "unicode": "e669",
+      "unicode_decimal": 58985
+    },
+    {
+      "icon_id": "6151158",
+      "name": "cloud upload-fill",
+      "font_class": "cloudupload-fill",
+      "unicode": "e7da",
+      "unicode_decimal": 59354
+    },
+    {
+      "icon_id": "43084711",
+      "name": "basic",
+      "font_class": "basic",
+      "unicode": "e613",
+      "unicode_decimal": 58899
+    },
+    {
+      "icon_id": "43083946",
+      "name": "Indexer",
+      "font_class": "indexer",
+      "unicode": "e612",
+      "unicode_decimal": 58898
+    },
+    {
+      "icon_id": "145727",
+      "name": "Retriever",
+      "font_class": "retriever",
+      "unicode": "e694",
+      "unicode_decimal": 59028
+    },
+    {
+      "icon_id": "4114056",
+      "name": "node-parser-icon",
+      "font_class": "node-parser",
+      "unicode": "e65d",
+      "unicode_decimal": 58973
+    },
+    {
+      "icon_id": "11791029",
+      "name": "Generator",
+      "font_class": "generator1",
+      "unicode": "e656",
+      "unicode_decimal": 58966
+    },
+    {
+      "icon_id": "21489495",
+      "name": "PostProcessor",
+      "font_class": "post-processor",
+      "unicode": "e718",
+      "unicode_decimal": 59160
+    },
+    {
+      "icon_id": "25487933",
+      "name": "active",
+      "font_class": "active",
+      "unicode": "e795",
+      "unicode_decimal": 59285
+    }
+  ]
+}
diff --git a/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.ttf b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.ttf
new file mode 100644
index 0000000000..38480000d3
Binary files /dev/null and b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.ttf differ
diff --git a/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.woff b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.woff
new file mode 100644
index 0000000000..c16ad1f171
Binary files /dev/null and b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.woff differ
diff --git a/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.woff2 b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.woff2
new file mode 100644
index 0000000000..45249bf369
Binary files /dev/null and b/EdgeCraftRAG/ui/vue/src/assets/iconFont/iconfont.woff2 differ
diff --git a/EdgeCraftRAG/ui/vue/src/assets/images/404-bg.png b/EdgeCraftRAG/ui/vue/src/assets/images/404-bg.png
new file mode 100644
index 0000000000..94c8394fed
Binary files /dev/null and b/EdgeCraftRAG/ui/vue/src/assets/images/404-bg.png differ
diff --git a/EdgeCraftRAG/ui/vue/src/assets/images/noData.png b/EdgeCraftRAG/ui/vue/src/assets/images/noData.png
new file mode 100644
index 0000000000..01e27e21c5
Binary files /dev/null and b/EdgeCraftRAG/ui/vue/src/assets/images/noData.png differ
diff --git a/EdgeCraftRAG/ui/vue/src/assets/svgs/404-icon.svg b/EdgeCraftRAG/ui/vue/src/assets/svgs/404-icon.svg
new file mode 100644
index 0000000000..dd5b9741c9
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/svgs/404-icon.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg height="188" node-id="1" sillyvg="true" template-height="188" template-width="280" version="1.1" width="280" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs node-id="88"><linearGradient gradientUnits="objectBoundingBox" id="linearGradient-1" node-id="7" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="0.99606735"><stop offset="0" stop-color="#c0c4cc"/><stop offset="1" stop-color="#f7fdfe" stop-opacity="0"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient1" node-id="96" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient-2" node-id="10" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient-3" node-id="14" spreadMethod="pad" x1="0.5" x2="0.5" y1="0.00071262673" y2="1"><stop offset="0.0007126267" stop-color="#ffffff"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient-14" node-id="43" spreadMethod="pad" x1="1.1088529" x2="0.43100062" y1="0.48609075" y2="0.5180712"><stop offset="0.00047508447" stop-color="#ebecef"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient-15" node-id="46" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient2" node-id="135" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient3" node-id="144" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient4" node-id="153" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient5" node-id="162" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><linearGradient gradientUnits="objectBoundingBox" id="linearGradient-16" node-id="50" spreadMethod="pad" x1="0.5" x2="0.5" y1="0" y2="1"><stop offset="0" stop-color="#e3e3e3"/><stop offset="0.4474621" stop-color="#e3e5e8"/><stop offset="1" stop-color="#c0c4cc"/></linearGradient><filter height="11.198883" id="filter-5" node-id="19" width="11.11631" x="65.24691" y="23.30056"><feOffset color-interpolation-filters="linearRGB" dx="-1" dy="1" in="SourceAlpha" result="shadowOffsetInner1"/><feComposite color-interpolation-filters="linearRGB" in="shadowOffsetInner1" in2="SourceAlpha" k1="0" k2="-1" k3="1" k4="0" operator="arithmetic" result="shadowInnerInner1"/><feColorMatrix color-interpolation-filters="linearRGB" in="shadowInnerInner1" result="result3" type="matrix" values="0 0 0 0 0.7529412 0 0 0 0 0.76862746 0 0 0 0 0.8 0 0 0 1 0"/></filter><filter height="9.502384" id="filter-7" node-id="24" width="9.432171" x="35.74527" y="41.148808"><feOffset color-interpolation-filters="linearRGB" dx="-1" dy="1" in="SourceAlpha" result="shadowOffsetInner1"/><feComposite color-interpolation-filters="linearRGB" in="shadowOffsetInner1" in2="SourceAlpha" k1="0" k2="-1" k3="1" k4="0" operator="arithmetic" result="shadowInnerInner1"/><feColorMatrix color-interpolation-filters="linearRGB" in="shadowInnerInner1" result="result3" type="matrix" values="0 0 0 0 0.7529412 0 0 0 0 0.76862746 0 0 0 0 0.8 0 0 0 1 0"/></filter><filter height="6.099205" id="filter-9" node-id="29" width="6.0588417" x="45.013435" y="16.500397"><feOffset color-interpolation-filters="linearRGB" dx="-1" dy="1" in="SourceAlpha" result="shadowOffsetInner1"/><feComposite color-interpolation-filters="linearRGB" in="shadowOffsetInner1" in2="SourceAlpha" k1="0" k2="-1" k3="1" k4="0" operator="arithmetic" result="shadowInnerInner1"/><feColorMatrix color-interpolation-filters="linearRGB" in="shadowInnerInner1" result="result3" type="matrix" values="0 0 0 0 0.7529412 0 0 0 0 0.76862746 0 0 0 0 0.8 0 0 0 1 0"/></filter><filter height="4.399315" id="filter-11" node-id="34" width="4.373024" x="69.45804" y="43.700344"><feOffset color-interpolation-filters="linearRGB" dx="-1" dy="1" in="SourceAlpha" result="shadowOffsetInner1"/><feComposite color-interpolation-filters="linearRGB" in="shadowOffsetInner1" in2="SourceAlpha" k1="0" k2="-1" k3="1" k4="0" operator="arithmetic" result="shadowInnerInner1"/><feColorMatrix color-interpolation-filters="linearRGB" in="shadowInnerInner1" result="result3" type="matrix" values="0 0 0 0 0.7529412 0 0 0 0 0.76862746 0 0 0 0 0.8 0 0 0 1 0"/></filter><filter height="6.0992126" id="filter-13" node-id="39" width="6.0588455" x="69.567795" y="59.08561"><feOffset color-interpolation-filters="linearRGB" dx="-1" dy="1" in="SourceAlpha" result="shadowOffsetInner1"/><feComposite color-interpolation-filters="linearRGB" in="shadowOffsetInner1" in2="SourceAlpha" k1="0" k2="-1" k3="1" k4="0" operator="arithmetic" result="shadowInnerInner1"/><feColorMatrix color-interpolation-filters="linearRGB" in="shadowInnerInner1" result="result3" type="matrix" values="0 0 0 0 0.7529412 0 0 0 0 0.76862746 0 0 0 0 0.8 0 0 0 1 0"/></filter></defs><g node-id="291"><g node-id="292"><g node-id="293"><path d="M 268.00 164.50 L 267.80 165.90 L 267.10 167.20 L 265.90 168.70 L 264.00 170.30 L 261.20 171.90 L 256.70 174.00 L 250.50 176.20 L 242.40 178.40 L 224.80 181.90 L 202.70 184.80 L 177.40 186.80 L 150.20 187.90 L 135.50 188.00 L 120.80 187.90 L 93.60 186.80 L 68.30 184.80 L 46.20 181.90 L 28.60 178.40 L 20.50 176.20 L 14.30 174.00 L 9.80 171.90 L 7.00 170.30 L 5.10 168.70 L 3.90 167.20 L 3.20 165.90 L 3.00 164.50 L 3.20 163.10 L 3.90 161.80 L 5.10 160.30 L 7.00 158.70 L 9.80 157.10 L 14.30 155.00 L 20.50 152.80 L 28.60 150.60 L 36.70 148.80 L 57.20 145.50 L 80.40 143.10 L 106.80 141.50 L 135.50 141.00 L 150.20 141.10 L 177.40 142.20 L 202.70 144.20 L 224.80 147.10 L 242.40 150.60 L 250.50 152.80 L 256.70 155.00 L 261.20 157.10 L 264.00 158.70 L 265.90 160.30 L 267.10 161.80 L 267.80 163.10 L 268.00 164.50 Z" fill="url(#linearGradient-1)" fill-rule="evenodd" group-id="1,2,3" id="椭圆形" node-id="58" stroke="none" target-height="47" target-width="265" target-x="3" target-y="141"/><g node-id="295"><path d="M 64.70 154.00 L 64.70 137.90 L 75.50 137.90 L 75.50 119.40 L 64.70 119.40 L 64.70 66.60 L 43.90 66.60 L 0.50 118.30 L 0.50 137.90 L 43.90 137.90 L 43.90 154.00 L 64.70 154.00 Z M 43.90 119.40 L 21.00 119.40 L 43.90 92.40 L 43.90 119.40 Z" fill="url(#linearGradient1)" fill-rule="evenodd" group-id="1,2,3,5" id="4" node-id="60" stroke="none" target-height="87.4" target-width="75" target-x="0.5" target-y="66.6"/><path d="M 250.70 154.00 L 250.70 137.90 L 261.50 137.90 L 261.50 119.40 L 250.70 119.40 L 250.70 66.60 L 229.90 66.60 L 186.50 118.30 L 186.50 137.90 L 229.90 137.90 L 229.90 154.00 L 250.70 154.00 Z M 229.90 119.40 L 207.00 119.40 L 229.90 92.40 L 229.90 119.40 Z" fill="url(#linearGradient-2)" fill-rule="evenodd" group-id="1,2,3,5" id="4" node-id="61" stroke="none" target-height="87.4" target-width="75" target-x="186.5" target-y="66.6"/><g node-id="296"><g node-id="297"><path d="M 170.20 115.00 L 168.80 122.60 L 166.00 129.30 L 163.40 133.50 L 160.40 137.20 L 156.90 140.50 L 153.10 143.40 L 148.90 145.70 L 144.40 147.50 L 137.20 149.10 L 129.60 149.20 L 122.10 147.80 L 115.30 145.00 L 111.10 142.40 L 107.40 139.40 L 104.10 135.90 L 101.30 132.00 L 99.00 127.90 L 97.20 123.30 L 95.60 116.20 L 95.50 108.50 L 96.90 101.00 L 99.70 94.20 L 102.30 90.00 L 105.30 86.30 L 108.80 83.00 L 112.60 80.10 L 116.80 77.80 L 121.30 76.00 L 128.50 74.40 L 136.10 74.30 L 143.60 75.80 L 150.40 78.50 L 154.50 81.10 L 158.30 84.10 L 161.60 87.60 L 164.40 91.50 L 166.70 95.70 L 168.50 100.20 L 170.10 107.40 L 170.20 115.00 Z" fill="url(#linearGradient-3)" fill-rule="evenodd" group-id="1,2,3,5,6,7" id="椭圆形" node-id="63" stroke="none" target-height="74.899994" target-width="74.7" target-x="95.5" target-y="74.3"/></g><g node-id="298"><g node-id="305"><path d="M 152.80 100.30 L 152.50 101.70 L 151.00 103.80 L 148.70 104.90 L 147.40 105.00 L 144.90 104.10 L 143.20 102.10 L 142.80 100.90 L 142.80 99.50 L 143.70 97.00 L 145.60 95.30 L 148.20 94.80 L 149.60 95.10 L 151.70 96.60 L 152.80 98.90 L 152.80 100.30 Z" fill="#e7e8ec" fill-rule="evenodd" group-id="1,2,3,5,6,8,15" node-id="236" stroke="none" target-height="10.199997" target-width="10" target-x="142.8" target-y="94.8"/></g><path d="M 152.80 100.30 L 152.50 101.70 L 151.00 103.80 L 148.70 104.90 L 147.40 105.00 L 144.90 104.10 L 143.20 102.10 L 142.80 100.90 L 142.80 99.50 L 143.70 97.00 L 145.60 95.30 L 148.20 94.80 L 149.60 95.10 L 151.70 96.60 L 152.80 98.90 L 152.80 100.30 Z" fill="#000000" fill-rule="evenodd" filter="url(#filter-5)" group-id="1,2,3,5,6,8,16" node-id="241" stroke="none" target-height="10.199997" target-width="10" target-x="142.8" target-y="94.8"/></g><g node-id="299"><g node-id="307"><path d="M 121.70 117.30 L 121.20 118.90 L 120.20 120.20 L 118.80 120.90 L 117.10 121.10 L 115.50 120.70 L 114.20 119.60 L 113.40 118.20 L 113.30 116.50 L 113.70 114.90 L 114.80 113.60 L 116.20 112.90 L 117.80 112.70 L 119.40 113.10 L 120.70 114.20 L 121.50 115.60 L 121.70 117.30 Z" fill="#e7e8ec" fill-rule="evenodd" group-id="1,2,3,5,6,9,17" node-id="248" stroke="none" target-height="8.400002" target-width="8.399994" target-x="113.3" target-y="112.7"/></g><path d="M 121.70 117.30 L 121.20 118.90 L 120.20 120.20 L 118.80 120.90 L 117.10 121.10 L 115.50 120.70 L 114.20 119.60 L 113.40 118.20 L 113.30 116.50 L 113.70 114.90 L 114.80 113.60 L 116.20 112.90 L 117.80 112.70 L 119.40 113.10 L 120.70 114.20 L 121.50 115.60 L 121.70 117.30 Z" fill="#000000" fill-rule="evenodd" filter="url(#filter-7)" group-id="1,2,3,5,6,9,18" node-id="253" stroke="none" target-height="8.400002" target-width="8.399994" target-x="113.3" target-y="112.7"/></g><g node-id="300"><g node-id="309"><path d="M 127.60 90.80 L 126.70 92.50 L 124.80 93.10 L 123.10 92.20 L 122.50 90.30 L 123.40 88.60 L 125.30 88.00 L 127.00 88.90 L 127.60 90.80 Z" fill="#e7e8ec" fill-rule="evenodd" group-id="1,2,3,5,6,10,19" node-id="260" stroke="none" target-height="5.0999985" target-width="5.0999985" target-x="122.5" target-y="88"/></g><path d="M 127.60 90.80 L 126.70 92.50 L 124.80 93.10 L 123.10 92.20 L 122.50 90.30 L 123.40 88.60 L 125.30 88.00 L 127.00 88.90 L 127.60 90.80 Z" fill="#000000" fill-rule="evenodd" filter="url(#filter-9)" group-id="1,2,3,5,6,10,20" node-id="265" stroke="none" target-height="5.0999985" target-width="5.0999985" target-x="122.5" target-y="88"/></g><g node-id="301"><g node-id="311"><path d="M 150.30 117.00 L 150.10 117.70 L 149.20 118.50 L 147.80 118.40 L 147.00 117.40 L 147.20 116.10 L 148.10 115.30 L 149.40 115.40 L 150.20 116.40 L 150.30 117.00 Z" fill="#e7e8ec" fill-rule="evenodd" group-id="1,2,3,5,6,11,21" node-id="272" stroke="none" target-height="3.199997" target-width="3.300003" target-x="147" target-y="115.3"/></g><path d="M 150.30 117.00 L 150.10 117.70 L 149.20 118.50 L 147.80 118.40 L 147.00 117.40 L 147.20 116.10 L 148.10 115.30 L 149.40 115.40 L 150.20 116.40 L 150.30 117.00 Z" fill="#000000" fill-rule="evenodd" filter="url(#filter-11)" group-id="1,2,3,5,6,11,22" node-id="277" stroke="none" target-height="3.199997" target-width="3.300003" target-x="147" target-y="115.3"/></g><g node-id="302"><g node-id="313"><path d="M 152.10 133.40 L 151.80 134.30 L 150.40 135.60 L 148.40 135.40 L 147.20 133.90 L 147.40 131.90 L 148.80 130.70 L 149.80 130.60 L 151.50 131.50 L 152.10 133.40 Z" fill="#e7e8ec" fill-rule="evenodd" group-id="1,2,3,5,6,12,23" node-id="284" stroke="none" target-height="5" target-width="4.900009" target-x="147.2" target-y="130.6"/></g><path d="M 152.10 133.40 L 151.80 134.30 L 150.40 135.60 L 148.40 135.40 L 147.20 133.90 L 147.40 131.90 L 148.80 130.70 L 149.80 130.60 L 151.50 131.50 L 152.10 133.40 Z" fill="#000000" fill-rule="evenodd" filter="url(#filter-13)" group-id="1,2,3,5,6,12,24" node-id="289" stroke="none" target-height="5" target-width="4.900009" target-x="147.2" target-y="130.6"/></g><g node-id="303"><path d="M 96.20 122.60 L 91.30 126.60 L 85.30 132.70 L 82.60 136.90 L 81.90 138.90 L 81.80 140.60 L 82.20 142.00 L 83.00 143.20 L 84.30 144.10 L 86.20 144.90 L 88.80 145.40 L 92.30 145.60 L 96.70 145.30 L 102.20 144.50 L 113.60 141.70 L 127.00 137.20 L 141.50 131.00 L 155.50 123.90 L 167.60 116.50 L 177.10 109.50 L 181.20 105.80 L 184.20 102.60 L 186.40 99.80 L 187.70 97.50 L 188.30 95.60 L 188.40 93.90 L 188.00 92.50 L 187.20 91.30 L 185.90 90.40 L 183.90 89.60 L 179.10 89.00 L 170.80 89.60 L 164.70 90.70" fill="none" group-id="1,2,3,5,6,13" id="椭圆形" node-id="79" stroke="url(#linearGradient-14)" stroke-linecap="round" stroke-width="2" target-height="56.600006" target-width="106.59999" target-x="81.8" target-y="89"/></g><path d="M 102.40 145.40 L 102.10 146.90 L 100.40 149.30 L 97.70 150.50 L 96.20 150.60 L 93.30 149.60 L 91.40 147.30 L 91.00 145.90 L 90.90 144.30 L 92.00 141.50 L 94.20 139.60 L 97.20 139.10 L 98.70 139.50 L 101.10 141.10 L 102.30 143.80 L 102.40 145.40 Z" fill="url(#linearGradient-15)" fill-rule="evenodd" group-id="1,2,3,5,6,14" id="椭圆形" node-id="80" stroke="none" target-height="11.5" target-width="11.5" target-x="90.9" target-y="139.1"/></g></g><path d="M 211.00 37.50 L 210.70 39.50 L 210.00 41.30 L 208.80 42.80 L 207.30 44.00 L 205.50 44.70 L 203.50 45.00 L 201.50 44.70 L 199.70 44.00 L 198.20 42.80 L 197.00 41.30 L 196.30 39.50 L 196.00 37.50 L 196.30 35.50 L 197.00 33.70 L 198.20 32.20 L 199.70 31.00 L 201.50 30.30 L 203.50 30.00 L 205.50 30.30 L 207.30 31.00 L 208.80 32.20 L 210.00 33.70 L 210.70 35.50 L 211.00 37.50 Z" fill="url(#linearGradient2)" fill-rule="evenodd" group-id="1,2,3" id="椭圆形备份" node-id="81" stroke="none" target-height="15" target-width="15" target-x="196" target-y="30"/><path d="M 280.00 150.00 L 279.80 151.20 L 279.10 152.10 L 278.20 152.80 L 277.00 153.00 L 275.80 152.80 L 274.90 152.10 L 274.20 151.20 L 274.00 150.00 L 274.20 148.80 L 274.90 147.90 L 275.80 147.20 L 277.00 147.00 L 278.20 147.20 L 279.10 147.90 L 279.80 148.80 L 280.00 150.00 Z" fill="url(#linearGradient3)" fill-rule="evenodd" group-id="1,2,3" id="椭圆形备份-3" node-id="82" stroke="none" target-height="6" target-width="6" target-x="274" target-y="147"/><path d="M 22.00 58.00 L 21.70 59.60 L 20.80 60.80 L 19.60 61.70 L 18.00 62.00 L 16.40 61.70 L 15.20 60.80 L 14.30 59.60 L 14.00 58.00 L 14.30 56.40 L 15.20 55.20 L 16.40 54.30 L 18.00 54.00 L 19.60 54.30 L 20.80 55.20 L 21.70 56.40 L 22.00 58.00 Z" fill="url(#linearGradient4)" fill-rule="evenodd" group-id="1,2,3" id="椭圆形备份-2" node-id="83" stroke="none" target-height="8" target-width="8" target-x="14" target-y="54"/></g><g node-id="294"><path d="M 106.60 1.00 L 106.00 0.40 L 101.40 0.10 L 96.80 1.30 L 94.80 2.50 L 91.50 5.70 L 87.50 7.60 L 84.90 10.00 L 84.00 11.70 L 84.60 12.40 L 88.90 11.80 L 88.90 12.10 L 91.80 15.20 L 94.90 18.10 L 95.20 18.10 L 94.60 22.40 L 95.30 23.00 L 95.80 22.90 L 98.00 21.20 L 99.30 19.50 L 101.30 15.50 L 104.50 12.20 L 105.70 10.20 L 106.90 5.60 L 106.60 1.00 Z M 100.90 10.10 L 99.90 10.80 L 98.90 11.00 L 97.80 10.80 L 96.90 10.10 L 96.20 9.20 L 96.00 8.10 L 96.20 7.10 L 96.90 6.10 L 97.80 5.50 L 98.90 5.30 L 99.90 5.50 L 100.90 6.10 L 101.50 7.10 L 101.70 8.10 L 101.50 9.20 L 100.90 10.10 Z" fill="url(#linearGradient5)" fill-rule="evenodd" group-id="1,2,4" id="形状" node-id="85" stroke="none" target-height="22.9" target-width="22.900002" target-x="84" target-y="0.1"/><path d="M 87.10 15.60 L 84.40 19.60 L 83.20 22.20 L 83.00 23.20 L 83.20 23.80 L 83.80 24.00 L 84.80 23.80 L 87.40 22.60 L 91.40 19.90 L 91.90 19.00 L 92.00 18.00 L 91.00 16.00 L 90.00 15.30 L 88.00 15.10 L 87.10 15.60 Z" fill="url(#linearGradient-16)" fill-rule="evenodd" group-id="1,2,4" id="路径" node-id="86" stroke="none" target-height="8.9" target-width="9" target-x="83" target-y="15.1"/></g></g></g></svg>
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/src/assets/svgs/dark-icon.svg b/EdgeCraftRAG/ui/vue/src/assets/svgs/dark-icon.svg
new file mode 100644
index 0000000000..296521d7f3
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/svgs/dark-icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><defs><clipPath id="master_svg0_88_0653"><rect x="6" y="4" width="12" height="16" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="24" height="24" rx="12" fill="#1F2937" fill-opacity="1" style="mix-blend-mode:passthrough"/><g clip-path="url(#master_svg0_88_0653)"><g transform="matrix(1,0,0,-1,0,38.687469482421875)"><g><path d="M12.992370859375,33.34373474121094Q11.038880859375,33.31253474121094,9.488490859375,32.374934741210936Q7.907100859375,31.468734741210938,6.976863859375,29.874934741210936Q6.077638659375,28.312484741210938,6.046630859375,26.343734741210938Q6.077638659375,24.374984741210938,7.007870859375,22.812484741210938Q7.907100859375,21.218734741210938,9.488490859375,20.312484741210938Q11.038880859375,19.374984741210938,12.992370859375,19.343734741210938Q15.876090859375,19.406234741210938,17.798530859375,21.312484741210938Q18.046630859375,21.593734741210938,17.891630859375,21.906234741210938Q17.736530859375,22.218734741210938,17.364430859375,22.187484741210938Q16.930330859374997,22.093734741210938,16.434230859375,22.093734741210938Q14.139650859375,22.156234741210938,12.589270859374999,23.718734741210938Q11.069890859375,25.249984741210938,11.007870859375,27.593734741210938Q11.007870859375,29.156234741210938,11.752060859375,30.406234741210938Q12.527250859375,31.656234741210938,13.767560859375,32.374934741210936Q14.077640859375,32.56253474121094,14.015620859375,32.93753474121094Q13.922600859374999,33.249934741210936,13.550510859374999,33.31253474121094Q13.271440859375,33.34373474121094,12.961360859374999,33.34373474121094L12.992370859375,33.34373474121094Z" fill="#F3F4F6" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></g></svg>
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/src/assets/svgs/header-log.svg b/EdgeCraftRAG/ui/vue/src/assets/svgs/header-log.svg
new file mode 100644
index 0000000000..a43a6b4ee7
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/svgs/header-log.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="122.59375" height="36" viewBox="0 0 122.59375 36"><g><rect x="0" y="0" width="122.59375" height="36" rx="0" fill="#000000" fill-opacity="0" style="mix-blend-mode:passthrough"/><g><rect x="0" y="14" width="8" height="8" rx="0" fill="#0068B5" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><rect x="16" y="0" width="90.59375" height="36" rx="0" fill="#000000" fill-opacity="0" style="mix-blend-mode:passthrough"/><g><rect x="16" y="0" width="59.0625" height="35" rx="0" fill="#000000" fill-opacity="0" style="mix-blend-mode:passthrough"/><g><path d="M22.09169,12.1504L22.09169,28L17.85829,28L17.85829,12.1504L22.09169,12.1504ZM17.5937,8.02571Q17.5937,7.09851,18.23961,6.49564Q18.88574,5.89253,19.96469,5.89253Q21.03883,5.89253,21.68954,6.49564Q22.34026,7.09851,22.34026,8.02571Q22.34026,8.94353,21.68954,9.54411Q21.03883,10.1447,19.96469,10.1447Q18.88574,10.1447,18.23961,9.54411Q17.5937,8.94353,17.5937,8.02571ZM29.7075,15.5342L29.7075,28L25.48875,28L25.48875,12.1504L29.4518,12.1504L29.7075,15.5342ZM29.102600000000002,19.5142L27.954700000000003,19.5199Q27.9595,17.7651,28.4084,16.3511Q28.8572,14.9368,29.6771,13.9313Q30.4969,12.9258,31.6363,12.3916Q32.7759,11.8574,34.171800000000005,11.8574Q35.2941,11.8574,36.2167,12.1774Q37.1393,12.4971,37.805800000000005,13.2055Q38.472300000000004,13.9139,38.8287,15.0647Q39.1853,16.2156,39.1853,17.8921L39.1853,28L34.942,28L34.942,17.8729Q34.942,16.8262,34.645399999999995,16.2405Q34.3488,15.6548,33.7846,15.4149Q33.2204,15.1748,32.4033,15.1748Q31.5614,15.1748,30.9373,15.5104Q30.3131,15.8457,29.9082,16.4389Q29.5036,17.0322,29.3031,17.818Q29.102600000000002,18.6037,29.102600000000002,19.5142ZM50.1618,12.1504L50.1618,15.1437L40.9273,15.1437L40.9273,12.1504L50.1618,12.1504ZM43.222300000000004,8.23834L47.441,8.23834L47.441,23.2306Q47.441,23.9172,47.6205,24.2827Q47.800200000000004,24.648,48.1707,24.7849Q48.5415,24.9218,49.0947,24.9218Q49.4914,24.9218,49.8004,24.8856Q50.1093,24.8494,50.3394,24.8055L50.3442,27.9107Q49.8134,28.0826,49.1792,28.1877Q48.5452,28.293,47.783,28.293Q46.3898,28.293,45.3612,27.8281Q44.3328,27.363,43.7776,26.3385Q43.222300000000004,25.3141,43.222300000000004,23.6428L43.222300000000004,8.23834ZM59.8377,28.293Q57.9939,28.293,56.5295,27.6992Q55.0651,27.1055,54.0459,26.0504Q53.0269,24.9952,52.4893,23.5988Q51.9518,22.2024,51.9518,20.6119L51.9518,20.0365Q51.9518,18.2318,52.4636,16.7356Q52.9754,15.2394,53.9369,14.1451Q54.8985,13.0506,56.2727,12.4541Q57.6469,11.8574,59.3669,11.8574Q61.0563,11.8574,62.3586,12.4136Q63.661,12.9698,64.5463,13.9924Q65.4316,15.0148,65.8869,16.4433Q66.3421,17.8717,66.3421,19.6211L66.3421,21.373L53.7518,21.373L53.7518,18.5655L62.2028,18.5655L62.2028,18.2412Q62.1938,17.3652,61.8798,16.6669Q61.566,15.9686,60.9389,15.5623Q60.312,15.1558,59.3372,15.1558Q58.4937,15.1558,57.8911,15.5193Q57.2885,15.8828,56.9154,16.5403Q56.5423,17.1977,56.3638,18.088Q56.1852,18.9782,56.1852,20.0365L56.1852,20.6119Q56.1852,21.5712,56.4505,22.3764Q56.7158,23.1816,57.2244,23.7648Q57.7332,24.348,58.4557,24.6737Q59.1786,24.9991,60.1032,24.9991Q61.2509,24.9991,62.2371,24.5521Q63.2234,24.1051,63.9407,23.1933L65.98830000000001,25.42Q65.4952,26.1369,64.6397,26.8004Q63.7843,27.4637,62.5797,27.8785Q61.3751,28.293,59.8377,28.293ZM73.20009999999999,5.5L73.20009999999999,28L68.9667,28L68.9667,5.5L73.20009999999999,5.5Z" fill="#0068B5" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g><g><path d="" fill="#000000" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><rect x="82.546875" y="0" width="24.046875" height="35" rx="0" fill="#000000" fill-opacity="0" style="mix-blend-mode:passthrough"/><g><path d="M92.746975,24.4265L92.746975,17.3691Q92.746975,16.6102,92.49751499999999,16.0583Q92.248265,15.5065,91.72938500000001,15.2Q91.210515,14.8935,90.389965,14.8935Q89.682035,14.8935,89.166135,15.1359Q88.650235,15.3783,88.371915,15.8207Q88.093595,16.2629,88.093595,16.8725L83.875075,16.8725Q83.875075,15.8542,84.349095,14.9448Q84.823335,14.0352,85.710475,13.3392Q86.597855,12.6432,87.841825,12.2504Q89.085805,11.8574,90.62045499999999,11.8574Q92.458145,11.8574,93.892975,12.4731Q95.327875,13.0888,96.156675,14.3177Q96.985675,15.5465,96.985675,17.3984L96.985675,24.1756Q96.985675,25.4828,97.149575,26.312Q97.313675,27.141,97.621675,27.7558L97.621675,28L93.355375,28Q93.050975,27.3525,92.89897500000001,26.3804Q92.746975,25.4081,92.746975,24.4265ZM93.305275,18.341L93.334575,20.7415L90.972475,20.7415Q90.132705,20.7415,89.517245,20.9296Q88.902005,21.1178,88.509935,21.4592Q88.118085,21.8005,87.929715,22.2555Q87.741575,22.7105,87.741575,23.2544Q87.741575,23.7929,87.994035,24.2246Q88.246715,24.6563,88.714325,24.9012Q89.182155,25.1458,89.811125,25.1458Q90.768315,25.1458,91.467315,24.7625Q92.166325,24.3789,92.556575,23.8257Q92.947075,23.2722,92.961275,22.7728L94.068775,24.5629Q93.837175,25.1584,93.437575,25.8062Q93.038175,26.4539,92.422665,27.016Q91.807205,27.5782,90.938145,27.9357Q90.069305,28.293,88.889415,28.293Q87.377425,28.293,86.158855,27.683Q84.940525,27.0728,84.229385,26.0009Q83.51825,24.9291,83.51825,23.5512Q83.51825,22.3066,83.981505,21.3375Q84.444765,20.3682,85.362355,19.7014Q86.280165,19.0347,87.658035,18.6879Q89.035905,18.341,90.850475,18.341L93.305275,18.341ZM104.693275,12.1504L104.693275,28L100.459875,28L100.459875,12.1504L104.693275,12.1504ZM100.195275,8.02571Q100.195275,7.09851,100.84117499999999,6.49564Q101.487275,5.89253,102.566275,5.89253Q103.640375,5.89253,104.291075,6.49564Q104.941775,7.09851,104.941775,8.02571Q104.941775,8.94353,104.291075,9.54411Q103.640375,10.1447,102.566275,10.1447Q101.487275,10.1447,100.84117499999999,9.54411Q100.195275,8.94353,100.195275,8.02571Z" fill="#00B0F0" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g><g><rect x="114.59375" y="14" width="8" height="8" rx="0" fill="#00B0F0" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/src/assets/svgs/light-icon.svg b/EdgeCraftRAG/ui/vue/src/assets/svgs/light-icon.svg
new file mode 100644
index 0000000000..79041030dd
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/svgs/light-icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><defs><clipPath id="master_svg0_88_0643"><rect x="4" y="4" width="16" height="16" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="24" height="24" rx="12" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/><g clip-path="url(#master_svg0_88_0643)"><g transform="matrix(1,0,0,-1,0,40.695343017578125)"><g><path d="M15.3488171875,36.320371508789066Q15.5658171875,36.195371508789066,15.6279171875,35.945371508789066L16.2480171875,32.570371508789066L19.5968171875,31.945371508789062Q19.844917187500002,31.882871508789062,19.9689171875,31.664071508789064Q20.0620171875,31.414071508789064,19.9069171875,31.164071508789064L17.9844171875,28.35158150878906L19.9069171875,25.539081508789064Q20.0620171875,25.289081508789064,19.9689171875,25.039081508789064Q19.844917187500002,24.820331508789064,19.5968171875,24.757831508789064L16.2480171875,24.132831508789064L15.6279171875,20.757827508789063Q15.5658171875,20.507827508789063,15.3488171875,20.382827708789062Q15.1007171875,20.289077808789063,14.8527171875,20.445327708789062L12.0619771875,22.382831508789064L9.2712771875,20.445327708789062Q9.0232171875,20.289077808789063,8.8061671875,20.382827708789062Q8.5580971875,20.507827508789063,8.496087187499999,20.757827508789063L7.8759271875,24.132831508789064L4.5270941875,24.757831508789064Q4.2790321875,24.820331508789064,4.1550008875,25.039081508789064Q4.0619776875,25.289081508789064,4.2170163875,25.539081508789064L6.1394971875,28.35158150878906L4.2170163875,31.164071508789064Q4.0619776875,31.414071508789064,4.1550008875,31.632871508789062Q4.2790321875,31.882871508789062,4.5270941875,31.945371508789062L7.8759271875,32.570371508789066L8.496087187499999,35.945371508789066Q8.5580971875,36.195371508789066,8.8061671875,36.320371508789066Q9.0232171875,36.414071508789064,9.2712771875,36.257871508789066L12.0619771875,34.320371508789066L14.8527171875,36.257871508789066Q15.1007171875,36.414071508789064,15.3488171875,36.320371508789066ZM9.085237187499999,28.35158150878906Q9.085237187499999,29.16408150878906,9.4883371875,29.85158150878906Q9.8914371875,30.539071508789064,10.5736071875,30.945371508789062Q11.2867871875,31.351571508789064,12.0619771875,31.351571508789064Q12.8371671875,31.351571508789064,13.5503471875,30.945371508789062Q14.2325171875,30.539071508789064,14.6356171875,29.85158150878906Q15.0387171875,29.16408150878906,15.0387171875,28.35158150878906Q15.0387171875,27.539081508789064,14.6356171875,26.851581508789064Q14.2325171875,26.164081508789064,13.5503471875,25.757831508789064Q12.8371671875,25.351581508789064,12.0619771875,25.351581508789064Q11.2867871875,25.351581508789064,10.5736071875,25.757831508789064Q9.8914371875,26.164081508789064,9.4883371875,26.851581508789064Q9.085237187499999,27.539081508789064,9.085237187499999,28.35158150878906ZM16.031017187499998,28.35158150878906Q16.031017187499998,29.44533150878906,15.5038171875,30.351571508789064Q14.9767171875,31.257871508789062,14.0464771875,31.820371508789062Q13.1162371875,32.351571508789064,12.0619771875,32.351571508789064Q11.007717187499999,32.351571508789064,10.077477187500001,31.820371508789062Q9.1472471875,31.257871508789062,8.6201171875,30.351571508789064Q8.0929871875,29.44533150878906,8.0929871875,28.35158150878906Q8.0929871875,27.257831508789064,8.6201171875,26.351581508789064Q9.1472471875,25.445331508789064,10.077477187500001,24.882831508789064Q11.007717187499999,24.351581508789064,12.0619771875,24.351581508789064Q13.1162371875,24.351581508789064,14.0464771875,24.882831508789064Q14.9767171875,25.445331508789064,15.5038171875,26.351581508789064Q16.031017187499998,27.257831508789064,16.031017187499998,28.35158150878906Z" fill="#FCD34D" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></g></svg>
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/src/assets/svgs/lightBulb.svg b/EdgeCraftRAG/ui/vue/src/assets/svgs/lightBulb.svg
new file mode 100644
index 0000000000..e0a41adcda
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/svgs/lightBulb.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="10.5" height="14" viewBox="0 0 10.5 14"><defs><clipPath id="master_svg0_2_264"><rect x="0" y="0" width="10.5" height="14" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_2_264)"><g transform="matrix(1,0,0,-1,0,28.6015625)"><g><path d="M7.63636,17.80078125Q8.14347,19.05859125,9.09801,20.12500125Q9.09801,20.15234125,9.09801,20.15234125Q9.33665,20.45312125,9.57528,20.75390125Q10.4702,21.92969125,10.5,23.48828125Q10.4403,25.539081250000002,8.94886,26.87888125Q7.48722,28.24608125,5.25,28.30078125Q3.01278,28.24608125,1.55114,26.87888125Q0.0596591,25.539081250000002,0,23.48828125Q0.0298295,21.92969125,0.924716,20.75390125Q1.16335,20.45312125,1.40199,20.17969125Q1.40199,20.15234125,1.40199,20.15234125Q2.35653,19.08594125,2.86364,17.80078125L7.63636,17.80078125ZM5.25,14.30078125Q6.2642,14.32812505,6.95028,14.92968725Q7.60653,15.55859125,7.63636,16.48828125L7.63636,16.92578125L2.86364,16.92578125L2.86364,16.48828125Q2.89347,15.55859125,3.54972,14.92968725Q4.2358,14.32812505,5.25,14.30078125ZM2.86364,23.48828125Q2.83381,23.078121250000002,2.38636,23.05078125Q1.93892,23.078121250000002,1.90909,23.48828125Q1.93892,24.80078125,2.89347,25.64848125Q3.81818,26.52348125,5.25,26.55078125Q5.69744,26.52348125,5.72727,26.11328125Q5.69744,25.70308125,5.25,25.67578125Q4.2358,25.64848125,3.54972,25.04688125Q2.89347,24.41798125,2.86364,23.48828125Z" fill="#EAB308" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg>
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/src/assets/svgs/uploaded.svg b/EdgeCraftRAG/ui/vue/src/assets/svgs/uploaded.svg
new file mode 100644
index 0000000000..cdaaf1ff85
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/assets/svgs/uploaded.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736912622846" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1753" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M692.312 956.978c-59.656 0-112.963-25.778-148.221-66.23H214.286V133.034h400.115v187.818h197.413v310.98c42.357 31.932 70.176 80.457 72.544 135.214v-510.52L674.346 64.223H141.742v895.335h742.615V782.244c-4.202 97.174-88.568 174.734-192.045 174.734z" fill="#666666" p-id="1754"></path><path d="M301.326 418.477h437.601v67.091H301.326zM301.326 578.493h219.957v67.091H301.326zM301.326 738.509h138.34V805.6h-138.34z" fill="#666666" p-id="1755"></path><path d="M811.814 631.832c-32.811-24.735-74.334-39.519-119.502-39.519-106.162 0-192.224 81.633-192.224 182.333 0 44.113 16.517 84.567 44.003 116.103 35.258 40.452 88.565 66.23 148.221 66.23 103.477 0 187.843-77.56 192.045-174.734 0.109-2.521 0.179-5.052 0.179-7.599s-0.07-5.078-0.179-7.599c-2.368-54.759-30.186-103.283-72.543-135.215zM660.869 888.809l-38.472-36.492 0.92-0.872-69.159-65.6 38.472-36.492 69.159 65.6 130.205-123.505 38.472 36.492-169.597 160.869z" fill="#50A135" p-id="1756"></path><path d="M791.994 691.447L661.789 814.952l-69.159-65.6-38.472 36.492 69.159 65.6-0.92 0.873 38.472 36.492L830.466 727.94z" fill="#FFFFFF" p-id="1757"></path></svg>
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/src/auto-imports.d.ts b/EdgeCraftRAG/ui/vue/src/auto-imports.d.ts
new file mode 100644
index 0000000000..d6fd8da012
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/auto-imports.d.ts
@@ -0,0 +1,106 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+// biome-ignore lint: disable
+export {}
+declare global {
+  const EffectScope: (typeof import("vue"))["EffectScope"];
+  const acceptHMRUpdate: (typeof import("pinia"))["acceptHMRUpdate"];
+  const computed: (typeof import("vue"))["computed"];
+  const createApp: (typeof import("vue"))["createApp"];
+  const createPinia: (typeof import("pinia"))["createPinia"];
+  const customRef: (typeof import("vue"))["customRef"];
+  const defineAsyncComponent: (typeof import("vue"))["defineAsyncComponent"];
+  const defineComponent: (typeof import("vue"))["defineComponent"];
+  const defineStore: (typeof import("pinia"))["defineStore"];
+  const effectScope: (typeof import("vue"))["effectScope"];
+  const getActivePinia: (typeof import("pinia"))["getActivePinia"];
+  const getCurrentInstance: (typeof import("vue"))["getCurrentInstance"];
+  const getCurrentScope: (typeof import("vue"))["getCurrentScope"];
+  const h: (typeof import("vue"))["h"];
+  const inject: (typeof import("vue"))["inject"];
+  const isProxy: (typeof import("vue"))["isProxy"];
+  const isReactive: (typeof import("vue"))["isReactive"];
+  const isReadonly: (typeof import("vue"))["isReadonly"];
+  const isRef: (typeof import("vue"))["isRef"];
+  const mapActions: (typeof import("pinia"))["mapActions"];
+  const mapGetters: (typeof import("pinia"))["mapGetters"];
+  const mapState: (typeof import("pinia"))["mapState"];
+  const mapStores: (typeof import("pinia"))["mapStores"];
+  const mapWritableState: (typeof import("pinia"))["mapWritableState"];
+  const markRaw: (typeof import("vue"))["markRaw"];
+  const nextTick: (typeof import("vue"))["nextTick"];
+  const onActivated: (typeof import("vue"))["onActivated"];
+  const onBeforeMount: (typeof import("vue"))["onBeforeMount"];
+  const onBeforeRouteLeave: (typeof import("vue-router"))["onBeforeRouteLeave"];
+  const onBeforeRouteUpdate: (typeof import("vue-router"))["onBeforeRouteUpdate"];
+  const onBeforeUnmount: (typeof import("vue"))["onBeforeUnmount"];
+  const onBeforeUpdate: (typeof import("vue"))["onBeforeUpdate"];
+  const onDeactivated: (typeof import("vue"))["onDeactivated"];
+  const onErrorCaptured: (typeof import("vue"))["onErrorCaptured"];
+  const onMounted: (typeof import("vue"))["onMounted"];
+  const onRenderTracked: (typeof import("vue"))["onRenderTracked"];
+  const onRenderTriggered: (typeof import("vue"))["onRenderTriggered"];
+  const onScopeDispose: (typeof import("vue"))["onScopeDispose"];
+  const onServerPrefetch: (typeof import("vue"))["onServerPrefetch"];
+  const onUnmounted: (typeof import("vue"))["onUnmounted"];
+  const onUpdated: (typeof import("vue"))["onUpdated"];
+  const onWatcherCleanup: (typeof import("vue"))["onWatcherCleanup"];
+  const provide: (typeof import("vue"))["provide"];
+  const reactive: (typeof import("vue"))["reactive"];
+  const readonly: (typeof import("vue"))["readonly"];
+  const ref: (typeof import("vue"))["ref"];
+  const resolveComponent: (typeof import("vue"))["resolveComponent"];
+  const setActivePinia: (typeof import("pinia"))["setActivePinia"];
+  const setMapStoreSuffix: (typeof import("pinia"))["setMapStoreSuffix"];
+  const shallowReactive: (typeof import("vue"))["shallowReactive"];
+  const shallowReadonly: (typeof import("vue"))["shallowReadonly"];
+  const shallowRef: (typeof import("vue"))["shallowRef"];
+  const storeToRefs: (typeof import("pinia"))["storeToRefs"];
+  const toRaw: (typeof import("vue"))["toRaw"];
+  const toRef: (typeof import("vue"))["toRef"];
+  const toRefs: (typeof import("vue"))["toRefs"];
+  const toValue: (typeof import("vue"))["toValue"];
+  const triggerRef: (typeof import("vue"))["triggerRef"];
+  const unref: (typeof import("vue"))["unref"];
+  const useAttrs: (typeof import("vue"))["useAttrs"];
+  const useCssModule: (typeof import("vue"))["useCssModule"];
+  const useCssVars: (typeof import("vue"))["useCssVars"];
+  const useId: (typeof import("vue"))["useId"];
+  const useLink: (typeof import("vue-router"))["useLink"];
+  const useModel: (typeof import("vue"))["useModel"];
+  const useRoute: (typeof import("vue-router"))["useRoute"];
+  const useRouter: (typeof import("vue-router"))["useRouter"];
+  const useSlots: (typeof import("vue"))["useSlots"];
+  const useTemplateRef: (typeof import("vue"))["useTemplateRef"];
+  const watch: (typeof import("vue"))["watch"];
+  const watchEffect: (typeof import("vue"))["watchEffect"];
+  const watchPostEffect: (typeof import("vue"))["watchPostEffect"];
+  const watchSyncEffect: (typeof import("vue"))["watchSyncEffect"];
+}
+// for type re-export
+declare global {
+  // @ts-ignore
+  export type {
+    Component,
+    ComponentPublicInstance,
+    ComputedRef,
+    DirectiveBinding,
+    ExtractDefaultPropTypes,
+    ExtractPropTypes,
+    ExtractPublicPropTypes,
+    InjectionKey,
+    PropType,
+    Ref,
+    MaybeRef,
+    MaybeRefOrGetter,
+    VNode,
+    WritableComputedRef,
+  } from "vue";
+  import("vue");
+}
diff --git a/EdgeCraftRAG/ui/vue/src/components.d.ts b/EdgeCraftRAG/ui/vue/src/components.d.ts
new file mode 100644
index 0000000000..f79d1e1d69
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/components.d.ts
@@ -0,0 +1,20 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+/* eslint-disable */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+export {};
+
+/* prettier-ignore */
+declare module 'vue' {
+  export interface GlobalComponents {
+    AButton: typeof import('ant-design-vue/es')['Button']
+    AInput: typeof import('ant-design-vue/es')['Input']
+    ALayout: typeof import('ant-design-vue/es')['Layout']
+    ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+  }
+}
diff --git a/EdgeCraftRAG/ui/vue/src/components/FormTooltip.vue b/EdgeCraftRAG/ui/vue/src/components/FormTooltip.vue
new file mode 100644
index 0000000000..9fb0d2256a
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/components/FormTooltip.vue
@@ -0,0 +1,26 @@
+<template>
+  <a-tooltip placement="topRight" arrow-point-at-center>
+    <template #title>
+      {{ title }}
+    </template>
+    <InfoCircleOutlined
+      :style="{ fontSize: size, color: 'var(--font-text-color)' }"
+    />
+  </a-tooltip>
+</template>
+
+<script setup lang="ts" name="FormTooltip">
+import { InfoCircleOutlined } from "@ant-design/icons-vue";
+
+const props = defineProps({
+  title: {
+    type: String,
+  },
+  size: {
+    type: String,
+    default: () => "16px",
+  },
+});
+</script>
+
+<style lang="less" scoped></style>
diff --git a/EdgeCraftRAG/ui/vue/src/components/SvgIcon.vue b/EdgeCraftRAG/ui/vue/src/components/SvgIcon.vue
new file mode 100644
index 0000000000..59532f96a1
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/components/SvgIcon.vue
@@ -0,0 +1,56 @@
+<template>
+  <i :class="['icon-intel', 'iconfont', getIconName]" :style="iconStyle" />
+</template>
+
+<script setup lang="ts" name="svgIcon">
+import { computed } from "vue";
+
+const props = defineProps({
+  name: {
+    type: String,
+  },
+  size: {
+    type: Number,
+    default: () => 20,
+  },
+  color: {
+    type: String,
+    default: () => "#666666",
+  },
+  inherit: {
+    type: Boolean,
+    default: () => false,
+  },
+});
+
+const getIconName = computed(() => {
+  return props.inherit ? ` inherit-style ${props?.name}` : ` ${props?.name}`;
+});
+
+const iconStyle = computed(() => {
+  return {
+    width: `${props.size}px`,
+    height: `${props.size}px`,
+    display: "inline-block",
+    fontSize: `${props.size}px`,
+    overflow: "hidden",
+    color: props.inherit ? "inherit" : props.color,
+  };
+});
+</script>
+
+<style lang="less" scoped>
+.icon-intel {
+  display: inline-flex !important;
+  align-items: center;
+  &:hover {
+    color: var(--color-primary) !important;
+  }
+
+  &.inherit-style {
+    &:hover {
+      color: inherit !important;
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/enums/antEnum.ts b/EdgeCraftRAG/ui/vue/src/enums/antEnum.ts
new file mode 100644
index 0000000000..e2a23162cf
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/enums/antEnum.ts
@@ -0,0 +1,6 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+export enum AntTheme {
+  Black = "",
+}
diff --git a/EdgeCraftRAG/ui/vue/src/i18n/en.ts b/EdgeCraftRAG/ui/vue/src/i18n/en.ts
new file mode 100644
index 0000000000..39be2d81aa
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/i18n/en.ts
@@ -0,0 +1,26 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+export default {
+  headerTitle: "Edge Craft RAG based Q&A Chatbot",
+  common: {
+    update: "Update",
+    edit: "Edit",
+    delete: "Delete",
+    active: "Activate",
+    deactivate: "Deactivate",
+    cancel: "Cancel",
+    confirm: "Confirm",
+    save: "Save",
+  },
+  setting: {
+    systemStatus: "System Status",
+    pipelines: "Pipelines",
+    create: "Create Pipeline",
+    config: "Configuration Log",
+    jump: "Go to chatbot",
+    used: "Used",
+    notUsed: "Not Used",
+    import: "Import Pipeline",
+  },
+};
diff --git a/EdgeCraftRAG/ui/vue/src/i18n/index.ts b/EdgeCraftRAG/ui/vue/src/i18n/index.ts
new file mode 100644
index 0000000000..6d2fab0c57
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/i18n/index.ts
@@ -0,0 +1,22 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { createI18n } from "vue-i18n";
+import { Local } from "@/utils/storage";
+import enLocale from "ant-design-vue/es/locale/en_US";
+import zhLocale from "ant-design-vue/es/locale/zh_CN";
+import en from "./en";
+import zh from "./zh";
+
+const messages = {
+  "en-US": { ...en, ...enLocale },
+  "zh-CN": { ...zh, ...zhLocale },
+};
+
+const i18n = createI18n({
+  locale: Local.get("lang") || "en-US",
+  fallbackLocale: "en-US",
+  messages,
+});
+
+export default i18n;
diff --git a/EdgeCraftRAG/ui/vue/src/i18n/zh.ts b/EdgeCraftRAG/ui/vue/src/i18n/zh.ts
new file mode 100644
index 0000000000..39be2d81aa
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/i18n/zh.ts
@@ -0,0 +1,26 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+export default {
+  headerTitle: "Edge Craft RAG based Q&A Chatbot",
+  common: {
+    update: "Update",
+    edit: "Edit",
+    delete: "Delete",
+    active: "Activate",
+    deactivate: "Deactivate",
+    cancel: "Cancel",
+    confirm: "Confirm",
+    save: "Save",
+  },
+  setting: {
+    systemStatus: "System Status",
+    pipelines: "Pipelines",
+    create: "Create Pipeline",
+    config: "Configuration Log",
+    jump: "Go to chatbot",
+    used: "Used",
+    notUsed: "Not Used",
+    import: "Import Pipeline",
+  },
+};
diff --git a/EdgeCraftRAG/ui/vue/src/layout/Header.vue b/EdgeCraftRAG/ui/vue/src/layout/Header.vue
new file mode 100644
index 0000000000..c8cd68d69a
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/layout/Header.vue
@@ -0,0 +1,84 @@
+<template>
+  <div class="header-wrap">
+    <img :height="36" :src="headerLog" />
+    <div class="header-title">
+      <h1>{{ $t("headerTitle") }}</h1>
+    </div>
+    <div class="theme-switch" @click="handleThemeChange">
+      <div class="icon-wrap" :class="{ 'slider-on': isDark }">
+        <img v-if="!isDark" :src="LightIcon" alt="Sun" class="icon" />
+        <img v-else :src="DarkIcon" alt="Moon" class="icon" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup name="Header">
+import DarkIcon from "@/assets/svgs/dark-icon.svg";
+import headerLog from "@/assets/svgs/header-log.svg";
+import LightIcon from "@/assets/svgs/light-icon.svg";
+import { themeAppStore } from "@/store/theme";
+
+const themeStore = themeAppStore();
+const emit = defineEmits(["change-theme"]);
+const isDark = ref<boolean>(false);
+
+const handleThemeChange = () => {
+  isDark.value = !isDark.value;
+  const theme = isDark.value ? "dark" : "light";
+  const body = document.documentElement as HTMLElement;
+  body.setAttribute("data-theme", theme);
+  themeStore.toggleTheme(theme);
+};
+onMounted(() => {
+  isDark.value = themeStore.theme === "dark";
+});
+</script>
+
+<style scoped lang="less">
+.header-wrap {
+  max-width: 1440px;
+  min-width: 960px;
+  margin: 0 auto;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  color: var(--font-main-color);
+  .header-title {
+    font-family: var(--header-font-family);
+  }
+}
+.theme-switch {
+  position: relative;
+  width: 50px;
+  height: 24px;
+  background-color: var(--color-switch-theme);
+  border-radius: 15px;
+  cursor: pointer;
+  overflow: hidden;
+}
+
+.icon-wrap {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 30px;
+  height: 24px;
+  border-radius: 50%;
+  transition: transform 0.3s ease;
+}
+
+.slider-on {
+  transform: translateX(20px);
+}
+
+.icon {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 20px;
+  height: 20px;
+  transition: opacity 0.3s ease;
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/layout/Main.vue b/EdgeCraftRAG/ui/vue/src/layout/Main.vue
new file mode 100644
index 0000000000..8fde16cd9c
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/layout/Main.vue
@@ -0,0 +1,15 @@
+<template>
+  <a-layout class="layout-container">
+    <a-layout-header class="layout-header">
+      <Header />
+    </a-layout-header>
+    <a-layout-content class="layout-main">
+      <router-view class="layout-view" />
+    </a-layout-content>
+  </a-layout>
+</template>
+<script lang="ts" setup name="Main">
+import Header from './Header.vue';
+
+</script>
+<style lang="less"></style>
\ No newline at end of file
diff --git a/EdgeCraftRAG/ui/vue/src/main.ts b/EdgeCraftRAG/ui/vue/src/main.ts
new file mode 100644
index 0000000000..180b48b4df
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/main.ts
@@ -0,0 +1,43 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { createApp } from "vue";
+import App from "./App.vue";
+import i18n from "./i18n";
+import router from "./router";
+import pinia from "./store";
+import "@/theme/index.less";
+import { antConfig } from "@/utils/other";
+import dayjs from "dayjs";
+import "dayjs/locale/en";
+import "dayjs/locale/zh-cn";
+import "@/assets/iconFont/iconfont.css";
+import { Local } from "@/utils/storage";
+
+// setting dayjs language
+const setDayjsLocale = (locale: string) => {
+  if (locale === "en-US") {
+    dayjs.locale("en");
+  } else {
+    dayjs.locale("zh-cn");
+  }
+};
+
+const body = document.documentElement as HTMLElement;
+
+if (Local.get("themeInfo")?.theme === "dark") body.setAttribute("data-theme", "dark");
+else body.setAttribute("data-theme", "");
+
+// watch i18n update dayjs language
+watch(
+  () => i18n.global.locale,
+  (newLocale) => {
+    setDayjsLocale(newLocale);
+  },
+  { immediate: true },
+);
+const app = createApp(App);
+
+antConfig(app);
+
+app.use(router).use(pinia).use(i18n).mount("#app");
diff --git a/EdgeCraftRAG/ui/vue/src/router/index.ts b/EdgeCraftRAG/ui/vue/src/router/index.ts
new file mode 100644
index 0000000000..96a3a03760
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/router/index.ts
@@ -0,0 +1,12 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { createRouter, createWebHashHistory } from "vue-router";
+import { routeList, notFoundAndNoPower } from "./routes";
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes: [...notFoundAndNoPower, ...routeList],
+});
+
+export default router;
diff --git a/EdgeCraftRAG/ui/vue/src/router/routes.ts b/EdgeCraftRAG/ui/vue/src/router/routes.ts
new file mode 100644
index 0000000000..9b7ea9cbea
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/router/routes.ts
@@ -0,0 +1,38 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import Layout from "@/layout/Main.vue";
+
+export const routeList = [
+  {
+    path: "/",
+    name: "Main",
+    component: Layout,
+    redirect: "/pipeline",
+    children: [
+      {
+        path: "/pipeline",
+        name: "Pipeline",
+        component: () => import("@/views/pipeline/index.vue"),
+        meta: { title: "Pipeline" },
+      },
+      {
+        path: "/chatbot",
+        name: "Chatbot",
+        component: () => import("@/views/chatbot/index.vue"),
+        meta: { title: "Chatbot" },
+      },
+    ],
+  },
+];
+
+export const notFoundAndNoPower = [
+  {
+    path: "/:path(.*)*",
+    name: "notFound",
+    component: () => import("@/views/error/404.vue"),
+    meta: {
+      title: "404",
+    },
+  },
+];
diff --git a/EdgeCraftRAG/ui/vue/src/store/chatbot.ts b/EdgeCraftRAG/ui/vue/src/store/chatbot.ts
new file mode 100644
index 0000000000..334bfbdffd
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/store/chatbot.ts
@@ -0,0 +1,31 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { defineStore } from "pinia";
+
+const configuration = {
+  configuration: {
+    top_n: 5,
+    temperature: 0.1,
+    top_p: 1,
+    top_k: 50,
+    repetition_penalty: 1.1,
+    max_tokens: 512,
+    stream: true,
+  },
+};
+export const chatbotAppStore = defineStore("chatbot", {
+  state: () => configuration,
+  persist: {
+    key: "chatbotConfiguration",
+    storage: localStorage,
+  },
+  actions: {
+    setChatbotConfiguration(configuration: EmptyObjectType) {
+      this.configuration = {
+        ...this.configuration,
+        ...configuration,
+      };
+    },
+  },
+});
diff --git a/EdgeCraftRAG/ui/vue/src/store/index.ts b/EdgeCraftRAG/ui/vue/src/store/index.ts
new file mode 100644
index 0000000000..ecc8127c19
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/store/index.ts
@@ -0,0 +1,10 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { createPinia } from "pinia";
+import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
+
+const pinia = createPinia();
+pinia.use(piniaPluginPersistedstate);
+
+export default pinia;
diff --git a/EdgeCraftRAG/ui/vue/src/store/pipeline.ts b/EdgeCraftRAG/ui/vue/src/store/pipeline.ts
new file mode 100644
index 0000000000..b7c09c2d2b
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/store/pipeline.ts
@@ -0,0 +1,19 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { defineStore } from "pinia";
+
+export const pipelineAppStore = defineStore("pipeline", {
+  state: () => ({
+    activatedPipeline: "",
+  }),
+  persist: {
+    key: "pipelineInfo",
+    storage: localStorage,
+  },
+  actions: {
+    setPipelineActivate(name: string) {
+      this.activatedPipeline = name;
+    },
+  },
+});
diff --git a/EdgeCraftRAG/ui/vue/src/store/theme.ts b/EdgeCraftRAG/ui/vue/src/store/theme.ts
new file mode 100644
index 0000000000..9f99b7c531
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/store/theme.ts
@@ -0,0 +1,19 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { defineStore } from "pinia";
+
+export const themeAppStore = defineStore("theme", {
+  state: () => ({
+    theme: "light",
+  }),
+  persist: {
+    key: "themeInfo",
+    storage: localStorage,
+  },
+  actions: {
+    toggleTheme(type: string) {
+      this.theme = type;
+    },
+  },
+});
diff --git a/EdgeCraftRAG/ui/vue/src/store/user.ts b/EdgeCraftRAG/ui/vue/src/store/user.ts
new file mode 100644
index 0000000000..c9d8a13c9c
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/store/user.ts
@@ -0,0 +1,19 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { defineStore } from "pinia";
+
+export const userAppStore = defineStore("user", {
+  state: () => ({
+    userGuide: false,
+  }),
+  persist: {
+    key: "userInfo",
+    storage: localStorage,
+  },
+  actions: {
+    setUserGuideState(state: boolean) {
+      this.userGuide = state;
+    },
+  },
+});
diff --git a/EdgeCraftRAG/ui/vue/src/theme/ant.less b/EdgeCraftRAG/ui/vue/src/theme/ant.less
new file mode 100644
index 0000000000..89f82ed7bf
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/theme/ant.less
@@ -0,0 +1,156 @@
+.intel-btn {
+  & + .intel-btn {
+    margin-left: 12px;
+  }
+  &.black-primary-button {
+    background-color: var(--bg-black-color);
+    border: 1px solid var(--border-black-color);
+
+    &:hover {
+      background-color: var(--bg-black-hover-color);
+    }
+  }
+
+  &.intel-btn-primary.intel-btn-background-ghost:not(:disabled):hover {
+    background-color: var(--color-primaryBg);
+  }
+
+  &.intel-btn-default.intel-btn-dangerous:not(:disabled):hover {
+    background-color: var(--color-errorBg);
+  }
+
+  &.intel-btn-success {
+    color: var(--color-success);
+    border-color: var(--color-success);
+
+    &:hover {
+      background-color: var(--color-successBg);
+    }
+  }
+
+  &.intel-btn-warning {
+    color: var(--color-warning);
+    border-color: var(--color-warning);
+
+    &:hover {
+      background-color: var(--color-warningBg);
+    }
+  }
+}
+
+.intel-btn-sm {
+  font-size: 12px !important;
+}
+
+.intel-table {
+  width: 100%;
+
+  th {
+    .p-10 !important;
+  }
+
+  td {
+    padding: 12px 10px !important;
+  }
+}
+
+.intel-btn-background-ghost:disabled,
+.ant-btn-background-ghost:disabled {
+  background-color: rgba(0, 0, 0, 0.04);
+}
+
+.intel-modal {
+  .intel-modal-content {
+    padding: 0;
+    .intel-modal-close {
+      top: 14px;
+    }
+    .intel-modal-header {
+      padding: 10px 24px;
+      border-bottom: 1px solid var(--border-main-color);
+      .intel-modal-title {
+        color: var(--font-main-color);
+      }
+    }
+
+    .intel-modal-body {
+      padding: 8px 24px;
+    }
+
+    .intel-modal-footer {
+      padding: 10px 24px;
+      border-top: 1px solid var(--border-main-color);
+    }
+  }
+}
+.ant-modal {
+  .ant-modal-content {
+    .error-icon {
+      color: var(--color-error);
+    }
+    .ant-btn-dangerous {
+      background-color: var(--color-error);
+      color: var(--color-white);
+      &:hover {
+        background-color: var(--color-error-hover);
+        color: var(--color-white);
+      }
+    }
+  }
+}
+.ant-notification {
+  .ant-notification-notice-message {
+    font-weight: 600;
+  }
+}
+
+.intel-drawer {
+  .intel-drawer-header {
+    padding: 12px 20px;
+
+    .intel-drawer-header-title {
+      position: relative;
+
+      .intel-drawer-title {
+        font-size: 18px;
+      }
+
+      .intel-drawer-close {
+        position: absolute;
+        right: 0;
+        color: var(--font-text-color);
+        margin-inline-end: 0;
+        margin-inline-start: 12px;
+      }
+    }
+  }
+
+  .intel-drawer-body {
+    padding: 16px 20px 20px 20px;
+  }
+  .intel-drawer-footer {
+    text-align: end;
+  }
+}
+.intel-pagination {
+  margin-top: 24px;
+  text-align: end;
+}
+
+.intel-empty-normal .intel-empty-image {
+  display: none;
+}
+
+.intel-empty-normal .intel-empty-description {
+  color: var(--font-text-color);
+  &::before {
+    content: "";
+    display: block;
+    background-image: url("@/assets/images/noData.png");
+    background-size: contain;
+    background-repeat: no-repeat;
+    width: 220px;
+    height: 140px;
+    margin: 12px auto;
+  }
+}
diff --git a/EdgeCraftRAG/ui/vue/src/theme/common.less b/EdgeCraftRAG/ui/vue/src/theme/common.less
new file mode 100644
index 0000000000..5fd7f82488
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/theme/common.less
@@ -0,0 +1,179 @@
+.card-shadow {
+  box-shadow: 0px 1px 2px -1px var(--bg-box-shadow),
+    0px 1px 3px 0px var(--bg-box-shadow);
+}
+.flex-left {
+  display: flex;
+  align-items: center;
+}
+.flex-between {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.vertical-center {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.special-button-primary {
+  background: linear-gradient(
+    to bottom,
+    var(--color-primary-hover),
+    var(--color-primary)
+  );
+  color: var(--color-white);
+  border: none;
+  border-radius: 20px;
+  transition: background-color 0.3s ease;
+  font-size: 14px;
+  &:hover {
+    color: var(--color-white);
+    background: var(--color-primary-hover) !important;
+    transition-duration: 0s;
+  }
+}
+.form-wrap {
+  .intel-form-item-control-input-content {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+  }
+  .slider-wrap {
+    &:not(:last-child) {
+      padding-bottom: 16px;
+      margin-bottom: 12px;
+      border-bottom: 1px dashed var(--border-main-color);
+    }
+    .intel-slider {
+      width: calc(100% - 100px);
+    }
+    .intel-form-item-control {
+      .pl-6;
+    }
+    .intel-form-item-control-input-content {
+      align-items: flex-start;
+    }
+    .intel-slider-rail {
+      width: 100%;
+      height: 6px;
+      background-color: var(--color-primaryBg);
+    }
+    .intel-slider-step {
+      top: 4px;
+      .intel-slider-dot {
+        width: 10px;
+        height: 10px;
+      }
+    }
+
+    .intel-slider-track {
+      height: 6px;
+      border-radius: 3px;
+      background-color: var(--color-primary);
+    }
+
+    .intel-slider-handle {
+      width: 12px;
+      height: 12px;
+    }
+    .intel-input-number {
+      width: 70px;
+      margin-left: 8px;
+      margin-top: 4px;
+      .intel-input-number-input {
+        height: 26px;
+      }
+    }
+    .anticon-info-circle {
+      margin-top: 10px;
+    }
+  }
+}
+.edit-dialog {
+  overflow: hidden;
+  .intel-modal-header,
+  .intel-modal-footer {
+    display: none !important;
+  }
+  .intel-modal-body {
+    padding: 0 !important;
+  }
+}
+.import-dialog {
+  overflow: hidden;
+  .intel-modal-body {
+    padding: 16px 24px 24px !important;
+  }
+}
+.centered-model {
+  .ant-modal-confirm-btns {
+    text-align: center;
+  }
+}
+.option-introduction {
+  position: relative;
+  top: -16px;
+  width: calc(100% - 24px);
+  padding: 0 4px;
+  line-height: 24px;
+  border-radius: 6px;
+  display: flex;
+  gap: 4px;
+  align-items: flex-start;
+  color: var(--color-primary);
+  background-color: var(--color-primaryBg);
+  .anticon {
+    position: relative;
+    top: 5px;
+  }
+}
+.loopStyle(@counter) when (@counter > 0) {
+  .p-@{counter} {
+    padding: (1px * @counter);
+  }
+
+  .pt-@{counter} {
+    padding-top: (1px * @counter);
+  }
+
+  .pr-@{counter} {
+    padding-right: (1px * @counter);
+  }
+
+  .pb-@{counter} {
+    padding-bottom: (1px * @counter);
+  }
+
+  .pl-@{counter} {
+    padding-left: (1px * @counter);
+  }
+
+  .m-@{counter} {
+    margin: (1px * @counter);
+  }
+
+  .mt-@{counter} {
+    margin-top: (1px * @counter);
+  }
+
+  .mr-@{counter} {
+    margin-right: (1px * @counter);
+  }
+
+  .mb-@{counter} {
+    margin-bottom: (1px * @counter);
+  }
+
+  .ml-@{counter} {
+    margin-left: (1px * @counter);
+  }
+
+  .fs-@{counter} {
+    font-size: (1px * @counter);
+  }
+
+  .loopStyle((@counter - 1));
+}
+
+.loopStyle(100);
diff --git a/EdgeCraftRAG/ui/vue/src/theme/index.less b/EdgeCraftRAG/ui/vue/src/theme/index.less
new file mode 100644
index 0000000000..14c4a6da13
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/theme/index.less
@@ -0,0 +1,6 @@
+@import "./variables.less";
+@import "./layout.less";
+@import "./ant.less";
+@import "./common.less";
+@import "./loading.less";
+@import "./markdown.less";
diff --git a/EdgeCraftRAG/ui/vue/src/theme/layout.less b/EdgeCraftRAG/ui/vue/src/theme/layout.less
new file mode 100644
index 0000000000..d27f45344d
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/theme/layout.less
@@ -0,0 +1,104 @@
+* {
+  box-sizing: border-box;
+  outline: none !important;
+}
+
+html,
+body,
+#app {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  font-family: var(--body-font-family);
+  font-weight: 400;
+  -webkit-font-smoothing: antialiased;
+  -webkit-tap-highlight-color: transparent;
+  background-color: var(--bg-main-color);
+  font-size: 14px;
+  color: var(--font-text-color);
+  overflow: hidden;
+  position: relative;
+}
+
+h1,
+p {
+  margin: 0;
+  padding: 0;
+}
+div[aria-hidden="true"] {
+  display: none !important;
+}
+.layout-container {
+  width: 100%;
+  height: 100%;
+
+  .layout-header {
+    position: relative;
+    z-index: 10;
+    background-color: var(--bg-content-color);
+    box-shadow: 0px 1px 2px 0px var(--bg-box-shadow);
+  }
+
+  .layout-main {
+    overflow: auto;
+    width: 100%;
+    padding: 20px 80px;
+
+    .layout-view {
+      max-width: 1440px;
+      min-width: 1100px;
+      margin: 0 auto;
+    }
+  }
+  @media (max-width: 1100px) {
+    .layout-main {
+      padding: 20px;
+    }
+  }
+}
+
+/* 宽高 100%
+------------------------------- */
+.w100 {
+  width: 100% !important;
+}
+
+.h100 {
+  height: 100% !important;
+}
+
+.vh100 {
+  height: 100vh !important;
+}
+
+.max100vh {
+  max-height: 100vh !important;
+}
+
+.min100vh {
+  min-height: 100vh !important;
+}
+
+// 滚动条凹槽的颜色，还可以设置边框属性
+&::-webkit-scrollbar-track-piece {
+  background-color: var(--bg-content-color);
+}
+
+// 滚动条的宽度
+&::-webkit-scrollbar {
+  width: 9px;
+  height: 9px;
+}
+
+// 滚动条的设置
+&::-webkit-scrollbar-thumb {
+  background-color: var(--bg-scrollbar);
+  background-clip: padding-box;
+  min-height: 28px;
+  border-radius: 3px;
+}
+
+&::-webkit-scrollbar-thumb:hover {
+  background-color: var(--bg-scrollbar-hover);
+}
diff --git a/EdgeCraftRAG/ui/vue/src/theme/loading.less b/EdgeCraftRAG/ui/vue/src/theme/loading.less
new file mode 100644
index 0000000000..d0c279e664
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/theme/loading.less
@@ -0,0 +1,54 @@
+.loading-next {
+	width: 100%;
+	height: 100%;
+  position: fixed;
+  z-index: 9999;
+  background-color: var(--bg-loading-color);
+}
+.loading-next .loading-next-box {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	transform: translate(-50%, -50%);
+}
+.loading-next .loading-next-box-warp {
+	width: 80px;
+	height: 80px;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item {
+	width: 33.333333%;
+	height: 33.333333%;
+	background: var(--color-primary);
+	float: left;
+	animation: loading-next-animation 1.2s infinite ease;
+	border-radius: 1px;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(7) {
+	animation-delay: 0s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(4),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(8) {
+	animation-delay: 0.1s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(1),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(5),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(9) {
+	animation-delay: 0.2s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(2),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(6) {
+	animation-delay: 0.3s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(3) {
+	animation-delay: 0.4s;
+}
+@keyframes loading-next-animation {
+	0%,
+	70%,
+	100% {
+		transform: scale3D(1, 1, 1);
+	}
+	35% {
+		transform: scale3D(0, 0, 1);
+	}
+}
diff --git a/EdgeCraftRAG/ui/vue/src/theme/markdown.less b/EdgeCraftRAG/ui/vue/src/theme/markdown.less
new file mode 100644
index 0000000000..918650ff33
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/theme/markdown.less
@@ -0,0 +1,72 @@
+.intel-markdown {
+  color: var(--font-main-color);
+  strong {
+    font-weight: 600;
+  }
+  code,
+  pre,
+  samp,
+  tt {
+    font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
+      Liberation Mono, monospace;
+  }
+  code,
+  tt {
+    background-color: var(--color-primaryBg);
+    border-radius: 6px;
+    font-size: 85%;
+    margin: 0;
+    padding: 0.2em 0.4em;
+    white-space: break-spaces;
+  }
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    font-weight: 600;
+    line-height: 1.8;
+    margin: 0.6em 0;
+  }
+
+  h1 {
+    font-size: 1.3em;
+  }
+
+  h2 {
+    font-size: 1.15em;
+  }
+
+  h3,
+  h4,
+  h5 {
+    font-size: 1em;
+  }
+  a {
+    background-color: transparent;
+    color: var(--color-primary-hover);
+    text-decoration: none;
+    &:hover {
+      text-decoration: underline;
+    }
+    &:focus {
+      box-shadow: none;
+      outline: 2px solid var(--color-primary-hover);
+      outline-offset: -2px;
+    }
+  }
+  img {
+    width: 100%;
+    max-width: 320px;
+    height: auto;
+    cursor: pointer;
+  }
+  img[align="right"] {
+    padding-left: 20px;
+  }
+
+  img[align="left"] {
+    padding-right: 20px;
+  }
+}
diff --git a/EdgeCraftRAG/ui/vue/src/theme/variables.less b/EdgeCraftRAG/ui/vue/src/theme/variables.less
new file mode 100644
index 0000000000..e8914623af
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/theme/variables.less
@@ -0,0 +1,77 @@
+:root {
+  --body-font-family: "IntelOneTextNormal", system-ui, Segoe UI, Helvetica,
+    sans-serif;
+  --header-font-family: "IntelOneDisplayNormal";
+  --color-white: #ffffff;
+  --color-primary: #00377c;
+  --color-primary-hover: #0054ae;
+  --color-primaryBg: #e0eaff;
+  --color-error: #ce0000;
+  --color-error-hover: #ff5d52;
+  --color-info: #aaaaaa;
+  --color-success: #179958;
+  --color-warning: #faad14;
+  --color-big-icon: #111111;
+  --color-successBg: #d6ffe8;
+  --color-warningBg: #feefd0;
+  --color-errorBg: #ffa3a3;
+  --color-infoBg: #ffffff;
+  --bg-main-color: #f5f5f5;
+  --bg-card-color: #f9f9f9;
+  --bg-loading-color: rgba(0, 0, 0, 0.45);
+  --bg-content-color: var(--color-white);
+  --font-main-color: #333333;
+  --font-text-color: #595959;
+  --font-info-color: #808080;
+  --font-tip-color: #999999;
+  --bg-scrollbar: #dddddd;
+  --bg-scrollbar-hover: #bbbbbb;
+  --bg-box-shadow: rgba(0, 0, 0, 0.05);
+  --bg-gradient-shadow: 0px 4px 6px -4px rgba(0, 0, 0, 0.1),
+    0px 10px 15px -3px rgba(0, 0, 0, 0.1);
+  --menu-bg: var(--bg-main-color);
+  --color-switch-theme: #e5e7eb;
+
+  //边框
+  --border-main-color: #e5e7eb;
+
+  //黑色按钮
+  --bg-black-color: #434343;
+  --bg-black-hover-color: #595959;
+  --bg-black-active-color: #262626;
+  --border-black-color: #434343;
+}
+
+[data-theme="dark"] {
+  --bg-main-color: #1e1e1e;
+  --bg-card-color: #2d2d2d;
+  --bg-loading-color: rgba(255, 255, 255, 0.45);
+  --bg-content-color: #141414;
+  --font-main-color: #ffffff;
+  --font-text-color: #e9e9e9;
+  --font-info-color: #aeaeae;
+  --font-tip-color: #aeaeae;
+  --bg-scrollbar: #dddddd;
+  --bg-scrollbar-hover: #bbbbbb;
+  --color-primary: #0054ae;
+  --color-primary-hover: #1668dc;
+  --color-primaryBg: #95b5fa;
+  --bg-box-shadow: rgba(109, 153, 233, 0.05);
+  --bg-gradient-shadow: 0px 4px 6px -4px rgba(255, 255, 255, 0.1),
+    0px 5px 8px 1px rgba(255, 255, 255, 0.1);
+  --menu-bg: #3e3e3e;
+  --color-big-icon: #ffffff;
+  --color-switch-theme: var(--color-primary-hover);
+
+  //边框
+  --border-main-color: #2b2b2b;
+
+  //黑色按钮
+  --bg-black-color: #434343;
+  --bg-black-hover-color: #595959;
+  --bg-black-active-color: #262626;
+  --border-black-color: #434343;
+}
+
+@use "ant-design-vue/es/style/themes/default.less";
+@use "ant-design-vue/es/style/themes/dark.less";
diff --git a/EdgeCraftRAG/ui/vue/src/types/axios.d.ts b/EdgeCraftRAG/ui/vue/src/types/axios.d.ts
new file mode 100644
index 0000000000..91a3d6b609
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/types/axios.d.ts
@@ -0,0 +1,21 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import "axios";
+
+// extend axios type
+declare module "axios" {
+  export interface AxiosResponse<T = any> {
+    code: number;
+    data: T;
+    message: string;
+    type?: string;
+    showLoading?: boolean;
+    showSuccessMsg?: boolean;
+    successMsg?: string;
+    [key: string]: T;
+  }
+  export interface AxiosRequestConfig<T = any> {
+    [key: string]: T;
+  }
+}
diff --git a/EdgeCraftRAG/ui/vue/src/types/global.d.ts b/EdgeCraftRAG/ui/vue/src/types/global.d.ts
new file mode 100644
index 0000000000..3bf3effaf4
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/types/global.d.ts
@@ -0,0 +1,133 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+// Declaration of External npm Plugin Modules
+declare module "js-cookie";
+declare module "qs";
+declare module "vite-plugin-vue-setup-extend-plus";
+declare module "vue3-markdown-it";
+declare module "event-source-polyfill";
+declare module "@kangc/v-md-editor/lib/preview";
+declare module "prismjs";
+
+// Declaration module to prevent errors when importing files
+declare module "*.json";
+declare module "*.png";
+declare module "*.jpg";
+declare module "*.less";
+declare module "*.ts";
+declare module "*.js";
+declare module "lodash";
+
+// Declaration vue file
+declare module "*.vue" {
+  import type { DefineComponent } from "vue";
+  const component: DefineComponent<{}, {}, any>;
+  export default component;
+}
+
+// Global Variable
+/* eslint-disable */
+declare interface Window {
+  nextLoading: boolean;
+  BMAP_SATELLITE_MAP: any;
+  BMap: any;
+}
+
+// Declaration Router
+declare type RouteItem<T = any> = {
+  path: string;
+  name?: string | symbol | undefined | null;
+  redirect?: string;
+  k?: T;
+  meta?: {
+    title?: string;
+    isLink?: string;
+    isHide?: boolean;
+    isKeepAlive?: boolean;
+    isAffix?: boolean;
+    isIframe?: boolean;
+    roles?: string[];
+    icon?: string;
+    isDynamic?: boolean;
+    isDynamicPath?: string;
+    isIframeOpen?: string;
+    loading?: boolean;
+  };
+  children: T[];
+  query?: { [key: string]: T };
+  params?: { [key: string]: T };
+  contextMenuClickId?: string | number;
+  commonUrl?: string;
+  isFnClick?: boolean;
+  url?: string;
+  transUrl?: string;
+  title?: string;
+  id?: string | number;
+};
+
+// Route to from
+declare interface RouteToFrom<T = any> extends RouteItem {
+  path?: string;
+  children?: T[];
+}
+
+// RouteItems
+declare type RouteItems<T extends RouteItem = any> = T[];
+
+//  ref
+declare type RefType<T = any> = T | null;
+
+//  HTMLElement
+declare type HtmlType = HTMLElement | string | undefined | null;
+
+// Array
+declare type EmptyArrayType<T = any> = T[];
+
+// Object
+declare type EmptyObjectType<T = any> = {
+  [key: string]: T;
+};
+
+//  Select option
+declare type SelectOptionType = {
+  value: string | number;
+  label: string | number;
+};
+
+// Table
+declare interface TableType<T = any> {
+  total: number;
+  data: T[];
+  param: {
+    pageNum: number;
+    pageSize: number;
+    [key: string]: T;
+  };
+}
+
+// Table Pagination
+declare interface paginationType<T = any> {
+  total: number;
+  pageNum: number;
+  pageSize: number;
+  [key: string]: T;
+}
+
+// Table Columns
+declare type TableColumns<T = any> = {
+  title: string;
+  key?: string;
+  dataIndex: string | string[];
+  width?: string;
+  align?: string;
+  ellipsis?: booleanstring;
+  fixed?: boolean | string;
+  [key: string]: T;
+};
+
+// Dialog
+declare interface DialogType<T = any> {
+  visible: boolean;
+  [key: string]: T;
+}
diff --git a/EdgeCraftRAG/ui/vue/src/utils/antTheme.ts b/EdgeCraftRAG/ui/vue/src/utils/antTheme.ts
new file mode 100644
index 0000000000..c642e78c6d
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/utils/antTheme.ts
@@ -0,0 +1,62 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { theme } from "ant-design-vue";
+export const antTheme = {
+  black: {
+    token: {
+      colorPrimary: "#111111",
+    },
+  },
+  subTheme: {
+    token: {
+      colorPrimary: "#0054AE",
+    },
+  },
+  success: {
+    token: {
+      colorPrimary: "#008A00",
+    },
+  },
+  danger: {
+    token: {
+      colorPrimary: "#ce0000",
+    },
+  },
+  light: {
+    algorithm: theme.defaultAlgorithm,
+    inherit: false,
+    token: {
+      colorPrimary: "#00377C",
+      colorPrimaryBg: "#E0EAFF",
+      colorError: "#EA0000",
+      colorInfo: "#AAAAAA",
+      colorSuccess: "#179958",
+      colorWarning: "#faad14",
+      colorTextBase: "#131313",
+      colorSuccessBg: "#D6FFE8",
+      colorWarningBg: "#feefd0",
+      colorErrorBg: "#FFA3A3",
+      colorInfoBg: "#EEEEEE",
+    },
+    cssVar: true,
+  },
+  dark: {
+    algorithm: theme.darkAlgorithm,
+    inherit: false,
+    // token: {
+    //   colorPrimary: "#0054ae",
+    //   colorPrimaryBg: "#0068B5",
+    //   colorError: "#EA0000",
+    //   colorInfo: "#AAAAAA",
+    //   colorSuccess: "#179958",
+    //   colorWarning: "#faad14",
+    //   colorTextBase: "#ffffff",
+    //   colorSuccessBg: "#D6FFE8",
+    //   colorWarningBg: "#feefd0",
+    //   colorErrorBg: "#FFA3A3",
+    //   colorInfoBg: "#EEEEEE",
+    // },
+    cssVar: true,
+  },
+};
diff --git a/EdgeCraftRAG/ui/vue/src/utils/common.ts b/EdgeCraftRAG/ui/vue/src/utils/common.ts
new file mode 100644
index 0000000000..efdf142be9
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/utils/common.ts
@@ -0,0 +1,21 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { inject } from "vue";
+import { customNotification } from "./notification";
+
+export const useNotification = () => {
+  const customNotificationInjected = inject<typeof customNotification>("customNotification");
+
+  if (!customNotificationInjected) {
+    throw new Error("Notification service not provided");
+  }
+  return {
+    antNotification: customNotificationInjected,
+  };
+};
+
+export const formatDecimals = (num: number, decimalPlaces: number = 2) => {
+  const factor = Math.pow(10, decimalPlaces);
+  return Math.round(num * factor) / factor;
+};
diff --git a/EdgeCraftRAG/ui/vue/src/utils/loading.ts b/EdgeCraftRAG/ui/vue/src/utils/loading.ts
new file mode 100644
index 0000000000..e0be1ba8da
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/utils/loading.ts
@@ -0,0 +1,46 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { nextTick } from "vue";
+import "@/theme/loading.less";
+
+/**
+ * Global Loading
+ * @method start create loading
+ * @method done remove loading
+ */
+export const NextLoading = {
+  start: () => {
+    const bodys: Element = document.body;
+    const div = <HTMLElement>document.createElement("div");
+    div.setAttribute("class", "loading-next");
+    const htmls = `
+			<div class="loading-next-box">
+				<div class="loading-next-box-warp">
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+				</div>
+			</div>
+		`;
+    div.innerHTML = htmls;
+    bodys.insertBefore(div, bodys.childNodes[0]);
+    window.nextLoading = true;
+  },
+
+  done: (time: number = 0) => {
+    nextTick(() => {
+      setTimeout(() => {
+        window.nextLoading = false;
+        const el = <HTMLElement>document.querySelector(".loading-next");
+        el?.parentNode?.removeChild(el);
+      }, time);
+    });
+  },
+};
diff --git a/EdgeCraftRAG/ui/vue/src/utils/notification.ts b/EdgeCraftRAG/ui/vue/src/utils/notification.ts
new file mode 100644
index 0000000000..de5b2c9cae
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/utils/notification.ts
@@ -0,0 +1,40 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import { CheckCircleFilled, CloseCircleFilled, ExclamationCircleFilled, InfoCircleFilled } from "@ant-design/icons-vue";
+import { notification } from "ant-design-vue";
+interface NotificationIcon {
+  icon: any;
+  color: string;
+}
+
+const getNotificationIcon = (type: string): NotificationIcon => {
+  switch (type) {
+    case "success":
+      return { icon: CheckCircleFilled, color: "--color-success" };
+    case "error":
+      return { icon: CloseCircleFilled, color: "--color-error" };
+    case "warning":
+      return { icon: ExclamationCircleFilled, color: "--color-warning" };
+    case "info":
+      return { icon: InfoCircleFilled, color: "--color-info" };
+    default:
+      return { icon: null, color: "" };
+  }
+};
+
+export const customNotification = (
+  type: "success" | "warning" | "error" | "info",
+  message: string,
+  description: string,
+) => {
+  const { icon, color } = getNotificationIcon(type);
+
+  const styledIcon = icon ? h(icon, { style: { color: `var(${color})` } }) : null;
+
+  notification[type]({
+    message: message,
+    description: description,
+    icon: styledIcon,
+  });
+};
diff --git a/EdgeCraftRAG/ui/vue/src/utils/other.ts b/EdgeCraftRAG/ui/vue/src/utils/other.ts
new file mode 100644
index 0000000000..cb5d56bc6a
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/utils/other.ts
@@ -0,0 +1,24 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import serviceManager from "@/utils/serviceManager";
+import { ConfigProvider, notification } from "ant-design-vue";
+import type { App } from "vue";
+import { customNotification } from "./notification";
+import FormTooltip from "@/components/FormTooltip.vue";
+import SvgIcon from "@/components/SvgIcon.vue";
+
+//Global Notification
+notification.config({
+  placement: "topRight",
+  duration: 3,
+  maxCount: 3,
+});
+
+export const antConfig = (app: App) => {
+  app.component("SvgIcon", SvgIcon);
+  app.component("FormTooltip", FormTooltip);
+  app.use(ConfigProvider);
+  app.provide("customNotification", customNotification);
+  serviceManager.registerService("antNotification", customNotification);
+};
diff --git a/EdgeCraftRAG/ui/vue/src/utils/serviceManager.ts b/EdgeCraftRAG/ui/vue/src/utils/serviceManager.ts
new file mode 100644
index 0000000000..cfe6f5b673
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/utils/serviceManager.ts
@@ -0,0 +1,30 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+type ServiceMap = {
+  antNotification: (type: "success" | "warning" | "error" | "info", message: string, description?: string) => void;
+};
+
+class ServiceManager {
+  private static instance: ServiceManager;
+  private services: { [P in keyof ServiceMap]?: ServiceMap[P] } = {};
+
+  private constructor() {}
+
+  public static getInstance(): ServiceManager {
+    if (!ServiceManager.instance) {
+      ServiceManager.instance = new ServiceManager();
+    }
+    return ServiceManager.instance;
+  }
+
+  public registerService<K extends keyof ServiceMap>(name: K, service: ServiceMap[K]): void {
+    this.services[name] = service;
+  }
+
+  public getService<K extends keyof ServiceMap>(name: K): ServiceMap[K] | undefined {
+    return this.services[name];
+  }
+}
+
+export default ServiceManager.getInstance();
diff --git a/EdgeCraftRAG/ui/vue/src/utils/storage.ts b/EdgeCraftRAG/ui/vue/src/utils/storage.ts
new file mode 100644
index 0000000000..2c5e6bd80f
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/utils/storage.ts
@@ -0,0 +1,64 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import Cookies from "js-cookie";
+
+/**
+ * window.localStorage
+ * @method set
+ * @method get
+ * @method remove
+ * @method clear
+ */
+export const Local = {
+  setKey(key: string) {
+    return `${key}`;
+  },
+
+  set<T>(key: string, val: T) {
+    window.localStorage.setItem(Local.setKey(key), JSON.stringify(val));
+  },
+
+  get(key: string) {
+    let json = <string>window.localStorage.getItem(Local.setKey(key));
+    return JSON.parse(json);
+  },
+
+  remove(key: string) {
+    window.localStorage.removeItem(Local.setKey(key));
+  },
+
+  clear() {
+    window.localStorage.clear();
+  },
+};
+
+/**
+ * window.sessionStorage
+ * @method set
+ * @method get
+ * @method remove
+ * @method clear
+ */
+export const Session = {
+  set<T>(key: string, val: T) {
+    if (key === "token") return Cookies.set(key, val);
+    window.sessionStorage.setItem(Local.setKey(key), JSON.stringify(val));
+  },
+
+  get(key: string) {
+    if (key === "token") return Cookies.get(key);
+    let json = <string>window.sessionStorage.getItem(Local.setKey(key));
+    return JSON.parse(json);
+  },
+
+  remove(key: string) {
+    if (key === "token") return Cookies.remove(key);
+    window.sessionStorage.removeItem(Local.setKey(key));
+  },
+
+  clear() {
+    Cookies.remove("token");
+    window.sessionStorage.clear();
+  },
+};
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/components/Chatbot.vue b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/Chatbot.vue
new file mode 100644
index 0000000000..34464506bd
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/Chatbot.vue
@@ -0,0 +1,275 @@
+<template>
+  <div class="chatbot-wrap">
+    <div class="message-box" ref="scrollContainer">
+      <div ref="messageComponent" class="intel-markdown">
+        <div v-for="(msg, index) in messagesList" :key="index" :inResponse>
+          <MessageItem
+            :message="msg"
+            ref="messageRef"
+            :inResponse
+            @preview="hadleImagePreview"
+          />
+        </div>
+      </div>
+    </div>
+    <div class="input-wrap">
+      <div class="chatbot-tip">
+        <img :src="lightBulb" alt="" />
+        <span class="info-wrap">
+          Uploading relevant documents can help AI answer questions more
+          accurately
+        </span>
+      </div>
+      <a-input
+        size="large"
+        v-model:value="inputKeywords"
+        @pressEnter="handleSendMessage"
+        placeholder="Please enter your question..."
+      />
+      <div class="button-wrap">
+        <a-button
+          type="primary"
+          size="large"
+          :disabled="inResponse"
+          @click="handleSendMessage"
+        >
+          <SvgIcon
+            name="icon-send"
+            :size="22"
+            :style="{ color: 'var(--color-white)' }"
+          />
+        </a-button>
+        <a-config-provider :theme="antTheme.subTheme">
+          <a-button
+            v-if="false"
+            type="primary"
+            size="large"
+            @click="handleStopDisplay"
+          >
+            <SvgIcon
+              name="icon-stop"
+              :style="{ color: 'var(--color-white)' }"
+            />
+          </a-button>
+          <a-button
+            type="primary"
+            size="large"
+            :icon="h(DeleteFilled)"
+            @click="handleClearMessage"
+          >
+          </a-button>
+        </a-config-provider>
+      </div>
+    </div>
+  </div>
+  <a-image
+    :style="{ display: 'none' }"
+    :preview="{
+      visible: imgVisible,
+      onVisibleChange: handleImageVisible,
+    }"
+    :src="imageSrc"
+  />
+</template>
+
+<script lang="ts" setup name="Chatbot">
+import { getBenchmark } from "@/api/chatbot";
+import lightBulb from "@/assets/svgs/lightBulb.svg";
+import { antTheme } from "@/utils/antTheme";
+import { DeleteFilled } from "@ant-design/icons-vue";
+import _ from "lodash";
+import { h, onMounted, reactive, ref, onBeforeUnmount } from "vue";
+import { Benchmark, ConfigType, IMessage } from "../type";
+import MessageItem from "./MessageItem.vue";
+import { handleMessageSend } from "./SseService";
+import { pipelineAppStore } from "@/store/pipeline";
+
+const props = defineProps({
+  configuration: {
+    type: Object as PropType<ConfigType>,
+    required: true,
+    default: () => {},
+  },
+});
+
+const ENV_URL = import.meta.env;
+const pipelineStore = pipelineAppStore();
+const defaultBenchmark = reactive<Benchmark>({
+  generator: "",
+  postprocessor: "",
+  retriever: "",
+});
+
+const messagesList = ref<IMessage[]>([
+  {
+    author: "Bot",
+    content: "Hello! I am an Edge AI assistant. May I help you?",
+  },
+]);
+const inputKeywords = ref<string>("");
+const scrollContainer = ref<HTMLElement | null>(null);
+const messageComponent = ref<HTMLElement | null>(null);
+let resizeObserver: ResizeObserver | null = null;
+const messageRef = ref<any>(null);
+const inResponse = ref<boolean>(false);
+const imgVisible = ref<boolean>(false);
+const imageSrc = ref<string>("");
+
+const handleEnvUrl = () => {
+  const { VITE_CHATBOT_URL } = ENV_URL;
+
+  return `${VITE_CHATBOT_URL}v1/chatqna`;
+};
+
+const handleMessageDisplay = (data: any) => {
+  if (inResponse.value)
+    messagesList.value[messagesList.value?.length - 1].content = data;
+};
+const handleStreamEnd = () => {
+  handleStopDisplay();
+  queryBenchmark();
+};
+const toggleConnection = () => {
+  if (inResponse.value) {
+    handleMessageSend(
+      handleEnvUrl(),
+      formatFormParam(),
+      handleMessageDisplay,
+      handleStreamEnd
+    );
+  }
+};
+
+// Format parameter
+const formatFormParam = () => {
+  const { configuration } = props;
+
+  return Object.assign({}, configuration, {
+    messages: inputKeywords.value,
+  });
+};
+const handleSendMessage = async () => {
+  if (!inputKeywords.value.trim()) return;
+  messagesList.value.push(
+    {
+      author: "User",
+      content: inputKeywords.value,
+    },
+    {
+      author: "Bot",
+      content: "",
+      benchmark: _.cloneDeep(defaultBenchmark),
+    }
+  );
+  inResponse.value = true;
+  toggleConnection();
+  inputKeywords.value = "";
+};
+const handleStopDisplay = () => {
+  inResponse.value = false;
+};
+
+const handleClearMessage = () => {
+  messagesList.value.splice(1);
+};
+
+const handleResize = (entries: ResizeObserverEntry[]) => {
+  for (let entry of entries) {
+    if (scrollContainer.value) {
+      scrollContainer.value.scrollTop = entry.contentRect.height;
+    }
+  }
+};
+const queryBenchmark = async () => {
+  const { activatedPipeline } = pipelineStore;
+  const data = (await getBenchmark(activatedPipeline)) || {};
+
+  if (data["Benchmark enabled"]) {
+    const benchmarkData = data.last_benchmark_data || {};
+    if (benchmarkData.generator) {
+      const processedBenchmarkData = Object.fromEntries(
+        Object.entries(benchmarkData).map(([key, value]: any) => [
+          key,
+          parseFloat(value.toFixed(4)),
+        ])
+      );
+      messagesList.value[messagesList.value.length - 1].benchmark =
+        processedBenchmarkData;
+    }
+  }
+};
+const hadleImagePreview = (url: string) => {
+  imageSrc.value = url;
+  handleImageVisible(true);
+};
+const handleImageVisible = (value: boolean = false) => {
+  imgVisible.value = value;
+};
+onMounted(() => {
+  if (messageComponent.value) {
+    resizeObserver = new ResizeObserver(handleResize);
+    resizeObserver.observe(messageComponent.value);
+  }
+});
+
+onBeforeUnmount(() => {
+  if (resizeObserver && messageComponent.value) {
+    resizeObserver.unobserve(messageComponent.value);
+    resizeObserver = null;
+  }
+});
+</script>
+<style scoped lang="less">
+.chatbot-wrap {
+  border-radius: 6px;
+  background-color: var(--bg-content-color);
+  overflow: hidden;
+  padding-top: 4px;
+  .message-box {
+    height: 400px;
+    width: 100%;
+    overflow-y: auto;
+    padding: 20px 24px 0 24px;
+  }
+  .input-wrap {
+    display: flex;
+    align-items: center;
+    padding: 32px 24px 24px 24px;
+    gap: 16px;
+    position: relative;
+    .chatbot-tip {
+      .vertical-center;
+      position: absolute;
+      width: calc(100% - 48px);
+      top: 6px;
+      gap: 4px;
+      .info-wrap {
+        background-color: var(--bg-main-color);
+        color: var(--font-text-color);
+        line-height: 20px;
+        border-radius: 10px;
+        padding: 0 12px;
+      }
+    }
+    .button-wrap {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      .intel-btn,
+      .ant-btn {
+        width: 65px;
+        .icon-intel {
+          position: relative;
+          top: 1px;
+        }
+      }
+      .intel-btn-primary:disabled {
+        background-color: var(--color-info);
+        .icon-intel {
+          color: var(--color-primaryBg) !important;
+        }
+      }
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/components/ConfigDrawer.vue b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/ConfigDrawer.vue
new file mode 100644
index 0000000000..635058ed6e
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/ConfigDrawer.vue
@@ -0,0 +1,234 @@
+<template>
+  <a-drawer
+    v-model:open="drawerVisible"
+    title="Ceneration Configuration"
+    destroyOnClose
+    width="600px"
+    :keyboard="false"
+    :maskClosable="false"
+    @close="handleClose"
+  >
+    <a-form
+      :model="form"
+      :rules="rules"
+      name="basic"
+      layout="vertical"
+      ref="formRef"
+      autocomplete="off"
+      class="form-wrap"
+    >
+      <div class="module-wrap">
+        <div class="module-title">Retriever Configuration</div>
+        <a-form-item label="Rerank top n" name="top_n" class="slider-wrap">
+          <a-slider
+            v-model:value="form.top_n"
+            :min="1"
+            :max="10"
+            :marks="sliderMarks.top_n"
+          />
+          <div class="tips-wrap">
+            <InfoCircleFilled />Number of rerank results
+          </div>
+        </a-form-item>
+      </div>
+      <div class="module-wrap">
+        <div class="module-title">Generation Configuration</div>
+        <a-form-item label="Temperature" name="temperature" class="slider-wrap">
+          <a-slider
+            v-model:value="form.temperature"
+            :min="0"
+            :max="1"
+            :step="0.1"
+            :marks="sliderMarks.temperature"
+          />
+          <div class="tips-wrap">
+            <InfoCircleFilled />Higher values produce more diverse outputs
+          </div>
+        </a-form-item>
+        <a-form-item
+          label="Top-p (nucleus sampling)"
+          name="top_p"
+          class="slider-wrap"
+        >
+          <a-slider
+            v-model:value="form.top_p"
+            :min="0"
+            :max="1"
+            :step="0.1"
+            :marks="sliderMarks.temperature"
+          />
+          <div class="tips-wrap">
+            <InfoCircleFilled />Sample from the smallest possible set of tokens
+            whose cumulative probability exceeds top_p. Set to 1 to disable and
+            sample from all tokens.
+          </div>
+        </a-form-item>
+        <a-form-item label="Top-k" name="top_k" class="slider-wrap">
+          <a-slider
+            v-model:value="form.top_k"
+            :min="0"
+            :max="200"
+            :marks="sliderMarks.top_k"
+          />
+          <div class="tips-wrap">
+            <InfoCircleFilled />Sample from a shortlist of top-k tokens — 0 to
+            disable and sample from all tokens.
+          </div>
+        </a-form-item>
+        <a-form-item
+          label="Repetition Penalty"
+          name="repetition_penalty"
+          class="slider-wrap"
+        >
+          <a-slider
+            v-model:value="form.repetition_penalty"
+            :min="1"
+            :max="2"
+            :step="0.1"
+            :marks="sliderMarks.repetition_penalty"
+          />
+          <div class="tips-wrap">
+            <InfoCircleFilled />Penalize repetition — 1.0 to disable.
+          </div>
+        </a-form-item>
+        <a-form-item
+          label="Max Token Number"
+          name="max_tokens"
+          class="slider-wrap"
+        >
+          <a-slider
+            v-model:value="form.max_tokens"
+            :min="1"
+            :max="8192"
+            :marks="sliderMarks.max_tokens"
+          />
+          <div class="tips-wrap"><InfoCircleFilled />Set Max Output Token.</div>
+        </a-form-item>
+      </div>
+    </a-form>
+    <template #footer>
+      <a-button style="margin-right: 8px" @click="handleClose">Cancel</a-button>
+      <a-button type="primary" :loading="submitLoading" @click="handleSubmit"
+        >Confirm</a-button
+      >
+    </template>
+  </a-drawer>
+</template>
+<script lang="ts" setup name="DetailDrawer">
+import { InfoCircleFilled } from "@ant-design/icons-vue";
+import { FormInstance } from "ant-design-vue";
+import { reactive, ref } from "vue";
+import { ConfigType } from "../type";
+
+const props = defineProps({
+  drawerData: {
+    type: Object,
+    required: true,
+    default: () => {},
+  },
+});
+const emit = defineEmits(["close", "update"]);
+
+const formRef = ref<FormInstance>();
+const drawerVisible = ref<boolean>(true);
+const submitLoading = ref<boolean>(false);
+const {
+  top_n = 5,
+  temperature = 0.1,
+  top_p = 1,
+  top_k = 50,
+  repetition_penalty = 1.1,
+  max_tokens = 512,
+  stream = true,
+} = props.drawerData;
+
+const form = reactive<ConfigType>({
+  top_n,
+  temperature,
+  top_p,
+  top_k,
+  repetition_penalty,
+  max_tokens,
+  stream,
+});
+const rules = reactive({
+  top_n: [{ required: true, trigger: "blur" }],
+  temperature: [{ required: true, trigger: "blur" }],
+  top_p: [{ required: true, trigger: "blur" }],
+  top_k: [{ required: true, trigger: "blur" }],
+  repetition_penalty: [{ required: true, trigger: "blur" }],
+  max_tokens: [{ required: true, trigger: "blur" }],
+});
+const sliderMarks = reactive<EmptyObjectType>({
+  top_n: {
+    1: "1",
+    10: "10",
+  },
+  temperature: {
+    0: "0",
+    1: "1",
+  },
+  top_k: {
+    0: "0",
+    200: "200",
+  },
+  repetition_penalty: {
+    1: "1",
+    2: "2",
+  },
+  max_tokens: {
+    1: "1",
+    8192: "8192",
+  },
+});
+const handleClose = () => {
+  emit("close");
+};
+const handleSubmit = () => {
+  formRef.value?.validate().then(() => {
+    emit("update", form);
+    handleClose();
+  });
+};
+</script>
+<style scoped lang="less">
+.form-wrap {
+  .slider-wrap .intel-slider {
+    width: calc(100% - 20px);
+  }
+  :deep(.intel-form-item-control-input-content) {
+    display: block;
+    .intel-slider-with-marks {
+      margin-bottom: 20px;
+    }
+  }
+  :deep(.intel-form-item) {
+    margin-bottom: 0;
+  }
+}
+.module-wrap {
+  padding: 16px;
+  border: 1px solid var(--border-main-color);
+  position: relative;
+  border-radius: 6px;
+  &:not(:last-child) {
+    margin-bottom: 16px;
+  }
+  .card-shadow;
+  .module-title {
+    font-size: 16px;
+    font-weight: 500;
+    margin-bottom: 12px;
+  }
+  .tips-wrap {
+    display: flex;
+    color: var(--font-info-color);
+    gap: 6px;
+    font-size: 12px;
+    .anticon-info-circle {
+      margin-top: 0;
+      font-size: 13px;
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/components/Header.vue b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/Header.vue
new file mode 100644
index 0000000000..d84e06bc3c
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/Header.vue
@@ -0,0 +1,118 @@
+<template>
+  <a-layout-header class="chatbot-header">
+    <div class="header-wrap">
+      <div class="left-wrap">
+        <img :height="36" :src="headerLog" />
+        <div class="header-title">
+          Intelligent AI assistant, quickly answer your questions
+        </div>
+      </div>
+      <div class="right-wrap">
+        <a-popover
+          trigger="click"
+          title="System status"
+          placement="bottomRight"
+        >
+          <span @click="querySystemStatus" class="button-wrap">
+            <SvgIcon
+              name="icon-notice-board"
+              :style="{ color: 'var(--color-primary)' }"
+            />
+            System status
+          </span>
+          <template #content>
+            <div class="chart-box">
+              <SystemChart :system-data="systemData" :chart-col="12" />
+            </div>
+          </template>
+        </a-popover>
+        <a-divider type="vertical" />
+        <span @click="handleConfig" class="button-wrap">
+          <SvgIcon
+            name="icon-setting"
+            :size="17"
+            :style="{ color: 'var(--color-primary)' }"
+          />
+          Configure
+        </span>
+      </div>
+    </div>
+  </a-layout-header>
+</template>
+
+<script lang="ts" setup name="Chatbot">
+import headerLog from "@/assets/svgs/header-log.svg";
+import SvgIcon from "@/components/SvgIcon.vue";
+import { SystemChart } from "@/views/pipeline/components";
+import { getSystemStatus } from "@/api/pipeline";
+
+const emit = defineEmits(["config"]);
+
+let systemData = reactive<EmptyObjectType>({});
+
+const querySystemStatus = async () => {
+  const data = await getSystemStatus();
+  Object.assign(systemData, data);
+};
+const handleConfig = () => {
+  emit("config");
+};
+onMounted(() => {
+  querySystemStatus();
+});
+</script>
+
+<style scoped lang="less">
+.chatbot-header {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 20;
+  background-color: var(--bg-content-color);
+  box-shadow: 0px 1px 2px 0px var(--bg-box-shadow);
+  width: 100%;
+}
+.header-wrap {
+  max-width: 1440px;
+  min-width: 960px;
+  margin: 0 auto;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  .left-wrap {
+    display: flex;
+    align-items: center;
+    .header-title {
+      margin-left: 24px;
+      color: var(--font-text-color);
+      font-size: 16px;
+    }
+  }
+
+  .right-wrap {
+    display: flex;
+    justify-items: flex-end;
+    align-items: center;
+    gap: 8px;
+    .button-wrap {
+      display: flex;
+      align-items: center;
+      gap: 4px;
+      cursor: pointer;
+      font-size: 14px;
+      font-weight: 500;
+      &:hover {
+        color: var(--color-primary);
+      }
+    }
+  }
+}
+.chart-box {
+  width: 440px;
+  padding: 10px;
+  :deep(.chart-wrap) {
+    width: 200px !important;
+    height: 140px !important;
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/components/MessageItem.vue b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/MessageItem.vue
new file mode 100644
index 0000000000..e1a828c165
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/MessageItem.vue
@@ -0,0 +1,217 @@
+<template>
+  <div id="message-container">
+    <template v-if="message.author === 'Bot'">
+      <div class="chatbot-session">
+        <div class="avatar-wrap">
+          <SvgIcon name="icon-chatbot" :size="24" />
+        </div>
+        <div class="message-wrap">
+          <div v-html="renderedMarkdown"></div>
+        </div>
+      </div>
+      <div
+        v-if="benchmarkData.generator"
+        :class="['benchmark-wrap', isExpanded ? 'expanded' : 'retract']"
+      >
+        <span class="item-tag">
+          <SvgIcon name="icon-generation" :size="14" />
+          Generation: {{ benchmarkData.generator }}s
+        </span>
+        <transition appear name="detail-transition">
+          <div v-if="isExpanded" class="detail-wrap">
+            <span class="item-tag">
+              <SvgIcon name="icon-search-time" :size="14" />
+              Retrieval: {{ benchmarkData.retriever }}s
+            </span>
+            <span class="item-tag">
+              <SvgIcon name="icon-handle-time" :size="14" />
+              Post-process: {{ benchmarkData.postprocessor }}s
+            </span>
+          </div></transition
+        >
+        <SvgIcon
+          :name="isExpanded ? 'icon-time-retract' : 'icon-time-expand'"
+          :size="16"
+          class="expanded-icon"
+          @click="toggleTabs"
+        />
+      </div>
+    </template>
+    <div v-else class="user-session">
+      <div class="message-wrap">{{ message.content }}</div>
+      <div class="avatar-wrap">
+        <SvgIcon name="icon-user" :size="22" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup name="MessageItem">
+import { marked } from "marked";
+import { PropType, reactive, ref } from "vue";
+import { Benchmark, IMessage } from "../type";
+
+const props = defineProps({
+  message: {
+    type: Object as PropType<IMessage>,
+    required: true,
+    default: () => {},
+  },
+  inResponse: {
+    type: Boolean,
+    required: true,
+  },
+});
+
+const emit = defineEmits(["preview"]);
+const benchmarkData = computed(() => {
+  return props.message?.benchmark || {};
+});
+const isExpanded = ref<boolean>(false);
+
+const renderer = new marked.Renderer();
+
+renderer.link = ({ href, title, text }) => {
+  let link = `<a href="${href}" target="_blank" rel="noopener noreferrer"`;
+  if (title) {
+    link += ` title="${title}"`;
+  }
+  link += `>${text}</a>`;
+  return link;
+};
+
+marked.setOptions({
+  pedantic: false,
+  gfm: true,
+  breaks: false,
+  renderer: renderer,
+});
+
+const renderedMarkdown = computed(() => marked(props.message.content));
+
+const toggleTabs = () => {
+  isExpanded.value = !isExpanded.value;
+};
+
+const addClickListeners = () => {
+  const images = document.querySelectorAll("#message-container img");
+
+  images.forEach((img) => {
+    img.addEventListener("click", (event) => {
+      const target = event.target as HTMLImageElement;
+      if (target && target.tagName.toLowerCase() === "img") {
+        emit("preview", target.src);
+      }
+    });
+  });
+};
+
+watch(
+  () => props.inResponse,
+  (newValue) => {
+    if (!newValue) {
+      addClickListeners();
+    }
+  }
+);
+</script>
+
+<style lang="less" scoped>
+@keyframes expandIcon {
+  from {
+    width: 250px;
+    left: 0;
+    transform: none;
+  }
+  to {
+    width: 700px;
+    left: 0;
+    transform: none;
+  }
+}
+@keyframes retractIcon {
+  from {
+    width: 700px;
+    left: 0;
+    transform: none;
+  }
+  to {
+    width: 250px;
+    left: 0;
+    transform: none;
+  }
+}
+.chatbot-session {
+  margin-bottom: 16px;
+  display: flex;
+  padding-right: 40px;
+  gap: 8px;
+  font-size: 16px;
+}
+.avatar-wrap {
+  background-color: var(--bg-main-color);
+  border-radius: 50%;
+  width: 32px;
+  height: 32px;
+  .vertical-center;
+}
+.message-wrap {
+  background-color: var(--bg-main-color);
+  border-radius: 6px;
+  line-height: 24px;
+  padding: 10px 14px;
+  max-width: calc(100% - 40px);
+}
+.user-session {
+  margin-bottom: 20px;
+  display: flex;
+  justify-content: flex-end;
+  padding-left: 40px;
+  gap: 8px;
+  font-size: 16px;
+  .message-wrap {
+    background-color: var(--color-primaryBg);
+  }
+}
+.benchmark-wrap {
+  .flex-left;
+  margin-bottom: 20px;
+  padding-left: 40px;
+  gap: 12px;
+  position: relative;
+  top: -8px;
+  transition: none;
+  overflow: hidden;
+  &.expanded {
+    animation: expandIcon 1s forwards;
+  }
+  &.retract {
+    animation: retractIcon 1s forwards;
+  }
+  .detail-wrap {
+    .flex-left;
+    gap: 12px;
+  }
+  .item-tag {
+    white-space: nowrap;
+    padding: 8px 12px;
+    border-radius: 8px;
+    .vertical-center;
+    background-color: var(--bg-card-color);
+    gap: 4px;
+    .icon-intel {
+      color: var(--color-primary) !important;
+    }
+  }
+  .expanded-icon {
+    cursor: pointer;
+    position: relative;
+    left: -4px;
+  }
+}
+/* 定义 <transition> 的进入和离开动画 */
+.detail-transition-enter-active,
+.detail-transition-leave-active {
+  transition: all 0.7s ease-in-out;
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/components/SseService.ts b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/SseService.ts
new file mode 100644
index 0000000000..cde72e4ffe
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/SseService.ts
@@ -0,0 +1,47 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+export const handleMessageSend = async (
+  url: string,
+  postData: any,
+  onDisplay: (data: any) => void,
+  onEnd?: () => void,
+): Promise<void> => {
+  try {
+    const response = await fetch(url, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        Accept: "text/event-stream",
+      },
+      body: JSON.stringify(postData),
+    });
+
+    if (!response.ok) {
+      throw new Error(`Network response was not ok: ${response.statusText}`);
+    }
+
+    const reader = response.body?.getReader();
+    const decoder = new TextDecoder("utf-8");
+    let buffer = "";
+
+    try {
+      while (true) {
+        const { done, value } = await reader.read();
+        if (done) {
+          onEnd?.();
+          break;
+        }
+        buffer += decoder.decode(value, { stream: true });
+        // console.log("buffer", buffer);
+        onDisplay(buffer);
+      }
+    } catch (error) {
+      console.error(error);
+      onEnd?.();
+    }
+  } catch (error) {
+    console.error(error);
+    onEnd?.();
+  }
+};
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/components/UploadFile.vue b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/UploadFile.vue
new file mode 100644
index 0000000000..1f5286c6c5
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/UploadFile.vue
@@ -0,0 +1,276 @@
+<template>
+  <div class="upload-wrap">
+    <a-upload-dragger
+      v-model:file-list="uploadFileList"
+      name="file"
+      multiple
+      :max-count="10"
+      :action="uploadFileUrl"
+      accept=".csv,.doc,.docx,.enex,.epub,.html,.md,.odt,.pdf,.ppt,.pptx,.txt,"
+      :before-upload="handleBeforeUpload"
+      @change="handleChange"
+    >
+      <SvgIcon
+        name="icon-cloudupload-fill"
+        :size="50"
+        :style="{ color: 'var(--font-tip-color)' }"
+      />
+      <p class="upload-text">Click to upload or drag the file here</p>
+      <div class="upload-tip">
+        <p>
+          Supported formats: PDF,PPT,PPTX,TXT,DOC,DOCX,HTML,MD,CSV,ENEX,EPUB and
+          ODT.
+        </p>
+        <p>Single file size not exceeding 20M.</p>
+      </div>
+    </a-upload-dragger>
+    <div class="uploaded-wrap" v-if="uploadedList.length">
+      <a-collapse v-model:activeKey="listActive" expandIconPosition="end" ghost>
+        <a-collapse-panel key="file" header="File uploaded">
+          <transition-group name="expand-fade" tag="div">
+            <div
+              v-for="item in displayFiles"
+              :key="item.file_id"
+              class="file-item"
+            >
+              <div class="left-wrap">
+                <img :src="uploaded" alt="" />
+                {{ item.file_name }}
+              </div>
+              <div class="right-wrap">
+                <a-tooltip placement="top">
+                  <template #title>
+                    <span>Delete File</span>
+                  </template>
+                  <DeleteFilled
+                    class="delete-icon"
+                    @click="handleFileDelete(item)"
+                  />
+                </a-tooltip>
+              </div>
+            </div>
+          </transition-group>
+          <div v-if="uploadedList.length > 10" class="expande-wrap">
+            <span @click="handleToggleExpand"
+              >{{ isExpanded ? "Collapse" : "Expand" }}
+              <component :is="isExpanded ? UpOutlined : DownOutlined"
+            /></span>
+          </div>
+        </a-collapse-panel>
+      </a-collapse>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup name="UploadFile">
+import {
+  getFilleList,
+  requestFileDelete,
+  requestParsingFiles,
+  uploadFileUrl,
+} from "@/api/chatbot";
+import uploaded from "@/assets/svgs/uploaded.svg";
+import router from "@/router";
+import { pipelineAppStore } from "@/store/pipeline";
+import { useNotification } from "@/utils/common";
+import {
+  CloseCircleFilled,
+  DeleteFilled,
+  DownOutlined,
+  UpOutlined,
+} from "@ant-design/icons-vue";
+import { message, Modal, UploadProps, UploadFile } from "ant-design-vue";
+import { createVNode, onMounted, ref } from "vue";
+
+const { antNotification } = useNotification();
+const pipelineStore = pipelineAppStore();
+const uploadFileList = ref([]);
+const uploadedList = ref<EmptyArrayType>([]);
+const listActive = ref<string>("file");
+const isExpanded = ref<boolean>(false);
+
+const displayFiles = computed(() =>
+  isExpanded.value ? uploadedList.value : uploadedList.value.slice(0, 10)
+);
+const handleBeforeUpload = (file: UploadProps["fileList"][number]) => {
+  const isFileSize = file.size / 1024 / 1024 < 20;
+
+  if (!isFileSize) {
+    message.error("Single file size not exceeding 20M.");
+  }
+
+  return isFileSize;
+};
+
+const handleChange = ({
+  fileList,
+  file,
+}: {
+  fileList: UploadFile[];
+  file: UploadFile;
+}) => {
+  const { response, status } = file;
+  if (status === "done") {
+    requestParsingFiles({
+      local_path: response,
+    });
+    handleSuccess(file);
+  } else if (status === "error") {
+    antNotification("error", "Error", response.detail);
+  }
+
+  const isAllEnd = fileList.every((file: any) => file.status !== "uploading");
+
+  if (isAllEnd) {
+    setTimeout(() => {
+      queryFilleList();
+    }, 50);
+  }
+};
+const handleSuccess = (file: UploadFile) => {
+  uploadFileList.value = uploadFileList.value.filter(
+    (item: UploadFile) => item.uid !== file.uid
+  );
+};
+const queryFilleList = async () => {
+  const data: any = await getFilleList();
+  uploadedList.value = [].concat(data);
+};
+
+const handleFileDelete = (row: EmptyObjectType) => {
+  const { file_name } = row;
+  Modal.confirm({
+    title: "Delete",
+    icon: createVNode(CloseCircleFilled, { class: "error-icon" }),
+    content: `Are you sure delete this file?`,
+    okText: "Confirm",
+    okType: "danger",
+    async onOk() {
+      await requestFileDelete(file_name);
+      queryFilleList();
+    },
+  });
+};
+const handleToggleExpand = () => {
+  isExpanded.value = !isExpanded.value;
+};
+onMounted(() => {
+  if (!pipelineStore.activatedPipeline) {
+    Modal.warning({
+      title: "Prompt",
+      content:
+        "There is no available pipeline. Please create or activate it first.",
+      okText: "Go Configure",
+      class: "centered-model",
+      onOk() {
+        router.push("/pipeline");
+      },
+    });
+
+    return false;
+  }
+  queryFilleList();
+});
+</script>
+
+<style scoped lang="less">
+.upload-wrap {
+  width: 100%;
+  padding: 20px;
+  border-radius: 6px;
+  margin-top: 20px;
+  background-color: var(--bg-content-color);
+
+  :deep(.intel-upload) {
+    border-radius: 6px;
+    background-color: var(--bg-content-color);
+    &:hover {
+      color: var(--color-primary);
+    }
+  }
+  .upload-text {
+    color: var(--font-text-color);
+    margin: 8px 0;
+    font-weight: 500;
+    font-size: 16px;
+  }
+  .upload-tip {
+    color: var(--font-tip-color);
+    font-size: 12px;
+    line-height: 1.4;
+    margin-top: 12px;
+  }
+  .uploaded-wrap {
+    margin-top: 16px;
+    :deep(.intel-collapse) {
+      .intel-collapse-header {
+        font-size: 16px;
+        font-weight: 600;
+        border-radius: 8px 8px 0 0;
+        background-color: var(--border-main-color);
+      }
+      .intel-collapse-content-box {
+        padding: 8px 0;
+      }
+    }
+    .file-item {
+      margin-bottom: 8px;
+      font-size: 14px;
+      height: 48px;
+      padding: 0 12px;
+      color: var(--font-text-color);
+      background-color: var(--bg-card-color);
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      .left-wrap {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        gap: 6px;
+        img {
+          height: 20px;
+        }
+        .size-text {
+          color: var(--font-tip-color);
+        }
+      }
+      .right-wrap {
+        width: 30px;
+        text-align: end;
+        font-size: 16px;
+        cursor: pointer;
+        color: var(--font-tip-color);
+        &:hover {
+          color: var(--color-primary);
+        }
+      }
+    }
+    .expande-wrap {
+      .vertical-center;
+      padding-top: 12px;
+      font-weight: 600;
+      font-size: 16px;
+      cursor: pointer;
+      color: var(--color-primary);
+      &:hover {
+        color: var(--color-primary-hover);
+      }
+    }
+  }
+}
+.delete-icon {
+  &:hover {
+    color: var(--color-error);
+  }
+}
+.expand-fade-enter-active,
+.expand-fade-leave-active {
+  transition: opacity 0.2s ease, transform 0.2s ease;
+}
+.expand-fade-enter-from,
+.expand-fade-leave-to {
+  opacity: 0;
+  transform: translateY(-10px);
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/components/index.ts b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/index.ts
new file mode 100644
index 0000000000..522f61b662
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/components/index.ts
@@ -0,0 +1,10 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import Chatbot from "./Chatbot.vue";
+import ConfigDrawer from "./ConfigDrawer.vue";
+import Header from "./Header.vue";
+import MessageItem from "./MessageItem.vue";
+import UploadFile from "./UploadFile.vue";
+
+export { Chatbot, ConfigDrawer, Header, MessageItem, UploadFile };
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/index.vue b/EdgeCraftRAG/ui/vue/src/views/chatbot/index.vue
new file mode 100644
index 0000000000..f3d8b81778
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/index.vue
@@ -0,0 +1,108 @@
+<template>
+  <div class="pipeline-container">
+    <a-affix :offset-top="450">
+      <div class="setting-icon" @click="jumpPipeline">
+        <SvgIcon
+          name="icon-setting"
+          :size="32"
+          :style="{ color: 'var(--color-big-icon)' }"
+          inherit
+        />
+        <div>Pipeline</div>
+      </div></a-affix
+    >
+    <!-- system status -->
+    <Header @config="handleConfig" />
+    <!-- chatbot-->
+    <Chatbot :configuration="chatbotConfiguration" />
+    <!-- upload file-->
+    <UploadFile />
+    <!-- config chatbot -->
+    <ConfigDrawer
+      v-if="configDrawer.visible"
+      :drawer-data="configDrawer.data"
+      @close="configDrawer.visible = false"
+      @update="handleUpdateConfiguration"
+    />
+  </div>
+</template>
+
+<script lang="ts" setup name="Chatbot">
+import router from "@/router";
+import { chatbotAppStore } from "@/store/chatbot";
+import { onMounted, reactive } from "vue";
+import { Chatbot, ConfigDrawer, Header, UploadFile } from "./components";
+import { ConfigType } from "./type";
+
+const chatbotStore = chatbotAppStore();
+
+let chatbotConfiguration = reactive<ConfigType>({
+  top_n: 5,
+  temperature: 0.1,
+  top_p: 1,
+  top_k: 50,
+  repetition_penalty: 1.1,
+  max_tokens: 512,
+  stream: true,
+});
+const configDrawer = reactive<DialogType>({
+  visible: false,
+  data: {},
+});
+
+const handleConfig = () => {
+  configDrawer.visible = true;
+  configDrawer.data = chatbotConfiguration;
+};
+//Jump Pipeline
+const jumpPipeline = () => {
+  router.push("/pipeline");
+};
+const handleUpdateConfiguration = (configuration: ConfigType) => {
+  chatbotConfiguration = {
+    ...chatbotConfiguration,
+    ...configuration,
+  };
+  chatbotStore.setChatbotConfiguration(configuration);
+};
+onMounted(() => {
+  if (chatbotStore?.configuration)
+    chatbotConfiguration = {
+      ...chatbotConfiguration,
+      ...chatbotStore.configuration,
+    };
+});
+</script>
+
+<style scoped lang="less">
+.pipeline-container {
+  position: relative;
+
+  .setting-icon {
+    padding: 12px 8px;
+    position: absolute;
+    transform: translateY(-50%);
+    top: 40%;
+    left: -80px;
+    z-index: 99;
+    background-color: var(--bg-content-color);
+    box-shadow: 0px 2px 4px 0px var(--bg-box-shadow);
+    border-radius: 6px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    cursor: pointer;
+    font-size: 14px;
+    font-weight: 500;
+    color: var(--font-main-color);
+    &:hover {
+      color: var(--color-primary);
+    }
+  }
+  @media (max-width: 1100px) {
+    .setting-icon {
+      left: 0;
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/chatbot/type.ts b/EdgeCraftRAG/ui/vue/src/views/chatbot/type.ts
new file mode 100644
index 0000000000..8959a98cd5
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/chatbot/type.ts
@@ -0,0 +1,22 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+export interface Benchmark {
+  generator: string;
+  postprocessor: string;
+  retriever: string;
+}
+export interface IMessage {
+  author: string;
+  content: string;
+  benchmark?: Benchmark | undefined;
+}
+export interface ConfigType {
+  top_n: number;
+  temperature: number;
+  top_p: number;
+  top_k: number;
+  repetition_penalty: number;
+  max_tokens: number;
+  stream: boolean;
+}
diff --git a/EdgeCraftRAG/ui/vue/src/views/error/404.vue b/EdgeCraftRAG/ui/vue/src/views/error/404.vue
new file mode 100644
index 0000000000..9f5be4c00c
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/error/404.vue
@@ -0,0 +1,43 @@
+<template>
+  <div class="not-found">
+    <img :src="notFound" alt="" class="not-found-icon" />
+    <p>Uh oh! It seems like you're lost</p>
+    <a-button
+      type="primary"
+      size="large"
+      :icon="h(HomeFilled)"
+      @click="handleGoHome"
+      >GO Home
+    </a-button>
+  </div>
+</template>
+
+<script lang="ts" setup name="404">
+import notFound from "@/assets/svgs/404-icon.svg";
+import router from "@/router";
+import { HomeFilled } from "@ant-design/icons-vue";
+import { h } from "vue";
+
+const handleGoHome = () => {
+  router.push("/");
+};
+</script>
+
+<style scoped lang="less">
+.not-found {
+  width: 100%;
+  height: 100vh;
+  padding-top: 30vh;
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  gap: 16px;
+  background-image: url("@/assets/images/404-bg.png");
+  background-size: cover;
+  background-repeat: no-repeat;
+  background-position: center;
+  .not-found-icon {
+    margin-bottom: 16px;
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/Configuration.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/Configuration.vue
new file mode 100644
index 0000000000..7a90ca999c
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/Configuration.vue
@@ -0,0 +1,54 @@
+<template>
+  <div>
+    <a-collapse expandIconPosition="end" class="config-container mt-20">
+      <a-collapse-panel :header="$t('setting.config')">
+        <vue-json-pretty :data="jsonData" virtual v-if="jsonData" />
+        <a-empty v-else />
+      </a-collapse-panel>
+    </a-collapse>
+  </div>
+</template>
+
+<script lang="ts" setup name="xxx">
+import VueJsonPretty from "vue-json-pretty";
+import "vue-json-pretty/lib/styles.css";
+
+const props = defineProps({
+  jsonData: {
+    type: String,
+    default: "",
+  },
+});
+
+const jsonData = ref(JSON.parse(props.jsonData));
+</script>
+
+<style scoped lang="less">
+.title {
+  .fs-16;
+  font-weight: 600;
+  color: var(--font-main-color);
+}
+
+.intel-collapse {
+  background-color: var(--bg-content-color);
+  border-color: transparent;
+  border: 1px solid transparent;
+
+  :deep(.intel-collapse-header-text) {
+    .title;
+  }
+
+  :deep(.intel-collapse-item) {
+    border-bottom: transparent;
+  }
+
+  :deep(.intel-collapse-content) {
+    border-top: transparent;
+
+    .intel-collapse-content-box {
+      padding-top: 0;
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/DetailDrawer.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/DetailDrawer.vue
new file mode 100644
index 0000000000..a586d842fa
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/DetailDrawer.vue
@@ -0,0 +1,283 @@
+<template>
+  <a-drawer
+    v-model:open="drawerVisible"
+    title="Pipeline Details"
+    destroyOnClose
+    width="500px"
+    @close="handleClose"
+  >
+    <!-- basic -->
+    <div class="basic-wrap">
+      <p class="basic-item">
+        <span class="label-wrap">Name</span>
+        <span class="content-wrap">{{ formData.name }}</span>
+      </p>
+      <p class="basic-item">
+        <span class="label-wrap">Status</span>
+        <span
+          :class="{ 'active-state': formData.active, 'content-wrap': true }"
+          >{{ formData.active ? "Activated" : "Inactive" }}</span
+        >
+      </p>
+    </div>
+    <!-- Node Parser -->
+    <div class="module-wrap">
+      <a-collapse v-model:activeKey="nodeParserActive" expandIconPosition="end">
+        <a-collapse-panel key="nodeParser" header="Node Parser">
+          <ul class="form-wrap">
+            <li class="item-wrap">
+              <span class="label-wrap">Node parser type</span>
+              <span class="content-wrap">{{
+                formData.node_parser.parser_type
+              }}</span>
+            </li>
+            <template v-if="!isHierarchical && !isSentencewindow">
+              <li class="item-wrap">
+                <span class="label-wrap">Chunk size</span>
+                <span class="content-wrap">{{
+                  formData.node_parser.chunk_size
+                }}</span>
+              </li>
+              <li class="item-wrap">
+                <span class="label-wrap">Chunk overlap</span>
+                <span class="content-wrap">{{
+                  formData.node_parser.chunk_overlap
+                }}</span>
+              </li></template
+            >
+            <template v-if="isSentencewindow">
+              <li class="item-wrap">
+                <span class="label-wrap">Window Size</span>
+                <span class="content-wrap">{{
+                  formData.node_parser.window_size
+                }}</span>
+              </li></template
+            >
+          </ul>
+        </a-collapse-panel>
+      </a-collapse>
+    </div>
+    <!-- Indexer -->
+    <div class="module-wrap">
+      <a-collapse v-model:activeKey="indexerActive" expandIconPosition="end">
+        <a-collapse-panel key="indexer" header="Indexer">
+          <ul class="form-wrap">
+            <li class="item-wrap">
+              <span class="label-wrap">Indexer Type</span>
+              <span class="content-wrap">{{
+                formData.indexer.indexer_type
+              }}</span>
+            </li>
+            <li class="item-wrap">
+              <span class="label-wrap">Embedding Model</span>
+              <span class="content-wrap">{{
+                formData.indexer.embedding_model.model_id
+              }}</span>
+            </li>
+            <li class="item-wrap">
+              <span class="label-wrap">Embedding run device</span>
+              <span class="content-wrap">{{
+                formData.indexer.embedding_model.device
+              }}</span>
+            </li>
+          </ul>
+        </a-collapse-panel>
+      </a-collapse>
+    </div>
+    <!-- Retriever -->
+    <div class="module-wrap">
+      <a-collapse v-model:activeKey="retrieverActive" expandIconPosition="end">
+        <a-collapse-panel key="retriever" header="Retriever">
+          <ul class="form-wrap">
+            <li class="item-wrap">
+              <span class="label-wrap">Retriever Type</span>
+              <span class="content-wrap">{{
+                formData.retriever.retriever_type
+              }}</span>
+            </li>
+            <li class="item-wrap">
+              <span class="label-wrap">Search top k</span>
+              <span class="content-wrap">{{
+                formData.retriever.retrieve_topk
+              }}</span>
+            </li>
+          </ul>
+        </a-collapse-panel>
+      </a-collapse>
+    </div>
+    <!-- PostProcessor -->
+    <div class="module-wrap">
+      <a-collapse
+        v-model:activeKey="postProcessorActive"
+        expandIconPosition="end"
+      >
+        <a-collapse-panel key="postProcessor" header="PostProcessor">
+          <ul
+            v-for="(item, index) in formData.postprocessor"
+            key="index"
+            :class="['form-wrap', index ? 'bt-border' : '']"
+          >
+            <li class="item-wrap">
+              {{ `PostProcessor Type${index + 1}:` }}
+              <span class="content-wrap">{{ item.processor_type }}</span>
+            </li>
+            <li class="item-wrap" v-if="item.reranker_model?.model_id">
+              <span class="label-wrap">Rerank Model</span>
+              <span class="content-wrap">{{
+                item.reranker_model.model_id
+              }}</span>
+            </li>
+            <li class="item-wrap" v-if="item.reranker_model?.device">
+              <span class="label-wrap">Rerank run device</span>
+              <span class="content-wrap">{{ item.reranker_model.device }}</span>
+            </li>
+          </ul>
+        </a-collapse-panel>
+      </a-collapse>
+    </div>
+    <!-- Generator -->
+    <div class="module-wrap">
+      <a-collapse v-model:activeKey="generatorActive" expandIconPosition="end">
+        <a-collapse-panel key="generator" header="Generator">
+          <ul class="form-wrap">
+            <li class="item-wrap">
+              <span class="label-wrap">Generator Type</span>
+              <span class="content-wrap"> chatqna </span>
+            </li>
+            <li class="item-wrap">
+              <span class="label-wrap">LLM Inference Type</span>
+              <span class="content-wrap">{{
+                formData.generator.inference_type
+              }}</span>
+            </li>
+            <template v-if="formData.generator.inference_type === 'local'">
+              <li class="item-wrap">
+                <span class="label-wrap">Large Language Model</span>
+                <span class="content-wrap">{{
+                  formData.generator.model.model_id
+                }}</span>
+              </li>
+              <li class="item-wrap">
+                <span class="label-wrap">LLM run device</span>
+                <span class="content-wrap">{{
+                  formData.generator.model.device
+                }}</span>
+              </li>
+              <li class="item-wrap">
+                <span class="label-wrap">Weights</span>
+                <span class="content-wrap">{{
+                  formData.generator.model.weight
+                }}</span>
+              </li></template
+            >
+          </ul>
+        </a-collapse-panel>
+      </a-collapse>
+    </div>
+  </a-drawer>
+</template>
+<script lang="ts" setup name="DetailDrawer">
+import { computed, reactive, ref } from "vue";
+
+const props = defineProps({
+  drawerData: {
+    type: Object,
+    required: true,
+    default: () => {},
+  },
+});
+const emit = defineEmits(["close"]);
+const formData = reactive<EmptyObjectType>(props.drawerData);
+const drawerVisible = ref<boolean>(true);
+const nodeParserActive = ref<string>("nodeParser");
+const indexerActive = ref<string>("indexer");
+const retrieverActive = ref<string>("retriever");
+const postProcessorActive = ref<string>("postProcessor");
+const generatorActive = ref<string>("generator");
+
+const isHierarchical = computed(() => {
+  return formData.node_parser.parser_type === "hierarchical";
+});
+const isSentencewindow = computed(() => {
+  return formData.node_parser.parser_type === "sentencewindow";
+});
+const handleClose = () => {
+  emit("close");
+};
+</script>
+<style scoped lang="less">
+.basic-wrap {
+  .basic-item {
+    display: flex;
+    gap: 12px;
+    margin: 0;
+    line-height: 24px;
+    .content-wrap {
+      color: var(--font-main-color);
+      font-weight: 600;
+    }
+    .active-state {
+      color: var(--color-success);
+    }
+  }
+}
+.module-wrap {
+  margin-top: 20px;
+  .intel-collapse {
+    border-color: var(--border-main-color);
+    :deep(.intel-collapse-header) {
+      padding: 8px 16px;
+    }
+    :deep(.intel-collapse-header-text) {
+      .fs-14;
+      font-weight: 600;
+      color: var(--font-main-color);
+    }
+
+    :deep(.intel-collapse-item) {
+      border-bottom: 1px solid var(--border-main-color);
+    }
+
+    :deep(.intel-collapse-content) {
+      border-top: 1px solid var(--border-main-color);
+      .intel-collapse-content-box {
+        padding: 10px 16px;
+      }
+    }
+  }
+  .form-wrap {
+    padding: 0;
+    margin: 0;
+    .item-wrap {
+      list-style: none;
+      display: flex;
+      gap: 12px;
+      line-height: 1.2;
+      padding: 6px 0;
+      display: flex;
+      color: var(--font-text-color);
+      word-wrap: break-word;
+      .content-wrap {
+        color: var(--font-main-color);
+        font-weight: 500;
+        display: inline-flex;
+        align-items: baseline;
+        justify-content: end;
+        flex: 1;
+        word-break: break-word;
+        overflow-wrap: break-word;
+      }
+    }
+    &.bt-border {
+      border-top: 1px dashed var(--border-main-color);
+    }
+  }
+}
+.label-wrap {
+  position: relative;
+  &::after {
+    content: ":";
+    .ml-2;
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/ImportDialog.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/ImportDialog.vue
new file mode 100644
index 0000000000..a17cc36862
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/ImportDialog.vue
@@ -0,0 +1,74 @@
+<template>
+  <a-modal
+    v-model:open="modelVisible"
+    width="500px"
+    centered
+    destroyOnClose
+    title="Import Pipeline"
+    :keyboard="false"
+    :maskClosable="false"
+    :footer="null"
+    @cancel="handleClose"
+    class="import-dialog"
+  >
+    <a-upload-dragger
+      v-model:fileList="fileList"
+      name="file"
+      :action="importUrl"
+      accept=".json"
+      :showUploadList="false"
+      @change="handleChange"
+    >
+      <SvgIcon
+        name="icon-cloudupload-fill"
+        :size="40"
+        :style="{ color: 'var(--color-primary)' }"
+      />
+      <p class="intel-upload-text">Click or drag file to this area to upload</p>
+      <p class="intel-upload-hint">
+        Supports JSON format, with file size not exceeding 10M.
+      </p>
+      <a-button type="primary" class="mt-12">Import</a-button>
+    </a-upload-dragger>
+  </a-modal>
+</template>
+
+<script lang="ts" setup name="ImportDialog">
+import { importUrl } from "@/api/pipeline";
+import { ref } from "vue";
+import { useNotification } from "@/utils/common";
+import { NextLoading } from "@/utils/loading";
+
+const { antNotification } = useNotification();
+
+const emit = defineEmits(["search", "close"]);
+const modelVisible = ref<boolean>(true);
+const fileList = ref([]);
+
+const handleChange = (info: any) => {
+  const el = <HTMLElement>document.querySelector(".loading-next");
+  if (!el) NextLoading.start();
+  try {
+    const status = info.file.status;
+
+    if (status === "done") {
+      emit("search");
+      handleClose();
+      NextLoading.done();
+      antNotification("success", "Success", "Files upload successful!");
+    } else if (status === "error") {
+      NextLoading.done();
+      antNotification("error", "Error", "Files upload failed!");
+    }
+  } catch (error) {
+    console.error(error);
+    if (NextLoading) NextLoading.done();
+  }
+};
+
+const handleClose = () => {
+  emit("close");
+};
+</script>
+
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/QuickStart.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/QuickStart.vue
new file mode 100644
index 0000000000..5dcff50db7
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/QuickStart.vue
@@ -0,0 +1,153 @@
+<template>
+  <a-modal
+    v-model:open="modelVisible"
+    width="800px"
+    centered
+    destroyOnClose
+    :keyboard="false"
+    :maskClosable="false"
+    :footer="null"
+    :aria-hidden="false"
+    @cancel="handleClose"
+    class="import-dialog"
+  >
+    <div class="guide-wrap">
+      <div class="guide-title">Quick Start</div>
+      <div class="steps-wrap">
+        <a-steps
+          :current="0"
+          :items="steps"
+          labelPlacement="vertical"
+        ></a-steps>
+      </div>
+      <div class="card-wrap">
+        <div class="item-wrap">
+          <div class="left-wrap">1</div>
+          <div class="right-wrap">
+            <div class="title">Create Pipeline</div>
+            <div class="des">
+              Build your RAG pipeline with customized settings to maximize the
+              potential of AI information processing capability.
+            </div>
+            <a-button
+              class="special-button-primary"
+              :icon="h(PlusOutlined)"
+              @click="handleCreate"
+              >Go Create</a-button
+            >
+          </div>
+        </div>
+        <div class="item-wrap">
+          <div class="left-wrap">2</div>
+          <div class="right-wrap">
+            <div class="title">Use the Chatbot</div>
+            <div class="des">
+              Start engaging with the intelligent chatbot, which supports file
+              uploads and information retrieval to assist you in completing
+              tasks more efficiently.
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </a-modal>
+</template>
+
+<script lang="ts" setup name="EditDialog">
+import { userAppStore } from "@/store/user";
+import { PlusOutlined } from "@ant-design/icons-vue";
+import { computed, h } from "vue";
+
+const userGuideStore = userAppStore();
+
+const emit = defineEmits(["create"]);
+const modelVisible = computed(() => {
+  return !userGuideStore.userGuide;
+});
+
+const steps = ref<EmptyArrayType>([
+  {
+    title: "Step 1",
+  },
+  {
+    title: "Step 2",
+  },
+]);
+const handleCreate = () => {
+  userGuideStore.setUserGuideState(true);
+  emit("create");
+};
+const handleClose = () => {
+  userGuideStore.setUserGuideState(true);
+};
+</script>
+
+<style scoped lang="less">
+.guide-wrap {
+  padding: 24px 0;
+  .guide-title {
+    font-size: 24px;
+    font-weight: 600;
+    color: var(--font-main-color);
+    text-align: center;
+  }
+  .steps-wrap {
+    width: 600px;
+    margin: 0 auto;
+    :deep(.intel-steps) {
+      padding-top: 30px;
+      .intel-steps-item-title {
+        position: absolute;
+        top: -35px;
+        left: 35px;
+        font-weight: 500;
+      }
+      .intel-steps-item-active {
+        .intel-steps-item-title {
+          color: var(--color-primary);
+        }
+      }
+      .intel-steps-item-tail::after {
+        height: 3px;
+        background-color: var(--color-primary);
+      }
+    }
+  }
+  .card-wrap {
+    display: flex;
+    gap: 32px;
+    margin-top: 24px;
+    .item-wrap {
+      flex: 1;
+      padding: 20px;
+      border-radius: 6px;
+      height: 200px;
+      box-shadow: var(--bg-gradient-shadow);
+      display: flex;
+      gap: 12px;
+      .left-wrap {
+        width: 30px;
+        color: var(--border-main-color);
+        font-size: 42px;
+        line-height: 42px;
+      }
+      .right-wrap {
+        flex: 1;
+        .title {
+          font-size: 18px;
+          font-weight: 500;
+          line-height: 28px;
+          color: var(--font-main-color);
+          white-space: pre-wrap;
+        }
+        .des {
+          margin-top: 12px;
+          margin-bottom: 20px;
+          line-height: 20px;
+          color: var(--font-text-color);
+        }
+      }
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/System.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/System.vue
new file mode 100644
index 0000000000..e6a025f2db
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/System.vue
@@ -0,0 +1,118 @@
+<template>
+  <div class="system-container">
+    <a-collapse v-model:activeKey="activeKey" expandIconPosition="end">
+      <a-collapse-panel key="status" :header="$t('setting.systemStatus')">
+        <SystemChart :system-data="systemData" :chart-col="chartCol" />
+        <div class="info-wrap">
+          <a-descriptions
+            title="system information"
+            :column="1"
+            :label-style="{ color: 'var(--font-text-color)' }"
+            :content-style="{
+              color: 'var(--font-main-color)',
+              justifyContent: 'end',
+            }"
+          >
+            <a-descriptions-item label="Kernel">{{
+              systemData.kernel
+            }}</a-descriptions-item>
+            <a-descriptions-item label="Processor">{{
+              systemData.processor
+            }}</a-descriptions-item>
+            <a-descriptions-item label="OS">{{
+              systemData.os
+            }}</a-descriptions-item>
+            <a-descriptions-item label="Current time">{{
+              systemData.currentTime
+            }}</a-descriptions-item>
+          </a-descriptions>
+        </div>
+      </a-collapse-panel>
+    </a-collapse>
+  </div>
+</template>
+
+<script lang="ts" setup name="System">
+import { getSystemStatus } from "@/api/pipeline";
+import { onMounted, onUnmounted, ref } from "vue";
+import { SystemChart } from "./index";
+import { SystemType } from "../type";
+
+const activeKey = ref<string>("status");
+const intervalId = ref<any>(null);
+let systemData = reactive<SystemType>({
+  cpuUsage: 0,
+  gpuUsage: 0,
+  diskUsage: 0,
+  memoryUsage: 0,
+  memoryUsed: "",
+  memoryTotal: "",
+  kernel: "",
+  processor: "",
+  os: "",
+  currentTime: "",
+});
+
+const chartCol = computed(() => {
+  return systemData.gpuUsage ? 6 : 8;
+});
+const querySystemStatus = async () => {
+  const data = await getSystemStatus();
+  Object.assign(systemData, data);
+};
+
+onMounted(() => {
+  querySystemStatus();
+  intervalId.value = setInterval(querySystemStatus, 50000);
+});
+onUnmounted(() => {
+  clearInterval(intervalId.value);
+});
+</script>
+
+<style scoped lang="less">
+.system-container {
+  .title {
+    .fs-16;
+    font-weight: 600;
+    color: var(--font-main-color);
+  }
+
+  .intel-collapse {
+    background-color: var(--bg-content-color);
+    border-color: transparent;
+    border: 1px solid transparent;
+
+    :deep(.intel-collapse-header-text) {
+      .title;
+    }
+
+    :deep(.intel-collapse-item) {
+      border-bottom: transparent;
+    }
+
+    :deep(.intel-collapse-content) {
+      border-top: transparent;
+
+      .intel-collapse-content-box {
+        padding-top: 0;
+      }
+    }
+  }
+  .info-wrap {
+    border-radius: 8px;
+    background-color: var(--bg-card-color);
+    padding: 16px 16px 0 16px;
+    margin-top: 20px;
+    width: 500px;
+    :deep(.intel-descriptions) {
+      .intel-descriptions-header {
+        margin-bottom: 12px;
+      }
+      .intel-descriptions-item {
+        padding-bottom: 8px;
+      }
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/SystemChart.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/SystemChart.vue
new file mode 100644
index 0000000000..199f6abb33
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/SystemChart.vue
@@ -0,0 +1,314 @@
+<template>
+  <div>
+    <a-row type="flex" wrap :gutter="[20, 20]">
+      <a-col :span="chartCol">
+        <v-chart
+          class="chart-wrap"
+          :option="cpuOption"
+          :theme="currentTheme"
+          autoresize
+        />
+      </a-col>
+      <a-col :span="chartCol" v-if="systemData.gpuUsage">
+        <v-chart
+          class="chart-wrap"
+          :option="gpuOption"
+          :theme="currentTheme"
+          autoresize
+        />
+      </a-col>
+      <a-col :span="chartCol">
+        <v-chart
+          class="chart-wrap"
+          :option="diskOption"
+          :theme="currentTheme"
+          autoresize
+        />
+      </a-col>
+      <a-col :span="chartCol">
+        <v-chart
+          class="chart-wrap"
+          :option="memoryOption"
+          :theme="currentTheme"
+          autoresize
+        />
+      </a-col>
+    </a-row>
+  </div>
+</template>
+
+<script lang="ts" setup name="SystemChart">
+import { themeAppStore } from "@/store/theme";
+import { PieChart } from "echarts/charts";
+import {
+  LegendComponent,
+  TitleComponent,
+  TooltipComponent,
+} from "echarts/components";
+import * as echarts from "echarts/core";
+import { CanvasRenderer } from "echarts/renderers";
+import { computed } from "vue";
+import VChart from "vue-echarts";
+import { useI18n } from "vue-i18n";
+import { formatDecimals } from "@/utils/common";
+
+const { t } = useI18n();
+const themeStore = themeAppStore();
+
+// Required components
+echarts.use([
+  CanvasRenderer,
+  PieChart,
+  TitleComponent,
+  TooltipComponent,
+  LegendComponent,
+]);
+
+const props = defineProps({
+  systemData: {
+    type: Object,
+    required: true,
+    default: () => {},
+  },
+  chartCol: {
+    type: Number,
+    default: 8,
+  },
+});
+
+const currentTheme = computed(() => {
+  return themeStore.theme;
+});
+const currentThemeBg = computed(() => {
+  return themeStore.theme === "dark" ? "#2d2d2d" : "";
+});
+const memoryInfo = computed(() => {
+  return `${formatDecimals(props.systemData.memoryUsed)}GB/${formatDecimals(
+    props.systemData.memoryTotal
+  )}GB`;
+});
+
+const cpuOption = computed(() => ({
+  color: ["#5470c6", "#D8D8D8"],
+  backgroundColor: currentThemeBg.value,
+  title: {
+    text: "CPU Usage",
+    left: "16",
+    top: "16",
+    textStyle: {
+      fontSize: 14,
+      fontWeight: "500",
+    },
+  },
+  tooltip: { trigger: "item", formatter: "{b}: {c}%" },
+  series: [
+    {
+      name: "CPU Usage",
+      type: "pie",
+      radius: ["65%", "80%"],
+      center: ["50%", "75%"],
+      itemStyle: {
+        borderRadius: 15,
+      },
+      label: {
+        show: false,
+        position: "center",
+        fontSize: 12,
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: "12",
+          fontWeight: "bold",
+        },
+      },
+      startAngle: 180,
+      endAngle: 360,
+      data: [
+        {
+          value: props.systemData.cpuUsage,
+          name: t("setting.used"),
+          label: { show: true, formatter: "{b}: {d}%" },
+        },
+        {
+          value: 100 - props.systemData.cpuUsage,
+          name: t("setting.notUsed"),
+          label: { show: false },
+        },
+      ],
+    },
+  ],
+}));
+const gpuOption = computed(() => ({
+  color: ["#91cc75", "#D8D8D8"],
+  backgroundColor: currentThemeBg.value,
+  title: {
+    text: "GPU Usage",
+    left: "16",
+    top: "16",
+    textStyle: {
+      fontSize: 14,
+      fontWeight: "500",
+    },
+  },
+  tooltip: { trigger: "item", formatter: "{b}: {c}%" },
+  series: [
+    {
+      name: "GPU Usage",
+      type: "pie",
+      radius: ["65%", "80%"],
+      center: ["50%", "75%"],
+      itemStyle: {
+        borderRadius: 15,
+      },
+      label: {
+        show: false,
+        position: "center",
+        fontSize: 12,
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: "14",
+          fontWeight: "bold",
+        },
+      },
+      startAngle: 180,
+      endAngle: 360,
+      data: [
+        {
+          value: props.systemData.gpuUsage,
+          name: t("setting.used"),
+          label: { show: true, formatter: "{b}: {d}%" },
+        },
+        {
+          value: 100 - props.systemData.gpuUsage,
+          name: t("setting.notUsed"),
+          label: { show: false },
+        },
+      ],
+    },
+  ],
+}));
+const diskOption = computed(() => ({
+  color: ["#fac858", "#D8D8D8"],
+  backgroundColor: currentThemeBg.value,
+  title: {
+    text: "Disk Usage",
+    left: "16",
+    top: "16",
+    textStyle: {
+      fontSize: 14,
+      fontWeight: "500",
+    },
+  },
+  tooltip: { trigger: "item", formatter: "{b}: {c}%" },
+  series: [
+    {
+      name: "Disk Usage",
+      type: "pie",
+      radius: ["65%", "80%"],
+      center: ["50%", "75%"],
+      itemStyle: {
+        borderRadius: 15,
+      },
+      label: {
+        show: false,
+        position: "center",
+        fontSize: 12,
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: "14",
+          fontWeight: "bold",
+        },
+      },
+      startAngle: 180,
+      endAngle: 360,
+      data: [
+        {
+          value: props.systemData.diskUsage,
+          name: t("setting.used"),
+          label: { show: true, formatter: "{b}: {d}%" },
+        },
+        {
+          value: 100 - props.systemData.diskUsage,
+          name: t("setting.notUsed"),
+          label: { show: false },
+        },
+      ],
+    },
+  ],
+}));
+const memoryOption = computed(() => ({
+  color: ["#66C7FD", "#D8D8D8"],
+  backgroundColor: currentThemeBg.value,
+  title: [
+    {
+      text: "Memory Usage",
+      left: "16",
+      top: "16",
+      textStyle: {
+        fontSize: 14,
+        fontWeight: "500",
+      },
+    },
+    {
+      subtext: memoryInfo.value,
+      left: "20",
+      bottom: "10",
+      textAlign: "left",
+      verticalAlign: "bottom",
+    },
+  ],
+  tooltip: { trigger: "item", formatter: "{b}: {c}%" },
+  series: [
+    {
+      name: "Memory Usage",
+      type: "pie",
+      radius: ["65%", "80%"],
+      center: ["50%", "75%"],
+      itemStyle: {
+        borderRadius: 15,
+      },
+      label: {
+        show: false,
+        position: "center",
+        fontSize: 12,
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: "14",
+          fontWeight: "bold",
+        },
+      },
+      startAngle: 180,
+      endAngle: 360,
+      data: [
+        {
+          value: props.systemData.memoryUsage,
+          name: t("setting.used"),
+          label: { show: true, formatter: "{b}: {d}%" },
+        },
+        {
+          value: 100 - props.systemData.memoryUsage,
+          name: t("setting.notUsed"),
+          label: { show: false },
+        },
+      ],
+    },
+  ],
+}));
+</script>
+
+<style scoped lang="less">
+.chart-wrap {
+  width: 100%;
+  height: 170px;
+  border-radius: 8px;
+  background-color: var(--bg-card-color);
+  overflow: hidden;
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/Table.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/Table.vue
new file mode 100644
index 0000000000..a7451c177e
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/Table.vue
@@ -0,0 +1,283 @@
+<template>
+  <div class="table-container">
+    <div class="header-wrap">
+      <span class="title">{{ $t("setting.pipelines") }}</span>
+      <div class="btn-wrap">
+        <a-config-provider :theme="antTheme.subTheme">
+          <a-button
+            type="primary"
+            :icon="h(CloudUploadOutlined)"
+            @click="handleImport()"
+            >{{ $t("setting.import") }}</a-button
+          ></a-config-provider
+        >
+        <a-button type="primary" @click="handleCreate">
+          <template #icon>
+            <PlusOutlined />
+          </template>
+          {{ $t("setting.create") }}</a-button
+        >
+      </div>
+    </div>
+    <a-table
+      :columns="tableColumns"
+      :data-source="tableList"
+      :pagination="false"
+    >
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.dataIndex === 'name'">
+          <span @click="handleView(record)" class="click-link">{{
+            record.name
+          }}</span>
+        </template>
+        <template v-if="column.dataIndex === 'status'">
+          <span>
+            <a-tag
+              :bordered="false"
+              :color="record.status?.active ? 'success' : 'default'"
+            >
+              {{ record.status?.active ? "Activated" : "Inactive" }}
+            </a-tag>
+          </span>
+        </template>
+        <template v-else-if="column.dataIndex === 'operation'">
+          <a-space wrap>
+            <a-button
+              type="primary"
+              ghost
+              size="small"
+              :disabled="record.status?.active"
+              @click="handleUpdate(record)"
+            >
+              {{ $t("common.update") }}</a-button
+            >
+            <a-button
+              v-if="!record.status?.active"
+              size="small"
+              class="intel-btn-success"
+              @click="handleSwitchState(record)"
+              >{{ $t("common.active") }}</a-button
+            >
+            <a-button
+              v-if="record.status?.active"
+              size="small"
+              class="intel-btn-warning"
+              @click="handleSwitchState(record)"
+              >{{ $t("common.deactivate") }}</a-button
+            >
+            <a-button
+              danger
+              size="small"
+              :disabled="record.status?.active"
+              @click="handleDelete(record)"
+              >{{ $t("common.delete") }}
+            </a-button>
+            <a-button
+              v-if="record.status?.active"
+              type="primary"
+              size="small"
+              :icon="h(CommentOutlined)"
+              @click="jumpChatbot"
+              >{{ $t("setting.jump") }}</a-button
+            >
+          </a-space>
+        </template>
+      </template>
+    </a-table>
+    <a-pagination
+      v-if="paginationData.total > 10"
+      v-model:current="paginationData.pageNum"
+      v-model:pageSize="paginationData.pageSize"
+      showSizeChanger
+      :total="paginationData.total"
+    />
+  </div>
+</template>
+
+<script lang="ts" setup name="Table">
+import {
+  requestPipelineDelete,
+  requestPipelineSwitchState,
+} from "@/api/pipeline";
+import router from "@/router";
+import { pipelineAppStore } from "@/store/pipeline";
+import { antTheme } from "@/utils/antTheme";
+import {
+  CloseCircleFilled,
+  CloudUploadOutlined,
+  CommentOutlined,
+  PlusOutlined,
+} from "@ant-design/icons-vue";
+import { Modal } from "ant-design-vue";
+import { createVNode, h, ref } from "vue";
+
+const pipelineStore = pipelineAppStore();
+
+const props = defineProps({
+  tableData: {
+    type: Array,
+    default: () => [],
+  },
+});
+
+const emit = defineEmits(["update", "create", "view", "import", "search"]);
+const paginationData = reactive<paginationType>({
+  total: props.tableData.length || 0,
+  pageNum: 1,
+  pageSize: 10,
+});
+const tableColumns = ref<TableColumns[]>([
+  {
+    title: "Name",
+    dataIndex: "name",
+    width: "25%",
+  },
+  {
+    title: "ID",
+    dataIndex: "idx",
+    width: "25%",
+    ellipsis: true,
+  },
+  {
+    title: "Status",
+    dataIndex: "status",
+    width: "20%",
+  },
+  {
+    title: "Operation",
+    dataIndex: "operation",
+    width: "30%",
+    fixed: "right",
+  },
+]);
+const tableList = computed(() => {
+  const { pageNum, pageSize } = paginationData;
+  const start = (pageNum - 1) * pageSize;
+  const end = start + pageSize;
+  return props.tableData.slice(start, end);
+});
+//create
+const handleCreate = () => {
+  emit("create");
+};
+//edit
+const handleUpdate = (row: EmptyObjectType) => {
+  emit("update", row);
+};
+
+//detail
+const handleView = (row: EmptyObjectType) => {
+  emit("view", row);
+};
+//activate/deactivate
+const handleSwitchState = (row: EmptyObjectType) => {
+  const { active } = row?.status;
+
+  const text = active ? "deactivate" : "activate";
+  Modal.confirm({
+    title: "Prompt",
+    content: `Are you sure ${text} this pipeline?`,
+    okText: "Confirm",
+    async onOk() {
+      await requestPipelineSwitchState(row.name, { active: !active });
+      pipelineStore.setPipelineActivate(active ? "" : row.name);
+      emit("search");
+    },
+  });
+};
+//delete
+const handleDelete = (row: EmptyObjectType) => {
+  Modal.confirm({
+    title: "Delete",
+    icon: createVNode(CloseCircleFilled, { class: "error-icon" }),
+    content: `Are you sure delete this pipeline?`,
+    okText: "Confirm",
+    okType: "danger",
+    async onOk() {
+      await requestPipelineDelete(row.name);
+      paginationData.pageNum = 1;
+      emit("search");
+    },
+  });
+};
+
+//import
+const handleImport = () => {
+  emit("import");
+};
+//Jump Chatbot
+const jumpChatbot = () => {
+  router.push("/chatbot");
+};
+
+watch(
+  () => props.tableData,
+  (tableData) => {
+    const activatedPipeline: EmptyObjectType =
+      tableData?.find((item: any) => item.status?.active) || {};
+
+    const { name = "" } = activatedPipeline;
+    pipelineStore.setPipelineActivate(name);
+  },
+  { immediate: true, deep: true }
+);
+</script>
+
+<style scoped lang="less">
+.table-container {
+  .p-16;
+  .pb-24;
+  .mt-20;
+  border-radius: 8px;
+  background-color: var(--bg-content-color);
+
+  .header-wrap {
+    .flex-between;
+    .mb-20;
+  }
+  .title {
+    .fs-16;
+    font-weight: 600;
+    color: var(--font-main-color);
+  }
+  .btn-wrap {
+    display: flex;
+    gap: 12px;
+  }
+  .intel-tag {
+    border-radius: 10px;
+    font-weight: 600;
+    display: inline-flex;
+    align-items: center;
+    line-height: 18px;
+  }
+}
+.click-link {
+  color: var(--color-primary);
+  cursor: pointer;
+  transition: color 0.3s;
+  &:hover {
+    color: var(--color-primary-hover);
+    text-decoration: underline;
+  }
+}
+
+.custom-benchmark {
+  position: absolute;
+  top: -40px;
+  height: 36px;
+  z-index: 20;
+
+  .container {
+    padding: 8px 16px;
+  }
+
+  h2 {
+    font-size: 14px;
+    padding: 0;
+    font-weight: 500;
+    color: #595959;
+    justify-content: end;
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Activated.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Activated.vue
new file mode 100644
index 0000000000..deca091010
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Activated.vue
@@ -0,0 +1,64 @@
+<template>
+  <a-form
+    :model="form"
+    :rules="rules"
+    name="basic"
+    layout="vertical"
+    ref="formRef"
+    autocomplete="off"
+    class="form-wrap"
+  >
+    <a-form-item label="Activated" name="active">
+      <a-radio-group v-model:value="form.active">
+        <a-radio :value="true">Activated</a-radio>
+        <a-radio :value="false">Inactive</a-radio>
+      </a-radio-group>
+    </a-form-item>
+  </a-form>
+</template>
+
+<script lang="ts" setup name="Basic">
+import type { FormInstance } from "ant-design-vue";
+import { reactive, ref } from "vue";
+
+const props = defineProps({
+  formData: {
+    type: Object,
+    default: () => {},
+  },
+});
+
+interface FormType {
+  active: boolean;
+}
+const { active = false } = props.formData;
+
+const formRef = ref<FormInstance>();
+const form = reactive<FormType>({
+  active,
+});
+const rules = reactive({
+  active: [{ required: true, message: "Please activated", trigger: "change" }],
+});
+// Validate the form, throw results form
+const handleValidate = (): Promise<object> => {
+  return new Promise((resolve) => {
+    formRef.value
+      ?.validate()
+      .then(() => {
+        resolve({
+          result: true,
+          data: form,
+        });
+      })
+      .catch(() => {
+        resolve({ result: false });
+      });
+  });
+};
+defineExpose({
+  validate: handleValidate,
+});
+</script>
+
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Basic.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Basic.vue
new file mode 100644
index 0000000000..6adaa78620
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Basic.vue
@@ -0,0 +1,81 @@
+<template>
+  <a-form
+    :model="form"
+    :rules="rules"
+    name="basic"
+    layout="vertical"
+    ref="formRef"
+    autocomplete="off"
+    class="form-wrap"
+  >
+    <a-form-item label="Name" name="name">
+      <a-input
+        v-model:value.trim="form.name"
+        :maxlength="30"
+        :disabled="disabledName"
+      />
+      <FormTooltip title="The name identifier of the pipeline" />
+    </a-form-item>
+  </a-form>
+</template>
+
+<script lang="ts" setup name="Basic">
+import type { FormInstance } from "ant-design-vue";
+import { reactive, ref, computed } from "vue";
+
+const props = defineProps({
+  formData: {
+    type: Object,
+    default: () => {},
+  },
+  formType: {
+    type: String,
+    default: "create",
+  },
+});
+const disabledName = computed(() => {
+  const { formType } = props;
+  return formType === "update";
+});
+interface FormType {
+  name: string;
+}
+const { name = "default" } = props.formData;
+
+const formRef = ref<FormInstance>();
+const form = reactive<FormType>({
+  name,
+});
+const rules = reactive({
+  name: [
+    { required: true, message: "Please input name", trigger: "blur" },
+    {
+      min: 2,
+      max: 30,
+      message: "Name should be between 2 and 30 characters",
+      trigger: "blur",
+    },
+  ],
+});
+// Validate the form, throw results form
+const handleValidate = (): Promise<object> => {
+  return new Promise((resolve) => {
+    formRef.value
+      ?.validate()
+      .then(() => {
+        resolve({
+          result: true,
+          data: form,
+        });
+      })
+      .catch(() => {
+        resolve({ result: false });
+      });
+  });
+};
+defineExpose({
+  validate: handleValidate,
+});
+</script>
+
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/CreateDialog.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/CreateDialog.vue
new file mode 100644
index 0000000000..4368e868ba
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/CreateDialog.vue
@@ -0,0 +1,225 @@
+<template>
+  <a-modal
+    v-model:open="modelVisible"
+    width="900px"
+    centered
+    destroyOnClose
+    title="Create Pipeline"
+    :keyboard="false"
+    :maskClosable="false"
+    @cancel="handleClose"
+  >
+    <div class="step-container">
+      <div
+        v-for="step in stepList"
+        :key="step.index"
+        :class="['step-wrap', currentStep === step.index ? 'step-active' : '']"
+      >
+        <SvgIcon :name="step.icon" :size="16" inherit />
+        {{ step.label }}
+      </div>
+    </div>
+    <div class="body-container">
+      <keep-alive>
+        <component
+          :is="currentComponent"
+          :form-data="formData"
+          ref="pipelineRef"
+        />
+      </keep-alive>
+    </div>
+    <template #footer>
+      <a-button
+        type="primary"
+        ghost
+        @click="handleLast"
+        v-if="currentStep > 1 && currentStep <= 7"
+        >Back</a-button
+      >
+      <a-button
+        type="primary"
+        @click="handleNext"
+        v-if="currentStep >= 1 && currentStep < 7"
+        >Next</a-button
+      >
+      <a-button
+        key="submit"
+        type="primary"
+        :loading="submitLoading"
+        @click="handleSubmit"
+        v-if="currentStep === 7"
+        >Submit</a-button
+      >
+    </template>
+  </a-modal>
+</template>
+
+<script lang="ts" setup name="CreateDialog">
+import { requestPipelineCreate } from "@/api/pipeline";
+import { computed, markRaw, ref } from "vue";
+import {
+  Activated,
+  Basic,
+  Generator,
+  Indexer,
+  NodeParser,
+  PostProcessor,
+  Retriever,
+} from "./index";
+
+const emit = defineEmits(["search", "close"]);
+const modelVisible = ref<boolean>(true);
+const formData = reactive<EmptyObjectType>({});
+const submitLoading = ref<boolean>(false);
+const currentStep = ref<number>(1);
+const pipelineRef = ref<any>(null);
+const stepList = ref<EmptyArrayType>([
+  {
+    label: "Basic",
+    index: 1,
+    icon: "icon-basic",
+    component: markRaw(Basic),
+  },
+  {
+    label: "Node Parser",
+    index: 2,
+    icon: "icon-node-parser",
+    component: markRaw(NodeParser),
+  },
+  {
+    label: "Indexer",
+    index: 3,
+    icon: "icon-indexer",
+    component: markRaw(Indexer),
+  },
+  {
+    label: "Retriever",
+    index: 4,
+    icon: "icon-retriever",
+    component: markRaw(Retriever),
+  },
+  {
+    label: "PostProcessor",
+    index: 5,
+    icon: "icon-post-processor",
+    component: markRaw(PostProcessor),
+  },
+  {
+    label: "Generator",
+    index: 6,
+    icon: "icon-generator",
+    component: markRaw(Generator),
+  },
+  {
+    label: "Activated",
+    index: 7,
+    icon: "icon-active",
+    component: markRaw(Activated),
+  },
+]);
+
+const currentComponent = computed(() => {
+  return stepList.value.find((item) => item.index === currentStep.value)
+    ?.component;
+});
+
+//last
+const handleLast = () => {
+  if (currentStep.value > 1) {
+    currentStep.value--;
+  }
+};
+
+//next，update form
+const handleNext = async () => {
+  if (pipelineRef.value) {
+    const { result = false, data = {} } = await pipelineRef.value?.validate();
+
+    if (result) {
+      Object.assign(formData, data);
+      if (currentStep.value < 7) {
+        currentStep.value++;
+      }
+    } else {
+      console.log("Form validation failed");
+    }
+  }
+};
+// Submit
+const handleSubmit = async () => {
+  await handleNext();
+
+  submitLoading.value = true;
+  requestPipelineCreate(formData)
+    .then(() => {
+      emit("search");
+      handleClose();
+    })
+    .catch((error) => {
+      console.error(error);
+    })
+    .finally(() => {
+      submitLoading.value = false;
+    });
+};
+
+//close
+const handleClose = () => {
+  emit("close");
+};
+</script>
+
+<style scoped lang="less">
+@keyframes expandBorder {
+  to {
+    width: 100%;
+    left: 0;
+    transform: none;
+  }
+}
+.step-container {
+  width: 100%;
+  margin-bottom: 20px;
+  border-radius: 6px 6px 0 0;
+  overflow: hidden;
+  display: flex;
+
+  .step-wrap {
+    flex: 1;
+    background-color: var(--menu-bg);
+    border-bottom: 1px solid var(--border-main-color);
+    height: 38px;
+    line-height: 38px;
+    text-align: center;
+    color: var(--font-text-color);
+    i {
+      position: relative;
+      top: 1px;
+    }
+
+    &.step-active {
+      color: var(--color-primary);
+      font-weight: 600;
+      background-color: var(--color-primaryBg);
+      position: relative;
+      i {
+        font-weight: 500;
+      }
+      &::after {
+        content: "";
+        position: absolute;
+        bottom: 0;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 0;
+        height: 2px;
+        background-color: var(--color-primary-hover);
+        animation: expandBorder 1s forwards;
+      }
+    }
+  }
+}
+.body-container {
+  min-height: 400px;
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/EditDialog.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/EditDialog.vue
new file mode 100644
index 0000000000..28bb06bd3b
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/EditDialog.vue
@@ -0,0 +1,239 @@
+<template>
+  <a-modal
+    v-model:open="modelVisible"
+    width="800px"
+    centered
+    destroyOnClose
+    title="Create Pipeline"
+    :keyboard="false"
+    :maskClosable="false"
+    :closable="false"
+    :footer="null"
+    class="edit-dialog"
+  >
+    <div class="edit-container">
+      <div class="menu-container">
+        <div
+          v-for="step in stepList"
+          :key="step.index"
+          :class="[
+            'step-wrap',
+            currentStep === step.index ? 'step-active' : '',
+          ]"
+          @click="handleSelect(step.index)"
+        >
+          <SvgIcon :name="step.icon" :size="16" inherit />
+          {{ step.label }}
+        </div>
+      </div>
+      <div class="body-container">
+        <div class="module-title">{{ currentTitle }}</div>
+        <keep-alive>
+          <component
+            :is="currentComponent"
+            :form-data="formData"
+            form-type="update"
+            ref="pipelineRef"
+            class="component-wrap"
+          />
+        </keep-alive>
+        <div class="footer-wrap">
+          <a-button type="primary" ghost @click="handleCancle">Cancel</a-button>
+          <a-button
+            key="submit"
+            type="primary"
+            :loading="submitLoading"
+            @click="handleSubmit"
+            >Update</a-button
+          >
+        </div>
+      </div>
+    </div>
+  </a-modal>
+</template>
+
+<script lang="ts" setup name="EditDialog">
+import { requestPipelineUpdate } from "@/api/pipeline";
+import _ from "lodash";
+import { computed, markRaw, ref } from "vue";
+import {
+  Activated,
+  Basic,
+  Generator,
+  Indexer,
+  NodeParser,
+  PostProcessor,
+  Retriever,
+} from "./index";
+
+const props = defineProps({
+  dialogData: {
+    type: Object,
+    default: () => {},
+  },
+});
+
+const emit = defineEmits(["search", "close"]);
+const modelVisible = ref<boolean>(true);
+const formData = _.cloneDeep(props.dialogData);
+const submitLoading = ref<boolean>(false);
+const currentStep = ref<number>(1);
+const pipelineRef = ref<any>(null);
+const stepList = ref<EmptyArrayType>([
+  {
+    label: "Basic",
+    index: 1,
+    icon: "icon-basic",
+    component: markRaw(Basic),
+  },
+  {
+    label: "Node Parser",
+    index: 2,
+    icon: "icon-node-parser",
+    component: markRaw(NodeParser),
+  },
+  {
+    label: "Indexer",
+    index: 3,
+    icon: "icon-indexer",
+    component: markRaw(Indexer),
+  },
+  {
+    label: "Retriever",
+    index: 4,
+    icon: "icon-retriever",
+    component: markRaw(Retriever),
+  },
+  {
+    label: "PostProcessor",
+    index: 5,
+    icon: "icon-post-processor",
+    component: markRaw(PostProcessor),
+  },
+  {
+    label: "Generator",
+    index: 6,
+    icon: "icon-generator",
+    component: markRaw(Generator),
+  },
+  {
+    label: "Activated",
+    index: 7,
+    icon: "icon-active",
+    component: markRaw(Activated),
+  },
+]);
+
+const currentComponent = computed(() => {
+  return stepList.value.find((item) => item.index === currentStep.value)
+    ?.component;
+});
+
+const currentTitle = computed(() => {
+  return stepList.value.find((item) => item.index === currentStep.value)?.label;
+});
+//last
+const handleCancle = () => {
+  emit("close");
+};
+
+//next，update form
+const handleSelect = async (value?: number) => {
+  if (pipelineRef.value) {
+    const { result = false, data = {} } = await pipelineRef.value?.validate();
+    if (result) {
+      await Object.assign(formData, data);
+      if (value) currentStep.value = value;
+    } else {
+      console.log("Form validation failed");
+    }
+  }
+};
+// Submit
+const handleSubmit = async () => {
+  await handleSelect();
+  submitLoading.value = true;
+  const { name } = props.dialogData;
+
+  requestPipelineUpdate(name, formData)
+    .then(() => {
+      emit("search");
+      handleCancle();
+    })
+    .catch((error) => {
+      console.error(error);
+    })
+    .finally(() => {
+      submitLoading.value = false;
+    });
+};
+</script>
+
+<style scoped lang="less">
+@keyframes expandBorder {
+  to {
+    width: 100%;
+    left: 0;
+    transform: none;
+  }
+}
+.edit-container {
+  display: grid;
+  grid-template-columns: 1fr 3fr;
+  border-radius: 6px;
+  overflow: hidden;
+  min-height: 500px;
+  .menu-container {
+    padding: 10px 16px;
+    background-color: var(--bg-card-color);
+
+    .step-wrap {
+      height: 40px;
+      line-height: 40px;
+      color: var(--font-text-color);
+      padding: 0 8px;
+      border-radius: 4px;
+      margin-bottom: 4px;
+      cursor: pointer;
+      &:hover {
+        background-color: var(--color-primaryBg);
+      }
+
+      &.step-active {
+        color: var(--color-primary);
+        font-weight: 600;
+        background-color: var(--color-primaryBg);
+        position: relative;
+        i {
+          font-weight: 500;
+        }
+      }
+    }
+  }
+  .body-container {
+    overflow: auto;
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    .component-wrap {
+      flex: 1;
+      padding: 10px 20px 20px 20px;
+    }
+    .module-title {
+      padding: 12px 20px 0 20px;
+      font-size: 18px;
+      font-weight: 500;
+      color: var(--font-main-color);
+    }
+    .footer-wrap {
+      width: 100%;
+      padding: 10px 24px;
+      display: flex;
+      justify-content: flex-end;
+      gap: 10px;
+      align-items: center;
+      border-top: 1px solid var(--border-main-color);
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Generator.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Generator.vue
new file mode 100644
index 0000000000..a092604961
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Generator.vue
@@ -0,0 +1,271 @@
+<template>
+  <a-form
+    :model="form"
+    :rules="rules"
+    name="generator"
+    layout="vertical"
+    ref="formRef"
+    autocomplete="off"
+    class="form-wrap"
+  >
+    <a-form-item label="Generator Type" name="generator_type">
+      <a-select
+        showSearch
+        v-model:value="form.generator_type"
+        placeholder="please select Generator Type"
+      >
+        <a-select-option
+          v-for="item in generatorList"
+          :key="item.value"
+          :value="item.value"
+          >{{ item.name }}</a-select-option
+        >
+      </a-select>
+      <FormTooltip title="Local inference generator or vllm generator" />
+    </a-form-item>
+    <a-form-item label="LLM Inference Type" name="inference_type">
+      <a-radio-group v-model:value="form.inference_type">
+        <a-radio value="local">Local</a-radio>
+        <a-radio value="vllm">Vllm</a-radio>
+      </a-radio-group>
+    </a-form-item>
+    <template v-if="form.inference_type === 'local'">
+      <a-form-item
+        label="Large Language Model"
+        :name="['model', 'model_id']"
+        :rules="rules.model_id"
+      >
+        <a-select
+          showSearch
+          v-model:value="form.model.model_id"
+          placeholder="please select Large Language Model"
+          @change="handleModelChange"
+          @dropdownVisibleChange="handleModelVisible"
+        >
+          <a-select-option
+            v-for="item in modelList"
+            :key="item"
+            :value="item"
+            >{{ item }}</a-select-option
+          >
+        </a-select>
+        <FormTooltip title="The large model used for generating dialogues" />
+      </a-form-item>
+      <a-form-item
+        label="LLM run device"
+        :name="['model', 'device']"
+        :rules="rules.device"
+      >
+        <a-select
+          showSearch
+          v-model:value="form.model.device"
+          placeholder="please select LLM run device"
+          @dropdownVisibleChange="handleDeviceVisible"
+        >
+          <a-select-option
+            v-for="item in deviceList"
+            :key="item"
+            :value="item"
+            >{{ item }}</a-select-option
+          >
+        </a-select>
+        <FormTooltip title="The device used by the  LLM" />
+      </a-form-item>
+      <a-form-item
+        label="Weights"
+        :name="['model', 'weight']"
+        :rules="rules.weight"
+      >
+        <a-select
+          showSearch
+          v-model:value="form.model.weight"
+          placeholder="please select Weights"
+          @dropdownVisibleChange="handleWeightVisible"
+        >
+          <a-select-option
+            v-for="item in generatorWeightList"
+            :key="item"
+            :value="item"
+            >{{ item }}</a-select-option
+          >
+        </a-select>
+        <FormTooltip title="Model weight" />
+      </a-form-item>
+    </template>
+  </a-form>
+</template>
+
+<script lang="ts" setup name="Generator">
+import { getModelWeight, getRunDevice, getModelList } from "@/api/pipeline";
+import type { FormInstance } from "ant-design-vue";
+import { reactive, ref } from "vue";
+import { Generator } from "../../enum.ts";
+import { ModelType } from "../../type.ts";
+
+const props = defineProps({
+  formData: {
+    type: Object,
+    default: () => {},
+  },
+});
+interface FormType {
+  generator_type: string;
+  inference_type: string;
+  model: ModelType;
+}
+const {
+  generator_type = "chatqna",
+  inference_type = "local",
+  model = {
+    model_id: "Qwen/Qwen2-7B-Instruct",
+    model_path: "",
+    device: "AUTO",
+    weight: "INT4",
+  },
+} = props.formData?.generator || {};
+
+const formRef = ref<FormInstance>();
+const form = reactive<FormType>({
+  generator_type,
+  inference_type,
+  model,
+});
+const rules = reactive({
+  generator_type: [
+    {
+      required: true,
+      message: "please select Generator Type",
+      trigger: "change",
+    },
+  ],
+  inference_type: [
+    {
+      required: true,
+      message: "Please select LLM Inference Type",
+      trigger: "change",
+    },
+  ],
+  model_id: [
+    {
+      required: true,
+      message: "Please select Large Language Model",
+      trigger: "change",
+    },
+  ],
+  device: [
+    {
+      required: true,
+      message: "Please select LLM run device",
+      trigger: "change",
+    },
+  ],
+  weight: [
+    {
+      required: true,
+      message: "Please select weight",
+      trigger: "change",
+    },
+  ],
+});
+const generatorList = Generator;
+const generatorWeightList = ref<EmptyArrayType>([]);
+const modelList = ref<EmptyArrayType>([]);
+const deviceList = ref<EmptyArrayType>([]);
+
+// Complete model_cath
+const handleModelChange = () => {
+  form.model.weight = "";
+};
+// Handling Model Folding Events
+const handleModelVisible = async (visible: boolean) => {
+  if (visible) {
+    try {
+      const data: any = await getModelList("LLM");
+      modelList.value = [].concat(data);
+    } catch (err) {
+      console.error(err);
+    }
+  }
+};
+const handleModelPath = (
+  modelId: string,
+  weights?: string,
+  prefix: string = "./models/"
+) => {
+  const modelDirs = {
+    fp16_model: prefix + modelId + "/FP16/",
+    int8_model: prefix + modelId + "/INT8_compressed_weights/",
+    int4_model: prefix + modelId + "/INT4_compressed_weights/",
+  };
+  let modelPath: string = "";
+  switch (weights) {
+    case "INT4":
+      modelPath = modelDirs["int4_model"];
+      break;
+    case "INT8":
+      modelPath = modelDirs["int8_model"];
+      break;
+    default:
+      modelPath = modelDirs["fp16_model"];
+  }
+  return modelPath;
+};
+// Handling Device Folding Events
+const handleDeviceVisible = async (visible: boolean) => {
+  if (visible) {
+    try {
+      const data: any = await getRunDevice();
+      deviceList.value = [].concat(data);
+    } catch (err) {
+      console.error(err);
+    }
+  }
+};
+// Handling Weight Folding Events
+const handleWeightVisible = async (visible: boolean) => {
+  if (visible) {
+    try {
+      const { model_id } = form.model;
+      const data: any = await getModelWeight(model_id);
+      generatorWeightList.value = [].concat(data);
+    } catch (err) {
+      console.error(err);
+    }
+  }
+};
+
+// Format parameter
+const formatFormParam = () => {
+  const { inference_type, model } = form;
+  const { model_id, weight } = model;
+  model.model_path = handleModelPath(model_id, weight);
+
+  return {
+    inference_type,
+    prompt_path: "./default_prompt.txt",
+    model,
+  };
+};
+
+// Validate the form, throw results form
+const handleValidate = (): Promise<object> => {
+  return new Promise((resolve) => {
+    formRef.value
+      ?.validate()
+      .then(() => {
+        resolve({
+          result: true,
+          data: { generator: formatFormParam() },
+        });
+      })
+      .catch(() => {
+        resolve({ result: false });
+      });
+  });
+};
+defineExpose({
+  validate: handleValidate,
+});
+</script>
+
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Indexer.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Indexer.vue
new file mode 100644
index 0000000000..db2c8c1e48
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Indexer.vue
@@ -0,0 +1,186 @@
+<template>
+  <a-form
+    :model="form"
+    :rules="rules"
+    name="indexer"
+    layout="vertical"
+    ref="formRef"
+    autocomplete="off"
+    class="form-wrap"
+  >
+    <a-form-item label="Indexer Type" name="indexer_type">
+      <a-select
+        showSearch
+        v-model:value="form.indexer_type"
+        placeholder="please select Indexer Type"
+      >
+        <a-select-option
+          v-for="item in indexerList"
+          :key="item.value"
+          :value="item.value"
+          >{{ item.name }}</a-select-option
+        >
+      </a-select>
+      <FormTooltip
+        title="The type of index structure responsible for building based on the parsed nodes"
+      />
+    </a-form-item>
+    <div class="option-introduction">
+      <InfoCircleOutlined />
+      {{ optionIntroduction }}
+    </div>
+    <a-form-item
+      label="Embedding Model"
+      :name="['embedding_model', 'model_id']"
+      :rules="rules.model_id"
+    >
+      <a-select
+        showSearch
+        v-model:value="form.embedding_model.model_id"
+        placeholder="please select Embedding Model"
+        @change="handleModelChange"
+        @dropdownVisibleChange="handleModelVisible"
+      >
+        <a-select-option v-for="item in modelList" :key="item" :value="item">{{
+          item
+        }}</a-select-option>
+      </a-select>
+      <FormTooltip
+        title="Embed the text data to represent it and build a vector index"
+      />
+    </a-form-item>
+    <a-form-item
+      label="Embedding run device"
+      :name="['embedding_model', 'device']"
+      :rules="rules.device"
+    >
+      <a-select
+        showSearch
+        v-model:value="form.embedding_model.device"
+        placeholder="please select Embedding run device"
+        @dropdownVisibleChange="handleDeviceVisible"
+      >
+        <a-select-option v-for="item in deviceList" :key="item" :value="item">{{
+          item
+        }}</a-select-option>
+      </a-select>
+      <FormTooltip title="The device used by the Embedding Model" />
+    </a-form-item>
+  </a-form>
+</template>
+
+<script lang="ts" setup name="Indexer">
+import { getRunDevice, getModelList } from "@/api/pipeline";
+import type { FormInstance } from "ant-design-vue";
+import { reactive, ref } from "vue";
+import { Indexer } from "../../enum.ts";
+import { ModelType } from "../../type.ts";
+import { InfoCircleOutlined } from "@ant-design/icons-vue";
+
+const props = defineProps({
+  formData: {
+    type: Object,
+    default: () => {},
+  },
+});
+interface FormType {
+  indexer_type: string;
+  embedding_model: ModelType;
+}
+const {
+  indexer_type = "faiss_vector",
+  embedding_model = {
+    model_id: "BAAI/bge-small-en-v1.5",
+    model_path: "./models/BAAI/bge-small-en-v1.5",
+    device: "AUTO",
+    weight: "INT4",
+  },
+} = props.formData?.indexer || {};
+
+const formRef = ref<FormInstance>();
+const form = reactive<FormType>({
+  indexer_type,
+  embedding_model,
+});
+const rules = reactive({
+  indexer_type: [
+    {
+      required: true,
+      message: "please select Indexer Type",
+      trigger: "change",
+    },
+  ],
+  model_id: [
+    {
+      required: true,
+      message: "Please select Embedding Model",
+      trigger: "change",
+    },
+  ],
+  device: [
+    {
+      required: true,
+      message: "Please select device",
+      trigger: "change",
+    },
+  ],
+});
+const indexerList = Indexer;
+const deviceList = ref<EmptyArrayType>([]);
+const modelList = ref<EmptyArrayType>([]);
+
+const optionIntroduction = computed(() => {
+  const { indexer_type } = form;
+
+  return indexerList.find((item) => item.value === indexer_type)?.describe;
+});
+
+// Handling Device Folding Events
+const handleDeviceVisible = async (visible: boolean) => {
+  if (visible) {
+    try {
+      const data: any = await getRunDevice();
+      deviceList.value = [].concat(data);
+    } catch (err) {
+      console.error(err);
+    }
+  }
+};
+// Handling Model Folding Events
+const handleModelVisible = async (visible: boolean) => {
+  if (visible) {
+    try {
+      const data: any = await getModelList("embedding");
+      modelList.value = [].concat(data);
+    } catch (err) {
+      console.error(err);
+    }
+  }
+};
+//Complete model_cath
+const handleModelChange = (value: string) => {
+  // form.embedding_model.model_path = `/home/edgeai/media/models/${value}`;
+  form.embedding_model.model_path = `./models/${value}`;
+};
+// Validate the form, throw results form
+const handleValidate = (): Promise<object> => {
+  return new Promise((resolve) => {
+    formRef.value
+      ?.validate()
+      .then(() => {
+        resolve({
+          result: true,
+          data: { indexer: form },
+        });
+      })
+      .catch(() => {
+        resolve({ result: false });
+      });
+  });
+};
+defineExpose({
+  validate: handleValidate,
+});
+</script>
+
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/NodeParser.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/NodeParser.vue
new file mode 100644
index 0000000000..564d44d184
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/NodeParser.vue
@@ -0,0 +1,238 @@
+<template>
+  <a-form
+    :model="form"
+    :rules="rules"
+    name="nodeParser"
+    layout="vertical"
+    ref="formRef"
+    autocomplete="off"
+    class="form-wrap"
+  >
+    <a-form-item label="Node Parser Type" name="parser_type">
+      <a-select
+        v-model:value="form.parser_type"
+        showSearch
+        placeholder="please select Node Parser Type"
+      >
+        <a-select-option
+          v-for="item in nodeParserList"
+          :key="item.value"
+          :value="item.value"
+          >{{ item.name }}</a-select-option
+        >
+      </a-select>
+      <FormTooltip title="Node parsing type when you use RAG" />
+    </a-form-item>
+    <div class="option-introduction">
+      <InfoCircleOutlined />
+      {{ optionIntroduction }}
+    </div>
+    <template v-if="!isHierarchical && !isSentencewindow">
+      <a-form-item label="Chunk Size" name="chunk_size" class="slider-wrap">
+        <a-slider
+          v-model:value="form.chunk_size"
+          :min="100"
+          :max="2000"
+          :marks="sliderMarks.size"
+        />
+        <a-form-item noStyle>
+          <a-input-number
+            v-model:value="form.chunk_size"
+            :min="100"
+            :max="2000"
+          />
+        </a-form-item>
+        <FormTooltip title="Size of each chunk for processing" />
+      </a-form-item>
+      <a-form-item
+        label="Chunk Overlap"
+        name="chunk_overlap"
+        class="slider-wrap"
+      >
+        <a-slider
+          v-model:value="form.chunk_overlap"
+          :min="0"
+          :max="400"
+          :marks="sliderMarks.overlap"
+        />
+        <a-form-item noStyle>
+          <a-input-number
+            v-model:value="form.chunk_overlap"
+            :min="0"
+            :max="400"
+          />
+        </a-form-item>
+        <FormTooltip title="Overlap size between chunks" /> </a-form-item
+    ></template>
+    <template v-if="isSentencewindow">
+      <a-form-item label="Window Size" name="window_size" class="slider-wrap">
+        <a-slider
+          v-model:value="form.window_size"
+          :min="1"
+          :max="10"
+          :marks="sliderMarks.size"
+        />
+        <a-form-item noStyle>
+          <a-input-number v-model:value="form.window_size" :min="1" :max="10" />
+        </a-form-item>
+        <FormTooltip
+          title="The number of sentences on each side of a sentence to capture"
+        />
+      </a-form-item>
+    </template>
+  </a-form>
+</template>
+
+<script lang="ts" setup name="NodeParser">
+import type { FormInstance } from "ant-design-vue";
+import { computed, reactive, ref } from "vue";
+import { NodeParser } from "../../enum.ts";
+import { InfoCircleOutlined } from "@ant-design/icons-vue";
+
+const props = defineProps({
+  formData: {
+    type: Object,
+    default: () => {},
+  },
+});
+interface FormType {
+  parser_type: string;
+  chunk_size?: number;
+  chunk_overlap?: number;
+  chunk_sizes?: number[];
+  window_size?: number;
+}
+
+const validate = {
+  chunkSize: async (_rule: any, value: number) => {
+    if (!value) {
+      return Promise.reject("Please select Chunk Size");
+    } else if (form?.chunk_overlap && value < form?.chunk_overlap) {
+      return Promise.reject(
+        "The value of Chunk Size cannot be less than Chunk Overlap"
+      );
+    } else {
+      return Promise.resolve();
+    }
+  },
+  chunkOverlap: async (_rule: any, value: number) => {
+    if (!value) {
+      return Promise.reject("Please select Chunk Overlap");
+    } else if (form?.chunk_size && value > form?.chunk_size) {
+      return Promise.reject(
+        "The value of Chunk Overlap cannot be greater than Chunk Size"
+      );
+    } else {
+      return Promise.resolve();
+    }
+  },
+};
+
+const {
+  parser_type = "simple",
+  chunk_size = 250,
+  chunk_overlap = 48,
+  chunk_sizes = [2048, 512, 128],
+  window_size = 3,
+} = props.formData?.node_parser || {};
+
+const formRef = ref<FormInstance>();
+const form = reactive<FormType>({
+  parser_type,
+  chunk_size,
+  chunk_overlap,
+  chunk_sizes,
+  window_size,
+});
+const rules = reactive({
+  parser_type: [
+    {
+      required: true,
+      message: "please select Node Parser Type",
+      trigger: "blur",
+    },
+  ],
+  chunk_size: [
+    {
+      required: true,
+      validator: validate.chunkSize,
+      trigger: ["change", "blur"],
+    },
+  ],
+  chunk_overlap: [
+    {
+      required: true,
+      validator: validate.chunkOverlap,
+      trigger: ["change", "blur"],
+    },
+  ],
+  window_size: [
+    {
+      required: true,
+      message: "Please select Chunk Window Size",
+      trigger: ["change", "blur"],
+    },
+  ],
+});
+
+const nodeParserList = NodeParser;
+const isHierarchical = computed(() => {
+  return form.parser_type === "hierarchical";
+});
+const isSentencewindow = computed(() => {
+  return form.parser_type === "sentencewindow";
+});
+const optionIntroduction = computed(() => {
+  const { parser_type } = form;
+
+  return nodeParserList.find((item) => item.value === parser_type)?.describe;
+});
+const sliderMarks = reactive<EmptyObjectType>({
+  size: {
+    100: "100",
+    2000: "2000",
+  },
+  overlap: {
+    0: "0",
+    400: "400",
+  },
+});
+// Format parameter
+const formatFormParam = () => {
+  const { parser_type, chunk_size, chunk_overlap, chunk_sizes, window_size } =
+    form;
+
+  return {
+    parser_type,
+    chunk_sizes: isHierarchical.value ? chunk_sizes : undefined,
+    window_size: isSentencewindow.value ? window_size : undefined,
+    chunk_size:
+      !isHierarchical.value && !isSentencewindow.value ? chunk_size : undefined,
+    chunk_overlap:
+      !isHierarchical.value && !isSentencewindow.value
+        ? chunk_overlap
+        : undefined,
+  };
+};
+// Validate the form, throw results form
+const handleValidate = (): Promise<object> => {
+  return new Promise((resolve) => {
+    formRef.value
+      ?.validate()
+      .then(() => {
+        resolve({
+          result: true,
+          data: { node_parser: formatFormParam() },
+        });
+      })
+      .catch(() => {
+        resolve({ result: false });
+      });
+  });
+};
+defineExpose({
+  validate: handleValidate,
+});
+</script>
+
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/PostProcessor.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/PostProcessor.vue
new file mode 100644
index 0000000000..d6ce3a3f1b
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/PostProcessor.vue
@@ -0,0 +1,267 @@
+<template>
+  <a-form
+    :model="form"
+    :rules="rules"
+    name="postProcessor"
+    layout="vertical"
+    ref="formRef"
+    autocomplete="off"
+    class="form-wrap"
+  >
+    <div
+      class="processor-wrap"
+      v-for="(processor, index) in form.postprocessor"
+      :key="index"
+    >
+      <a-form-item
+        label="PostProcessor Type"
+        :name="['postprocessor', index, 'processor_type']"
+        :rules="rules.processor_type"
+      >
+        <a-select
+          showSearch
+          v-model:value="processor.processor_type"
+          placeholder="please select PostProcessor Type"
+        >
+          <a-select-option
+            v-for="item in postProcessorList"
+            :key="item.value"
+            :value="item.value"
+            :disabled="getDisable(item.value)"
+            >{{ item.name }}</a-select-option
+          >
+        </a-select>
+        <FormTooltip
+          title="Select postprocessors for post-processing of the context"
+        />
+      </a-form-item>
+      <div class="option-introduction" v-if="processor.processor_type">
+        <InfoCircleOutlined />
+        {{ getOptionIntroduction(processor.processor_type) }}
+      </div>
+      <template v-if="processor.processor_type === 'reranker'">
+        <a-form-item
+          label="Rerank Model"
+          :name="['postprocessor', index, 'reranker_model', 'model_id']"
+          :rules="rules.model_id"
+        >
+          <a-select
+            showSearch
+            v-model:value="processor.reranker_model.model_id"
+            placeholder="please select Rerank Model"
+            @dropdownVisibleChange="handleModelVisible"
+            @change="(value:string) => handleModelChange(processor.reranker_model, value)"
+          >
+            <a-select-option
+              v-for="item in modelList"
+              :key="item"
+              :value="item"
+              >{{ item }}</a-select-option
+            >
+          </a-select>
+          <FormTooltip title="Rerank Model" />
+        </a-form-item>
+        <a-form-item
+          label="Rerank run device"
+          :name="['postprocessor', index, 'reranker_model', 'device']"
+          :rules="rules.device"
+        >
+          <a-select
+            showSearch
+            v-model:value="processor.reranker_model.device"
+            placeholder="please select Rerank run device"
+            @dropdownVisibleChange="handleDeviceVisible"
+          >
+            <a-select-option
+              v-for="item in deviceList"
+              :key="item"
+              :value="item"
+              >{{ item }}</a-select-option
+            >
+          </a-select>
+          <FormTooltip title="Rerank run device" /> </a-form-item
+      ></template>
+      <div class="icon-wrap">
+        <a-tooltip
+          placement="topRight"
+          arrow-point-at-center
+          title="Add"
+          v-if="form.postprocessor?.length < 2"
+        >
+          <PlusCircleOutlined @click="handleAdd" />
+        </a-tooltip>
+        <a-tooltip
+          placement="topRight"
+          arrow-point-at-center
+          title="Delete"
+          v-if="form.postprocessor?.length > 1"
+        >
+          <MinusCircleOutlined @click="handleDelete(index)" />
+        </a-tooltip>
+      </div>
+    </div>
+  </a-form>
+</template>
+
+<script lang="ts" setup name="PostProcessor">
+import { getRunDevice, getModelList } from "@/api/pipeline";
+import { MinusCircleOutlined, PlusCircleOutlined } from "@ant-design/icons-vue";
+import type { FormInstance } from "ant-design-vue";
+import { reactive, ref } from "vue";
+import { PostProcessor } from "../../enum.ts";
+import { ModelType } from "../../type.ts";
+import { InfoCircleOutlined } from "@ant-design/icons-vue";
+
+const props = defineProps({
+  formData: {
+    type: Object,
+    default: () => {},
+  },
+});
+
+interface ProcessorType {
+  processor_type: string;
+  top_n?: number;
+  reranker_model: ModelType;
+}
+interface FormType {
+  postprocessor: ProcessorType[];
+}
+const { postprocessor = [] } = props.formData.postprocessor || [];
+
+const defaultConfig = [
+  {
+    processor_type: "reranker",
+    top_n: 5,
+    reranker_model: {
+      model_id: "BAAI/bge-reranker-large",
+      model_path: "./models/BAAI/bge-reranker-large",
+      device: "AUTO",
+      weight: "INT4",
+    },
+  },
+];
+const formRef = ref<FormInstance>();
+const form = reactive<FormType>({
+  postprocessor: postprocessor.length ? postprocessor : defaultConfig,
+});
+const rules = reactive({
+  processor_type: [
+    {
+      required: true,
+      message: "please select PostProcessor Type",
+      trigger: "change",
+    },
+  ],
+  model_id: [
+    {
+      required: true,
+      message: "Please select Rerank Model",
+      trigger: "change",
+    },
+  ],
+  device: [
+    {
+      required: true,
+      message: "Please select Rerank Device",
+      trigger: "change",
+    },
+  ],
+});
+const postProcessorList = PostProcessor;
+const modelList = ref<EmptyArrayType>([]);
+const deviceList = ref<EmptyArrayType>([]);
+const getDisable = (value: string) => {
+  return form.postprocessor.some((item) => item.processor_type === value);
+};
+const getOptionIntroduction = (value: string) => {
+  return postProcessorList.find((item) => item.value === value)?.describe;
+};
+// Handling Model Folding Events
+const handleModelVisible = async (visible: boolean) => {
+  if (visible) {
+    try {
+      const data: any = await getModelList("reranker");
+      modelList.value = [].concat(data);
+    } catch (err) {
+      console.error(err);
+    }
+  }
+};
+//Complete model_cath
+const handleModelChange = (item: EmptyObjectType, value: string) => {
+  item.model_path = `./models/${value}`;
+};
+const handleAdd = () => {
+  form.postprocessor.push({
+    processor_type: "",
+    reranker_model: {
+      model_id: "",
+      model_path: "",
+      device: "",
+      weight: "INT4",
+    },
+  });
+};
+const handleDelete = (index: number) => {
+  form.postprocessor.splice(index, 1);
+};
+// Handling Device Folding Events
+const handleDeviceVisible = async (visible: boolean) => {
+  if (visible) {
+    try {
+      const data: any = await getRunDevice();
+      deviceList.value = [].concat(data);
+    } catch (err) {
+      console.error(err);
+    }
+  }
+};
+// Validate the form, throw results form
+const handleValidate = (): Promise<object> => {
+  return new Promise((resolve) => {
+    formRef.value
+      ?.validate()
+      .then(() => {
+        resolve({
+          result: true,
+          data: form,
+        });
+      })
+      .catch(() => {
+        resolve({ result: false });
+      });
+  });
+};
+defineExpose({
+  validate: handleValidate,
+});
+</script>
+
+<style scoped lang="less">
+.processor-wrap {
+  padding: 16px;
+  border: 1px solid var(--border-main-color);
+  position: relative;
+  margin-bottom: 20px;
+  border-radius: 6px;
+  .icon-wrap {
+    position: absolute;
+    top: 12px;
+    right: 16px;
+    cursor: pointer;
+    display: inline-flex;
+    align-items: center;
+    gap: 8px;
+    .anticon {
+      font-size: 16px;
+      &:hover {
+        color: var(--color-primary);
+      }
+    }
+  }
+  .slider-wrap {
+    border-bottom: none !important;
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Retriever.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Retriever.vue
new file mode 100644
index 0000000000..5b4bb6b75f
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/Retriever.vue
@@ -0,0 +1,120 @@
+<template>
+  <a-form
+    :model="form"
+    :rules="rules"
+    name="retriever"
+    layout="vertical"
+    ref="formRef"
+    autocomplete="off"
+    class="form-wrap"
+  >
+    <a-form-item label="Retriever Type" name="retriever_type">
+      <a-select
+        v-model:value="form.retriever_type"
+        showSearch
+        placeholder="please select Retriever Type"
+      >
+        <a-select-option
+          v-for="item in retrieverList"
+          :key="item.value"
+          :value="item.value"
+          >{{ item.name }}</a-select-option
+        >
+      </a-select>
+      <FormTooltip
+        title="The retrieval type used when retrieving relevant nodes from the index according to the user's query"
+      />
+    </a-form-item>
+    <div class="option-introduction">
+      <InfoCircleOutlined />
+      {{ optionIntroduction }}
+    </div>
+    <a-form-item label="Search top k" name="retrieve_topk" class="slider-wrap">
+      <a-slider
+        v-model:value="form.retrieve_topk"
+        :min="1"
+        :max="50"
+        :marks="sliderMarks.retrieval"
+      />
+      <a-form-item noStyle>
+        <a-input-number v-model:value="form.retrieve_topk" :min="1" :max="50" />
+      </a-form-item>
+      <FormTooltip title="The number of top k results to return" />
+    </a-form-item>
+  </a-form>
+</template>
+
+<script lang="ts" setup name="Retriever">
+import type { FormInstance } from "ant-design-vue";
+import { reactive, ref } from "vue";
+import { Retriever } from "../../enum.ts";
+import { InfoCircleOutlined } from "@ant-design/icons-vue";
+
+const props = defineProps({
+  formData: {
+    type: Object,
+    default: () => {},
+  },
+});
+interface FormType {
+  retriever_type: string;
+  retrieve_topk: number;
+}
+const { retriever_type = "vectorsimilarity", retrieve_topk = 30 } =
+  props.formData?.retriever || {};
+
+const formRef = ref<FormInstance>();
+const form = reactive<FormType>({
+  retriever_type,
+  retrieve_topk,
+});
+const rules = reactive({
+  retriever_type: [
+    {
+      required: true,
+      message: "please select Retriever Type",
+      trigger: "blur",
+    },
+  ],
+  retrieve_topk: [
+    {
+      required: true,
+      message: "Please select Search top k",
+      trigger: ["change", "blur"],
+    },
+  ],
+});
+const retrieverList = Retriever;
+const optionIntroduction = computed(() => {
+  const { retriever_type } = form;
+
+  return retrieverList.find((item) => item.value === retriever_type)?.describe;
+});
+const sliderMarks = reactive<EmptyObjectType>({
+  retrieval: {
+    1: "1",
+    50: "50",
+  },
+});
+// Validate the form, throw results form
+const handleValidate = (): Promise<object> => {
+  return new Promise((resolve) => {
+    formRef.value
+      ?.validate()
+      .then(() => {
+        resolve({
+          result: true,
+          data: { retriever: form },
+        });
+      })
+      .catch(() => {
+        resolve({ result: false });
+      });
+  });
+};
+defineExpose({
+  validate: handleValidate,
+});
+</script>
+
+<style scoped lang="less"></style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/index.ts b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/index.ts
new file mode 100644
index 0000000000..9540289fc5
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/UpdateDialog/index.ts
@@ -0,0 +1,12 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import Activated from "./Activated.vue";
+import Basic from "./Basic.vue";
+import Generator from "./Generator.vue";
+import Indexer from "./Indexer.vue";
+import NodeParser from "./NodeParser.vue";
+import PostProcessor from "./PostProcessor.vue";
+import Retriever from "./Retriever.vue";
+
+export { Activated, Basic, Generator, Indexer, NodeParser, PostProcessor, Retriever };
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/components/index.ts b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/index.ts
new file mode 100644
index 0000000000..a8003e9dff
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/components/index.ts
@@ -0,0 +1,14 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import Configuration from "./Configuration.vue";
+import DetailDrawer from "./DetailDrawer.vue";
+import ImportDialog from "./ImportDialog.vue";
+import QuickStart from "./QuickStart.vue";
+import System from "./System.vue";
+import SystemChart from "./SystemChart.vue";
+import Table from "./Table.vue";
+import CreateDialog from "./UpdateDialog/CreateDialog.vue";
+import EditDialog from "./UpdateDialog/EditDialog.vue";
+
+export { Configuration, CreateDialog, DetailDrawer, EditDialog, ImportDialog, QuickStart, System, SystemChart, Table };
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/enum.ts b/EdgeCraftRAG/ui/vue/src/views/pipeline/enum.ts
new file mode 100644
index 0000000000..38d8832054
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/enum.ts
@@ -0,0 +1,76 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+export const NodeParser = [
+  {
+    name: "Simple",
+    value: "simple",
+    describe: "Parse text with a preference for complete sentences.",
+  },
+  {
+    name: "Hierarchical",
+    value: "hierarchical",
+    describe: "Splits a document into a recursive hierarchy Nodes using a NodeParser.",
+  },
+  {
+    name: "Sentencewindow",
+    value: "sentencewindow",
+    describe:
+      "Sentence window node parser. Splits a document into Nodes, with each node being a sentence. Each node contains a window from the surrounding sentences in the metadata.",
+  },
+  {
+    name: "Unstructured",
+    value: "unstructured",
+    describe: "UnstructedNodeParser is a component that processes unstructured data.",
+  },
+] as const;
+
+export const Indexer = [
+  {
+    name: "FaissVector",
+    value: "faiss_vector",
+    describe: "Embeddings are stored within a Faiss index.",
+  },
+  {
+    name: "Vector",
+    value: "vector",
+    describe: "Vector Store Index.",
+  },
+] as const;
+export const Retriever = [
+  {
+    name: "Vectorsimilarity",
+    value: "vectorsimilarity",
+    describe: "retrieval according to vector similarity",
+  },
+  {
+    name: "Auto Merge",
+    value: "auto_merge",
+    describe: "This retriever will try to merge context into parent context.",
+  },
+  {
+    name: "Bm25",
+    value: "bm25",
+    describe: "A BM25 retriever that uses the BM25 algorithm to retrieve nodes.",
+  },
+] as const;
+
+export const PostProcessor = [
+  {
+    name: "Reranker",
+    value: "reranker",
+    describe: "The model for reranking.",
+  },
+  {
+    name: "MetadataReplace",
+    value: "metadata_replace",
+    describe: "Used to replace the node content with a field from the node metadata.",
+  },
+] as const;
+
+export const Generator = [
+  {
+    name: "Chatqna",
+    value: "chatqna",
+  },
+] as const;
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/index.vue b/EdgeCraftRAG/ui/vue/src/views/pipeline/index.vue
new file mode 100644
index 0000000000..d30e779a7d
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/index.vue
@@ -0,0 +1,174 @@
+<template>
+  <div class="setting-container">
+    <a-affix :offset-top="450">
+      <div class="chatbot-wrap" @click="jumpChatbot">
+        <SvgIcon
+          name="icon-chatbot"
+          :size="40"
+          :style="{ color: 'var(--color-big-icon)' }"
+          inherit
+        />
+        <div>Chatbot</div>
+      </div></a-affix
+    >
+    <!-- system status -->
+    <System />
+    <!-- pipelines list -->
+    <Table
+      :table-data="tableData"
+      @create="handleCreate"
+      @update="handleUpdate"
+      @view="handleView"
+      @import="handleImport"
+      @search="handleSearch"
+    />
+    <!-- configuration -->
+    <!-- <Configuration :json-data="jsonData" /> -->
+    <!-- createDialog -->
+    <CreateDialog
+      v-if="createDialog.visible"
+      @search="handleSearch"
+      @close="createDialog.visible = false"
+    />
+    <!-- editDialog -->
+    <EditDialog
+      v-if="editDialog.visible"
+      :dialog-data="editDialog.data"
+      @search="handleSearch"
+      @close="editDialog.visible = false"
+    />
+    <!-- detailDrawer -->
+    <DetailDrawer
+      v-if="detailDrawer.visible"
+      :drawer-data="detailDrawer.data"
+      @close="detailDrawer.visible = false"
+    />
+    <!-- importDialog -->
+    <ImportDialog
+      v-if="importDialog.visible"
+      @close="importDialog.visible = false"
+      @search="handleSearch"
+    />
+    <!-- QuickStart -->
+    <QuickStart @create="handleCreate" />
+  </div>
+</template>
+
+<script lang="ts" setup name="xxx">
+import { getPipelineDetialByName, getPipelineList } from "@/api/pipeline";
+import router from "@/router";
+import { pipelineAppStore } from "@/store/pipeline";
+import { useNotification } from "@/utils/common";
+import { onMounted, reactive, ref } from "vue";
+import {
+  Configuration,
+  CreateDialog,
+  DetailDrawer,
+  EditDialog,
+  ImportDialog,
+  QuickStart,
+  System,
+  Table,
+} from "./components";
+
+const { antNotification } = useNotification();
+const pipelineStore = pipelineAppStore();
+const createDialog = reactive<DialogType>({
+  visible: false,
+});
+const editDialog = reactive<DialogType>({
+  visible: false,
+  data: {},
+});
+const detailDrawer = reactive<DialogType>({
+  visible: false,
+  data: {},
+});
+const importDialog = reactive<DialogType>({
+  visible: false,
+});
+
+const tableData = ref<EmptyArrayType>([]);
+const jsonData = ref<string>("");
+
+const queryPipelineList = async () => {
+  const data: any = await getPipelineList();
+
+  tableData.value = [].concat(data);
+};
+
+//create
+const handleCreate = () => {
+  createDialog.visible = true;
+};
+//edit
+const handleUpdate = async (row: EmptyObjectType) => {
+  const data: any = await getPipelineDetialByName(row.name);
+
+  editDialog.data = JSON.parse(data);
+  editDialog.visible = true;
+};
+//detail
+const handleView = async (row: EmptyObjectType) => {
+  const data: any = await getPipelineDetialByName(row.name);
+
+  detailDrawer.data = JSON.parse(data);
+  detailDrawer.visible = true;
+};
+//search
+const handleSearch = () => {
+  queryPipelineList();
+};
+//import
+const handleImport = () => {
+  importDialog.visible = true;
+};
+//Jump Chatbot
+const jumpChatbot = () => {
+  if (pipelineStore.activatedPipeline) {
+    router.push("/chatbot");
+  } else {
+    antNotification(
+      "warning",
+      "Prompt",
+      "There is no available pipeline. Please create or activate it first."
+    );
+  }
+};
+onMounted(() => {
+  queryPipelineList();
+});
+</script>
+
+<style scoped lang="less">
+.setting-container {
+  position: relative;
+
+  .chatbot-wrap {
+    padding: 12px 8px;
+    position: absolute;
+    transform: translateY(-50%);
+    top: 40%;
+    left: -80px;
+    z-index: 99;
+    background-color: var(--bg-content-color);
+    box-shadow: 0px 2px 4px 0px var(--bg-box-shadow);
+    border-radius: 6px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    cursor: pointer;
+    font-size: 14px;
+    font-weight: 500;
+    color: var(--font-main-color);
+    &:hover {
+      color: var(--color-primary);
+    }
+  }
+  @media (max-width: 1100px) {
+    .chatbot-wrap {
+      left: 0;
+    }
+  }
+}
+</style>
diff --git a/EdgeCraftRAG/ui/vue/src/views/pipeline/type.ts b/EdgeCraftRAG/ui/vue/src/views/pipeline/type.ts
new file mode 100644
index 0000000000..f842d086da
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/views/pipeline/type.ts
@@ -0,0 +1,22 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+export interface ModelType {
+  model_id: string;
+  model_path: string;
+  device: string;
+  weight?: string;
+}
+
+export interface SystemType {
+  cpuUsage: number;
+  gpuUsage: number;
+  diskUsage: number;
+  memoryUsage: number;
+  memoryUsed: string;
+  memoryTotal: string;
+  kernel: string;
+  processor: string;
+  os: string;
+  currentTime: string;
+}
diff --git a/EdgeCraftRAG/ui/vue/src/vite-env.d.ts b/EdgeCraftRAG/ui/vue/src/vite-env.d.ts
new file mode 100644
index 0000000000..465d38cd4d
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/src/vite-env.d.ts
@@ -0,0 +1,4 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+/// <reference types="vite/client" />
diff --git a/EdgeCraftRAG/ui/vue/tsconfig.app.json b/EdgeCraftRAG/ui/vue/tsconfig.app.json
new file mode 100644
index 0000000000..b07df7b0a6
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/tsconfig.app.json
@@ -0,0 +1,28 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["./src/*"]
+    },
+    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
+    "skipLibCheck": true,
+
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "isolatedModules": true,
+    "moduleDetection": "force",
+    "noEmit": true,
+    "jsx": "preserve",
+
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "noUncheckedSideEffectImports": true
+  },
+  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
+}
diff --git a/EdgeCraftRAG/ui/vue/tsconfig.json b/EdgeCraftRAG/ui/vue/tsconfig.json
new file mode 100644
index 0000000000..d32ff68200
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/tsconfig.json
@@ -0,0 +1,4 @@
+{
+  "files": [],
+  "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
+}
diff --git a/EdgeCraftRAG/ui/vue/tsconfig.node.json b/EdgeCraftRAG/ui/vue/tsconfig.node.json
new file mode 100644
index 0000000000..c921d9744d
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/tsconfig.node.json
@@ -0,0 +1,22 @@
+{
+  "compilerOptions": {
+    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+    "target": "ES2022",
+    "lib": ["ES2023"],
+    "module": "ESNext",
+    "skipLibCheck": true,
+
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "isolatedModules": true,
+    "moduleDetection": "force",
+    "noEmit": true,
+
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "noUncheckedSideEffectImports": true
+  },
+  "include": ["vite.config.ts"]
+}
diff --git a/EdgeCraftRAG/ui/vue/vite.config.ts b/EdgeCraftRAG/ui/vue/vite.config.ts
new file mode 100644
index 0000000000..23fda73b7b
--- /dev/null
+++ b/EdgeCraftRAG/ui/vue/vite.config.ts
@@ -0,0 +1,76 @@
+// Copyright (C) 2025 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+import vue from "@vitejs/plugin-vue";
+import path, { resolve } from "path";
+import AutoImport from "unplugin-auto-import/vite";
+import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
+import Components from "unplugin-vue-components/vite";
+import { ConfigEnv, defineConfig } from "vite";
+import viteCompression from "vite-plugin-compression";
+
+const pathResolve = (dir: string) => {
+  return resolve(__dirname, ".", dir);
+};
+
+const alias: Record<string, string> = {
+  "@": pathResolve("./src/"),
+  "vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js",
+};
+
+const viteConfig = defineConfig((mode: ConfigEnv) => {
+  return {
+    plugins: [
+      vue(),
+      viteCompression(),
+      AutoImport({
+        imports: ["vue", "vue-router", "pinia"],
+        dts: "src/auto-imports.d.ts",
+        resolvers: [AntDesignVueResolver()],
+      }),
+      Components({
+        resolvers: [
+          AntDesignVueResolver({
+            importStyle: false, // css in js
+          }),
+        ],
+      }),
+    ],
+    root: process.cwd(),
+    resolve: { alias },
+    server: {
+      host: "0.0.0.0",
+      port: 7777,
+      hmr: true,
+      //   proxy: {
+      //     '/api': {
+      //       target: 'http://10.67.106.236:16010',
+      //       ws: true,
+      //       changeOrigin: true,
+      //       rewrite: (path) => path.replace(/^\/api/, ''),
+      //     },
+      //   },
+    },
+    build: {
+      outDir: "dist",
+      chunkSizeWarningLimit: 1500,
+      rollupOptions: {
+        output: {
+          chunkFileNames: "assets/js/[name]-[hash].js",
+          entryFileNames: "assets/js/[name]-[hash].js",
+          assetFileNames: "assets/[ext]/[name]-[hash].[ext]",
+        },
+      },
+    },
+    css: {
+      preprocessorOptions: {
+        less: {
+          javascriptEnabled: true,
+          additionalData: `@import "${path.resolve(__dirname, "src/theme/index.less")}";`,
+        },
+      },
+    },
+  };
+});
+
+export default viteConfig;
