Download models/predictor_wrapper.py from OneScience-Group/Medgemma: direct link, hf CLI and curl.
- Browser
- Download file 6.28 kB
-
https://huggingface.co/OneScience-Group/Medgemma/resolve/main/models/predictor_wrapper.py
- Command line
-
hf download hf://OneScience-Group/Medgemma/models/predictor_wrapper.py
-
curl -L -o predictor_wrapper.py https://huggingface.co/OneScience-Group/Medgemma/resolve/main/models/predictor_wrapper.py
6.28 kB
| # MedGemma 推理包装器 | |
| # 包装 MedGemma 原始 predictor.py 的推理逻辑 | |
| import logging | |
| import os | |
| import sys | |
| from typing import Any, Dict, List, Optional | |
| logger = logging.getLogger(__name__) | |
| class MedGemmaPredictor: | |
| """ | |
| MedGemma 推理包装器 | |
| 包装原始 MedGemma predictor 逻辑,提供 OneScience 兼容接口 | |
| """ | |
| def __init__(self, model_runner: Any, configs: Any): | |
| """ | |
| 初始化推理包装器 | |
| Args: | |
| model_runner: 模型运行器(VLLMModelRunner 或 TransformersModelRunner) | |
| configs: 配置对象 | |
| """ | |
| self.model_runner = model_runner | |
| self.configs = configs | |
| # 尝试导入原始 MedGemma 组件(如果可用) | |
| self._init_medgemma_components() | |
| def _init_medgemma_components(self): | |
| """初始化 MedGemma 原始组件""" | |
| try: | |
| # 添加 MedGemma 原始代码路径到 sys.path | |
| medgemma_base = os.path.abspath( | |
| os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "..", "..", "medgemma", "python") | |
| ) | |
| if os.path.exists(medgemma_base) and medgemma_base not in sys.path: | |
| sys.path.insert(0, medgemma_base) | |
| logger.info(f"Added MedGemma path: {medgemma_base}") | |
| # 尝试导入 MedGemma predictor 组件 | |
| try: | |
| from serving import predictor | |
| self.has_original_predictor = True | |
| logger.info("Successfully imported original MedGemma predictor") | |
| except ImportError as e: | |
| logger.warning(f"Could not import original MedGemma predictor: {e}") | |
| self.has_original_predictor = False | |
| except Exception as e: | |
| logger.warning(f"Error initializing MedGemma components: {e}") | |
| self.has_original_predictor = False | |
| def predict( | |
| self, | |
| messages: List[Dict[str, Any]], | |
| max_tokens: int = 500, | |
| temperature: float = 0.7, | |
| top_p: float = 0.9, | |
| n: int = 1, | |
| ) -> Dict[str, Any]: | |
| """ | |
| 运行推理 | |
| Args: | |
| messages: OpenAI Chat Completion 格式的消息列表 | |
| max_tokens: 最大生成 token 数 | |
| temperature: 采样温度 | |
| top_p: Nucleus 采样参数 | |
| n: 生成数量 | |
| Returns: | |
| OpenAI 兼容格式的响应 | |
| """ | |
| # 转换消息为 prompt | |
| prompt = self._messages_to_prompt(messages) | |
| # 运行模型推理 | |
| results = self.model_runner.generate( | |
| prompts=[prompt], | |
| max_tokens=max_tokens, | |
| temperature=temperature, | |
| top_p=top_p, | |
| n=n, | |
| ) | |
| # 格式化响应为 OpenAI 格式 | |
| return self._format_openai_response(results[0], messages) | |
| def _messages_to_prompt(self, messages: List[Dict[str, Any]]) -> str: | |
| """ | |
| 将 OpenAI 消息格式转换为 prompt | |
| Args: | |
| messages: 消息列表 | |
| Returns: | |
| 格式化的 prompt 字符串 | |
| """ | |
| prompt_parts = [] | |
| for message in messages: | |
| role = message.get("role", "user") | |
| content = message.get("content", "") | |
| # 处理不同角色的消息 | |
| if role == "system": | |
| prompt_parts.append(f"System: {content}") | |
| elif role == "user": | |
| prompt_parts.append(f"User: {content}") | |
| elif role == "assistant": | |
| prompt_parts.append(f"Assistant: {content}") | |
| else: | |
| prompt_parts.append(f"{role}: {content}") | |
| # 添加 Assistant 前缀以开始生成 | |
| prompt_parts.append("Assistant:") | |
| return "\n".join(prompt_parts) | |
| def _format_openai_response( | |
| self, | |
| result: Dict[str, Any], | |
| messages: List[Dict[str, Any]] | |
| ) -> Dict[str, Any]: | |
| """ | |
| 将模型输出格式化为 OpenAI Chat Completion 格式 | |
| Args: | |
| result: 模型生成结果 | |
| messages: 原始消息 | |
| Returns: | |
| OpenAI 格式的响应 | |
| """ | |
| import time | |
| import uuid | |
| choices = [] | |
| for idx, output in enumerate(result["outputs"]): | |
| choice = { | |
| "index": idx, | |
| "message": { | |
| "role": "assistant", | |
| "content": output["text"].replace(result["prompt"], "").strip(), | |
| }, | |
| "finish_reason": output.get("finish_reason", "stop"), | |
| } | |
| choices.append(choice) | |
| response = { | |
| "id": f"chatcmpl-{uuid.uuid4().hex[:8]}", | |
| "object": "chat.completion", | |
| "created": int(time.time()), | |
| "model": self.configs.model.variant, | |
| "choices": choices, | |
| "usage": { | |
| "prompt_tokens": result.get("num_input_tokens", 0), | |
| "completion_tokens": sum( | |
| len(output.get("token_ids", [])) if output.get("token_ids") else 0 | |
| for output in result["outputs"] | |
| ), | |
| "total_tokens": result.get("num_input_tokens", 0) + sum( | |
| len(output.get("token_ids", [])) if output.get("token_ids") else 0 | |
| for output in result["outputs"] | |
| ), | |
| }, | |
| } | |
| return response | |
| def predict_with_images( | |
| self, | |
| messages: List[Dict[str, Any]], | |
| images: List[Any], | |
| max_tokens: int = 500, | |
| temperature: float = 0.7, | |
| top_p: float = 0.9, | |
| ) -> Dict[str, Any]: | |
| """ | |
| 多模态推理(文本 + 图像) | |
| Args: | |
| messages: 消息列表 | |
| images: 图像列表 | |
| max_tokens: 最大生成 token 数 | |
| temperature: 采样温度 | |
| top_p: Nucleus 采样参数 | |
| Returns: | |
| 响应字典 | |
| """ | |
| # TODO: 实现多模态推理 | |
| # 这需要集成 MedGemma 的图像处理逻辑 | |
| logger.warning("Multimodal inference not yet fully implemented") | |
| # 暂时只处理文本 | |
| return self.predict(messages, max_tokens, temperature, top_p) | |