Skip to content

Changelog

All notable changes to Flowgentic are recorded here. The format follows Keep a Changelog, and versions follow Semantic Versioning.

Tags v0.1.0 (3e43c13c, 23 Sep 2025) and 0.2.0 (72e972d4, 2 Oct 2025) predate this changelog, so their changes are not listed here.

0.4.0 - 2026-10-30

A repair release. It removes features that 0.3 shipped broken or unsafe, or that did not serve Flowgentic's purpose: running agent decisions as workflow tasks, checking their outputs and tracing them with the tasks. It adds acceptance of agent outputs and OpenTelemetry tracing.

Breaking changes. Each is detailed below. Where 0.3's behaviour can be kept, the entry says how.

  • import flowgentic no longer configures logging. To print Flowgentic's records, call flowgentic.configure_logging().
  • The logging utilities (flowgentic.utils.Logger, its formatters and filter, add_context_to_log) and the settings file (config.yml, FLOWGENTIC_CONFIG, flowgentic.settings) are gone. Configure logging in the application, or call flowgentic.configure_logging().
  • Each decorated call runs once: the retry loop, flowgentic.langGraph.fault_tolerance and the decorator's retry= are gone, and an unknown keyword raises TypeError. The nearest to 0.3's default of 3 attempts of at most 30 s each is, on a graph node, add_node(..., retry_policy=RetryPolicy(max_attempts=3), timeout=30), where timeout needs LangGraph 1.2 or later. LangGraph's default retry_on retries ConnectionError and an attempt that timed out, but not the other OSErrors that 0.3 retried: name the transient ones in retry_on. On a model call, the chat model's max_retries and timeout take its place; on an AsyncFlow task, a wrapper in the application.
  • flowgentic.academy is gone, and Flowgentic no longer depends on academy-py. Call Academy agents from AsyncFlow tasks directly, with academy-py>=1.0,<2 installed, as Academy actions as AsyncFlow tasks shows.
  • The node report is gone: flowgentic.utils.telemetry, LangraphIntegration.agent_introspector, generate_execution_artifacts and LangraphUtils.render_graph. Export the run's spans to a trace backend or a JSON-lines file instead, and draw a graph with LangGraph's app.get_graph().draw_png(path).
  • flowgentic.langGraph.memory, the integration's memory_manager and flowgentic.langGraph.mutable_graph are gone, with two attributes of ExecutionWrappersLangraph that nothing used. Bound an agent's history with LangChain's SummarizationMiddleware, ContextEditingMiddleware or trim_messages, and build a new StateGraph to change a graph between runs.

Added

  • OpenTelemetry spans that follow OpenTelemetry's GenAI semantic conventions, so an agent's decisions, the AsyncFlow tasks they run in and the model calls inside them form one trace:
  • Each call of an agent bound with bind_agent is an invoke_agent <identity> span, with the AsyncFlow task's uid and backend (flowgentic.task.uid, flowgentic.task.backend).
  • Every task and service Flowgentic submits runs in its caller's trace context. AsyncFlow runs a task's function in a pool thread or another process, where a model call used to start a trace of its own.
  • Model calls, tools and graphs are recorded by OpenTelemetry's GenAI instrumentation, and tasks by RHAPSODY, through flow.start_telemetry().

Flowgentic depends on opentelemetry-api>=1.43,<2 only: without the SDK, its spans are no-ops. The examples extra adds the SDK, the GenAI instrumentation for LangChain and OpenAI, and rhapsody-py[telemetry]. See Telemetry. - The AI-HPC demo's runners take --trace PATH, which writes the run's spans and events to PATH, one JSON object per line. examples/telemetry/cost.py computes cost from such a file in three ways: the cost a provider reported, when a span carries one; usage times a price, for a remote model; and a share of a deployment's GPU-hours, for a model on the allocation. - flowgentic.acceptance, which accepts or rejects an agent's output on explicit criteria, for runs with no human in the loop. accept(propose, *, checks=(), attempts=1, fallback=None, name=None): - asks the producer for a proposal, and runs the checks on it in order, stopping at the first rejection; - asks again, within attempts, handing the producer every earlier attempt so that it can build the re-ask; - returns an Outcome with the value, the disposition (accepted or fallback) and every attempt, or raises NotAccepted with the attempts when nothing passed and there is no fallback.

A check or the producer rejects an output with Rejected(reason, check=...) or a Pydantic ValidationError; any other exception propagates at once, with no further attempt. Each call is an accept <name> span with one accept.attempt <name> span per attempt, and each check that ran emits a gen_ai.evaluation.result event through OpenTelemetry's Logs API. A rejected value is recorded only when OpenTelemetry's GenAI content capture puts content on spans. See Acceptance. - The agentic AI-HPC demo's planner passes accept: three checks (the batch size, the bounds, distinct candidates), one re-ask with the reasons, and the surrogate's batch as the fallback. Its record of each decision holds the disposition and every rejected proposal, and the runner prints their count. The version without Flowgentic writes the same policy by hand, and the substitution tests check that both reach the same decisions on a re-ask and on a fallback.

Changed

  • Breaking: import flowgentic configures nothing. It used to set the root logger to DEBUG, add a handler that printed every record to stdout, start a thread and register an atexit hook; in test runs, that handler wrote to streams the runner had closed. The flowgentic logger now has no handler: without configuration, warnings and errors reach stderr through Python's last-resort handler, and the rest is the application's. To get Flowgentic's records back, call flowgentic.configure_logging(), which prints them in plain text to stderr; see Logging.
  • Log records follow a level contract: DEBUG for per-call detail, INFO for lifecycle events, WARNING for degraded results and refused requests, ERROR for failures. Per-call records that were INFO are DEBUG, such as the decorator's "Registering function … as AsyncFlow …". No record carries prompts, model outputs, message lists or graph states, at any level. Records use lazy %-style arguments, and CI checks it.
  • The AI-HPC demo's decision model keeps the model's raw message (with_structured_output(..., include_raw=True)), and rejects an answer it cannot use with Rejected: one that does not parse, a refusal, or one cut off at the token limit. Its messages no longer carry token counts set to zero, which claimed a measurement nobody made.
  • The documentation site follows the latest release: it deploys when the repository's latest GitHub release is published (not a pre-release, nor a patch to an older line), or by hand for a given release tag, no longer on every push to main. It stops describing changes that no release contains.
  • CI runs three design-pattern examples, sequential/research_agent, supervisor/toy and supervisor/product_research, with a scripted chat model in place of ChatLLMProvider, and checks that each completes. They need OpenRouter credentials, so CI had never run them. Live runs stay opt-in: make tests-integration-llm locally, or the LLM examples workflow, which a maintainer starts by hand on GitHub with the repository secret OPEN_ROUTER_API_KEY.
  • Dependencies are ranges, not pins: radical-asyncflow>=0.6,<0.7, where 0.3 required ==0.6.0, because before 1.0 an AsyncFlow minor release may break; langchain-openai and langchain-ollama at >=1.0,<2.0, like the other LangChain packages. The examples and dev extras have floors, and dev keeps pymdown-extensions below 12.2: 12.2 breaks mkdocstrings before 1.0.7, and 1.0.7 needs Python 3.11. CI runs the suite on the lowest versions the ranges allow. A weekly workflow runs the suite and the docs build on the newest releases within them, and the suite on the newest AsyncFlow and Academy releases beyond them. Dependabot updates uv.lock within the ranges, never the ranges themselves, and the workflows' actions.

Removed

Nobody uses Flowgentic yet, so these go without a deprecation period.

  • Breaking: the retry loop around every decorated call, with flowgentic.langGraph.fault_tolerance (RetryConfig, LangraphToolFaultTolerance) and the asyncflow decorator's retry= argument. Every tool, task, service and graph node ran in it, by default with 3 attempts of at most 30 s each, retrying OSError among other errors. On graph nodes it re-ran finished scientific work: with a simulation that kept failing, it made 24 attempts where the application's own policy made 8, and it cancelled and re-ran a node whose work took longer than 30 s until the cycle failed (substitution experiment, row 3). Each call now runs once. A model call's retries and deadline belong to the chat model (max_retries, timeout), a graph node's to LangGraph (add_node(..., retry_policy=..., timeout=...)), and a task's to the application until AsyncFlow has per-task retry, as in the AI-HPC demos. See Retries and timeouts. The decorator's keyword arguments are now explicit, so an unknown one, such as retry=, raises TypeError; tool_description was the only other.
  • Breaking: flowgentic.academy (AcademyIntegration) and its two examples. In 0.3 it could not run an Academy action through AsyncFlow: every call failed with ExchangeClientNotFoundError, and the examples failed at import. Repaired and then compared with direct Academy calls in AsyncFlow tasks, it added no capability: the same results, tasks and agent lifecycle, for 6 fewer lines of application code (Academy substitution check). The guide Academy actions as AsyncFlow tasks now shows the direct calls, with a tested example. Placing Academy agents on HPC resources belongs to RADICAL's execution layer, and an agent that drives the workflow to ADR. Flowgentic no longer depends on academy-py; the examples extra brings academy-py>=1.0,<2 for the example.
  • Breaking: the settings file: config.yml, looked up at import in the working directory, ~/.flowgentic/, the file named by FLOWGENTIC_CONFIG and the source tree, and the flowgentic.settings module. An unrelated config.yml in the working directory made import flowgentic fail with KeyError, and the file held only the log level, now the application's, and the report's paths, which went with the report.
  • Breaking: flowgentic.utils.Logger, its colored and JSON formatters, the base64 redaction filter and flowgentic.utils.add_context_to_log. Use flowgentic.configure_logging() or the application's own logging configuration; OpenTelemetry's logging instrumentation adds trace and span ids to every record.
  • The pyyaml and python-json-logger dependencies.
  • Breaking: the node report:
  • flowgentic.utils.telemetry (GraphIntrospector, its extractor, schemas and report generator);
  • LangraphIntegration.agent_introspector and generate_execution_artifacts;
  • LangraphUtils and its render_graph.

The report rebuilt each node's record from the graph's final messages after the run. So it saw no tasks, retries or rejected proposals, and it summed token counts that the demo had set to zero. The spans replace it; any trace backend that reads OTLP shows them, and so does a JSON-lines file. To draw a graph, call LangGraph's app.get_graph().draw_png(path). The design-pattern examples' committed agent_execution_results/ files are gone, and the examples' entry points return the final state. - Breaking: the memory manager and the mutable graph. Neither runs agent decisions or model calls as workflow tasks, checks their outputs or traces them, and LangChain and LangGraph cover both: - flowgentic.langGraph.memory (MemoryConfig, MemoryManager, LangraphMemoryManager, MemoryEnabledState), the integration's memory_manager, and the example sequential/research_agent_memory. Its trimming ignored the pairing of a tool call with its result, so every strategy could return a conversation that a chat API rejects: trim_last could keep a tool result whose call it had dropped, and importance_based reordered the messages by score. "summarize" made a blocking model call on the event loop, and five parameters had no effect. For an agent built with create_agent, SummarizationMiddleware and ContextEditingMiddleware from langchain.agents.middleware bound the history and keep each tool call with its result; in a graph of one's own, langchain_core.messages.trim_messages does. - flowgentic.langGraph.mutable_graph (MutableGraph) and its example. It changed no running graph: each edit recompiled a new sequential StateGraph from a list of node names, and refused edits returned False. Within a run, LangGraph routes with conditional edges, Send and Command(goto=...); between runs, build a new StateGraph. - ExecutionWrappersLangraph.react_agents and service_registry, which nothing used.

Fixed

  • langgraph 1.1.5 or later is required; 0.3 allowed 1.0. langgraph 1.0 to 1.1.4 install with the newest langgraph-prebuilt 1.0.x, which imports names that langgraph 1.1.5 added, so importing Flowgentic's LangGraph integration failed with ImportError.
  • The supervisor/toy example hid the error of a failed run: when the graph raised, the finally block that writes the report read a result that was never set, and the run ended in UnboundLocalError. The graph's own error now propagates.
  • The sequential/research_agent example exited 0 when a stage failed: its nodes route failures to the error handler, so a live run in which every model call failed, for want of a key or in an outage, passed tests/integration/test_integration.py, which checks only the exit code. A run that does not complete now raises RuntimeError with the stages' errors.

0.3.1 - 2026-10-23

A patch release: fixes for the defects that 0.3.0's documentation lists.

Fixed

  • Memory, trim_middle: with one free place beside the system messages, every message was kept. The strategy now fills every free place, and the most recent messages take the odd place of an odd count.
  • Memory, importance_based: after the first trim, each message was scored by another message's importance. The scores now follow the trimmed history, which also corrects the importance weighting in get_relevant_context.
  • Memory, summarize: the summary could take the history one message over the limit. It now fits within the limit. With one free place, or when no summary is made, the strategy trims as trim_last does; a failed summary used to leave its place empty, or keep the oldest messages.
  • MutableGraph.update_graph returns False for a replacement node that is already active. It used to list the node twice, and the rebuild raised LangGraph's ValueError.
  • generate_execution_artifacts(generate_report=...) was inverted: True skipped the report. True now writes it and False skips it, and the default becomes True, so a call without the flag still writes the report.
  • Telemetry: tool calls are read from AIMessage.tool_calls, and malformed ones from invalid_tool_calls. Only OpenAI-format additional_kwargs were read, so other models' tool calls were missing from the report.
  • Telemetry: an AI message's text is recorded as the node's final response even when the message has no additional_kwargs, and list content is joined into text.

0.3.0 - 2026-10-22

A consolidation release. It preserves the work since 0.2.0 as a supported, documented snapshot: fixes and evidence, no new features beyond those already on main.

Added

  • flowgentic.agent.bind_agent: binds a framework-specific agent component to an AsyncFlow task on a named backend; each call runs the component as that task and returns its structured decision with the agent's identity and timing.
  • Named AsyncFlow backends: Flowgentic's asyncflow decorator takes backend= and passes it to AsyncFlow, except for EXECUTION_BLOCK.
  • Service tasks (AsyncFlowType.SERVICE_TASK) for resident AsyncFlow services, and MutableGraph (flowgentic.langGraph.mutable_graph) for graphs changed at runtime.
  • AI-HPC coupling demos in examples/ai-hpc-coupling/: a RADICAL-only baseline, a deterministic LangGraph version, an agentic version and an agent-augmented version, with offline rehearsal decisions and optional ADR control.
  • --integration direct for the agentic demo (direct_agentic_application.py) runs the same agent graph on AsyncFlow without Flowgentic, and the run's campaign_summary.json records which integration ran. The substitution experiment compares the two (tests/integration/test_substitution.py, docs/evaluation/substitution.md), including LLM tool selection and an agent handoff driven by a scripted chat model.
  • An examples extra (python-dotenv), included in dev.
  • CI: a strict MkDocs build job, and Python 3.12 in the test matrix.
  • Tests that run the offline mutable-graph and service-task examples (tests/integration/test_feature_examples.py).
  • This changelog.

Changed

  • Breaking: the decorator API moved from integration.agents.asyncflow(flow_type=TOOL | NODE | UTISL_TASK | BLOCK) to integration.execution_wrappers.asyncflow(flow_type=AGENT_TOOL_AS_FUNCTION | FUNCTION_TASK | SERVICE_TASK | EXECUTION_BLOCK). Execution reports moved from integration.agent_logger to integration.agent_introspector (flowgentic.utils.telemetry).
  • Python 3.10 or later is required (0.2.0 allowed 3.9). CI tests 3.10, 3.11 and 3.12.
  • Dependencies are released versions:
  • radical-asyncflow==0.6.0 and academy-py==0.3.0 come from PyPI instead of git URLs.
  • LangGraph, langchain-core and langchain move to 1.x (>=1.0,<2.0); the lock tests LangGraph 1.2.16 and langchain-core 1.6.13.
  • python-json-logger>=3.1.0.
  • pyyaml is declared.
  • python-dotenv moved from the core dependencies to the examples extra.
  • LangGraph 1.x's ToolNode re-raises tool errors by default, where 0.6 returned them to the model as messages. A Flowgentic tool that still fails after its retries now aborts the graph run; pass handle_tool_errors=True to ToolNode to keep the 0.6 behaviour.
  • The LangGraph examples build their agents with langchain.agents.create_agent, which replaces LangGraph's deprecated create_react_agent.
  • The AI-HPC demo launchers use the installed AsyncFlow, and import a local checkout only when ASYNCFLOW_SRC is set. make examples-ai-hpc-deterministic and make examples-ai-hpc-agentic create the environment and run in a fresh clone.
  • ADR mode (--controller adr) is documented as unsupported. It needs an ADR source checkout (ADR_SRC).
  • Development uses uv and the checked-in uv.lock. CI checks formatting with the locked Ruff, and builds the documentation strictly from the locked environment.
  • The documentation site describes 0.3.0: its status, a getting-started page, guides for bind_agent, for LangraphIntegration and for LangGraph on AsyncFlow without Flowgentic, a project status page and this changelog. Its code blocks include tested files, and the strict build also fails on broken links and anchors. The README matches the site's home page.

Removed

  • The langchain-community and langchain-mcp-adapters dependencies, which nothing imported.
  • The flowgentic console script, whose target flowgentic:main did not exist.
  • The committed results folder examples/ai-hpc-coupling/agentic_demo_results/.
  • Two unused create_react_agent imports from flowgentic.langGraph.
  • Documentation pages for code that is not on main: MCP tools (AGENT_TOOL_AS_MCP, archived as archive/feature/mcp), the framework × backend roadmap, the hierarchical design pattern, which has no example, and the chatbot pattern, whose interactive example no test runs.

Fixed

  • Installing the wheel with pip. It could resolve python-json-logger 2.x, which lacks the pythonjsonlogger.json module Flowgentic imports, and pyyaml was imported but not declared.
  • The agentic demos' candidate policy no longer rejects a proposal at a bound for rounding alone, and never returns a candidate outside its bounds.
  • create_task_description_handoff_tool: every handoff failed with AttributeError, because the tool called a GraphIntrospector method removed in October 2025. The call is gone, and handoffs work, with Pydantic and TypedDict graph states (such as create_agent's).
  • The LangGraph examples import again: they used ConcurrentExecutionBackend, which AsyncFlow 0.3.0 and later do not export, and now use LocalExecutionBackend.
  • Seven mkdocs build --strict warnings: three missing annotations and four broken links.
  • The mutable-graph example's conditional graph always took the standard route: its routing flag was missing from the state schema that the graph and the router read.
  • The supervisor toy example stored agent B's result as agent_b, and its gather node, which reads agent_B, passed "N/A" to the synthesis model.
  • CI:
  • astral-sh/setup-uv is pinned to an existing release (it publishes no v9 tag).
  • Each test matrix job runs its own Python with the dev extra.
  • The bandit job runs again.