~/tools / Локальная LLM на своём сервере / Ollama Stream Reader
Ollama Stream Reader — Соберите потоковый ответ Ollama, узнайте скорость и причину остановки
Вставьте потоковый ответ Ollama, от /api/chat, /api/generate или OpenAI-совместимого /v1/chat/completions, и инструмент соберёт части обратно в полный текст, thinking и вызовы инструментов. Он покажет, почему модель остановилась, как быстро она генерировала и обрабатывала промпт и был ли поток оборван, а также выдаст сообщение ассистента для следующего запроса. Данные не покидают ваш браузер.
Как сохранить поток с помощью curl
-N отключает буферизацию curl, а > stream.txt сохраняет поток в файл. Замените модель и сообщение на свои.
curl -sN http://localhost:11434/api/chat -d '{"model":"qwen2.5:7b","messages":[{"role":"user","content":"Hello"}]}' > stream.txt
curl -sN http://localhost:11434/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"qwen2.5:7b","stream":true,"stream_options":{"include_usage":true},"messages":[{"role":"user","content":"Hello"}]}' > stream.txtПрочитано как поток /api/chat (по одному JSON-объекту в строке).
Сводка
- Модель
- qwen2.5:7b
- Причина остановки
- stop
- Фрагменты
- 7
- Сгенерированные токены
- 7
- Скорость генерации
- 10.4 токенов/с
- Токены промпта
- 35
- Скорость промпта (токены не из кэша)
- 64.1 токенов/с
- Время загрузки модели
- 4.923 с
- Общее время
- 6.310 с
- От первого до последнего фрагмента
- 0.675 с
Текст
Hello, nice to meet.
Сообщение ассистента для следующего запроса
Добавьте его в messages, а за ним результаты вызовов инструментов, если они были.
{
"role": "assistant",
"content": "Hello, nice to meet."
}eval_count учитывает и токены вызовов инструментов, которые не передаются потоком как текст. В нашем тесте один вызов инструмента пришёл одним фрагментом с eval_count 20.
В документации Ollama сказано, что prompt_eval_duration охватывает только те токены промпта, которых не было в кэше. Скорость промпта здесь делит только их. Все длительности даны в наносекундах; в форме OpenAI timings даны в целых миллисекундах.
В OpenAI-совместимой форме число токенов отправляется только при stream_options.include_usage, в дополнительном фрагменте перед data: [DONE]. Если поток оборван, этот фрагмент не приходит.
Форматы по документации Ollama о потоковой передаче и usage и по её исходному коду (v0.40.15), проверены на настоящих потоках Ollama 0.40.11 2026-11-07. Ollama: Streaming
Всё выполняется в вашем браузере. Инструмент ни к чему не подключается.
Ollama по умолчанию передаёт ответы потоком. /api/chat и /api/generate отправляют по одному JSON-объекту в строке (newline-delimited JSON): каждая строка содержит несколько символов ответа, а в последней строке done равно true, и в ней есть число токенов и длительности в наносекундах. OpenAI-совместимый /v1/chat/completions передаёт поток, только если stream равно true. Тогда он отправляет server-sent events: строки, начинающиеся с data:, отдельный фрагмент с пустым delta и finish_reason, а в конце data: [DONE]. Число токенов приходит, только если запрос задаёт stream_options.include_usage, в дополнительном фрагменте с пустым списком choices, и Ollama добавляет к нему объект timings. Этот инструмент читает все три формы, включая вывод curl -i с заголовками HTTP, склеивает content, thinking (reasoning в форме OpenAI) и вызовы инструментов и перечисляет, что не так: поток без строки done или без [DONE] был оборван; строка {"error": …} означает, что Ollama остановилась с ошибкой уже после того, как начала отправку; done_reason length означает, что достигнут num_predict или max_tokens. Скорость генерации равна eval_count, делённому на eval_duration. Для промпта в документации Ollama сказано, что prompt_eval_duration охватывает только те токены промпта, которых не было в кэше, поэтому инструмент делит на это время только их. В форме OpenAI собственное timings.prompt_per_second у Ollama делит на это время все токены промпта, включая взятые из кэша. В нашем тесте оно показало 360.8 токена в секунду для промпта, в котором 34 из 35 токенов пришли из кэша, а один оставшийся токен занял 97 ms. Вызовы инструментов в форме OpenAI могут приходить частями с общим index, поэтому инструмент склеивает аргументы по index; сама Ollama отправляет каждый вызов инструмента целиком, одним фрагментом. Мы проверили инструмент на настоящих потоках Ollama 0.40.11 с qwen2.5:7b, а форматы сверили с документацией и исходным кодом Ollama.
Основная логика этого инструмента открыта под лицензией MIT. github.com/seigo5555/toolkitbox-lib/src/ollama-stream.ts
Когда пригодится
- Увидеть весь ответ потокового запроса, сделанного через curl, вместо чтения по кусочку в каждой строке
- Выяснить, был ли поток оборван, остановился ли на лимите длины или закончился ошибкой
- Измерить скорость генерации и обработки промпта модели на вашем компьютере или сервере
Как пользоваться
- Выполните запрос с curl -sN и сохраните вывод в файл или скопируйте то, что получила ваша программа.
- Вставьте весь поток. Инструмент определит, откуда он: от /api/chat, /api/generate или /v1/chat/completions.
- Прочитайте проблемы и числа и скопируйте текст или сообщение ассистента для следующего запроса.
Вопросы
Почему в моём потоке /v1/chat/completions нет числа токенов?
В OpenAI-совместимой форме числа отправляются, только если запрос задаёт stream_options равным {"include_usage": true}. Тогда они приходят в дополнительном фрагменте с пустым списком choices, перед самым data: [DONE]. /api/chat и /api/generate всегда кладут их в последнюю строку.
Почему скорость промпта так сильно отличается от timings.prompt_per_second?
В документации Ollama сказано, что prompt_eval_duration охватывает только те токены промпта, которых не было в кэше. timings.prompt_per_second у Ollama делит на это время все токены промпта, включая взятые из кэша, поэтому при использовании кэша значение выглядит намного быстрее. Инструмент делит только токены, которых не было в кэше.
Модель вызвала инструмент, но eval_count равно 20. Где эти токены?
Текст вызова инструмента не передаётся потоком как content; Ollama превращает его в tool_calls и отправляет одним фрагментом. Токены всё равно генерируются и считаются. В нашем тесте один вызов инструмента с одним аргументом пришёл одним фрагментом с eval_count 20.
Почему /api/chat возвращает stop, когда модель вызвала инструмент?
/api/chat оставляет done_reason равным stop. OpenAI-совместимый эндпоинт меняет stop на tool_calls в finish_reason, если был отправлен вызов инструмента.
Что делать с сообщением ассистента?
В документации Ollama отмечено, что для сохранения истории разговора нужно склеить части потока, что особенно важно для вызовов инструментов. Добавьте сообщение в messages, добавьте результат каждого вызова инструмента как сообщение с ролью tool и отправьте следующий запрос.
Отправляются ли мои данные куда-либо?
Нет. Поток читается в вашем браузере. Команды curl — это лишь текст, который вы запускаете сами.
Похожие статьи
- Следим за памятью Ollama на небольшом VPSЧто Ollama держит в памяти, как долго и какие настройки это меняют: ollama ps, keep_alive и лимиты по официальному FAQ.
- Подключаем Dify к Ollama на собственном VPSЗапустите Dify на своём VPS и используйте Ollama как модель: установка, поля плагина, проблема адреса в Docker и эмбеддинги — по официальной документации.
- Локальная LLM без дорогого ПК: руководство для начинающих по работе через VPSНет мощного ПК? Арендуйте VPS и запустите небольшую локальную LLM из браузера: шесть шагов от оценки памяти до защищённого экрана чата, с честным описанием ограничений.