n8n-nodes-teamorouter/README.md

380 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# n8n-nodes-teamorouter
Кастомные узлы n8n для шлюза **TeamoRouter** (`https://api.teamorouter.com/v1`) — OpenAI-совместимого роутера к GPT, Claude, Gemini, DeepSeek, GLM, Grok, Kimi и другим моделям.
Пакет содержит два узла и один тип учётных данных:
| Узел | Тип | Назначение |
| --- | --- | --- |
| **TeamoRouter Chat Model** (`lmChatTeamRouter`) | sub-node, `ai_languageModel` | Подключается к **AI Agent**, **Basic LLM Chain**, **Question and Answer Chain** и любым другим узлам, которые принимают Language Model |
| **TeamoRouter** (`teamorouter`) | обычный узел | Три операции: `Chat → Message a Model`, `Image → Generate an Image`, `Model → List Models` |
| **TeamoRouter API** (`teamorouterApi`) | credentials | API-ключ + Base URL |
---
## 1. Установка
### Вариант А. Через интерфейс n8n (рекомендуется)
Community nodes в n8n ставятся из npm, поэтому пакет должен быть опубликован:
1. Выгрузите папку в свой git-репозиторий.
2. Опубликуйте в npm:
```bash
npm install
npm run build
npm publish --access public
```
3. В n8n: **Settings → Community nodes → Install a community node**, введите `n8n-nodes-teamorouter`, подтвердите предупреждение.
4. Перезапускать n8n не нужно — узлы появляются сразу.
Нужна роль Owner/Admin и включённые community nodes. Community nodes ставятся на весь инстанс.
### Вариант Б. Копированием папки (если есть доступ к серверу n8n)
1. `npm install && npm run build` — на выходе нужна папка `dist/`.
2. Положите **всю папку пакета** (в ней обязателен `package.json`) в каталог пользовательских расширений, например:
* в Docker: `/home/node/.n8n/custom/n8n-nodes-teamorouter/`
* через переменную окружения: `N8N_CUSTOM_EXTENSIONS=/path/to/custom-nodes`, а внутри уже папка пакета
3. Внутри папки пакета один раз выполните `npm install --omit=dev`, чтобы `n8n-workflow` (peer-зависимость) был доступен из вашей папки.
4. Перезапустите n8n.
### Вариант В. Локальная разработка
```bash
npm install
npm run build:watch # tsc в режиме наблюдения
export N8N_CUSTOM_EXTENSIONS="$(pwd)"
npx n8n start
```
---
## 2. Учётные данные
**Settings → Credentials → New → TeamoRouter API**
| Поле | Значение |
| --- | --- |
| **API Key** | ключ из панели TeamoRouter, начинается с `sk-teamo-` |
| **Base URL** | `https://api.teamorouter.com/v1` (по умолчанию; меняйте только для зеркала `.cn` или своего роутера) |
Кнопка проверки делает `GET /v1/models` с заголовком `Authorization: Bearer <ключ>`.
---
## 3. TeamoRouter Chat Model (для AI Agent)
1. Добавьте **AI Agent** (или **Basic LLM Chain**).
2. В слот **Chat Model** добавьте **TeamoRouter Chat Model**.
3. Выберите модель из списка — он читается вживую из `GET /v1/models`. В подписи к модели видны провайдер и цена за 1M токенов (если роутер их отдаёт).
### Опции
| Опция | Что делает |
| --- | --- |
| **Sampling Temperature** | случайность ответа, 0…2 |
| **Maximum Number of Tokens** | лимит генерации; `-1` = решает модель |
| **Top P** | nucleus sampling; меняйте либо temperature, либо topP |
| **Force JSON Response** | просит модель отвечать объектом JSON (в промпте должно быть слово JSON) |
| **Service Tier → Fast** | режим Fast у TeamoRouter: до 2,5× быстрее, тарифицируется ×2 |
| **Extra Body** | произвольные JSON-поля в тело запроса, например `{"top_k": 40}`; перекрывает всё выше |
| **Timeout** | по умолчанию 600000 мс (роутер допускает ответы до 600 с) |
| **Max Retries** | повторы при ошибке, по умолчанию 2 |
### Модель, которой нет в списке
Поле **Model** можно переключить в режим выражения (переключатель `Expression` в правом верхнем углу поля) и вписать ID руками:
```
{{ 'some-new-model-id' }}
```
Это пригодится, если модель появилась у роутера, а список в UI ещё не перечитан.
---
## 4. Узел TeamoRouter (обычные операции)
### Chat → Message a Model
Отправляет `POST /v1/chat/completions`. Принимает текст и/или картинки, возвращает ответ модели.
| Поле | Описание |
| --- | --- |
| **Model** | список читается из `GET /v1/models` (только текстовые модели; картинные скрыты) |
| **Prompt** | сообщение пользователя. Можно оставить пустым, если приложены картинки |
| **Input Images** | сколько угодно картинок, каждая — Binary File (из предыдущего узла) или Image URL |
| **Options → System Prompt** | системная инструкция |
| **Options →** остальное | temperature, topP, max tokens, JSON-ответ, Service Tier Fast, Extra Body, Timeout |
| **Chat Reply** | включите, если узел стоит последним после Chat Trigger — ответ уйдёт прямо в чат |
Результат: `{{ $json.text }}` — текст ответа, `{{ $json.usage }}` — токены, `{{ $json.model }}`, `{{ $json.finishReason }}`. Если мультимодальная модель вернула ещё и картинку, она придёт бинарным полем `data` плюс ссылками в `{{ $json.images }}`.
Картинки нужны vision-модели: GPT, Claude, Gemini. Для «что на фото» достаточно **Input Images** и короткого промпта.
### Image → Generate an Image / Edit an Image
`POST /v1/images/generations` — генерация по промпту, `POST /v1/images/edits` — правка готовой картинки (уходит multipart-ом вместе с исходником и, при необходимости, маской).
| Поле | Описание |
| --- | --- |
| **Model** | `gpt-image-2.5-sunburst` для актуальной модели, по умолчанию `gpt-image-2`; можно вписать ID выражением |
| **Prompt** | описание картинки или правки |
| **Input Image (Binary Property)** | только для Edit: бинарное поле с исходной картинкой |
| **Mask (Binary Property)** | только для Edit: маска, прозрачные пиксели которой помечают область правки |
| **Put Output File in Field** | имя бинарного поля с результатом, по умолчанию `data` |
| **Options → Size** | auto, 1024x1024, 1536x1024, 1024x1536, 2048x2048 или своё значение выражением (стороны кратны 16, максимум 3840, соотношение не более 3:1) |
| **Options → Quality** | auto / low / medium / high |
| **Options → Number of Images** | сколько картинок вернуть за один запрос |
| **Options → Output Format / Compression** | png / jpeg / webp и степень сжатия 0–100 |
| **Options → Background** | auto / opaque / transparent (прозрачность — только png и webp) |
| **Options → Input Fidelity / Moderation** | насколько точно следовать исходнику (Edit) / фильтр контента (Generate) |
| **Chat Reply** | ответ для чата: картинка уйдёт в чат как markdown-разметка |
Каждая сгенерированная картинка — отдельный элемент: бинарное поле `data`, `data1`, `data2`… Дальше файл уходит в **Write Binary File**, в Telegram, в S3 и т. д.
С включённым **Chat Reply** узел отдаёт один элемент с полем `output`. Chat Trigger (Response Mode → *When Last Node Finishes*) сразу покажет текст или картинку в чате — Code-узел для этого не нужен. Долгие генерации узел ждёт до 5 минут.
### Model → List Models
Возвращает модели роутера: по одному элементу на модель (`id`, `object`, `owned_by`, `pricing` и др.). Удобно, чтобы построить собственный справочник или посчитать цены.
---
## 5. Известные особенности и ограничения
* **Ответы идут на `/v1/chat/completions`.** Исключение — модель из семейства `gpt-5.6*` и всё, что содержит `codex`: LangChain внутри n8n сам переключает такие ID на `/v1/responses` (это поведение зашито в `@langchain/openai` и не отключается настройками). TeamoRouter поддерживает `/v1/responses`, но **только для GPT-моделей**, поэтому схема рабочая. Для Claude/Gemini запросы идут обычным Chat Completions.
* **Claude через OpenAI-формат не отдаёт prompt caching.** Это ограничение формата роутера, а не узла: кэширование промпта доступно только через нативный протокол Anthropic (в README TeamoRouter это указано отдельно).
* **`max_tokens`, а не `max_completion_tokens`.** Узел отправляет именно `max_tokens` — как в примере из документации TeamoRouter. Если конкретная модель требует `max_completion_tokens`, добавьте его через **Extra Body**.
* **Формат ответа JSON** передаётся через `response_format` — это параметр Chat Completions. Для моделей, которые уходят на `/v1/responses`, он может игнорироваться.
* **Список картинных моделей** определяется по имени: `*-image`, `gpt-image*`, `nano-banana*`, `dall-e*`, `imagen*`. Если роутер назовёт модель иначе, она попадёт в список чата (или наоборот); при этом поле всегда можно заполнить вручную выражением.
* **Цены в списке моделей** — подсказка. Роутер может отдавать цену за токен или уже за 1M токенов; узел считает значения меньше `0.005` как «за токен». Это только отображение, на запросы не влияет.
* У полей **Model** список загружается при открытии, кэша нет: если модель только что появилась, нажмите перезагрузить список или используйте выражение.
---
## 6. Разработка и проверки
```bash
npm install
npm run build # tsc + копирование иконок в dist/
npm test # проверка структуры пакета + тесты узлов на мок-сервере
npm run typecheck
npm run mock # мок TeamoRouter на http://127.0.0.1:4123/v1 (ключ test-key)
```
`npm test` запускает два набора:
1. `test/validate-nodes.mjs` — имитирует загрузку пакета n8n: проверяет описание узлов, наличие иконок, реализацию `loadOptionsMethod`, ссылки на credential, тип соединения `ai_languageModel` у саб-узла.
2. `test/run-node-tests.mjs` — поднимает мок роутера (`test/mock-server.mjs`, без зависимостей) и прогоняет скомпилированные узлы с настоящим HTTP и настоящим `@langchain/openai`: список моделей и фильтрация картинных, `supplyData` + реальный вызов модели, проброс опций (`service_tier`, `extra_body`, лимиты), чат с текстом и картинками (multimodal), правка картинки через multipart, генерация в бинарном виде и в markdown для чата, разбор ошибок 401/402/404, `Continue On Fail` и таймаут.
Тесты не требуют ключа TeamoRouter. Модели `fail-401`, `fail-402`, `fail-404`, `fail-500` в моке принудительно отдают ошибку, `slow-model` отвечает через 2,5 с.
---
## 7. Первый запуск и диагностика
* Пакет собран и проверен автотестами (см. `npm test`). Первый запуск всё равно делайте на тестовом воркфлоу.
* При установке через `Settings → Community nodes` интерфейс может показать предупреждение о сообщественном узле — это нормально.
* Если n8n пишет, что узел не загрузился, проверьте `Help → About` (версия n8n) и логи контейнера: чаще всего причина — недоступная peer-зависимость `n8n-workflow` (Вариант Б, шаг 3).
## Лицензия
MIT
---
---
# English
Custom n8n nodes for the **TeamoRouter** gateway (`https://api.teamorouter.com/v1`) — an OpenAI-compatible router in front of GPT, Claude, Gemini, DeepSeek, GLM, Grok, Kimi and other models.
The package ships two nodes and one credential type:
| Node | Type | Purpose |
| --- | --- | --- |
| **TeamoRouter Chat Model** (`lmChatTeamRouter`) | sub-node, `ai_languageModel` | Plugs into **AI Agent**, **Basic LLM Chain**, **Question and Answer Chain** and any other node that takes a Language Model |
| **TeamoRouter** (`teamorouter`) | regular node | Three operations: `Chat → Message a Model`, `Image → Generate an Image`, `Model → List Models` |
| **TeamoRouter API** (`teamorouterApi`) | credentials | API key + Base URL |
---
## 1. Installation
### Option A. Through the n8n UI (recommended)
Community nodes in n8n are installed from npm, so the package has to be published:
1. Push this folder to your git repository.
2. Publish to npm:
```bash
npm install
npm run build
npm publish --access public
```
3. In n8n: **Settings → Community nodes → Install a community node**, enter `n8n-nodes-teamorouter` and confirm the warning.
4. No restart is needed — the nodes appear right away.
The Owner/Admin role and enabled community nodes are required. Community nodes are installed instance-wide.
### Option B. Copying the folder (if you have access to the n8n server)
1. Run `npm install && npm run build` — you need the resulting `dist/` folder.
2. Put the **whole package folder** (it must contain `package.json`) into the custom extensions directory, for example:
* in Docker: `/home/node/.n8n/custom/n8n-nodes-teamorouter/`
* via an environment variable: `N8N_CUSTOM_EXTENSIONS=/path/to/custom-nodes`, with the package folder inside it
3. Inside the package folder run `npm install --omit=dev` once, so that `n8n-workflow` (a peer dependency) is reachable from your folder.
4. Restart n8n.
### Option C. Local development
```bash
npm install
npm run build:watch # tsc in watch mode
export N8N_CUSTOM_EXTENSIONS="$(pwd)"
npx n8n start
```
---
## 2. Credentials
**Settings → Credentials → New → TeamoRouter API**
| Field | Value |
| --- | --- |
| **API Key** | the key from the TeamoRouter dashboard, it starts with `sk-teamo-` |
| **Base URL** | `https://api.teamorouter.com/v1` (the default; change it only for the `.cn` mirror or your own router) |
The test button issues `GET /v1/models` with the `Authorization: Bearer <key>` header.
---
## 3. TeamoRouter Chat Model (for the AI Agent)
1. Add an **AI Agent** (or a **Basic LLM Chain**).
2. Add **TeamoRouter Chat Model** into the **Chat Model** slot.
3. Pick a model from the list — it is read live from `GET /v1/models`. The label of each model shows the provider and the price per 1M tokens (when the router reports them).
### Options
| Option | What it does |
| --- | --- |
| **Sampling Temperature** | randomness of the answer, 0…2 |
| **Maximum Number of Tokens** | generation limit; `-1` = decided by the model |
| **Top P** | nucleus sampling; change either temperature or topP, not both |
| **Force JSON Response** | asks the model to answer with a JSON object (the prompt must mention JSON) |
| **Service Tier → Fast** | TeamoRouter Fast mode: up to 2.5x faster, billed at 2x |
| **Extra Body** | arbitrary JSON fields for the request body, e.g. `{"top_k": 40}`; overrides everything above |
| **Timeout** | 600000 ms by default (the router allows answers up to 600 s) |
| **Max Retries** | retries on failure, 2 by default |
### A model that is missing from the list
The **Model** field can be switched to expression mode (the `Expression` toggle in the top right corner of the field) and filled with the ID by hand:
```
{{ 'some-new-model-id' }}
```
Useful when the router already serves a new model but the list in the UI has not been reloaded yet.
---
## 4. The TeamoRouter node (regular operations)
### Chat → Message a Model
Sends `POST /v1/chat/completions`. Takes text and/or images and returns the model's answer.
| Field | Description |
| --- | --- |
| **Model** | the list is read from `GET /v1/models` (text models only; image models are hidden) |
| **Prompt** | the user message. May be left empty when images are attached |
| **Input Images** | any number of images, each a Binary File (from an earlier node) or an Image URL |
| **Options → System Prompt** | system instruction |
| **Options →** the rest | temperature, topP, max tokens, JSON answer, Service Tier Fast, Extra Body, Timeout |
| **Chat Reply** | turn it on when the node is the last one after a Chat Trigger — the answer goes straight to the chat |
The answer text is available as `{{ $json.text }}`, the token usage as `{{ $json.usage }}`, plus `{{ $json.model }}` and `{{ $json.finishReason }}`. If a multimodal model answers with an image too, it arrives as the binary field `data` with the links in `{{ $json.images }}`.
Images need a vision model: GPT, Claude or Gemini. For “what is in this picture” the **Input Images** field and a short prompt are enough.
### Image → Generate an Image / Edit an Image
`POST /v1/images/generations` creates an image from a prompt, `POST /v1/images/edits` changes an existing one (sent as multipart together with the source image and, optionally, a mask).
| Field | Description |
| --- | --- |
| **Model** | `gpt-image-2.5-sunburst` for the current model, `gpt-image-2` by default; the ID can be entered as an expression |
| **Prompt** | description of the image, or of the change to apply |
| **Input Image (Binary Property)** | Edit only: the binary field holding the source image |
| **Mask (Binary Property)** | Edit only: a mask whose transparent pixels mark the area to change |
| **Put Output File in Field** | name of the binary property that holds the result, `data` by default |
| **Options → Size** | auto, 1024x1024, 1536x1024, 1024x1536, 2048x2048, or your own value as an expression (sides multiple of 16, at most 3840, aspect ratio at most 3:1) |
| **Options → Quality** | auto / low / medium / high |
| **Options → Number of Images** | how many images to return per request |
| **Options → Output Format / Compression** | png / jpeg / webp and a compression level of 0–100 |
| **Options → Background** | auto / opaque / transparent (transparency needs png or webp) |
| **Options → Input Fidelity / Moderation** | how closely to follow the source (Edit) / the content filter (Generate) |
| **Chat Reply** | chat answer: the image is sent to the chat as markdown |
Every generated image becomes its own item: the binary field `data`, `data1`, `data2`…, so the file can go to **Write Binary File**, Telegram, S3, and so on.
With **Chat Reply** turned on the node returns a single item with an `output` field. A Chat Trigger (Response Mode → *When Last Node Finishes*) then shows the text or the image in the chat — no Code node needed. Long generations are awaited for up to 5 minutes.
### Model → List Models
Returns the models of the router, one item per model (`id`, `object`, `owned_by`, `pricing` and more). Handy for building your own catalogue or for counting costs.
---
## 5. Known details and limitations
* **Requests go to `/v1/chat/completions`.** The exception is the `gpt-5.6*` model family and anything containing `codex`: LangChain inside n8n switches such IDs to `/v1/responses` on its own (this behaviour is hardcoded in `@langchain/openai` and cannot be turned off by settings). TeamoRouter supports `/v1/responses`, but **for GPT models only**, so the scheme works. Claude/Gemini requests use plain Chat Completions.
* **Claude through the OpenAI format gives up prompt caching.** That is a limitation of the router's format, not of the node: prompt caching is available only through the native Anthropic protocol (the TeamoRouter README says so explicitly).
* **`max_tokens`, not `max_completion_tokens`.** The node sends `max_tokens`, exactly as in the TeamoRouter documentation example. If a particular model requires `max_completion_tokens`, add it through **Extra Body**.
* **The JSON response format** is passed as `response_format`, which is a Chat Completions parameter. For models that go to `/v1/responses` it may be ignored.
* **The list of image models** is detected by name: `*-image`, `gpt-image*`, `nano-banana*`, `dall-e*`, `imagen*`. If the router names a model differently, it will land in the chat list (or the other way round); the field can always be filled in by hand as an expression.
* **Prices in the model list** are a hint. The router may report a price per token or already per 1M tokens; the node treats values below `0.005` as “per token”. This is display only and does not affect requests.
* The list of the **Model** fields is loaded when the field is opened and is not cached: if a model has just appeared, reload the list or use an expression.
---
## 6. Development and checks
```bash
npm install
npm run build # tsc + copying the icons into dist/
npm test # package shape validation + node tests against a mock server
npm run typecheck
npm run mock # mock TeamoRouter on http://127.0.0.1:4123/v1 (key: test-key)
```
`npm test` runs two suites:
1. `test/validate-nodes.mjs` — imitates n8n loading the package: checks the node descriptions, the presence of the icons, the implementation of every `loadOptionsMethod`, the credential references, and the `ai_languageModel` connection type of the sub-node.
2. `test/run-node-tests.mjs` — starts the mock router (`test/mock-server.mjs`, dependency-free) and drives the compiled nodes with real HTTP and real `@langchain/openai`: the model list and the filtering of image models, `supplyData` plus a real model call, the passing of options (`service_tier`, `extra_body`, limits), a chat with text and images (multimodal), an image edit over multipart, generation into a binary field and into markdown for the chat, the handling of 401/402/404 errors, `Continue On Fail` and the timeout.
The tests need no TeamoRouter key. The `fail-401`, `fail-402`, `fail-404` and `fail-500` models in the mock return an error on purpose, and `slow-model` answers after 2.5 s.
---
## 7. First run and troubleshooting
* The package is built and covered by the automated tests (see `npm test`). Do the first run on a test workflow anyway.
* When installing through `Settings → Community nodes`, the UI may show a warning about a community node — that is normal.
* If n8n reports that the node failed to load, check `Help → About` (the n8n version) and the container logs: the usual cause is the unreachable `n8n-workflow` peer dependency (Option B, step 3).
## License
MIT