Local GGUF model serving engine (pure-Rust Candle) — chat-template generation, token streaming, session cache, and an OpenAI-compatible API with a Python binding.
Project description
rust_ai_serving_engine
Rust 기반 로컬 AI 모델 서빙 엔진
Hugging Face 에서 내려받은 GGUF 모델을
등록 → 무결성 검증 → 로드 → 추론 → OpenAI 호환 API 서빙으로 흘려보낸다. Ollama · llama.cpp · LM Studio 가 제공하는 로컬 모델 실행 경험을 순수 Rust 단일 바이너리 + Python 한 줄 import 로 구현한다.
이 문서는 엔진의 완결된 개발자 매뉴얼이다. 설계 원칙, 공개 API, 지원 모델, 채팅 템플릿과 생성 제어, HTTP/CLI/Python 사용법, 서비스 통합, 새 아키텍처 추가법, 빌드·테스트 절차를 담는다.
[주요 참고 논문]
- Attention Is All You Need (Transformer 구조의 원전) - https://arxiv.org/abs/1706.03762
- LLaMA: Open and Efficient Foundation Language Models (Llama 계열 디코더 구조) - https://arxiv.org/abs/2302.13971
- Efficiently Scaling Transformer Inference (추론을 프리필·디코드 국면으로 분해하고 연산량·메모리 대역폭 상한으로 성능을 분석하는 모델의 원전. RASE_PROFILE 구간 프로파일링의 이론 근거) - https://arxiv.org/abs/2211.05102
- The Case for 4-bit Precision: k-bit Inference Scaling Laws (4비트 양자화 추론의 근거) - https://arxiv.org/abs/2212.09720
- Efficient Memory Management for Large Language Model Serving with PagedAttention (LLM 서빙과 KV 캐시 관리) - https://arxiv.org/abs/2309.06180
목차
- 핵심 특징
- 빠른 시작
- 설치와 Cargo Feature
- 아키텍처
- 모델 매니페스트와 레지스트리
- 공개 API 레퍼런스
- 지원 모델
- 채팅 템플릿과 생성 제어
- HTTP API (OpenAI 호환)
- CLI 도구
- Python 바인딩 (PyO3)
- 서비스 파이프라인에 붙이기
- 새 모델 아키텍처 추가하기
- 빌드 · Feature 조합 · 테스트
- 디렉토리 구조
- 라이선스와 모델 책임
1. 핵심 특징
로컬 대규모 언어모델(LLM) 실행에서 과소평가되는 영역이 모델 수명주기와 서빙 계약이다. 추론 커널이 아무리 좋아도 "어떤 파일이 실행 가능한 모델인지, 지금 메모리에 무엇이 올라 있는지, 같은 입력이 같은 출력을 내는지"가 관리되지 않으면 로컬 AI 는 재현 불가능한 장난감이 된다. 이 엔진은 추론 커널을 새로 만드는 대신, 그 위아래의 시스템 엔지니어링을 책임지는 런타임 틀을 지향한다.
| 원칙 | 의미 |
|---|---|
| 커널은 만들지 않고 조립한다 | 텐서 연산·모델 구현은 Hugging Face 의 Rust 프레임워크 Candle 을 쓴다. 엔진의 차별점은 모델 수명주기(등록·검증·로드·캐시·언로드)와 서빙 계약이다. 단 CPU 프리필은 예외적으로 자체 하이브리드 커널 경로를 갖는다(아래). |
| 하이브리드 프리필 + GQA 디코드 | Candle 의 양자화 행렬곱은 프롬프트 토큰마다 가중치 전체를 다시 역양자화해 프리필이 디코드만큼 느리다. 엔진은 긴 프롬프트에서 레이어별 가중치를 1회만 역양자화해 f32 행렬곱(GEMM)으로 처리하고, 디코드는 메모리 최적인 양자화 커널을 유지한다. 긴 프롬프트의 어텐션도 자체 블록형 커널(질의 16행이 K/V 읽기를 공유, online softmax 정확 계산)로 처리하며 — 16코어 AVX2 노트북 실측 기준 1,500자 문서 컨텍스트의 첫 토큰이 141초에서 20초로, 4,000자는 97초에서 48초로 단축 — 디코드 어텐션도 KV 공유비가 큰 GQA 모델(쿼리:KV 가 2:1 초과, 예: Qwen3-4B 의 32:8)에서 KV 를 1회만 읽고 그룹의 쿼리 헤드 전체를 갱신하는 자체 커널로 중복 읽기를 제거한다. |
| 매니페스트가 곧 계약 | 모델 파일은 SHA-256 해시·아키텍처·토크나이저·채팅 템플릿이 기록된 TOML 매니페스트로만 실행된다. "실행 가능한 모델"과 "그냥 큰 파일"을 구분한다. |
| 결정적 생성 | 같은 모델·프롬프트·시드·샘플링 설정이면 같은 출력. 고정 시드 샘플러와 결정적 생성 루프로 회귀 시험이 가능하다. |
| 한 번 로드, 계속 재사용 | 프로세스 전역 세션 캐시가 해시 검증·모델 로드를 최초 1회만 수행한다. 요청마다 수 GB 를 다시 읽지 않는다. |
| 순수 Rust / zero 외부 런타임 | C++ llama.cpp 래퍼가 아니다. Python·Node.js·외부 프로세스 없이 단일 바이너리로 동작하고, Python 은 PyO3 확장 모듈로 붙는다. |
Ollama 와 무엇이 같고 무엇이 다른가
사용자 경험의 목표는 같다 — 모델을 받아서, 등록하고, 로컬에서 대화한다. 구현 철학이 다르다.
- Ollama 는 llama.cpp(C++)를 감싼 Go 서버다. 이 엔진은 전 계층이 Rust 라서 하나의 Cargo 워크스페이스에서 타입 안전하게 조립되고, 라이브러리·CLI·Python 확장이 같은 코어를 공유한다.
- 모델 관리가 암묵적 캐시가 아니라 명시적 매니페스트다. 가중치·토크나이저의 해시가 기록되고, 로드 전 무결성이 검증되며, 파일이 바뀌면 캐시가 자동 무효화된다.
- 임베드가 1급 시나리오다. 서버를 따로 켜지 않아도 Rust crate 또는 Python 모듈로 호스트 서비스 프로세스 안에서 직접 추론할 수 있다.
2. 빠른 시작
세 표면(CLI 서버, Python, Rust) 모두 같은 흐름이다 — 모델을 받아 등록하고, 토크나이저를 붙이고, 생성한다.
CLI — 모델 받기부터 OpenAI 호환 서버까지
cargo build --release
# 1) Hugging Face 에서 가중치 + tokenizer.json 을 받아 실행 가능한 번들로 등록
./target/release/rust-ai-serving-engine model pull \
--repo unsloth/Qwen3-4B-Instruct-2507-GGUF \
--file Qwen3-4B-Instruct-2507-Q4_K_M.gguf \
--id qwen3-4b \
--architecture qwen3 \
--tokenizer-repo Qwen/Qwen3-4B-Instruct-2507 \
--tokenizer-file tokenizer.json
# 2) OpenAI 호환 서버 기동
./target/release/rust-ai-serving-engine serve --port 8080
# 3) 어떤 OpenAI 클라이언트로든 대화 (스트리밍은 "stream": true)
curl -s http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "qwen3-4b", "messages": [{"role": "user", "content": "안녕?"}]}'
Python
import rust_ai_serving_engine as engine
# 등록 (최초 1회) — 가중치 다운로드 + 로컬 tokenizer.json 연결
engine.pull_model("./models", "unsloth/Qwen3-4B-Instruct-2507-GGUF",
"Qwen3-4B-Instruct-2507-Q4_K_M.gguf", "qwen3-4b", architecture="qwen3")
engine.attach_tokenizer("./models", "qwen3-4b", "./tokenizer.json")
# 대화 — 채팅 템플릿·종료 토큰 자동 적용, 모델은 프로세스 캐시에 상주
answer = engine.generate_chat_registered_gguf(
"./models", "qwen3-4b",
[{"role": "user", "content": "한 문장으로 자기소개 해줘."}],
max_tokens=64,
)
print(answer)
Rust 라이브러리
use rust_ai_serving_engine_core::{DevicePreference, GenerationConfig, ModelRegistry, generate};
use rust_ai_serving_engine_models::{ChatMessage, SessionCache};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let registry = ModelRegistry::open("./models")?;
let cache = SessionCache::new();
// 최초 호출: 해시 검증 + 로드 / 이후: 메모리 상주 세션 재사용
let session = cache.get_or_load(®istry, "qwen3-4b", DevicePreference::Auto)?;
let mut session = session.lock().unwrap();
let template = session.chat_template.expect("chat template resolved from manifest");
let prompt = template.render(&[ChatMessage {
role: "user".into(),
content: "What is the capital of France?".into(),
}])?;
let prompt_tokens = session.tokenizer.encode(&prompt, false)?;
let mut config = GenerationConfig::default();
if let Some(eos) = session.eos_token {
config.stop_tokens.push(eos); // 종료 토큰에서 생성 자동 종료
}
let result = generate(session.decoder.as_mut(), &prompt_tokens, &config, || false)?;
println!("{}", session.tokenizer.decode(&result.tokens, true)?);
Ok(())
}
3. 설치와 Cargo Feature
Rust 프로젝트의 Cargo.toml:
[dependencies]
rust-ai-serving-engine-core = { git = "https://github.com/arabangoo/rust_ai_serving_engine" }
rust-ai-serving-engine-models = { git = "https://github.com/arabangoo/rust_ai_serving_engine" }
워크스페이스 크레이트 구성
| 크레이트 | 역할 | 주요 의존 |
|---|---|---|
rust_ai_serving_engine_core |
매니페스트·레지스트리·생성 루프·샘플러·장치 선택·에러 계약 | candle-core, hf-hub, sha2 |
rust_ai_serving_engine_models |
GGUF 디코더(Llama·Qwen3)·토크나이저·채팅 템플릿·세션 캐시 | candle-transformers, tokenizers |
rust_ai_serving_engine_api |
OpenAI 호환 HTTP API 와 SSE 스트리밍 | axum, tokio |
rust_ai_serving_engine_cli |
model·runtime·serve 명령줄 (바이너리명 rust-ai-serving-engine) |
clap |
rust_ai_serving_engine_python |
PyO3 확장 모듈 (모듈명 rust_ai_serving_engine) |
pyo3(abi3) |
Feature 목록
| Feature | 크레이트 | 활성화 대상 | 비고 |
|---|---|---|---|
cpu |
core, models | CPU 실행 (기본 활성) | 순수 Rust, 외부 런타임 없음 |
cuda |
core, models | NVIDIA GPU 실행 경로 | candle-core/cuda 전달 |
metal |
core, models | Apple Silicon GPU 실행 경로 | candle-core/metal 전달 |
python |
python | PyO3 cdylib 바인딩 | maturin 이 자동 활성화 |
기본(CPU) 빌드는 외부 공유 라이브러리나 subprocess 를 요구하지 않는다. 모델 파일과 바이너리 하나면 오프라인·에어갭 환경에서도 동작한다 (Hugging Face 다운로드는
model pull사용 시에만 필요).
4. 아키텍처
요청(HTTP/CLI/Python)
→ 레지스트리: 매니페스트 조회 (아키텍처·토크나이저·템플릿·해시)
→ 세션 캐시: 최초 1회만 해시 검증 + 디코더·토크나이저 로드
→ 채팅 템플릿 렌더 → 토크나이즈
→ prefill: 프롬프트 전체 평가 + KV 캐시 생성
→ decode: 토큰 샘플링 → KV 캐시 갱신 반복 (종료 토큰·stop 문자열·취소 확인)
→ 토큰 콜백 → SSE 델타 전송 또는 완성 텍스트 조립
핵심은 계약의 분리다. core 는 추론 백엔드를 포함하지 않고 매니페스트·생성 루프·트레이트만 정의한다.
models 는 그 계약의 Candle 구현체다. HTTP·CLI·Python 은 같은 models 위의 세 가지 표면일 뿐이다.
- 등록 —
ModelRegistry가 가중치를 해시하고 TOML 매니페스트를 원자적으로 기록한다. - 로드 —
SessionCache가 매니페스트 해시가 같으면 메모리 상주 세션을 재사용하고, 다르면 검증 후 재로드한다. - 생성 —
generate/generate_with가 아키텍처 중립 디코드 루프를 돌린다. KV(Key-Value) 캐시는 디코더가 소유한다. - 직렬화 — 같은 모델에 대한 동시 요청은 세션 뮤텍스로 직렬화된다 (KV 캐시는 공유 불가). 서로 다른 모델은 동시 생성된다.
5. 모델 매니페스트와 레지스트리
모델 저장소(store)는 폴더 하나다. manifests/ 아래 모델당 TOML 파일 하나가 기록된다.
id = "qwen3-4b"
kind = "generator" # generator | embedding
format = "gguf" # gguf | safetensors
weights = "<가중치 파일 절대 경로>"
sha256 = "<가중치 SHA-256>"
tokenizer = "<tokenizer.json 절대 경로>"
tokenizer_sha256 = "<토크나이저 SHA-256>"
architecture = "qwen3"
context_length = 262144
chat_template = "chatml" # chatml | llama3 | mistral (생략 시 아키텍처 기본값)
매니페스트는 실행 가능한 모델과 단순 파일을 구분하는 계약이다:
- 무결성 —
verify는 가중치·토크나이저의 SHA-256 을 재계산해 매니페스트와 대조한다. 세션 캐시도 로드 시점에 같은 검증을 수행하고, 해시가 달라지면 캐시를 버리고 재로드한다. - 실행 가능 조건 — 생성에는
format = "gguf"+architecture+tokenizer세 가지가 필요하다. 하나라도 없으면 로드 단계에서 무엇이 빠졌는지 명시하고 거절한다. - 가중치 파일 위치 —
model pull은 Hugging Face 캐시에 내려받은 파일을 매니페스트로 가리킨다. 캐시를 정리하면 모델을 다시 받아야 한다. 장기 보관할 모델은 원하는 폴더로 옮긴 뒤model import로 등록한다.
6. 공개 API 레퍼런스
6.1 ModelRegistry — core
ModelRegistry::open(root) -> Result<Self> // store 폴더 열기(없으면 생성)
fn import_local(&self, id, weights, kind: ModelKind,
architecture: Option<String>, context_length: Option<u32>,
chat_template: Option<String>) -> Result<ImportedModel>
fn attach_tokenizer(&self, id, tokenizer_path) -> Result<ModelManifest>
fn get(&self, id) -> Result<ModelManifest> // 매니페스트 조회 (해시 재계산 없음)
fn list(&self) -> Result<Vec<ModelManifest>> // id 정렬 목록
fn verify(&self, id) -> Result<ModelManifest> // 가중치·토크나이저 해시 재검증
HuggingFaceHub::download(repo, file) -> Result<PathBuf> 가 Hugging Face Hub 공개 파일을
관리 캐시에 내려받는다 (core, hf-hub 기반).
6.2 생성 계약 — core
/// 로드된 모델이 구현하는 아키텍처 중립 디코더 계약.
pub trait TokenDecoder: Send {
fn prefill(&mut self, prompt: &[u32]) -> Result<Vec<f32>>; // KV 캐시 초기화 + 첫 logits
fn decode(&mut self, token: u32) -> Result<Vec<f32>>; // 토큰 1개 평가 → 다음 logits
fn eos_token(&self) -> Option<u32> { None } // 모델 파일이 선언한 종료 토큰
}
pub struct GenerationConfig {
pub max_tokens: usize, // 기본 256
pub temperature: f32, // 기본 0.7 (0.0 = 탐욕적 선택)
pub top_k: Option<usize>, // 기본 Some(40)
pub seed: u64, // 기본 0 — 같은 시드는 같은 출력
pub stop_tokens: Vec<u32>, // 이 토큰이 나오면 즉시 종료
}
// 완성형 생성 — 취소 콜백이 true 를 반환하면 중단
generate(decoder, prompt, &config, cancelled) -> Result<GenerationResult>
// 스트리밍용 — 토큰마다 on_token 이 불리고, false 반환 시 디코딩 중단
generate_with(decoder, prompt, &config, cancelled, on_token) -> Result<GenerationResult>
pub struct GenerationResult { pub tokens: Vec<u32>, pub stop_reason: GenerationStopReason }
pub enum GenerationStopReason { MaxTokens, StopToken, Cancelled }
6.3 ModelSession · SessionCache — models
/// 로드된 모델 1개 — 디코더 + 토크나이저 + 종료 토큰 + 채팅 템플릿.
pub struct ModelSession {
pub decoder: Box<dyn TokenDecoder>,
pub tokenizer: LocalTokenizer,
pub eos_token: Option<u32>,
pub chat_template: Option<ChatTemplate>,
}
ModelSession::load(&manifest, &runtime) -> Result<Self>
/// 프로세스 전역 세션 캐시. 키 = 모델 id + 장치.
SessionCache::new() -> Self
fn get_or_load(&self, registry, id, device: DevicePreference)
-> Result<Arc<Mutex<ModelSession>>> // 해시 불변이면 재사용, 변경이면 검증 후 재로드
fn clear(&self) // 전체 언로드 (메모리 해제)
6.4 디코더·토크나이저 — models
// 등록된 아키텍처 이름으로 GGUF 디코더 선택
load_gguf_decoder(architecture, weights, &runtime) -> Result<Box<dyn TokenDecoder>>
LlamaGgufDecoder::load(path, &runtime) -> Result<Self> // Llama·Mistral 호환 GGUF
Qwen3GgufDecoder::load(path, &runtime) -> Result<Self> // Qwen3 호환 GGUF
LocalTokenizer::from_file(path) -> Result<Self> // Hugging Face tokenizer.json
fn encode(&self, text, add_special_tokens: bool) -> Result<Vec<u32>>
fn decode(&self, tokens, skip_special_tokens: bool) -> Result<String>
6.5 장치 선택 — core
pub enum DevicePreference { Auto, Cpu, Cuda, Metal } // Auto = CUDA → Metal → CPU 폴백
RuntimeDevice::select(preference) -> Result<RuntimeDevice>
fn smoke_test(&self) -> Result<()> // 실제 텐서 연산으로 백엔드 동작 확인
fn is_accelerated(&self) -> bool
6.6 에러 타입 — core
pub enum EngineError {
InvalidModelId(String), UnsupportedFormat(String), UnsupportedArchitecture(String),
ModelNotFound(String), ModelFileNotFound(String),
IntegrityMismatch { id, expected, actual },
BackendUnavailable(String), Candle(String), Tokenizer(String), HuggingFaceHub(String),
InvalidGenerationConfig(String), InvalidLogits,
Io(std::io::Error), TomlSerialize(..), TomlDeserialize(..),
}
로드 실패는 원인별로 구분된다 — 손상 파일·해시 불일치·미지원 아키텍처·백엔드 부재가 각각 다른 에러로 보고되므로, 호출자는 "왜 안 되는지"를 사용자에게 그대로 전달할 수 있다.
7. 지원 모델
실행 형식은 GGUF 양자화 모델이고, 아키텍처 이름이 디코더를 고른다.
아키텍처 (--architecture) |
디코더 | 대표 모델 |
|---|---|---|
qwen3 |
Qwen3GgufDecoder (Candle quantized_qwen3) |
Qwen3-1.7B · Qwen3-4B-Instruct-2507 — 실모델로 채팅 완성·SSE 스트리밍·한국어 다중 바이트·세션 캐시 재사용까지 종단 간 검증. 디코드 실측(16코어 하이브리드 CPU 노트북, 짧은 컨텍스트 기준): 1.7B q4 약 초당 40-46토큰, 4B q4 약 초당 20토큰 (8장 디코드 스레드 정책 적용 시) |
llama llama2 llama3 mistral mixtral |
LlamaGgufDecoder (Candle quantized_llama) |
Llama 2·3, Mistral, Mixtral instruct 계열 |
동작 메모:
- 지원 밖 아키텍처는 잘못된 출력 대신 명확한 에러로 거절한다.
qwen2는 Qwen3 GGUF 사용을 안내하는 에러를,phi는 지원 제외 사유를 담은 에러를 반환한다. Safetensors 는 레지스트리 등록·해시 검증 대상이고 실행은 GGUF 로 한다. - Qwen3 하이브리드(thinking) 모델은
/no_think로 다룬다. Qwen3 기본판(0.6B·1.7B 등)은 답변 전<think>추론 블록을 생성하는 하이브리드 모델이다. CPU 서빙에서는 시스템 프롬프트에 Qwen 공식 소프트 스위치/no_think를 붙여 추론을 끄는 것이 정석이며, 이 방식으로 Qwen3-1.7B 실서비스 운용을 검증했다. 단 엔진의 ChatML 템플릿은<think>블록을 걸러주지 않으므로 잔여 태그 처리는 호출측 몫이다 —/no_think상태에서도 빈 블록(<think></think>), 여는 태그 없는</think>, 닫는 태그 중복 같은 변종이 스트림 선두에 관찰되므로 호출측 필터가 필요하다. thinking 이 제거된 instruct 변형(Qwen3-4B-Instruct-2507 등)은 이런 처리 없이 ChatML 템플릿 그대로 동작한다 (검증 완료). - 생성 중 같은 모델은 직렬화된다. KV 캐시가 요청 간 공유될 수 없어 세션 뮤텍스로 순차 처리한다. 서로 다른 모델은 동시 생성된다. 다중 사용자 대규모 배치는 이 엔진의 비목표다 (vLLM 의 영역).
- 토크나이저는 외부
tokenizer.json을 쓴다. 양자화 GGUF 저장소에 tokenizer.json 이 없으면 원본 모델 저장소에서 받아 붙인다 (model pull --tokenizer-repo가 이를 한 번에 처리).
8. 채팅 템플릿과 생성 제어
instruct 모델은 학습 때 쓰인 대화 마크업을 그대로 재현해야 정상 동작한다. 엔진은 대화 메시지 목록을 모델별 마크업으로 렌더하고, 종료 토큰에서 생성을 자동으로 끝낸다.
템플릿 선택 규칙
- 매니페스트의
chat_template값(chatml|llama3|mistral)이 있으면 그것을 쓴다. - 없으면 아키텍처 기본값 —
qwen3→ ChatML,llama3→ Llama3,llama·llama2·mistral·mixtral→ Mistral[INST]. - 어느 쪽도 없으면 채팅 요청을 거절한다 (완성 API 는 템플릿 없이 동작).
| 템플릿 | 마크업 | 대상 |
|---|---|---|
chatml |
`< | im_start |
llama3 |
`< | start_header_id |
mistral |
<s>[INST] ... [/INST] (system 은 다음 user 턴에 병합) |
Mistral·Llama 2 instruct |
템플릿이 특수 토큰을 직접 표기하므로, 채팅 프롬프트는 토크나이저의 자동 특수 토큰 없이 인코딩된다.
종료 토큰(EOS) 자동 종료
GGUF 메타데이터의 tokenizer.ggml.eos_token_id 를 로드 시 읽어 두고, HTTP·Python 채팅 표면이
자동으로 stop 토큰에 추가한다. 사용자가 토큰 id 를 알 필요가 없다.
stop 문자열
OpenAI 호환 stop(문자열 하나 또는 배열)을 지원한다. 생성 텍스트에 stop 문자열이 나타나면
그 직전까지만 반환하고 종료한다. 스트리밍에서는 stop 문자열 길이만큼 텍스트를 홀드백(hold-back)해서
청크 경계에 걸친 stop 문자열도 클라이언트로 새어 나가지 않는다.
샘플링
temperature = 0.0— 결정적 탐욕 선택 (회귀 시험용)temperature > 0+top_k— 고정 시드(seed) 기반 확률 샘플링. 같은 시드는 같은 출력- 다중 바이트 문자(한글 등)가 토큰 경계에 걸치면 완성될 때까지 방출을 보류한다 — 깨진 문자가 스트림에 나가지 않는다
wgpu 프리필 GEMM 오프로드 (실험적, RASE_GPU=1 옵트인)
프리필의 양자화 선형층(Q4_K)을 GPU 로 오프로드하는 실험 경로다. 양자화 가중치를 행렬 단위로 GPU 에 상주시키고 WGSL 셰이더 안에서 역양자화하며 곱한다 (역양자화된 f32 를 매 호출 복사하면 수 GB 라 손해이기 때문). 디코드는 메모리 대역폭 바운드라 내장 GPU 이득이 없어 항상 CPU 에 남는다.
- 켜는 법:
RASE_GPU=1(기본 비활성). 현재 f32 셰이더는 통합 GPU 에서 CPU 하이브리드 GEMM 과 비슷한 속도라 기본값이 꺼져 있다. f16 셰이더 경로가 CPU 를 상회하면 기본 활성으로 바뀔 예정이다 - 안전장치: 소프트웨어 어댑터(WARP·llvmpipe 류, DeviceType Cpu)는 자동 제외 / 런타임 실패(디바이스 유실·매핑 실패) 시 그 즉시 전 과정이 CPU 경로로 복귀 / Q4_K 외 dtype(Q6_K 등)과 GPU 부재 환경은 행렬 단위로 CPU 폴백
- 진단: Python
gpu_info()가active: <어댑터>/fallback(runtime-failure)/inactive를 반환한다. 프로파일링 카운터gemm_gpu_ns·gemm_gpu_calls로 오프로드 몫을 계측한다 (위 성능 프로파일링 절) - 수치 특성: GPU 역양자화 GEMM 은 연산 순서 차이로 CPU 와 비트 동일하지 않지만, 로짓 허용오차 내에서 일치한다 (같은 시드 greedy 디코드 출력 일치를 실측 확인)
디코드 스레드 정책 (CANDLE_NUM_THREADS)
디코드(토큰 생성)의 양자화 matvec 과 융합 어텐션은 CANDLE_NUM_THREADS 로 크기가 정해지는
배리어 풀에서 정적 균등 분할로 돈다. 하이브리드 CPU(성능 코어 + 효율 코어 + 저전력 효율 코어
혼합)에서는 매 배리어가 가장 느린 코어를 기다리게 되어, 코어를 전부 쓰는 기본값이 오히려
디코드 처리량을 반토막 낸다 (16코어 Core Ultra 7 255H 실측: Qwen3-4B 디코드가 16스레드
10 tok/s, 12스레드 20 tok/s. 절벽은 저전력 코어가 풀에 들어오는 지점에서 생긴다).
엔진은 첫 모델 로드 시 다음 기본값을 적용한다:
CANDLE_NUM_THREADS가 이미 설정돼 있으면 그대로 존중한다 (기본값 미적용)- 미설정이고 물리 코어가 12개 이상이면
물리 코어 - 4로 설정한다. 디코드는 메모리 대역폭 바운드라 코어 수 이하에서 포화하므로, 균질 다코어 CPU 에서 이 상한의 손실은 작고 하이브리드 CPU 에서는 낙오자 페널티가 사라진다 - 물리 코어 12개 미만이면 손대지 않는다
프리필 경로(하이브리드 GEMM 의 f32 행렬곱, 블록형 어텐션)는 별도 rayon 풀
(RAYON_NUM_THREADS, 기본 = 전체 물리 코어)을 쓰므로 이 정책의 영향을 받지 않는다.
스레드 수는 작업 분할만 바꾸고 출력 원소별 계산 주체는 동일하므로, 같은 시드의 출력은
스레드 수와 무관하게 같다.
9. HTTP API (OpenAI 호환)
serve 명령으로 기동한다. 기본 바인딩은 127.0.0.1:8080 (로컬 전용 — 외부 노출은 사용자 책임).
| 경로 | 메서드 | 역할 |
|---|---|---|
/health |
GET | 프로세스 생존 확인 |
/v1/models |
GET | 등록 모델 목록 (OpenAI list 형식) |
/v1/models/{id} |
GET | 모델 존재 확인 |
/v1/completions |
POST | 프롬프트 완성 (비스트리밍) |
/v1/chat/completions |
POST | 채팅 완성 — stream: true 면 SSE 토큰 스트리밍 |
채팅 완성
curl -s http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-4b",
"messages": [
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "What is the capital of France?"}
],
"max_tokens": 64,
"temperature": 0.0,
"stop": ["\n\n"]
}'
응답은 OpenAI chat.completion 형식이다 — choices[0].message.content, finish_reason(stop | length), usage 토큰 집계.
SSE 스트리밍
"stream": true 를 주면 text/event-stream 으로 OpenAI chat.completion.chunk 를 흘려보낸다.
첫 청크는 role, 이후 청크는 delta.content, 마지막 청크는 finish_reason, 종료 표시는 data: [DONE].
클라이언트가 연결을 끊으면 서버는 다음 토큰 경계에서 디코딩을 중단한다 (낭비 계산 없음).
curl -sN http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "qwen3-4b", "messages": [{"role": "user", "content": "Count to 5."}], "stream": true}'
요청 파라미터
| 파라미터 | 타입 | 기본값 | 비고 |
|---|---|---|---|
model |
string | 필수 | 등록된 모델 id |
messages / prompt |
array / string | 필수 | 채팅 / 완성 |
max_tokens |
int | 256 | |
temperature |
float | 0.7 | 0.0 = 결정적 |
top_k |
int | 40 | OpenAI 표준 외 확장 |
seed |
int | 0 | 고정 시드 재현 |
stop |
string 또는 array | 없음 | stop 문자열. 종료 토큰은 별도로 항상 적용 |
stream |
bool | false | 채팅 완성만 지원 |
10. CLI 도구
바이너리명은 rust-ai-serving-engine, 모델 저장소는 --store <폴더> (기본 .rust_ai_serving_engine).
| 명령 | 인자 | 동작 |
|---|---|---|
model import |
<path> --id [--kind] [--architecture] [--context-length] [--chat-template] |
로컬 GGUF·Safetensors 를 해시와 함께 등록 |
model pull |
--repo --file --id [--architecture] [--chat-template] [--tokenizer-repo --tokenizer-file] |
Hugging Face 에서 받아 등록. 토크나이저 옵션을 주면 다운로드 후 자동 연결 |
model attach-tokenizer |
<id> --tokenizer <path> |
등록 모델에 로컬 tokenizer.json 연결 |
model list |
등록 모델 목록 | |
model inspect |
<id> |
매니페스트 TOML 출력 |
model verify |
<id> |
가중치·토크나이저 해시 재검증 |
runtime probe |
[--device auto|cpu|cuda|metal] |
장치 선택 + 실제 텐서 연산 스모크 테스트 |
serve |
[--host] [--port] [--device] |
OpenAI 호환 API 서버 기동 |
# 로컬 파일 등록 후 무결성 검증
rust-ai-serving-engine model import ./my-model.gguf --id my-model --architecture llama3
rust-ai-serving-engine model verify my-model
# 장치 확인
rust-ai-serving-engine runtime probe --device auto
11. Python 바인딩 (PyO3)
abi3(stable ABI) 로 빌드되어 Python 3.9 이상 단일 휠로 호환된다. 모듈명은 rust_ai_serving_engine.
설치
# PyPI 게시 후 — Rust 툴체인 불필요
pip install rust_ai_serving_engine
# 소스에서(최신 main / 게시 전) — 설치 머신에 Rust 툴체인 필요
pip install "git+https://github.com/arabangoo/rust_ai_serving_engine"
API
import rust_ai_serving_engine as engine
engine.__version__ # 예: "0.1.3"
engine.probe_runtime(device="auto") # 장치 선택 + 텐서 스모크 테스트
# 모델 수명주기 (store = 모델 저장소 폴더)
engine.pull_model(store, repo, file, id, kind="generator",
architecture=None, context_length=None, chat_template=None)
engine.import_model(store, path, id, ...) # 로컬 파일 등록 (인자 동일)
engine.attach_tokenizer(store, id, tokenizer_path)
engine.list_models(store) # ["qwen3-4b", ...]
engine.inspect_model(store, id) # 매니페스트 TOML 문자열
engine.verify_model(store, id) # 해시 재검증
engine.unload_models() # 프로세스 캐시 전체 해제
# 생성 — 등록 모델 (프로세스 캐시 상주, 종료 토큰 자동)
engine.generate_registered_gguf(store, id, prompt, max_tokens=256,
temperature=0.7, top_k=40, seed=0,
stop_tokens=[], device="auto")
# 채팅 생성 — 템플릿 자동 적용
engine.generate_chat_registered_gguf(store, id,
[{"role": "user", "content": "..."}], max_tokens=256, ...)
# 채팅 스트리밍 — 텍스트 조각마다 콜백 호출. 콜백이 False 를 반환하면 중단되고
# 그때까지의 부분 텍스트가 반환된다 (None 등 다른 반환값은 계속 진행).
engine.generate_chat_stream_registered_gguf(store, id, messages, on_delta,
max_tokens=256, ...)
# 생성 — 파일 직접 지정 (레지스트리 없이 1회성)
engine.generate_llama_gguf(weights_path, tokenizer_path, prompt, ...)
# 성능 프로파일링 — 포워드 패스 구간 카운터 (아래 "성능 프로파일링" 절)
engine.profiling_snapshot(reset=True) # JSON 문자열
# wgpu 프리필 오프로드 상태 (8장 wgpu 절) — "active: <어댑터>" | "inactive"
engine.gpu_info()
성능 프로파일링 (RASE_PROFILE)
포워드 패스의 구간별 소요 시간을 나노초 카운터로 집계하는 진단 표면이다. 프리필을 "양자화 선형 GEMM 작업"과 "어텐션 커널 작업"으로, 디코드를 "양자화 matvec"과 "융합 어텐션"으로 분해해, 커널 최적화나 GPU 오프로드의 이득 상한을 코드 작성 전에 수치로 판단할 수 있게 한다.
- 켜는 법: 프로세스 시작 전에 환경변수
RASE_PROFILE=1. 프로세스당 1회만 읽으므로 실행 중 변경은 무효다. 꺼져 있으면(기본) 타이머를 아예 잡지 않아 추론 경로 비용이 0이고, 계측은 출력에 영향을 주지 않는다 (같은 시드는 켜고 꺼도 같은 출력). - 읽는 법:
profiling_snapshot(reset=True)가 카운터 전체를 JSON 문자열로 반환한다.reset=True는 읽은 뒤 0으로 초기화하므로 연속 호출 사이가 곧 측정 창이 된다. - 적용 범위: Qwen3 GGUF 디코더의 CPU 경로. 다른 디코더·장치에서는 카운터가 0에 머문다.
| 카운터 | 의미 |
|---|---|
prefill_calls / prefill_tokens |
시퀀스 길이 2 이상 forward 호출 수 / 처리한 프롬프트 토큰 수 |
prefill_forward_ns |
프리필 forward 전체 벽시계 (임베딩부터 logits 까지) |
gemm_dequant_ns / gemm_matmul_ns |
하이브리드 GEMM 경로의 역양자화 / f32 행렬곱 시간 |
attn_blocked_ns / attn_flash_ns |
프리필 어텐션 커널 시간 (블록형 / candle flash) |
decode_steps / decode_forward_ns |
단일 토큰 forward 수 / 벽시계 (디코드 tok/s 계산용) |
decode_matvec_ns / decode_attn_ns |
디코드의 양자화 matvec / 융합 어텐션 시간 |
import json
import os
os.environ["RASE_PROFILE"] = "1" # 반드시 첫 추론 전에
import rust_ai_serving_engine as engine
engine.generate_chat_registered_gguf("./models", "qwen3-4b", [...], max_tokens=256)
p = json.loads(engine.profiling_snapshot(reset=True))
prefill = p["prefill_forward_ns"] / 1e9
gemm = (p["gemm_dequant_ns"] + p["gemm_matmul_ns"]) / 1e9
attn = (p["attn_blocked_ns"] + p["attn_flash_ns"]) / 1e9
print(f"prefill {prefill:.1f}s = GEMM {gemm:.1f}s + attention {attn:.1f}s + etc")
print(f"decode {p['decode_steps'] / (p['decode_forward_ns'] / 1e9):.1f} tok/s")
주의: decode_matvec_ns 는 시퀀스 길이 1 선형 호출 전체를 세므로, 프리필 마지막의
lm_head 호출(호출당 1회)도 포함된다. 디코드가 수백 토큰이면 오차는 1% 미만이다.
스트리밍 통합 레시피
콜백이 원시 API 다. 서버 전송 이벤트(SSE)나 제너레이터가 필요하면 스레드 + 큐로 감싼다 — 생성 루프는 GIL 을 해제한 채 돌고 콜백 호출 순간에만 GIL 을 잡으므로, 호스트 서비스와 자연스럽게 병행된다.
import queue
import threading
def stream_chat(messages):
"""토큰 조각을 순서대로 내놓는 제너레이터 (FastAPI StreamingResponse 등에 직결)."""
q: queue.Queue = queue.Queue()
done = object()
def worker():
try:
engine.generate_chat_stream_registered_gguf(
"./models", "qwen3-4b", messages,
lambda delta: q.put(delta) or True,
)
finally:
q.put(done)
threading.Thread(target=worker, daemon=True).start()
while (item := q.get()) is not done:
yield item
호스트 서비스를 멈추지 않는다 — GIL 해제
다운로드·해시 검증·모델 로드·토큰 생성 같은 장시간 작업은 모두 GIL(Global Interpreter Lock)을 해제한 채 Rust 에서 수행된다. FastAPI·Flask 같은 호스트 서비스에 임베드해도 생성 중에 다른 요청 스레드가 멈추지 않는다 (생성 중 파이썬 하트비트 스레드가 정상 동작함을 실측 확인).
캐시 동작
등록 모델 생성 함수의 최초 호출이 해시 검증 + 로드를 수행하고, 이후 호출은 메모리 상주 모델을
재사용한다. 매니페스트의 해시가 바뀌면(모델 파일 교체) 자동으로 재검증·재로드된다.
메모리를 돌려받으려면 unload_models() 를 부른다.
12. 서비스 파이프라인에 붙이기
이 엔진은 단독 앱이 아니라 로컬 추론이 필요한 자리에 박아 넣는 코어 의존성이다. 호스트 환경에 따라 아래 표면 중 하나를 고른다.
| 호스트 | 표면 | 통합 방법 |
|---|---|---|
| 기존 OpenAI 클라이언트 코드 | HTTP 서버 | base_url 만 로컬로 변경 |
| Python 서비스 (FastAPI 등) | Python 모듈 | 서버 없이 in-process 추론 |
| Rust 서비스 | crate | 레지스트리 + 세션 캐시 직접 사용 |
| 타 언어 / 배치 / 오케스트레이션 | CLI + HTTP | serve 를 사이드카로 |
12.1 OpenAI SDK — base_url 교체 한 줄
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8080/v1", api_key="unused")
out = client.chat.completions.create(
model="qwen3-4b",
messages=[{"role": "user", "content": "요약해줘: ..."}],
stream=True,
)
for chunk in out:
print(chunk.choices[0].delta.content or "", end="")
LangChain 도 같은 방식이다 — ChatOpenAI(base_url="http://127.0.0.1:8080/v1", model="qwen3-4b").
12.2 Python 서비스에 in-process 임베드
서버 프로세스를 따로 두지 않고 서비스 안에서 직접 추론한다. GIL 이 해제되므로
이벤트 루프는 run_in_executor(또는 FastAPI 의 스레드풀)로 감싸면 된다.
import asyncio
import rust_ai_serving_engine as engine
STORE = "./models"
async def answer(messages: list[dict]) -> str:
loop = asyncio.get_running_loop()
return await loop.run_in_executor(
None,
lambda: engine.generate_chat_registered_gguf(STORE, "qwen3-4b", messages, max_tokens=256),
)
12.3 Rust 서비스에 임베드
생성은 동기·CPU 바운드이므로 async 서버(axum 등)에서는 spawn_blocking 으로 감싼다.
SessionCache 를 Arc 로 공유하면 모델은 프로세스에서 한 번만 로드된다.
use std::sync::Arc;
use rust_ai_serving_engine_core::{DevicePreference, GenerationConfig, ModelRegistry, generate};
use rust_ai_serving_engine_models::SessionCache;
// 기동 시 1회
let cache = Arc::new(SessionCache::new());
// 핸들러
let cache = cache.clone();
let text = tokio::task::spawn_blocking(move || -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let registry = ModelRegistry::open("./models")?;
let session = cache.get_or_load(®istry, "qwen3-4b", DevicePreference::Auto)?;
let mut session = session.lock().unwrap();
let tokens = session.tokenizer.encode("The capital of France is", true)?;
let mut config = GenerationConfig::default();
if let Some(eos) = session.eos_token { config.stop_tokens.push(eos); }
let out = generate(session.decoder.as_mut(), &tokens, &config, || false)?;
Ok(session.tokenizer.decode(&out.tokens, true)?)
}).await??;
기성 HTTP 표면이 필요하면 rust_ai_serving_engine_api::{router, serve, ApiState} 를 그대로
자기 서버에 합성할 수도 있다.
12.4 타 언어 / 배치 — 사이드카
Java·Node·Go 등에서는 serve 를 사이드카 프로세스로 띄우고 OpenAI 클라이언트로 붙는 것이
가장 단순하다. 단일 바이너리라 컨테이너에 실행 파일 하나 + 모델 폴더만 넣으면 된다.
13. 새 모델 아키텍처 추가하기
새 GGUF 아키텍처는 세 단계로 붙는다. 코어 생성 루프·API·CLI 는 건드리지 않는다.
- 디코더 구현 —
TokenDecoder를 구현한다. Candle 의 quantized 모델 구현을 감싸는 것이 기본형이다.
use rust_ai_serving_engine_core::{Result, TokenDecoder};
pub struct MyArchDecoder { /* ModelWeights + device + position */ }
impl TokenDecoder for MyArchDecoder {
fn prefill(&mut self, prompt: &[u32]) -> Result<Vec<f32>> {
// KV 캐시 초기화 → 프롬프트 전체 forward → 마지막 위치 logits (rank-1 Vec<f32>)
}
fn decode(&mut self, token: u32) -> Result<Vec<f32>> {
// 토큰 1개 forward (KV 캐시 누적) → 다음 logits
}
fn eos_token(&self) -> Option<u32> { /* GGUF 메타데이터에서 읽은 값 */ }
}
-
아키텍처 매핑 등록 —
load_gguf_decoder의 match 에 아키텍처 이름을 추가한다. 지원하지 않는 조합은 잘못된 출력 대신UnsupportedArchitecture로 거절하는 것이 규약이다. -
채팅 템플릿 연결 — 기존 3종으로 충분하면
ChatTemplate::for_architecture에 기본값만 추가하고, 새 마크업이 필요하면 variant 와 렌더 함수를 추가한다 (렌더는 순수 함수라 단위 테스트로 고정한다).
주의할 계약 두 가지 — prefill 은 반드시 KV 캐시를 초기화해야 하고(이전 대화 오염 방지),
logits 는 rank-1 벡터로 반환해야 한다 (Candle forward 가 (batch, vocab) rank-2 를 주면 squeeze 필요 —
실제로 이 처리 누락이 치명 버그였고 실모델 종단 간 시험으로 잡았다).
14. 빌드 · Feature 조합 · 테스트
이 저장소를 clone 한 경우, Rust 툴체인(stable) 으로 한 번 빌드해야 한다.
| 쓰는 방식 | 빌드 명령 | 결과물 |
|---|---|---|
| CLI + 서버 | cargo build --release |
target/release/rust-ai-serving-engine 단일 바이너리 |
| Python 모듈 | pip install maturin && maturin develop --release |
현재 venv 에 import rust_ai_serving_engine |
| Rust 라이브러리 | Cargo.toml 에 git/path 의존성 |
다른 Rust 프로젝트에 링크 |
# 전체 워크스페이스 빌드·테스트
cargo build --release
cargo test --workspace
cargo clippy --all-targets
# Python 확장 게이트 컴파일 확인
cargo check -p rust-ai-serving-engine-python --features python
# 배포 휠 빌드
maturin build --release # dist/ 에 abi3 휠
테스트는 모델 파일 없이 동작하는 범위를 결정적으로 검증한다 — 생성 루프(정지·취소), 레지스트리 (등록·해시 변조 감지·토크나이저 연결), 채팅 템플릿 렌더 3종, stop 문자열 파싱·홀드백 경계.
실모델 스모크 (수동)
코드 변경 후 실모델 회귀는 2장의 CLI 흐름 그대로 — 소형 GGUF 를 pull 해
serve 를 띄우고 채팅 완성(비스트리밍·스트리밍)을 호출한다. temperature: 0.0 + 고정 seed 로
같은 입력의 출력 안정성까지 확인한다.
15. 디렉토리 구조
rust_ai_serving_engine/
Cargo.toml # 워크스페이스 정의
pyproject.toml # maturin 빌드 메타데이터 (PyPI 패키지)
README.md # 이 문서
crates/
rust_ai_serving_engine_core/
src/
lib.rs # 크레이트 루트 · re-export
manifest.rs # ModelManifest / ModelKind / ModelFormat
registry.rs # ModelRegistry (등록·해시·검증, 원자적 기록)
generation.rs # TokenDecoder / GenerationConfig / generate(_with) / 샘플러
runtime.rs # DevicePreference / RuntimeDevice (CPU·CUDA·Metal)
hub.rs # HuggingFaceHub 다운로드
error.rs # EngineError
rust_ai_serving_engine_models/
src/
lib.rs # load_gguf_decoder · GGUF EOS 추출
llama_gguf.rs # Llama·Mistral GGUF 디코더
qwen3_gguf.rs # Qwen3 GGUF 디코더
qwen3_model.rs # Qwen3 forward (하이브리드 프리필 GEMM + 블록 어텐션)
profiling.rs # RASE_PROFILE 구간 카운터 (11장 성능 프로파일링)
threading.rs # 디코드 스레드 기본 정책 (8장 디코드 스레드 정책)
gpu_gemm.rs # wgpu 프리필 GEMM 오프로드 (8장, RASE_GPU=1 옵트인)
chat.rs # ChatTemplate (ChatML·Llama3·Mistral) + 렌더 테스트
session.rs # ModelSession / SessionCache
tokenizer.rs # LocalTokenizer (tokenizer.json)
rust_ai_serving_engine_api/
src/lib.rs # OpenAI 호환 HTTP API + SSE 스트리밍
rust_ai_serving_engine_cli/
src/main.rs # model / runtime / serve 명령
rust_ai_serving_engine_python/
src/
lib.rs # feature 게이트
python.rs # PyO3 바인딩 (GIL 해제 + 프로세스 세션 캐시)
16. 라이선스와 모델 책임
엔진 코드는 Apache-2.0 이다.
모델 가중치·토크나이저·GGUF 변환물의 라이선스는 엔진과 별개다. 레지스트리에 등록하는 각 모델의 출처와 라이선스 조건(재배포 가능 여부 포함)은 사용자가 확인해야 하며, 상업 배포에서는 모델별 조건을 확인한 뒤에만 번들에 포함한다.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rust_ai_serving_engine-0.1.5.tar.gz.
File metadata
- Download URL: rust_ai_serving_engine-0.1.5.tar.gz
- Upload date:
- Size: 118.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25a06bbb4427cf5a1a567a9e5f370d7028612405c6bca76cd0e73a2eac9506a4
|
|
| MD5 |
d9d9bb7246963d9c79374a6abf19ca92
|
|
| BLAKE2b-256 |
bc496ac2b266d9f490f8892d29ab5c45f017d0ec56ca238d1778342c0f957790
|
Provenance
The following attestation bundles were made for rust_ai_serving_engine-0.1.5.tar.gz:
Publisher:
release.yml on arabangoo/rust_ai_serving_engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rust_ai_serving_engine-0.1.5.tar.gz -
Subject digest:
25a06bbb4427cf5a1a567a9e5f370d7028612405c6bca76cd0e73a2eac9506a4 - Sigstore transparency entry: 2216241454
- Sigstore integration time:
-
Permalink:
arabangoo/rust_ai_serving_engine@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/arabangoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rust_ai_serving_engine-0.1.5-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: rust_ai_serving_engine-0.1.5-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 6.7 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83817da96e35adb1fceaf27c20053f02bab1e00301ba1755f1729dba18e2b262
|
|
| MD5 |
39cce57f3a0bb7df270af5dadc2e4343
|
|
| BLAKE2b-256 |
6f0c5368a1610fdce58b1d653cdc63cca1f4756f3b4db4a9d5db85647b4dea04
|
Provenance
The following attestation bundles were made for rust_ai_serving_engine-0.1.5-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on arabangoo/rust_ai_serving_engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rust_ai_serving_engine-0.1.5-cp39-abi3-win_amd64.whl -
Subject digest:
83817da96e35adb1fceaf27c20053f02bab1e00301ba1755f1729dba18e2b262 - Sigstore transparency entry: 2216241745
- Sigstore integration time:
-
Permalink:
arabangoo/rust_ai_serving_engine@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/arabangoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 6.9 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c20ecef1072530ad83fc6c817cfb1d986b45bba863ed6d63e6d503872f40f23e
|
|
| MD5 |
46fa3e533768f08c2df0f8bcd04f32a2
|
|
| BLAKE2b-256 |
472a77412a2b01e507005bea2d1ea0309d7b6e62d9323d2984008e532fe8bfdf
|
Provenance
The following attestation bundles were made for rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on arabangoo/rust_ai_serving_engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
c20ecef1072530ad83fc6c817cfb1d986b45bba863ed6d63e6d503872f40f23e - Sigstore transparency entry: 2216241625
- Sigstore integration time:
-
Permalink:
arabangoo/rust_ai_serving_engine@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/arabangoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 6.6 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3713d6bbe6d8471269d0cef132aa49b098de3b0d9f0fe627e4cf02e702b18d86
|
|
| MD5 |
fe3e6a543ea2583077a70db0c7fd271a
|
|
| BLAKE2b-256 |
c40424e77efdc19395c3a20ea48798f89f9c2d1303268258be74fb1c7360da18
|
Provenance
The following attestation bundles were made for rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on arabangoo/rust_ai_serving_engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rust_ai_serving_engine-0.1.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
3713d6bbe6d8471269d0cef132aa49b098de3b0d9f0fe627e4cf02e702b18d86 - Sigstore transparency entry: 2216241835
- Sigstore integration time:
-
Permalink:
arabangoo/rust_ai_serving_engine@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/arabangoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 6.1 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0dc19f6cf42035c040f34815b72c9e36ef82dfa60406300d3bcc9b7c360c2b3e
|
|
| MD5 |
8f3ac8ba579dd6e0569e050d0c8c0ea3
|
|
| BLAKE2b-256 |
cf1270a0d1039cb39656bd36f06d089ae87e2a21d767f44bd1a1c1acb7e436b9
|
Provenance
The following attestation bundles were made for rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on arabangoo/rust_ai_serving_engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
0dc19f6cf42035c040f34815b72c9e36ef82dfa60406300d3bcc9b7c360c2b3e - Sigstore transparency entry: 2216241522
- Sigstore integration time:
-
Permalink:
arabangoo/rust_ai_serving_engine@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/arabangoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 6.3 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0730d0c802a7bc2a5a82b0544c1937c1ded66127fb276ccdd312300cf06ca9a
|
|
| MD5 |
20d40e5ea9c4e9fb9620330f2f867799
|
|
| BLAKE2b-256 |
51137de16866425ea904ef68aefe11b8111c4134af11ea9e9773af4f3faaf1a6
|
Provenance
The following attestation bundles were made for rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on arabangoo/rust_ai_serving_engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rust_ai_serving_engine-0.1.5-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
c0730d0c802a7bc2a5a82b0544c1937c1ded66127fb276ccdd312300cf06ca9a - Sigstore transparency entry: 2216241897
- Sigstore integration time:
-
Permalink:
arabangoo/rust_ai_serving_engine@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/arabangoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@902893f4163755c4cff8dc289a3cf013a4a8e633 -
Trigger Event:
push
-
Statement type: