add precommit

This commit is contained in:
Gaohan123 committed 2025-10-24 16:20:58 +08:00
1 parent 1bf3424e96
commit a04e945166
24 files changed
+434 -377

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
# Set default behavior to automatically normalize line endings
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout
*.py text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.md text eol=lf
*.txt text eol=lf
*.json text eol=lf
*.toml text eol=lf
*.sh text eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
# Declare files that will always have CRLF line endings on checkout
*.bat text eol=crlf
*.cmd text eol=crlf
# Denote all files that are truly binary and should not be modified
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.woff2 binary
*.pyc binary
*.pdf binary
*.ai binary
*.psd binary
*.xcf binary
+29
View File
@@ -0,0 +1,29 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
# list of supported hooks: https://pre-commit.com/hooks.html
- id: check-yaml
args: ["--unsafe"]
- id: debug-statements
- id: end-of-file-fixer
- id: mixed-line-ending
args: ["--fix=lf"]
- id: trailing-whitespace
args: ["--markdown-linebreak-ext=md"]
- repo: https://github.com/PyCQA/isort
rev: 5.11.5
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
args: ["--max-line-length=88", "--extend-ignore=E203,W503"]
+17 -17
View File
@@ -73,20 +73,20 @@ from enum import Enum
class DiTCacheManager: class DiTCacheManager:
"""Manages DiT cache for diffusion models.""" """Manages DiT cache for diffusion models."""
def __init__(self, config: DiTCacheConfig): def __init__(self, config: DiTCacheConfig):
self.config = config self.config = config
self.cache = {} self.cache = {}
self.cache_stats = {} self.cache_stats = {}
async def get_cache(self, cache_key: str) -> Optional[Any]: async def get_cache(self, cache_key: str) -> Optional[Any]:
"""Retrieve cached data.""" """Retrieve cached data."""
pass pass
async def set_cache(self, cache_key: str, data: Any) -> None: async def set_cache(self, cache_key: str, data: Any) -> None:
"""Store data in cache.""" """Store data in cache."""
pass pass
async def invalidate_cache(self, cache_key: str) -> None: async def invalidate_cache(self, cache_key: str) -> None:
"""Invalidate cached data.""" """Invalidate cached data."""
pass pass
@@ -103,7 +103,7 @@ class OmniScheduler():
self.scheduler = self._create_scheduler() self.scheduler = self._create_scheduler()
self.cache_manager = DiTCacheManager(config.dit_cache_config) self.cache_manager = DiTCacheManager(config.dit_cache_config)
self._running = False self._running = False
def _create_scheduler(self) -> BaseScheduler: def _create_scheduler(self) -> BaseScheduler:
"""Factory method to create appropriate scheduler.""" """Factory method to create appropriate scheduler."""
if self.config.scheduler_type == SchedulerType.FIFO: if self.config.scheduler_type == SchedulerType.FIFO:
@@ -118,13 +118,13 @@ class OmniScheduler():
async def schedule(self, request: OmniRequest) -> bool: async def schedule(self, request: OmniRequest) -> bool:
""" """
Schedule a request for processing. Schedule a request for processing.
Args: Args:
request: The request to schedule request: The request to schedule
Returns: Returns:
bool: True if successfully scheduled, False otherwise bool: True if successfully scheduled, False otherwise
Raises: Raises:
SchedulerError: If scheduling fails SchedulerError: If scheduling fails
QueueFullError: If queue is at capacity QueueFullError: If queue is at capacity
@@ -137,7 +137,7 @@ class OmniScheduler():
def update_config(self, new_config: SchedulerConfig) -> None: def update_config(self, new_config: SchedulerConfig) -> None:
"""Update scheduler configuration.""" """Update scheduler configuration."""
pass pass
def get_config(self) -> SchedulerConfig: def get_config(self) -> SchedulerConfig:
"""Get current configuration.""" """Get current configuration."""
pass pass
@@ -149,12 +149,12 @@ class OmniScheduler():
"""Start the scheduler.""" """Start the scheduler."""
self._running = True self._running = True
# Start background tasks # Start background tasks
async def stop(self) -> None: async def stop(self) -> None:
"""Stop the scheduler gracefully.""" """Stop the scheduler gracefully."""
self._running = False self._running = False
# Cleanup resources # Cleanup resources
async def shutdown(self) -> None: async def shutdown(self) -> None:
"""Force shutdown the scheduler.""" """Force shutdown the scheduler."""
# Immediate cleanup # Immediate cleanup
@@ -170,7 +170,7 @@ class OmniScheduler():
"processed_requests": self.scheduler.processed_count(), "processed_requests": self.scheduler.processed_count(),
"cache_hit_rate": self.cache_manager.get_hit_rate() "cache_hit_rate": self.cache_manager.get_hit_rate()
} }
def get_metrics(self) -> Dict[str, float]: def get_metrics(self) -> Dict[str, float]:
"""Get performance metrics.""" """Get performance metrics."""
pass pass
@@ -182,21 +182,21 @@ class OmniScheduler():
@dataclass @dataclass
class OmniConfig: class OmniConfig:
"""Configuration for the core module.""" """Configuration for the core module."""
# Scheduler configuration # Scheduler configuration
scheduler: SchedulerConfig = field(default_factory=SchedulerConfig) scheduler: SchedulerConfig = field(default_factory=SchedulerConfig)
# Cache configuration # Cache configuration
dit_cache: DiTCacheConfig = field(default_factory=DiTCacheConfig) dit_cache: DiTCacheConfig = field(default_factory=DiTCacheConfig)
# Resource limits # Resource limits
max_memory_gb: float = 16.0 max_memory_gb: float = 16.0
max_gpu_utilization: float = 0.8 max_gpu_utilization: float = 0.8
# Timeouts # Timeouts
request_timeout: int = 300 request_timeout: int = 300
worker_timeout: int = 60 worker_timeout: int = 60
def validate(self) -> None: def validate(self) -> None:
"""Validate configuration parameters.""" """Validate configuration parameters."""
if self.max_memory_gb <= 0: if self.max_memory_gb <= 0:
+2 -2
View File
@@ -157,10 +157,10 @@ Each API template follows this standardized structure:
# Clone the repository # Clone the repository
git clone https://github.com/hsliuustc0106/vllm-omni.git git clone https://github.com/hsliuustc0106/vllm-omni.git
cd vllm-omni cd vllm-omni
# Install dependencies # Install dependencies
pip install -r requirements-dev.txt pip install -r requirements-dev.txt
# Set up pre-commit hooks # Set up pre-commit hooks
pre-commit install pre-commit install
``` ```
+3 -3
View File
@@ -52,7 +52,7 @@ According to analysis for current popular open-source models, most of them have
* **Extension, multiple AR+DiT (e.g. Qwen-omni)** * **Extension, multiple AR+DiT (e.g. Qwen-omni)**
A new model for multimodal input and output. It designs the Thinker-Talker-Codec structure, which is a double-AR+DiT format. A new model for multimodal input and output. It designs the Thinker-Talker-Codec structure, which is a double-AR+DiT format.
![][image5] ![][image5]
# Architectural Overview # Architectural Overview
@@ -112,8 +112,8 @@ vLLM-omni will import core modules from existing vLLM framework and introduce se
# Contact # Contact
For any questions or feedback regarding this design document, please contact For any questions or feedback regarding this design document, please contact
Han Gao: [hgaoaf@connect.ust.hk](mailto:hgaoaf@connect.ust.hk), Han Gao: [hgaoaf@connect.ust.hk](mailto:hgaoaf@connect.ust.hk),
Hongsheng Liu: [hsliuustc@gmail.com](mailto:hsliuustc@gmail.com) Hongsheng Liu: [hsliuustc@gmail.com](mailto:hsliuustc@gmail.com)
[image1]: <data:image/png;base64,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 truncated [image1]: <data:image/png;base64,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 truncated
@@ -136,7 +136,7 @@ class OmniLLM(LLM):
self.engine_list: List[LLMEngine] = [] self.engine_list: List[LLMEngine] = []
self.output_processor = MultimodalOutputProcessor() self.output_processor = MultimodalOutputProcessor()
self._initialize_stage_engines() self._initialize_stage_engines()
def _initialize_stage_engines(self) -> None: def _initialize_stage_engines(self) -> None:
"""Initialize LLMEngine instances for each stage""" """Initialize LLMEngine instances for each stage"""
for stage_config in self.stage_configs: for stage_config in self.stage_configs:
@@ -145,24 +145,24 @@ class OmniLLM(LLM):
elif stage_config.engine_type == "DiT": elif stage_config.engine_type == "DiT":
engine = self._create_dit_engine(stage_config) engine = self._create_dit_engine(stage_config)
self.engine_list.append(engine) self.engine_list.append(engine)
def generate(self, stage_args_list: List[Dict], **kwargs) -> List[RequestOutput]: def generate(self, stage_args_list: List[Dict], **kwargs) -> List[RequestOutput]:
"""Main generation interface - orchestrates multi-stage processing""" """Main generation interface - orchestrates multi-stage processing"""
current_output = None current_output = None
for i, (stage_config, stage_args) in enumerate(zip(self.stage_configs, stage_args_list)): for i, (stage_config, stage_args) in enumerate(zip(self.stage_configs, stage_args_list)):
stage_engine = self.engine_list[i] stage_engine = self.engine_list[i]
# Prepare input for this stage # Prepare input for this stage
processed_input = self._process_stage_inputs(stage_config, stage_args, current_output) processed_input = self._process_stage_inputs(stage_config, stage_args, current_output)
# Execute stage # Execute stage
stage_output = self._execute_stage(stage_engine, processed_input) stage_output = self._execute_stage(stage_engine, processed_input)
# Update for next stage # Update for next stage
current_output = stage_output current_output = stage_output
stage_config.stage_output = stage_output stage_config.stage_output = stage_output
# Process final output # Process final output
return self.output_processor.process_output(current_output) return self.output_processor.process_output(current_output)
``` ```
@@ -178,7 +178,7 @@ class AsyncOmniLLM(AsyncLLM):
self.async_engine_list: List[AsyncLLM] = [] self.async_engine_list: List[AsyncLLM] = []
self.output_processor = MultimodalOutputProcessor() self.output_processor = MultimodalOutputProcessor()
self._initialize_async_stage_engines() self._initialize_async_stage_engines()
async def generate_async(self, stage_args_list: List[Dict], **kwargs) -> List[RequestOutput]: async def generate_async(self, stage_args_list: List[Dict], **kwargs) -> List[RequestOutput]:
"""Async generation interface""" """Async generation interface"""
# Similar to OmniLLM but with async/await patterns # Similar to OmniLLM but with async/await patterns
@@ -241,7 +241,7 @@ class MultimodalOutputProcessor(OutputProcessor):
"latents": self._process_latents_output, "latents": self._process_latents_output,
"text": self._process_text_output, "text": self._process_text_output,
} }
def process_outputs(self, engine_core_outputs: List[EngineCoreOutput], ...): def process_outputs(self, engine_core_outputs: List[EngineCoreOutput], ...):
"""Process multimodal outputs based on type""" """Process multimodal outputs based on type"""
for engine_core_output in engine_core_outputs: for engine_core_output in engine_core_outputs:
+19 -26
View File
@@ -6,9 +6,9 @@ This example shows how to interact with the vLLM-omni API server
using HTTP requests. using HTTP requests.
""" """
import requests
import json import json
import time
import requests
def test_health(host="localhost", port=8000): def test_health(host="localhost", port=8000):
@@ -30,7 +30,7 @@ def test_health(host="localhost", port=8000):
def generate_text(prompt, host="localhost", port=8000, **kwargs): def generate_text(prompt, host="localhost", port=8000, **kwargs):
"""Generate text using the API server.""" """Generate text using the API server."""
url = f"http://{host}:{port}/generate" url = f"http://{host}:{port}/generate"
# Default parameters # Default parameters
params = { params = {
"prompts": [prompt], "prompts": [prompt],
@@ -40,11 +40,11 @@ def generate_text(prompt, host="localhost", port=8000, **kwargs):
"frequency_penalty": kwargs.get("frequency_penalty", 0.0), "frequency_penalty": kwargs.get("frequency_penalty", 0.0),
"presence_penalty": kwargs.get("presence_penalty", 0.0), "presence_penalty": kwargs.get("presence_penalty", 0.0),
} }
# Add optional parameters # Add optional parameters
if "stop" in kwargs: if "stop" in kwargs:
params["stop"] = kwargs["stop"] params["stop"] = kwargs["stop"]
try: try:
response = requests.post(url, json=params, timeout=30) response = requests.post(url, json=params, timeout=30)
response.raise_for_status() response.raise_for_status()
@@ -58,71 +58,64 @@ def main():
"""Main example function.""" """Main example function."""
print("vLLM-omni API Client Example") print("vLLM-omni API Client Example")
print("=" * 40) print("=" * 40)
# Test server health # Test server health
if not test_health(): if not test_health():
print("Please start the server first:") print("Please start the server first:")
print(" vllm serve Qwen/Qwen3-0.6B --omni --port 8000") print(" vllm serve Qwen/Qwen3-0.6B --omni --port 8000")
return return
# Example 1: Simple generation # Example 1: Simple generation
print("\n1. Simple text generation:") print("\n1. Simple text generation:")
result = generate_text("Hello, how are you?", max_tokens=50) result = generate_text("Hello, how are you?", max_tokens=50)
if result: if result:
text = result["outputs"][0]["text"] text = result["outputs"][0]["text"]
print(f" Input: Hello, how are you?") print(" Input: Hello, how are you?")
print(f" Output: {text}") print(f" Output: {text}")
# Example 2: Creative writing # Example 2: Creative writing
print("\n2. Creative writing:") print("\n2. Creative writing:")
result = generate_text( result = generate_text(
"Write a short story about a robot learning to paint", "Write a short story about a robot learning to paint",
max_tokens=150, max_tokens=150,
temperature=0.9 temperature=0.9,
) )
if result: if result:
text = result["outputs"][0]["text"] text = result["outputs"][0]["text"]
print(f" Input: Write a short story about a robot learning to paint") print(" Input: Write a short story about a robot learning to paint")
print(f" Output: {text}") print(f" Output: {text}")
# Example 3: Question answering # Example 3: Question answering
print("\n3. Question answering:") print("\n3. Question answering:")
result = generate_text( result = generate_text(
"What is the capital of France?", "What is the capital of France?", max_tokens=30, temperature=0.3
max_tokens=30,
temperature=0.3
) )
if result: if result:
text = result["outputs"][0]["text"] text = result["outputs"][0]["text"]
print(f" Input: What is the capital of France?") print(" Input: What is the capital of France?")
print(f" Output: {text}") print(f" Output: {text}")
# Example 4: Multiple prompts # Example 4: Multiple prompts
print("\n4. Multiple prompts:") print("\n4. Multiple prompts:")
prompts = [ prompts = ["Tell me a joke", "What's 2+2?", "Describe the color blue"]
"Tell me a joke",
"What's 2+2?",
"Describe the color blue"
]
for prompt in prompts: for prompt in prompts:
result = generate_text(prompt, max_tokens=30) result = generate_text(prompt, max_tokens=30)
if result: if result:
text = result["outputs"][0]["text"] text = result["outputs"][0]["text"]
print(f" Q: {prompt}") print(f" Q: {prompt}")
print(f" A: {text}") print(f" A: {text}")
# Example 5: Show full response structure # Example 5: Show full response structure
print("\n5. Full response structure:") print("\n5. Full response structure:")
result = generate_text("Hello world", max_tokens=20) result = generate_text("Hello world", max_tokens=20)
if result: if result:
print(" Full API response:") print(" Full API response:")
print(json.dumps(result, indent=2)) print(json.dumps(result, indent=2))
print("\n" + "=" * 40) print("\n" + "=" * 40)
print("API client examples completed!") print("API client examples completed!")
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+44 -43
View File
@@ -7,40 +7,43 @@ with a single AR (Autoregressive) stage.
""" """
import asyncio import asyncio
from vllm_omni.entrypoints.omni_llm import OmniLLM, AsyncOmniLLM
from vllm_omni.config import create_ar_stage_config from vllm_omni.config import create_ar_stage_config
from vllm_omni.entrypoints.omni_llm import AsyncOmniLLM, OmniLLM
def sync_example(): def sync_example():
"""Synchronous usage example.""" """Synchronous usage example."""
print("=== Synchronous vLLM-omni Example ===") print("=== Synchronous vLLM-omni Example ===")
# Create a simple AR stage configuration # Create a simple AR stage configuration
stage_config = create_ar_stage_config( stage_config = create_ar_stage_config(
stage_id=0, stage_id=0,
model_path="Qwen/Qwen3-0.6B", model_path="Qwen/Qwen3-0.6B",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
# Initialize OmniLLM # Initialize OmniLLM
omni_llm = OmniLLM([stage_config]) omni_llm = OmniLLM([stage_config])
# Prepare stage arguments # Prepare stage arguments
stage_args = [{ stage_args = [
"prompt": "Hello, how are you today?", {
"max_tokens": 50, "prompt": "Hello, how are you today?",
"temperature": 0.7 "max_tokens": 50,
}] "temperature": 0.7,
}
]
# Generate text # Generate text
print(f"Input: {stage_args[0]['prompt']}") print(f"Input: {stage_args[0]['prompt']}")
outputs = omni_llm.generate(stage_args) outputs = omni_llm.generate(stage_args)
# Display results # Display results
for i, output in enumerate(outputs): for i, output in enumerate(outputs):
print(f"Output {i}:") print(f"Output {i}:")
if hasattr(output, 'outputs') and output.outputs: if hasattr(output, "outputs") and output.outputs:
for completion in output.outputs: for completion in output.outputs:
print(f" Text: {completion.text}") print(f" Text: {completion.text}")
print(f" Finished: {completion.finish_reason != 'length'}") print(f" Finished: {completion.finish_reason != 'length'}")
@@ -50,33 +53,35 @@ def sync_example():
async def async_example(): async def async_example():
"""Asynchronous usage example.""" """Asynchronous usage example."""
print("\n=== Asynchronous vLLM-omni Example ===") print("\n=== Asynchronous vLLM-omni Example ===")
# Create a simple AR stage configuration # Create a simple AR stage configuration
stage_config = create_ar_stage_config( stage_config = create_ar_stage_config(
stage_id=0, stage_id=0,
model_path="Qwen/Qwen3-0.6B", model_path="Qwen/Qwen3-0.6B",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
# Initialize AsyncOmniLLM # Initialize AsyncOmniLLM
omni_llm = AsyncOmniLLM([stage_config]) omni_llm = AsyncOmniLLM([stage_config])
# Prepare stage arguments # Prepare stage arguments
stage_args = [{ stage_args = [
"prompt": "What is artificial intelligence?", {
"max_tokens": 100, "prompt": "What is artificial intelligence?",
"temperature": 0.8 "max_tokens": 100,
}] "temperature": 0.8,
}
]
# Generate text asynchronously # Generate text asynchronously
print(f"Input: {stage_args[0]['prompt']}") print(f"Input: {stage_args[0]['prompt']}")
outputs = await omni_llm.generate_async(stage_args) outputs = await omni_llm.generate_async(stage_args)
# Display results # Display results
for i, output in enumerate(outputs): for i, output in enumerate(outputs):
print(f"Output {i}:") print(f"Output {i}:")
if hasattr(output, 'outputs') and output.outputs: if hasattr(output, "outputs") and output.outputs:
for completion in output.outputs: for completion in output.outputs:
print(f" Text: {completion.text}") print(f" Text: {completion.text}")
print(f" Finished: {completion.finish_reason != 'length'}") print(f" Finished: {completion.finish_reason != 'length'}")
@@ -86,36 +91,32 @@ async def async_example():
def multi_prompt_example(): def multi_prompt_example():
"""Example with multiple prompts.""" """Example with multiple prompts."""
print("\n=== Multi-Prompt Example ===") print("\n=== Multi-Prompt Example ===")
# Create stage configuration # Create stage configuration
stage_config = create_ar_stage_config( stage_config = create_ar_stage_config(
stage_id=0, stage_id=0,
model_path="Qwen/Qwen3-0.6B", model_path="Qwen/Qwen3-0.6B",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
# Initialize OmniLLM # Initialize OmniLLM
omni_llm = OmniLLM([stage_config]) omni_llm = OmniLLM([stage_config])
# Multiple prompts # Multiple prompts
prompts = [ prompts = [
"Tell me a joke", "Tell me a joke",
"What's the weather like?", "What's the weather like?",
"Explain quantum computing" "Explain quantum computing",
] ]
for prompt in prompts: for prompt in prompts:
print(f"\nInput: {prompt}") print(f"\nInput: {prompt}")
stage_args = [{ stage_args = [{"prompt": prompt, "max_tokens": 30, "temperature": 0.9}]
"prompt": prompt,
"max_tokens": 30,
"temperature": 0.9
}]
outputs = omni_llm.generate(stage_args) outputs = omni_llm.generate(stage_args)
if outputs and hasattr(outputs[0], 'outputs') and outputs[0].outputs: if outputs and hasattr(outputs[0], "outputs") and outputs[0].outputs:
completion = outputs[0].outputs[0] completion = outputs[0].outputs[0]
print(f"Output: {completion.text}") print(f"Output: {completion.text}")
@@ -123,15 +124,15 @@ def multi_prompt_example():
if __name__ == "__main__": if __name__ == "__main__":
print("vLLM-omni Simple Usage Examples") print("vLLM-omni Simple Usage Examples")
print("=" * 40) print("=" * 40)
# Run synchronous example # Run synchronous example
sync_example() sync_example()
# Run asynchronous example # Run asynchronous example
asyncio.run(async_example()) asyncio.run(async_example())
# Run multi-prompt example # Run multi-prompt example
multi_prompt_example() multi_prompt_example()
print("\n" + "=" * 40) print("\n" + "=" * 40)
print("Examples completed!") print("Examples completed!")
+1 -1
View File
@@ -10,8 +10,8 @@ from typing import Dict, List
import yaml import yaml
from vllm_omni import ( from vllm_omni import (
OmniLLM,
DiTConfig, DiTConfig,
OmniLLM,
create_ar_stage_config, create_ar_stage_config,
create_dit_stage_config, create_dit_stage_config,
) )
+4
View File
@@ -103,6 +103,10 @@ multi_line_output = 3
line_length = 88 line_length = 88
known_first_party = ["vllm_omni"] known_first_party = ["vllm_omni"]
[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "W503"]
[tool.mypy] [tool.mypy]
python_version = "3.8" python_version = "3.8"
warn_return_any = true warn_return_any = true
-1
View File
@@ -106,4 +106,3 @@ python scripts/download_models.py
- `server.log`: Server startup and runtime logs - `server.log`: Server startup and runtime logs
- `api_client_test.log`: API client test results - `api_client_test.log`: API client test results
- `simple_usage_test.log`: Simple usage test results - `simple_usage_test.log`: Simple usage test results
+15 -15
View File
@@ -99,15 +99,15 @@ print('✅ All imports successful')
start_server() { start_server() {
log_info "Starting vLLM-omni server on port $PORT..." log_info "Starting vLLM-omni server on port $PORT..."
log_info "Command: vllm serve $MODEL_PATH --omni --port $PORT" log_info "Command: vllm serve $MODEL_PATH --omni --port $PORT"
# Start server in background # Start server in background
vllm serve "$MODEL_PATH" --omni --port "$PORT" > server.log 2>&1 & vllm serve "$MODEL_PATH" --omni --port "$PORT" > server.log 2>&1 &
SERVER_PID=$! SERVER_PID=$!
log_info "Server started with PID: $SERVER_PID" log_info "Server started with PID: $SERVER_PID"
log_info "Waiting $SERVER_STARTUP_WAIT seconds for server to initialize..." log_info "Waiting $SERVER_STARTUP_WAIT seconds for server to initialize..."
sleep $SERVER_STARTUP_WAIT sleep $SERVER_STARTUP_WAIT
# Check if server is still running # Check if server is still running
if ! kill -0 $SERVER_PID 2>/dev/null; then if ! kill -0 $SERVER_PID 2>/dev/null; then
log_error "Server failed to start" log_error "Server failed to start"
@@ -115,7 +115,7 @@ start_server() {
cat server.log cat server.log
exit 1 exit 1
fi fi
log_success "Server appears to be running" log_success "Server appears to be running"
} }
@@ -146,7 +146,7 @@ test_info() {
local response=$(curl -s -w "%{http_code}" http://$HOST:$PORT/info 2>/dev/null || echo "000") local response=$(curl -s -w "%{http_code}" http://$HOST:$PORT/info 2>/dev/null || echo "000")
local http_code="${response: -3}" local http_code="${response: -3}"
local body="${response%???}" local body="${response%???}"
if [ "$http_code" = "200" ]; then if [ "$http_code" = "200" ]; then
log_success "Info endpoint working" log_success "Info endpoint working"
echo "$body" | python -m json.tool 2>/dev/null || log_warning "Info response not valid JSON" echo "$body" | python -m json.tool 2>/dev/null || log_warning "Info response not valid JSON"
@@ -159,13 +159,13 @@ test_info() {
# Test text generation # Test text generation
test_generation() { test_generation() {
log_info "Testing text generation..." log_info "Testing text generation..."
# Test 1: Simple generation # Test 1: Simple generation
local response=$(curl -s -X POST http://$HOST:$PORT/generate \ local response=$(curl -s -X POST http://$HOST:$PORT/generate \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"prompts": ["Test the server functionality"], "max_tokens": 20, "temperature": 0.7}' \ -d '{"prompts": ["Test the server functionality"], "max_tokens": 20, "temperature": 0.7}' \
2>/dev/null || echo "{}") 2>/dev/null || echo "{}")
if echo "$response" | python -c "import sys, json; data=json.load(sys.stdin); exit(0 if 'outputs' in data and len(data['outputs']) > 0 else 1)" 2>/dev/null; then if echo "$response" | python -c "import sys, json; data=json.load(sys.stdin); exit(0 if 'outputs' in data and len(data['outputs']) > 0 else 1)" 2>/dev/null; then
log_success "Text generation test passed" log_success "Text generation test passed"
echo "$response" | python -c "import sys, json; data=json.load(sys.stdin); print('Generated text:', data['outputs'][0]['text'][:100] + '...' if len(data['outputs'][0]['text']) > 100 else data['outputs'][0]['text'])" 2>/dev/null echo "$response" | python -c "import sys, json; data=json.load(sys.stdin); print('Generated text:', data['outputs'][0]['text'][:100] + '...' if len(data['outputs'][0]['text']) > 100 else data['outputs'][0]['text'])" 2>/dev/null
@@ -271,7 +271,7 @@ test_simple_usage() {
# Performance test # Performance test
test_performance() { test_performance() {
log_info "Running performance test..." log_info "Running performance test..."
local start_time=$(date +%s) local start_time=$(date +%s)
local response=$(curl -s -X POST http://$HOST:$PORT/generate \ local response=$(curl -s -X POST http://$HOST:$PORT/generate \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
@@ -279,7 +279,7 @@ test_performance() {
2>/dev/null) 2>/dev/null)
local end_time=$(date +%s) local end_time=$(date +%s)
local duration=$((end_time - start_time)) local duration=$((end_time - start_time))
if echo "$response" | python -c "import sys, json; data=json.load(sys.stdin); exit(0 if 'outputs' in data else 1)" 2>/dev/null; then if echo "$response" | python -c "import sys, json; data=json.load(sys.stdin); exit(0 if 'outputs' in data else 1)" 2>/dev/null; then
log_success "Performance test passed (${duration}s response time)" log_success "Performance test passed (${duration}s response time)"
else else
@@ -295,16 +295,16 @@ run_tests() {
log_info "Port: $PORT" log_info "Port: $PORT"
log_info "Host: $HOST" log_info "Host: $HOST"
echo "==========================================" echo "=========================================="
# Run all tests # Run all tests
check_model check_model
check_environment check_environment
test_imports test_imports
start_server start_server
# Wait a bit more for server to be fully ready # Wait a bit more for server to be fully ready
sleep 5 sleep 5
test_health test_health
test_info test_info
test_generation test_generation
@@ -348,13 +348,13 @@ main() {
show_usage show_usage
exit 0 exit 0
fi fi
# Change to script directory # Change to script directory
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
# Create logs directory if it doesn't exist # Create logs directory if it doesn't exist
mkdir -p logs mkdir -p logs
# Run tests # Run tests
run_tests run_tests
} }
+2 -1
View File
@@ -1,5 +1,6 @@
""" """
Test suite for vLLM-omni. Test suite for vLLM-omni.
This package contains unit tests, integration tests, and benchmarks for vLLM-omni. This package contains unit tests, integration tests, and benchmarks
for vLLM-omni.
""" """
+18 -14
View File
@@ -2,10 +2,17 @@
Shared fixtures and configuration for vLLM-omni tests. Shared fixtures and configuration for vLLM-omni tests.
""" """
from unittest.mock import Mock
import pytest import pytest
import torch import torch
from unittest.mock import Mock
from vllm_omni.config import OmniStageConfig, DiTConfig, DiTCacheConfig, create_ar_stage_config, create_dit_stage_config from vllm_omni.config import (
DiTCacheConfig,
DiTConfig,
create_ar_stage_config,
create_dit_stage_config,
)
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
@@ -21,7 +28,7 @@ def sample_ar_stage_config():
stage_id=0, stage_id=0,
model_path="test-ar-model", model_path="test-ar-model",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
@@ -32,15 +39,15 @@ def sample_dit_stage_config():
model_type="dit", model_type="dit",
scheduler_type="ddpm", scheduler_type="ddpm",
num_inference_steps=10, num_inference_steps=10,
guidance_scale=7.5 guidance_scale=7.5,
) )
return create_dit_stage_config( return create_dit_stage_config(
stage_id=1, stage_id=1,
model_path="test-dit-model", model_path="test-dit-model",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["image"], output_modalities=["image"],
dit_config=dit_config dit_config=dit_config,
) )
@@ -64,19 +71,16 @@ def mock_vllm_config():
def mock_dit_cache_config(): def mock_dit_cache_config():
"""Mock DiT cache configuration.""" """Mock DiT cache configuration."""
from vllm_omni.config import DiTCacheTensor from vllm_omni.config import DiTCacheTensor
cache_tensors = [ cache_tensors = [
DiTCacheTensor( DiTCacheTensor(
name="test_tensor", name="test_tensor", shape=[1, 512, 512], dtype="float32", persistent=True
shape=[1, 512, 512],
dtype="float32",
persistent=True
) )
] ]
return DiTCacheConfig( return DiTCacheConfig(
cache_tensors=cache_tensors, cache_tensors=cache_tensors,
max_cache_size=1024 * 1024 * 1024, # 1GB max_cache_size=1024 * 1024 * 1024, # 1GB
cache_strategy="fifo", cache_strategy="fifo",
enable_optimization=True enable_optimization=True,
) )
+30 -49
View File
@@ -3,11 +3,12 @@ Unit tests for configuration modules.
""" """
import pytest import pytest
from vllm_omni.config import ( from vllm_omni.config import (
OmniStageConfig,
DiTConfig,
DiTCacheConfig, DiTCacheConfig,
DiTCacheTensor, DiTCacheTensor,
DiTConfig,
OmniStageConfig,
create_ar_stage_config, create_ar_stage_config,
create_dit_stage_config, create_dit_stage_config,
) )
@@ -21,33 +22,29 @@ class TestOmniStageConfig:
engine_type="AR", engine_type="AR",
model_path="test-model", model_path="test-model",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
assert config.engine_type == "AR" assert config.engine_type == "AR"
assert config.stage_id == 0 assert config.stage_id == 0
assert config.model_path == "test-model" assert config.model_path == "test-model"
assert config.input_modalities == ["text"] assert config.input_modalities == ["text"]
assert config.output_modalities == ["text"] assert config.output_modalities == ["text"]
def test_dit_stage_config_creation(self): def test_dit_stage_config_creation(self):
"""Test DiT stage configuration creation.""" """Test DiT stage configuration creation."""
dit_config = DiTConfig( dit_config = DiTConfig(num_inference_steps=50)
model_type="dit",
scheduler_type="ddpm",
num_inference_steps=50
)
config = OmniStageConfig( config = OmniStageConfig(
stage_id=1, stage_id=1,
engine_type="DiT", engine_type="DiT",
model_path="test-dit-model", model_path="test-dit-model",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["image"], output_modalities=["image"],
dit_config=dit_config dit_config=dit_config,
) )
assert config.engine_type == "DiT" assert config.engine_type == "DiT"
assert config.dit_config is not None assert config.dit_config is not None
assert config.dit_config.model_type == "dit" assert config.dit_config.num_inference_steps == 50
def test_invalid_engine_type(self): def test_invalid_engine_type(self):
"""Test validation of engine type.""" """Test validation of engine type."""
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -56,9 +53,9 @@ class TestOmniStageConfig:
engine_type="INVALID", engine_type="INVALID",
model_path="test-model", model_path="test-model",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
def test_empty_modalities(self): def test_empty_modalities(self):
"""Test validation of empty modalities.""" """Test validation of empty modalities."""
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -67,18 +64,18 @@ class TestOmniStageConfig:
engine_type="AR", engine_type="AR",
model_path="test-model", model_path="test-model",
input_modalities=[], input_modalities=[],
output_modalities=["text"] output_modalities=["text"],
) )
with pytest.raises(ValueError): with pytest.raises(ValueError):
OmniStageConfig( OmniStageConfig(
stage_id=0, stage_id=0,
engine_type="AR", engine_type="AR",
model_path="test-model", model_path="test-model",
input_modalities=["text"], input_modalities=["text"],
output_modalities=[] output_modalities=[],
) )
def test_dit_requires_config(self): def test_dit_requires_config(self):
"""Test that DiT engine requires dit_config.""" """Test that DiT engine requires dit_config."""
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -87,32 +84,26 @@ class TestOmniStageConfig:
engine_type="DiT", engine_type="DiT",
model_path="test-dit-model", model_path="test-dit-model",
input_modalities=["text"], input_modalities=["text"],
output_modalities=["image"] output_modalities=["image"],
) )
class TestDiTConfig: class TestDiTConfig:
def test_dit_config_defaults(self): def test_dit_config_defaults(self):
"""Test DiT configuration with defaults.""" """Test DiT configuration with defaults."""
config = DiTConfig( config = DiTConfig(num_inference_steps=50)
model_type="dit",
scheduler_type="ddpm",
num_inference_steps=50
)
assert config.use_diffusers is False assert config.use_diffusers is False
assert config.diffusers_pipeline is None assert config.diffusers_pipeline is None
assert config.guidance_scale == 7.5 assert config.guidance_scale == 7.5
assert config.height == 512 assert config.height == 512
assert config.width == 512 assert config.width == 512
def test_diffusers_config(self): def test_diffusers_config(self):
"""Test DiT configuration with diffusers.""" """Test DiT configuration with diffusers."""
config = DiTConfig( config = DiTConfig(
model_type="dit",
scheduler_type="ddpm",
num_inference_steps=50, num_inference_steps=50,
use_diffusers=True, use_diffusers=True,
diffusers_pipeline="stable-diffusion" diffusers_pipeline="stable-diffusion",
) )
assert config.use_diffusers is True assert config.use_diffusers is True
assert config.diffusers_pipeline == "stable-diffusion" assert config.diffusers_pipeline == "stable-diffusion"
@@ -126,16 +117,16 @@ class TestDiTCacheConfig:
name="test_tensor", name="test_tensor",
shape=[1, 512, 512], shape=[1, 512, 512],
dtype="float32", dtype="float32",
persistent=True persistent=True,
) )
] ]
config = DiTCacheConfig( config = DiTCacheConfig(
cache_tensors=cache_tensors, cache_tensors=cache_tensors,
max_cache_size=1024 * 1024 * 1024, max_cache_size=1024 * 1024 * 1024,
cache_strategy="fifo" cache_strategy="fifo",
) )
assert len(config.cache_tensors) == 1 assert len(config.cache_tensors) == 1
assert config.max_cache_size == 1024 * 1024 * 1024 assert config.max_cache_size == 1024 * 1024 * 1024
assert config.cache_strategy == "fifo" assert config.cache_strategy == "fifo"
@@ -145,35 +136,25 @@ class TestDiTCacheConfig:
class TestHelperFunctions: class TestHelperFunctions:
def test_create_ar_stage_config(self): def test_create_ar_stage_config(self):
"""Test create_ar_stage_config helper function.""" """Test create_ar_stage_config helper function."""
config = create_ar_stage_config( config = create_ar_stage_config(stage_id=0, model_path="test-model")
stage_id=0,
model_path="test-model"
)
assert config.stage_id == 0 assert config.stage_id == 0
assert config.engine_type == "AR" assert config.engine_type == "AR"
assert config.model_path == "test-model" assert config.model_path == "test-model"
assert config.input_modalities == ["text"] assert config.input_modalities == ["text"]
assert config.output_modalities == ["text"] assert config.output_modalities == ["text"]
def test_create_dit_stage_config(self): def test_create_dit_stage_config(self):
"""Test create_dit_stage_config helper function.""" """Test create_dit_stage_config helper function."""
dit_config = DiTConfig( dit_config = DiTConfig(num_inference_steps=50)
model_type="dit",
scheduler_type="ddpm",
num_inference_steps=50
)
config = create_dit_stage_config( config = create_dit_stage_config(
stage_id=1, stage_id=1, model_path="test-dit-model", dit_config=dit_config
model_path="test-dit-model",
dit_config=dit_config
) )
assert config.stage_id == 1 assert config.stage_id == 1
assert config.engine_type == "DiT" assert config.engine_type == "DiT"
assert config.model_path == "test-dit-model" assert config.model_path == "test-dit-model"
assert config.input_modalities == ["text"] assert config.input_modalities == ["text"]
assert config.output_modalities == ["image"] assert config.output_modalities == ["image"]
assert config.dit_config is not None assert config.dit_config is not None
+15 -14
View File
@@ -1,45 +1,46 @@
""" """
vLLM-omni: Multi-modality models inference and serving with non-autoregressive structures. vLLM-omni: Multi-modality models inference and serving with
non-autoregressive structures.
This package extends vLLM beyond traditional text-based, autoregressive generation This package extends vLLM beyond traditional text-based, autoregressive
to support multi-modality models with non-autoregressive structures and non-textual outputs. generation to support multi-modality models with non-autoregressive
structures and non-textual outputs.
Architecture: Architecture:
- 🟡 Modified: vLLM components modified for multimodal support - 🟡 Modified: vLLM components modified for multimodal support
- 🔴 Added: New components for multimodal and non-autoregressive processing - 🔴 Added: New components for multimodal and non-autoregressive
processing
""" """
__version__ = "0.1.0" __version__ = "0.1.0"
__author__ = "vLLM-omni Team" __author__ = "vLLM-omni Team"
__email__ = "hsliuustc@gmail.com" __email__ = "hsliuustc@gmail.com"
# Main entry points
from .entrypoints.omni_llm import OmniLLM, AsyncOmniLLM
from .config import ( from .config import (
OmniStageConfig,
DiTConfig,
DiTCacheConfig, DiTCacheConfig,
DiTConfig,
OmniStageConfig,
create_ar_stage_config, create_ar_stage_config,
create_dit_stage_config, create_dit_stage_config,
) )
# Main entry points
from .entrypoints.omni_llm import AsyncOmniLLM, OmniLLM
__all__ = [ __all__ = [
# Version info # Version info
"__version__", "__version__",
"__author__", "__author__",
"__email__", "__email__",
# Main components # Main components
"OmniLLM", "OmniLLM",
"AsyncOmniLLM", "AsyncOmniLLM",
# Configuration # Configuration
"OmniStageConfig", "OmniStageConfig",
"DiTConfig", "DiTConfig",
"DiTCacheConfig", "DiTCacheConfig",
"create_ar_stage_config", "create_ar_stage_config",
"create_dit_stage_config", "create_dit_stage_config",
# All other components are available through their respective modules # All other components are available through their respective modules
# processors.*, schedulers.*, executors.*, etc. # processors.*, schedulers.*, executors.*, etc.
] ]
+4 -4
View File
@@ -11,10 +11,10 @@ from vllm.config import config
import vllm_omni.model_executor.models as me_models import vllm_omni.model_executor.models as me_models
from .stage_config import ( from .stage_config import (
OmniStageConfig,
DiTConfig,
DiTCacheConfig, DiTCacheConfig,
DiTCacheTensor, DiTCacheTensor,
DiTConfig,
OmniStageConfig,
create_ar_stage_config, create_ar_stage_config,
create_dit_stage_config, create_dit_stage_config,
) )
@@ -42,9 +42,9 @@ class OmniModelConfig(ModelConfig):
__all__ = [ __all__ = [
"OmniModelConfig", "OmniModelConfig",
"OmniStageConfig", "OmniStageConfig",
"DiTConfig", "DiTConfig",
"DiTCacheConfig", "DiTCacheConfig",
"DiTCacheTensor", "DiTCacheTensor",
"create_ar_stage_config", "create_ar_stage_config",
"create_dit_stage_config", "create_dit_stage_config",
] ]
+28 -15
View File
@@ -3,19 +3,22 @@ Stage configuration for vLLM-omni multi-stage processing.
""" """
from dataclasses import dataclass from dataclasses import dataclass
from typing import List, Optional, Type, Literal, Any, Dict from typing import Any, Dict, List, Literal, Optional, Type
from vllm.config import ( from vllm.config import (
VllmConfig, CompilationConfig,
DeviceConfig, DeviceConfig,
LoadConfig, LoadConfig,
ModelConfig, ModelConfig,
CompilationConfig, VllmConfig,
) )
from vllm.executor.executor_base import ExecutorBase as Executor from vllm.executor.executor_base import ExecutorBase as Executor
@dataclass @dataclass
class DiTCacheTensor: class DiTCacheTensor:
"""Configuration for DiT cache tensors.""" """Configuration for DiT cache tensors."""
name: str name: str
shape: List[int] shape: List[int]
dtype: str = "float32" dtype: str = "float32"
@@ -25,6 +28,7 @@ class DiTCacheTensor:
@dataclass @dataclass
class DiTCacheConfig: class DiTCacheConfig:
"""Configuration for DiT caching system.""" """Configuration for DiT caching system."""
cache_tensors: List[DiTCacheTensor] cache_tensors: List[DiTCacheTensor]
max_cache_size: int = 1024 * 1024 * 1024 # 1GB max_cache_size: int = 1024 * 1024 * 1024 # 1GB
cache_strategy: str = "fifo" # fifo, lru, lfu cache_strategy: str = "fifo" # fifo, lru, lfu
@@ -73,38 +77,47 @@ class OmniStageConfig:
executor_class: Type[Executor] = None # Will be set based on engine_type executor_class: Type[Executor] = None # Will be set based on engine_type
default_stage_args: Optional[Dict[str, Any]] = None default_stage_args: Optional[Dict[str, Any]] = None
stage_output: Optional[Any] = None stage_output: Optional[Any] = None
def __post_init__(self): def __post_init__(self):
"""Validate configuration after initialization.""" """Validate configuration after initialization."""
if self.engine_type not in ["AR", "DiT"]: if self.engine_type not in ["AR", "DiT"]:
raise ValueError(f"Invalid engine_type: {self.engine_type}. Must be 'AR' or 'DiT'") raise ValueError(
f"Invalid engine_type: {self.engine_type}. Must be 'AR' or 'DiT'"
)
if self.engine_type == "DiT" and self.dit_config is None: if self.engine_type == "DiT" and self.dit_config is None:
raise ValueError("DiT engine requires dit_config") raise ValueError("DiT engine requires dit_config")
if not self.input_modalities: if not self.input_modalities:
raise ValueError("input_modalities cannot be empty") raise ValueError("input_modalities cannot be empty")
if not self.output_modalities: if not self.output_modalities:
raise ValueError("output_modalities cannot be empty") raise ValueError("output_modalities cannot be empty")
def get_executor_class(self) -> Type[Executor]: def get_executor_class(self) -> Type[Executor]:
"""Get the appropriate executor class for this stage.""" """Get the appropriate executor class for this stage."""
if self.executor_class is not None: if self.executor_class is not None:
return self.executor_class return self.executor_class
if self.engine_type == "AR": if self.engine_type == "AR":
from vllm.executor.uniproc_executor import UniProcExecutor from vllm.executor.uniproc_executor import UniProcExecutor
return UniProcExecutor return UniProcExecutor
elif self.engine_type == "DiT": elif self.engine_type == "DiT":
if self.dit_config and self.dit_config.use_diffusers: if self.dit_config and self.dit_config.use_diffusers:
from vllm_omni.executor.diffusers_executor import DiffusersPipelineExecutor from vllm_omni.executor.diffusers_executor import (
DiffusersPipelineExecutor,
)
return DiffusersPipelineExecutor return DiffusersPipelineExecutor
else: else:
from vllm.executor.uniproc_executor import UniProcExecutor from vllm.executor.uniproc_executor import UniProcExecutor
return UniProcExecutor return UniProcExecutor
raise ValueError(f"No executor class available for engine_type: {self.engine_type}") raise ValueError(
f"No executor class available for engine_type: {self.engine_type}"
)
def create_ar_stage_config( def create_ar_stage_config(
@@ -121,7 +134,7 @@ def create_ar_stage_config(
input_modalities = ["text"] input_modalities = ["text"]
if output_modalities is None: if output_modalities is None:
output_modalities = ["text"] output_modalities = ["text"]
# For now, we'll create minimal configs # For now, we'll create minimal configs
# vllm_config and executor_class will be handled by the LLM class # vllm_config and executor_class will be handled by the LLM class
return OmniStageConfig( return OmniStageConfig(
@@ -150,7 +163,7 @@ def create_dit_stage_config(
input_modalities = ["text"] input_modalities = ["text"]
if output_modalities is None: if output_modalities is None:
output_modalities = ["image"] output_modalities = ["image"]
if dit_config is None: if dit_config is None:
dit_config = DiTConfig(num_inference_steps=50) dit_config = DiTConfig(num_inference_steps=50)
+48 -41
View File
@@ -1,20 +1,21 @@
""" """
DiT Cache Manager for vLLM-omni. DiT Cache Manager for vLLM-omni.
This module provides caching functionality for DiT (Diffusion Transformer) models This module provides caching functionality for DiT (Diffusion Transformer)
to optimize inference performance and memory usage. models to optimize inference performance and memory usage.
""" """
import time import time
from typing import Any, Dict, List, Optional
import torch import torch
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
from ..config import DiTCacheConfig, DiTCacheTensor from ..config import DiTCacheConfig, DiTCacheTensor
class DiTCacheManager: class DiTCacheManager:
"""Manages DiT-specific caching for optimized inference.""" """Manages DiT-specific caching for optimized inference."""
def __init__(self, config: DiTCacheConfig): def __init__(self, config: DiTCacheConfig):
self.config = config self.config = config
self.cache_tensors: Dict[str, DiTCacheTensor] = {} self.cache_tensors: Dict[str, DiTCacheTensor] = {}
@@ -24,30 +25,30 @@ class DiTCacheManager:
self.cache_hits = 0 self.cache_hits = 0
self.cache_misses = 0 self.cache_misses = 0
self.cache_timeout = 3600 # 1 hour default timeout self.cache_timeout = 3600 # 1 hour default timeout
def allocate_cache(self, request_id: str, size: int) -> torch.Tensor: def allocate_cache(self, request_id: str, size: int) -> torch.Tensor:
"""Allocate cache for a specific request.""" """Allocate cache for a specific request."""
# Check if we have enough space # Check if we have enough space
if self.current_cache_size + size > self.max_cache_size: if self.current_cache_size + size > self.max_cache_size:
self._evict_cache() self._evict_cache()
# Create tensor # Create tensor
tensor = torch.zeros(size, dtype=torch.float32) tensor = torch.zeros(size, dtype=torch.float32)
# Store in cache # Store in cache
cache_tensor = DiTCacheTensor( cache_tensor = DiTCacheTensor(
name=request_id, name=request_id,
tensor=tensor, tensor=tensor,
timestamp=time.time(), timestamp=time.time(),
access_count=0, access_count=0,
size_bytes=size * 4 # Assuming float32 size_bytes=size * 4, # Assuming float32
) )
self.cache_tensors[request_id] = cache_tensor self.cache_tensors[request_id] = cache_tensor
self.current_cache_size += cache_tensor.size_bytes self.current_cache_size += cache_tensor.size_bytes
return tensor return tensor
def get_cache(self, request_id: str) -> Optional[torch.Tensor]: def get_cache(self, request_id: str) -> Optional[torch.Tensor]:
"""Get cached tensor for a request.""" """Get cached tensor for a request."""
if request_id in self.cache_tensors: if request_id in self.cache_tensors:
@@ -58,58 +59,58 @@ class DiTCacheManager:
else: else:
self.cache_misses += 1 self.cache_misses += 1
return None return None
def store_cache(self, request_id: str, tensor: torch.Tensor) -> None: def store_cache(self, request_id: str, tensor: torch.Tensor) -> None:
"""Store a tensor in the cache.""" """Store a tensor in the cache."""
if request_id in self.cache_tensors: if request_id in self.cache_tensors:
# Update existing cache # Update existing cache
old_tensor = self.cache_tensors[request_id] old_tensor = self.cache_tensors[request_id]
self.current_cache_size -= old_tensor.size_bytes self.current_cache_size -= old_tensor.size_bytes
new_size_bytes = tensor.numel() * 4 # Assuming float32 new_size_bytes = tensor.numel() * 4 # Assuming float32
self.current_cache_size += new_size_bytes self.current_cache_size += new_size_bytes
self.cache_tensors[request_id] = DiTCacheTensor( self.cache_tensors[request_id] = DiTCacheTensor(
name=request_id, name=request_id,
tensor=tensor, tensor=tensor,
timestamp=time.time(), timestamp=time.time(),
access_count=old_tensor.access_count, access_count=old_tensor.access_count,
size_bytes=new_size_bytes size_bytes=new_size_bytes,
) )
else: else:
# Create new cache entry # Create new cache entry
new_size_bytes = tensor.numel() * 4 new_size_bytes = tensor.numel() * 4
if self.current_cache_size + new_size_bytes > self.max_cache_size: if self.current_cache_size + new_size_bytes > self.max_cache_size:
self._evict_cache() self._evict_cache()
self.cache_tensors[request_id] = DiTCacheTensor( self.cache_tensors[request_id] = DiTCacheTensor(
name=request_id, name=request_id,
tensor=tensor, tensor=tensor,
timestamp=time.time(), timestamp=time.time(),
access_count=0, access_count=0,
size_bytes=new_size_bytes size_bytes=new_size_bytes,
) )
self.current_cache_size += new_size_bytes self.current_cache_size += new_size_bytes
def release_cache(self, request_id: str) -> None: def release_cache(self, request_id: str) -> None:
"""Release cache for a request.""" """Release cache for a request."""
if request_id in self.cache_tensors: if request_id in self.cache_tensors:
cache_tensor = self.cache_tensors[request_id] cache_tensor = self.cache_tensors[request_id]
self.current_cache_size -= cache_tensor.size_bytes self.current_cache_size -= cache_tensor.size_bytes
del self.cache_tensors[request_id] del self.cache_tensors[request_id]
def clear_expired_cache(self) -> None: def clear_expired_cache(self) -> None:
"""Clear expired cache entries.""" """Clear expired cache entries."""
current_time = time.time() current_time = time.time()
expired_keys = [] expired_keys = []
for request_id, cache_tensor in self.cache_tensors.items(): for request_id, cache_tensor in self.cache_tensors.items():
if current_time - cache_tensor.timestamp > self.cache_timeout: if current_time - cache_tensor.timestamp > self.cache_timeout:
expired_keys.append(request_id) expired_keys.append(request_id)
for request_id in expired_keys: for request_id in expired_keys:
self.release_cache(request_id) self.release_cache(request_id)
def _evict_cache(self) -> None: def _evict_cache(self) -> None:
"""Evict cache entries based on strategy.""" """Evict cache entries based on strategy."""
if self.config.cache_strategy == "fifo": if self.config.cache_strategy == "fifo":
@@ -120,42 +121,48 @@ class DiTCacheManager:
self._evict_lfu() self._evict_lfu()
else: else:
self._evict_fifo() # Default to FIFO self._evict_fifo() # Default to FIFO
def _evict_fifo(self) -> None: def _evict_fifo(self) -> None:
"""Evict cache entries using FIFO strategy.""" """Evict cache entries using FIFO strategy."""
if not self.cache_tensors: if not self.cache_tensors:
return return
# Find oldest entry # Find oldest entry
oldest_key = min(self.cache_tensors.keys(), oldest_key = min(
key=lambda k: self.cache_tensors[k].timestamp) self.cache_tensors.keys(),
key=lambda k: self.cache_tensors[k].timestamp,
)
self.release_cache(oldest_key) self.release_cache(oldest_key)
def _evict_lru(self) -> None: def _evict_lru(self) -> None:
"""Evict cache entries using LRU strategy.""" """Evict cache entries using LRU strategy."""
if not self.cache_tensors: if not self.cache_tensors:
return return
# Find least recently used entry # Find least recently used entry
lru_key = min(self.cache_tensors.keys(), lru_key = min(
key=lambda k: self.cache_tensors[k].access_count) self.cache_tensors.keys(),
key=lambda k: self.cache_tensors[k].access_count,
)
self.release_cache(lru_key) self.release_cache(lru_key)
def _evict_lfu(self) -> None: def _evict_lfu(self) -> None:
"""Evict cache entries using LFU strategy.""" """Evict cache entries using LFU strategy."""
if not self.cache_tensors: if not self.cache_tensors:
return return
# Find least frequently used entry # Find least frequently used entry
lfu_key = min(self.cache_tensors.keys(), lfu_key = min(
key=lambda k: self.cache_tensors[k].access_count) self.cache_tensors.keys(),
key=lambda k: self.cache_tensors[k].access_count,
)
self.release_cache(lfu_key) self.release_cache(lfu_key)
def get_statistics(self) -> Dict[str, Any]: def get_statistics(self) -> Dict[str, Any]:
"""Get cache statistics.""" """Get cache statistics."""
total_requests = self.cache_hits + self.cache_misses total_requests = self.cache_hits + self.cache_misses
hit_rate = self.cache_hits / total_requests if total_requests > 0 else 0 hit_rate = self.cache_hits / total_requests if total_requests > 0 else 0
return { return {
"cache_size": self.current_cache_size, "cache_size": self.current_cache_size,
"max_cache_size": self.max_cache_size, "max_cache_size": self.max_cache_size,
@@ -163,16 +170,16 @@ class DiTCacheManager:
"cache_hits": self.cache_hits, "cache_hits": self.cache_hits,
"cache_misses": self.cache_misses, "cache_misses": self.cache_misses,
"hit_rate": hit_rate, "hit_rate": hit_rate,
"num_cached_tensors": len(self.cache_tensors) "num_cached_tensors": len(self.cache_tensors),
} }
def clear_all_cache(self) -> None: def clear_all_cache(self) -> None:
"""Clear all cache entries.""" """Clear all cache entries."""
self.cache_tensors.clear() self.cache_tensors.clear()
self.current_cache_size = 0 self.current_cache_size = 0
self.cache_hits = 0 self.cache_hits = 0
self.cache_misses = 0 self.cache_misses = 0
def set_cache_timeout(self, timeout: float) -> None: def set_cache_timeout(self, timeout: float) -> None:
"""Set cache timeout in seconds.""" """Set cache timeout in seconds."""
self.cache_timeout = timeout self.cache_timeout = timeout
+3 -2
View File
@@ -7,8 +7,9 @@ class OmniScheduler(VLLMScheduler):
""" """
OmniScheduler: Scheduler for vLLM-omni multimodal processing. OmniScheduler: Scheduler for vLLM-omni multimodal processing.
This scheduler extends vLLM's scheduler to support multimodal and non-autoregressive This scheduler extends vLLM's scheduler to support multimodal and
processing with additional fields and methods specific to vLLM-omni. non-autoregressive processing with additional fields and methods
specific to vLLM-omni.
""" """
# Ensure scheduled_new_reqs carry omni-specific payloads (e.g., additional_information) # Ensure scheduled_new_reqs carry omni-specific payloads (e.g., additional_information)
+6 -13
View File
@@ -5,10 +5,7 @@ from __future__ import annotations
from typing import Any, Optional from typing import Any, Optional
from ..config import DiTConfig from ..config import DiTConfig
from ..worker.gpu_diffusion_worker import ( from ..worker.gpu_diffusion_worker import DiffusionGPUWorker, DiffusionRunnerOutput
DiffusionGPUWorker,
DiffusionRunnerOutput,
)
class DiffusionEngine: class DiffusionEngine:
@@ -27,14 +24,12 @@ class DiffusionEngine:
config_model_path = None config_model_path = None
if model_cfg is not None: if model_cfg is not None:
if isinstance(model_cfg, dict): if isinstance(model_cfg, dict):
config_model_path = ( config_model_path = model_cfg.get("model") or model_cfg.get(
model_cfg.get("model") "model_path"
or model_cfg.get("model_path")
) )
else: else:
config_model_path = ( config_model_path = getattr(model_cfg, "model", None) or getattr(
getattr(model_cfg, "model", None) model_cfg, "model_path", None
or getattr(model_cfg, "model_path", None)
) )
self.model_path = model_path or config_model_path self.model_path = model_path or config_model_path
@@ -116,9 +111,7 @@ class DiffusersPipelineEngine(DiffusionEngine):
else self.num_inference_steps else self.num_inference_steps
) )
guidance = ( guidance = (
float(guidance_scale) float(guidance_scale) if guidance_scale is not None else self.guidance_scale
if guidance_scale is not None
else self.guidance_scale
) )
return self.worker.generate( return self.worker.generate(
+45 -41
View File
@@ -3,17 +3,18 @@ API server for vLLM-omni.
""" """
import asyncio import asyncio
from typing import Dict, Any, List from typing import Any, Dict, List
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel
import uvicorn import uvicorn
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from .omni_llm import AsyncOmniLLM from .omni_llm import AsyncOmniLLM
class GenerateRequest(BaseModel): class GenerateRequest(BaseModel):
"""Request model for generation.""" """Request model for generation."""
prompts: List[str] prompts: List[str]
max_tokens: int = 100 max_tokens: int = 100
temperature: float = 0.7 temperature: float = 0.7
@@ -22,6 +23,7 @@ class GenerateRequest(BaseModel):
class GenerateResponse(BaseModel): class GenerateResponse(BaseModel):
"""Response model for generation.""" """Response model for generation."""
outputs: List[Dict[str, Any]] outputs: List[Dict[str, Any]]
stage_outputs: List[Dict[str, Any]] = None stage_outputs: List[Dict[str, Any]] = None
@@ -29,7 +31,7 @@ class GenerateResponse(BaseModel):
app = FastAPI( app = FastAPI(
title="vLLM-omni API", title="vLLM-omni API",
description="Multi-modality models inference and serving", description="Multi-modality models inference and serving",
version="0.1.0" version="0.1.0",
) )
# Global omni_llm instance # Global omni_llm instance
@@ -50,48 +52,51 @@ async def generate(request: GenerateRequest):
try: try:
if omni_llm is None: if omni_llm is None:
raise HTTPException(status_code=500, detail="OmniLLM not initialized") raise HTTPException(status_code=500, detail="OmniLLM not initialized")
# Prepare stage arguments # Prepare stage arguments
if request.stage_args is None: if request.stage_args is None:
# Create default stage arguments - one per stage config # Create default stage arguments - one per stage config
# For now, we'll process all prompts in the first stage # For now, we'll process all prompts in the first stage
stage_args = [{ stage_args = [
"prompt": " ".join(request.prompts) if request.prompts else "", {
"max_tokens": request.max_tokens, "prompt": " ".join(request.prompts) if request.prompts else "",
"temperature": request.temperature "max_tokens": request.max_tokens,
}] "temperature": request.temperature,
}
]
else: else:
stage_args = request.stage_args stage_args = request.stage_args
# Generate using omni_llm # Generate using omni_llm
try: try:
outputs = await omni_llm.generate_async(stage_args) outputs = await omni_llm.generate_async(stage_args)
except Exception as e: except Exception:
# Fallback to synchronous generation # Fallback to synchronous generation
outputs = omni_llm.generate(stage_args) outputs = omni_llm.generate(stage_args)
# Convert outputs to response format # Convert outputs to response format
response_outputs = [] response_outputs = []
for output in outputs: for output in outputs:
if hasattr(output, 'outputs') and output.outputs: if hasattr(output, "outputs") and output.outputs:
for out in output.outputs: for out in output.outputs:
response_outputs.append({ response_outputs.append(
"text": getattr(out, 'text', ''), {
"finished": getattr(out, 'finish_reason', 'length') != 'length', "text": getattr(out, "text", ""),
"tokens": getattr(out, 'token_ids', []) "finished": getattr(out, "finish_reason", "length")
}) != "length",
"tokens": getattr(out, "token_ids", []),
}
)
else: else:
response_outputs.append({ response_outputs.append({"text": "", "finished": True, "tokens": []})
"text": "",
"finished": True,
"tokens": []
})
return GenerateResponse( return GenerateResponse(
outputs=response_outputs, outputs=response_outputs,
stage_outputs=[{"stage": i, "output": "processed"} for i in range(len(stage_args))] stage_outputs=[
{"stage": i, "output": "processed"} for i in range(len(stage_args))
],
) )
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@@ -107,36 +112,35 @@ async def get_info():
"""Get information about the service.""" """Get information about the service."""
if omni_llm is None: if omni_llm is None:
return {"error": "OmniLLM not initialized"} return {"error": "OmniLLM not initialized"}
return { return {
"service": "vllm-omni", "service": "vllm-omni",
"version": "0.1.0", "version": "0.1.0",
"num_stages": omni_llm.get_num_stages() if hasattr(omni_llm, 'get_num_stages') else 0, "num_stages": omni_llm.get_num_stages()
if hasattr(omni_llm, "get_num_stages")
else 0,
"stage_configs": [ "stage_configs": [
{ {
"stage_id": config.stage_id, "stage_id": config.stage_id,
"engine_type": config.engine_type, "engine_type": config.engine_type,
"model_path": config.model_path, "model_path": config.model_path,
"input_modalities": config.input_modalities, "input_modalities": config.input_modalities,
"output_modalities": config.output_modalities "output_modalities": config.output_modalities,
} }
for config in omni_llm.stage_configs for config in omni_llm.stage_configs
] ],
} }
async def run_server(omni_llm_instance: AsyncOmniLLM, host: str = "0.0.0.0", port: int = 8000): async def run_server(
omni_llm_instance: AsyncOmniLLM, host: str = "0.0.0.0", port: int = 8000
):
"""Run the API server.""" """Run the API server."""
global omni_llm global omni_llm
omni_llm = omni_llm_instance omni_llm = omni_llm_instance
config = uvicorn.Config( config = uvicorn.Config(app=app, host=host, port=port, log_level="info")
app=app,
host=host,
port=port,
log_level="info"
)
server = uvicorn.Server(config) server = uvicorn.Server(config)
await server.serve() await server.serve()
+2 -2
View File
@@ -3,8 +3,7 @@ CLI entry point for vLLM-omni that intercepts vLLM commands.
""" """
import sys import sys
import argparse
from typing import List, Optional
from vllm_omni.entrypoints.cli.serve import OmniServeCommand from vllm_omni.entrypoints.cli.serve import OmniServeCommand
@@ -19,6 +18,7 @@ def main():
else: else:
# Forward to original vLLM CLI # Forward to original vLLM CLI
from vllm.entrypoints.cli.main import main as vllm_main from vllm.entrypoints.cli.main import main as vllm_main
vllm_main() vllm_main()
+46 -64
View File
@@ -4,125 +4,111 @@ Omni serve command for vLLM-omni.
import argparse import argparse
import asyncio import asyncio
from typing import List, Optional from typing import List
from vllm_omni.config import DiTConfig, create_ar_stage_config, create_dit_stage_config
from ..omni_llm import AsyncOmniLLM from ..omni_llm import AsyncOmniLLM
from vllm_omni.config import (
DiTConfig,
create_ar_stage_config,
create_dit_stage_config,
)
class OmniServeCommand: class OmniServeCommand:
"""Command handler for vLLM-omni serve command.""" """Command handler for vLLM-omni serve command."""
def __init__(self): def __init__(self):
self.parser = self._create_parser() self.parser = self._create_parser()
def _create_parser(self) -> argparse.ArgumentParser: def _create_parser(self) -> argparse.ArgumentParser:
"""Create argument parser for omni serve command.""" """Create argument parser for omni serve command."""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="vLLM-omni: Multi-modality models inference and serving" description="vLLM-omni: Multi-modality models inference and serving"
) )
# Model arguments - make it optional with default # Model arguments - make it optional with default
parser.add_argument( parser.add_argument(
"model", "model",
nargs="?", nargs="?",
default="Qwen/Qwen3-0.6B", default="Qwen/Qwen3-0.6B",
help="Path to the model or model name (default: Qwen/Qwen3-0.6B)" help="Path to the model or model name (default: Qwen/Qwen3-0.6B)",
) )
# Server arguments # Server arguments
parser.add_argument( parser.add_argument(
"--port", "--port",
type=int, type=int,
default=8000, default=8000,
help="Port to run the server on" help="Port to run the server on",
) )
parser.add_argument( parser.add_argument(
"--host", "--host",
type=str, type=str,
default="0.0.0.0", default="0.0.0.0",
help="Host to run the server on" help="Host to run the server on",
) )
# Stage configuration arguments # Stage configuration arguments
parser.add_argument( parser.add_argument("--ar-stage", type=str, help="AR stage model path")
"--ar-stage",
type=str, parser.add_argument("--dit-stage", type=str, help="DiT stage model path")
help="AR stage model path"
)
parser.add_argument(
"--dit-stage",
type=str,
help="DiT stage model path"
)
parser.add_argument( parser.add_argument(
"--dit-steps", "--dit-steps",
type=int, type=int,
default=50, default=50,
help="Number of DiT inference steps" help="Number of DiT inference steps",
) )
parser.add_argument( parser.add_argument(
"--dit-guidance-scale", "--dit-guidance-scale",
type=float, type=float,
default=7.5, default=7.5,
help="DiT guidance scale" help="DiT guidance scale",
) )
parser.add_argument( parser.add_argument(
"--use-diffusers", "--use-diffusers",
action="store_true", action="store_true",
help="Use diffusers pipeline for DiT stage" help="Use diffusers pipeline for DiT stage",
) )
# Other arguments # Other arguments
parser.add_argument( parser.add_argument(
"--log-stats", "--log-stats", action="store_true", help="Enable logging statistics"
action="store_true",
help="Enable logging statistics"
) )
return parser return parser
def run(self, args: List[str]) -> None: def run(self, args: List[str]) -> None:
"""Run the omni serve command.""" """Run the omni serve command."""
parsed_args = self.parser.parse_args(args) parsed_args = self.parser.parse_args(args)
# Create stage configurations # Create stage configurations
stage_configs = self._create_stage_configs(parsed_args) stage_configs = self._create_stage_configs(parsed_args)
# Create AsyncOmniLLM instance # Create AsyncOmniLLM instance
omni_llm = AsyncOmniLLM( omni_llm = AsyncOmniLLM(
stage_configs=stage_configs, stage_configs=stage_configs, log_stats=parsed_args.log_stats
log_stats=parsed_args.log_stats
) )
# Start the server # Start the server
asyncio.run(self._start_server(omni_llm, parsed_args)) asyncio.run(self._start_server(omni_llm, parsed_args))
def _create_stage_configs(self, args) -> List: def _create_stage_configs(self, args) -> List:
"""Create stage configurations based on arguments.""" """Create stage configurations based on arguments."""
stage_configs = [] stage_configs = []
stage_id = 0 stage_id = 0
# Add AR stage - use main model if ar_stage not specified # Add AR stage - use main model if ar_stage not specified
ar_model = args.ar_stage if args.ar_stage else args.model ar_model = args.ar_stage if args.ar_stage else args.model
ar_config = create_ar_stage_config( ar_config = create_ar_stage_config(
stage_id=stage_id, stage_id=stage_id,
model_path=ar_model, model_path=ar_model,
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
stage_configs.append(ar_config) stage_configs.append(ar_config)
stage_id += 1 stage_id += 1
# Add DiT stage if specified # Add DiT stage if specified
if args.dit_stage: if args.dit_stage:
dit_model = args.dit_stage dit_model = args.dit_stage
@@ -131,7 +117,7 @@ class OmniServeCommand:
dit_model = "stabilityai/stable-diffusion-2-1" dit_model = "stabilityai/stable-diffusion-2-1"
else: else:
dit_model = None dit_model = None
if dit_model: if dit_model:
dit_config = DiTConfig( dit_config = DiTConfig(
num_inference_steps=args.dit_steps, num_inference_steps=args.dit_steps,
@@ -139,17 +125,17 @@ class OmniServeCommand:
use_diffusers=args.use_diffusers, use_diffusers=args.use_diffusers,
diffusers_pipeline="auto" if args.use_diffusers else None, diffusers_pipeline="auto" if args.use_diffusers else None,
) )
dit_stage_config = create_dit_stage_config( dit_stage_config = create_dit_stage_config(
stage_id=stage_id, stage_id=stage_id,
model_path=dit_model, model_path=dit_model,
input_modalities=["text"], input_modalities=["text"],
output_modalities=["image"], output_modalities=["image"],
dit_config=dit_config dit_config=dit_config,
) )
stage_configs.append(dit_stage_config) stage_configs.append(dit_stage_config)
stage_id += 1 stage_id += 1
# If no specific stages are specified, use the main model # If no specific stages are specified, use the main model
if not stage_configs: if not stage_configs:
# Try to detect if it's a multimodal model # Try to detect if it's a multimodal model
@@ -159,23 +145,23 @@ class OmniServeCommand:
stage_id=0, stage_id=0,
model_path=args.model, model_path=args.model,
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
stage_configs.append(ar_config) stage_configs.append(ar_config)
dit_config = DiTConfig( dit_config = DiTConfig(
num_inference_steps=args.dit_steps, num_inference_steps=args.dit_steps,
guidance_scale=args.dit_guidance_scale, guidance_scale=args.dit_guidance_scale,
use_diffusers=args.use_diffusers, use_diffusers=args.use_diffusers,
diffusers_pipeline="auto" if args.use_diffusers else None, diffusers_pipeline="auto" if args.use_diffusers else None,
) )
dit_stage_config = create_dit_stage_config( dit_stage_config = create_dit_stage_config(
stage_id=1, stage_id=1,
model_path=args.model, model_path=args.model,
input_modalities=["text"], input_modalities=["text"],
output_modalities=["image"], output_modalities=["image"],
dit_config=dit_config dit_config=dit_config,
) )
stage_configs.append(dit_stage_config) stage_configs.append(dit_stage_config)
else: else:
@@ -184,23 +170,19 @@ class OmniServeCommand:
stage_id=0, stage_id=0,
model_path=args.model, model_path=args.model,
input_modalities=["text"], input_modalities=["text"],
output_modalities=["text"] output_modalities=["text"],
) )
stage_configs.append(ar_config) stage_configs.append(ar_config)
return stage_configs return stage_configs
async def _start_server(self, omni_llm: AsyncOmniLLM, args) -> None: async def _start_server(self, omni_llm: AsyncOmniLLM, args) -> None:
"""Start the API server.""" """Start the API server."""
try: try:
# Import here to avoid circular imports # Import here to avoid circular imports
from vllm_omni.entrypoints.api_server import run_server from vllm_omni.entrypoints.api_server import run_server
await run_server( await run_server(omni_llm_instance=omni_llm, host=args.host, port=args.port)
omni_llm_instance=omni_llm,
host=args.host,
port=args.port
)
except KeyboardInterrupt: except KeyboardInterrupt:
print("\nShutting down server...") print("\nShutting down server...")
except Exception as e: except Exception as e: