Переезд с reactifact 0.1.x на 0.20.0¶
Эта страница — для приложений, которые уже используют reactifact. Если вы переносите код с LangGraph, CrewAI, LlamaIndex или обычного Python, начните с переезда с других фреймворков.
0.20.0 — ломающий релиз, но для большинства приложений миграция механическая: рантайм и commit DAG те же, а авторский слой становится явно типизированным. Старые имена ещё импортируются в окне совместимости 0.20; ниже — замены и что запланировать к 0.21.0.
1. Что изменилось¶
| 0.1.x | 0.20.0 |
|---|---|
Produce / @produce / ProduceCall |
Step / @step / StepCall |
Agent.produces=[...] |
Agent.steps=[...] |
контракты размазаны между Agent.consumes и Produce.reacts_to |
каждый шаг владеет consumes / produces |
call.trigger + ручные isinstance-проверки |
типизированные входы T / Artifact[T] или Consume(T) |
ручной context.get(stage_id(...)) |
scope= / Scope(...) + типы или Latest[T] |
self.effects.* |
effects: Effects в run() класса или call.effects в функции |
мутабельный Artifact с artifact.update() |
frozen-handle; изменения через Context / Effects |
artifact.history / .at() / .diff() |
Context.version_history / artifact_version / diff версий |
| v1 session payload | v2 blobs + data_ref; v1 мигрирует при загрузке |
reactifact.tool_use, reactifact.native_tool_use, reactifact.agent_tool |
reactifact.tools.use, reactifact.tools.native, reactifact.tools.agent |
Всё, что помечено как bridge, доступно в 0.20.x. Удаление планируется в 0.21.0, поэтому миграцию стоит закончить до этого релиза.
2. Шаги и агенты¶
Классовая форма сохраняет вид — объявления переезжают на шаг, а тело получает типизированные входы:
from reactifact import Artifact, Effects, Produce, Step
class Estimate(Produce):
artifact_type = Project
reacts_to = Project
async def produce(self, call: ProduceCall) -> None:
self.effects.update(call.trigger, stage="estimate")
становится:
from reactifact import Artifact, Effects, Step
class Estimate(Step):
consumes = (Project,)
produces = (Project,)
async def run(self, project: Artifact[Project], effects: Effects) -> None:
effects.update(project, stage="estimate")
Агент перечисляет шаги вместо produces:
становится:
Agent.produces остаётся свойством совместимости. Новый код использует
Agent.steps; для интроспекции читайте agent.steps.
Функциональная форма меняет @produce на @step, а self.effects — на
call.effects:
from reactifact import Answer, Evidence, step
@step(produces=Answer, consumes=Evidence)
async def answer(call):
return Answer(text=call.trigger.data.text)
Функции-шаги тоже могут принимать типизированные входы:
@step(produces=Answer, consumes=Evidence)
async def answer(evidence: Evidence, effects: Effects) -> Answer:
return Answer(text=evidence.text)
3. Типизированные входы и корреляция¶
В 0.1.x шаг начинался с проверок:
if call.event is None or not isinstance(call.trigger.data, Question):
return None
question = call.trigger.data
В 0.20.0 контракт входа — это аннотация типа:
class Research(Step):
consumes = (Question,)
produces = (Answer,)
async def run(self, question: Question, effects: Effects) -> Answer:
return Answer(text=question.text)
Используйте Artifact[T] вместо T, если нужны handle, id или провенанс.
list[Artifact[T]] — для всех подходящих версий типа.
Корреляция, которая раньше была ручным поиском по stable-id, теперь Scope:
class BuildAnswer(Step):
consumes = (
Consume(ResearchTurn),
Consume(Evidence, wakes=False),
)
produces = (Answer,)
scope = "query_id"
async def run(
self,
turn: ResearchTurn,
evidences: list[Artifact[Evidence]],
effects: Effects,
) -> None:
...
scope= держит артефакты с одинаковым query_id вместе. Consume(...,
wakes=False) заменяет чтение входа без перезапуска на каждое обновление.
Latest[T] выбирает самое новое значение, когда шагу не нужна вся история.
When(...) — условия по значению, Wakes(False) — не будящие входы.
4. Эффекты¶
self.effects удалён. Классовый шаг получает effects: Effects; функция
использует call.effects:
| 0.1.x | 0.20.0 |
|---|---|
self.effects.create(value) |
effects.create(value) |
self.effects.update(artifact, **fields) |
effects.update(artifact, **fields) |
self.effects.link(source, "relation", target) |
effects.link(source, "relation", target) |
self.effects.announce(...) |
effects.announce(...) |
Возврат BaseModel из run() — это create-sugar. Всё сверх создания
объявленного выхода — обновления, связи, дополнительные create, HITL — идёт
через effects. Объявления по-прежнему валидируют коммит: каждый созданный тип
должен быть в produces; первым идёт основной output, затем дополнительные
create: produces=(Receipt, PendingAction) (старое also_creates= — deprecated
алиас).
5. Артефакты и история версий¶
Artifact теперь frozen-handle в content-addressed хранилище версий. Не
мутируйте его и не вызывайте artifact.update().
| 0.1.x | 0.20.0 |
|---|---|
artifact.update(data) |
ctx.update(artifact.id, data) (возвращает новый handle) |
artifact.history |
ctx.version_history(artifact.id) |
artifact.at(version) |
ctx.artifact_version(artifact.id, version) |
artifact.diff(other) |
ctx.version_diff(...) / ctx.diff(...) |
Старые Artifact.at/history/diff остаются deprecated-bridges в 0.20.x.
Компакция авторитетна: ctx.compact(keep_versions=...) выбирает, какие версии
остаются читаемыми, а удалённые артефакты сохраняют читаемую историю. Код,
полагавшийся на мутабельный _history, должен читать версии через Context.
6. Сессии и персистентность¶
Session payload — v2: индекс версий артефактов плюс blobs по ключу
(data_type, content_hash), а commit-операции Create/Update используют
data_ref.
Для существующих хранилищ код менять не нужно: Context.from_dict загружает
v1 payload, а следующее сохранение пишет v2. Если вы разбираете payload
напрямую, читайте версии через публичные методы Context, а не через старый
layout data/history.
7. Импорты инструментов¶
Старые top-level модули — shims. Предпочитайте пути пакета:
from reactifact.tools import tool
from reactifact.tools.use import ToolUse, ToolUseHITL
from reactifact.tools.native import parse_tool_calls
from reactifact.tools.agent import AgentAsTool
| 0.1.x | 0.20.0 |
|---|---|
reactifact.tool_use |
reactifact.tools.use |
reactifact.native_tool_use |
reactifact.tools.native |
reactifact.agent_tool |
reactifact.tools.agent |
Shims остаются в 0.20.x и запланированы к удалению в 0.21.0.
8. Удалённые примеры¶
В дереве 0.1.x было много маленьких примеров, дублировавших одну и ту же идею.
0.20.0 оставляет десять reference-приложений: devops, knowledge, forklab,
repair, medic_lab, fintech_audit, starter_app, chat, outbox и
repo_agent; tour изолирует примитивы. Если старый
пример был вашей отправной точкой, возьмите ближайший из оставшихся и страницу
паттернов. Удалённые примеры не входят в публичный API.
9. Чеклист миграции¶
- Замените
ProduceнаStep,ProduceCallнаStepCall,@produceна@step. - Замените
Agent(produces=[...])наAgent(steps=[...]); перенесите контрактыAgent.consumesна соответствующие шаги. - Замените
self.effects.*на инжектированныйeffectsилиcall.effectsв функциях. - Замените проверки
call.trigger.dataна типизированные параметры,Consume(...),Latest[...]илиWhen(...). - Замените ручную корреляцию по stable-id на
scope=/Scope(...). - Замените
artifact.update()наctx.update(); историю читайте черезContext.version_history/artifact_version. - Обновите импорты инструментов на
reactifact.tools.*. - Найдите остатки:
rg "self\.effects|Agent\(produces=|\.update\(.*artifact|artifact\.history|reactifact\.tool_use|reactifact\.native_tool_use|reactifact\.agent_tool" .
- Запустите тесты и уберите
DeprecationWarningот@produceи history-bridges. Ваш код не должен порождать эти предупреждения. - Если вы храните сессии вне
SessionStore, проверьте, что сохранённый v1 payload загружается и перезаписывается как v2 при сохранении.
10. Совместимость и удаление¶
Остаётся в 0.20.x, удаляется в 0.21.0:
Produce,ProduceCall,@produceAgent.producesArtifact.at,Artifact.history,Artifact.diffreactifact.tool_use,reactifact.native_tool_use,reactifact.agent_tool
@produce выдаёт DeprecationWarning; алиасы и bridges — нет. Считайте их
помощниками миграции, а не вторым API.
Если для чего-то нет прямого соответствия, проверьте changelog 0.20.0 и страницу паттернов, прежде чем открывать issue.