Gabriel Luiz Freitas Almeida 93e5472a34 feat: make content_blocks the source of truth for Message content (#13364)
* feat: make content_blocks the source of truth for Message content

Migrate Message.text from a Pydantic field to a @computed_field over
content_blocks, and unify ContentBlock into the discriminated ContentType
union so a Message's payload is one uniform shape.

Schema changes:
- Add 7 new content types (Image, Audio, Video, File, Reasoning, Usage,
  Citation) with validators for media sources, non-negative tokens, and
  ordered citation indices
- Promote 'contents: list[ContentType]' to BaseContent so any node can
  nest (multimodal tool outputs, multi-step reasoning, grouped errors)
- Fold ContentBlock into BaseContent and into the ContentType union with
  tag 'group'; content_blocks is now 'list[ContentType]' everywhere
- Fix Data.__setattr__ to route through property descriptors via MRO walk

Setter / serialization:
- text setter appends a single TextContent at the end of content_blocks,
  preserving non-text blocks in chronological order (tool calls first,
  final text last)
- model_post_init preserves explicit None in data['text'] when no
  TextContent exists in content_blocks, so callers can still distinguish
  'text was never set' from 'text was set to empty string'

from_lc_message:
- Handle AIMessage tool_calls and usage_metadata regardless of whether
  content is a string or a list (tool-calling agents commonly emit
  content='' alongside tool_calls)
- Tolerate explicit source=None in multimodal image payloads

MessageResponse.from_message / MessageTable.from_message:
- Accept any of (data['text'] set, text_stream pending, content_blocks
  non-empty) as 'content present', so tool-call-only and media-only
  messages persist rather than getting rejected as missing required
  fields

Tests cover all new content types, the unified ContentType union, the
text/content_blocks contract, the setter's chronological append, and the
required-fields gate.

(cherry picked from commit 3b92500349)

* feat: stable id on content blocks + plumb LangChain tool_call_id

Adds an optional 'id: str | None' field to BaseContent for stable
identity across re-emissions of the same logical block. Producers
that have a natural id (LangChain tool_call_id, external API id, a
UUID stamped before the first emission) set it; consumers use it for
dedup and cross-frame correlation. Without an id, consumers fall back
to position-derived dedup, which assumes content_blocks is append-only
within a message lifetime.

Plumbs LangChain's 'tool_call_id' through 'Message.from_lc_message'
into 'ToolContent.id'. The same logical tool call across start, args
streaming, and result lifecycle now carries the same id, so a
re-fired add_message dedups to one ToolContent instead of producing
duplicates.

Tests cover id default/round-trip/inheritance across every concrete
content type, plus tool_call_id stability across repeated conversion,
multiple tool calls each keeping their own id, and tool_calls
alongside string content.

(cherry picked from commit a3e8b40811)

* fix(schema): MessageResponse parses microsecond timestamps and ContentBlock partial updates preserve unset fields

Two schema regressions surfaced in QA across the content-blocks chain:

1. MessageResponse.timestamp was typed as a bare datetime, but
   Message.timestamp default is a string with microsecond precision and
   a UTC timezone label ('%Y-%m-%d %H:%M:%S.%f %Z') that Pydantic's
   default datetime parser rejects. Any freshly built Message routed
   through MessageResponse.from_message raised ValidationError. Reuse
   the shared str_to_timestamp_validator so MessageResponse accepts
   every format Message itself recognises.

2. ContentBlock.__init__ marked every field as model_fields_set, not
   just the discriminator. The override defeated exclude_unset for the
   group content type: a patch like ContentBlock(title='new') dumped
   every defaulted field and, when merged onto an existing block by
   aupdate_messages, overwrote fields the caller never touched. Mark
   only 'type' (the discriminator) so partial updates carry the variant
   tag without clobbering the rest.

Adds regression tests in test_message_content_blocks.py: from_message
round-trips Message.timestamp without crashing, and ContentBlock
exclude_unset stays narrow to the explicit fields plus the
discriminator.

(cherry picked from commit 6f6639374f)

* fix(schema): address content_blocks review feedback

- sync langflow-base ContentBlock.__init__ with the lfx copy (model_fields_set
  parity) so exclude_unset no longer clobbers type; add cross-module regression test
- route MessageResponse content_blocks discriminator-first so stored flat blocks
  with contents=[] validate instead of raising
- move Message SecretStr coercion into model_post_init and drop the dead
  validate_text before-validator
- drop the no-op _fold_text_into_content_blocks validator
- log a shape-only debug line when from_lc_message drops an undecodable image
- type MessageResponse.content_blocks as list[ContentType] | None

(cherry picked from commit 6c7cec8a529c38d2f9eb7a35f94277611c8696cc)

* fix(agents): stop duplicating the final answer in content_blocks

With content_blocks as the source of truth, Message.text is a computed
field whose setter appends a trailing top-level TextContent. handle_on_chain_end
also appended the same answer into the Agent Steps group, so the final
answer rendered twice (assert 2 == 1 in test_multiple_events). The streaming
path already relies on the setter alone; make the non-streaming path match.

* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]

* feat(schema): project new content_blocks back to the v1 wire shape

The in-memory Message and the v2 (AG-UI) path use the new content_blocks
union (groups tagged "group", every node carries id/contents, the agent
answer is a trailing top-level TextContent). The v1 API keeps emitting the
pre-1.11.0 shape via a pure legacy_render projection that runs only at the
v1 boundaries: the v1 read/response models, the memories endpoint, the v1
build SSE stream, the webhook events SSE stream, and the /run response and
stream. The build, webhook, and /run projections recurse so the Data mirror
(data.data.content_blocks) is projected alongside the top-level copy. v2
serializes the live Message and keeps the new shape.

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* fix(api): keep simple_run_flow returning RunResponse, project v1 at the HTTP boundary

simple_run_flow is a shared helper, so wrapping its return in a JSONResponse to
apply the v1 content_blocks projection broke internal callers that call
.model_dump() on the result (the streaming run_flow_generator and
get_build_results). Return the RunResponse object from the helper and apply the
projection at the non-stream HTTP boundary in _run_flow_internal instead. The
streaming path already projects the end event via _project_run_event.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-25 13:39:55 -07:00
2026-06-25 18:33:53 +00:00
2026-06-09 13:16:48 -07:00
2026-06-25 18:33:53 +00:00
2026-06-25 18:33:53 +00:00
2025-03-20 00:05:55 +00:00
2026-04-23 17:49:53 -07:00
2026-06-25 18:33:53 +00:00

Langflow logo

Release Notes PyPI - License PyPI - Downloads Twitter YouTube Channel Discord Server Ask DeepWiki

Langflow is a powerful platform for building and deploying AI-powered agents and workflows. It provides developers with both a visual authoring experience and built-in API and MCP servers that turn every workflow into a tool that can be integrated into applications built on any framework or stack. Langflow comes with batteries included and supports all major LLMs, vector databases and a growing library of AI tools.

Highlight features

  • Visual builder interface to quickly get started and iterate.
  • Source code access lets you customize any component using Python.
  • Interactive playground to immediately test and refine your flows with step-by-step control.
  • Multi-agent orchestration with conversation management and retrieval.
  • Deploy as an API or export as JSON for Python apps.
  • Deploy as an MCP server and turn your flows into tools for MCP clients.
  • Observability with LangSmith, LangFuse and other integrations.
  • Enterprise-ready security and scalability.

🖥️ Langflow Desktop

Langflow Desktop is the easiest way to get started with Langflow. All dependencies are included, so you don't need to manage Python environments or install packages manually. Available for Windows and macOS.

📥 Download Langflow Desktop

Quickstart

Requires Python 3.103.14 and uv (recommended package manager).

Install

From a fresh directory, run:

uv pip install langflow -U

The latest Langflow package is installed. For more information, see Install and run the Langflow OSS Python package.

Run

To start Langflow, run:

uv run langflow run

Langflow starts at http://127.0.0.1:7860.

That's it! You're ready to build with Langflow! 🎉

📦 Other install options

Run from source

If you've cloned this repository and want to contribute, run this command from the repository root:

make run_cli

For more information, see DEVELOPMENT.md.

Docker

Start a Langflow container with default settings:

docker run -p 7860:7860 langflowai/langflow:latest

Langflow is available at http://localhost:7860/. For configuration options, see the Docker deployment guide.

🛡️ Security

For security information, see our Security Policy.

🚀 Deployment

Langflow is completely open source and you can deploy it to all major deployment clouds. To learn how to deploy Langflow, see our Langflow deployment guides.

Stay up-to-date

Star Langflow on GitHub to be instantly notified of new releases.

Star Langflow

👋 Contribute

We welcome contributions from developers of all levels. If you'd like to contribute, please check our contributing guidelines and help make Langflow more accessible.


Star History Chart

❤️ Contributors

langflow contributors

Description
Langflow is a powerful tool for building and deploying AI-powered agents and workflows.
Readme MIT 2.3 GiB
Languages
Python 64.5%
TypeScript 23.4%
JavaScript 11.4%
CSS 0.3%
Makefile 0.2%
Other 0.1%