Skip to content

Commit 40af103

Browse files
authored
Merge branch 'main' into unit_test
2 parents 26dde9d + 2146787 commit 40af103

File tree

17 files changed

+259
-40
lines changed

17 files changed

+259
-40
lines changed

.github/workflows/docker.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
images: vectorinstitute/vector-inference
4646

4747
- name: Build and push Docker image
48-
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1
48+
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0
4949
with:
5050
context: .
5151
file: ./Dockerfile

.github/workflows/unit_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
uv run pytest tests/test_imports.py
7373
7474
- name: Upload coverage to Codecov
75-
uses: codecov/codecov-action@v5.4.2
75+
uses: codecov/codecov-action@v5.4.3
7676
with:
7777
token: ${{ secrets.CODECOV_TOKEN }}
7878
file: ./coverage.xml

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ repos:
1717
- id: check-toml
1818

1919
- repo: https://github.com/astral-sh/ruff-pre-commit
20-
rev: 'v0.11.9'
20+
rev: 'v0.11.11'
2121
hooks:
2222
- id: ruff
2323
args: [--fix, --exit-non-zero-on-fix]

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ models:
8585
vllm_args:
8686
--max-model-len: 1010000
8787
--max-num-seqs: 256
88-
--compilation-confi: 3
88+
--compilation-config: 3
8989
```
9090
9191
You would then set the `VEC_INF_CONFIG` path using:
@@ -94,7 +94,11 @@ You would then set the `VEC_INF_CONFIG` path using:
9494
export VEC_INF_CONFIG=/h/<username>/my-model-config.yaml
9595
```
9696

97-
Note that there are other parameters that can also be added to the config but not shown in this example, check the [`ModelConfig`](vec_inf/client/config.py) for details.
97+
**NOTE**
98+
* There are other parameters that can also be added to the config but not shown in this example, check the [`ModelConfig`](vec_inf/client/config.py) for details.
99+
* Check [vLLM Engine Arguments](https://docs.vllm.ai/en/stable/serving/engine_args.html) for the full list of available vLLM engine arguments, the default parallel size for any parallelization is default to 1, so none of the sizes were set specifically in this example
100+
* For GPU partitions with non-Ampere architectures, e.g. `rtx6000`, `t4v2`, BF16 isn't supported. For models that have BF16 as the default type, when using a non-Ampere GPU, use FP16 instead, i.e. `--dtype: float16`
101+
* Setting `--compilation-config` to `3` currently breaks multi-node model launches, so we don't set them for models that require multiple nodes of GPUs.
98102

99103
#### Other commands
100104

@@ -161,7 +165,7 @@ Once the inference server is ready, you can start sending in inference requests.
161165
"prompt_logprobs":null
162166
}
163167
```
164-
**NOTE**: For multimodal models, currently only `ChatCompletion` is available, and only one image can be provided for each prompt.
168+
**NOTE**: Certain models don't adhere to OpenAI's chat template, e.g. Mistral family. For these models, you can either change your prompt to follow the model's default chat template or provide your own chat template via `--chat-template: TEMPLATE_PATH`
165169

166170
## SSH tunnel from your local device
167171
If you want to run inference from your local device, you can open a SSH tunnel to your cluster environment like the following:

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
- [`logits.py`](logits/logits.py): Python example of getting logits from hosted model.
1010
- [`api`](api): Examples for using the Python API
1111
- [`basic_usage.py`](api/basic_usage.py): Basic Python example demonstrating the Vector Inference API
12+
- [`slurm_dependency`](slurm_dependency): Example of launching a model with `vec-inf` and running a downstream SLURM job that waits for the server to be ready before sending a request.

examples/slurm_dependency/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# SLURM Dependency Workflow Example
2+
3+
This example demonstrates how to launch a model server using `vec-inf`, and run a downstream SLURM job that waits for the server to become ready before querying it.
4+
5+
## Files
6+
7+
This directory contains the following:
8+
9+
1. [run_workflow.sh](run_workflow.sh)
10+
Launches the model server and submits the downstream job with a dependency, so it starts only after the server job begins running.
11+
12+
2. [downstream_job.sbatch](downstream_job.sbatch)
13+
A SLURM job script that runs the downstream logic (e.g., prompting the model).
14+
15+
3. [run_downstream.py](run_downstream.py)
16+
A Python script that waits until the inference server is ready, then sends a request using the OpenAI-compatible API.
17+
18+
## What to update
19+
20+
Before running this example, update the following in [downstream_job.sbatch](downstream_job.sbatch):
21+
22+
- `--job-name`, `--output`, and `--error` paths
23+
- Virtual environment path in the `source` line
24+
- SLURM resource configuration (e.g., partition, memory, GPU)
25+
26+
Also update the model name in [run_downstream.py](run_downstream.py) to match what you're launching.
27+
28+
## Running the example
29+
30+
First, activate a virtual environment where `vec-inf` is installed. Then, from this directory, run:
31+
32+
```bash
33+
bash run_workflow.sh
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
#SBATCH --job-name=Meta-Llama-3.1-8B-Instruct-downstream
3+
#SBATCH --partition=a40
4+
#SBATCH --qos=m2
5+
#SBATCH --time=08:00:00
6+
#SBATCH --nodes=1
7+
#SBATCH --gpus-per-node=1
8+
#SBATCH --cpus-per-task=4
9+
#SBATCH --mem=8G
10+
#SBATCH --output=$HOME/.vec-inf-logs/Meta-Llama-3.1-8B-Instruct-downstream.%j.out
11+
#SBATCH --error=$HOME/.vec-inf-logs/Meta-Llama-3.1-8B-Instruct-downstream.%j.err
12+
13+
# Activate your environment
14+
# TODO: update this path to match your venv location
15+
source $HOME/vector-inference/.venv/bin/activate
16+
17+
# Wait for the server to be ready using the job ID passed as CLI arg
18+
python run_downstream.py "$SERVER_JOB_ID"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Example script to query a launched model via the OpenAI-compatible API."""
2+
3+
import sys
4+
5+
from openai import OpenAI
6+
7+
from vec_inf.client import VecInfClient
8+
9+
10+
if len(sys.argv) < 2:
11+
raise ValueError("Expected server job ID as the first argument.")
12+
job_id = int(sys.argv[1])
13+
14+
vi_client = VecInfClient()
15+
print(f"Waiting for SLURM job {job_id} to be ready...")
16+
status = vi_client.wait_until_ready(slurm_job_id=job_id)
17+
print(f"Server is ready at {status.base_url}")
18+
19+
api_client = OpenAI(base_url=status.base_url, api_key="EMPTY")
20+
resp = api_client.completions.create(
21+
model="Meta-Llama-3.1-8B-Instruct",
22+
prompt="Where is the capital of Canada?",
23+
max_tokens=20,
24+
)
25+
26+
print(resp)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
# ---- Config ----
4+
MODEL_NAME="Meta-Llama-3.1-8B-Instruct"
5+
LAUNCH_ARGS="$MODEL_NAME"
6+
7+
# ---- Step 1: Launch the server
8+
RAW_JSON=$(vec-inf launch $LAUNCH_ARGS --json-mode)
9+
SERVER_JOB_ID=$(echo "$RAW_JSON" | python3 -c "import sys, json; print(json.load(sys.stdin)['slurm_job_id'])")
10+
echo "Launched server as job $SERVER_JOB_ID"
11+
echo "$RAW_JSON"
12+
13+
# ---- Step 2: Submit downstream job
14+
sbatch --dependency=after:$SERVER_JOB_ID --export=SERVER_JOB_ID=$SERVER_JOB_ID downstream_job.sbatch

tests/vec_inf/client/test_api.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pytest
66

77
from vec_inf.client import ModelStatus, ModelType, VecInfClient
8+
from vec_inf.client._exceptions import ServerError, SlurmJobError
89

910

1011
@pytest.fixture
@@ -128,3 +129,84 @@ def test_wait_until_ready():
128129
assert result.server_status == ModelStatus.READY
129130
assert result.base_url == "http://gpu123:8080/v1"
130131
assert mock_status.call_count == 2
132+
133+
134+
def test_shutdown_model_success():
135+
"""Test model shutdown success."""
136+
client = VecInfClient()
137+
with patch("vec_inf.client.api.run_bash_command") as mock_command:
138+
mock_command.return_value = ("", "")
139+
result = client.shutdown_model(12345)
140+
141+
assert result is True
142+
mock_command.assert_called_once_with("scancel 12345")
143+
144+
145+
def test_shutdown_model_failure():
146+
"""Test model shutdown failure."""
147+
client = VecInfClient()
148+
with patch("vec_inf.client.api.run_bash_command") as mock_command:
149+
mock_command.return_value = ("", "Error: Job not found")
150+
with pytest.raises(
151+
SlurmJobError, match="Failed to shutdown model: Error: Job not found"
152+
):
153+
client.shutdown_model(12345)
154+
155+
156+
def test_wait_until_ready_timeout():
157+
"""Test timeout in wait_until_ready."""
158+
client = VecInfClient()
159+
160+
with patch.object(client, "get_status") as mock_status:
161+
mock_response = MagicMock()
162+
mock_response.server_status = ModelStatus.LAUNCHING
163+
mock_status.return_value = mock_response
164+
165+
with (
166+
patch("time.sleep"),
167+
pytest.raises(ServerError, match="Timed out waiting for model"),
168+
):
169+
client.wait_until_ready(12345, timeout_seconds=1, poll_interval_seconds=0.5)
170+
171+
172+
def test_wait_until_ready_failed_status():
173+
"""Test wait_until_ready when model fails."""
174+
client = VecInfClient()
175+
176+
with patch.object(client, "get_status") as mock_status:
177+
mock_response = MagicMock()
178+
mock_response.server_status = ModelStatus.FAILED
179+
mock_response.failed_reason = "Out of memory"
180+
mock_status.return_value = mock_response
181+
182+
with pytest.raises(ServerError, match="Model failed to start: Out of memory"):
183+
client.wait_until_ready(12345)
184+
185+
186+
def test_wait_until_ready_failed_no_reason():
187+
"""Test wait_until_ready when model fails without reason."""
188+
client = VecInfClient()
189+
190+
with patch.object(client, "get_status") as mock_status:
191+
mock_response = MagicMock()
192+
mock_response.server_status = ModelStatus.FAILED
193+
mock_response.failed_reason = None
194+
mock_status.return_value = mock_response
195+
196+
with pytest.raises(ServerError, match="Model failed to start: Unknown error"):
197+
client.wait_until_ready(12345)
198+
199+
200+
def test_wait_until_ready_shutdown():
201+
"""Test wait_until_ready when model is shutdown."""
202+
client = VecInfClient()
203+
204+
with patch.object(client, "get_status") as mock_status:
205+
mock_response = MagicMock()
206+
mock_response.server_status = ModelStatus.SHUTDOWN
207+
mock_status.return_value = mock_response
208+
209+
with pytest.raises(
210+
ServerError, match="Model was shutdown before it became ready"
211+
):
212+
client.wait_until_ready(12345)

0 commit comments

Comments
 (0)