Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 24 additions & 29 deletions .github/workflows/api_tests/mock_vllm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import hashlib
import time
import uuid
from typing import Any, List, Optional, Union
from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel
Expand All @@ -22,19 +22,19 @@

class EmbeddingRequest(BaseModel):
model: str
input: Union[str, List[str]]
input: str | list[str]
encoding_format: str = "float"


class EmbeddingData(BaseModel):
object: str = "embedding"
embedding: List[float]
embedding: list[float]
index: int


class EmbeddingResponse(BaseModel):
object: str = "list"
data: List[EmbeddingData]
data: list[EmbeddingData]
model: str
usage: dict

Expand All @@ -49,13 +49,13 @@ class ChatMessage(BaseModel):

class ChatCompletionRequest(BaseModel):
model: str
messages: List[ChatMessage]
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = 1024
stream: Optional[bool] = False
top_p: Optional[float] = 1.0
n: Optional[int] = 1
stop: Optional[Union[str, List[str]]] = None
messages: list[ChatMessage]
temperature: float | None = 0.7
max_tokens: int | None = 1024
stream: bool | None = False
top_p: float | None = 1.0
n: int | None = 1
stop: str | list[str] | None = None


class ChatCompletionChoice(BaseModel):
Expand All @@ -75,7 +75,7 @@ class ChatCompletionResponse(BaseModel):
object: str = "chat.completion"
created: int
model: str
choices: List[ChatCompletionChoice]
choices: list[ChatCompletionChoice]
usage: ChatCompletionUsage


Expand All @@ -84,13 +84,13 @@ class ChatCompletionResponse(BaseModel):

class TextCompletionRequest(BaseModel):
model: str
prompt: Union[str, List[str]]
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = 1024
stream: Optional[bool] = False
top_p: Optional[float] = 1.0
n: Optional[int] = 1
stop: Optional[Union[str, List[str]]] = None
prompt: str | list[str]
temperature: float | None = 0.7
max_tokens: int | None = 1024
stream: bool | None = False
top_p: float | None = 1.0
n: int | None = 1
stop: str | list[str] | None = None


class TextCompletionChoice(BaseModel):
Expand All @@ -104,14 +104,14 @@ class TextCompletionResponse(BaseModel):
object: str = "text_completion"
created: int
model: str
choices: List[TextCompletionChoice]
choices: list[TextCompletionChoice]
usage: ChatCompletionUsage # Same structure as chat


# ============== Helper Functions ==============


def generate_fake_embedding(text: str, dim: int = EMBEDDING_DIM) -> List[float]:
def generate_fake_embedding(text: str, dim: int = EMBEDDING_DIM) -> list[float]:
"""Generate deterministic fake embedding based on text hash."""
h = hashlib.md5(text.encode()).digest()
result = []
Expand All @@ -128,7 +128,7 @@ def count_tokens(text: str) -> int:
return 10 # Default for non-string content


def generate_mock_response(messages: List[ChatMessage]) -> str:
def generate_mock_response(messages: list[ChatMessage]) -> str:
"""Generate a mock response based on the input messages."""
last_message = messages[-1] if messages else None
if not last_message:
Expand Down Expand Up @@ -180,10 +180,7 @@ async def list_models():
async def create_embeddings(request: EmbeddingRequest) -> EmbeddingResponse:
inputs = request.input if isinstance(request.input, list) else [request.input]

data = [
EmbeddingData(embedding=generate_fake_embedding(text), index=i)
for i, text in enumerate(inputs)
]
data = [EmbeddingData(embedding=generate_fake_embedding(text), index=i) for i, text in enumerate(inputs)]

return EmbeddingResponse(
data=data,
Expand All @@ -196,9 +193,7 @@ async def create_embeddings(request: EmbeddingRequest) -> EmbeddingResponse:
async def create_chat_completion(request: ChatCompletionRequest) -> ChatCompletionResponse:
"""Mock chat completion endpoint for LLM/VLM requests."""
# Calculate token counts
prompt_tokens = sum(
count_tokens(str(msg.content)) for msg in request.messages
)
prompt_tokens = sum(count_tokens(str(msg.content)) for msg in request.messages)

# Generate mock response
response_text = generate_mock_response(request.messages)
Expand Down
52 changes: 52 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Linting

on:
push:
branches: [ main, dev ]

# Temporarily disabled until merge
# pull_request:
# branches: [ main, dev ]

jobs:
lint:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ["3.12"]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH

- name: Cache uv
uses: actions/cache@v4
with:
path: |
~/.cache/uv
key: uv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml', '**/uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-${{ matrix.python-version }}-

- name: Install dependencies with uv
run: |
uv sync --group lint

- name: Run Ruff linting
run: |
uv run ruff check . --output-format=github

- name: Check formatting
run: |
uv run ruff format --check .
148 changes: 148 additions & 0 deletions LINTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Linting Setup Guide

This project uses **Ruff** for Python linting and code formatting. All developers must ensure their code passes linting checks before submitting pull requests.

## Installation

The linting dependencies are already included in `pyproject.toml`. Install them with:

```bash
uv sync --group lint
```

Or install all groups including dev:

```bash
uv sync
```

## Running Linting Locally

### Check for linting issues

```bash
uv run ruff check .
```

### Auto-fix linting issues

```bash
uv run ruff check . --fix
```

### Check code formatting

```bash
uv run ruff format --check .
```

### Auto-format code

```bash
uv run ruff format .
```

## Ruff Configuration

Ruff configuration is defined in `pyproject.toml` under `[tool.ruff]` and `[tool.ruff.lint]` sections.

### Key Rules Enabled

- **E, W**: PEP 8 errors and warnings
- **F**: Pyflakes (undefined names, unused imports)
- **I**: isort-style import sorting
- **C4**: flake8-comprehensions (optimization suggestions)
- **UP**: pyupgrade (modernize Python code)
- **PIE**: flake8-pie (misc linting rules)
- **RUF**: Ruff-specific rules (**`not applied currently`**)

### Excluded Directories

The following directories are excluded from linting:
- `.git`, `.venv`, `.pytest_cache`, `.ruff_cache`, `.vscode`
- `node_modules`, `vdb`, `ray_mount`, `model_weights`, `logs`
- `.astro`, `openrag.egg-info`

## CI/CD Integration

A GitHub Actions workflow (`.github/workflows/lint.yml`) automatically runs on every push and pull request. The workflow:

1. Checks for linting issues with `ruff check`
2. Verifies code formatting with `ruff format --check`
3. Reports results directly to GitHub (with file and line annotations)

### Pull Request Requirements

All pull requests must pass the linting CI check. If your PR fails:

1. Run `uv run ruff check . --fix` to auto-fix issues
2. Run `uv run ruff format .` to format the code
3. Review any remaining issues that require manual fixes
4. Commit and push your changes

## Pre-commit Hook (Optional)
One can also setup a pre-commit hook to run linting automatically before each commit.

```bash
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash

# Get list of staged Python files
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')

if [ -z "$FILES" ]; then
exit 0
fi

# Run ruff check (will auto-fix if possible)
echo "Running ruff check..."
uv run ruff check $FILES --fix

# Run ruff format
echo "Running ruff format..."
uv run ruff format $FILES

# Re-add the modified files
git add $FILES

# Run ruff check again without --fix to catch remaining errors
echo "Checking for remaining issues..."
uv run ruff check $FILES

Comment on lines +86 to +111

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Pre-commit snippet should handle filenames with spaces safely.
Unquoted $FILES will break on paths with spaces. Consider using a bash array via mapfile.

Suggested doc snippet update
-FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')
+mapfile -t FILES < <(git diff --cached --name-only --diff-filter=ACM | grep -E '\.py$')

-if [ -z "$FILES" ]; then
+if [ ${`#FILES`[@]} -eq 0 ]; then
   exit 0
 fi

-uv run ruff check $FILES --fix
+uv run ruff check --fix "${FILES[@]}"

-uv run ruff format $FILES
+uv run ruff format "${FILES[@]}"

-git add $FILES
+git add "${FILES[@]}"

-uv run ruff check $FILES
+uv run ruff check "${FILES[@]}"
🤖 Prompt for AI Agents
In `@LINTING.md` around lines 86 - 111, The pre-commit hook currently collects
staged Python files into the unquoted FILES variable which breaks on paths with
spaces; change the collection to a bash array (e.g., use mapfile -t FILES <
<(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')) and then use
the array safely everywhere by expanding as "${FILES[@]}" when invoking commands
(uv run ruff check/format, git add) and when testing emptiness use: if [
${`#FILES`[@]} -eq 0 ]; then exit 0; fi; update usages of FILES in the hook to the
array form to ensure filenames with spaces are handled correctly.

# If ruff check fails, block the commit
if [ $? -ne 0 ]; then
echo "Ruff check failed. Please fix errors before committing."
exit 1
fi

exit 0
EOF

chmod +x .git/hooks/pre-commit
```

* To disable pre-commit

```bash
rm .git/hooks/pre-commit
```

## Troubleshooting

### "ruff: command not found"
Make sure you've installed the lint dependencies:
```bash
uv sync --group lint
```

### Auto-fix doesn't resolve all issues
Some issues require manual fixes. Review the error messages and fix them manually, then commit again.
Sometimes, you will have to ignore a particular check if deemed unnecessary

```python
# Example: C417 rule suggest that map here is unnecessary


docs_with_tokens = list(map(lambda d: (_length_function(d.page_content), d), docs)) # noqa: C417

```
Loading