func()

in cli/azd/extensions/microsoft.azd.ai.builder/internal/cmd/start.go [738:1739]


func (a *startAction) createQuestions(ctx context.Context) (map[string]qna.Question, error) {
	resourceTypes, err := a.azdClient.Compose().ListResourceTypes(ctx, &azdext.EmptyRequest{})
	if err != nil {
		return nil, fmt.Errorf("failed to list resource types: %w", err)
	}

	dbResourceMap := make(map[string]*azdext.ComposedResourceType)
	vectorStoreMap := make(map[string]*azdext.ComposedResourceType)
	messagingResourceMap := make(map[string]*azdext.ComposedResourceType)
	for _, resourceType := range resourceTypes.ResourceTypes {
		key := resourceType.Name
		if strings.HasPrefix(key, "db.") {
			dbResourceMap[key] = resourceType
		} else if strings.HasPrefix(key, "messaging.") {
			messagingResourceMap[key] = resourceType
		}

		if strings.Contains(key, "ai.search") || strings.Contains(key, "db.cosmos") {
			vectorStoreMap[key] = resourceType
		}
	}

	return map[string]qna.Question{
		"root": {
			Binding: &a.scenarioData.SelectedScenario,
			Heading: "Identify AI Scenario",
			Message: "Let's start drilling into your AI scenario to identify all the required infrastructure we will need.",
			Prompt: &qna.SingleSelectPrompt{
				Client:          a.azdClient,
				Message:         "What type of AI scenario are you building?",
				HelpMessage:     "Choose the scenario that best fits your needs.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "RAG Application (Retrieval-Augmented Generation)", Value: "rag"},
					{Label: "AI Agent", Value: "agent"},
					{Label: "Other Scenarios (Coming Soon)", Value: "other-scenarios"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"rag":   {{Key: "use-custom-data"}},
				"agent": {{Key: "agent-tasks"}},
			},
		},
		"use-custom-data": {
			Binding: &a.scenarioData.UseCustomData,
			Prompt: &qna.ConfirmPrompt{
				Client:       a.azdClient,
				Message:      "Does your application require custom data?",
				HelpMessage:  "Custom data is data that is not publicly available and is specific to your application.",
				DefaultValue: to.Ptr(true),
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, _ any) error {
				switch a.scenarioData.SelectedScenario {
				case "rag":
					q.Branches = map[any][]qna.QuestionReference{
						true:  {{Key: "choose-data-types"}},
						false: {{Key: "rag-user-interaction"}},
					}
				case "agent":
					q.Branches = map[any][]qna.QuestionReference{
						true:  {{Key: "choose-data-types"}},
						false: {{Key: "agent-tasks"}},
					}
				}

				return nil
			},
		},
		"choose-data-types": {
			Binding: &a.scenarioData.DataTypes,
			Heading: "Data Sources",
			Message: "Lets identify all the data source that will be used in your application.",
			Prompt: &qna.MultiSelectPrompt{
				Client:          a.azdClient,
				Message:         "What type of data are you using?",
				HelpMessage:     "Select all the data types that apply to your application.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Structured documents, ex. JSON, CSV", Value: "structured-documents"},
					{Label: "Unstructured documents, ex. PDF, Word", Value: "unstructured-documents"},
					{Label: "Videos", Value: "videos"},
					{Label: "Images", Value: "images"},
					{Label: "Audio", Value: "audio"},
				},
			},
			Next: []qna.QuestionReference{{Key: "data-location"}},
		},
		"data-location": {
			Binding: &a.scenarioData.DataLocations,
			Prompt: &qna.MultiSelectPrompt{
				Client:          a.azdClient,
				Message:         "Where is your data located?",
				HelpMessage:     "Select all the data locations that apply to your application.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Azure Blob Storage", Value: "blob-storage"},
					{Label: "Azure Database", Value: "databases"},
					{Label: "Local file system", Value: "local-file-system"},
					{Label: "Other", Value: "other-datasource"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"blob-storage":      {{Key: "choose-storage"}},
				"databases":         {{Key: "choose-database"}},
				"local-file-system": {{Key: "local-file-system"}},
			},
			Next: []qna.QuestionReference{{Key: "choose-vector-store"}},
		},
		"choose-storage": {
			Heading: "Storage Account",
			Message: "We'll need to setup a storage account to store the data for your application.",
			BeforeAsk: func(ctx context.Context, q *qna.Question, _ any) error {
				hasStorageResource := false
				for _, resource := range a.composedResources {
					if resource.Type == "storage" {
						hasStorageResource = true
						break
					}
				}

				promptMessage := "It looks like you already have a configured storage account. Do you want to reuse it?"

				if hasStorageResource {
					q.Prompt = &qna.ConfirmPrompt{
						Client:       a.azdClient,
						Message:      promptMessage,
						DefaultValue: to.Ptr(true),
						HelpMessage:  "Using an existing storage account will save you time and resources.",
					}
				}

				q.State["hasStorageResource"] = hasStorageResource

				return nil
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				hasStorageResource := q.State["hasStorageResource"].(bool)
				reuseStorage, ok := value.(bool)

				if !hasStorageResource || ok && !reuseStorage {
					q.Next = []qna.QuestionReference{{Key: "choose-storage-resource"}}
				}

				return nil
			},
		},
		"choose-storage-resource": {
			Binding: &a.scenarioData.StorageAccountId,
			Prompt: &qna.SubscriptionResourcePrompt{
				Client:                  a.azdClient,
				ResourceType:            "Microsoft.Storage/storageAccounts",
				ResourceTypeDisplayName: "Storage Account",
				HelpMessage:             "Select an existing storage account or create a new one.",
				AzureContext:            a.azureContext,
			},
		},
		"choose-database": {
			Heading: "Database",
			Message: "We'll need to setup a database that will be used by your application to power AI model(s).",
			BeforeAsk: func(ctx context.Context, q *qna.Question, _ any) error {
				hasDatabaseResource := false
				for _, resource := range a.composedResources {
					if strings.HasPrefix(resource.Type, "db.") {
						hasDatabaseResource = true
						break
					}
				}

				if hasDatabaseResource {
					q.Prompt = &qna.ConfirmPrompt{
						Client:       a.azdClient,
						Message:      "It looks like you already have a configured database. Do you want to reuse it?",
						DefaultValue: to.Ptr(true),
						HelpMessage:  "Using an existing database will save you time and resources.",
					}
				}

				q.State["hasDatabaseResource"] = hasDatabaseResource

				return nil
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				hasDatabaseResource := q.State["hasDatabaseResource"].(bool)
				reuseDatabase, ok := value.(bool)

				if !hasDatabaseResource || ok && !reuseDatabase {
					q.Next = []qna.QuestionReference{{Key: "choose-database-type"}}
				}

				return nil
			},
		},
		"choose-database-type": {
			Binding: &a.scenarioData.DatabaseType,
			Prompt: &qna.SingleSelectPrompt{
				Message:         "Which type of database?",
				HelpMessage:     "Select the type of database that best fits your needs.",
				Client:          a.azdClient,
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "CosmosDB", Value: "db.cosmos"},
					{Label: "PostgreSQL", Value: "db.postgres"},
					{Label: "MySQL", Value: "db.mysql"},
					{Label: "Redis", Value: "db.redis"},
					{Label: "MongoDB", Value: "db.mongo"},
				},
			},
			Next: []qna.QuestionReference{{Key: "choose-database-resource"}},
		},
		"choose-database-resource": {
			Binding: &a.scenarioData.DatabaseId,
			Prompt: &qna.SubscriptionResourcePrompt{
				HelpMessage:  "Select an existing database or create a new one.",
				Client:       a.azdClient,
				AzureContext: a.azureContext,
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.SubscriptionResourcePrompt) error {
					resourceType, has := dbResourceMap[a.scenarioData.DatabaseType]
					if !has {
						return fmt.Errorf(
							"unknown resource type for database: %s",
							a.scenarioData.DatabaseType,
						)
					}

					p.ResourceType = resourceType.Type
					p.Kinds = resourceType.Kinds
					p.ResourceTypeDisplayName = resourceType.DisplayName

					return nil
				},
			},
		},
		"local-file-system": {
			Binding: &a.scenarioData.LocalFilePath,
			Heading: "Local File System",
			Message: "Lets identify the files that will be used in your application. " +
				"Later on we will upload these files to Azure so they can be used by your application.",
			Prompt: &qna.TextPrompt{
				Client:  a.azdClient,
				Message: "Path to the local files",
				HelpMessage: "This path can be absolute or relative to the current working directory. " +
					"Please make sure the path is accessible from the machine running this command.",
				Placeholder: "./data",
			},
			Next: []qna.QuestionReference{{Key: "local-file-choose-files"}},
		},
		"local-file-choose-files": {
			Binding: &a.scenarioData.LocalFileSelection,
			Prompt: &qna.SingleSelectPrompt{
				Client:          a.azdClient,
				Message:         "Which files?",
				HelpMessage:     "Select all files or use a glob expression to filter the files.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "All Files", Value: "all-files"},
					{Label: "Glob Expression", Value: "glob-expression"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"glob-expression": {{Key: "local-file-glob"}},
			},
		},
		"local-file-glob": {
			Binding: &a.scenarioData.LocalFileGlobFilter,
			Prompt: &qna.TextPrompt{
				Client:  a.azdClient,
				Message: "Enter a glob expression to filter files",
				HelpMessage: "A glob expression is a string that uses wildcard characters to match file names. " +
					" For example, *.txt will match all text files in the current directory.",
				Placeholder: "*.json",
			},
		},
		"choose-vector-store": {
			Heading: "Vector Store",
			Message: "Based on your choices we're going to need a vector store to store the text embeddings for your data.",
			BeforeAsk: func(ctx context.Context, q *qna.Question, _ any) error {
				hasVectorStoreResource := false
				for _, resource := range a.composedResources {
					if resource.Type == "ai.search" {
						hasVectorStoreResource = true
						break
					}
				}

				if hasVectorStoreResource {
					q.Prompt = &qna.ConfirmPrompt{
						Client:       a.azdClient,
						Message:      "It looks like you already have a configured vector store. Do you want to reuse it?",
						DefaultValue: to.Ptr(true),
						HelpMessage:  "Using an existing vector store will save you time and resources.",
					}
				}

				q.State["hasVectorStoreResource"] = hasVectorStoreResource

				return nil
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				hasVectorStoreResource := q.State["hasVectorStoreResource"].(bool)
				reuseVectorStore, ok := value.(bool)

				var next string
				if a.scenarioData.SelectedScenario == "rag" {
					next = "rag-user-interaction"
				} else {
					next = "agent-interaction"
				}

				if !hasVectorStoreResource || ok && !reuseVectorStore {
					next = "choose-vector-store-type"
				}

				q.Next = []qna.QuestionReference{{Key: next}}

				return nil
			},
		},
		"choose-vector-store-type": {
			Binding: &a.scenarioData.VectorStoreType,
			Prompt: &qna.SingleSelectPrompt{
				Message:         "What type of vector store do you want to use?",
				HelpMessage:     "Select the type of vector store that best fits your needs.",
				Client:          a.azdClient,
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Choose for me", Value: "ai.search"},
					{Label: "AI Search", Value: "ai.search"},
					{Label: "CosmosDB", Value: "db.cosmos"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"ai.search": {{Key: "choose-vector-store-resource"}},
				"db.cosmos": {{Key: "choose-vector-store-resource"}},
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, _ any) error {
				switch a.scenarioData.SelectedScenario {
				case "rag":
					q.Next = []qna.QuestionReference{{Key: "rag-user-interaction"}}
				case "agent":
					q.Next = []qna.QuestionReference{{Key: "agent-interaction"}}
				}
				return nil
			},
		},
		"choose-vector-store-resource": {
			Binding: &a.scenarioData.VectorStoreId,
			Prompt: &qna.SubscriptionResourcePrompt{
				HelpMessage:  "Select an existing vector store or create a new one.",
				Client:       a.azdClient,
				AzureContext: a.azureContext,
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.SubscriptionResourcePrompt) error {
					resourceType, has := vectorStoreMap[a.scenarioData.VectorStoreType]
					if !has {
						return fmt.Errorf(
							"unknown resource type for vector store: %s",
							a.scenarioData.VectorStoreType,
						)
					}

					p.ResourceType = resourceType.Type
					p.Kinds = resourceType.Kinds
					p.ResourceTypeDisplayName = resourceType.DisplayName
					return nil
				},
			},
		},
		"rag-user-interaction": {
			Binding: &a.scenarioData.InteractionTypes,
			Heading: "User Interaction",
			Message: "Now we will figure out all the different ways users will interact with your application.",
			Prompt: &qna.MultiSelectPrompt{
				Client:          a.azdClient,
				Message:         "How do you want users to interact with the data?",
				HelpMessage:     "Select all the data interaction types that apply to your application.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Chatbot UI Frontend", Value: "rag-ui"},
					{Label: "API Backend Application", Value: "rag-api"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"rag-ui":  {{Key: "choose-app"}},
				"rag-api": {{Key: "choose-app"}},
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.State["interactionTypes"] = value
				return nil
			},
			Next: []qna.QuestionReference{{Key: "start-choose-models"}},
		},
		"choose-app": {
			BeforeAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.Heading = fmt.Sprintf("Configure '%s' Application", value)
				q.Message = fmt.Sprintf("Lets collect some information about your %s application.", value)
				q.State["interactionType"] = value

				hasHostAppResource := false
				appHostCount := 0
				for _, resource := range a.composedResources {
					if strings.HasPrefix(resource.Type, "host.") {
						appHostCount++
						hasHostAppResource = true
					}
				}

				hostName := "host"
				hostName2 := "it"
				if appHostCount > 1 {
					hostName = "hosts"
					hostName2 = "them"
				}

				msg := fmt.Sprintf(
					"It looks like you project already contains %d application %s. Do you want to reuse %s?",
					appHostCount,
					hostName,
					hostName2,
				)

				if hasHostAppResource {
					q.Prompt = &qna.ConfirmPrompt{
						Client:       a.azdClient,
						Message:      msg,
						DefaultValue: to.Ptr(true),
						HelpMessage:  "Using an existing application host will save you time and resources.",
					}
				}

				q.State["hasHostAppResource"] = hasHostAppResource

				return nil
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				hasHostAppResource := q.State["hasHostAppResource"].(bool)
				reuseHostApp, ok := value.(bool)
				if !hasHostAppResource || ok && !reuseHostApp {
					q.Next = []qna.QuestionReference{{Key: "choose-app-type"}}
				}

				delete(q.State, "hasHostAppResource")

				return nil
			},
		},
		"choose-app-type": {
			Binding: &a.scenarioData.AppHostTypes,
			Prompt: &qna.SingleSelectPrompt{
				Message:         "Which application host do you want to use?",
				Client:          a.azdClient,
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Choose for me", Value: "choose-app"},
					{Label: "Container App", Value: "host.containerapp"},
					{Label: "App Service (Coming Soon)", Value: "host.webapp"},
					{Label: "Function App (Coming Soon)", Value: "host.functionapp"},
					{Label: "Static Web App (Coming Soon)", Value: "host.staticwebapp"},
					{Label: "Other", Value: "other-app"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"host.containerapp": {{Key: "choose-app-resource"}},
			},
			Next: []qna.QuestionReference{
				{Key: "choose-app-language"},
			},
		},
		"choose-app-language": {
			Prompt: &qna.SingleSelectPrompt{
				Client:          a.azdClient,
				Message:         "Which programming language do you want to use?",
				HelpMessage:     "Select the programming language that best fits your needs.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Choose for me", Value: "default"},
					{Label: "C#", Value: "csharp"},
					{Label: "Python", Value: "python"},
					{Label: "JavaScript", Value: "js"},
					{Label: "TypeScript", Value: "ts"},
					{Label: "Java", Value: "java"},
					{Label: "Other", Value: "other"},
				},
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				selectedLanguage := value.(string)

				// Find the default language for the selected interaction type if available.
				if selectedLanguage == "default" {
					interactionType := q.State["interactionType"].(string)
					interactionDefault, has := defaultAppLanguageMap[interactionType]
					if has {
						selectedLanguage = interactionDefault
					} else {
						selectedLanguage = "python"
					}
				}

				a.scenarioData.AppLanguages = append(a.scenarioData.AppLanguages, selectedLanguage)
				return nil
			},
		},
		"choose-app-resource": {
			Binding: &a.scenarioData.AppResourceIds,
			BeforeAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.State["appType"] = value
				return nil
			},
			Prompt: &qna.SubscriptionResourcePrompt{
				HelpMessage:  "Select an existing application or create a new one.",
				Client:       a.azdClient,
				AzureContext: a.azureContext,
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.SubscriptionResourcePrompt) error {
					appType := q.State["appType"].(string)

					resourceType, has := appResourceMap[appType]
					if !has {
						return fmt.Errorf(
							"unknown resource type for database: %s",
							appType,
						)
					}

					p.ResourceType = resourceType.ResourceType
					p.Kinds = resourceType.Kinds
					p.ResourceTypeDisplayName = resourceType.ResourceTypeDisplayName

					return nil
				},
			},
		},
		"agent-interaction": {
			Binding: &a.scenarioData.InteractionTypes,
			Heading: "Agent Hosting",
			Message: "Now we will figure out all the different ways users and systems will interact with your agent.",
			Prompt: &qna.MultiSelectPrompt{
				Client:          a.azdClient,
				Message:         "How do you want users to interact with the agent?",
				HelpMessage:     "Select all the data interaction types that apply to your application.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Chatbot UI Frontend", Value: "agent-ui"},
					{Label: "API Backend Application", Value: "agent-api"},
					{Label: "Message based Backed Queue", Value: "agent-messaging"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"agent-ui":        {{Key: "choose-app"}},
				"agent-api":       {{Key: "choose-app"}},
				"agent-messaging": {{Key: "choose-app"}, {Key: "choose-messaging"}},
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.State["interactionTypes"] = value
				return nil
			},
			Next: []qna.QuestionReference{{Key: "start-choose-models"}},
		},
		"agent-tasks": {
			Binding: &a.scenarioData.ModelTasks,
			Prompt: &qna.MultiSelectPrompt{
				Client:          a.azdClient,
				Message:         "What tasks do you want the AI agent to perform?",
				HelpMessage:     "Select all the tasks that apply to your application.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Custom Function Calling", Value: "custom-function-calling"},
					{Label: "Integrate with Open API based services", Value: "openapi"},
					{Label: "Run Azure Functions", Value: "azure-functions"},
					{Label: "Other", Value: "other-model-tasks"},
				},
			},
			Next: []qna.QuestionReference{{Key: "use-custom-data"}},
		},
		"choose-messaging": {
			BeforeAsk: func(ctx context.Context, q *qna.Question, _ any) error {
				hasMessagingResource := false
				for _, resource := range a.composedResources {
					if strings.HasPrefix(resource.Type, "messaging.") {
						hasMessagingResource = true
						break
					}
				}

				if hasMessagingResource {
					promptMessage := "It looks like you already have a configured messaging source. Do you want to reuse it?"

					q.Prompt = &qna.ConfirmPrompt{
						Client:       a.azdClient,
						Message:      promptMessage,
						DefaultValue: to.Ptr(true),
						HelpMessage:  "Using an existing database will save you time and resources.",
					}
				}

				q.State["hasMessagingResource"] = hasMessagingResource

				return nil
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				hasMessagingResource := q.State["hasMessagingResource"].(bool)
				reuseMessaging, ok := value.(bool)

				if !hasMessagingResource || ok && !reuseMessaging {
					q.Next = []qna.QuestionReference{{Key: "choose-messaging-type"}}
				}

				return nil
			},
		},
		"choose-messaging-type": {
			Binding: &a.scenarioData.MessagingType,
			Prompt: &qna.SingleSelectPrompt{
				Client:          a.azdClient,
				Message:         "Which messaging service do you want to use?",
				HelpMessage:     "Select the messaging service that best fits your needs.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Choose for me", Value: "messaging.servicebus"},
					{Label: "Azure Service Bus", Value: "messaging.servicebus"},
					{Label: "Azure Event Hubs", Value: "messaging.eventhubs"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"messaging.eventhubs":  {{Key: "choose-messaging-resource"}},
				"messaging.servicebus": {{Key: "choose-messaging-resource"}},
			},
		},
		"choose-messaging-resource": {
			Binding: &a.scenarioData.MessagingId,
			Prompt: &qna.SubscriptionResourcePrompt{
				HelpMessage:  "Select an existing messaging service or create a new one.",
				Client:       a.azdClient,
				AzureContext: a.azureContext,
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.SubscriptionResourcePrompt) error {
					resourceType, has := messagingResourceMap[a.scenarioData.MessagingType]
					if !has {
						return fmt.Errorf(
							"unknown resource type for messaging: %s",
							a.scenarioData.MessagingType,
						)
					}

					p.ResourceType = resourceType.Type
					p.Kinds = resourceType.Kinds
					p.ResourceTypeDisplayName = resourceType.DisplayName
					return nil
				},
			},
		},
		"start-choose-models": {
			Heading: "AI Model Selection",
			Message: "Now we will figure out the best AI model(s) for your application.",
			AfterAsk: func(ctx context.Context, question *qna.Question, value any) error {
				if err := a.loadAiCatalog(ctx); err != nil {
					return fmt.Errorf("failed to load AI model catalog: %w", err)
				}

				allModelTypes := map[string]struct {
					Heading           string
					Description       string
					QuestionReference qna.QuestionReference
				}{
					"llm": {
						Heading:     "Large Language Model (LLM) (For generating responses)",
						Description: "Processes user queries and retrieved documents to generate intelligent responses.",
						QuestionReference: qna.QuestionReference{
							Key: "start-choose-model",
							State: map[string]any{
								"modelSelectMessage": "Lets choose a chat completion model",
								"capabilities":       []string{"chatCompletion"},
							},
						},
					},
					"embeddings": {
						Heading: "Embedding Model (For vectorizing text)",
						Description: "Used to convert documents and queries into vector representations " +
							"for efficient similarity searches.",
						QuestionReference: qna.QuestionReference{
							Key: "start-choose-model",
							State: map[string]any{
								"modelSelectMessage": "Lets choose a text embedding model",
								"capabilities":       []string{"embeddings"},
							},
						},
					},
					"audio": {
						Heading:     "Audio Model (For transcribing audio)",
						Description: "Used to convert audio files into text for further processing.",
						QuestionReference: qna.QuestionReference{
							Key: "start-choose-model",
							State: map[string]any{
								"modelSelectMessage": "Lets choose a audio model",
								"capabilities":       []string{"audio"},
							},
						},
					},
					"images": {
						Heading:     "Image Generation Model (For generating images)",
						Description: "Used to generate images based on text prompts.",
						QuestionReference: qna.QuestionReference{
							Key: "start-choose-model",
							State: map[string]any{
								"modelSelectMessage": "Lets choose a image generation model",
								"capabilities":       []string{"imageGenerations"},
							},
						},
					},
				}

				requiredModels := []string{"llm"}
				if slices.Contains(a.scenarioData.DataTypes, "structured-documents") ||
					slices.Contains(a.scenarioData.DataTypes, "unstructured-documents") {
					requiredModels = append(requiredModels, "embeddings")
				}

				if slices.Contains(a.scenarioData.DataTypes, "audio") {
					requiredModels = append(requiredModels, "audio")
				}

				if slices.Contains(a.scenarioData.DataTypes, "images") ||
					slices.Contains(a.scenarioData.DataTypes, "videos") {
					requiredModels = append(requiredModels, "images")
				}

				nextQuestions := []qna.QuestionReference{}

				fmt.Printf("  Based on your choices, you will need the following AI models:\n\n")
				for _, model := range requiredModels {
					if modelType, ok := allModelTypes[model]; ok {
						fmt.Printf("  - %s\n", output.WithBold("%s", modelType.Heading))
						fmt.Printf("    %s\n", output.WithGrayFormat(modelType.Description))
						fmt.Println()
						nextQuestions = append(nextQuestions, modelType.QuestionReference)
					}
				}

				question.Next = nextQuestions

				return nil
			},
		},
		"start-choose-model": {
			BeforeAsk: func(ctx context.Context, question *qna.Question, value any) error {
				if err := a.loadAiCatalog(ctx); err != nil {
					return fmt.Errorf("failed to load AI model catalog: %w", err)
				}

				return nil
			},
			Prompt: &qna.SingleSelectPrompt{
				Client: a.azdClient,
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.SingleSelectPrompt) error {
					// Override the message
					if message, ok := q.State["modelSelectMessage"].(string); ok {
						p.Message = message
					}

					return nil
				},
				Message:         "How do you want to find the right model?",
				HelpMessage:     "Select the option that best fits your needs.",
				EnableFiltering: to.Ptr(false),
				Choices: []qna.Choice{
					{Label: "Choose for me", Value: "choose-model"},
					{Label: "Help me choose", Value: "guide-model"},
					{Label: "I will choose model", Value: "user-model"},
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"guide-model": {{Key: "guide-model-select"}},
				"user-model":  {{Key: "user-model-select"}},
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				selectedValue := value.(string)
				if selectedValue == "choose-model" {
					capabilities, ok := q.State["capabilities"].([]string)
					if !ok {
						return nil
					}

					// If the user selected "choose-model", we need to set the model selection
					for key, value := range defaultModelMap {
						if slices.Contains(capabilities, key) {
							a.scenarioData.ModelSelections = append(
								a.scenarioData.ModelSelections,
								value,
							)
						}
					}
				}

				return nil
			},
		},
		"guide-model-select": {
			Prompt: &qna.MultiSelectPrompt{
				Client:  a.azdClient,
				Message: "Filter AI Models",
				HelpMessage: "Select all the filters that apply to your application. " +
					"These filters will help you narrow down the type of models you need.",
				EnableFiltering: to.Ptr(false),
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.MultiSelectPrompt) error {
					choices := []qna.Choice{}

					if _, has := q.State["capabilities"]; !has {
						choices = append(choices, qna.Choice{
							Label: "Filter by capabilities",
							Value: "filter-model-capability",
						})
					}

					if _, has := q.State["formats"]; !has {
						choices = append(choices, qna.Choice{
							Label: "Filter by author",
							Value: "filter-model-format",
						})
					}
					if _, has := q.State["status"]; !has {
						choices = append(choices, qna.Choice{
							Label: "Filter by status",
							Value: "filter-model-status",
						})
					}
					if _, has := q.State["locations"]; !has {
						choices = append(choices, qna.Choice{
							Label: "Filter by location",
							Value: "filter-model-location",
						})
					}

					p.Choices = choices
					return nil
				},
			},
			Branches: map[any][]qna.QuestionReference{
				"filter-model-capability": {{Key: "filter-model-capability"}},
				"filter-model-format":     {{Key: "filter-model-format"}},
				"filter-model-status":     {{Key: "filter-model-status"}},
				"filter-model-location":   {{Key: "filter-model-location"}},
			},
			Next: []qna.QuestionReference{{Key: "user-model-select"}},
		},
		"user-model-select": {
			Binding: &a.scenarioData.ModelSelections,
			Prompt: &qna.SingleSelectPrompt{
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.SingleSelectPrompt) error {
					var capabilities []string
					var formats []string
					var statuses []string
					var locations []string

					if val, ok := q.State["capabilities"]; ok {
						capabilities = val.([]string)
					}
					if val, ok := q.State["formats"]; ok {
						formats = val.([]string)
					}
					if val, ok := q.State["status"]; ok {
						statuses = val.([]string)
					}
					if val, ok := q.State["locations"]; ok {
						locations = val.([]string)
					}

					filterOptions := &ai.FilterOptions{
						Capabilities: capabilities,
						Formats:      formats,
						Statuses:     statuses,
						Locations:    locations,
					}
					filteredModels := a.modelCatalogService.ListFilteredModels(ctx, a.modelCatalog, filterOptions)

					choices := make([]qna.Choice, len(filteredModels))
					for i, model := range filteredModels {
						choices[i] = qna.Choice{
							Label: fmt.Sprintf("%s %s",
								model.Name,
								output.WithGrayFormat("(%s)", *model.Locations[0].Model.Model.Format),
							),
							Value: model.Name,
						}
					}
					p.Choices = choices

					return nil
				},
				Client:      a.azdClient,
				Message:     "Which model do you want to use?",
				HelpMessage: "Select the model that best fits your needs.",
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				delete(q.State, "capabilities")
				delete(q.State, "formats")
				delete(q.State, "status")
				delete(q.State, "locations")
				return nil
			},
		},
		"filter-model-capability": {
			Prompt: &qna.MultiSelectPrompt{
				Client:      a.azdClient,
				Message:     "What capabilities do you want the model to have?",
				HelpMessage: "Select all the capabilities that apply to your application.",
				Choices: []qna.Choice{
					{Label: "Audio", Value: "audio"},
					{Label: "Chat Completion", Value: "chatCompletion"},
					{Label: "Text Completion", Value: "completion"},
					{Label: "Generate Vector Embeddings", Value: "embeddings"},
					{Label: "Image Generation", Value: "imageGenerations"},
				},
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.State["capabilities"] = value
				return nil
			},
		},
		"filter-model-format": {
			Prompt: &qna.MultiSelectPrompt{
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.MultiSelectPrompt) error {
					formats := a.modelCatalogService.ListAllFormats(ctx, a.modelCatalog)
					choices := make([]qna.Choice, len(formats))
					for i, format := range formats {
						choices[i] = qna.Choice{
							Label: format,
							Value: format,
						}
					}
					p.Choices = choices
					return nil
				},
				Client:      a.azdClient,
				Message:     "Filter my by company or creator",
				HelpMessage: "Select all the companies or creators that apply to your application.",
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.State["formats"] = value
				return nil
			},
		},
		"filter-model-status": {
			Prompt: &qna.MultiSelectPrompt{
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.MultiSelectPrompt) error {
					statuses := a.modelCatalogService.ListAllStatuses(ctx, a.modelCatalog)
					choices := make([]qna.Choice, len(statuses))
					for i, status := range statuses {
						choices[i] = qna.Choice{
							Label: status,
							Value: status,
						}
					}
					p.Choices = choices
					return nil
				},
				Client:          a.azdClient,
				Message:         "Filter by model release status?",
				HelpMessage:     "Select all the model release status that apply to your application.",
				EnableFiltering: to.Ptr(false),
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.State["status"] = value
				return nil
			},
		},
		"filter-model-location": {
			Prompt: &qna.MultiSelectPrompt{
				BeforeAsk: func(ctx context.Context, q *qna.Question, p *qna.MultiSelectPrompt) error {
					spinner := ux.NewSpinner(&ux.SpinnerOptions{
						Text: "Loading Locations",
					})

					err := spinner.Run(ctx, func(ctx context.Context) error {
						locations, err := a.azureClient.ListLocations(ctx, a.azureContext.Scope.SubscriptionId)
						if err != nil {
							return fmt.Errorf("failed to list locations: %w", err)
						}

						choices := make([]qna.Choice, len(locations))
						for i, location := range locations {
							choices[i] = qna.Choice{
								Label: fmt.Sprintf("%s (%s)", *location.DisplayName, *location.Name),
								Value: *location.Name,
							}
						}

						p.Choices = choices
						return nil
					})
					if err != nil {
						return fmt.Errorf("failed to load locations: %w", err)
					}

					return nil
				},
				Client:      a.azdClient,
				Message:     "Filter by model location?",
				HelpMessage: "Select all the model locations that apply to your application.",
			},
			AfterAsk: func(ctx context.Context, q *qna.Question, value any) error {
				q.State["locations"] = value
				return nil
			},
		},
	}, nil
}