380 lines
25 KiB
Markdown
380 lines
25 KiB
Markdown
# 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
|