K2 Horizon
K2 Horizon
K2 Horizon은 IFM의 모델 제품군으로, 0.9B부터 375B 파라미터까지 여섯 개 체크포인트를 아우러요. 이 제품군에는 Dense 모델, Mixture-of-Experts(MoE) 모델, 그리고 MoVA-36B-A4B 체크포인트의 routed Mixture-of-Value-Attention 경로가 포함돼요. 이 페이지는 이 제품군에 대한 네이티브 SGLang 서빙 레시피를 제공해요.
출처: 문서
본문
1. Model introduction
K2 Horizon은 IFM 모델 제품군으로 0.9B에서 375B 파라미터까지 여섯 개 체크포인트를 아우러요. 이 제품군에는 Dense 모델, Mixture-of-Experts 모델, 그리고 MoVA-36B-A4B 체크포인트의 routed Mixture-of-Value-Attention 경로가 포함돼요. 이 페이지는 이 제품군에 대한 네이티브 SGLang 서빙 레시피를 제공해요.
| Checkpoint | Architecture | Context length | H200 recipe |
|---|---|---|---|
| 0.9B | Dense | 131,072 | BF16 · TP1 |
| 3.7B | Dense | 524,288 | BF16 · TP1 |
| 7B | Dense | 524,288 | BF16 · TP1 |
| 32B | Dense | 524,288 | BF16 · TP2 |
| MoVA-36B-A4B | MoE + MoVA | 524,288 | BF16 · TP2 |
| 375B-A23B | MoE | 524,288 | BF16 · TP8 |
여섯 체크포인트 모두 Apache License 2.0을 사용해요.
리소스: K2 Horizon collection · K2 Horizon 375B-A23B model card.
2. Configuration tips
- 하드웨어와 정밀도: 현재 매트릭스는 NVIDIA H200 GPU와 BF16 체크포인트만 다뤄요.
- 병렬화: 0.9B·3.7B·7B는 TP1을, 32B와 MoVA-36B-A4B는 TP2를, 375B-A23B는 TP8을 사용해요. 각 레시피는 한 노드 안에 머물러요.
- 어텐션: 검증된 기본 레시피들은
--attention-backend fa3으로 FlashAttention-3를 선택해요. - Revisions: 레시피들은 revision
9b9ec1f7e17f62ed218df542687a144116219d84(0.9B),c177771836a4c460743c00002c22483f6f18d1eb(3.7B),69ada542b68fe13d767479db2ab9421baff88681(7B),e1fd0277713e4eefcd3416348fd6fedacf7f2392(32B),16d20c739c687c08423422d1a2fbba6c529014cd(MoVA-36B-A4B),12812264242a14dce44aa7ae27f931ff4584bcbf(375B-A23B)를 고정해요. - Parsers: 기본 레시피는
--reasoning-parser k2_horizon으로 reasoning을 활성화해요. tool calling이 필요하면 Playground의 Parsers 카드에서--tool-call-parser k2_horizon을 켜요. - MoVA-36B-A4B router provenance: MoVA-36B-A4B 체크포인트의 소스 xLLM router는 두 개의 GEMM partition을 사용했어요.
--json-model-override-args '{"xllm_source_router_gemm_partitions":2}'이 BF16 partial-GEMM 반올림과 FP32 reduction 순서를 보존해요; SGLang은 이 소스 토폴로지를 런타임 TP로부터 추론하지 않아요. - 375B-A23B 로딩: 375B-A23B 레시피는
--model-loader-extra-config '{"enable_multithread_load":false}'로 멀티스레드 가중치 로딩을 비활성화해요. - Playground features: TP, EP, NGRAM, PD disaggregation, HiCache는 선택 사항이며 검증되지 않은 명령 오버라이드예요. tool-call parser는 별도로 기능 검증되었어요.
- 의도적 생략: context parallelism은 xLLM에서 지원되지 않거나 검증되지 않았어요. DP-attention과 대체 MoE 백엔드 셀렉터는 검증되기 전까지 노출되지 않아요.
- Validation: 여섯 레시피 모두 PR #37654의 K2 Horizon 런타임 지원으로 H200에서 네이티브 속도와 전체 GSM8K 평가를 완료했어요.
- 375B-A23B provenance: 375B-A23B 벤치마크는 직접 마운트된 체크포인트를 시작하고 revision
12812264242a14dce44aa7ae27f931ff4584bcbf를 서버에 전달했어요. 나머지 다섯 개 실행은 고정된 revision의 materialized Hugging Face 스냅샷을 사용했어요.
3. Benchmark results
속도는 고정된 랜덤 토큰 ID(8,192 입력 토큰, 1,024 출력 토큰, 64 워밍업 요청, 캐시 플러시, seed 20260901)를 사용해 네이티브 sglang.benchmark.serving으로 측정했어요. 지연 시간 실행은 concurrency 1에서 32 프롬프트를 사용했어요. 처리량 실행은 TP1에서는 concurrency 64로, TP2에서는 32로, TP8에서는 8로 각각 256 프롬프트를 사용했어요. TTFT와 TPOT은 각 실행 내에서의 중앙값이며, 벤치마크 카드는 두 실행에 걸친 산술 평균을 보여줘요. GPU당 처리량은 (total input tokens + total output tokens) / duration / TP예요.
GSM8K는 sgl-eval로 전체 1,319-예제 테스트 스플릿, 32 클라이언트 스레드, 32,768-토큰 출력 제한, temperature 0, top-p 0.95, seed 0, 높은 reasoning effort를 사용했어요. 평균과 두 독립 서버 실행 점수가 아래에 나와요. 모든 요청이 오류 없이 완료됐어요. 출력 제한에 도달한 응답은 점수에 남아 있어요: 잘림은 0.9B 두 실행 모두에서 6.90%, 3.7B에서 4.09%와 4.70%, 나머지 체크포인트에서는 최대 1.14%였어요.
| Checkpoint | Launch 1 | Launch 2 | Mean |
|---|---|---|---|
| 0.9B | 85.22% | 85.29% | 85.25% |
| 3.7B | 92.12% | 91.89% | 92.00% |
| 7B | 95.30% | 94.47% | 94.88% |
| 32B | 95.75% | 96.21% | 95.98% |
| MoVA-36B-A4B | 95.45% | 94.84% | 95.15% |
| 375B-A23B | 95.53% | 95.60% | 95.56% |
4. Advanced usage
4.1 Reasoning
기본 레시피에는 --reasoning-parser k2_horizon이 포함돼요. 이 parser는 thinking을 reasoning_content로, 최종 답을 content로 반환해요.
아래 예제 출력은 3.7B 체크포인트로 두 parser 플래그를 모두 켜고 캡처한 결과예요.
from openai import OpenAI
client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
response = client.chat.completions.create(
model="IFM/K2-Horizon-3.7B",
messages=[{"role": "user", "content": "Hello"}],
)
message = response.choices[0].message
reasoning = getattr(message, "reasoning_content", None)
if reasoning is None:
reasoning = (message.model_extra or {}).get("reasoning_content")
print("Reasoning:", reasoning)
print("Answer:", message.content)
Reasoning: We have a user who just said "Hello". We need to respond. As an AI, we should be friendly, perhaps ask how can we help. If says "Hello" alone, respond with greeting and offer assistance. Potentially ask what they need. There's no special instruction; just typical.
But maybe we must think about the context: The user gave just a greeting; we should respond with a greeting and ask how we can help. Could also ask if they want anything specific.
We should be concise but friendly: "Hello! How can I assist you today?" Provide some content.
But maybe the user expects something else. But in this scenario, we just reply with a greeting.
Thus answer: "Hello! What can I do for you?" Etc.
Check any style guidelines: We want to keep it short or perhaps ask follow-up. The user didn't ask any question, just saying hello. Could also respond: "Hello! ..."
Thus arguably the answer: "Hi there! How can I help you today?" Let's do that.
But also maybe we could ask if they need help with something specific. That's appropriate.
Thus the final answer: "Hello! How can I assist you today?" Possibly add friendly banter.
We'll respond.
Answer:
Hello! 👋 How can I help you today?
4.2 Tool calling
Playground의 Parsers 카드에서 Tool Call Parser를 켜면 --tool-call-parser k2_horizon이 추가돼요. 이 parser는 K2 tool 출력을 OpenAI 호환 message.tool_calls로 노출해요.
아래 예제 출력은 32B 체크포인트로 두 parser 플래그를 모두 켜고 캡처한 결과예요.
from openai import OpenAI
client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Return weather for a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}
]
response = client.chat.completions.create(
model="IFM/K2-Horizon-32B",
messages=[{"role": "user", "content": "Call get_weather for Paris."}],
tools=tools,
tool_choice={"type": "function", "function": {"name": "get_weather"}},
max_tokens=1024,
)
message = response.choices[0].message
reasoning = getattr(message, "reasoning_content", None)
if reasoning is None:
reasoning = (message.model_extra or {}).get("reasoning_content")
print("Reasoning:", reasoning)
print("Content:", message.content or "")
for tool_call in message.tool_calls or []:
print(f"Tool call: {tool_call.function.name}({tool_call.function.arguments})")
Reasoning: Okay, the user wants me to call the get_weather function for Paris. Let me check the tools available. There's a function called get_weather that takes a city parameter. The required parameter is city, and it's a string. So I need to make sure to pass "Paris" as the city. I don't see any other parameters needed. The user didn't mention any other details, so I'll just use the city name. Let me structure the tool call correctly. The function name is get_weather, and the arguments should be a JSON object with "city": "Paris". I need to make sure the JSON is properly formatted. Alright, that should do it.
Content:
Tool call: get_weather({"city": "Paris"})
4.3 HiCache
큰 체크포인트나 반복되는 긴 컨텍스트 prefix에는 Playground에서 HiCache를 켠 다음 스토리지 백엔드와 write policy를 선택해요. 생성된 오버라이드는 저장 스택에서 직접 시험해볼 때까지는 검증되지 않은 것으로 취급하세요.