Giting.
낮의 Giting

홈 · Jev 재현 · constrained decode · 오픈 모델 · SGLang · Qwen · Python

openjev-sglang · jevlike 대표 이미지 openjev-sglang 604664a (2026-09-18, 전체 18커밋)
분석 리포트실측 정보적용점원문 README

하이프의 뒤편

이번 주 타임라인은 온통 Jev였다. "항공권 7초", "광고 724개 40초", "논문 1,018개를 8센트에". madewithjev.com 에 이런 사례가 98개 걸렸고, 국내 계정들이 "미친 활용 15가지"로 퍼 날랐다. 그런데 그 98개를 실측 기준으로 세어 보면, 정작 클론해서 확인할 수 있는 오픈 repo 는 20여 개뿐이다. 나머지는 X 데모와 저자가 스스로 잰 수치다.

사례 목록은 재미있지만, 진짜 뉴스는 그 아래에 있다.

두뇌를 그대로 베낀다

TypeSafe 는 Jev 를 닫힌 유료 API로 내놨다(앞선 리포트 seq 28 참고, 속도의 비밀은 '인덱스드 액션 스페이스 + 1왕복 투기 디코드'이고, 결정 두뇌는 그들의 호스팅 모델이다). 그리고 나흘 만에 openjev-sglang(ekzhang)이 나왔다.

이건 얇은 래퍼가 아니라, Jev 의 HTTP API 를 오픈 모델로 그대로 구현한 서버다. Qwen3.6-35B-A3B 를 SGLang 위에 올리고, Rust 프론트엔드, radix 캐싱, breakable prefill CUDA 그래프, FastAPI 로 Jev 호환 엔드포인트를 낸다. 핵심은 prefill-only: 텍스트를 생성하지 않고 판단만 뽑는다. Jev 의 본질이 '생성이 아니라 선택'이라는 걸 서빙 레벨에서 그대로 옮긴 것이다.

유료 Jev 에 붙여 둔 코드가 있다면, 엔드포인트만 이 오픈 서버로 바꿔 끼울 수 있다는 뜻이다.

이 물결은 며칠짜리다

그러나 실측은 성숙도도 같이 드러낸다.

openjev-sglang 은 18커밋·CI 0·1인이고, 라이선스 파일이 없다, 기본값은 전권 유보라 법적으로 그대로 못 쓴다. 랩탑에선 안 돌고 Modal 의 B200 GPU 가 있어야 한다. 옆에 선 jevlike(958★·MIT)는 스타가 화려하지만 하루짜리 아키텍처 스케치고, madewithjev 목록에 함께 걸려 있던 reflex 는 이 글을 쓰는 나흘 사이 이미 404 로 사라졌다.

방향은 분명한데, 각 구현은 주말이면 바뀔 실험이다.

그래서 우리가 볼 것

98개 사례에 눈을 뺏기지 말자. 이번 주의 진짜 신호는 하나다. 닫힌 결정 모델을 오픈 가중치로 나흘 만에 흉내 낼 수 있다. 그게 맞다면 Jev 의 해자는 모델 자체가 아니라 API·속도·에코시스템이고, 오픈으로 같은 구조를 세울 자리가 열려 있다는 뜻이다.

판정은 관망이다. 지금 제품에 넣을 '오픈 Jev'는 없다. 하지만 이 공간은 주 단위로 굳어질 것이고, openjev-sglang 의 SGLang prefill-only 서빙과 Jev HTTP 스펙은 그때를 대비해 지금 읽어 둘 표본이다.

실측 정보

앵커 커밋
openjev-sglang 604664a (2026-09-18, 전체 18커밋)
무엇
openjev-sglang(ekzhang) = TypeSafe/Jev 의 HTTP API 를 오픈 모델로 그대로 구현한 서버. Qwen3.6-35B-A3B 를 SGLang(Rust 프론트·radix 캐싱·breakable prefill CUDA 그래프)에 올리고, FastAPI+uvloop 로 Jev 호환 엔드포인트를 낸다. prefill-only(생성 아님, 판단만)
규모 / 실측
텍스트 11,843줄 · Python 4,889줄 · 테스트 9 · CI 0 · 1인 · 의존성 10. 스타 185
동류
jevlike(vinnylarouge, 958★, MIT) = Jev 아키텍처 역설계+커스텀 학습, 하루짜리 · openjev on Qwen 4B(AlexWortega, HF) = 소형 MLP 복제 · reflex(WebGPU 결정모델) = ⚠️ 이미 404(나흘 새 사라짐)
실행 현실
openjev 는 랩탑서 안 돈다. 모델은 Modal 의 B200 컨테이너에서만. `uv sync` 는 API 클라이언트·배포툴·테스트만 깐다. 무인증 퍼블릭 엔드포인트(unauthenticated=True)로 뜨고 5분 유휴 시 0으로 스케일다운
하이프의 정체
madewithjev.com 에 98개 사례. 그중 실측 가능한 오픈 repo 는 20여 개뿐, 나머지는 X 데모·마케팅 수치(항공권 7초·724광고·1kpapers 등: 저자 주장이라 우리는 인용만)
앞선 실측
[[jev-ultrafast]](seq 28): Jev 의 속도는 '인덱스드 액션 스페이스 + 1왕복 투기 디코드'에서 나오고, 결정 두뇌는 TypeSafe 유료 호스팅 API(클로즈드)다. 이 리포트는 그 두뇌의 오픈 재현을 추적한다
라이선스
⚠️ openjev-sglang 은 라이선스 파일이 없다. 기본값은 '모든 권리 유보'라 그대로 가져다 쓰면 안 된다. jevlike 는 MIT

적용점

  • 이럴 때 유용Jev 의 '생성 대신 선택' 구조가 정말 오픈 모델로 재현되는지 두 눈으로 확인하고 싶을 때. openjev-sglang 은 Jev HTTP API 를 그대로 흉내 내므로, 유료 Jev 에 붙인 코드를 오픈 엔드포인트로 갈아 끼워 보는 실험이 가능하다(B200 을 Modal 에서 빌릴 수 있다면). constrained decode 서빙을 공부하는 사람에겐 SGLang 위 prefill-only 판단 서버가 좋은 표본이다.
  • 이럴 땐 부적합지금 제품에 넣을 오픈 Jev 를 찾는다면 이르다. openjev 는 라이선스가 없어 법적으로 못 쓰고, 랩탑이 아니라 B200 이 필요하며, 18커밋·CI 0·1인·나흘이다. jevlike 는 하루짜리 아키텍처 스케치고, reflex 는 벌써 사라졌다. 이 물결은 며칠 단위로 나고 진다. 의존할 인프라가 아니다.
  • 먼저 볼 것사례 목록(98개)에 눈을 뺏기지 말 것. 그건 대부분 X 데모다. 진짜 신호는 '닫힌 결정 모델을 오픈 가중치로 나흘 만에 흉내 낼 수 있다'는 사실 자체다. 그게 맞다면 Jev 의 해자는 모델이 아니라 API·속도·에코시스템이고, 우리(또는 누구든)가 오픈으로 같은 구조를 세울 여지가 있다는 뜻이다. openjev-sglang 의 SGLang prefill-only 서빙과 Jev HTTP 스펙을 먼저 보라.
◇ 관망

판정은 관망이다. 먼저 이 물결이 진짜라는 걸 인정하자, TypeSafe 가 Jev 를 닫힌 유료 API 로 내놓자, 나흘 만에 openjev-sglang 이 Qwen3.6 을 SGLang 에 올려 같은 HTTP API 를 구현했고, jevlike 는 아키텍처를 역설계했으며, 여러 사람이 소형 모델로 흉내를 냈다. 이건 우리가 앞선 리포트(seq 28)에서 '두뇌가 클로즈드니 오픈으로 재현하면 값'이라 적은 바로 그 일이고, 그 예측이 실시간으로 맞아떨어지는 걸 보는 건 드문 일이다. 구조적으로도 말이 된다. Jev 의 값은 무거운 생성이 아니라 '선택을 typed 로 뽑는 prefill-only 판단'이라, 작은 오픈 모델로도 근사가 가능하다. 그러나 실측이 성숙도를 드러낸다. openjev-sglang 은 라이선스 파일이 없어 법적으로 그대로 못 쓰고(기본값은 전권 유보), 랩탑이 아니라 Modal 의 B200 에서만 돌며, 18커밋·CI 0·1인이다. jevlike 는 958스타가 붙었지만 하루짜리 스케치고, madewithjev 에 걸려 있던 reflex 는 나흘 새 404 로 사라졌다. 즉 방향은 확실하되 각 구현은 며칠짜리 실험이다. 그래서 관망: 지금 제품에 넣을 '오픈 Jev'는 없지만, 이 공간은 주 단위로 굳어질 것이고 우리가 지켜봐야 할(그리고 언젠가 직접 세워 볼 수 있는) 자리다. 하이프의 98개 사례가 아니라, 그 아래에서 벌어지는 이 재현이 이번 주의 진짜 뉴스다.

원문 README

아래는 ekzhang 외/openjev-sglang · jevlike 저장소의 README 원문입니다(GitHub 렌더, 수집 시점 기준). 저작권은 원 프로젝트에 있으며 원 저장소의 라이선스를 따릅니다. GitHub에서 보기

README 전문 펼치기

openjev-sglang

A server implementing the TypeSafe/Jev HTTP API with Qwen3.6-35B-A3B on SGLang.

Each container one B200 with SGLang 0.5.19's Rust frontend, radix caching, and breakable prefill CUDA graphs. A separate Python API process uses FastAPI, uvloop, the Rust-backed HF tokenizer, and pooled asynchronous HTTP connections to SGLang on localhost. CUDA dependencies stay in SGLang's container; uv sync on your laptop installs only the API, deployment tools, and tests.

Run on Modal

uv sync
# Only if you haven't authenticated Modal on this machine:
uv run modal setup

# Start a temporary Server, run actual inference checks, then shut it down:
uv run modal run modal_app.py

# Deploy a stable public endpoint:
uv run modal deploy modal_app.py

The deployment prints a https://...us-west.modal.direct URL. It uses a Modal Server, unauthenticated=True, routing_region="us-west", and compute_region=["us-west", "us-central", "us"]. Autoscaling has no explicit container cap and scales to zero after five idle minutes. Set min_containers=1 in modal_app.py to keep a B200 warm. If SGLang exits unexpectedly, the API exits too. The Modal launcher watches the API and exits the container so Modal can replace it, rather than leaving a live HTTP process with a dead inference backend. Normal shutdown disarms both watchers.

Cache warmups also request one unused token probability to avoid SGLang's mixed-logprob batch crash. This keeps warmups and scoring requests batch-compatible without patching SGLang.

The first build imports a large SGLang image. The first GPU start also downloads weights and compiles/captures kernels. Model weights persist in the openjev-huggingface Modal Volume, alongside SGLang's tuning cache and Triton compilation cache. Later starts reuse these files; CUDA graph capture still runs at startup. The Rust frontend receives an explicit local tokenizer directory to avoid remote-name lookup issues with revision-pinned snapshots. A scaled-to-zero Server returns 503 while it starts; the included smoke command retries startup responses.

uv run openjev smoke https://YOUR-SERVER.us-west.modal.direct

The smoke test covers all three answer types, a 64-answer question, basic semantic sanity checks, and rejection of 65 answers. It reports startup wait, inference latency, and cache usage. modal run saves this report as smoke-result.json.

Request

curl "$OPENJEV_URL/v1/systemone" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "jev-latest",
    "state": [
      {"role": "system", "content": "You are a support assistant."},
      {"role": "user", "content": "I was charged twice. Please refund the duplicate."}
    ],
    "questions": {
      "refund": {
        "type": "noul",
        "instructions": "Does the user request a refund?"
      },
      "department": {
        "type": "choice",
        "instructions": "Which department should handle this?",
        "criteria": {"billing": "Payments and refunds", "technical": "Software bugs"}
      },
      "urgency": {
        "type": "score",
        "instructions": "How urgent is the request?",
        "criteria": ["Routine", "Urgent", "Emergency"]
      }
    }
  }'

Or use curl "$OPENJEV_URL/v1/systemone" -H 'Content-Type: application/json' --data-binary @examples/request.json.

state and instructions accept strings, JSON objects, or arrays. A state that is a list of chat messages, or exactly {"messages": [...]}, is rendered using the model's native chat template. Original roles and message objects are retained; the classification question becomes an additional user turn, even after another user turn. Other structured state is serialized intact into a user message. Objects containing messages plus additional fields are kept intact so metadata isn't silently discarded. Chat state supports text, not image/audio/video content.

Route Purpose
POST /v1/systemone Noul, Choice, and Score evaluation
GET /v1/models Model catalogue with TypeSafe and OpenAI-style fields
GET /v1/limits Admission limits
GET /health Readiness, including SGLang health and startup duration
GET /health/live API process liveness
GET / Scalar API reference with an editable example and request client
GET /docs Built-in Swagger UI
GET /openapi.json Generated API schema

jev-latest is a compatibility alias for the configured Qwen model. The public model ID is Qwen/Qwen3.6-35B-A3B; NVIDIA's repository is only the internal weight source. Set OPENJEV_SERVED_MODEL_NAME or openjev serve --served-model-name NAME to override the public ID. It is also passed to SGLang as --served-model-name. No requests go to TypeSafe. The public server is the evaluation API; SGLang's generation and administration routes remain on localhost and aren't forwarded publicly.

How inference works

  1. Validate the schema, answer count, body size, context length, and total token budget.
  2. Render the native chat template once, with thinking disabled. Split out a common prefix and independently tokenize each question suffix.
  3. Send the common prefix to /generate with max_new_tokens=1, await completion, and discard the sampled token. This warms SGLang's radix cache.
  4. Concurrently send prefix + question suffix + assistant header + "Answer:\n" for each question. Every call again has max_new_tokens=1. Request token_ids_logprob for every answer label and logprob_start_len=-1, so there is no need to recompute prompt logprobs. The sampled token itself is ignored.
  5. Renormalize the requested label logprobs with stable softmax. Noul returns P(yes), Choice returns the argmax and full distribution, and Score returns sum(level_index * probability) with zero-based levels and a legend.

Options are rendered as A: description, B: description, etc., without JSON wrappers. Choice keys identify response fields and are hidden from the model, except when a description is null: then the option key supplies its meaning, matching Jev's nullable description schema.

This is a prefill plus first-token-readout workload: there is no generated chain of thought and no autoregressive continuation after the first token. There are N+1 one-token calls for N questions, including the cache-warming call. Speculative decoding is not enabled.

Qwen tokenizes 10 and 64 as multiple tokens. Answer labels are therefore A–Z, followed by verified single-token letter combinations (AA, AB, ...). All 64 labels are checked against the actual tokenizer at startup. Your original option names are preserved in the returned distribution. This keeps 64-way classification an exact one-token readout instead of comparing only the first digit of a multi-token number.

Radix reuse is opportunistic, not a pinned per-request KV session. Hybrid Qwen's recurrent state, cache page boundaries, cache pressure, and concurrent requests can reduce hits. The backend uses --mamba-radix-cache-strategy extra_buffer. x-openjev-prefix-tokens exposes the requested common prefix size. When SGLang reports cache counts, x-openjev-cached-tokens sums the branch cache hits. SGLang 0.5.19's Rust frontend omits these counts: the header is absent and smoke reports null, rather than a misleading zero. Scheduler logs still show actual cache hits (verified on the live B200 deployment). Server-Timing separates prompt preparation, the shared prefill, and branch inference.

usage.input_tokens sums SGLang's full prompt counts across the warm-up and all branches, including cached tokens. usage.output_tokens is N+1. These are backend usage counts, not TypeSafe billing estimates or unique tokens actually computed.

Limits and configuration

Defaults are 64 questions, 2–64 answers per Choice/Score, 2 MiB JSON, 32,768 tokens per branch including its output, 262,144 total submitted input tokens, 16 simultaneous evaluations, and 64 simultaneous backend calls. Invalid requests return 422 before inference; oversized bodies return 413; overload returns 529 with Retry-After. Backend timeouts return 504. Failed or cancelled evaluations cancel sibling requests and attempt to abort them in SGLang.

All settings can be provided as OPENJEV_* environment variables; see src/openjev/config.py. Common settings:

Variable Default
OPENJEV_MODEL nvidia/Qwen3.6-35B-A3B-NVFP4
OPENJEV_SERVED_MODEL_NAME Qwen/Qwen3.6-35B-A3B (profile-specific public name)
OPENJEV_REVISION Pinned NVIDIA checkpoint revision for the default model
OPENJEV_FRONTEND rust (python is an explicit fallback)
OPENJEV_MAX_INPUT_TOKENS 32768
OPENJEV_MAX_TOTAL_INPUT_TOKENS 262144
OPENJEV_MAX_CONCURRENT_REQUESTS 16
OPENJEV_MAX_CONCURRENT_BRANCHES 64
OPENJEV_REQUEST_TIMEOUT 120 seconds
OPENJEV_TEMPERATURE 1.0, applied during label normalization
OPENJEV_API_KEY Unset; optional Bearer authentication for the API
OPENJEV_BACKEND_API_KEY Unset; optional separate SGLang Bearer key

The Modal launch script forwards OPENJEV_PROFILE, OPENJEV_FRONTEND, and OPENJEV_SERVED_MODEL_NAME from the local environment. To customize other remote settings, add them to image.env(...) or use a Modal Secret for keys. The default Modal endpoint intentionally has no auth.

OpenJev defines confidence = 1 - H(probabilities) / log(number_of_options), clamped to [0, 1]. This is zero for a uniform distribution and one for a point mass. Probabilities are conditioned on the supplied options, depend on prompt and label ordering, and are not calibrated estimates of correctness.

Local development / existing SGLang

uv sync
uv run pytest                         # offline unit + API tests
uv run pytest -m integration          # real tokenizer, small HF download, no GPU
uv run ruff check .
uv run openjev schema                 # no GPU or model download

# Connect to an existing backend; it must have matching model/tokenizer revision,
# selected-token logprobs, radix cache, and a sufficient context length:
uv run openjev serve --connect http://127.0.0.1:30000

# On a B200 host/container with SGLang 0.5.19 installed in another environment:
uv run openjev serve --sglang-python /path/to/sglang/bin/python

의견

판정에 대한 반론, 도입 경험, 정정 제보를 환영합니다. GitHub 계정으로 참여합니다.

이것도 재봤습니다

전체 리포트 →