Skip to content

Add Dext.AI.Graph: LangGraph-style agent orchestration for Dext.AI.Agent - #202

Open
alepmedeiros wants to merge 4 commits into
dotpas:mainfrom
alepmedeiros:feature/dext-ai-graph
Open

Add Dext.AI.Graph: LangGraph-style agent orchestration for Dext.AI.Agent#202
alepmedeiros wants to merge 4 commits into
dotpas:mainfrom
alepmedeiros:feature/dext-ai-graph

Conversation

@alepmedeiros

Copy link
Copy Markdown
Contributor

Adds a graph-based orchestration layer (TAgentGraph/ICompiledAgent) on top of the existing Dext.AI.Agent ReAct runner, with immutable state, fixed and conditional edges, checkpointing (memory/file), and human-in-the-loop approval via RequireApproval/InterruptBefore + Resume/Cancel. Includes the GraphDemo console sample (and the untracked AgentDemo sample). Verified via live run: tool calls, HITL pause/resume, and cross-turn history through the checkpointer all behave as specified.

Adds a graph-based orchestration layer (TAgentGraph/ICompiledAgent) on top
of the existing Dext.AI.Agent ReAct runner, with immutable state, fixed
and conditional edges, checkpointing (memory/file), and human-in-the-loop
approval via RequireApproval/InterruptBefore + Resume/Cancel. Includes the
GraphDemo console sample (and the untracked AgentDemo sample). Verified via
live run: tool calls, HITL pause/resume, and cross-turn history through the
checkpointer all behave as specified.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@alepmedeiros

Copy link
Copy Markdown
Contributor Author

Summary

  • Adds Dext.AI.Graph, a LangGraph-style state-graph orchestration layer on top of the existing Dext.AI.Agent ReAct runner: TAgentGraph (StateGraph), immutable TAgentState, fixed/conditional edges, and ICompiledAgent execution (Run/Resume/Cancel/GetState).
  • Adds checkpointing (TMemoryCheckpointer, TFileCheckpointer) so conversation state persists across turns on the same thread ID.
  • Adds human-in-the-loop support: RequireApproval/InterruptBefore pause execution before a node and wait for Resume() or Cancel().
  • Adds the standard call_llm/execute_tools node pair (TLLMNode, TToolsNode) reusing the existing TMCPToolProvider RTTI-based tool pattern — zero new tool-registration mechanism.
  • Adds Examples/AI/GraphDemo, a console sample mirroring AgentDemo but wired through the compiled graph, with a filesystem tool provider for manual testing.
  • Zero modifications to existing Sources/AI/Agent/* units; zero external dependencies beyond the Delphi RTL and existing Dext units.

Test plan

  • Compiles clean (0 errors/warnings) on Win32 and Win64 via dcc32/dcc64.
  • Live run with OPENAI_API_KEY: tool-calling round trip (list_files) returns a correct answer.
  • Human-in-the-loop: RequireApproval('execute_tools') pauses before the tool node, resumes correctly on approval.
  • Checkpointer: a follow-up question on the same thread reuses prior tool-call context without re-invoking the tool.
  • execute_tools cancel path (Agent.Cancel) — not yet exercised manually.

alepmedeiros and others added 3 commits August 31, 2026 08:22
Lets a compiled graph be embedded as a single node in a parent TAgentGraph
(TAgentGraph.AddNode('x', SubAgent.AsNode)), enabling composition of
reusable sub-agents (e.g. a Fiscal sub-graph inside a larger ERP graph)
without flattening every sub-agent's nodes into the parent graph.

- TNodeContext/TNodeHandler moved from Dext.AI.Graph.Graph into
  Dext.AI.Graph.Contracts, since ICompiledAgent.AsNode needs to return
  TNodeHandler and Contracts can't depend on Graph (would be circular).
- TCompiledAgent.RunAsSubgraph runs the subgraph from its own entry point
  through its own GRAPH_END/IsDone directly against the shared TAgentState
  (no schema translation needed - state is not per-graph typed), then
  clears IsDone before returning so the parent's own edges decide what
  happens next.
- AsNode raises EGraphCompileError up front for graphs with
  RequireApproval/InterruptBefore - nested human-in-the-loop isn't
  supported yet, so this fails loudly instead of silently skipping
  the approval step.
- Fixed TAgentGraph.ValidateReachability to match ResolveNextNode's
  runtime fallback: a node with no outgoing edge implicitly reaches
  GRAPH_END. Without this, valid single-node terminal graphs (the
  minimal shape needed for a subgraph) were rejected at compile time
  with a false ECycleDetected.

Verified with a standalone harness (fake ILLMProvider, no API key
needed): state propagates from subgraph to parent, the parent continues
past the subgraph node via its own edges, the interrupt guard rejects
RequireApproval subgraphs, and an exception inside a subgraph surfaces
as grsError in the parent without corrupting state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GraphDemo previously only exercised the sequential/conditional-edge and
RequireApproval parts of Dext.AI.Graph, leaving two shipped primitives
completely unused anywhere in the repo: ICompiledAgent.AsNode and
TFileCheckpointer. Since the example is the reference for how to use the
framework, wire both in:

- Compile a small independent PolishGraph (single node, no tools) and
  embed it as the 'polish_agent' node of the main graph via
  PolishAgent.AsNode, routed to from call_llm's conditional edge instead
  of going straight to GRAPH_END. Its rewritten answer becomes the run's
  FinalAnswer by design (AsNode preserves FinalAnswer while clearing
  IsDone, so the parent's own edges decide what happens next).
- Switch the main checkpointer from TMemoryCheckpointer to
  TFileCheckpointer so the thread survives across process restarts, and
  print its path on startup.
- Add a ":estado" command to the input loop that calls Agent.GetState to
  inspect the persisted thread without running it.

Comment on the subgraph documents the one hard constraint: a subgraph
node can't itself use RequireApproval/InterruptBefore (AsNode raises
EGraphCompileError) - approval has to sit on the parent node that wraps
the subgraph call.

Compiles clean on Win32/Win64. Not yet live-tested against a real
OPENAI_API_KEY in this session - the polish_agent routing and FinalAnswer
override should be verified end-to-end before relying on it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds Docs/Book/16-ai-agents and its Docs/Book.pt-br mirror, following the
existing per-chapter README.md convention (see chapter 15 for MCP). Covers:
quick starts for both Dext.AI.Agent (single-agent ReAct, LangChain-style)
and Dext.AI.Graph (graph orchestration, LangGraph-style), the core type
table, human-in-the-loop, checkpointing, and subgraphs via AsNode - plus
an explicit LangGraph coverage table (what maps 1:1, what's a real gap:
no typed per-graph state schema, no conditional entry point, no
interrupt_after or dynamic interrupts, no update_state, no nested HITL,
no state history/time-travel, no cross-thread store, streaming only via
IAgentObserver callbacks, and a documented pitfall - a second AddEdge
from the same source node is silently ignored rather than fanning out).

Wires the new chapter into both Book/README.md and Book.pt-br/README.md
TOCs and example tables, and annotates Docs/roadmap/ai-roadmap.md to
point at what's actually implemented under the Dext.AI.Agent/Dext.AI.Graph
names, without rewriting the original Dext.SemanticKernel-branded roadmap
someone else wrote - just noting where the two now overlap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@alepmedeiros

Copy link
Copy Markdown
Contributor Author

Summary

  • Adds Dext.AI.Graph, a LangGraph-style state-graph orchestration layer on top of the existing Dext.AI.Agent ReAct runner: TAgentGraph (StateGraph), immutable TAgentState, fixed/conditional edges, and ICompiledAgent execution (Run/Resume/Cancel/GetState).
  • Adds checkpointing (TMemoryCheckpointer, TFileCheckpointer) so conversation state persists across turns on the same thread ID, and across process restarts with the file backend.
  • Adds human-in-the-loop support: RequireApproval/InterruptBefore pause execution before a node and wait for Resume() or Cancel().
  • Adds subgraph composition: ICompiledAgent.AsNode lets a compiled graph be embedded as a single node of a parent graph, for building larger systems out of independently-tested sub-agents. Nested human-in-the-loop is explicitly rejected (EGraphCompileError) rather than silently skipped.
  • Adds the standard call_llm/execute_tools node pair (TLLMNode, TToolsNode) reusing the existing TMCPToolProvider RTTI-based tool pattern — zero new tool-registration mechanism.
  • Fixes a compile-time validation bug (ValidateReachability) that rejected valid single-node terminal graphs, inconsistent with the runtime's own no-edge-means-GRAPH_END fallback.
  • Updates Examples/AI/GraphDemo to exercise every shipped primitive together: conditional routing, RequireApproval + Resume/Cancel, TFileCheckpointer persistence, a :estado command over GetState, and a polish_agent subgraph wired in via AsNode.
  • Documents both Dext.AI.Agent and Dext.AI.Graph as Book chapter 16 (Docs/Book/16-ai-agents, mirrored in Docs/Book.pt-br), including a coverage table against LangGraph (what maps 1:1, and real gaps: no typed per-graph state schema, no conditional entry point, no interrupt_after/dynamic interrupts, no update_state, no state history, no cross-thread store, and a documented pitfall where a second AddEdge from the same source node is silently ignored instead of fanning out). Annotates Docs/roadmap/ai-roadmap.md to point at what's now implemented under these names.
  • Zero modifications to pre-existing Sources/AI/Agent/* units; zero external dependencies beyond the Delphi RTL and existing Dext units.

Test plan

  • Compiles clean (0 errors/warnings) on Win32 and Win64 via dcc32/dcc64.
  • Live run with OPENAI_API_KEY: tool-calling round trip (list_files) returns a correct answer.
  • Human-in-the-loop: RequireApproval('execute_tools') pauses before the tool node, resumes correctly on approval.
  • Checkpointer: a follow-up question on the same thread reuses prior tool-call context without re-invoking the tool.
  • AsNode/subgraph mechanics verified with a standalone harness (fake ILLMProvider, no API key): state propagates parent→subgraph→parent, the parent continues past the subgraph node via its own edges, the interrupt guard rejects RequireApproval subgraphs, and an exception inside a subgraph surfaces as grsError without corrupting state.
  • polish_agent subgraph and TFileCheckpointer paths in the updated GraphDemo — compiled clean, not yet run live against a real API key.
  • execute_tools cancel path (Agent.Cancel) — not yet exercised manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant