Async generator 패턴
Node 20+랑 요즘 브라우저에서 Ollama NDJSON을 받아먹는 정석은 이 셋의 조합이야. fetch().body가 주는 ReadableStream, TextDecoder, 그리고 직접 굴리는 newline buffer. 이걸 async function* generator로 감싸두면 바깥에서는 for await 한 줄로 stream을 돌 수 있어.
감싸는 게 왜 남는 장사냐면, 소비하는 쪽이 HTTP도 buffer도 몰라도 되기 때문이야. 터미널에 찍든, DOM 노드에 붙이든, 다른 stream으로 넘기든 for await는 똑같아. Python 쪽 iter_lines() 패턴이랑 정확히 같은 자리에 서는 조각이고, 그래서 두 언어의 client가 같은 모양으로 생기게 돼.
Buffer 패턴 (절대 skip 금지)
Network chunk는 JSON 줄 경계랑 안 맞아. read() 한 번이 이렇게 돌아올 수 있어:
'{"a":1,"b":2}\n{"c":3,'— 마지막 줄 미완성'4}\n{"e":5}\n'— 첫 부분이 이전 줄 완성
그래서 순서가 정해져 있어. Buffer에 쌓고, \n로 자르고, 완성된 줄만 전부 parse하고, 잘린 tail은 buffer에 남겨둬. 네 단계 중 마지막 하나를 빼먹는 게 제일 흔한 실수야. 짧은 답변으로 test하면 read가 한 번에 끝나서 tail이 안 생기고, 그래서 버그가 멀쩡히 통과해. 그러다 답이 길어지는 순간 토큰이 조용히 사라지지. TypeScript Ollama client에서 버그가 제일 많이 나는 자리가 정확히 여기야.