Add parameter to specify a flow config version in the ai/duo_workflows/agent_workflows endpoint

What does this MR do and why?

Duo Chat is getting a /goal slash command (epic &23107). Goal is a flow configuration of the Developer foundational flow. You can find its definition in the AI Gateway/Duo Workflow Service project: duo_workflow_service/agent_platform/v1/flows/configs/developer/2.0.0-goal.yml.

The REST API endpoint ai/duo_workflows/agent_workflows used by the chat agent to start a flow can't specify which flow config to use. This Merge Request adds an optional mode parameter to create this capability.

Each flow declares the modes a caller may name in its selectable_flow_modes attribute, and maps a mode onto a flow config version in its flow_version_resolver. In this way, we don't expose flow registry internals in a public API.

Why this parameter is safe to replace later

A review thread (!255291 (comment 3855793083)) asks whether a new mode parameter is the right long-term design, or whether we are optimising too locally. A related discussion in the AI gateway project (gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!6978 (closed)) proposes a surface concept that might eventually absorb this. This MR does not settle that question, and it does not need to.

mode only selects which config version a foundational flow starts in. It does not change which flow runs, so billing, enablement and analytics are unaffected. It is gated by a default-off feature flag, and the endpoint already carries route_setting :lifecycle, :experiment, which the API styleguide says makes it non-contractual:

API elements marked as experiment or beta are exempt from the breaking changes policy, and can be changed or removed at any time without prior notice.

Marking the parameter documentation: { hidden: true } ticks the last box on that experiment checklist, keeping it out of the generated OpenAPI spec. So if surface lands, mode can be dropped without a deprecation cycle.

One deliberate deviation from the checklist: it says an added argument must be ignored while its flag is off. Here mode returns a 400 instead, because silently ignoring it would start the default Developer config rather than the goal config, with no signal to the caller.

References

Screenshots or screen recordings

How to set up and validate locally

User-level testing

  1. Checkout this branch.

  2. Apply this patch to make the developer flow slash command send the goal mode in Duo Chat.

    diff --git a/ee/app/assets/javascripts/ai/duo_agentic_chat/context/slash_commands_context.js b/ee/app/assets/javascripts/ai/duo_agentic_chat/context/slash_commands_context.js
    index 191917e83dfb..203c56a8d186 100644
    --- a/ee/app/assets/javascripts/ai/duo_agentic_chat/context/slash_commands_context.js
    +++ b/ee/app/assets/javascripts/ai/duo_agentic_chat/context/slash_commands_context.js
    @@ -20,6 +20,18 @@ Flow commands are the only kind that contributes today.
      */
     export const SLASH_COMMAND_CONTEXT_CATEGORY = 'duo_chat_command';
     
    +/* TEST PATCH - do not commit.
    + *
    + * Starts a flow in a given mode from the existing flow menu, so `/flow:developer <task>`
    + * exercises the goal config end to end before a real `/goal` command exists.
    + *
    + * Keyed by the command value, which is `/flow:` plus the slugified flow name. If the
    + * Developer flow is named differently in your catalog, change the key to match what the
    + * slash menu shows.
    + */
    +/* eslint-disable-next-line @gitlab/no-hardcoded-urls -- a slash command, not a URL */
    +const TEST_MODES = { '/flow:developer': 'goal' };
    +
     /**
      * What a flow command adds to the turn it starts.
      *
    @@ -43,6 +55,7 @@ export const slashCommandContextFor = ({ text = '', slashCommands = [] } = {}) =
       if (!trimmed.toLowerCase().startsWith(command.value.toLowerCase())) return [];
     
       const goal = trimmed.slice(command.value.length).trim();
    +  const mode = TEST_MODES[command.value];
     
       return [
         {
    @@ -51,6 +64,7 @@ export const slashCommandContextFor = ({ text = '', slashCommands = [] } = {}) =
             command: 'flow',
             ai_catalog_item_consumer_id: command.consumerId,
             goal: goal || null,
    +        ...(mode ? { mode } : {}),
           }),
           metadata: '{}',
         },
    
  3. Enable the flag in the rails console: Feature.enable(:duo_chat_goal_command).

  4. Checkout the GitLab AI Gateway MR gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!6945 (closed).

  5. Enable the Developer foundational flow if it isn't enabled yet.

  6. Start a developer flow in Duo Chat:

  7. Confirm the session records developer/v1 as its flow while running the goal config, and that passing a mode the flow does not declare is rejected.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Enrique Alcántara

Merge request reports

Loading
Loading