feat(思维导图助手):优化打包

develop
panyy 2026-07-06 19:54:09 +08:00
parent 4b50b6bf4e
commit 9f42a65e9e
9 changed files with 197 additions and 50 deletions

View File

@ -38,11 +38,42 @@ RUN python3 -m pip install -U pip -i https://mirrors.aliyun.com/pypi/simple && \
python3 -m pip install \
"numpy==1.26.4" \
"opencv-python==4.11.0.86" \
"pydantic>=2.12,<3" \
"modelscope" \
"magic-pdf" \
boto3 \
"pdfminer.six>=20251230" \
pypdfium2 \
pypdf \
reportlab \
pdftext \
huggingface-hub \
json-repair \
fast-langdetect \
scikit-image \
openai \
beautifulsoup4 \
magika \
mineru-vl-utils \
qwen-vl-utils \
matplotlib \
ultralytics \
doclayout_yolo==0.0.4 \
dill \
PyYAML \
ftfy \
shapely \
pyclipper \
omegaconf \
onnxruntime \
python-pptx \
python-docx \
openpyxl \
xlrd \
xlwt \
pylatexenc \
mammoth \
-i https://mirrors.aliyun.com/pypi/simple && \
python3 -m pip install --force-reinstall --no-cache-dir "pydantic>=2.12,<3" -i https://mirrors.aliyun.com/pypi/simple && \
python3 -c "import pydantic; print('pydantic', pydantic.__version__); from pydantic import TypeAdapter; print('pydantic TypeAdapter ok')" && \
python3 -m pip cache purge
WORKDIR /app

View File

@ -0,0 +1,36 @@
FROM unis-mindmap-api:latest
RUN python3 -m pip install \
boto3 \
"pdfminer.six>=20251230" \
pypdfium2 \
pypdf \
reportlab \
pdftext \
huggingface-hub \
json-repair \
fast-langdetect \
scikit-image \
openai \
beautifulsoup4 \
magika \
mineru-vl-utils \
qwen-vl-utils \
matplotlib \
ultralytics \
doclayout_yolo==0.0.4 \
dill \
PyYAML \
ftfy \
shapely \
pyclipper \
omegaconf \
onnxruntime \
python-pptx \
python-docx \
openpyxl \
xlrd \
xlwt \
pylatexenc \
mammoth \
-i https://mirrors.aliyun.com/pypi/simple && \
python3 -m pip cache purge

View File

@ -110,6 +110,7 @@ class ModelSingleton:
vllm_llm = vllm.LLM(**kwargs)
elif backend == "vllm-async-engine":
try:
import inspect
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.v1.engine.async_llm import AsyncLLM
except ImportError:
@ -122,6 +123,8 @@ class ModelSingleton:
from mineru_vl_utils import MinerULogitsProcessor
kwargs["logits_processors"] = [MinerULogitsProcessor]
# 使用kwargs为 vllm初始化参数
supported_args = set(inspect.signature(AsyncEngineArgs).parameters)
kwargs = {k: v for k, v in kwargs.items() if k in supported_args}
vllm_async_llm = AsyncLLM.from_engine_args(AsyncEngineArgs(**kwargs))
elif backend == "lmdeploy-engine":
try:

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
VERSION=${VERSION:-$(date +%Y%m%d%H%M%S)}
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
cd "$ROOT_DIR"
echo "Building UnisMindMap API image: unis-mindmap-api:${VERSION}"
sudo docker build -f docker/china/Dockerfile \
-t unis-mindmap-api:${VERSION} \
-t unis-mindmap-api:latest \
-t mineru:test \
.
echo "Building UnisMindMap Web image: unis-mindmap-web:${VERSION}"
sudo docker build -f web_ui/Dockerfile.portable \
-t unis-mindmap-web:${VERSION} \
-t unis-mindmap-web:latest \
web_ui

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
NETWORK=${NETWORK:-unis-ai}
API_NAME=${API_NAME:-mineru-api}
WEB_NAME=${WEB_NAME:-mindmap-web}
WEB_PORT=${WEB_PORT:-9898}
MINERU_API_URL=${MINERU_API_URL:-http://${API_NAME}:8000}
sudo docker network create "$NETWORK" >/dev/null 2>&1 || true
sudo docker rm -f "$API_NAME" "$WEB_NAME" >/dev/null 2>&1 || true
sudo docker run -d --name "$API_NAME" --network "$NETWORK" \
--privileged --ipc=host --shm-size=32g \
-e MINERU_MODEL_SOURCE=local \
-e PYTHONPATH=/app:/usr/local/corex-4.4.0/lib64/python3/dist-packages \
unis-mindmap-api:latest \
mineru-api --host 0.0.0.0 --port 8000
sudo docker run -d --name "$WEB_NAME" --network "$NETWORK" \
-p "${WEB_PORT}:9898" \
-e MINERU_API_URL="$MINERU_API_URL" \
unis-mindmap-web:latest

View File

@ -8,13 +8,44 @@ server {
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
location ~* ^/(assets|mindmap/assets)/.*\.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
rewrite ^/mindmap/(.*)$ /$1 break;
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
location = /mindmap {
return 301 /mindmap/;
}
location = /mindmap/api/health {
proxy_pass ${MINERU_API_URL}/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location = /api/health {
proxy_pass ${MINERU_API_URL}/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ^~ /mindmap/api/ {
rewrite ^/mindmap/api/(.*)$ /api/$1 break;
proxy_pass ${MINERU_API_URL};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100M;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
location = /file_parse {
@ -30,7 +61,7 @@ server {
}
location /api/ {
proxy_pass ${MINERU_API_URL}/;
proxy_pass ${MINERU_API_URL}/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -40,4 +71,13 @@ server {
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
location ^~ /mindmap/ {
rewrite ^/mindmap/(.*)$ /$1 break;
try_files $uri $uri/ /index.html;
}
location / {
try_files $uri $uri/ /index.html;
}
}

View File

@ -41,17 +41,6 @@ export interface MindmapOrganizeProgress extends ParseProgress {
result_md?: string | null
}
export interface MindmapLlmConfig {
base_url?: string
api_key?: string
model?: string
timeout?: number
temperature?: number
max_context_tokens?: number
max_output_tokens?: number
safety_tokens?: number
}
export const documentApi = {
/**
*
@ -84,7 +73,7 @@ export const documentApi = {
formData.append('task_id', taskId)
}
return request.post('/api/file_parse', formData, {
return request.post('/file_parse', formData, {
headers: {
'Content-Type': 'multipart/form-data',
...(taskId ? { 'X-Task-ID': taskId } : {})
@ -99,7 +88,7 @@ export const documentApi = {
},
createParseTask(taskId: string): Promise<ParseProgress> {
return request.post(`/api/parse_tasks/${encodeURIComponent(taskId)}`).then(result => {
return request.post(`/parse_tasks/${encodeURIComponent(taskId)}`).then(result => {
return result as unknown as ParseProgress
})
},
@ -108,30 +97,23 @@ export const documentApi = {
*
*/
getParseProgress(taskId: string): Promise<ParseProgress> {
return request.get(`/api/parse_progress/${taskId}`).then(result => {
return request.get(`/parse_progress/${taskId}`).then(result => {
return result as unknown as ParseProgress
})
},
createMindmapTask(
taskId: string,
markdown: string,
mode = 'smart',
prompt?: string,
llmConfig?: MindmapLlmConfig
): Promise<MindmapOrganizeProgress> {
return request.post(`/api/mindmap_tasks/${encodeURIComponent(taskId)}`, {
createMindmapTask(taskId: string, markdown: string, mode = 'smart', prompt?: string): Promise<MindmapOrganizeProgress> {
return request.post(`/mindmap_tasks/${encodeURIComponent(taskId)}`, {
markdown,
mode,
prompt,
llm_config: llmConfig
prompt
}).then(result => {
return result as unknown as MindmapOrganizeProgress
})
},
getMindmapProgress(taskId: string): Promise<MindmapOrganizeProgress> {
return request.get(`/api/mindmap_progress/${encodeURIComponent(taskId)}`).then(result => {
return request.get(`/mindmap_progress/${encodeURIComponent(taskId)}`).then(result => {
return result as unknown as MindmapOrganizeProgress
})
}

View File

@ -1,18 +1,24 @@
import axios from 'axios'
// 创建 axios 实例
const getApiBaseURL = () => {
const pathname = window.location.pathname
const basePath = pathname === '/mindmap' || pathname.startsWith('/mindmap/') ? '/mindmap' : ''
return `${basePath}/api`
}
// ?? axios ??
const request = axios.create({
baseURL: '',
timeout: 300000, // 5分钟超时
baseURL: getApiBaseURL(),
timeout: 300000, // 5????
headers: {
'Content-Type': 'application/json'
}
})
// 请求拦截器
// ?????
request.interceptors.request.use(
(config) => {
// 可以在这里添加认证token等
// ?????????token?
return config
},
(error) => {
@ -20,13 +26,13 @@ request.interceptors.request.use(
}
)
// 响应拦截器
// ?????
request.interceptors.response.use(
(response) => {
return response.data
},
(error) => {
// 统一错误处理
// ??????
if (error.response) {
const serverMessage = error.response.data?.error || error.response.data?.detail
if (serverMessage) {
@ -36,34 +42,34 @@ request.interceptors.response.use(
switch (error.response.status) {
case 400:
error.message = '请求参数错误'
error.message = '??????'
break
case 401:
error.message = '未授权,请重新登录'
error.message = '?????????'
break
case 403:
error.message = '拒绝访问'
error.message = '????'
break
case 404:
error.message = '请求资源不存在'
error.message = '???????'
break
case 500:
error.message = '服务器内部错误'
error.message = '???????'
break
case 502:
error.message = '网关错误'
error.message = '????'
break
case 503:
error.message = '服务不可用'
error.message = '?????'
break
case 504:
error.message = '网关超时'
error.message = '????'
break
default:
error.message = `连接错误${error.response.status}`
error.message = `????${error.response.status}`
}
} else {
error.message = '网络连接异常'
error.message = '??????'
}
return Promise.reject(error)

View File

@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
base: './',
plugins: [vue()],
resolve: {
alias: {
@ -13,8 +14,13 @@ export default defineConfig({
port: 3000,
proxy: {
'/api': {
target: 'http://10.100.52.76:8000',
target: process.env.VITE_API_PROXY || 'http://127.0.0.1:8000',
changeOrigin: true
},
'/mindmap/api': {
target: process.env.VITE_API_PROXY || 'http://127.0.0.1:8000',
changeOrigin: true,
rewrite: path => path.replace(/^\/mindmap\/api/, '/api')
}
}
},
@ -30,4 +36,4 @@ export default defineConfig({
}
}
}
})
})