Обновить
32K+
17
@Perruerread⁠-⁠only

Пользователь

103,2
Рейтинг
3
Подписчики
Отправить сообщение

Условия GitHub действительно дают кое-что: выложив публичный репозиторий, автор разрешает другим пользователям смотреть код и форкать его средствами самого GitHub. Но на этом разрешение и кончается. Оно не распространяется на то, что обычно делают с форком: запускать в продакшене, собирать Docker-образ, раздавать людям вне GitHub.🫡

А коммерческая лицензия Flowise про это говорит прямо: в продакшене — только с подпиской, копировать и менять можно лишь для разработки и тестирования, а права на все изменения остаются у FlowiseAI. И последней строкой — запрет копировать, публиковать и распространять. Подписку теперь не купить, компании нет. Так что форк на GitHub сделать можно, а выпустить из него продукт, который люди поставят у себя, — нет. Поэтому я вырезал эту папку и написал замену под Apache-2.0. Я не юрист, но для проекта, который ставят компании, лучше, чтобы вопрос вообще не возникал.

Справедливо. ~/.keelflow появился как зеркало ~/.flowise: главное было, чтобы переход с Flowise работал без ручного переноса данных. Если ~/.keelflow пустой, а в ~/.flowise что-то есть, Keelflow берёт старую папку. Но новым установкам засорять $HOME незачем.

Сделаю так:

  • новые установки хранят данные в $XDG_DATA_HOME/keelflow (по умолчанию ~/.local/share/keelflow), на macOS — в ~/Library/Application Support/keelflow, на Windows — в %LOCALAPPDATA%\keelflow;

  • существующие ~/.keelflow и ~/.flowise продолжают работать как есть: молча переносить чужую базу с ключом шифрования учётных данных — плохая идея;

  • KEELFLOW_HOME по-прежнему переопределяет всё, в Docker путь задаётся им явно;

  • логи, скорее всего, уедут отдельно в $XDG_STATE_HOME.

Спасибо, войдёт в ближайший релиз.

Спасибо! Согласен, для open core это необычно. Обычно открытое ядро полностью рабочее, а коммерческая часть его дополняет, как у GitLab с его отдельным FOSS-вариантом. У Flowise так было до версии 3.0: в 1.x и 2.x вход настраивался логином и паролем в переменных окружения, и открытая часть работала сама. В 3.0 (май 2025) появились пользователи, организации и рабочие пространства. Их положили в коммерческую папку, но бесплатный режим начал работать через тот же код, просто с одним владельцем. Скорее всего, это удобство разработки, а не злой умысел, но в итоге без коммерческой папки открытая версия не запускается.

Форки смотрел, и при разведке, и сейчас ещё раз. Из 25 тысяч почти все — копии без своих коммитов. У самых звёздных форков (десяток-полтора звёзд) последние коммиты сделаны до закрытия, и коммерческая папка в них на месте. the-answerai/theanswer — отдельный продукт на базе Flowise, последний коммит в июне. После закрытия я нашёл только один форк, который тоже вырезал коммерческий код и сделал вход для одного пользователя: DimVai/Dim-Flowise. Но это личный проект автора, без звёзд и без явной лицензии. Остальные, судя по обсуждениям, уходят на Langflow, Dify или n8n.

Как раз наоборот: миграции были коммерческими. 51 файл (по 12–13 на каждую из четырёх СУБД) лежал в той же папке enterprise под лицензией FlowiseAI. Я удалил их из репозитория вместе с историей, распространять этот код нельзя. Остались не миграции, а таблицы в базах пользователей, которые уже обновились до Flowise 3.x.

С самими таблицами проблемы нет: схема базы и данные пользователя — не код FlowiseAI. Проблема в том, что код, который их создаёт, пришлось выкинуть, а открытая часть на эти таблицы опирается. Колонку workspaceId используют девять открытых миграций и запросы по всему серверу, а открытая миграция ModifyChatflowType для SQLite пересоздаёт chat_flow с внешним ключом на workspace. Поэтому «добавить миграцию, которая всё удалит» не получится. Без workspace новая база не соберётся, открытый код упадёт на первом же запросе, а у тех, кто обновляется с Flowise 3.x, мы бы удалили данные.

Так что я сделал обратное: свои миграции под Apache-2.0 создают user, organization, workspace и колонки workspaceId, если их нет, и ничего не делают, если есть. Keelflow сам ими пользуется: владелец входит через ту же таблицу user. Таблицы, которые Keelflow больше не нужны (roles, workspace_users, workspace_shared, login_activity), в старых базах остаются нетронутыми. Удалять миграцией данные пользователя без спроса не хочу, а если кто-то попросит, это будет отдельная команда очистки, которую запускают явно.

По нулям вы правы: из state старого SDK «не задано» и «явно 0» не различить. SDKv2 пишет нулевое значение в обоих случаях, а в схеме протокола нет даже значения по умолчанию (Default), так что провайдер нам ничего не подскажет.

Но эвристика не решает, что удалять, — она только выбирает порядок. Цикл трогает лишь аргументы, на которые провайдер вернул ошибку валидации. Необязательный desired_count = 0 без конфликта до цикла просто не доходит, и неважно, осмысленный он или нет. Обязательные поля не удаляются никогда. «Нулевые первыми» решает только, какой из отклонённых аргументов убрать раньше. Типичный случай — пара с ConflictsWith, где один аргумент нулевой, а второй нет. Явный ноль здесь почти исключён: SDKv2 проверяет ConflictsWith по сырому конфигу, и явный 0 рядом с заданным партнёром не прошёл бы валидацию ещё у самого пользователя. Раз ресурс существует, такой пары в его конфиге не было, и нулевой член пары — артефакт state.

Где эвристика реально может ошибиться: оба аргумента нулевые или оба нет (тогда порядок ничего не меняет), и поля с ненулевым Default, которые мы не видим. Если убрать явный 0 у поля с Default = 5, провайдер подставит 5, и plan покажет изменение. От этого страхует не эвристика, а проверка результата. В CI есть e2e: Unclick импортирует набор ресурсов из moto, и tofu plan должен показать только импорт — без изменений, созданий и удалений. Так что ваш сценарий упадёт там, а не у пользователя через полгода. Хорошее дополнение: в итоговом списке показывать, какие аргументы были убраны, чтобы при расхождении в plan было видно, откуда оно.

По тесту — гибрид. Множество полей тест будет брать из самих .proto автоматически, через дескрипторы сгенерированных Go-пакетов (protoreflect). Сравнивать v5 с v6 нужно по именам, а не по номерам: номера расходятся намеренно, это и была исходная проблема. А решение «это поле переносим или сознательно пропускаем» автоматизировать нельзя: это смысл, а не структура. Поэтому рядом лежит список пропускаемых полей. Тест падает, если в дескрипторе появилось поле, которого нет ни среди перенесённых, ни среди пропускаемых. При обновлении протокола он сам найдёт новые поля — ровно то, что вы описали, — но требует от человека решения по каждому. Одна оговорка: .proto у нас не подтягиваются из terraform-plugin-go, а скопированы в репозиторий, как советует сам HashiCorp в шапке файла («copy this definition into your own codebase»). Значит, обновление — это копирование новой версии и перегенерация, и тест срабатывает на этом шаге.

Спасибо, оба вопроса по делу.

Про кодогенерацию. Номера полей мешают только перекодированию на уровне байтов. Генератор, который сопоставляет поля по имени, был бы от этого защищён, так что идея рабочая. Я не пошёл на неё потому, что различия не только в номерах: вложенные атрибуты (nested_type) есть только в v6, и эту ветку всё равно пришлось бы писать руками. На ~300 строк генератор с исключениями выходит дороже самих адаптеров. Terraform и OpenTofu, насколько я знаю, тоже держат для v5 и v6 два отдельных конвертера, написанных вручную. Настоящий риск в другом: протокол обновится, а адаптер тихо пропустит новое поле. От этого лучше защищает не генератор, а тест, который сверяет поля обоих .proto с тем, что адаптеры реально переносят, и падает на незнакомом поле. Добавлю такой.

Про maxFixRounds. Вы нашли дыру, и она хуже, чем вы предположили: предупреждения на этот случай нет. Если 16 раундов кончились, цикл выходит молча, и ресурс остаётся с ошибкой до tofu plan. Цепочки вида «A конфликтует с B, после удаления A — C с B» цикл проходит: каждый шаг — один раунд. На реальных ресурсах, которые я гонял, хватало 1–3 раундов, но 16 — число с потолка. Исправлю так:

  1. число раундов — по количеству заданных необязательных аргументов: каждый раунд убирает один, так что цикл гарантированно закончится, а искусственный предел не нужен;

  2. в первую очередь убирать аргументы с нулевыми значениями — именно их пишет в state старый SDK, и из-за них чаще всего конфликты;

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

Сейчас сканеры проверяют каждое сообщение отдельно, так что атака, разбитая на несколько сообщений, по частям может пройти. Что можно сделать уже сейчас: склеивать последние сообщения пользователя и проверять их вместе (match_type="chunks") — это ловит инструкцию, разбитую на части. Кроме того, стоит проверять ответ модели сканерами вывода: неважно, за сколько сообщений её «уговорили», результат виден в ответе.

Полноценную проверку диалога добавлю в 0.5.0: историю в формате OpenAI в API, окно из последних сообщений, накопленный риск по диалогу (против постепенного подведения к цели, когда каждое сообщение по отдельности безобидно) и отдельную проверку ответов инструментов и документов RAG. Честно скажу: полной защиты от постепенного подведения к цели классификатором нет ни у кого, поэтому здесь важна связка проверок на входе и на выходе.

Спасибо, очень по делу. Часть уже работает: свой классификатор подключается через PromptInjection(model=Model(path=...)), своя NER-модель с собственными типами данных — через recognizer_conf в Anonymize и Sensitive. Но полноценного интерфейса пока нет: категории инъекций не поддерживаются, NER-модель может быть только одна и только с Hugging Face, а сервер своих моделей почти не принимает. Беру это в следующую версию (0.5.0): свои метки и категории для классификатора инъекций, подключение любых распознавателей Presidio и нескольких NER-моделей, плагины через YAML-конфиг сервера и entry points. Если у вас есть конкретный сценарий — какие модели и типы данных, библиотека или API-сервер — напишите в issues, проверю на нём.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность