sync: UI animations, select styling, TLS verify flag via proxy second line, brand spacing

This commit is contained in:
2025-09-15 19:31:28 +03:00
parent 563663f9f1
commit 46d2fb8173
7 changed files with 723 additions and 375 deletions

View File

@@ -1,328 +1,434 @@
# Переменные и макросы НадTavern
Краткая, человеко‑понятная шпаргалка по тому, какие переменные и макросы доступны в шаблонах (в том числе в Prompt Blocks), как они устроены и как их правильно использовать. Док ниже соответствует текущему коду.
Реализация формирует единый «контекст» переменных для всех нод пайплайна, дополняет его выходами уже выполненных нод, а узел ProviderCall добавляет свои служебные структуры для удобной сборки промпта.
Ссылки на код:
- Формирование контекста запроса: [build_macro_context()](agentui/api/server.py:142)
- Исполнитель пайплайна и снапшот OUT: [PipelineExecutor.run()](agentui/pipeline/executor.py:136)
- Узел провайдера (Prompt Blocks → provider payload): [ProviderCallNode.run()](agentui/pipeline/executor.py:650)
- Шаблоны/макросы ([[...]] и {{ ... }}): [render_template_simple()](agentui/pipeline/templating.py:187)
- Короткая форма [[OUTx]] (извлечение текста): [_best_text_from_outputs()](agentui/pipeline/templating.py:124)
- Прямой форвард запросов: [RawForwardNode.run()](agentui/pipeline/executor.py:833)
---
## 1) Общие переменные контекста (для всех нод)
Эти переменные доступны в шаблонах любой ноды. Они добавляются на стороне сервера при обработке входящего HTTPзапроса.
- model — строка с именем модели.
Пример: "gpt-4o-mini"
- vendor_format — вендор/протокол запроса: "openai" | "gemini" | "claude" | "unknown"
- system — «системный» текст, если он был во входящем запросе; иначе пустая строка.
- params — стандартные параметры генерации (можно использовать как дефолты)
- params.temperature — число с плавающей точкой (по умолчанию 0.7)
- params.max_tokens — целое или null
- params.top_p — число (по умолчанию 1.0)
- params.stop — массив строк или null
- chat — сведения о чате во входящем запросе
- chat.last_user — последнее сообщение пользователя (строка)
- chat.messages — массив сообщений в унифицированной форме:
- role — "system" | "user" | "assistant" | "tool"
- content — содержимое (обычно строка)
- name — опционально, строка
- tool_call_id — опционально
- incoming — детали ВХОДЯЩЕГО HTTPзапроса
- incoming.method — метод ("POST" и т.п.)
- incoming.url — полный URL (в query ключи маскируются для логов)
- incoming.path — путь (например, /v1/chat/completions)
- incoming.query — строка query без вопросительного знака
- incoming.query_params — объект со всеми queryпараметрами
- incoming.headers — объект всех заголовков запроса
- incoming.json — сырой JSON тела запроса, как прислал клиент
- incoming.api_keys — удобные «срезы» ключей
- incoming.api_keys.authorization — значение из заголовка Authorization (если есть)
- incoming.api_keys.key — значение из query (?key=...) — удобно для Gemini
Пример использования в шаблоне:
- [[VAR:incoming.api_keys.key]] — возьмёт ключ из строки запроса (?key=...).
- [[VAR:incoming.headers.x-api-key]] — возьмёт ключ из заголовка x-api-key (типично для Anthropic).
- {{ params.temperature|default(0.7) }} — безопасно подставит число, если не задано во входящих данных.
---
## 2) Выходы нод (OUT) и ссылки на них
Во время исполнения пайплайна результаты предыдущих нод собираются в снапшот OUT и доступны при рендере шаблонов следующих нод:
- OUT — словарь выходов нод, ключи — id нод в пайплайне (например, "n1", "n2").
- OUT.n1, OUT.n2, ... — объект результата соответствующей ноды.
Формы доступа:
- Полная форма: [[OUT:n1.result.choices.0.message.content]]
(или фигурными скобками: {{ OUT.n1.result.choices.0.message.content }})
- Короткая форма «просто текст»: [[OUT1]], [[OUT2]], ...
Это эвристика: берётся самое вероятное «текстовое» поле из результата (см. [_best_text_from_outputs()](agentui/pipeline/templating.py:121)).
Что возвращают встроенные ноды:
# Переменные и макросы в НадTavern: очень понятный гайд
Этот файл объясняет, как в проекте подставлять нужные кусочки данных прямо в шаблоны, не ломая голову. Представьте «наклейки» — вы клеите их в JSON на нужные места, и система сама подменяет их на значения: из входящего запроса, из предыдущих узлов, из ваших переменных, из памяти выполнения.
Если вы открыли визуальный редактор по адресу http://127.0.0.1:7860/ui/editor.html — все примеры ниже можно копировать прямо туда.
Файлы, где «живет» эта магия:
- Сервер и конечные точки: [agentui/api/server.py](agentui/api/server.py)
- Исполнитель узлов (сердце конвейера): [PipelineExecutor.run()](agentui/pipeline/executor.py:170)
- Ноды: [ProviderCallNode.run()](agentui/pipeline/executor.py:1631), [RawForwardNode.run()](agentui/pipeline/executor.py:1939), [ReturnNode.run()](agentui/pipeline/executor.py:2256), [IfNode.run()](agentui/pipeline/executor.py:2350), SetVars внутри того же файла
- Шаблонизатор (подстановки [[...]] и {{ ... }}): [render_template_simple()](agentui/pipeline/templating.py:191), булевы выражения If: [eval_condition_expr()](agentui/pipeline/templating.py:336)
— — —
1) Что такое «переменные» и «макросы»
- Переменные — это значения, которые вы где-то определили: в узле SetVars, они появились из входящего запроса, их вернул провайдер (OpenAI/Gemini/Claude), или их сохранил движок выполнения в «память».
- Макросы — короткие «заклинания» вида [[...]] или {{ ... }}, которые вы вставляете прямо в текст/JSON. На ходу они заменяются на нужные значения.
Простейший пример:
- Было в шаблоне: "Authorization":"Bearer [[VAR:incoming.headers.authorization]]"
- Стало при выполнении: "Authorization":"Bearer eyJhbGciOi..."
— — —
2) Откуда берутся данные (куда «лезут» макросы)
- incoming.* — всё про входящий HTTPзапрос клиента:
- method, url, path, query
- headers — словарь заголовков
- json — JSON из тела запроса
- Быстро взять: [[VAR:incoming.headers.authorization]], [[VAR:incoming.json.model]]
- Формируется на сервере: [build_macro_context()](agentui/api/server.py:143)
- params.* — «нормализованные» параметры (температура, топ‑п и т.д.):
- Примеры: [[VAR:params.temperature]], [[VAR:params.max_tokens]]
- См. нормализацию: [normalize_to_unified()](agentui/api/server.py:44)
- model, system, vendor_format — общие поля запроса:
- [[VAR:model]], [[VAR:system]], [[VAR:vendor_format]]
- chat.* — удобный доступ к сообщениям:
- [[VAR:chat.last_user]] — текст последнего пользователя
- [[VAR:chat.messages]] — весь список сообщений
- vars.* — ваши переменные из SetVars:
- Можно писать коротко, без VAR и точек: [[MY_VAR]] или {{ MY_VAR }}
- Задаются в узле SetVars (подробно ниже)
- OUT.* — машина времени с результатами узлов:
- [[OUT1]] — «лучший текст» из узла n1 (короткая форма)
- [[OUT:n2.result]] — весь JSON результата узла n2
- [[OUT:n3.result.choices.0.message.content]] — вложенный путь в JSON
- Как извлекается «лучший текст», описано ниже (раздел «[[OUTx]] внутри»)
- STORE.* — постоянное хранилище (между шагами пайплайна):
- [[STORE:MY_VAR]] — значение переменной, сохранённой узлами (например SetVars)
- [[VAR:STORE.snapshot.OUT_TEXT.n1]] — текст, который движок сохранил как «лучший» для узла n1
- Доступ фигурными скобками тоже работает: {{ store.snapshot.OUT_TEXT.n1 }}
— — —
3) Главные «заклинания» (макросы)
- [[VAR:путь]] — взять значение из контекста
- Примеры: [[VAR:incoming.headers.authorization]], [[VAR:params.temperature]]
- [[OUT1]], [[OUT2]], … — быстро взять «лучший» текст из узла n1, n2, …
- Удобно для Return и подсказок к следующему провайдеру
- [[OUT:nX.что‑то]] — взять сырой JSON из узла и провалиться по пути
- [[OUT:n2.result]] — весь ответ провайдера
- [[OUT:n2.result.candidates.0.content.parts.0.text]] — кусочек Geminiответа
- [[PROMPT]] — хитрый фрагмент JSON из Prompt Blocks
- В ноде ProviderCall он разворачивается в правильные поля, в зависимости от провайдера:
- OpenAI → "messages": [...]
- Gemini → "contents": [...], "systemInstruction": {...}
- Claude → "system": "...", "messages": [...]
- Под капотом это делает: [ProviderCallNode.run()](agentui/pipeline/executor.py:1631)
- {{ путь }} — «фигурные скобки» возвращают значение без кавычек
- Подходит для чисел/массивов/объектов. Пример: "temperature": {{ params.temperature|default(0.7) }}
- Есть фильтр по умолчанию: |default(значение) — безопасно подставит запасной вариант
- Разбирает наш шаблонизатор: [render_template_simple()](agentui/pipeline/templating.py:191)
- [[NAME]] — «голая» переменная из SetVars (короткая запись)
- Ищется сперва в ваших vars, потом в общем контексте
- [[STORE:путь]] — достать из постоянного хранилища (store.*)
Подсказка: макросы [[...]] можно писать в строках JSON, а {{ ... }} — там, где нужно вставить число/массив/объект без кавычек.
— — —
4) Куда именно это писать (по узлам)
4.1 ProviderCall — «собрать запрос к провайдеру»
Где живут макросы:
- endpoint — можно вставлять {{ model }} или ключи в URL (например Gemini ?key=…)
- headers — это JSONобъект в текстовом поле: сюда можно писать [[VAR:incoming.headers.authorization]], [[Clod]] и т.д.
- template — главное поле: JSONтело запроса. Обязательно валидный JSON после разворачивания макросов.
Мини‑шпаргалка:
- OpenAI (пример заголовка): {"Authorization":"Bearer [[VAR:incoming.headers.authorization]]"}
- Gemini (ключ в URL): /v1beta/models/{{ model }}:generateContent?key=[[VAR:incoming.api_keys.key]]
- Claude (заголовки): {"x-api-key":"[[VAR:incoming.headers.x-api-key]]","anthropic-version":"2023-06-01"}
Полный пример OpenAIтела:
{
"model": "{{ model }}",
[[PROMPT]],
"temperature": {{ incoming.json.temperature|default(params.temperature|default(0.7)) }},
"top_p": {{ incoming.json.top_p|default(params.top_p|default(1)) }},
"max_tokens": {{ incoming.json.max_tokens|default(params.max_tokens|default(256)) }},
"stop": {{ incoming.json.stop|default(params.stop|default([])) }},
"stream": {{ incoming.json.stream|default(false) }}
}
Важное правило:
- [[PROMPT]] — это «кусок» JSON без запятых по краям. Убедитесь, что вокруг него стоят запятые там, где нужно, но не лишние.
- Если шаблон не превращается в валидный JSON — ProviderCall упадёт с понятной ошибкой.
4.2 RawForward — «пропусти запрос как есть» (reverse proxy)
Куда писать:
- base_url — куда слать (можно с макросами)
- override_path — переопределить путь (опционально)
- extra_headers — JSONобъект (строкой), можно вставлять [[...]] и {{ ... }}
По умолчанию узел пробрасывает заголовки клиента (кроме Host/ContentLength) и тело JSON «как есть». Это удобно, если вы хотите просто дойти до провайдера без сложной сборки, но при этом подмешать один‑два заголовка.
4.3 Return — «оформить финальный ответ в стиле клиента»
- target_format: auto/openai/gemini/claude — во что завернуть ответ
- text_template — обычно "[[OUT1]]", но можно собрать возврат из нескольких кусков:
- "Вот ваш результат: [[OUT2]]"
Return сам завернёт текст в правильную структуру. Пример OpenAIответа возвращается как "choices[0].message.content".
4.4 If — «ветвление по условию»
Пишем одно выражение, которое возвращает true/false. Поддерживаются:
- Логика: &&, ||, ! (not)
- Сравнения: ==, !=, <, <=, >, >=
- Ключевое слово contains — проверка подстроки или вхождения в список
- Макросы [[...]] и {{ ... }} прямо в выражении
Примеры:
- [[OUT1]] contains "Красиво"
- {{ OUT.n6.response_text|default('') }} != ""
- {{ params.temperature|default(0.7) }} >= 0.3 && {{ params.temperature|default(0.7) }} <= 1
- !([[OUT3]] contains "error")
Вычислитель выражений: [eval_condition_expr()](agentui/pipeline/templating.py:336)
4.5 SetVars — «завести свои переменные»
Два режима на каждую переменную:
- mode=string — строка после разворачивания макросов (обычный случай)
- mode=expr — мини‑формула (безопасная), в которой доступны специальные функции.
Доступные безопасные функции в expr:
- from_json(x)
- Парсит JSONстроку в объект (если в строке макросы — они сначала подставятся)
- jp(value, path, join_sep="\n")
- Достаёт данные по «dotJSONPath» (индексы и звёздочка поддерживаются)
- Примеры путей: a.b.0.c, items.*.title
- jp_text(value, path, join_sep="\n")
- То же, что jp, но вернёт строку: если массив — склеит через join_sep
Готовые рецепты:
- Извлечь текст из ответа провайдера в переменную TXT и вернуть:
- name: TXT
- mode: expr
- value: jp_text([[OUT:n2.result]], 'candidates.0.content.parts.0.text')
- Return.text_template: "[[TXT]]"
- Парсинг JSONстроки и взять число:
- name: VAL
- mode: expr
- value: jp(from_json('{"a":{"b":[{"x":1},{"x":2}]}}'), 'a.b.1.x')
- результат: 2
- Собрать несколько полей в строку:
- name: TITLES
- mode: expr
- value: jp_text([[OUT:n4.result]], 'items.*.title', ' | ')
- Return: "[[TITLES]]" → "Title1 | Title2 | Title3"
Подсказки:
- Если путь возвращает несколько элементов — используйте jp_text, чтобы сразу получить склейку.
- Если у вас уже объект/массив (не строка) — можно сразу передать его в jp/jp_text без from_json.
- Внутри expr макросы пишите внутри кавычек (в аргументах функций) — они развернутся перед вычислением.
Ограничения безопасности в expr:
- Разрешены только литералы, базовая арифметика/логика, сравнения и ф‑ции rand(), randint(a,b), choice(list), from_json(), jp(), jp_text().
- Доступ к произвольным атрибутам/импортам Python запрещён.
— — —
5) Где в редакторе быстро подсмотреть макросы
Вверху справа есть кнопка «ПЕРЕМЕННЫЕ». Откроется панель:
- Выберите «snapshot», чтобы видеть всю «картину мира» последнего запуска (incoming, params, OUT_TEXT, алиасы OUT1/OUT2 и т.д.)
- Клик по строке копирует готовый макрос в буфер (можно переключить режим «фигурные» — для {{ ... }})
- Поиск работает и по именам, и по значениям
За поведение панели отвечают страничка и скрипты:
- [static/editor.html](static/editor.html)
- [static/js/serialization.js](static/js/serialization.js)
- [static/js/pm-ui.js](static/js/pm-ui.js)
— — —
6) Что на самом деле делает [[OUTx]] (как выбирается «лучший текст»)
Когда нода (ProviderCall или RawForward) получила JSON от провайдера, движок старается «вынуть» из него удобный текст:
- OpenAI: choices[0].message.content
- Gemini: candidates[0].content.parts[0].text
- Claude: content[].text (склейка)
- Если формат неизвестен — идёт «лучший догадчик» по глубине, чтобы найти текст
- Можно задать пресет (JSONPath) в настройках ноды или глобально в «Запуск» → «Пресеты парсинга OUTx»
Логика извлечения и пресетов находится в: [ProviderCallNode.run()](agentui/pipeline/executor.py:1631), [RawForwardNode.run()](agentui/pipeline/executor.py:1939)
Для продвинутых: глобальные мета‑настройки пайплайна (timeouts, стратегия, пресеты) редактируются в «Запуск», а сохраняются в [pipeline.json](pipeline.json).
— — —
7) Безопасность (прочитать обязательно)
- Не храните настоящие APIключи в файлах пайплайна/пресетах. Передавайте их через заголовки клиента:
- OpenAI: Authorization: Bearer XXXXXX
- Anthropic: x-api-key: XXXXXX
- Gemini: ?key=XXXXXX в URL
- В шаблонах используйте значения из входящего запроса: [[VAR:incoming.headers.authorization]], [[VAR:incoming.headers.x-api-key]], [[VAR:incoming.api_keys.key]]
- Логи в редакторе показывают запросы/ответы целиком. Для продакшена отключайте/маскируйте чувствительные части.
- Прокси и TLSпроверка (если нужно сниффить трафик/пробрасывать через Burp) настраиваются здесь: [agentui/config.py](agentui/config.py), HTTPклиент: [build_client()](agentui/providers/http_client.py:21)
— — —
8) Рецепты «скопируй и вставь»
8.1 Самый короткий пайплайн «получил → отправил к OpenAI → вернул»
- Узел ProviderCall:
- provider: openai
- headers: {"Authorization":"Bearer [[VAR:incoming.headers.authorization]]"}
- template — возьмите из примера OpenAI выше (с [[PROMPT]])
- Prompt Blocks:
- system: «Ты — помощник. Отвечай коротко.»
- user: «[[VAR:chat.last_user]] — перепиши аккуратнее»
- Узел Return:
- target_format: auto
- text_template: "[[OUT1]]"
8.2 Gemini через ключ в URL, без заголовков
- ProviderCall:
- OUT.nX.result — сырой JSON ответа провайдера
- OUT.nX.response_text — уже извлечённый «лучший текст» (строка)
- RawForward:
- OUT.nX.result — JSON, как пришёл от апстрима (или {"error": "...", "text": "..."} при неJSON ответе)
- provider: gemini
- endpoint: /v1beta/models/{{ model }}:generateContent?key=[[VAR:incoming.api_keys.key]]
- headers: {}
- template — пример Gemini выше (с [[PROMPT]])
8.3 Склейка двух ответов и проверка условием If
- ProviderCall(n2) и ProviderCall(n3) что‑то генерят
- ProviderCall(n6): user prompt: «Объедини [[OUT3]] и [[OUT2]] красиво. Напиши слово "Красиво" в конце.»
- If(n7): expr = [[OUT6]] contains "Красиво"
- Return: text_template = "[[OUT6]]"
— — —
9) Где это в коде (для любопытных)
- Создание приложения/роутов: [create_app()](agentui/api/server.py:270)
- Нормализация payload (OpenAI/Gemini/Claude → единый вид): [normalize_to_unified()](agentui/api/server.py:44)
- Контекст для макросов (incoming/chat/params/…): [build_macro_context()](agentui/api/server.py:143)
- Исполнитель конвейера и «волны»: [PipelineExecutor.run()](agentui/pipeline/executor.py:170)
- Узел вызова провайдера (сборка [[PROMPT]], лог HTTP): [ProviderCallNode.run()](agentui/pipeline/executor.py:1631)
- Прямой форвард запроса: [RawForwardNode.run()](agentui/pipeline/executor.py:1939)
- Финализация ответа под формат клиента: [ReturnNode.run()](agentui/pipeline/executor.py:2256)
- Условия If (contains, &&, ||, ! и макросы): [IfNode.run()](agentui/pipeline/executor.py:2350), [eval_condition_expr()](agentui/pipeline/templating.py:336)
- Шаблонизатор макросов [[...]] и {{ ... }}: [render_template_simple()](agentui/pipeline/templating.py:191)
- Определение провайдера по форме JSON: [detect_vendor()](agentui/common/vendors.py:8)
— — —
Всё. Старайтесь думать так: «Мне нужен кусочек нужного значения в нужное место». Найдите его в панели «ПЕРЕМЕННЫЕ», скопируйте макрос — и вклейте. Если «кусочков» несколько — заведите переменные SetVars и соберите их в строку. Если сомневаетесь — начните с Return и [[OUT1]]: это самый короткий путь увидеть, что реально получается.
— — —
Подсказка по короткой форме [[OUTx]]:
- OpenAI: вернёт choices[0].message.content
- Gemini: вернёт candidates[0].content.parts[0].text
- Claude: склеит content[].text
- Если явных полей нет — выполнит «глубокий поиск» по ключам "text"/"content"
Приложение A. SetVars: функции и операции в mode=expr (простым языком)
---
Где это запрограммировано
- Логика разбора и разрешённые операции реализованы в [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:1092). Если коротко: там «белый список» безопасных функций и операций, всё остальное запрещено.
- Подстановка макросов внутри строк (для функций from_json/jp/jp_text) делает [render_template_simple()](agentui/pipeline/templating.py:191).
## 3) Макросы подстановки и синтаксис
Что можно писать в mode=expr
1) Случайные значения
- rand() — число от 0 до 1 (типа 0.01.0)
Пример: 0.35, 0.92 …
В шаблонах доступны обе формы подстановки:
1) Квадратные скобки [[ ... ]] — простая подстановка
- [[VAR:путь]] — взять значение из контекста по точечному пути
Пример: [[VAR:incoming.json.max_tokens]]
- [[OUT:путь]] — взять значение из OUT (см. раздел выше)
Пример: [[OUT:n1.result.choices.0.message.content]]
- [[OUT1]] / [[OUT2]] — короткая форма «просто текст»
- [[PROMPT]] — специальный JSONфрагмент из Prompt Blocks (см. ниже)
2) Фигурные скобки {{ ... }} — «джинджа‑лайт»
- {{ путь }} — взять значение по пути из контекста (или из OUT.* если начать с OUT.)
Пример: {{ OUT.n1.result }}
- Фильтр по умолчанию: {{ что-то|default(значение) }}
- randint(a, b) — целое в диапазоне [a; b] включительно
Примеры:
- {{ params.temperature|default(0.7) }}
- {{ incoming.json.stop|default([]) }}
- {{ anthropic_version|default('2023-06-01') }} — см. «Опциональные поля» ниже
- Фигурные скобки удобны там, где нужно вставить внутрь JSON не строку, а ЧИСЛО/ОБЪЕКТ/МАССИВ без кавычек и/или задать дефолт.
- randint(1, 6) → бросок кубика d6
- randint(100, 999) → трёхзначный код
---
- choice(list_or_tuple) — случайный элемент из списка/кортежа
Примеры:
- choice(['ru','en','de'])
- choice((128, 256, 512))
## 4) ProviderCall: Prompt Blocks, pm.* и [[PROMPT]]
2) Арифметика (как в обычных формулах)
- Операции: +, -, *, /, //, %
- / — деление с плавающей точкой
- // — целочисленное деление (отбрасывает дробную часть)
- % — остаток от деления
Узел ProviderCall собирает ваши Prompt Blocks (блоки вида: роль/текст/вкл‑выкл/порядок) в стандартные «сообщения» и превращает их в структуру для конкретного провайдера.
Внутри шаблонов этого узла доступны:
- pm — «сырьевые» структуры из Prompt Blocks
- Для OpenAI:
- pm.messages — массив { role, content, name? }
- pm.system_text — один большой текст из всех systemблоков
- Для Gemini:
- pm.contents — массив { role: "user"|"model", parts: [{text}] }
- pm.systemInstruction — объект вида { parts: [{text}] } или пустой {}
- pm.system_text — строка
- Для Claude:
- pm.system_text — строка
- pm.system — то же самое (удобно подставлять в поле "system")
- pm.messages — массив { role: "user"|"assistant", content: [{type:"text", text:"..."}] }
- [[PROMPT]] — готовый JSONфрагмент на основе pm, безопасный для вставки внутрь шаблона:
- OpenAI → подставит: "messages": [...]
- Gemini → подставит: "contents": [...], "systemInstruction": {...}
- Claude → подставит: "system": "...", "messages": [...]
Зачем это нужно?
- Чтобы 1) удобно собирать промпт из визуальных блоков, 2) не «сломать» JSON руками.
Вы можете вручную использовать {{ pm.* }}, но [[PROMPT]] — рекомендуемый и самый безопасный вариант.
---
## 5) Частые сценарии и примеры
Примеры ниже можно вклеивать в поле «template» ноды ProviderCall. Они уже используют [[PROMPT]] и аккуратные дефолты.
OpenAI (POST /v1/chat/completions):
```
{
"model": "{{ model }}",
[[PROMPT]],
"temperature": {{ incoming.json.temperature|default(params.temperature|default(0.7)) }},
"top_p": {{ incoming.json.top_p|default(params.top_p|default(1)) }},
"max_tokens": {{ incoming.json.max_tokens|default(params.max_tokens|default(256)) }},
"stop": {{ incoming.json.stop|default(params.stop|default([])) }}
}
```
Gemini (POST /v1beta/models/{model}:generateContent):
```
{
"model": "{{ model }}",
[[PROMPT]],
"safetySettings": {{ incoming.json.safetySettings|default([]) }},
"generationConfig": {
"temperature": {{ incoming.json.generationConfig.temperature|default(params.temperature|default(0.7)) }},
"topP": {{ incoming.json.generationConfig.topP|default(params.top_p|default(1)) }},
"maxOutputTokens": {{ incoming.json.generationConfig.maxOutputTokens|default(params.max_tokens|default(256)) }},
"stopSequences": {{ incoming.json.generationConfig.stopSequences|default(params.stop|default([])) }}
}
}
```
Подсказка: ключ Gemini удобно брать из строки запроса:
в endpoint используйте …?key=[[VAR:incoming.api_keys.key]]
Claude (POST /v1/messages):
```
{
"model": "{{ model }}",
[[PROMPT]],
"temperature": {{ incoming.json.temperature|default(params.temperature|default(0.7)) }},
"top_p": {{ incoming.json.top_p|default(params.top_p|default(1)) }},
"max_tokens": {{ incoming.json.max_tokens|default(params.max_tokens|default(256)) }},
"system": {{ pm.system|default("") }}
}
```
Подсказка: ключ Anthropic обычно передают в заголовке x-api-key.
В UIпресете это поле уже есть в headers.
RawForward (прямой форвард входящего запроса):
- Поля конфигурации base_url, override_path, extra_headers проходят через те же макросы, поэтому можно подставлять динамику:
- base_url: https://generativelanguage.googleapis.com
- override_path: [[VAR:incoming.path]] (или задать свой)
- extra_headers (JSON): `{"X-Trace":"req-{{ incoming.query_params.session|default('no-session') }}"}`
---
## 6) Опциональные/редкие поля, о которых стоит знать
- anthropic_version — используется как HTTPзаголовок для Claude ("anthropic-version"). В тело запроса не вставляется.
Если нужен дефолт, задавайте его в headers (например, в конфиге ноды/шаблоне заголовков). В шаблонах тела используйте [[PROMPT]]/pm.* без anthropic_version.
- stream — в MVP стриминг отключён, сервер принудительно не стримит ответ.
В шаблонах можно встретить поля stream, но по умолчанию они не включены.
---
## 7) Когда использовать [[...]] и когда {{ ... }}
- Внутрь JSON как ОБЪЕКТ/МАССИВ/ЧИСЛО: используйте {{ ... }}
(фигурные скобки вставляют «как есть», без кавычек, и умеют |default(...))
- Для строк/URL/заголовков/простых значений: можно использовать [[...]]
(квадратные скобки удобны и короче писать)
- Скобки и унарные знаки:
- (1 + 2) * 3
- -5, +7
Примеры:
- {{ pm.contents }} — вставит массив как настоящий массив (без кавычек)
- {{ params.temperature|default(0.7) }} — безопасный дефолт для числа
- [[VAR:incoming.api_keys.authorization]] — быстро подставить строку Authorization
- 9.99 * 1.2 → 11.988
- (rand() * 100) // 1 → псевдо‑процент от 0 до 99
- randint(1, 6) + 10 → 11..16
---
3) Логика и сравнения
- Логические: and, or
- Сравнения: ==, !=, <, <=, >, >=
- Можно «цепочкой»: 1 &lt; x &lt;= 10
## 8) Отладка и рекомендации
Примеры:
- (rand() &gt; 0.5) and (choice([True, False]) or True)
- 120 &lt;= choice([64,128,256]) and 256 &gt;= 200 → True
- ProviderCall печатает в консоль DEBUG сведения: выбранный провайдер, конечный URL, первые символы тела запроса — удобно для проверки корректности шаблона.
- Если «ничего не подставилось»:
1) Проверьте, что вы НЕ передаёте сырое входное тело напрямую в ProviderCall (узел строит тело из шаблона и Prompt Blocks).
2) Убедитесь, что итоговый JSON валиден (закрывающие скобки, запятые).
3) Проверьте точность путей в макросах (OUT vs OUTx, правильные id нод n1/n2/...).
- Для ссылок на выходы предыдущих нод используйте [[OUT1]] как «просто текст», либо полные пути [[OUT:n1...]] для точного фрагмента.
4) Литералы (что можно писать напрямую)
- Числа: 42, 3.14
- Строки: 'привет', "hello"
- Списки/кортежи/словарики: [1,2,3], (1,2,3), {'a':1, 'b':2}
- Логические и nullподобные: True, False, None
---
Отдельный режим: «чистый JSON»
- Если ВЕСЬ expr — корректный JSON (и только он), он принимается как значение.
Примеры:
- {"a":{"b":[{"x":1},{"x":2}]}}
- ["ru","en","de"]
- true / false / null
- Внутри «обычных» выражений используйте Pythonформы True/False/None.
## 9) Быстрая памятка по ключам доступа
5) Макросы внутри expr — как правильно
Важно: «голые» макросы внутри expr не работают. Подставлять их можно ТОЛЬКО как строки‑аргументы специальных функций from_json/jp/jp_text — они сначала разворачиваются в текст, потом парсятся.
- Gemini: [[VAR:incoming.api_keys.key]] — рекомендовано; ключ приходит в query (?key=...).
- OpenAI: [[VAR:incoming.headers.authorization]] (или [[VAR:incoming.api_keys.authorization]]) — стандартный Bearerтокен.
- Anthropic: [[VAR:incoming.headers.x-api-key]] — ключ в заголовке.
Примеры (готовы к копипасте):
- Взять JSON из предыдущей ноды и достать одно число:
name: SCORE
mode: expr
value: jp(from_json('[[OUT:n2.result]]'), 'meta.score')
---
- Вытащить текст из OpenAIответа и склеить варианты через двойной перенос:
name: TEXTS
mode: expr
value: jp_text(from_json('[[OUT:n6.result]]'), 'choices.*.message.content', '\n\n')
## 10) Ссылки на реализацию (для интересующихся деталями)
- Разобрать JSON из входящего запроса:
name: UID
mode: expr
value: jp(from_json('[[VAR:incoming.json]]'), 'user.id')
- Контекст (переменные): [build_macro_context()](agentui/api/server.py:142)
- Исполнение пайплайна, зависимости, снапшоты OUT: [PipelineExecutor.run()](agentui/pipeline/executor.py:136)
- Узел провайдера (Prompt Blocks → провайдер): [ProviderCallNode.run()](agentui/pipeline/executor.py:650)
- PMструктуры для шаблонов: [ProviderCallNode._blocks_struct_for_template()](agentui/pipeline/executor.py:592)
- Подстановка [[PROMPT]], макросы, дефолты: [render_template_simple()](agentui/pipeline/templating.py:187)
- Короткая форма [[OUTx]] и поиск «лучшего текста»: [_best_text_from_outputs()](agentui/pipeline/templating.py:124)
- Прямой форвард входящего запроса: [RawForwardNode.run()](agentui/pipeline/executor.py:833)
- Детекция вендора по входному payload: [detect_vendor()](agentui/common/vendors.py:8)
- Подставить фигурные скобки как строку и распарсить:
name: LIMIT
mode: expr
value: jp(from_json('{{ params|default({"max_tokens":256}) }}'), 'max_tokens')
Удачного редактирования!
---
## Пользовательские переменные (SetVars) — «для людей»
Доступные функции (подробнее)
- from_json(x)
- Принимает строку JSON (в т.ч. со вставленными макросами), возвращает объект/массив/число/строку/bool/None.
- Удобно, когда источник — текст, а работать хочется с полями.
Задача: в начале пайплайна положить свои значения и потом использовать их в шаблонах одной строкой — например [[MY_KEY]] или {{ MAX_TOKENS }}.
- jp(value, path, join_sep="\n")
- «Точечный JSONPath»: a.b.0.c, items.*.title
- Возвращает значение или список значений (если в пути есть «*»)
- join_sep здесь игнорируется (имеет смысл в jp_text)
Где это в UI
- В левой панели добавьте ноду SetVars и откройте её в инспекторе.
- Жмите «Добавить переменную», у каждой переменной есть три поля:
- name — имя переменной (латинские буквы/цифры/подчёркивание, не с цифры): MY_KEY, REGION, MAX_TOKENS
- mode — режим обработки значения:
- string — строка, в которой работают макросы ([[...]] и {{ ... }})
- expr — «мини‑формула» без макросов (подробнее ниже)
- value — собственно значение
- jp_text(value, path, join_sep="\n")
- То же, но всегда возвращает строку. Если значений несколько — склеит через join_sep.
Как потом вставлять переменные
- Для строк (URL/заголовки/текст) — квадратные скобки: [[MY_KEY]]
- Для чисел/массивов/объектов — фигурные скобки: {{ MAX_TOKENS }}, {{ GEN_CFG }}
- rand(), randint(a,b), choice(list_or_tuple)
- Случайности без сидирования (не детерминированы).
- randint — границы включительные. choice — список/кортеж не должен быть пустым.
Примеры «как надо»
- Переменная-строка (mode=string):
- name: AUTH
- value: "Bearer [[VAR:incoming.headers.authorization]]"
- Использование в заголовке: "Authorization": "[[AUTH]]"
- Переменная-число (mode=expr):
- name: MAX_TOKENS
- value: 128 + 64
- Использование в JSON: "max_tokens": {{ MAX_TOKENS }}
- Переменная-объект (mode=expr):
- name: GEN_CFG
- value: {"temperature": 0.3, "topP": 0.9, "safe": true}
- Использование: "generationConfig": {{ GEN_CFG }}
Чего делать нельзя (и почему)
- Нельзя любые другие функции: len(), sum(), not, print(), и т.п. — «белый список» (смотри [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:1092))
- Нельзя обращаться к переменным/атрибутам по именам (кроме ваших [[NAME]] в шаблонах вне expr)
- Нельзя генераторы/списки‑выражения, **kwargs, *args
- Нельзя «голые» макросы внутри expr: [[...]] или {{ ... }} нужно класть строкой в аргумент from_json/jp/jp_text
Важно про два режима
- string — это «шаблон». Внутри работают все макросы ([[VAR:...]], [[OUT:...]], [[PROMPT]], {{ ... }}). Значение прогоняется через рендер [render_template_simple()](agentui/pipeline/templating.py:184).
- expr — это «мини‑формула». Внутри НЕТ макросов и НЕТ доступа к контексту; только литералы и операции (см. ниже). Вычисляет значение безопасно — без eval, на белом списке AST (реализация: [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:291)).
Быстрая шпаргалка «на каждый день»
1) Бросок кубика d6
- name: DICE
- mode: expr
- value: randint(1, 6)
Что умеет expr (мини‑формулы)
- Числа и арифметика: 128 + 64, (5 * 60) + 30, 42 % 2, -5, 23 // 10
- Строки: "eu" + "-central" → "eu-central" (строки склеиваем знаком +)
- Булева логика: (2 < 3) and (10 % 2 == 0), 1 < 2 < 5
- Коллекции: ["fast", "safe"], {"temperature": 0.3, "topP": 0.9, "safe": true}
- JSONлитералы: true/false/null, объекты и массивы если выражение является чистым JSON, оно разбирается напрямую (без макросов), т.е. trueTrue, nullNone и т.п.
- Запрещено: функции (кроме специально разрешённых ниже), доступ к переменным/контексту, атрибуты/индексация/условные выражения.
2) Случайный стиль ответа
- name: TONE
- mode: expr
- value: choice(['официально','дружелюбно','коротко'])
Рандом в expr
- В expr доступны три простые функции случайности:
- rand() число с плавающей точкой в диапазоне [0, 1)
- randint(a, b) целое число от a до b включительно
- choice(list) случайный элемент из списка/кортежа
- Примеры:
- name: RAND_F, mode: expr, value: rand()
- "temperature": {{ RAND_F }}
- name: DICE, mode: expr, value: randint(1, 6)
- "dice_roll": {{ DICE }}
- name: PICK_MODEL, mode: expr, value: choice(["gpt-4o-mini", "gpt-4o", "o3-mini"])
- "model": "[[PICK_MODEL]]"
- Зерна/seed нет каждый запуск выдаёт новое значение.
3) Выбрать лимит токенов
- name: TOKENS
- mode: expr
- value: choice((128, 256, 512))
«Почему в expr нельзя подставлять переменные/макросы
- Для безопасности и предсказуемости: expr это закрытый миниязык без окружения.
- Если нужно использовать другие переменные/макросы делайте это в режиме string (там всё рендерится шаблонизатором).
- Технические детали: защита реализована в [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:291), а вставка stringзначений через [render_template_simple()](agentui/pipeline/templating.py:184).
4) Наценка 20%
- name: PRICE_WITH_VAT
- mode: expr
- value: price * 1.2 ← если price — ваша строковая переменная, используйте вне expr (в шаблоне) {{ PRICE }}; внутри expr внешние имена не видны
Как это работает внутри (если интересно)
- SetVars исполняется как обычная нода пайплайна и отдаёт {"vars": {...}}.
- Исполнитель добавляет эти значения в контекст для последующих нод как context.vars (см. [PipelineExecutor.run()](agentui/pipeline/executor.py:131)).
- При рендере шаблонов:
- [[NAME]] и {{ NAME }} подставляются с приоритетом из пользовательских переменных (см. обработку в [render_template_simple()](agentui/pipeline/templating.py:184)).
- Сам SetVars считает переменные в порядке списка и возвращает их одним пакетом (внутри одной ноды значения не зависят друг от друга).
5) Вытянуть текст из ответа модели n6
- name: ANSWER
- mode: expr
- value: jp_text(from_json('[[OUT:n6.result]]'), 'choices.0.message.content')
Частые вопросы
- «Хочу собрать строку с частями из внешнего запроса»: делайте mode=string и пишите: "Bearer [[VAR:incoming.headers.authorization]]".
- «Хочу массив случайных чисел»: mode=expr [rand(), rand(), rand()], а в JSON: "numbers": {{ MY_LIST }}
- «Почему мои значения не сохраняются нажмите «Сохранить параметры» в инспекторе ноды, затем «Сохранить пайплайн» в шапке. UI синхронизирует данные в node.data и сохраняет в pipeline.json (см. [static/editor.html](static/editor.html)).
Подсказка по ошибкам
- Function X is not allowed — вызвали неразрешённую функцию
- choice() expects list or tuple — передали не список/кортеж
- choice() on empty sequence — пустой список
- randint invalid arguments — границы не числа или a &gt; b
- Expression not allowed — в выражении есть запрещённые элементы (например, not/len/компрехеншены)
Ссылки на реализацию (для любопытных)
- Нода переменных: [SetVarsNode](agentui/pipeline/executor.py:264), [SetVarsNode._safe_eval_expr()](agentui/pipeline/executor.py:291), [SetVarsNode.run()](agentui/pipeline/executor.py:354)
- Исполнитель/контекст vars: [PipelineExecutor.run()](agentui/pipeline/executor.py:131)
- Шаблоны и макросы (включая «голые» [[NAME]]/{{ NAME }}): [render_template_simple()](agentui/pipeline/templating.py:184)
Если видите ошибку — упростите выражение: разбейте задачу на 2 переменные SetVars (сначала jp/from_json, потом математика/выбор), либо перенесите часть логики в шаблон {{ ... }} (но помните: в шаблоне нет rand/randint/choice).
Где посмотреть действующие значения
- В редакторе нажмите «ПЕРЕМЕННЫЕ»: там видно STORE (включая snapshot OUT_TEXT и алиасы OUT1/OUT2). Клик по строке — копирование макроса для вставки.
Схема работы панели описана в [static/editor.html](static/editor.html) и скриптах [static/js/serialization.js](static/js/serialization.js), [static/js/pm-ui.js](static/js/pm-ui.js).