Перейти к содержанию
CADCAMFUN
Документы

Документация

Создание клиента потокового помощника

Откройте ход, визуализируйте его реальные события, ответьте на ожидающие запросы и восстановите разговор после разрыва соединения.

Автоматически переведено с английского. Смотреть оригинал на английском

Подключитесь к работающей сайдкаре

Клиент-помощник по потоковой передаче подключается к той же локальной сайдкаре, что и рабочий стол. Получите назначенный URL-адрес обратной связи и текущий токен носителя приложения из соединения, с которым вы интегрируетесь. Порт, сохраненный со вчерашнего запуска, не является постоянным адресом: сайдкар запрашивает временный порт при каждом запуске. Токен сеанса учетной записи, токен сопряжения компаньона и токен Action-Bridge с ограниченной областью действия имеют разные цели и не могут заменить носитель приложения на этом маршруте чата.

Прежде чем отправлять ход, прочтите GET /api/ai/status, /api/ai/providersи необязательно /api/ai/models?configuredOnly=true. Настроенные учетные данные означают, что ключ присутствует; это не доказывает, что поставщик примет запрос или что конечная точка поддерживает выбранную модель. Хост по умолчанию — встроенный nikcli. Прямой режим может передавать в потоковом режиме ответ модели, но не обеспечивает постоянный инструментальный цикл встроенного хоста или специальный планировщик.

В этом руководстве используются CADCAMFUN_URL и CADCAMFUN_TOKEN как переменные оболочки клиента. Предоставьте их через существующую обработку соединений. См. Справочник по API ИИ точные схемы и Сайдкар для поведения при запуске. В примерах не устанавливаются учетные данные и не выбирают модель от вашего имени.

Выберите проект, чертеж и идентификатор сеанса

Клиент может предоставить sessionId в первый ход и повторно использовать его в последующих ходах. Служба сопоставляет этот идентификатор клиента с фактическим сеансом хоста и проверяет привязку перед каждым запросом. Сохраняйте идентификатор события/сеанса как состояние разговора, вместо того, чтобы создавать новый идентификатор каждый раз при повторном подключении пользовательского интерфейса.

Настоящий projectId дает очередь доступ к разрешенным ресурсам этого проекта. Дополнительный drawingId должен принадлежать этому проекту; он привязывает инструменты поворота к этому рисунку. Чертеж не может быть назван без его проекта. Сеанс, уже принадлежащий другой учетной записи или проекту, отклоняется. Это проверки допуска, прежде чем модель сможет использовать инструменты проекта, а не подсказки в системной подсказке.

Для общей объяснительной беседы проект и рисунок опускаем. Приложение использует контекст по умолчанию. Для работы с геометрией передавайте фактически выбранные идентификаторы проекта/чертежа через чат, восстановление состояния, очередь, отмену и запросы управления. Переключение учетной записи не должно автоматически перенимать активный сеанс предыдущей учетной записи.

Отправить потоковую передачу

POST /api/ai/chat/stream возвращает тело SSE. Для этого требуется непустое сообщение, хотя транспортная схема делает несколько полей необязательными, чтобы обработчики могли возвращать читаемые сообщения проверки. Следующий общий вопрос отключает внедрение сохраненного контекста, что делает его пригодным для проверки транспорта независимо от геометрии проекта.

Первый потоковый запрос
curl --no-buffer --fail-with-body \
  -H "Authorization: Bearer $CADCAMFUN_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"message":"Explain the difference between G0 and G1.","sessionId":"inspection-demo","includeContext":false,"mode":"gcode"}' \
  "$CADCAMFUN_URL/api/ai/chat/stream"

Добавлять projectId и drawingId работать над конкретным рисунком. mode принимает общие значения, CAD, CAM, Gcode или траекторию инструмента. А model селектор может содержать поставщика, модель, уровень и вариант; уровни быстрые, сбалансированные или лучшие. Найдите идентификаторы поставщиков/моделей из текущего каталога вместо того, чтобы жестко запрограммировать предположение о том, какой поставщик доступен.

permissionMode принимает «Одобрить», «Спросить» или «Авто». Группы деструктивных инструментов сохраняют одобрение людей в любом режиме. agent поле выбирает настроенную роль помощника; читать GET /api/ai/agents вместо того, чтобы угадывать идентификаторы ролей.

Чтение полных кадров данных SSE

В маршруте используется режим данных SSE Effect: каждая полезная нагрузка данных SSE представляет собой запись события JSON с type дискриминатор. Сетевой фрагмент не является границей события. JSON, окончания строк и символы UTF-8 можно разделить между операциями чтения. Накапливайте декодированный текст и анализируйте только полные кадры, объединяя несколько строк данных, если они есть.

Минимальное средство чтения кадров данных JSON.
async function* events(response: Response): AsyncGenerator<Record<string, unknown>> {
  if (!response.ok) throw new Error(await response.text())
  if (!response.body) throw new Error("The response has no stream")
  const reader = response.body.getReader()
  const decoder = new TextDecoder()
  let pending = ""
  try {
    while (true) {
      const next = await reader.read()
      pending += decoder.decode(next.value, { stream: !next.done })
      let boundary: RegExpExecArray | null
      while ((boundary = /\r?\n\r?\n/.exec(pending)) !== null) {
        const frame = pending.slice(0, boundary.index)
        pending = pending.slice(boundary.index + boundary[0].length)
        const data = frame.split(/\r?\n/)
          .filter((line) => line.startsWith("data:"))
          .map((line) => line.slice(5).replace(/^ /, "")).join("\n")
        if (!data) continue
        const event: unknown = JSON.parse(data)
        if (!event || typeof event !== "object" || !("type" in event)) {
          throw new Error("Invalid assistant event")
        }
        yield event as Record<string, unknown>
      }
      if (next.done) break
    }
  } finally {
    await reader.cancel().catch(() => {})
    reader.releaseLock()
  }
}

Этот читатель демонстрирует кадрирование; производственный клиент также должен проверять каждое событие на соответствие общему контракту или использовать типизированный сгенерированный потоковый клиент. Он намеренно игнорирует поля комментариев/управления SSE, поскольку состояние приложения этой конечной точки находится в записях данных JSON. Освобождение клиентского потока — это очистка транспорта, а не явный запрос на прекращение работы на стороне сервера.

Рендеринг прогресса без дублирования контента

Запустите выборку с тем же носителем и телом JSON, показанными выше, затем выполните итерацию средства чтения. Добавить delta.text к видимому тексту ответа и маршруту reasoning.text на свой взгляд. А done событие содержит окончательный текст ответа; замените или согласуйте накопленный ответ с этим окончательным значением вместо того, чтобы снова добавлять весь ответ.

СобытияГосударство для поддержания
turn, session_status, stepСостояние занятости/повторной попытки и текущий шаг; за завершением шага может последовать еще один шаг в том же ходу.
tool_call, tool_progress, tool_resultЗапись по callId с именем, вводом, ходом аргумента, выводом и успехом; агент идентифицирует специалиста при его присутствии.
tool_diagnosticВыводы аргумента/выполнения/частичной проверки/проверки, серьезность, текст восстановления и необязательная мутацияApplied.
todo, goalЗамените текущий снимок плана/цели, а не добавляйте повторяющиеся копии.
retry, compaction, compactedОбъясните продолжение работы, сохраняя при этом текущий текст разговора и идентификатор сеанса.
heartbeatНет видимого контента; сервер отправляет один примерно каждые пять секунд, пока исходный поток остается активным.

Сопоставьте вызовы инструментов и результаты по callId, а не их позиция в массиве. Неисправный инструмент может возвращать полезную диагностику, пока помощник продолжает работу. Ошибка поставщика после успешного использования инструмента редактирования не отменяет изменение геометрии.

Отвечать на разрешения и вопросы

A permission_request включает sessionId, requestId, разрешение и шаблоны. Предъявите запрос человеку и отправьте выбранное решение на POST /api/ai/permission/reply. Эквивалентное действие по управлению сеансом: reply_permission. Сервер проверяет, что запрос все еще находится на рассмотрении в диалоге или в разрешенном дочернем сеансе.

Текст ответа на разрешение
{
  "sessionId": "inspection-demo",
  "requestId": "request-id-from-the-event",
  "decision": "once"
}

Решение — один раз, всегда или отклонить. Это решение о разрешении сервера, отличное от аргумента действия, такого как подтверждение. Ожидаемый question_request содержит упорядоченный массив вопросов. Ответить через /api/ai/sessions/control с действием answer_question, идентификатор запроса и один массив ответов на вопрос; отклонить это с reject_question.

Ответьте на один вопрос с одним выбором
{
  "sessionId": "inspection-demo",
  "action": "answer_question",
  "requestId": "question-id-from-the-event",
  "answers": [["The selected option label"]]
}

Каждый ответ должен быть непустым. Вопрос с одним выбором отклоняет несколько ответов, а параметр custom=false допускает только перечисленные метки. Сохраняйте точные метки и порядок вопросов. Если сервер сообщает, что запрос больше не находится на рассмотрении, обновите элементы управления вместо повторной публикации старого ответа.

Ставьте в очередь, управляйте и явно останавливайте работу

Использовать POST /api/ai/chat/queue за сообщение, отправленное во время работы очереди. Он принимает идентификатор сеанса, сообщение, необязательный идентификатор проекта и доставку. queue используется по умолчанию и ожидает следующего безопасного шага; steer просит хоста включить его как можно скорее. А user_message событие сигнализирует о том, что сообщение вошло в разговор.

Использовать POST /api/ai/chat/abort с идентификацией разговора, когда человек нажимает «Стоп». Это останавливает ход и присоединяет связанных с ним рабочих. Не реализуйте Stop исключительно путем отмены выборки: потеря HTTP-потока — это не та же операция. Затем сеанс может принять еще один ход без создания нового идентификатора проекта.

POST /api/ai/sessions/remove удаляет сеанс хоста после отмены и присоединения к его дочерним элементам. Он возвращает удаленный флаг. Удаление беседы не является откатом изменений проекта, внесенных ее инструментами; при необходимости просмотрите или восстановите их через API проекта/истории.

Восстановление после потери потока

Когда программа чтения завершается без финального события, считайте завершение неизвестным. Вызов POST /api/ai/sessions/state с тем же идентификатором сеанса и идентификатором проекта. Ответ содержит занято и текст, а также дополнительные восстановленные события инструмента/задачи/диагностики, использование и задачи. Восстановите пользовательский интерфейс из этого снимка; воспроизведение восстановленных событий инструмента не приводит к их повторному выполнению.

Затем запросите /api/ai/sessions/control со статусом действия для проверки ожидающих разрешений/вопросов, рабочих задач, целей и контекстной политики. Занятая очередь в ожидании вопроса без ответа требует ответа, а не второй копии исходного запроса. Сеанс, о котором сообщается, что он простаивает, с накопленным текстом, может отображаться как восстановленный вывод, даже если исходное TCP-соединение исчезло.

Следует различать сбой сети, событие типизированной ошибки поставщика/конфигурации, явное прерывание и удаление сеанса. Повторите запрос модели только после проверки того, что на самом деле сделал первый ход. В частности, читать текущий чертеж после частичной диагностики мутации или прерванного редактирования рабочим.

Внутри сервера разорванное соединение модели возобновляется до пяти раз с задержкой в 2 секунды, которая удваивается до 30 секунд. Клиент, повторно подключающийся к потоку, все равно должен читать состояние сеанса, поскольку эти возобновления могут завершить работу, которую клиент не видел в потоке.

Контекст, вложения и сжатие

includeContext: false отключает получение сохраненного контекста; явный контекстный текст все еще может быть предоставлен для этого хода. Обычная сборка заказывает постоянные требования, текущие наблюдения, снимки пользовательского интерфейса, память и вспомогательные ссылки, дедуплицирует их и применяет 24,000-символьный совокупный бюджет. Сообщается о пропущенных/усеченных блоках; этот бюджет не является окном токена провайдера.

Вложения включают имя, необязательный MIME, данные и необязательную текстовую/базовую64 кодировку. Их не более пяти на запрос. Изображения ограничены 5 МБ каждое, PDF-файлы — 10 МБ, а текст — 2,000,000 символов; короткий текст длиной до 6,000 символов также отображается внутри строки. Текст, сохраняемый для инструментов, хранится в памяти проекта, поэтому повторное подключение отличается от перезапуска службы. Неподдерживаемые файлы отклоняются с указанием причин.

Контекст управляющего действия запрашивает подробную разбивку токенов; обычный опрос статуса позволяет избежать работы токенизатора. Action Compact просит ведущего обобщить старый материал разговора. Консолидация памяти — это отдельная операция хоста. Сохраняйте исходные запросы на чертежи и производство через собственные API, а не полагайтесь на сжатый диалог для воспроизведения точной геометрии.

Отделяйте транспортные ошибки от результатов домена

HTTP 401 означает, что запрос не прошел авторизацию носителя. 400 перед потоковой передачей обычно означает пустое сообщение, недоступный идентификатор сеанса, недопустимую комбинацию проекта/чертежа или неправильно сформированное тело. После установления потока обработчик может выдать событие ошибки с конфигурацией, поставщиком, прерванной или внутренней классификацией; обработать это событие, даже если статус ответа был 200.

Результат инструмента с ok=false — это результат инструмента, а не обязательно неудачный поток. Покажите его выходные данные и диагностику, чтобы помощник или человек мог восстановиться. Рабочая очередь также может быть полной: планировщик допускает четыре активных и шестнадцать ожидающих задач. Прерванное чтение может быть восстановлено за ограниченное количество попыток; работа по редактированию рассматривается как неопределенная, а не воспроизводится автоматически.

Прочтите исполняемые контракты в packages/api/src/groups/ai.ts и ai-events.ts; прием и проводное поведение в apps/sidecar/src/http/ai.ts; отображение сеансов в packages/ai/src/service.ts; проверка вопроса в nikcli/session-controls.ts; время сердцебиения в heartbeat.ts. Эти источники определяют, какой результат должен представить клиент, независимо от прозы ассистента.

Ссылка на событие

Каждый SSE data: фрейм — это один объект JSON с type и sessionId. Разбирать только полные кадры; кадр может прийти разделенным на сетевые фрагменты.

типКлючевые поляКак справиться
turnphase: start, recovering, continuing, finish; необязательный reasonПоказать состояние прогресса. recovering означает, что соединение с моделью возобновляется.
session_statusstatus: busy, idle, retryВключите или отключите кнопку отправки и очередь.
user_messageсохраненное сообщение пользователяСогласуйте оптимистичный интерфейс.
deltatextДобавьте к видимому ответу.
reasoningтекст рассужденияНеобязательный; показать в складной области.
step, todo, goalпланировать прогрессОтобразите список задач или план.
tool_callcallId, инструмент, аргументы, необязательно agentПокажите, какой инструмент работает и для какого специалиста.
tool_progresscallId, прогрессОбновите долго работающий инструмент.
tool_resultcallId, результатПоказать результат; сопоставить это с callId.
tool_diagnosticдетали, включая mutationAppliedРешите, изменилась ли модель. Прочтите его, прежде чем повторять редактирование.
permission_request / permission_resolved / permission_blockedrequestId; отвечать once, always, rejectСпросите пользователя, затем POST /api/ai/permission/reply.
question_request / question_resolvedданные вопросаЗадайте вопрос и отправьте ответ.
context_snapshot, compaction, compactedсостояние контекстаСообщите пользователю, что была обобщена старая история.
retryattempt, messageПоказать временное уведомление; не отправляйте приглашение повторно.
donetext, необязательный usage, необязательный recoveredИтоговый текст хода. Замените им накопившиеся дельты.
errorcode: provider, config, aborted, internal; messageЗавершите ход в пользовательском интерфейсе сообщением; config обычно означает отсутствие учетных данных.
heartbeatниктоПоддержание активности примерно каждые 5 секунд; игнорировать.

Полноценный потоковый клиент

stream-turn.ts
const base = process.env.CADCAMFUN_URL!       // e.g. http://127.0.0.1:53211
const token = process.env.CADCAMFUN_TOKEN!

const response = await fetch(`${base}/api/ai/chat/stream`, {
  method: "POST",
  headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
  body: JSON.stringify({ projectId: "p1", message: "Summarise this project", mode: "general" }),
})
if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`)

const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
let buffer = "", answer = ""
for (;;) {
  const { value, done } = await reader.read()
  if (done) break
  buffer += value
  let end
  while ((end = buffer.indexOf("\n\n")) >= 0) {        // one SSE frame
    const frame = buffer.slice(0, end); buffer = buffer.slice(end + 2)
    const data = frame.split("\n").filter((l) => l.startsWith("data:"))
                      .map((l) => l.slice(5).trimStart()).join("\n")
    if (!data) continue
    const event = JSON.parse(data)
    switch (event.type) {
      case "delta": answer += event.text; break
      case "permission_request": /* ask, then POST /api/ai/permission/reply */ break
      case "done": answer = event.text; console.log(answer); break
      case "error": throw new Error(`${event.code}: ${event.message}`)
    }
  }
}

Если соединение прервется, не отправляйте сообщение снова. Прочитайте сессию с POST /api/ai/sessions/state, поскольку сервер мог возобновить соединение модели и завершить ход без вас. Использовать /api/ai/chat/abort намеренно остановить поворот и /api/ai/chat/queue чтобы добавить сообщение, пока сеанс занят.

Как выглядит поток без провайдера

Если поставщик искусственного интеллекта не настроен, поток по-прежнему открывается и отвечает типизированной ошибкой, за которой следует контрольный сигнал. Снято со свежей сайдкара:

POST/api/ai/чат/поток
data: {"type":"error","sessionId":"","code":"config","message":"No AI provider is configured. Add an API key in the AI settings."}

data: {"type":"heartbeat"}
  • Статус HTTP по-прежнему 200, а тип контента text/event-stream: проблемы конфигурации — это результаты предметной области, представленные в виде событий.
  • sessionId пуст, поскольку сеанс не был создан.
  • heartbeat фреймы поддерживают соединение; игнорируйте их при рендеринге и не считайте прогрессом.
  • Каждый кадр заканчивается пустой строкой. Разбор путем разделения на пустые строки и объединения data: строки кадра, как это делает итератор SDK.

Протестируйте свой клиент без модели

Направьте клиента на вспомогательную машину без учетных данных: он бесплатно проверяет полный путь SSE и ветвь ошибок.

Через 3Д

Хранение разговоров Перетащите на орбиту · прокрутите, чтобы увеличить · перетащите вправо, чтобы панорамировать
Разговор, Сообщение и связанные таблицы с их внешними ключами из миграций.