J.B-Lin commited on
Commit
8193cd7
·
1 Parent(s): 269d57b

core: 更新 ModelLoader 对接远端 Modal API + README 同步

Browse files
Files changed (3) hide show
  1. README.md +7 -1
  2. core/model_loader.py +106 -14
  3. modal_deploy/README.md +7 -6
README.md CHANGED
@@ -40,7 +40,13 @@ PregoPal 是一款面向孕期家庭的 AI 陪护工具,支持:
40
  | 📝 饮食记录储存(JSON + Markdown) | 已实现 | `modules/diet_logger.py` + `modules/diet_extractor.py` |
41
  | 📊 营养分析与可视化报告 | 基线可用 | `modules/nutrition_analyzer.py` + `modules/nutrition_standards.py` |
42
 
43
- **技术栈**: Python 3.13 · Gradio 6.16 · MiniCPM-o 4.5 · Matplotlib · Pandas
 
 
 
 
 
 
44
 
45
  ---
46
 
 
40
  | 📝 饮食记录储存(JSON + Markdown) | 已实现 | `modules/diet_logger.py` + `modules/diet_extractor.py` |
41
  | 📊 营养分析与可视化报告 | 基线可用 | `modules/nutrition_analyzer.py` + `modules/nutrition_standards.py` |
42
 
43
+ **技术栈**: Python 3.13 · Gradio 6.16 · MiniCPM-o 4.5 · llama-cpp-python (CUDA) · Modal T4 · Matplotlib · Pandas
44
+
45
+ **模型部署**: MiniCPM-o-4_5 GGUF (Q4_K_M) on Modal (T4 GPU, $0.50/hr)
46
+ - API 端点: `https://andrew-jiabin--prego-pal-minicpm-serve.modal.run`
47
+ - 集成方式: `core/model_loader.py` ←HTTP→ Modal API (OpenAI 兼容)
48
+ - 模型数据: Modal Volume `minicpm-o-4_5-models`(持久化,仅需上传一次)
49
+ - 详见: `modal_deploy/README.md`
50
 
51
  ---
52
 
core/model_loader.py CHANGED
@@ -1,26 +1,118 @@
1
  """
2
  PregoPal - 模型加载器
3
  ======================
4
- 加载 MiniCPM-o 4.5 多模态模型
5
 
6
- 当前空接口(等待 MiniCPM-o 部署)
7
- 后续:使用 OpenBMB 官方推理代码加载模型
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  """
9
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  class ModelLoader:
12
- """MiniCPM-o 4.5 模型加载器"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- def __init__(self, model_path: str = None):
15
- self.model_path = model_path
16
- self.model = None
17
- self.processor = None
 
 
 
 
18
 
19
- def load(self):
20
- """加载模型(待实现)"""
21
- raise NotImplementedError("等待 MiniCPM-o 4.5 部署后实现")
22
 
23
  def unload(self):
24
- """卸载模型释放显存"""
25
- self.model = None
26
- self.processor = None
 
 
1
  """
2
  PregoPal - 模型加载器
3
  ======================
4
+ 对接 Modal 远端部署的 MiniCPM-o 4.5 API
5
 
6
+ 架构
7
+ core/model_loader.py ←HTTP→ modal_deploy/deploy.py (Modal T4 GPU)
8
+
9
+ llama-cpp-python (CUDA)
10
+
11
+ MiniCPM-o-4_5 GGUF (Volume)
12
+
13
+ 用法:
14
+ from core.model_loader import ModelLoader
15
+
16
+ loader = ModelLoader()
17
+ # 远程调用(走 Modal API)
18
+ resp = loader.chat("今天孕妇可以吃什么?")
19
+ # 本地调用(如果有本地模型)
20
+ loader.load_local("/path/to/model.gguf")
21
  """
22
 
23
+ import os
24
+ import logging
25
+ from typing import Optional
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ # Modal 部署的 API 端点(由环境变量配置,默认使用线上 T4 部署)
30
+ DEFAULT_API_BASE = os.environ.get(
31
+ "MINICPM_API_BASE",
32
+ "https://andrew-jiabin--prego-pal-minicpm-serve.modal.run",
33
+ )
34
+
35
 
36
  class ModelLoader:
37
+ """MiniCPM-o 4.5 模型加载器(远端 API 模式 / 本地模式)"""
38
+
39
+ def __init__(self, api_base: str = None):
40
+ self.api_base = (api_base or DEFAULT_API_BASE).rstrip("/")
41
+ self._client = None # lazy init
42
+ self._local_model = None
43
+ self._local_mmproj = None
44
+
45
+ # ── 远端 API 模式(默认) ─────────────────────────────────
46
+
47
+ @property
48
+ def client(self):
49
+ if self._client is None:
50
+ from modal_deploy.client import MiniCPMClient
51
+ self._client = MiniCPMClient(base_url=self.api_base)
52
+ return self._client
53
+
54
+ def chat(self, messages: list[dict], **kwargs) -> dict:
55
+ """远端对话(走 Modal T4)"""
56
+ return self.client.chat(messages, **kwargs)
57
+
58
+ def ask(self, prompt: str, system_prompt: Optional[str] = None) -> str:
59
+ """远端简化调用"""
60
+ return self.client.ask(prompt, system_prompt=system_prompt)
61
+
62
+ def chat_with_image(
63
+ self, prompt: str, image_base64: str, **kwargs
64
+ ) -> dict:
65
+ """远端多模态"""
66
+ return self.client.chat_with_image(prompt, image_base64, **kwargs)
67
+
68
+ def health(self) -> dict:
69
+ """检查远端服务状态"""
70
+ return self.client.health()
71
+
72
+ # ── 本地模式(备选,用于开发调试) ────────────────────────
73
+
74
+ def load_local(
75
+ self,
76
+ model_path: str,
77
+ mmproj_path: Optional[str] = None,
78
+ n_gpu_layers: int = -1,
79
+ n_ctx: int = 8192,
80
+ ):
81
+ """加载本地 GGUF 模型(需安装 llama-cpp-python)"""
82
+ try:
83
+ from llama_cpp import Llama
84
+ except ImportError:
85
+ raise ImportError(
86
+ "本地模式需要 llama-cpp-python: pip install llama-cpp-python"
87
+ )
88
+
89
+ kwargs = dict(
90
+ model_path=model_path,
91
+ n_gpu_layers=n_gpu_layers,
92
+ n_ctx=n_ctx,
93
+ verbose=False,
94
+ )
95
+ if mmproj_path and os.path.isfile(mmproj_path):
96
+ kwargs["mmproj"] = mmproj_path
97
+
98
+ logger.info(f"[ModelLoader] Loading local model: {model_path}")
99
+ self._local_model = Llama(**kwargs)
100
+ self._local_mmproj = mmproj_path
101
+ logger.info("[ModelLoader] [OK] Local model loaded")
102
 
103
+ def chat_local(self, messages: list[dict], **kwargs) -> dict:
104
+ """本地推理"""
105
+ if self._local_model is None:
106
+ raise RuntimeError("本地模型未加载,请先调用 load_local()")
107
+ return self._local_model.create_chat_completion(
108
+ messages=messages,
109
+ **kwargs,
110
+ )
111
 
112
+ # ── 通用接口 ─────────────────────────────────────────────
 
 
113
 
114
  def unload(self):
115
+ """卸载释放资源"""
116
+ self._client = None
117
+ self._local_model = None
118
+ self._local_mmproj = None
modal_deploy/README.md CHANGED
@@ -2,7 +2,7 @@
2
 
3
  ## 概述
4
 
5
- 在 Modal.com 的 A100 GPU 上部署 MiniCPM-o-4_5 模型,通过 llama.cpp server 提供 OpenAI 兼容的 API 接口。
6
 
7
  ## 架构
8
 
@@ -129,9 +129,9 @@ modal run modal_deploy/deploy.py::upload_models
129
 
130
  在 `deploy.py` 中可调整的参数:
131
  - `container_idle_timeout=300` — 空闲 5 分钟后自动关闭
132
- - `gpu="A100"` — GPU 类型(也可用 H100、A10G
133
- - `-c 8192` — 上下文窗口大小
134
- - `-ngl 99` — GPU 层数(99=全部到 GPU)
135
 
136
  ## 与 PregoPal 集成
137
 
@@ -154,7 +154,8 @@ def ask_minicpm(prompt: str) -> str:
154
  ## 注意事项
155
 
156
  1. **冷启动**:无请求 >5 分钟后容器关闭,下次请求需等待 ~30-60 秒(模型加载)
157
- 2. **成本**:A100 约 $1.10/小时,按实际使用计费
158
  3. **模型上传**:Volume 是持久化的,模型只需上传一次
159
  4. **并发**:`@modal.concurrent(max_inputs=10)` 支持 10 个并发请求
160
- 5. **修复说明**: deploy.py `find_mmproj_files()` 递归搜索到 9 .gguf 文件并全部作为 `--mmproj` 传入导致 llama-server 启动失败。新版已明确指定 `--mmproj` 使 vision 投影层(仅一个文件)。
 
 
2
 
3
  ## 概述
4
 
5
+ 在 Modal.com 的 **T4 GPU** 上部署 MiniCPM-o-4_5 模型,通过 llama-cpp-python (CUDA pre-built wheel) 提供 OpenAI 兼容的 API 接口。
6
 
7
  ## 架构
8
 
 
129
 
130
  在 `deploy.py` 中可调整的参数:
131
  - `container_idle_timeout=300` — 空闲 5 分钟后自动关闭
132
+ - `gpu="T4"` — GPU 类型(16GB VRAM,$0.50/hr。也可用 A100: $1.50/hr
133
+ - `n_ctx=8192` — 上下文窗口大小
134
+ - `n_gpu_layers=-1` — GPU 层数(-1=全部到 GPU)
135
 
136
  ## 与 PregoPal 集成
137
 
 
154
  ## 注意事项
155
 
156
  1. **冷启动**:无请求 >5 分钟后容器关闭,下次请求需等待 ~30-60 秒(模型加载)
157
+ 2. **成本**:T4 约 $0.50/小时(vs A100 $1.50/hr),按实际使用计费。降级节省 **67%**
158
  3. **模型上传**:Volume 是持久化的,模型只需上传一次
159
  4. **并发**:`@modal.concurrent(max_inputs=10)` 支持 10 个并发请求
160
+ 5. **VRAM 使用**:Q4_K_M 模型 4.7GB + vision 0.5GB + KV cache 6GBT4 16GB 完全够
161
+ 6. **修复说明**:旧 deploy.py 的 `find_mmproj_files()` 递归搜索到 9 个 .gguf 文件并全部作为 `--mmproj` 传入,导致启动失败。新版已明确指定 `--mmproj` 使用 vision 投影层(仅一个文件)。