generate() vs stream_generate()
Lesson 1 의 두 줄이 generate() 썼는데, 끝나면 전체 completion 을 한 문자열로 줘. UI 작업 — chat bubble, 터미널 스트리밍, 토큰이 생성되는 대로 나타나길 원하는 무엇이든 — 에는 stream_generate() 원해. 토큰 당 chunk 하나씩 yield 하는 Python generator.
둘 다 mlx-lm 의 진짜 API. generate() 가 stream_generate() 를 감싸고 chunk 들을 join 하는 얇은 wrapper 로 구현. Latency 가 중요할 땐 streaming 버전, 안 중요할 땐 buffering 버전.
stream_generate 가 yield 하는 것
각 yield 는 raw token 만이 아니라 풍부한 metadata 가진 GenerationResponse 객체. 실제로 쓸 필드:
.text— 이 step 의 새 텍스트 fragment (보통 토큰 하나, 가끔 조각으로 스트림되는 Unicode glyph 의 일부)..finish_reason— 생성 중엔None, 마지막 yield 에'stop'(stop 토큰 hit) 또는'length'(max_tokens hit)..generation_tokens— 지금까지 생성된 토큰 수 누적..generation_tps— 생성 시작 이후 측정된 tokens-per-second; 라이브 throughput 디스플레이에 유용..peak_memory— 이 생성 중 peak GPU 메모리 (byte).
이 metadata 의 풍부함이 mlx-lm 의 조용한 강점 중 하나 — throughput 보려고 별도 profiler 안 필요하고, 왜 생성 멈췄는지 추측 안 해도 돼.
Stop 조건, demystify
세 가지 중 하나가 일어나면 생성 멈춰:
- 모델이 자기 tokenizer 의 EOS 토큰 emit (예 Llama 3 의
<|eot_id|>, Qwen 의<|im_end|>). Tokenizer 가 자기 EOS 알아. mlx-lm 이 거기서 가져와.finish_reason='stop'. max_tokenshit.finish_reason='length'. 두면 모델이 계속 갔을 거.- 수동으로 멈춤 — generator 에서 break 함. 모델은 문장 중간, 특별한 finish_reason 없음 — 그냥 더 이상 yield 없음.
대부분 quickstart 가 안 언급하는 함정 — chat-tuned 모델은 매 턴 EOS 토큰에 의존해서 멈춰. Chat template (lesson 5) 적용 잊으면, 모델이 자연스러운 멈춤 지점에서 EOS emit 안 하는 일 잦고, max_tokens 가 차야 멈추는 폭주 생성 보게 될 거.