From 886e28c939598c9c4ef86f20d23b3df19a190a78 Mon Sep 17 00:00:00 2001 From: JiaX-TCS Date: Tue, 22 Jul 2025 17:22:11 +0800 Subject: [PATCH 1/5] add models --- .vscode/sftp.json | 9 + DEVELOPMENT.md | 79 +++++++ conftest.py | 12 ++ install_dev.py | 53 +++++ pyproject.toml | 7 + setup.py | 18 ++ spectrumlab/__init__.py | 46 ++++ spectrumlab/config/base_config.py | 37 ++++ spectrumlab/models/__init__.py | 8 +- spectrumlab/models/claude_api.py | 288 +++++++++++++++++++++++++ spectrumlab/models/gemini_api.py | 80 +++++++ spectrumlab/models/gpt4_v_api.py | 147 +++++++++++++ spectrumlab/models/grok_api.py | 76 +++++++ spectrumlab/models/qwen_vl_api.py | 76 +++++++ test_import.py | 39 ++++ tests/conftest.py | 15 ++ tests/models/test_claude_haiku_3_5.py | 40 ++++ tests/models/test_claude_opus_4.py | 40 ++++ tests/models/test_claude_sonnet_3_5.py | 40 ++++ tests/models/test_claude_sonnet_4.py | 40 ++++ tests/models/test_gemini_pro_2_5.py | 40 ++++ tests/models/test_gpt_4_1.py | 40 ++++ tests/models/test_gpt_4_v.py | 40 ++++ tests/models/test_grok_2_v.py | 40 ++++ tests/models/test_internvl.py | 4 +- tests/models/test_qwen_vl.py | 40 ++++ tests/test_import.py | 19 ++ 27 files changed, 1370 insertions(+), 3 deletions(-) create mode 100644 .vscode/sftp.json create mode 100644 DEVELOPMENT.md create mode 100644 conftest.py create mode 100644 install_dev.py create mode 100644 setup.py create mode 100644 spectrumlab/__init__.py create mode 100644 spectrumlab/models/claude_api.py create mode 100644 spectrumlab/models/gemini_api.py create mode 100644 spectrumlab/models/gpt4_v_api.py create mode 100644 spectrumlab/models/grok_api.py create mode 100644 spectrumlab/models/qwen_vl_api.py create mode 100644 test_import.py create mode 100644 tests/conftest.py create mode 100644 tests/models/test_claude_haiku_3_5.py create mode 100644 tests/models/test_claude_opus_4.py create mode 100644 tests/models/test_claude_sonnet_3_5.py create mode 100644 tests/models/test_claude_sonnet_4.py create mode 100644 tests/models/test_gemini_pro_2_5.py create mode 100644 tests/models/test_gpt_4_1.py create mode 100644 tests/models/test_gpt_4_v.py create mode 100644 tests/models/test_grok_2_v.py create mode 100644 tests/models/test_qwen_vl.py create mode 100644 tests/test_import.py diff --git a/.vscode/sftp.json b/.vscode/sftp.json new file mode 100644 index 0000000..cc7c9a3 --- /dev/null +++ b/.vscode/sftp.json @@ -0,0 +1,9 @@ +{ + "host": "101.126.157.149", + "port": 29, + "username": "root", + "protocol": "sftp", + "remotePath": "/root/SpectrumLab", + "privateKeyPath": "~/.ssh/id_rsa", + "uploadOnSave": true +} \ No newline at end of file diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..035ad90 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,79 @@ +# Development Guide + +## 解决模块导入问题 + +如果你遇到 `ModuleNotFoundError: No module named 'spectrumlab'` 错误,请按照以下步骤解决: + +### 方法1: 安装开发模式(推荐) + +```bash +# 在项目根目录运行 +pip install -e . +``` + +### 方法2: 使用安装脚本 + +```bash +# 在项目根目录运行 +python install_dev.py +``` + +### 方法3: 手动设置Python路径 + +```bash +# 在项目根目录运行测试 +PYTHONPATH=. python -m pytest tests/ +``` + +或者在Python脚本中: + +```python +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +``` + +## 运行测试 + +安装完成后,你可以运行测试: + +```bash +# 运行所有测试 +python -m pytest tests/ + +# 运行特定测试 +python -m pytest tests/models/test_claude.py + +# 运行导入测试 +python -m pytest tests/test_import.py +``` + +## 环境配置 + +确保你已经在项目根目录创建了 `.env` 文件,包含必要的API密钥: + +```env +# Claude API Configuration +CLAUDE_API_KEY=your_claude_key +CLAUDE_BASE_URL=https://api.claude.com +CLAUDE_MODEL_NAME=claude-model + +# 其他API配置... +``` + +## 常见问题 + +### 1. 导入错误 +- 确保在项目根目录运行命令 +- 确保已安装所有依赖:`pip install -r requirements.txt` +- 尝试重新安装开发模式:`pip install -e . --force-reinstall` + +### 2. 环境变量问题 +- 确保 `.env` 文件在项目根目录 +- 检查环境变量是否正确设置 +- 重启Python解释器或IDE + +### 3. 测试失败 +- 检查API密钥是否正确配置 +- 确保网络连接正常 +- 查看具体的错误信息 \ No newline at end of file diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..b10e1df --- /dev/null +++ b/conftest.py @@ -0,0 +1,12 @@ +""" +Pytest configuration file for SpectrumLab project. +This ensures the spectrumlab module is available for all tests. +""" + +import sys +import os +from pathlib import Path + +# Add current directory to Python path +current_dir = Path(__file__).parent +sys.path.insert(0, str(current_dir)) \ No newline at end of file diff --git a/install_dev.py b/install_dev.py new file mode 100644 index 0000000..f54a4c1 --- /dev/null +++ b/install_dev.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +""" +Development installation script for SpectrumLab. +This script installs the package in development mode. +""" + +import subprocess +import sys +import os + +def run_command(command, description): + """Run a command and handle errors.""" + print(f"🔄 {description}...") + try: + result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) + print(f"✅ {description} completed successfully") + return True + except subprocess.CalledProcessError as e: + print(f"❌ {description} failed:") + print(f" Command: {command}") + print(f" Error: {e.stderr}") + return False + +def main(): + print("🚀 Installing SpectrumLab in development mode...") + + # Check if we're in the right directory + if not os.path.exists("spectrumlab"): + print("❌ Error: spectrumlab directory not found. Please run this script from the project root.") + sys.exit(1) + + # Install in development mode + if not run_command("pip install -e .", "Installing package in development mode"): + print("❌ Installation failed. Please check the error messages above.") + sys.exit(1) + + # Test import + print("🧪 Testing imports...") + try: + import spectrumlab + from spectrumlab.models import Claude + from spectrumlab.config import Config + print("✅ All imports successful!") + except ImportError as e: + print(f"❌ Import test failed: {e}") + print("💡 Try running: python -m pytest tests/test_import.py") + sys.exit(1) + + print("\n🎉 SpectrumLab development environment setup complete!") + print("💡 You can now run tests with: python -m pytest tests/") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index a4151e5..cf983a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,3 +32,10 @@ allow-direct-references = true [tool.black] line-length = 120 skip-string-normalization = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +addopts = "-v --tb=short" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ce486a3 --- /dev/null +++ b/setup.py @@ -0,0 +1,18 @@ +""" +Setup script for SpectrumLab package. +This allows the package to be installed in development mode. +""" + +from setuptools import setup, find_packages + +setup( + name="spectrumlab", + version="0.0.1", + description="Comprehensive toolkit for spectroscopy deep learning", + packages=find_packages(), + install_requires=[ + "dotenv>=0.9.9", + "openai>=1.93.0", + ], + python_requires=">=3.10", +) \ No newline at end of file diff --git a/spectrumlab/__init__.py b/spectrumlab/__init__.py new file mode 100644 index 0000000..7a20670 --- /dev/null +++ b/spectrumlab/__init__.py @@ -0,0 +1,46 @@ +""" +SpectrumLab - Comprehensive toolkit for spectroscopy deep learning. + +This package provides tools for dataset loading, training, evaluation, +inference, and more for spectroscopy applications. +""" + +__version__ = "0.0.1" +__author__ = "Zhuo Yang, Tianfan Fu" + +# Import main modules +from . import models +from . import config +from . import evaluator +from . import benchmark +from . import utils +from . import cli + +# Export main classes for convenience +from .models import DeepSeek, GPT4o, InternVL +from .models import Claude_Sonnet_3_5, Claude_Opus_4, Claude_Haiku_3_5, Claude_Sonnet_4 +from .models import GPT4_1, GPT4_Vision +from .models import Grok_2_Vision +from .models import Gemini_2_5_Pro +from .config import Config + +__all__ = [ + "models", + "config", + "evaluator", + "benchmark", + "utils", + "cli", + "DeepSeek", + "GPT4o", + "InternVL", + "Claude_Sonnet_3_5", + "Claude_Opus_4", + "Claude_Haiku_3_5", + "Claude_Sonnet_4", + "GPT4_1", + "GPT4_Vision", + "Grok_2_Vision", + "Gemini_2_5_Pro", + "Config", +] \ No newline at end of file diff --git a/spectrumlab/config/base_config.py b/spectrumlab/config/base_config.py index 11d54aa..f98f47e 100644 --- a/spectrumlab/config/base_config.py +++ b/spectrumlab/config/base_config.py @@ -6,11 +6,16 @@ # Load .env from project root directory project_root = Path(__file__).parent.parent.parent env_path = project_root / ".env" +print(env_path) load_dotenv(env_path) @dataclass class Config: + # This api key is for testing closed MLLMs by Boyue Richdata + BOYUE_API_KEY: str = os.getenv("BOYUE_API_KEY") + BOYUE_BASE_URL: str = os.getenv("BOYUE_BASE_URL") + # DeepSeek API Configuration deepseek_api_key: str = os.getenv("DEEPSEEK_API_KEY") deepseek_base_url: str = os.getenv("DEEPSEEK_BASE_URL") @@ -25,3 +30,35 @@ class Config: internvl_api_key: str = os.getenv("INTERNVL_API_KEY") internvl_base_url: str = os.getenv("INTERNVL_BASE_URL") internvl_model_name: str = os.getenv("INTERNVL_MODEL_NAME") + + # Claude API Configuration + claude_api_key: str = BOYUE_API_KEY + claude_base_url: str = BOYUE_BASE_URL + claude_sonnet_3_5_model_name: str = os.getenv("CLAUDE_SONNET_3_5") + claude_opus_4_model_name: str = os.getenv("CLAUDE_OPUS_4") + claude_haiku_3_5_model_name: str = os.getenv("CLAUDE_HAIKU_3_5") + claude_sonnet_4_model_name: str = os.getenv("CLAUDE_SONNET_4") + + # GPT-4.1, GPT-4-Vision + gpt4_1_api_key: str = BOYUE_API_KEY + gpt4_1_base_url: str = BOYUE_BASE_URL + gpt4_1_model_name: str = os.getenv("GPT4_1") + gpt4_vision_api_key: str = BOYUE_API_KEY + gpt4_vision_base_url: str = BOYUE_BASE_URL + gpt4_vision_model_name: str = os.getenv("GPT4_VISION") + + # Grok-2-Vision + grok_2_vision_api_key: str = BOYUE_API_KEY + grok_2_vision_base_url: str = BOYUE_BASE_URL + grok_2_vision_model_name: str = os.getenv("GROK_2_VISION") + + # Gemini-2.5-Pro + gemini_2_5_pro_api_key: str = BOYUE_API_KEY + gemini_2_5_pro_base_url: str = BOYUE_BASE_URL + gemini_2_5_pro_model_name: str = os.getenv("GEMINI_2_5_PRO") + + # Qwen-VL-Max + qwen_vl_api_key: str = BOYUE_API_KEY + qwen_vl_base_url: str = BOYUE_BASE_URL + qwen_vl_model_name: str = os.getenv("QWEN_VL") + diff --git a/spectrumlab/models/__init__.py b/spectrumlab/models/__init__.py index 93ce4d1..5537518 100644 --- a/spectrumlab/models/__init__.py +++ b/spectrumlab/models/__init__.py @@ -1,5 +1,11 @@ from .deepseek_api import DeepSeek from .gpt4o_api import GPT4o from .internvl_api import InternVL +from .claude_api import Claude_Sonnet_3_5, Claude_Opus_4, Claude_Haiku_3_5, Claude_Sonnet_4 +from .gpt4_v_api import GPT4_1, GPT4_Vision +from .grok_api import Grok_2_Vision +from .gemini_api import Gemini_2_5_Pro +from .qwen_vl_api import Qwen_VL_Max -__all__ = ["DeepSeek", "GPT4o", "InternVL"] +__all__ = ["DeepSeek", "GPT4o", "InternVL", "Claude_Sonnet_3_5", "Claude_Opus_4", + "Claude_Haiku_3_5", "Claude_Sonnet_4", "GPT4_1", "GPT4_Vision", "Grok_2_Vision", "Gemini_2_5_Pro", "Qwen_VL_Max"] diff --git a/spectrumlab/models/claude_api.py b/spectrumlab/models/claude_api.py new file mode 100644 index 0000000..9711061 --- /dev/null +++ b/spectrumlab/models/claude_api.py @@ -0,0 +1,288 @@ +from typing import Optional, Union, Dict, Any +from .base_api import BaseAPIModel +from spectrumlab.config import Config +from openai import OpenAI + + +class Claude_Sonnet_3_5(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.BOYUE_API_KEY + self.base_url = base_url or config.BOYUE_BASE_URL + self.model_name = model_name or config.claude_sonnet_3_5_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "Claude API key not found. Please set CLAUDE_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_out_len: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_out_len: Maximum tokens to generate + + Returns: + Generated response string + """ + + # Link: https://docs.anthropic.com/claude/reference/getting-started-with-the-api + messages = [] + + if isinstance(prompt, dict) and "images" in prompt: + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_out_len, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"Claude API call failed: {e}") + + +class Claude_Opus_4(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.BOYUE_API_KEY + self.base_url = base_url or config.BOYUE_BASE_URL + self.model_name = model_name or config.claude_opus_4_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "Claude API key not found. Please set CLAUDE_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_out_len: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_out_len: Maximum tokens to generate + + Returns: + Generated response string + """ + + # Link: https://docs.anthropic.com/claude/reference/getting-started-with-the-api + messages = [] + + if isinstance(prompt, dict) and "images" in prompt: + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_out_len, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"Claude API call failed: {e}") + + +class Claude_Haiku_3_5(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.BOYUE_API_KEY + self.base_url = base_url or config.BOYUE_BASE_URL + self.model_name = model_name or config.claude_haiku_3_5_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "Claude API key not found. Please set CLAUDE_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_out_len: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_out_len: Maximum tokens to generate + + Returns: + Generated response string + """ + + # Link: https://docs.anthropic.com/claude/reference/getting-started-with-the-api + messages = [] + + if isinstance(prompt, dict) and "images" in prompt: + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_out_len, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"Claude API call failed: {e}") + +class Claude_Sonnet_4(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.BOYUE_API_KEY + self.base_url = base_url or config.BOYUE_BASE_URL + self.model_name = model_name or config.claude_sonnet_4_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "Claude API key not found. Please set CLAUDE_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_out_len: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_out_len: Maximum tokens to generate + + Returns: + Generated response string + """ + + # Link: https://docs.anthropic.com/claude/reference/getting-started-with-the-api + messages = [] + + if isinstance(prompt, dict) and "images" in prompt: + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_out_len, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"Claude API call failed: {e}") + diff --git a/spectrumlab/models/gemini_api.py b/spectrumlab/models/gemini_api.py new file mode 100644 index 0000000..9626c64 --- /dev/null +++ b/spectrumlab/models/gemini_api.py @@ -0,0 +1,80 @@ +from typing import Dict, Any, Optional, Union +from .base_api import BaseAPIModel +from spectrumlab.config import Config +from openai import OpenAI + + +class Gemini_2_5_Pro(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.gemini_2_5_pro_api_key + self.base_url = base_url or config.gemini_2_5_pro_base_url + self.model_name = model_name or config.gemini_2_5_pro_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "Gemini-2.5-Pro API key not found. Please set GEMINI_2_5_PRO_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_tokens: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_tokens: Maximum tokens to generate + + Returns: + Generated response string + """ + messages = [] + + # Handle multimodal vs text-only prompts + if isinstance(prompt, dict) and "images" in prompt: + # Multimodal prompt + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + # Text-only prompt + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + # print(messages) + + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_tokens, + ) + # print(response) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"Gemini-2.5-Pro API call failed: {e}") diff --git a/spectrumlab/models/gpt4_v_api.py b/spectrumlab/models/gpt4_v_api.py new file mode 100644 index 0000000..c14312c --- /dev/null +++ b/spectrumlab/models/gpt4_v_api.py @@ -0,0 +1,147 @@ +from typing import Dict, Any, Optional, Union +from .base_api import BaseAPIModel +from spectrumlab.config import Config +from openai import OpenAI + + +class GPT4_1(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.gpt4_1_api_key + self.base_url = base_url or config.gpt4_1_base_url + self.model_name = model_name or config.gpt4_1_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "GPT-4.1 API key not found. Please set GPT4_1_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_tokens: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_tokens: Maximum tokens to generate + + Returns: + Generated response string + """ + messages = [] + + # Handle multimodal vs text-only prompts + if isinstance(prompt, dict) and "images" in prompt: + # Multimodal prompt + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + # Text-only prompt + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_tokens, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"GPT-4.1 API call failed: {e}") + +class GPT4_Vision(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.gpt4_vision_api_key + self.base_url = base_url or config.gpt4_vision_base_url + self.model_name = model_name or config.gpt4_vision_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "GPT-4 Vision API key not found. Please set GPT4_VISION_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_tokens: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_tokens: Maximum tokens to generate + + Returns: + Generated response string + """ + messages = [] + + # Handle multimodal vs text-only prompts + if isinstance(prompt, dict) and "images" in prompt: + # Multimodal prompt + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + # Text-only prompt + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_tokens, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"GPT-4 Vision API call failed: {e}") \ No newline at end of file diff --git a/spectrumlab/models/grok_api.py b/spectrumlab/models/grok_api.py new file mode 100644 index 0000000..243287a --- /dev/null +++ b/spectrumlab/models/grok_api.py @@ -0,0 +1,76 @@ +from typing import Dict, Any, Optional, Union +from .base_api import BaseAPIModel +from spectrumlab.config import Config +from openai import OpenAI + + +class Grok_2_Vision(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.grok_2_vision_api_key + self.base_url = base_url or config.grok_2_vision_base_url + self.model_name = model_name or config.grok_2_vision_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "Grok-2-Vision API key not found. Please set GROK_2_VISION_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_tokens: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_tokens: Maximum tokens to generate + + Returns: + Generated response string + """ + messages = [] + + # Handle multimodal vs text-only prompts + if isinstance(prompt, dict) and "images" in prompt: + # Multimodal prompt + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + # Text-only prompt + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_tokens, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"Grok-2-Vision API call failed: {e}") diff --git a/spectrumlab/models/qwen_vl_api.py b/spectrumlab/models/qwen_vl_api.py new file mode 100644 index 0000000..d4c6817 --- /dev/null +++ b/spectrumlab/models/qwen_vl_api.py @@ -0,0 +1,76 @@ +from typing import Dict, Any, Optional, Union +from .base_api import BaseAPIModel +from spectrumlab.config import Config +from openai import OpenAI + + +class Qwen_VL_Max(BaseAPIModel): + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + model_name: Optional[str] = None, + **kwargs, + ): + config = Config() + + # Use provided parameters or fall back to config + self.api_key = api_key or config.qwen_vl_api_key + self.base_url = base_url or config.qwen_vl_base_url + self.model_name = model_name or config.qwen_vl_model_name + + # Validate that we have required configuration + if not self.api_key: + raise ValueError( + "Qwen-VL-Max API key not found. Please set QWEN_VL_MAX_API_KEY in your .env file " + "or provide api_key parameter." + ) + + self.client = OpenAI( + api_key=self.api_key, + base_url=self.base_url, + ) + + # Initialize parent class + super().__init__(model_name=self.model_name, **kwargs) + + def generate( + self, prompt: Union[str, Dict[str, Any]], max_tokens: int = 512 + ) -> str: + """ + Generate response supporting both text and multimodal input. + + Args: + prompt: Either text string or multimodal dict + max_tokens: Maximum tokens to generate + + Returns: + Generated response string + """ + messages = [] + + # Handle multimodal vs text-only prompts + if isinstance(prompt, dict) and "images" in prompt: + # Multimodal prompt + content = [] + + content.append({"type": "text", "text": prompt["text"]}) + + for image_data in prompt["images"]: + content.append(image_data) + + messages.append({"role": "user", "content": content}) + else: + # Text-only prompt + text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") + messages.append({"role": "user", "content": text_content}) + + try: + response = self.client.chat.completions.create( + model=self.model_name, + messages=messages, + max_tokens=max_tokens, + ) + return response.choices[0].message.content + except Exception as e: + raise RuntimeError(f"Qwen-VL-Max API call failed: {e}") diff --git a/test_import.py b/test_import.py new file mode 100644 index 0000000..575ee01 --- /dev/null +++ b/test_import.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +""" +Simple test script to verify spectrumlab module imports work correctly. +""" + +import sys +import os + +# Add project root to Python path +project_root = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, project_root) + +try: + print("Testing spectrumlab imports...") + + # Test basic imports + from spectrumlab import Config + print("✅ Config import successful") + + from spectrumlab.models import Claude + print("✅ Claude model import successful") + + from spectrumlab.utils.image_utils import encode_image_to_base64 + print("✅ image_utils import successful") + + from spectrumlab.benchmark.signal_group import SignalGroup + print("✅ signal_group import successful") + + from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + print("✅ choice_evaluator import successful") + + print("\n🎉 All imports successful!") + +except ImportError as e: + print(f"❌ Import error: {e}") + sys.exit(1) +except Exception as e: + print(f"❌ Unexpected error: {e}") + sys.exit(1) \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..a43be3b --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,15 @@ +""" +Pytest configuration file for SpectrumLab tests. +This file sets up the Python path to include the project root. +""" + +import sys +import os +from pathlib import Path + +# Get the project root directory (parent of tests directory) +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root)) + +# Also add the project root to PYTHONPATH environment variable +os.environ['PYTHONPATH'] = str(project_root) + os.pathsep + os.environ.get('PYTHONPATH', '') \ No newline at end of file diff --git a/tests/models/test_claude_haiku_3_5.py b/tests/models/test_claude_haiku_3_5.py new file mode 100644 index 0000000..16ea288 --- /dev/null +++ b/tests/models/test_claude_haiku_3_5.py @@ -0,0 +1,40 @@ +from spectrumlab.models import Claude_Haiku_3_5 +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = Claude_Haiku_3_5() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = Claude_Haiku_3_5() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = Claude_Haiku_3_5() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_claude_opus_4.py b/tests/models/test_claude_opus_4.py new file mode 100644 index 0000000..66b383b --- /dev/null +++ b/tests/models/test_claude_opus_4.py @@ -0,0 +1,40 @@ +from spectrumlab.models import Claude_Opus_4 +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = Claude_Opus_4() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = Claude_Opus_4() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = Claude_Opus_4() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_claude_sonnet_3_5.py b/tests/models/test_claude_sonnet_3_5.py new file mode 100644 index 0000000..818470a --- /dev/null +++ b/tests/models/test_claude_sonnet_3_5.py @@ -0,0 +1,40 @@ +from spectrumlab.models import Claude_Sonnet_3_5 +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = Claude_Sonnet_3_5() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = Claude_Sonnet_3_5() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = Claude_Sonnet_3_5() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_claude_sonnet_4.py b/tests/models/test_claude_sonnet_4.py new file mode 100644 index 0000000..1d284b3 --- /dev/null +++ b/tests/models/test_claude_sonnet_4.py @@ -0,0 +1,40 @@ +from spectrumlab.models import Claude_Sonnet_4 +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = Claude_Sonnet_4() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = Claude_Sonnet_4() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = Claude_Sonnet_4() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_gemini_pro_2_5.py b/tests/models/test_gemini_pro_2_5.py new file mode 100644 index 0000000..e8f94df --- /dev/null +++ b/tests/models/test_gemini_pro_2_5.py @@ -0,0 +1,40 @@ +from spectrumlab.models import Gemini_2_5_Pro +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = Gemini_2_5_Pro() + prompt = "What is spectroscopy?" + response = model.generate(prompt, 8192) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = Gemini_2_5_Pro() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64, {image_base64}"}, + } + ], + } + response = model.generate(prompt, 8192) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = Gemini_2_5_Pro() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None, max_out_len=8192) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_gpt_4_1.py b/tests/models/test_gpt_4_1.py new file mode 100644 index 0000000..1982848 --- /dev/null +++ b/tests/models/test_gpt_4_1.py @@ -0,0 +1,40 @@ +from spectrumlab.models import GPT4_1 +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = GPT4_1() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = GPT4_1() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = GPT4_1() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_gpt_4_v.py b/tests/models/test_gpt_4_v.py new file mode 100644 index 0000000..9b52442 --- /dev/null +++ b/tests/models/test_gpt_4_v.py @@ -0,0 +1,40 @@ +from spectrumlab.models import GPT4_Vision +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = GPT4_Vision() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = GPT4_Vision() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = GPT4_Vision() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_grok_2_v.py b/tests/models/test_grok_2_v.py new file mode 100644 index 0000000..29bb571 --- /dev/null +++ b/tests/models/test_grok_2_v.py @@ -0,0 +1,40 @@ +from spectrumlab.models import Grok_2_Vision +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = Grok_2_Vision() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = Grok_2_Vision() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = Grok_2_Vision() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/models/test_internvl.py b/tests/models/test_internvl.py index f718961..f469c90 100644 --- a/tests/models/test_internvl.py +++ b/tests/models/test_internvl.py @@ -14,14 +14,14 @@ def test_internvl_text_generation(): def test_internvl_multimodal_generation(): model = InternVL() - image_path = "playground/models/test.png" + image_path = "playground/models/test.jpg" image_base64 = encode_image_to_base64(image_path) prompt = { "text": "Please explain this spectroscopy image.", "images": [ { "type": "image_url", - "image_url": {"url": f"data:image/png;base64,{image_base64}"}, + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, } ], } diff --git a/tests/models/test_qwen_vl.py b/tests/models/test_qwen_vl.py new file mode 100644 index 0000000..8310935 --- /dev/null +++ b/tests/models/test_qwen_vl.py @@ -0,0 +1,40 @@ +from spectrumlab.models import Qwen_VL_Max +from spectrumlab.utils.image_utils import encode_image_to_base64 +from spectrumlab.benchmark.signal_group import SignalGroup +from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator + + +def test_claude_text_generation(): + model = Qwen_VL_Max() + prompt = "What is spectroscopy?" + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_multimodal_generation(): + model = Qwen_VL_Max() + image_path = "playground/models/test.jpg" + image_base64 = encode_image_to_base64(image_path) + prompt = { + "text": "Please explain this spectroscopy image.", + "images": [ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpg;base64,{image_base64}"}, + } + ], + } + response = model.generate(prompt) + assert isinstance(response, str) + assert len(response) > 0 + + +def test_claude_signalgroup_evaluation(): + model = Qwen_VL_Max() + signal_group = SignalGroup("data") + data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) + evaluator = ChoiceEvaluator() + results = evaluator.evaluate(data_items=data, model=model, save_path=None) + assert "metrics" in results + assert "overall" in results["metrics"] diff --git a/tests/test_import.py b/tests/test_import.py new file mode 100644 index 0000000..093f715 --- /dev/null +++ b/tests/test_import.py @@ -0,0 +1,19 @@ +""" +Simple test to verify that spectrumlab module can be imported correctly. +""" + +def test_spectrumlab_import(): + """Test that spectrumlab module can be imported.""" + try: + from spectrumlab.models import Claude + assert Claude is not None + except ImportError as e: + raise ImportError(f"Failed to import spectrumlab.models.Claude: {e}") + +def test_config_import(): + """Test that Config can be imported.""" + try: + from spectrumlab.config import Config + assert Config is not None + except ImportError as e: + raise ImportError(f"Failed to import spectrumlab.config.Config: {e}") \ No newline at end of file From a0df7e1e7ae6ebe6962f0b6e29c8dd97ac15134e Mon Sep 17 00:00:00 2001 From: JiaX-TCS Date: Tue, 22 Jul 2025 17:28:37 +0800 Subject: [PATCH 2/5] new --- DEVELOPMENT.md | 79 -------------------------------------------- install_dev.py | 53 ----------------------------- tests/conftest.py | 15 --------- tests/test_import.py | 19 ----------- 4 files changed, 166 deletions(-) delete mode 100644 DEVELOPMENT.md delete mode 100644 install_dev.py delete mode 100644 tests/conftest.py delete mode 100644 tests/test_import.py diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index 035ad90..0000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,79 +0,0 @@ -# Development Guide - -## 解决模块导入问题 - -如果你遇到 `ModuleNotFoundError: No module named 'spectrumlab'` 错误,请按照以下步骤解决: - -### 方法1: 安装开发模式(推荐) - -```bash -# 在项目根目录运行 -pip install -e . -``` - -### 方法2: 使用安装脚本 - -```bash -# 在项目根目录运行 -python install_dev.py -``` - -### 方法3: 手动设置Python路径 - -```bash -# 在项目根目录运行测试 -PYTHONPATH=. python -m pytest tests/ -``` - -或者在Python脚本中: - -```python -import sys -import os -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) -``` - -## 运行测试 - -安装完成后,你可以运行测试: - -```bash -# 运行所有测试 -python -m pytest tests/ - -# 运行特定测试 -python -m pytest tests/models/test_claude.py - -# 运行导入测试 -python -m pytest tests/test_import.py -``` - -## 环境配置 - -确保你已经在项目根目录创建了 `.env` 文件,包含必要的API密钥: - -```env -# Claude API Configuration -CLAUDE_API_KEY=your_claude_key -CLAUDE_BASE_URL=https://api.claude.com -CLAUDE_MODEL_NAME=claude-model - -# 其他API配置... -``` - -## 常见问题 - -### 1. 导入错误 -- 确保在项目根目录运行命令 -- 确保已安装所有依赖:`pip install -r requirements.txt` -- 尝试重新安装开发模式:`pip install -e . --force-reinstall` - -### 2. 环境变量问题 -- 确保 `.env` 文件在项目根目录 -- 检查环境变量是否正确设置 -- 重启Python解释器或IDE - -### 3. 测试失败 -- 检查API密钥是否正确配置 -- 确保网络连接正常 -- 查看具体的错误信息 \ No newline at end of file diff --git a/install_dev.py b/install_dev.py deleted file mode 100644 index f54a4c1..0000000 --- a/install_dev.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -""" -Development installation script for SpectrumLab. -This script installs the package in development mode. -""" - -import subprocess -import sys -import os - -def run_command(command, description): - """Run a command and handle errors.""" - print(f"🔄 {description}...") - try: - result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) - print(f"✅ {description} completed successfully") - return True - except subprocess.CalledProcessError as e: - print(f"❌ {description} failed:") - print(f" Command: {command}") - print(f" Error: {e.stderr}") - return False - -def main(): - print("🚀 Installing SpectrumLab in development mode...") - - # Check if we're in the right directory - if not os.path.exists("spectrumlab"): - print("❌ Error: spectrumlab directory not found. Please run this script from the project root.") - sys.exit(1) - - # Install in development mode - if not run_command("pip install -e .", "Installing package in development mode"): - print("❌ Installation failed. Please check the error messages above.") - sys.exit(1) - - # Test import - print("🧪 Testing imports...") - try: - import spectrumlab - from spectrumlab.models import Claude - from spectrumlab.config import Config - print("✅ All imports successful!") - except ImportError as e: - print(f"❌ Import test failed: {e}") - print("💡 Try running: python -m pytest tests/test_import.py") - sys.exit(1) - - print("\n🎉 SpectrumLab development environment setup complete!") - print("💡 You can now run tests with: python -m pytest tests/") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index a43be3b..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Pytest configuration file for SpectrumLab tests. -This file sets up the Python path to include the project root. -""" - -import sys -import os -from pathlib import Path - -# Get the project root directory (parent of tests directory) -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -# Also add the project root to PYTHONPATH environment variable -os.environ['PYTHONPATH'] = str(project_root) + os.pathsep + os.environ.get('PYTHONPATH', '') \ No newline at end of file diff --git a/tests/test_import.py b/tests/test_import.py deleted file mode 100644 index 093f715..0000000 --- a/tests/test_import.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Simple test to verify that spectrumlab module can be imported correctly. -""" - -def test_spectrumlab_import(): - """Test that spectrumlab module can be imported.""" - try: - from spectrumlab.models import Claude - assert Claude is not None - except ImportError as e: - raise ImportError(f"Failed to import spectrumlab.models.Claude: {e}") - -def test_config_import(): - """Test that Config can be imported.""" - try: - from spectrumlab.config import Config - assert Config is not None - except ImportError as e: - raise ImportError(f"Failed to import spectrumlab.config.Config: {e}") \ No newline at end of file From 07cde7aaeefdba8711391374a0733e6a306abe8f Mon Sep 17 00:00:00 2001 From: JiaX-TCS Date: Tue, 22 Jul 2025 18:48:21 +0800 Subject: [PATCH 3/5] delete gemini 2.5 pro --- spectrumlab/__init__.py | 2 - spectrumlab/config/base_config.py | 5 -- spectrumlab/models/__init__.py | 3 +- spectrumlab/models/gemini_api.py | 80 ----------------------------- tests/models/test_gemini_pro_2_5.py | 40 --------------- 5 files changed, 1 insertion(+), 129 deletions(-) delete mode 100644 spectrumlab/models/gemini_api.py delete mode 100644 tests/models/test_gemini_pro_2_5.py diff --git a/spectrumlab/__init__.py b/spectrumlab/__init__.py index 7a20670..0e37434 100644 --- a/spectrumlab/__init__.py +++ b/spectrumlab/__init__.py @@ -21,7 +21,6 @@ from .models import Claude_Sonnet_3_5, Claude_Opus_4, Claude_Haiku_3_5, Claude_Sonnet_4 from .models import GPT4_1, GPT4_Vision from .models import Grok_2_Vision -from .models import Gemini_2_5_Pro from .config import Config __all__ = [ @@ -41,6 +40,5 @@ "GPT4_1", "GPT4_Vision", "Grok_2_Vision", - "Gemini_2_5_Pro", "Config", ] \ No newline at end of file diff --git a/spectrumlab/config/base_config.py b/spectrumlab/config/base_config.py index f98f47e..8352357 100644 --- a/spectrumlab/config/base_config.py +++ b/spectrumlab/config/base_config.py @@ -52,11 +52,6 @@ class Config: grok_2_vision_base_url: str = BOYUE_BASE_URL grok_2_vision_model_name: str = os.getenv("GROK_2_VISION") - # Gemini-2.5-Pro - gemini_2_5_pro_api_key: str = BOYUE_API_KEY - gemini_2_5_pro_base_url: str = BOYUE_BASE_URL - gemini_2_5_pro_model_name: str = os.getenv("GEMINI_2_5_PRO") - # Qwen-VL-Max qwen_vl_api_key: str = BOYUE_API_KEY qwen_vl_base_url: str = BOYUE_BASE_URL diff --git a/spectrumlab/models/__init__.py b/spectrumlab/models/__init__.py index 5537518..0750ae3 100644 --- a/spectrumlab/models/__init__.py +++ b/spectrumlab/models/__init__.py @@ -4,8 +4,7 @@ from .claude_api import Claude_Sonnet_3_5, Claude_Opus_4, Claude_Haiku_3_5, Claude_Sonnet_4 from .gpt4_v_api import GPT4_1, GPT4_Vision from .grok_api import Grok_2_Vision -from .gemini_api import Gemini_2_5_Pro from .qwen_vl_api import Qwen_VL_Max __all__ = ["DeepSeek", "GPT4o", "InternVL", "Claude_Sonnet_3_5", "Claude_Opus_4", - "Claude_Haiku_3_5", "Claude_Sonnet_4", "GPT4_1", "GPT4_Vision", "Grok_2_Vision", "Gemini_2_5_Pro", "Qwen_VL_Max"] + "Claude_Haiku_3_5", "Claude_Sonnet_4", "GPT4_1", "GPT4_Vision", "Grok_2_Vision", "Qwen_VL_Max"] diff --git a/spectrumlab/models/gemini_api.py b/spectrumlab/models/gemini_api.py deleted file mode 100644 index 9626c64..0000000 --- a/spectrumlab/models/gemini_api.py +++ /dev/null @@ -1,80 +0,0 @@ -from typing import Dict, Any, Optional, Union -from .base_api import BaseAPIModel -from spectrumlab.config import Config -from openai import OpenAI - - -class Gemini_2_5_Pro(BaseAPIModel): - def __init__( - self, - api_key: Optional[str] = None, - base_url: Optional[str] = None, - model_name: Optional[str] = None, - **kwargs, - ): - config = Config() - - # Use provided parameters or fall back to config - self.api_key = api_key or config.gemini_2_5_pro_api_key - self.base_url = base_url or config.gemini_2_5_pro_base_url - self.model_name = model_name or config.gemini_2_5_pro_model_name - - # Validate that we have required configuration - if not self.api_key: - raise ValueError( - "Gemini-2.5-Pro API key not found. Please set GEMINI_2_5_PRO_API_KEY in your .env file " - "or provide api_key parameter." - ) - - self.client = OpenAI( - api_key=self.api_key, - base_url=self.base_url, - ) - - # Initialize parent class - super().__init__(model_name=self.model_name, **kwargs) - - def generate( - self, prompt: Union[str, Dict[str, Any]], max_tokens: int = 512 - ) -> str: - """ - Generate response supporting both text and multimodal input. - - Args: - prompt: Either text string or multimodal dict - max_tokens: Maximum tokens to generate - - Returns: - Generated response string - """ - messages = [] - - # Handle multimodal vs text-only prompts - if isinstance(prompt, dict) and "images" in prompt: - # Multimodal prompt - content = [] - - content.append({"type": "text", "text": prompt["text"]}) - - for image_data in prompt["images"]: - content.append(image_data) - - messages.append({"role": "user", "content": content}) - else: - # Text-only prompt - text_content = prompt if isinstance(prompt, str) else prompt.get("text", "") - messages.append({"role": "user", "content": text_content}) - - # print(messages) - - - try: - response = self.client.chat.completions.create( - model=self.model_name, - messages=messages, - max_tokens=max_tokens, - ) - # print(response) - return response.choices[0].message.content - except Exception as e: - raise RuntimeError(f"Gemini-2.5-Pro API call failed: {e}") diff --git a/tests/models/test_gemini_pro_2_5.py b/tests/models/test_gemini_pro_2_5.py deleted file mode 100644 index e8f94df..0000000 --- a/tests/models/test_gemini_pro_2_5.py +++ /dev/null @@ -1,40 +0,0 @@ -from spectrumlab.models import Gemini_2_5_Pro -from spectrumlab.utils.image_utils import encode_image_to_base64 -from spectrumlab.benchmark.signal_group import SignalGroup -from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator - - -def test_claude_text_generation(): - model = Gemini_2_5_Pro() - prompt = "What is spectroscopy?" - response = model.generate(prompt, 8192) - assert isinstance(response, str) - assert len(response) > 0 - - -def test_claude_multimodal_generation(): - model = Gemini_2_5_Pro() - image_path = "playground/models/test.jpg" - image_base64 = encode_image_to_base64(image_path) - prompt = { - "text": "Please explain this spectroscopy image.", - "images": [ - { - "type": "image_url", - "image_url": {"url": f"data:image/jpg;base64, {image_base64}"}, - } - ], - } - response = model.generate(prompt, 8192) - assert isinstance(response, str) - assert len(response) > 0 - - -def test_claude_signalgroup_evaluation(): - model = Gemini_2_5_Pro() - signal_group = SignalGroup("data") - data = signal_group.get_data_by_subcategories(["Spectrum Type Classification"]) - evaluator = ChoiceEvaluator() - results = evaluator.evaluate(data_items=data, model=model, save_path=None, max_out_len=8192) - assert "metrics" in results - assert "overall" in results["metrics"] From 8f97fab77c6c61e4405fd9125c8307b24c2fe9c9 Mon Sep 17 00:00:00 2001 From: Jiaqing Xie <55912913+jiaqingxie@users.noreply.github.com> Date: Tue, 22 Jul 2025 19:03:01 +0800 Subject: [PATCH 4/5] Delete .vscode/sftp.json --- .vscode/sftp.json | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .vscode/sftp.json diff --git a/.vscode/sftp.json b/.vscode/sftp.json deleted file mode 100644 index cc7c9a3..0000000 --- a/.vscode/sftp.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "host": "101.126.157.149", - "port": 29, - "username": "root", - "protocol": "sftp", - "remotePath": "/root/SpectrumLab", - "privateKeyPath": "~/.ssh/id_rsa", - "uploadOnSave": true -} \ No newline at end of file From e080399129abccad99c11a9d3a8a99eb0cf46451 Mon Sep 17 00:00:00 2001 From: little1d <2431542525@qq.com> Date: Tue, 22 Jul 2025 20:07:41 +0800 Subject: [PATCH 5/5] bugfix: clear some useless file and debug code --- conftest.py | 12 --------- setup.py | 18 ------------- spectrumlab/__init__.py | 44 ------------------------------- spectrumlab/config/base_config.py | 10 +++---- test_import.py | 39 --------------------------- 5 files changed, 4 insertions(+), 119 deletions(-) delete mode 100644 conftest.py delete mode 100644 setup.py delete mode 100644 spectrumlab/__init__.py delete mode 100644 test_import.py diff --git a/conftest.py b/conftest.py deleted file mode 100644 index b10e1df..0000000 --- a/conftest.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -Pytest configuration file for SpectrumLab project. -This ensures the spectrumlab module is available for all tests. -""" - -import sys -import os -from pathlib import Path - -# Add current directory to Python path -current_dir = Path(__file__).parent -sys.path.insert(0, str(current_dir)) \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index ce486a3..0000000 --- a/setup.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Setup script for SpectrumLab package. -This allows the package to be installed in development mode. -""" - -from setuptools import setup, find_packages - -setup( - name="spectrumlab", - version="0.0.1", - description="Comprehensive toolkit for spectroscopy deep learning", - packages=find_packages(), - install_requires=[ - "dotenv>=0.9.9", - "openai>=1.93.0", - ], - python_requires=">=3.10", -) \ No newline at end of file diff --git a/spectrumlab/__init__.py b/spectrumlab/__init__.py deleted file mode 100644 index 0e37434..0000000 --- a/spectrumlab/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -SpectrumLab - Comprehensive toolkit for spectroscopy deep learning. - -This package provides tools for dataset loading, training, evaluation, -inference, and more for spectroscopy applications. -""" - -__version__ = "0.0.1" -__author__ = "Zhuo Yang, Tianfan Fu" - -# Import main modules -from . import models -from . import config -from . import evaluator -from . import benchmark -from . import utils -from . import cli - -# Export main classes for convenience -from .models import DeepSeek, GPT4o, InternVL -from .models import Claude_Sonnet_3_5, Claude_Opus_4, Claude_Haiku_3_5, Claude_Sonnet_4 -from .models import GPT4_1, GPT4_Vision -from .models import Grok_2_Vision -from .config import Config - -__all__ = [ - "models", - "config", - "evaluator", - "benchmark", - "utils", - "cli", - "DeepSeek", - "GPT4o", - "InternVL", - "Claude_Sonnet_3_5", - "Claude_Opus_4", - "Claude_Haiku_3_5", - "Claude_Sonnet_4", - "GPT4_1", - "GPT4_Vision", - "Grok_2_Vision", - "Config", -] \ No newline at end of file diff --git a/spectrumlab/config/base_config.py b/spectrumlab/config/base_config.py index 8352357..c0640c9 100644 --- a/spectrumlab/config/base_config.py +++ b/spectrumlab/config/base_config.py @@ -6,7 +6,6 @@ # Load .env from project root directory project_root = Path(__file__).parent.parent.parent env_path = project_root / ".env" -print(env_path) load_dotenv(env_path) @@ -15,7 +14,7 @@ class Config: # This api key is for testing closed MLLMs by Boyue Richdata BOYUE_API_KEY: str = os.getenv("BOYUE_API_KEY") BOYUE_BASE_URL: str = os.getenv("BOYUE_BASE_URL") - + # DeepSeek API Configuration deepseek_api_key: str = os.getenv("DEEPSEEK_API_KEY") deepseek_base_url: str = os.getenv("DEEPSEEK_BASE_URL") @@ -46,14 +45,13 @@ class Config: gpt4_vision_api_key: str = BOYUE_API_KEY gpt4_vision_base_url: str = BOYUE_BASE_URL gpt4_vision_model_name: str = os.getenv("GPT4_VISION") - + # Grok-2-Vision grok_2_vision_api_key: str = BOYUE_API_KEY grok_2_vision_base_url: str = BOYUE_BASE_URL grok_2_vision_model_name: str = os.getenv("GROK_2_VISION") - - # Qwen-VL-Max + + # Qwen-VL-Max qwen_vl_api_key: str = BOYUE_API_KEY qwen_vl_base_url: str = BOYUE_BASE_URL qwen_vl_model_name: str = os.getenv("QWEN_VL") - diff --git a/test_import.py b/test_import.py deleted file mode 100644 index 575ee01..0000000 --- a/test_import.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -""" -Simple test script to verify spectrumlab module imports work correctly. -""" - -import sys -import os - -# Add project root to Python path -project_root = os.path.dirname(os.path.abspath(__file__)) -sys.path.insert(0, project_root) - -try: - print("Testing spectrumlab imports...") - - # Test basic imports - from spectrumlab import Config - print("✅ Config import successful") - - from spectrumlab.models import Claude - print("✅ Claude model import successful") - - from spectrumlab.utils.image_utils import encode_image_to_base64 - print("✅ image_utils import successful") - - from spectrumlab.benchmark.signal_group import SignalGroup - print("✅ signal_group import successful") - - from spectrumlab.evaluator.choice_evaluator import ChoiceEvaluator - print("✅ choice_evaluator import successful") - - print("\n🎉 All imports successful!") - -except ImportError as e: - print(f"❌ Import error: {e}") - sys.exit(1) -except Exception as e: - print(f"❌ Unexpected error: {e}") - sys.exit(1) \ No newline at end of file