From f593c97479701215cea8068b2f69404d1de88c71 Mon Sep 17 00:00:00 2001 From: Vanessa Otto Date: Wed, 5 Nov 2025 14:13:38 +0100 Subject: [PATCH 1/5] Set default value for flow and third party flows --- .../ai/catalog/components/ai_catalog_flow_form.vue | 11 ++++++++--- ee/app/assets/javascripts/ai/catalog/constants.js | 6 ++++++ locale/gitlab.pot | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue index 62818d377d6722..fb6284afafe1a8 100644 --- a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue +++ b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue @@ -13,6 +13,8 @@ import { VISIBILITY_LEVEL_PRIVATE, VISIBILITY_LEVEL_PUBLIC, FLOW_VISIBILITY_LEVEL_DESCRIPTIONS, + DEFAULT_FLOW_YML_STRING, + DEFAULT_THIRD_PARTY_FLOW_YML_STRING, } from 'ee/ai/catalog/constants'; import { TYPENAME_PROJECT } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; @@ -29,6 +31,7 @@ import FormSection from './form_section.vue'; import VisibilityLevelRadioGroup from './visibility_level_radio_group.vue'; export default { + name: 'AiCatalogFlowForm', components: { ErrorsAlert, AiCatalogFormButtons, @@ -99,8 +102,10 @@ export default { formValues: { ...initialValuesWithoutDefinition, type, - definitionFlow: isFlowType ? definition || '' : '', - definitionThirdPartyFlow: !isFlowType ? definition || '' : '', + definitionFlow: isFlowType ? definition || DEFAULT_FLOW_YML_STRING : '', + definitionThirdPartyFlow: !isFlowType + ? definition || DEFAULT_THIRD_PARTY_FLOW_YML_STRING + : '', projectId: !this.isGlobal && this.projectId ? convertToGraphQLId(TYPENAME_PROJECT, this.projectId) @@ -258,7 +263,7 @@ export default { }, groupAttrs: { labelDescription: s__( - 'AICatalog|This YAML configuration file determines the prompts, tools, and capabilities of your flow. Required properties: version, environment, components, routers, flow, prompts.', + 'AICatalog|This YAML configuration file determines the prompts, tools, and capabilities of your flow. Required properties: version, environment, components, prompts, routers, flow.', ), }, }, diff --git a/ee/app/assets/javascripts/ai/catalog/constants.js b/ee/app/assets/javascripts/ai/catalog/constants.js index 1ec5b76d6c6704..d764a25b3dd6e5 100644 --- a/ee/app/assets/javascripts/ai/catalog/constants.js +++ b/ee/app/assets/javascripts/ai/catalog/constants.js @@ -114,3 +114,9 @@ export const FLOW_TYPE_APOLLO_CONFIG = { }, }, }; + +export const DEFAULT_FLOW_YML_STRING = + 'version: "v1"\nenvironment: ambient\n\ncomponents:\n - name: "my_agent"\n type: AgentComponent\n prompt_id: "my_prompt"\n inputs: ["context:goal"]\n toolset: []\n\nprompts:\n - prompt_id: "my_prompt"\n name: "My Agent Prompt"\n model:\n params:\n model_class_provider: anthropic\n model: claude-sonnet-4-20250514\n max_tokens: 32768\n unit_primitives: []\n prompt_template:\n system: |\n You are GitLab Duo Chat, an agentic AI Coding assistant built by GitLab.\n Your role is to help the user complete their request by using the available tools.\n Your response style is concise and actionable.\n user: |\n Here is my task:\n {{goal}}\n placeholder: history\n params:\n timeout: 180\n\nrouters:\n - from: "my_agent"\n to: "end"\n\nflow:\n entry_point: "my_agent"'; + +export const DEFAULT_THIRD_PARTY_FLOW_YML_STRING = + 'image: alpine:latest\ninjectGatewayToken: true\ncommands:\n - echo "Hello, World!"'; diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 85d3182e4cdf6c..f91faf1ddee298 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2853,7 +2853,7 @@ msgstr "" msgid "AICatalog|This YAML configuration file determines the prompts, tools, and capabilities of your flow. Required properties: injectGatewayToken, image, commands" msgstr "" -msgid "AICatalog|This YAML configuration file determines the prompts, tools, and capabilities of your flow. Required properties: version, environment, components, routers, flow, prompts." +msgid "AICatalog|This YAML configuration file determines the prompts, tools, and capabilities of your flow. Required properties: version, environment, components, prompts, routers, flow." msgstr "" msgid "AICatalog|This agent can be made private if it is not used." -- GitLab From 60ff4e396cd40dca6641f6509a9307e7269d856a Mon Sep 17 00:00:00 2001 From: Vanessa Otto Date: Mon, 10 Nov 2025 10:14:51 +0100 Subject: [PATCH 2/5] Update Custom Flow YML constant --- ee/app/assets/javascripts/ai/catalog/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/app/assets/javascripts/ai/catalog/constants.js b/ee/app/assets/javascripts/ai/catalog/constants.js index d764a25b3dd6e5..53e2a3933fb0d9 100644 --- a/ee/app/assets/javascripts/ai/catalog/constants.js +++ b/ee/app/assets/javascripts/ai/catalog/constants.js @@ -116,7 +116,7 @@ export const FLOW_TYPE_APOLLO_CONFIG = { }; export const DEFAULT_FLOW_YML_STRING = - 'version: "v1"\nenvironment: ambient\n\ncomponents:\n - name: "my_agent"\n type: AgentComponent\n prompt_id: "my_prompt"\n inputs: ["context:goal"]\n toolset: []\n\nprompts:\n - prompt_id: "my_prompt"\n name: "My Agent Prompt"\n model:\n params:\n model_class_provider: anthropic\n model: claude-sonnet-4-20250514\n max_tokens: 32768\n unit_primitives: []\n prompt_template:\n system: |\n You are GitLab Duo Chat, an agentic AI Coding assistant built by GitLab.\n Your role is to help the user complete their request by using the available tools.\n Your response style is concise and actionable.\n user: |\n Here is my task:\n {{goal}}\n placeholder: history\n params:\n timeout: 180\n\nrouters:\n - from: "my_agent"\n to: "end"\n\nflow:\n entry_point: "my_agent"'; + '# Schema version\nversion: "v1"\n# Environment where the flow runs (ambient = GitLab\'s managed environment)\nenvironment: ambient\n\n# Components define the steps in your flow\n# Each component can be an Agent, DeterministicStep, or other component types\ncomponents:\n - name: "my_agent"\n type: AgentComponent # Options: AgentComponent, DeterministicStepComponent\n prompt_id: "my_prompt" # References a prompt defined below\n inputs: \n - "context:goal" # Input from user or previous component\n toolset: [] # Add tool names here: ["get_issue", "create_issue_note"]\n \n # Optional: Add UI logging for debugging\n # ui_log_events:\n # - "on_agent_final_answer"\n # - "on_tool_execution_success"\n\n# Define your prompts here\n# Each prompt configures an AI agent\'s behavior and model settings\nprompts:\n - prompt_id: "my_prompt" # Must match the prompt_id referenced above\n name: "My Agent Prompt"\n model:\n params:\n model_class_provider: anthropic # Provider: anthropic, openai, etc.\n model: claude-sonnet-4-20250514 # Model identifier\n max_tokens: 8192 # Adjust based on expected response length\n \n # System and user prompts define the agent\'s behavior\n prompt_template:\n system: |\n You are GitLab Duo Chat, an agentic AI assistant.\n Your role is to help users with their GitLab tasks.\n Be concise, accurate, and actionable in your responses.\n \n # Add specific instructions for your use case here\n \n user: |\n {{goal}}\n \n # Available variables depend on your inputs:\n # {{goal}} - The user\'s request\n # {{context}} - Additional context from previous steps\n \n placeholder: history # Maintains conversation context\n \n params:\n timeout: 180 # Seconds before timeout\n\n# Routers define the flow between components\n# Use "end" as the final destination\nrouters:\n - from: "my_agent"\n to: "end"\n \n # Example: Multi-step flow\n # - from: "fetch_data"\n # to: "process_data"\n # - from: "process_data"\n # to: "my_agent"\n # - from: "my_agent"\n # to: "end"\n\n# Define the entry point for your flow\nflow:\n entry_point: "my_agent"'; export const DEFAULT_THIRD_PARTY_FLOW_YML_STRING = 'image: alpine:latest\ninjectGatewayToken: true\ncommands:\n - echo "Hello, World!"'; -- GitLab From 2b9cf8a256cdcd812a0619b6bafd035ed5bce8eb Mon Sep 17 00:00:00 2001 From: Vanessa Otto Date: Mon, 10 Nov 2025 10:27:38 +0100 Subject: [PATCH 3/5] Update specs to reflect default values --- .../components/ai_catalog_flow_form_spec.js | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/ee/spec/frontend/ai/catalog/components/ai_catalog_flow_form_spec.js b/ee/spec/frontend/ai/catalog/components/ai_catalog_flow_form_spec.js index d767be11a51625..f9d9c6efdcf06a 100644 --- a/ee/spec/frontend/ai/catalog/components/ai_catalog_flow_form_spec.js +++ b/ee/spec/frontend/ai/catalog/components/ai_catalog_flow_form_spec.js @@ -6,7 +6,12 @@ import FormFlowType from 'ee/ai/catalog/components/form_flow_type.vue'; import FormProjectDropdown from 'ee/ai/catalog/components/form_project_dropdown.vue'; import VisibilityLevelRadioGroup from 'ee/ai/catalog/components/visibility_level_radio_group.vue'; import FormGroup from 'ee/ai/catalog/components/form_group.vue'; -import { VISIBILITY_LEVEL_PRIVATE, VISIBILITY_LEVEL_PUBLIC } from 'ee/ai/catalog/constants'; +import { + VISIBILITY_LEVEL_PRIVATE, + VISIBILITY_LEVEL_PUBLIC, + DEFAULT_FLOW_YML_STRING, + DEFAULT_THIRD_PARTY_FLOW_YML_STRING, +} from 'ee/ai/catalog/constants'; describe('AiCatalogFlowForm', () => { let wrapper; @@ -94,7 +99,7 @@ describe('AiCatalogFlowForm', () => { expect(findDescriptionField().props('value')).toBe(''); expect(findVisibilityLevelRadioGroup().props('initialValue')).toBe(false); expect(findVisibilityLevelRadioGroup().props('value')).toBe(VISIBILITY_LEVEL_PRIVATE); - expect(findDefinitionFlowField().props('value')).toBe(''); + expect(findDefinitionFlowField().props('value')).toBe(DEFAULT_FLOW_YML_STRING); expect(findDefinitionThirdPartyFlowField().exists()).toBe(false); }); @@ -107,7 +112,7 @@ describe('AiCatalogFlowForm', () => { expect(findDescriptionField().props('value')).toBe(''); expect(findVisibilityLevelRadioGroup().props('initialValue')).toBe(false); expect(findVisibilityLevelRadioGroup().props('value')).toBe(VISIBILITY_LEVEL_PRIVATE); - expect(findDefinitionFlowField().props('value')).toBe(''); + expect(findDefinitionFlowField().props('value')).toBe(DEFAULT_FLOW_YML_STRING); expect(findDefinitionThirdPartyFlowField().exists()).toBe(false); }); @@ -159,6 +164,22 @@ describe('AiCatalogFlowForm', () => { expect(findDefinitionThirdPartyFlowField().props('value')).toBe('third-party-definition'); expect(findDefinitionFlowField().exists()).toBe(false); }); + + it('renders default value when there was no definition set', () => { + createWrapper({ + props: { + initialValues: { + ...initialValues, + definition: '', + type: 'THIRD_PARTY_FLOW', + }, + }, + }); + + expect(findDefinitionThirdPartyFlowField().props('value')).toBe( + DEFAULT_THIRD_PARTY_FLOW_YML_STRING, + ); + }); }); }); -- GitLab From 9623b00ed5085a02aacc3c33fc542da541e0db49 Mon Sep 17 00:00:00 2001 From: Vanessa Otto Date: Mon, 10 Nov 2025 20:47:09 +0100 Subject: [PATCH 4/5] Set default value even in case the type is not active --- .../components/ai_catalog_flow_form.vue | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue index fb6284afafe1a8..6c5e121d38ad6f 100644 --- a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue +++ b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue @@ -96,23 +96,35 @@ export default { type = AI_CATALOG_TYPE_THIRD_PARTY_FLOW; } - const isFlowType = type === AI_CATALOG_TYPE_FLOW; const { definition, ...initialValuesWithoutDefinition } = this.initialValues; + + const definitionFlow = + type === AI_CATALOG_TYPE_FLOW + ? definition || DEFAULT_FLOW_YML_STRING + : DEFAULT_FLOW_YML_STRING; + + const definitionThirdPartyFlow = + type === AI_CATALOG_TYPE_THIRD_PARTY_FLOW + ? definition || DEFAULT_THIRD_PARTY_FLOW_YML_STRING + : DEFAULT_THIRD_PARTY_FLOW_YML_STRING; + + const projectId = + !this.isGlobal && this.projectId + ? convertToGraphQLId(TYPENAME_PROJECT, this.projectId) + : this.initialValues.projectId; + + const visibilityLevel = this.initialValues.public + ? VISIBILITY_LEVEL_PUBLIC + : VISIBILITY_LEVEL_PRIVATE; + return { formValues: { ...initialValuesWithoutDefinition, type, - definitionFlow: isFlowType ? definition || DEFAULT_FLOW_YML_STRING : '', - definitionThirdPartyFlow: !isFlowType - ? definition || DEFAULT_THIRD_PARTY_FLOW_YML_STRING - : '', - projectId: - !this.isGlobal && this.projectId - ? convertToGraphQLId(TYPENAME_PROJECT, this.projectId) - : this.initialValues.projectId, - visibilityLevel: this.initialValues.public - ? VISIBILITY_LEVEL_PUBLIC - : VISIBILITY_LEVEL_PRIVATE, + definitionFlow, + definitionThirdPartyFlow, + projectId, + visibilityLevel, }, formErrors: [], }; -- GitLab From 41bcca0171eee2461740e7d56cabcbbbf1acb25a Mon Sep 17 00:00:00 2001 From: Vanessa Otto Date: Mon, 10 Nov 2025 20:52:09 +0100 Subject: [PATCH 5/5] Use multiline strings for default values --- .../components/ai_catalog_flow_form.vue | 8 +- .../javascripts/ai/catalog/constants.js | 81 ++++++++++++++++++- 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue index 6c5e121d38ad6f..e9db1faf5b8938 100644 --- a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue +++ b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_flow_form.vue @@ -99,13 +99,11 @@ export default { const { definition, ...initialValuesWithoutDefinition } = this.initialValues; const definitionFlow = - type === AI_CATALOG_TYPE_FLOW - ? definition || DEFAULT_FLOW_YML_STRING - : DEFAULT_FLOW_YML_STRING; + type === AI_CATALOG_TYPE_FLOW && definition ? definition : DEFAULT_FLOW_YML_STRING; const definitionThirdPartyFlow = - type === AI_CATALOG_TYPE_THIRD_PARTY_FLOW - ? definition || DEFAULT_THIRD_PARTY_FLOW_YML_STRING + type === AI_CATALOG_TYPE_THIRD_PARTY_FLOW && definition + ? definition : DEFAULT_THIRD_PARTY_FLOW_YML_STRING; const projectId = diff --git a/ee/app/assets/javascripts/ai/catalog/constants.js b/ee/app/assets/javascripts/ai/catalog/constants.js index 53e2a3933fb0d9..0ccfd30bb75f1e 100644 --- a/ee/app/assets/javascripts/ai/catalog/constants.js +++ b/ee/app/assets/javascripts/ai/catalog/constants.js @@ -115,8 +115,81 @@ export const FLOW_TYPE_APOLLO_CONFIG = { }, }; -export const DEFAULT_FLOW_YML_STRING = - '# Schema version\nversion: "v1"\n# Environment where the flow runs (ambient = GitLab\'s managed environment)\nenvironment: ambient\n\n# Components define the steps in your flow\n# Each component can be an Agent, DeterministicStep, or other component types\ncomponents:\n - name: "my_agent"\n type: AgentComponent # Options: AgentComponent, DeterministicStepComponent\n prompt_id: "my_prompt" # References a prompt defined below\n inputs: \n - "context:goal" # Input from user or previous component\n toolset: [] # Add tool names here: ["get_issue", "create_issue_note"]\n \n # Optional: Add UI logging for debugging\n # ui_log_events:\n # - "on_agent_final_answer"\n # - "on_tool_execution_success"\n\n# Define your prompts here\n# Each prompt configures an AI agent\'s behavior and model settings\nprompts:\n - prompt_id: "my_prompt" # Must match the prompt_id referenced above\n name: "My Agent Prompt"\n model:\n params:\n model_class_provider: anthropic # Provider: anthropic, openai, etc.\n model: claude-sonnet-4-20250514 # Model identifier\n max_tokens: 8192 # Adjust based on expected response length\n \n # System and user prompts define the agent\'s behavior\n prompt_template:\n system: |\n You are GitLab Duo Chat, an agentic AI assistant.\n Your role is to help users with their GitLab tasks.\n Be concise, accurate, and actionable in your responses.\n \n # Add specific instructions for your use case here\n \n user: |\n {{goal}}\n \n # Available variables depend on your inputs:\n # {{goal}} - The user\'s request\n # {{context}} - Additional context from previous steps\n \n placeholder: history # Maintains conversation context\n \n params:\n timeout: 180 # Seconds before timeout\n\n# Routers define the flow between components\n# Use "end" as the final destination\nrouters:\n - from: "my_agent"\n to: "end"\n \n # Example: Multi-step flow\n # - from: "fetch_data"\n # to: "process_data"\n # - from: "process_data"\n # to: "my_agent"\n # - from: "my_agent"\n # to: "end"\n\n# Define the entry point for your flow\nflow:\n entry_point: "my_agent"'; +export const DEFAULT_FLOW_YML_STRING = `\ +# Schema version +version: "v1" +# Environment where the flow runs (ambient = GitLab's managed environment) +environment: ambient -export const DEFAULT_THIRD_PARTY_FLOW_YML_STRING = - 'image: alpine:latest\ninjectGatewayToken: true\ncommands:\n - echo "Hello, World!"'; +# Components define the steps in your flow +# Each component can be an Agent, DeterministicStep, or other component types +components: + - name: "my_agent" + type: AgentComponent # Options: AgentComponent, DeterministicStepComponent + prompt_id: "my_prompt" # References a prompt defined below + inputs: + - "context:goal" # Input from user or previous component + toolset: [] # Add tool names here: ["get_issue", "create_issue_note"] + + # Optional: Add UI logging for debugging + # ui_log_events: + # - "on_agent_final_answer" + # - "on_tool_execution_success" + +# Define your prompts here +# Each prompt configures an AI agent's behavior and model settings +prompts: + - prompt_id: "my_prompt" # Must match the prompt_id referenced above + name: "My Agent Prompt" + model: + params: + model_class_provider: anthropic # Provider: anthropic, openai, etc. + model: claude-sonnet-4-20250514 # Model identifier + max_tokens: 8192 # Adjust based on expected response length + + # System and user prompts define the agent's behavior + prompt_template: + system: | + You are GitLab Duo Chat, an agentic AI assistant. + Your role is to help users with their GitLab tasks. + Be concise, accurate, and actionable in your responses. + + # Add specific instructions for your use case here + + user: | + {{goal}} + + # Available variables depend on your inputs: + # {{goal}} - The user's request + # {{context}} - Additional context from previous steps + + placeholder: history # Maintains conversation context + + params: + timeout: 180 # Seconds before timeout + +# Routers define the flow between components +# Use "end" as the final destination +routers: + - from: "my_agent" + to: "end" + + # Example: Multi-step flow + # - from: "fetch_data" + # to: "process_data" + # - from: "process_data" + # to: "my_agent" + # - from: "my_agent" + # to: "end" + +# Define the entry point for your flow +flow: + entry_point: "my_agent" +`; + +export const DEFAULT_THIRD_PARTY_FLOW_YML_STRING = `\ +image: alpine:latest +injectGatewayToken: true +commands: + - echo "Hello, World!" +`; -- GitLab