~13 min · fleet, inference-hub, omlx, ollama, fail-closed, measured
Level 0스펙 시트 훑는 사람
0 XP0/91 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete
"맥 하나가 모델을 들고 있어. 다른 모든 맥은 URL을 들고 있어. 그게 아키텍처의 전부고, 카운터가 그게 된다고 말해."
허브가 서비스하는 것
server는 추론 데몬 셋을 돌리고 가족의 앱들이 공유하는 모든 모델을 들고 있는 512 GB Studio야. 자체 API로 직접 읽은 그 MLX 엔드포인트는 모델 다섯을 나열해. 리랭커 둘, fp16의 bge-m3 임베딩 모델, bf16의 31B 채팅 모델, 문서 변환기. 그 Ollama 데몬은 읽는 순간 모델 하나를 들고 있었어. Ollama 형식의 같은 임베딩 모델, 아직 그걸 쓰는 영혼 기억 관리자를 위해 따뜻하게 유지된. 그 이미지 엔진은 MPS 위의 PyTorch와 diffusers, 여정 트랙의 큰 연산 사례를 돌려. 드로잉 앱, 파일 작업대, 피파의 이미지 서술을 위해. 같은 분에 읽은 MLX 엔드포인트 자체의 카운터: 143,626 요청, 329,536,723 프롬프트 토큰, 완성 토큰 0, 캐시 토큰 0. 가족이 여태 보낸 모든 요청이 프리필이었어. 임베딩이나 리랭크. 목록의 채팅 모델은 클라이언트가 고를 수 있는 계층이지 가진 것이 아니야.
클라이언트가 얇은 이유
패턴은 GPU 호스트 하나와 공유 가중치를 안 드는 클라이언트 여럿이야(4번 레슨이 서술하는 로컬 계층, 묻는 맥 위의 Ollama 데몬은 표면마다 고른 의도된 예외야). 이미지 엔진 자체의 집 규칙이 가장 엄격한 판을 명시해. server가 유일한 추론 호스트고, 클라이언트는 "닫힌 채 실패해". 허브에 못 닿는 노트북은 자기 GPU로 폴백하지 않고 허브가 죽었다고 보고해. 가혹하게 들리고 맞는 거래야. 모델마다 사본 하나, 갱신할 자리 하나, GPU 트랙의 요청자 규율이 작업 집합을 지켜야 할 기계 하나, 낡은 체크포인트를 조용히 돌리는 노트북 없음. 피파 자신의 검색, 말뭉치 엔진, 산문 편집기의 공동 집필이 전부 같은 엔드포인트에 닿아. 인터페이스 레슨은 네트워크 횡단을 요청당 몇 밀리초로 값 매겼어. 숫자 1,024개를 읽어 오는 질의엔 아무것도 아니고, 채팅 스트림엔 토큰 하나의 디코드 시간보다 적어. 허브는 풀이 충분히 커서 충분히 빠르고, 풀은 기계 하나가 갖고 있어서 충분히 커.
어느 워크로드인지 말하는 숫자
143,626 요청에 완성 토큰 0이 카운터에서 가장 많은 걸 말해 주는 줄이야. 물리 트랙의 두 단계, 연산에 묶인 프리필과 대역폭에 묶인 디코드가 배포 사실이 돼. 이 허브의 부하는 전부 프리필이고, 장부가 유도하는 합산 프리필 시간 기준 초당 1,229 프롬프트 토큰 근처의 합계 속도로, 동시성 때문에 벽시계로는 그게 과대 집계야. MLX 서버의 SSD 캐시, 맞는 접두사의 KV 블록을 다시 계산하는 대신 디스크에서 다시 불러오는 능력은 그런 부하에선 재사용할 게 없고, 읽을 때 캐시 디렉터리는 0바이트였어. MLX 서버의 캐싱이 그 일부를 Ollama보다 빠르게 만든다는 이 집의 결정은 반복되는 접두사를 가진 생성에 참이고, 임베딩만 하는 허브는 아직 그걸 쓴 적이 없어. 기억 검색 레슨이 양쪽 절반을 다 말해. 허브가 증명하는 건 더 단순해. 512 GB 풀이 가족의 검색을 몇 달씩, 책상 위에서, 조용히, 데몬마다 모델 하나를 따뜻하게 두고 서비스하는 것.
Code
hub_models.py — 허브 자체의 API와 카운터에서 읽은, 허브가 서비스하는 것·python
#!/usr/bin/env python3
"""One inference hub, many clients: what the hub serves. Two daemons on one Mac, read
through their own APIs. The hub's address comes from the environment, never from a
quest; run this on the hub itself or point HUB_MLX / HUB_OLLAMA at it."""
import json, os, urllib.request
mlx = os.environ.get("HUB_MLX", "http://127.0.0.1:8000") # the MLX server's OpenAI-compatible base
oll = os.environ.get("HUB_OLLAMA", "http://127.0.0.1:11434") # the Ollama daemon
models = json.load(urllib.request.urlopen(mlx + "/v1/models"))["data"]
print("MLX endpoint serves:", [m["id"] for m in models])
ps = json.load(urllib.request.urlopen(oll + "/api/ps"))["models"]
print("Ollama has loaded: ", [(m["name"], f"{m['size']/1e9:.1f} GB") for m in ps])
stats = json.load(open(os.path.expanduser("~/.omlx/stats.json"))) # the MLX server's own counters
print("MLX totals: ", {k: stats[k] for k in ("total_requests", "total_prompt_tokens", "total_completion_tokens", "total_cached_tokens")})
# server, 2026-09-15:
# MLX endpoint serves: ['Qwen3-Reranker-0.6B-mlx-8Bit', 'Qwen3-Reranker-4B-mxfp8', 'bge-m3-mlx-fp16', 'gemma-4-31b-it-bf16', 'MarkItDown']
# Ollama has loaded: [('bge-m3:latest', '0.7 GB')]
# MLX totals: {'total_requests': 143626, 'total_prompt_tokens': 329536723, 'total_completion_tokens': 0, 'total_cached_tokens': 0}