mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
docs: name the history inputs wherever linked steps list their flow-local inputs
This commit is contained in:
@@ -1095,7 +1095,8 @@ pub enum FlowModuleValue {
|
||||
omit_output_from_conversation: bool,
|
||||
/// When set, the agent brain config (provider/model/system prompt/etc.) and tools are
|
||||
/// resolved at runtime from this `ai_agent` resource path (hybrid linking). The module's
|
||||
/// `input_transforms` then only carry the flow-local inputs (user_message/user_attachments).
|
||||
/// `input_transforms` then only carry the flow-local inputs: user_message,
|
||||
/// user_attachments and the history inputs memory_id and messages.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
agent: Option<String>,
|
||||
/// Binds an agent's tools to *this* flow's context, keyed by tool id then input key, without
|
||||
|
||||
+12
-10
@@ -49,11 +49,12 @@ which memory it is:
|
||||
names two memories. A uuid is used as is. Nothing is generated at save time, so schedules,
|
||||
webhooks, evals and plain runs pass no id and run stateless.
|
||||
- **Step: history inputs.** Flow-local, so they stay on a linked step. `memory_id` overrides the
|
||||
run's id, hashed the same way: a fixed value is one memory shared by every run, an expression such as
|
||||
`flow_input.customer_id` one memory per key, and an expression that evaluates to nothing runs
|
||||
run's id, hashed the same way: a fixed value is one memory shared by every run, an expression such
|
||||
as `flow_input.customer_id` one memory per key, and an expression that evaluates to nothing runs
|
||||
stateless rather than falling back to the run's id. `messages` supplies the history itself and
|
||||
bypasses memory; an expression that evaluates to null sends no history and still bypasses it. The editor writes at most one of them and never seeds a placeholder for
|
||||
either, because a present key is the step's choice; if both are present, `messages` wins.
|
||||
bypasses memory; an expression that evaluates to null sends no history and still bypasses it. The
|
||||
editor writes at most one of them and never seeds a placeholder for either, because a present key
|
||||
is the step's choice; if both are present, `messages` wins.
|
||||
|
||||
The worker reconciles them once per agent invocation, nested agent tools included, in
|
||||
`resolve_history_source` (`windmill-worker/src/ai_executor.rs`):
|
||||
@@ -63,9 +64,9 @@ The worker reconciles them once per agent invocation, nested agent tools include
|
||||
3. The memory id is the step's, else the run's, else a legacy id baked into the `auto` object.
|
||||
4. With no memory id the agent runs stateless and says so in the job log.
|
||||
|
||||
Memory is stored per (memory id, step id), in `ai_agent_memory` or S3 at
|
||||
`memory/{workspace}/{memory id}/{step}.json`. The chat transcript (`flow_conversation_message`)
|
||||
always follows the run's id, even when a step sets its own. Nothing expires stored memory: deleting a chat conversation deletes
|
||||
Memory is stored per (memory id, step id), in `ai_agent_memory` or S3 at `memory/{workspace}/{memory
|
||||
id}/{step}.json`. The chat transcript (`flow_conversation_message`) always follows the run's id,
|
||||
even when a step sets its own. Nothing expires stored memory: deleting a chat conversation deletes
|
||||
its memory, and a memory named by a string id stays until it is overwritten.
|
||||
|
||||
Compatibility runs one way. New workers read every older shape. The editor rewrites a legacy step
|
||||
@@ -87,9 +88,10 @@ A flow does not wait for that deploy to see the draft:
|
||||
- Testing the flow, or a single linked step, runs the draft. `runFlowPreview` and `ModuleTest`
|
||||
substitute each linked step for the standalone step the draft would run as
|
||||
(`linkedAgentDrafts.ts`): `agent` cleared, the draft's brain as static input transforms, the
|
||||
draft's tools on the step, and the step's own `user_message`/`user_attachments` kept on top —
|
||||
the same overlay order `ai_executor.rs` applies to a linked step. `tool_inputs` is untouched,
|
||||
since the worker overlays it in both branches.
|
||||
draft's tools on the step, and the step's own flow-local inputs (`user_message`,
|
||||
`user_attachments`, `memory_id`, `messages`) kept on top — the same overlay order `ai_executor.rs`
|
||||
applies to a linked step. `tool_inputs` is untouched, since the worker overlays it in both
|
||||
branches.
|
||||
- The step's linked card and the graph's tool nodes show the draft, with a *Draft* badge, so the
|
||||
editor describes what a test would run. Read-only surfaces (the deployed flow page, the run
|
||||
viewer) stay on the deployed agent: they resolve tools through `publishLinkedAgentTools` without
|
||||
|
||||
@@ -143,16 +143,23 @@ describe('nonStaticBrainKeys', () => {
|
||||
})
|
||||
|
||||
describe('flowLocalInputs', () => {
|
||||
it('keeps only user_message/user_attachments, dropping brain transforms', () => {
|
||||
// Linking keeps exactly these on the step, history inputs included: dropping one would silently
|
||||
// move a linked step onto the run's memory.
|
||||
it('keeps the user message, attachments and history inputs, dropping brain transforms', () => {
|
||||
expect(
|
||||
flowLocalInputs({
|
||||
provider: { type: 'static', value: {} },
|
||||
memory: { type: 'static', value: { kind: 'window', context_length: 10 } },
|
||||
user_message: { type: 'static', value: 'hi' },
|
||||
user_attachments: { type: 'static', value: [] }
|
||||
user_attachments: { type: 'static', value: [] },
|
||||
memory_id: { type: 'javascript', expr: 'flow_input.customer_id' },
|
||||
messages: { type: 'static', value: [{ role: 'user', content: 'earlier' }] }
|
||||
} as any)
|
||||
).toEqual({
|
||||
user_message: { type: 'static', value: 'hi' },
|
||||
user_attachments: { type: 'static', value: [] }
|
||||
user_attachments: { type: 'static', value: [] },
|
||||
memory_id: { type: 'javascript', expr: 'flow_input.customer_id' },
|
||||
messages: { type: 'static', value: [{ role: 'user', content: 'earlier' }] }
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -177,8 +177,8 @@ type AiAgentValue = Extract<FlowModule['value'], { type: 'aiagent' }>
|
||||
* the step's own flow-local inputs kept on top.
|
||||
*
|
||||
* The overlay order is the worker's (`ai_executor.rs`): its linked branch interpolates the whole
|
||||
* resource brain and only then writes `user_message`/`user_attachments` back from the step's own
|
||||
* args. `tool_inputs` stays untouched — the worker overlays it onto the tools in both branches, so
|
||||
* resource brain and only then writes the flow-local inputs (`user_message`, `user_attachments`,
|
||||
* `memory_id`, `messages`) back from the step's own args. `tool_inputs` stays untouched — the worker overlays it onto the tools in both branches, so
|
||||
* an inlined step keeps the host flow's tool bindings.
|
||||
*/
|
||||
export function inlineAgentDraft(value: AiAgentValue, args: AIAgentConfig): AiAgentValue {
|
||||
|
||||
Reference in New Issue
Block a user