From ab06045cd224586e3477adf4e2501050c96a829a Mon Sep 17 00:00:00 2001 From: hacka0wi <124970567+hacka0wi@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:29:59 +0700 Subject: [PATCH 1/2] =?UTF-8?q?feat(flow):=20add=20AI=20node=20=E2=80=94?= =?UTF-8?q?=20in-flow=20LLM=20completion=20(Claude/OpenAI/Kimi)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a 25th flow node that calls an LLM from inside a DAG. Provider, API keys, and default models resolve from the project secret vault exactly like the in-app assistant (CLAUDE_API_KEY / OPENAI_API_KEY / KIMI_API_KEY, *_DEFAULT_MODEL, CLAUDE_DEFAULT_PROVIDER), or per-node overrides. Engine: - new internal/llm package — provider-agnostic client (Anthropic Messages API + OpenAI-compatible chat completions), standard-library only, no vendor SDK. Endpoints are vars so tests can target httptest. - executeAINode + dispatch case models.JobTypeAI in flow_executor.go. Prompts are already ${{...}}-substituted by the time dispatch runs; optional JSON-object mode parses output to ${{nodeId.json.field}}. Output carries response/model/provider/stopReason/usage. Runs through the existing retry + circuit-breaker path. - 5 new tests (provider request shaping, JSON mode, token cap, upstream error, validation). UI: - AINode + AINodeProperties, registered in nodes/properties indexes. - new "AI" palette category (Sparkles icon, #8B5CF6), icon/color in BaseNode. - node type 'ai' follows the existing nodeType.toUpperCase() === JobType convention, so it serializes to "AI" with no mapping changes. Docs: README features (24→25 + AI bullet), CHANGELOG [Unreleased], CLAUDE.md, in-app API docs node list. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 14 + CLAUDE.md | 2 +- README.md | 8 +- .../internal/executor/flow_executor.go | 184 ++++++++++ apps/runloop-engine/internal/llm/client.go | 318 ++++++++++++++++++ .../internal/llm/client_test.go | 189 +++++++++++ .../internal/models/scheduler.go | 1 + .../(protected)/p/[projectId]/docs/page.tsx | 2 +- .../src/components/flow/FlowCanvas.tsx | 5 +- .../src/components/flow/nodes/AINode.tsx | 26 ++ .../src/components/flow/nodes/BaseNode.tsx | 4 +- .../src/components/flow/nodes/index.ts | 4 +- .../flow/properties/AINodeProperties.tsx | 140 ++++++++ .../src/components/flow/properties/index.ts | 4 +- 14 files changed, 894 insertions(+), 7 deletions(-) create mode 100644 apps/runloop-engine/internal/llm/client.go create mode 100644 apps/runloop-engine/internal/llm/client_test.go create mode 100644 apps/runloop/src/components/flow/nodes/AINode.tsx create mode 100644 apps/runloop/src/components/flow/properties/AINodeProperties.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 510d653..642fd45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ versioning: [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Added + +- **AI node** — a new flow node that calls an LLM (Claude / OpenAI / Kimi) + from inside a DAG. Provider, API keys, and default models resolve from the + project secret vault exactly like the in-app assistant + (`CLAUDE_API_KEY` / `OPENAI_API_KEY` / `KIMI_API_KEY`, + `*_DEFAULT_MODEL`, `CLAUDE_DEFAULT_PROVIDER`), or per-node overrides. + Prompts support full `${{...}}` variable substitution; an optional + JSON-object response mode parses the output so downstream nodes can read + `${{nodeId.json.field}}`. Output also carries `model`, `provider`, + `stopReason`, and token `usage`. Runs through the engine's existing retry + and circuit-breaker machinery. New engine package `internal/llm` + (standard-library only, no vendor SDK). + ## [0.1.3] — Patch release Two improvements that surfaced while validating v0.1.2 against a clean diff --git a/CLAUDE.md b/CLAUDE.md index 2868998..c165501 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,7 +80,7 @@ Real-time execution updates: browser connects directly to Go engine at `ws://hos ### Flow Editor - `src/components/flow/FlowCanvas.tsx` — React Flow surface (uses `reactflow` v11). -- `src/components/flow/nodes/` — 23 node types built on shared `BaseNode` with `iconMap`/`colorMap`. Adding a node = component in `nodes/`, register in `nodes/index.ts`, properties editor in `properties/`, and (if the engine should execute it) a case in `internal/executor/flow_executor.go`. +- `src/components/flow/nodes/` — 25 node types built on shared `BaseNode` with `iconMap`/`colorMap`. Adding a node = component in `nodes/`, register in `nodes/index.ts`, properties editor in `properties/`, and (if the engine should execute it) a case in `internal/executor/flow_executor.go`. The AI node (LLM completion) is dispatched to `internal/llm` from `flow_executor.go`. - `src/components/flow/properties/` — Per-node-type property editors. - `src/components/Combobox.tsx` — In-house searchable dropdown (no third-party lib). Used everywhere instead of native `