Files
HadTavern/docs/VARIABLES.md

23 KiB
Raw Blame History

Переменные и макросы НадTavern

Краткая, человеко‑понятная шпаргалка по тому, какие переменные и макросы доступны в шаблонах (в том числе в Prompt Blocks), как они устроены и как их правильно использовать. Док ниже соответствует текущему коду.

Реализация формирует единый «контекст» переменных для всех нод пайплайна, дополняет его выходами уже выполненных нод, а узел ProviderCall добавляет свои служебные структуры для удобной сборки промпта.

Ссылки на код:


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()).

Что возвращают встроенные ноды:

  • ProviderCall:
    • OUT.nX.result — сырой JSON ответа провайдера
    • OUT.nX.response_text — уже извлечённый «лучший текст» (строка)
  • RawForward:
    • OUT.nX.result — JSON, как пришёл от апстрима (или {"error": "...", "text": "..."} при неJSON ответе)

Подсказка по короткой форме OUTx:

  • OpenAI: вернёт choices[0].message.content
  • Gemini: вернёт candidates[0].content.parts[0].text
  • Claude: склеит content[].text
  • Если явных полей нет — выполнит «глубокий поиск» по ключам "text"/"content"

3) Макросы подстановки и синтаксис

В шаблонах доступны обе формы подстановки:

  1. Квадратные скобки ... — простая подстановка
  1. Фигурные скобки {{ ... }} — «джинджа‑лайт»
  • {{ путь }} — взять значение по пути из контекста (или из OUT.* если начать с OUT.)
    Пример: {{ OUT.n1.result }}
  • Фильтр по умолчанию: {{ что-то|default(значение) }}
    Примеры:
    • {{ params.temperature|default(0.7) }}
    • {{ incoming.json.stop|default([]) }}
    • {{ anthropic_version|default('2023-06-01') }} — см. «Опциональные поля» ниже
  • Фигурные скобки удобны там, где нужно вставить внутрь JSON не строку, а ЧИСЛО/ОБЪЕКТ/МАССИВ без кавычек и/или задать дефолт.

4) ProviderCall: Prompt Blocks, pm.* и PROMPT

Узел 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 проходят через те же макросы, поэтому можно подставлять динамику:

6) Опциональные/редкие поля, о которых стоит знать

  • anthropic_version — используется как HTTPзаголовок для Claude ("anthropic-version"). В тело запроса не вставляется. Если нужен дефолт, задавайте его в headers (например, в конфиге ноды/шаблоне заголовков). В шаблонах тела используйте PROMPT/pm.* без anthropic_version.

  • stream — в MVP стриминг отключён, сервер принудительно не стримит ответ.
    В шаблонах можно встретить поля stream, но по умолчанию они не включены.


7) Когда использовать ... и когда {{ ... }}

  • Внутрь JSON как ОБЪЕКТ/МАССИВ/ЧИСЛО: используйте {{ ... }}
    (фигурные скобки вставляют «как есть», без кавычек, и умеют |default(...))
  • Для строк/URL/заголовков/простых значений: можно использовать ...
    (квадратные скобки удобны и короче писать)

Примеры:

  • {{ pm.contents }} — вставит массив как настоящий массив (без кавычек)
  • {{ params.temperature|default(0.7) }} — безопасный дефолт для числа
  • VAR:incoming.api_keys.authorization — быстро подставить строку Authorization

8) Отладка и рекомендации

  • ProviderCall печатает в консоль DEBUG сведения: выбранный провайдер, конечный URL, первые символы тела запроса — удобно для проверки корректности шаблона.
  • Если «ничего не подставилось»:
    1. Проверьте, что вы НЕ передаёте сырое входное тело напрямую в ProviderCall (узел строит тело из шаблона и Prompt Blocks).
    2. Убедитесь, что итоговый JSON валиден (закрывающие скобки, запятые).
    3. Проверьте точность путей в макросах (OUT vs OUTx, правильные id нод n1/n2/...).
  • Для ссылок на выходы предыдущих нод используйте OUT1 как «просто текст», либо полные пути OUT:n1... для точного фрагмента.

9) Быстрая памятка по ключам доступа


10) Ссылки на реализацию (для интересующихся деталями)

Удачного редактирования!

Пользовательские переменные (SetVars) — «для людей»

Задача: в начале пайплайна положить свои значения и потом использовать их в шаблонах одной строкой — например MY_KEY или {{ MAX_TOKENS }}.

Где это в UI

  • В левой панели добавьте ноду SetVars и откройте её в инспекторе.
  • Жмите «Добавить переменную», у каждой переменной есть три поля:
    • name — имя переменной (латинские буквы/цифры/подчёркивание, не с цифры): MY_KEY, REGION, MAX_TOKENS
    • mode — режим обработки значения:
      • string — строка, в которой работают макросы (... и {{ ... }})
      • expr — «мини‑формула» без макросов (подробнее ниже)
    • value — собственно значение

Как потом вставлять переменные

  • Для строк (URL/заголовки/текст) — квадратные скобки: MY_KEY
  • Для чисел/массивов/объектов — фигурные скобки: {{ MAX_TOKENS }}, {{ GEN_CFG }}

Примеры «как надо»

  • Переменная-строка (mode=string):
  • Переменная-число (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 }}

Важно про два режима

  • string — это «шаблон». Внутри работают все макросы (VAR:..., OUT:..., PROMPT, {{ ... }}). Значение прогоняется через рендер render_template_simple().
  • expr — это «мини‑формула». Внутри НЕТ макросов и НЕТ доступа к контексту; только литералы и операции (см. ниже). Вычисляет значение безопасно — без eval, на белом списке AST (реализация: SetVarsNode._safe_eval_expr()).

Что умеет 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, оно разбирается напрямую (без макросов), т.е. true→True, null→None и т.п.
  • Запрещено: функции (кроме специально разрешённых ниже), доступ к переменным/контексту, атрибуты/индексация/условные выражения.

Рандом в 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"])
  • Зерна/seed нет — каждый запуск выдаёт новое значение.

«Почему в expr нельзя подставлять переменные/макросы?»

  • Для безопасности и предсказуемости: expr — это закрытый мини‑язык без окружения.
  • Если нужно использовать другие переменные/макросы — делайте это в режиме string (там всё рендерится шаблонизатором).
  • Технические детали: защита реализована в SetVarsNode._safe_eval_expr(), а вставка stringзначений — через render_template_simple().

Как это работает внутри (если интересно)

  • SetVars исполняется как обычная нода пайплайна и отдаёт {"vars": {...}}.
  • Исполнитель добавляет эти значения в контекст для последующих нод как context.vars (см. PipelineExecutor.run()).
  • При рендере шаблонов:
    • NAME и {{ NAME }} подставляются с приоритетом из пользовательских переменных (см. обработку в render_template_simple()).
  • Сам SetVars считает переменные в порядке списка и возвращает их одним пакетом (внутри одной ноды значения не зависят друг от друга).

Частые вопросы

  • «Хочу собрать строку с частями из внешнего запроса»: делайте mode=string и пишите: "Bearer VAR:incoming.headers.authorization".
  • «Хочу массив случайных чисел»: mode=expr → [rand(), rand(), rand()], а в JSON: "numbers": {{ MY_LIST }}
  • «Почему мои значения не сохраняются?» — нажмите «Сохранить параметры» в инспекторе ноды, затем «Сохранить пайплайн» в шапке. UI синхронизирует данные в node.data и сохраняет в pipeline.json (см. static/editor.html).

Ссылки на реализацию (для любопытных)