Skip to content

Commit 01b9ba8

Browse files
jjmachanshahules786Jithin James
authored
chore: adding a CI/CD pipeline (#11)
* add max_length * add max_length * black fixes * added benchmarks * pretty print benchmarks * added makefile for all CI/CD * fix Makefiles * new CI workflow * unit-test workflow * add github token * permissions * install library * add code stype checks * fix linting and formating * fix type annotation errors * fix check for types * fix lint * fix old state storage --------- Co-authored-by: Shahules786 <[email protected]> Co-authored-by: Jithin James <[email protected]>
1 parent b8970bc commit 01b9ba8

File tree

16 files changed

+403
-161
lines changed

16 files changed

+403
-161
lines changed

.github/workflows/ci.yaml

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
6+
permissions:
7+
contents: read
8+
9+
env:
10+
LINES: 120
11+
COLUMNS: 120
12+
13+
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#defaultsrun
14+
defaults:
15+
run:
16+
shell: bash --noprofile --norc -exo pipefail {0}
17+
18+
jobs:
19+
diff:
20+
runs-on: ubuntu-latest
21+
outputs:
22+
related: ${{ steps.filter.outputs.related }}
23+
belar: ${{ steps.filter.outputs.belar }}
24+
docs: ${{ steps.filter.outputs.docs }}
25+
steps:
26+
- uses: actions/checkout@v3
27+
- uses: dorny/paths-filter@v2
28+
id: filter
29+
with:
30+
base: "main"
31+
token: ${{ github.token }}
32+
filters: |
33+
related: &related
34+
- .github/workflows/ci.yml
35+
- codecov.yml
36+
- pyproject.toml
37+
- requirements/test.txt
38+
belar:
39+
- "belar/**"
40+
- "tests/**"
41+
- "examples/**"
42+
docs:
43+
- *related
44+
- requirements/docs-requirements.txt
45+
- "docs/**"
46+
47+
unit_tests:
48+
needs:
49+
- diff
50+
51+
strategy:
52+
fail-fast: false
53+
matrix:
54+
os: [ubuntu-latest, macos-latest, windows-latest]
55+
python-version: ["3.7", "3.8", "3.9", "3.10"]
56+
57+
if: ${{ (github.event_name == 'pull_request' && needs.diff.outputs.belar == 'true') || github.event_name == 'push' }}
58+
name: python${{ matrix.python-version }}_unit_tests (${{ matrix.os }})
59+
runs-on: ${{ matrix.os }}
60+
61+
steps:
62+
- uses: actions/checkout@v3
63+
with:
64+
fetch-depth: 0 # fetch all tags and branches
65+
66+
- name: Setup python
67+
uses: actions/setup-python@v4
68+
with:
69+
python-version: ${{ matrix.python-version }}
70+
architecture: x64
71+
72+
- name: Get pip cache dir
73+
id: cache-dir
74+
run: |
75+
echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
76+
77+
- name: Cache pip dependencies
78+
uses: actions/cache@v3
79+
id: cache-pip
80+
with:
81+
path: ${{ steps.cache-dir.outputs.dir }}
82+
key: ${{ runner.os }}-tests-${{ hashFiles('requirements/test.txt') }}
83+
84+
- name: Install dependencies
85+
run: |
86+
pip install "."
87+
pip install -r requirements/test.txt
88+
89+
- name: Run unit tests
90+
run: |
91+
# OPTS=(--cov-config pyproject.toml --cov=src/bentoml --cov-append)
92+
if [ "${{ matrix.os }}" != 'windows-latest' ]; then
93+
# we will use pytest-xdist to improve tests run-time.
94+
OPTS=(--dist loadfile -n auto)
95+
fi
96+
# Now run the unit tests
97+
pytest tests/unit "${OPTS[@]}"
98+
99+
codestyle_check:
100+
runs-on: ubuntu-latest
101+
needs:
102+
- diff
103+
104+
if: ${{ (github.event_name == 'pull_request' && needs.diff.outputs.belar == 'true') || github.event_name == 'push' }}
105+
106+
steps:
107+
- uses: actions/checkout@v3
108+
109+
- name: Setup python
110+
uses: actions/setup-python@v4
111+
with:
112+
python-version: "3.10.6"
113+
architecture: x64
114+
115+
- name: Get pip cache dir
116+
id: cache-dir
117+
run: |
118+
echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
119+
120+
- name: Fetch origin
121+
run: git fetch origin "$GITHUB_BASE_REF"
122+
123+
- name: Setup node
124+
uses: actions/setup-node@v3
125+
with:
126+
node-version: "17"
127+
128+
- name: Cache pip dependencies
129+
uses: actions/cache@v3
130+
id: cache-pip
131+
with:
132+
path: ${{ steps.cache-dir.outputs.dir }}
133+
key: codestyle-${{ hashFiles('requirements/dev.txt') }}
134+
135+
- name: Install dependencies
136+
run: |
137+
pip install .
138+
pip install -r requirements/dev.txt
139+
140+
- name: Format check
141+
run: |
142+
make format
143+
- name: Lint check
144+
run: make lint
145+
- name: Type check
146+
if: ${{ github.event_name == 'pull_request' }}
147+
run: git diff --name-only --diff-filter=AM "origin/$GITHUB_BASE_REF" -z -- '**/*.py' '**/*.pyi' | xargs -0 --no-run-if-empty pyright

Makefile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
GIT_ROOT ?= $(shell git rev-parse --show-toplevel)
2+
3+
help: ## Show all Makefile targets
4+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[33m%-30s\033[0m %s\n", $$1, $$2}'
5+
6+
.PHONY: format lint type style clean run-benchmarks
7+
format: ## Running code formatter: black and isort
8+
@echo "(black) Formatting codebase..."
9+
@black --config pyproject.toml belar tests examples
10+
@echo "(black) Formatting stubs..."
11+
@find belar -name "*.pyi" ! -name "*_pb2*" -exec black --pyi --config pyproject.toml {} \;
12+
@echo "(isort) Reordering imports..."
13+
@isort .
14+
@echo "(ruff) Running fix only..."
15+
@ruff check belar examples tests --fix-only
16+
lint: ## Running lint checker: ruff
17+
@echo "(ruff) Linting development project..."
18+
@ruff check belar examples tests
19+
type: ## Running type checker: pyright
20+
@echo "(pyright) Typechecking codebase..."
21+
@pyright -p belar
22+
clean: ## Clean all generated files
23+
@echo "Cleaning all generated files..."
24+
@cd $(GIT_ROOT)/docs && make clean
25+
@cd $(GIT_ROOT) || exit 1
26+
@find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete
27+
run-ci: format lint type ## Running all CI checks
28+
run-benchmarks: ## Run benchmarks
29+
@echo "Running benchmarks..."
30+
@cd $(GIT_ROOT)/tests/benchmarks && python benchmark.py

belar/metrics/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
from belar.metrics.base import Evaluation, Metric
22
from belar.metrics.factual import EntailmentScore
33
from belar.metrics.similarity import SBERTScore
4-
from belar.metrics.simple import *
4+
from belar.metrics.simple import (BLUE, EditDistance, EditRatio, Rouge1,
5+
Rouge2, RougeL)
6+
7+
__all__ = [
8+
"Evaluation",
9+
"Metric",
10+
"EntailmentScore",
11+
"SBERTScore",
12+
"BLUE",
13+
"EditDistance",
14+
"EditRatio",
15+
"RougeL",
16+
"Rouge1",
17+
"Rouge2",
18+
]

belar/metrics/base.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import typing as t
44
from abc import ABC, abstractmethod
5-
from collections import namedtuple
65
from dataclasses import dataclass
76

87
import numpy as np
@@ -13,16 +12,18 @@
1312
class Metric(ABC):
1413
@property
1514
@abstractmethod
16-
def name(self) -> str:
15+
def name(self: t.Self) -> str:
1716
...
1817

1918
@property
2019
@abstractmethod
21-
def is_batchable(self) -> bool:
20+
def is_batchable(self: t.Self) -> bool:
2221
...
2322

2423
@abstractmethod
25-
def score(self, ground_truth: list[str], generated_text: list[str]) -> list[float]:
24+
def score(
25+
self: t.Self, ground_truth: list[str], generated_text: list[str]
26+
) -> list[float]:
2627
...
2728

2829

@@ -68,7 +69,11 @@ def _get_score(self, row: dict[str, list[t.Any]] | dict[str, t.Any]):
6869
else: # not batched
6970
split_indices = len(row["ground_truth"])
7071
ground_truths = row["ground_truth"]
71-
generated_texts = [row["generated_text"]] * split_indices
72+
generated_text = row["generated_text"]
73+
assert isinstance(
74+
generated_text, str
75+
), f"generated_text should be str but got {type(generated_text)}"
76+
generated_texts = [generated_text] * split_indices
7277
scores = metric.score(ground_truths, generated_texts)
7378
score = np.max(scores)
7479

belar/metrics/factual.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
from belar.metrics import Metric
99
from belar.utils import device_check
1010

11+
if t.TYPE_CHECKING:
12+
from torch import device as Device
13+
1114

1215
@dataclass
1316
class EntailmentScore(Metric):
@@ -18,7 +21,7 @@ class EntailmentScore(Metric):
1821
model_name: str = "typeform/distilbert-base-uncased-mnli"
1922
max_length: int = 512
2023
batch_size: int = 4
21-
device: t.Literal["cpu", "cuda"] = "cpu"
24+
device: t.Literal["cpu", "cuda"] | Device = "cpu"
2225

2326
def __post_init__(self):
2427
self.device = device_check(self.device)

belar/metrics/similarity.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
from belar.metrics.base import Metric
1111

12+
if t.TYPE_CHECKING:
13+
from torch import Tensor
14+
1215
SBERT_METRIC = t.Literal["cosine", "euclidean"]
1316

1417

@@ -42,6 +45,10 @@ def score(
4245
gentext_emb = self.model.encode(
4346
generated_text, batch_size=self.batch_size, convert_to_numpy=True
4447
)
48+
assert isinstance(gentext_emb, Tensor) and isinstance(gndtruth_emb, Tensor), (
49+
f"Both gndtruth_emb[{type(gentext_emb)}], gentext_emb[{type(gentext_emb)}]"
50+
" should be Tensor."
51+
)
4552

4653
if self.similarity_metric == "cosine":
4754
score = np.dot(gndtruth_emb, gentext_emb.T) / (

belar/metrics/simple.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515

1616
@dataclass
17-
class BLEU(Metric):
17+
class BLEUScore(Metric):
1818
weights: list[float] = field(default_factory=lambda: [0.25, 0.25, 0.25, 0.25])
1919
smoothing_function = None
2020

@@ -94,8 +94,6 @@ def score(self, ground_truth: t.List[str], generated_text: t.List[str]):
9494
Rouge1 = ROUGE("rouge1")
9595
Rouge2 = ROUGE("rouge2")
9696
RougeL = ROUGE("rougeL")
97-
BLUE = BLEU()
97+
BLUE = BLEUScore()
9898
EditDistance = EditScore("distance")
9999
EditRatio = EditScore("ratio")
100-
101-
__all__ = ["Rouge1", "Rouge2", "RougeL", "BLEU", "EditDistance", "EditRatio"]

belar/utils.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
import torch
21
import typing as t
32
from warnings import warn
43

4+
import torch
5+
6+
if t.TYPE_CHECKING:
7+
from torch import device as Device
8+
59
DEVICES = ["cpu", "cuda"]
610

711

8-
def device_check(device: t.Literal[DEVICES]):
9-
if device == "cuda":
10-
if torch.cuda.is_available():
11-
device = torch.device("cuda")
12-
else:
13-
warn("cuda not available, using cpu")
14-
elif device == "cpu":
15-
device = torch.device("cpu")
16-
else:
12+
def device_check(device: t.Literal["cpu", "cuda"] | Device) -> torch.device:
13+
if isinstance(device, Device):
14+
return device
15+
if device not in DEVICES:
1716
raise ValueError(f"Invalid device {device}")
17+
if device == "cuda" and not torch.cuda.is_available():
18+
warn("cuda not available, using cpu")
19+
device = "cpu"
1820

19-
return device
21+
return torch.device(device)

0 commit comments

Comments
 (0)