Символы верхнего уровня, экспортируемые reactifact (см. reactifact/__init__.py).
Формат по группам: имя — роль в одну строку. Детали — в док-строках модулей —
см. автосгенерированный референс (только на английском,
докстринги в коде не переведены): сигнатуры, типы и полный текст докстринга
рендерятся прямо из исходников на сайте документации, а не копируются сюда
вручную.
reactifact всё ещё pre-1.0 (0.20.1), но не rc — поверхность ниже это
стабильный контракт, а не движущаяся цель. Он проверяется машиной:
tests/test_public_api.py фиксирует каждый __all__, перечисленный здесь,
поэтому новый экспорт добавляется осознанно (и документируется на этой
странице), а не случайно.
Публичный API = каждое имя в reactifact.__all__ (и в __all__ каждого
подмодуля — reactifact.recipes, reactifact.providers, reactifact.viz,
reactifact.eval, reactifact.tracing, reactifact.quick,
reactifact.redaction, reactifact.audit, …) — это ровно тот набор символов,
что задокументирован на этой странице и зафиксирован freeze-тестом. Если
что-то импортируется из reactifact, но не входит в __all__ — это
внутренняя деталь без гарантий совместимости. Например,
reactifact.relations.RelationGraph и reactifact.commit_log.CommitLog
существуют потому, что Context разбили на модули поменьше ради
читаемости, но ни один из них не экспортируется: поддерживаемая
поверхность — это Context, а не они.
SemVer в pre-1.0-стиле: минорный бамп (0.20.1 → 0.21.0) может добавить
символы или, в редких случаях, изменить поведение так, что CHANGELOG.md
явно пометит это Breaking — минорные релизы до 1.0 всё ещё позволяют
reactifact исправить архитектурную ошибку. Патч (0.14.0 → 0.14.1) никогда
не убирает и не переименовывает публичный символ и никогда не меняет
задокументированное поведение — только чинит баги относительно него.
Депрекейшен: символ на пути к удалению остаётся импортируемым и работает
минимум один минорный релиз; он помечается deprecated в docstring и в
CHANGELOG.md (с заменой) до любого удаления, а удаление происходит только в
минорном релизе под заголовком ### Breaking.
Любое ломающее изменение помечено в CHANGELOG.md заголовком
### Breaking, даже в pre-1.0 релизе — см. release.md. Если
перед апгрейдом читать только один раздел — читайте этот.
1.0 означает, что эта страница замирает: критерии релиза —
это планка выхода из pre-1.0; после него действует SemVer 1.x (никаких
ломающих изменений в миноре).
Всё, что находится под reactifact.cli.* за пределами задокументированных
подкоманд python -m reactifact …, а также любые тест-хелперы модулей —
деталь реализации, даже если формально импортируется.
Тонкий сахар над примитивами ниже для четырёх частых первых задач — у каждого
объекта есть настоящий .agent/.agents и .context запуска, поэтому он
выпускается в Consume/Step/Effects без переписывания. См.
Быстрый старт §0.
Символ
Роль
agent(system, schema)
один структурированный вызов LLM → один типизированный артефакт
rag(sources)
поиск → материализация → ответ, с провенансом supported_by
tools_agent(system, tools, human=False)
LLM + тулы (human=True → HITL-вопросы)
chat_agent(agents)
настроенный ChatAssistant (по умолчанию store в памяти)
Question / Doc / Answer
обобщённые модели артефактов, которые использует фасад
версионируемое рабочее состояние; ресурсы; request (per-request mapping активного хода); запросы; latest(Model); announce; diff/rollback; compact(keep_commits=, keep_versions=) для ограничения истории (compacted_at); apply_commit(commit) проигрывает коммит в живое состояние (путь загрузки append-only сессии)
View
результат типа-запроса (context.view(...))
RuntimeResources
провайдеры + источники + ресурсы приложения; register(Type, instance) / get(Type) / require(Type) / has(Type) для типизированных коллабораторов (ResourceKey[T], когда их два одного типа); строковые get/set остаются escape hatch'ом (additional); redactor= вычищает текст трейсов; trace_truncate= ограничивает JSON артефактов/ответов в трейсе (None — без обрезки) — deprecated fallback (даёт DeprecationWarning), лучше TraceLimits(max_data_chars=…); await resources.aclose() закрывает HTTP-клиенты llm/embedder (duck-typed) — вызывайте сами при завершении; record_tool_args=True пишет аргументы тулов в ToolRun (вычищаются redactor=)
ResourceKey[T]
типизированный ключ для регистрации двух ресурсов одного типа (primary/replica, per-tenant)
ResourceScope / RuntimeResources.scope(factory)
async with-билдер: создаёт ресурсы на текущем loop и закрывает на выходе — loop-safe способ владеть провайдерами
current_request()
request-маппинг активного хода (то же, что StepCall.request)
стабильная персистентная идентичность типа: переименование/перенос модели больше не ломает загрузку старых сессий/коммитов/событий. type_id_of/resolve/migrate_payload; дефолтный id — qualified name
Patch
скомпилированный набор изменений рантайма (транспорт); шаги пишут эффекты (effects/call.effects), Patch собирает рантайм
тонкий контейнер: name, steps, concurrency_limit; триггеры выводятся из consumes шагов
create_agent
конструктор-фабрика агента — без подкласса для обычных контейнеров
Wakes(False) / Scope / Consume / consume
декларативная (или декоратор) завязка реакции; Consume.by_field для скоуп-событий; Consume.by_base / include_subclasses=True будит на наследниках marker-базы; wakes=False — читать как вход, не будя агента; debounce=True схлопывает несколько событий одного поколения в один запуск
reactifact.consume.CorrelatedConsume
срабатывает (и питает входы) только для ключа корреляции, где присутствуют все типы из require и отсутствуют все из forbid — механизм за JoinConsume/AbsentConsume
reactifact.consume.JoinConsume(*parts, key=…)
фабрика над CorrelatedConsume: срабатывает, когда для одного ключа существуют все перечисленные типы
фабрика над CorrelatedConsume: срабатывает для type, только если для того же ключа ещё нет absent_type
Step / @step
единица работы: класс run(...) получает типизированные входы + effects: Effects; функция @step получает call: StepCall и пишет через call.effects. consumes объявляет входы/триггеры, produces — разрешённые Create-типы; возврат модели — create-сахар. Annotated[T, When(...)]/Consume.by_* задают условия; дополнительные create — в том же tuple (produces=(Primary, Extra)); also_creates=/reacts_to= — deprecated алиасы
Trigger
вторичное (не артефактное) условие входа produce; context_condition(artifact, context) — для условий, которым нужны другие артефакты (join/корреляция); флаг debounce, который читает Runtime
блокирующий цикл LLM+инструменты (system, tools, max_steps, deferred_tool_groups)
HITLLMAgent
цикл LLM+инструменты с паузами на ответ человека (max_asks, отчёт о возобновлении)
ToolUse, ToolUseHITL
produce цикла инструментов; HITL-вариант ждёт одобрения перед исполнением
DeferredToolGroup (reactifact.tools.use)
группа инструментов, чьи схемы не попадают в промпт, пока встроенный инструмент load_tools в ToolUse их не запросит (только LLMAgent/ToolUse, не HITL-вариант)
Tool, FunctionTool, tool, ToolOutput, ToolError
абстракция и регистрация инструментов; ToolOutput.failed(ToolError(code=, message=, retryable=)) даёт типизированную ошибку, а ToolOutput.error остаётся обычной строкой
ToolAnswer, Observation
результаты инструментов и наблюдения модели (протокол цикла)
PendingQuestion
HITL-примитив: приостановленный вопрос, ждущий ответа человека, возобновляется через effects.resume(...)
Effects.act(kind, *, key, payload=…)
outbox-примитив: записывает намерение PendingAction (стабильный id action:{key}) вместо I/O в produce; None, если уже существует
PendingAction
записанный внешний side effect (kind, idempotency_key, payload, status, attempts); выполняется один раз через Runtime(dispatcher=...), затем dispatched / failed (retry_action возвращает в очередь)
будит агентов по событиям; run / arun / astream (каждый принимает request=Mapping); бюджет и параллельность; isolate_errors=True + on_agent_error(agent, event, exc), чтобы исключение одного агента не обрывало весь запуск (по умолчанию — пробрасывается, §69); dispatcher=async (context, action) дренирует outbox PendingAction после каждого коммита (один раз на стабильный id, удерживая ход живым, пока outbox не пуст); on_dispatch_error(action, exc) срабатывает, когда диспетчер падает (действие помечается failed, исключение пробрасывается); flush_pending_actions() дренирует без поколения (например, после merge()); Budget.max_seconds — жёсткий дедлайн хода (отменяет генерацию в полёте)
read-only снимки активных ходов процесса (RunInfo) и точечная отмена — readiness/ops, только внутри процесса
StepCall.request
request-маппинг хода внутри шага (Runtime.arun(request=…) / ChatAssistant.stream(request=…)); пустой, если не задан
Budget, RunOutcome, RunStats, BudgetTracker
лимиты запуска (max_runs/max_iterations/max_seconds/max_tool_calls/max_tokens/max_cost) и итог/статистика usage (RunOutcome включает budget_time_exceeded и stopped). Лимиты токенов/стоимости считают usage LLM без трейсера; для стоимости нужен инжектированный Pricer (reactifact.pricing)
сессии + цикл хода + история в одном handle (stream/invoke/history); хуки: agents, user_message, reply, session_state; dispatcher=/on_dispatch_error= пробрасываются в Runtime (outbox-эффекты прямо из чат-хода)
ChatMemory
необязательная ограниченная память для ChatAssistant, поля независимы: keep (обрезать сырой поток), summarize+summary_type (растущий дайджест), compact_commits (Context.compact — ограничивает историю, не трогая артефакты); message_types=[UserQuery, Answer] (или marker-база) ограничивает гетерогенный поток
ChatEvent
один транспорт-нейтральный фрейм; kind — закрытый Literal["session","status","message"], схема интроспектируема
записывает каждый LLM-вызов в JSONL или воспроизводит их точно; ReplayMiss при расхождении
ReplayMiss
воспроизводимый вызов не совпал с записью
verify_run(build, *, recording=None, repeat=2)
прогоняет build(resources) -> Contextrepeat раз под записанной моделью и строгими id (counter_ids), возвращает ReproReport; ok=False, если context_hash разошёлся (реальная недетерминированность)
counter_ids()
детерминированный id_factory (Model:0000, Model:0001, …) для RuntimeResources(id_factory=…) — делает артефакты без явного id воспроизводимыми
replay_context(store, session_id, version=None)
восстанавливает состояние сохранённой сессии на коммите
replay_summary(context)
компактная сводка состояния для CLI replay — включает dispatched_actions / pending_actions / failed_actions (replay никогда их не пере-отправляет)
прогон target по каждому примеру со скорингом; target возвращает Context, ScenarioResult, mapping выходов или RunResult; on_result(result) отдаёт каждый посчитанный EvalResult (при конкурентности — в порядке завершения)
ResultCallback
type alias Callable[[EvalResult], None] для прогресс-хука on_result
один структурный вызов; None при честном сбое; validate(model)->bool добавляет домен-проверку, повторяемую как parse-сбой; repair(invalid_or_None, last_reply)->str даёт инструкцию для повтора; on_error(reason, exc) ("no_provider"|"provider_error"|"parse_error"|"validation_error") — понять почему, не меняя контракт None
строгий рендер {var}: объявленные variables, KeyError при нехватке, поля атрибутов модели ({question.text}); подставляются только placeholder'ы-идентификаторы — литеральный JSON {"name": …} / {} в промпте остаётся как есть, без экранирования; .hash — стабильный sha256 шаблона
MessagesPrompt([(role, template), …])
рендерит чат-последовательность в list[Message]; .hash покрывает все строки
Каждый провайдер лениво создаёт httpx.AsyncClient и перепривязывает его к
текущему event loop, поэтому один экземпляр провайдера переживает повторные
asyncio.run(...) и per-test loop'ы (без RuntimeError: Event loop is closed);
aclose() закрывает клиент текущего loop. То же — для WebSource и
OTLP/Langfuse-приёмников.
эмбеддинги/TTS/STT для вендоров, чьи не-чат эндпоинты подтверждённо OpenAI-совместимы (см. providers)
Message, Role
одно сообщение чата; role — закрытый Literal + фабрики Message.system/user/assistant/tool
LLMRequest
одна генерация: messages + temperature/max_tokens — None = дефолт провайдера (вызов перекрывает провайдера, провайдер None = поле не отправляется)
LLMResponse, LLMResponseChunk
результат одной генерации / один поточный чанк, возвращаемые провайдером
*_from_env(**overrides)
подключение из .env; возвращает None, если не настроено
CachingLLM, KVCache (reactifact.cache)
точный кэш complete(): повторный запрос отвечается без провайдера (cache= — для персистентности), хит не списывается бюджетом; не семантический, stream() не кэшируется
RouterLLM, retryable_only (reactifact.routing)
failover провайдеров: пробует по порядку, переходит при сбое (should_fallback=/on_fallback=); только complete()
from_env(**overrides)
выбор в один вызов: сперва OPENROUTER_API_KEY, иначе OPENAI_BASE_URL, иначе None — тот самый двухветочный дефолт, что каждый пример вручную собирает в своём build_llm()
многоходовой разговор, обобщённый над моделью сообщения (вашей или дефолтной ConversationMessage): добавлять ходы, рендерить поток в провайдерские Message (хелперы turn_count / conversation_state)
Transcript / MessageSpec
read-only view для приложений с разными типами на стороны (MessageSpec(Question, "user"), MessageSpec(FinalResponse, "assistant", render=…)): сливает типизированные ходы по ролям в один Message-список (messages / state)
llm_summarizer(system=…)
строит колбэк WindowSummarizer(summarize=…) из системного промпта через llm_reply
opt-in цикл native tool-calling поверх reactifact.tools.native: ограниченные раунды, параллельное выполнение тулов, дожим обязательного тула, форсированный финальный ответ; соблюдает Budget хода (max_tool_calls, max_seconds, токены/стоимость); ограничивает наблюдения (max_observation_chars), опционально редактирует их через resources.redactor, повторяет ToolError(retryable=True) при retry_tool_errors=True, принимает parse_calls-кодек (например, текстовый DSML), fallback для markup-ответов и on_event-хук для стриминга; возвращает ToolLoopResult (текст, транскрипт, ToolObservation, stop_reason/error)
долгоживущая память чата между запросами; SessionStore(backend, compact_every=N) ограничивает append-журнал (примерно каждые N сохранений снапшот перезаписывается)
key/value чекпоинты под сессии (pg extra для Postgres) — async-native: файловый I/O уходит в отдельный поток, SQLite/Postgres держат одно постоянное соединение (WAL + busy_timeout у SQLite) под asyncio.Lock; SQLite/Postgres также реализуют AppendBackend (снапшот + append-only журнал коммитов, сохранение пишет только новые коммиты)
примитивы трейсинга (async-приёмники: export/query/get; AgentSpan.event_artifact_type/event_artifact_id — артефакт-триггер, AgentSpan.contract — объявленные consumes/produces/tools/priority, AgentSpan.produces — отработавшие StepRun (ProduceRun alias), а AgentSpan.tools — ToolRun; get(span_limit=…, span_offset=…) постранично читает большой run и отдаёт span_total/has_more_spans/trace_truncated; оба стора умеют delete_run/delete_session)
LangfuseTracer, OTLPTracer, PostgresStore
внешние приёмники трейсов — OTLPTracer вендор-нейтральный (GenAI semconv, любой OTLP/HTTP-коллектор), LangfuseTracer заточен под Langfuse, Postgres поддерживает async чтение+запись; дашборд (create_trace_router) принимает любой TraceReader
TraceLimits
write-time лимиты трейса для Tracer(..., limits=TraceLimits(max_spans=5000, max_refs_per_span=50, max_llm_calls_per_span=20, max_data_chars=None)); max_data_chars=None берёт legacy-значение из RuntimeResources.trace_truncate; отброшенные данные помечают трейс trace_truncated
TraceColumn
настраиваемая колонка таблицы из артефактов для create_trace_router(store, columns=[…]): label, field (точечный путь, список→первый), agent, type (класс или имя), direction read/write/any, index, scope run/session, default
Prometheus-метрики без зависимостей: счётчики/гистограммы (runs_total, agent_runs_total, llm_calls_total, llm_tokens_total, llm_cost_total, artifacts_written_total, relations_total, латентности…) из трейс-хука; render() выдаёт text exposition, create_metrics_router отдаёт GET /metrics. RuntimeResources(metrics=…) открывает тот же sink produce'ам (по умолчанию no-op)
scenario-харнесс: посев артефактов, прогон агентов, ассерты (артефакты/тулы/путь/отношения/llm/ошибки/события), mode= live/record/replay. Инъекция сбоев и заглушек: fail/stub_tool (тулы) и fail_resource/stub_resource (любой ресурс по строковому имени или типизированному Type/ResourceKey), у каждого times/when/delay; у stub_resource — returns/side_effect. run_sync/turn_sync повторяют async-API для обычных тестов
группы ассертов ScenarioResult, Scenario
result.artifacts(Type) (.linked(relation, Type) для провенанса), result.relations (has/none/count/outgoing/incoming), result.tools, result.path, result.llm, result.errors, result.events. result.explain() выводит весь прогон
итоги хода (или агрегат по многоходовому scenario): llm_calls, prompt_tokens/completion_tokens/total_tokens, tool_calls, spans, agents, errors, outcomes, с to_dict() и render(). result.llm также даёт prompt_tokens/completion_tokens/tokens
current_result()
последний ScenarioResult, произведённый этой задачей — чтобы инструменты (CLI) могли показать состояние прогона, когда падает более поздний ассерт
заморозка/сверка GoldenRun; файловый вариант заводит снапшот, если его нет (или update/$REACTIFACT_GOLDEN_UPDATE), а ScenarioResult.assert_golden(path)/Scenario.assert_golden(path) — обёртки над ним
replay_resources(recording, *, base=…)
RuntimeResources, чей llm реплеит запись — офлайн-регресс на реальном прогоне
reactifact.testing.pytest_plugin
opt-in pytest-плагин (pytest_plugins = ["reactifact.testing.pytest_plugin"]): гоняет async def-сценарии без pytest-asyncio, превращает ScenarioSkip в skip, даёт фикстуру-фабрику scenario_lab