silnik basal-rs
Przyjęcie żądania
Serwer HTTP (biblioteki axum i tokio w Rust) przyjmuje żądanie POST na /v1/systemone albo /v1/basal. Pole model w żądaniu wskazuje model, a serwer przekazuje żądanie do wątku tego modelu.
- gdy w toku jest ponad 1024 żądania (limit max_inflight), serwer odpowiada kodem 529 z nagłówkiem Retry-After: 1, czyli prośbą o ponowienie za sekundę
- każdy model ma własny wątek, kolejkę i tor długich żądań
- żądania, na które klient przestał czekać, są pomijane
silnik basal-rsprotokół Basal · Remek Kinas
Walidacja i plan
Wątek modelu sprawdza żądanie, dzieli tekst na tokeny i buduje prompty według kontraktu Basala: dla każdego pytania dwa prompty z opcjami w dwóch porządkach. Błędne żądanie wraca do klienta od razu, zanim trafi do kolejki.
- szablon promptu, reguła rozpoznawania języka i litery opcji takie same jak w upstream (prompt.py)
- koszt żądania to liczba tokenów do policzenia, gdy wspólne prefiksy liczą się raz
- silnik nie załaduje modelu, który ma inny kontrakt promptu
silnik basal-rs
Kolejka HRRN
Kolejka działa według reguły HRRN (Highest Response Ratio Next). Wybiera żądanie o najwyższym stosunku (czekanie + szacowany czas) / szacowany czas. Krótkie żądania wyprzedzają więc długie, a długie przesuwają się w górę kolejki, im dłużej czekają.
- żądania powyżej 4096 tokenów idą do osobnego toru długich żądań
- ustawienie schedule: fifo przywraca obsługę w kolejności przyjścia
silnik basal-rs
Partia i drzewo prefiksów
Partia to grupa żądań liczonych razem na GPU: pierwsze żądanie z kolejki i każde następne, które mieści się w 8192 tokenach. Prompty wszystkich żądań partii trafiają do wierszy. Każdy wiersz to drzewo, w którym wspólny początek promptów (prefiks) liczy się raz.
- wiersz ma do 32 768 tokenów, jeden przebieg modelu (forward) do 8192
- głębokość drzewa do 8 bloków
- identyczne prompty dzielą jeden odczyt wyniku
silnik basal-rs
Przebieg modelu na GPU
Model Llama liczy w precyzji f16 tokeny całej partii ułożone w jedną zwartą listę, bez paddingu, czyli bez pustych tokenów wyrównujących długość promptów. W ostatniej warstwie blok MLP i logity (surowe wyniki przed zamianą na prawdopodobieństwa) liczą się tylko dla pozycji odczytu i liter odpowiedzi.
- mnożenie macierzy (GEMM): cuBLASLt z tabelą algorytmów na CUDA albo kod przeniesiony z biblioteki MLX na Metal
- attention w f32 po blokach drzewa, na własnych kernelach CUDA i Metal
- sumy pośrednie w mnożeniu macierzy (akumulacja) w f32
protokół Basal · Remek Kinassilnik basal-rs
Decyzja i odpowiedź
Z logitów liter powstają prawdopodobieństwa dla obu porządków. Po przywróceniu kolejności opcji z żądania oba rozkłady są uśredniane i kalibrowane temperaturą przypisaną do typu pytania w CALIBRATION.json. Serwer składa odpowiedź w formacie API TypeSafe System One, z pewnością (confidence) liczoną wzorami TypeSafe.
- nagłówki odpowiedzi x-basal-queue-ms (czas w kolejce), x-basal-compute-ms (czas obliczeń) i x-basal-batch-requests (liczba żądań w partii)
- prawdopodobieństwa w pełnej precyzji (TypeSafe zaokrągla je do dwóch miejsc po przecinku)