From e0e679e7a3176f3a76c7739cb0994333b725effd Mon Sep 17 00:00:00 2001
From: Julie Huang <julhuang@gitlab.com>
Date: Fri, 26 Jul 2024 15:44:10 +0000
Subject: [PATCH] Initialise NewSelfHostedModel Vue app

- Adds graphql mutation definition
- Initialises NewSelfHostedModel vue app if FF is enabled
- Adds heading and description to page, but does not include form content
---
 .../components/new_self_hosted_model.vue      | 42 +++++++++++++++++++
 .../create_self_hosted_model.mutation.graphql |  5 +++
 .../admin/ai/self_hosted_models/new/index.js  |  4 ++
 .../admin/ai/self_hosted_models/new.html.haml | 13 ++++--
 .../admin/ai/self_hosted_models/mock_data.js  |  8 ++++
 .../new_self_hosted_model_spec.js             | 30 +++++++++++++
 locale/gitlab.pot                             |  6 +++
 7 files changed, 104 insertions(+), 4 deletions(-)
 create mode 100644 ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/components/new_self_hosted_model.vue
 create mode 100644 ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/graphql/mutations/create_self_hosted_model.mutation.graphql
 create mode 100644 ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/new/index.js
 create mode 100644 ee/spec/frontend/pages/admin/ai/self_hosted_models/new_self_hosted_model_spec.js

diff --git a/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/components/new_self_hosted_model.vue b/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/components/new_self_hosted_model.vue
new file mode 100644
index 0000000000000..fb18c1fbdec60
--- /dev/null
+++ b/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/components/new_self_hosted_model.vue
@@ -0,0 +1,42 @@
+<script>
+import { s__ } from '~/locale';
+import selfHostedModelCreateMutation from '../graphql/mutations/create_self_hosted_model.mutation.graphql';
+
+const MUTATION_NAME = 'aiSelfHostedModelCreate';
+
+export default {
+  name: 'NewSelfHostedModel',
+  props: {
+    basePath: {
+      type: String,
+      required: true,
+    },
+    modelOptions: {
+      type: Array,
+      required: true,
+    },
+  },
+  i18n: {
+    title: s__('AdminSelfHostedModels|Add self-hosted models'),
+    description: s__(
+      'AdminSelfHostedModels|Add a new AI model that can be used for GitLab Duo features.',
+    ),
+  },
+  mutationData: {
+    name: MUTATION_NAME,
+    mutation: selfHostedModelCreateMutation,
+  },
+};
+</script>
+<template>
+  <div>
+    <h1>{{ $options.i18n.title }}</h1>
+    <p class="gl-pt-3 gl-pb-2">
+      {{ $options.i18n.description }}
+    </p>
+    <!--
+      TODO: Add form for creating self hosted model.
+      The form will be passed props basePath, modelOptions and mutationData.
+    -->
+  </div>
+</template>
diff --git a/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/graphql/mutations/create_self_hosted_model.mutation.graphql b/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/graphql/mutations/create_self_hosted_model.mutation.graphql
new file mode 100644
index 0000000000000..e6f0e8284fc64
--- /dev/null
+++ b/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/graphql/mutations/create_self_hosted_model.mutation.graphql
@@ -0,0 +1,5 @@
+mutation createSelfHostedModel($input: AiSelfHostedModelCreateInput!) {
+  aiSelfHostedModelCreate(input: $input) {
+    errors
+  }
+}
diff --git a/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/new/index.js b/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/new/index.js
new file mode 100644
index 0000000000000..62b85bcbeb23b
--- /dev/null
+++ b/ee/app/assets/javascripts/pages/admin/ai/self_hosted_models/new/index.js
@@ -0,0 +1,4 @@
+import { initSimpleApp } from '~/helpers/init_simple_app_helper';
+import NewSelfHostedModel from '../components/new_self_hosted_model.vue';
+
+initSimpleApp('#js-new-self-hosted-model', NewSelfHostedModel, { withApolloProvider: true });
diff --git a/ee/app/views/admin/ai/self_hosted_models/new.html.haml b/ee/app/views/admin/ai/self_hosted_models/new.html.haml
index e9ec43849e0dc..949a5422b0c40 100644
--- a/ee/app/views/admin/ai/self_hosted_models/new.html.haml
+++ b/ee/app/views/admin/ai/self_hosted_models/new.html.haml
@@ -1,4 +1,9 @@
-- page_title s_('AdminSelfHostedModels|New self-hosted model')
-%h1.page-title.gl-font-size-h-display
-  = s_('AdminSelfHostedModels|New self-hosted model')
-= render 'form', url: admin_ai_self_hosted_models_path, back_path: admin_ai_self_hosted_models_path
+- page_title s_('AdminSelfHostedModels|Add self-hosted models')
+- if Feature.enabled?(:custom_models_vue_app, current_user)
+  - model_options = Ai::SelfHostedModel.models.map { |name, _| { modelValue: name.upcase, modelName: name.capitalize } }
+
+  #js-new-self-hosted-model{ data: { view_model: { basePath: admin_ai_self_hosted_models_path, modelOptions: model_options }.to_json } }
+- else
+  %h1.page-title.gl-text-size-h-display
+    = s_('AdminSelfHostedModels|Add self-hosted models')
+  = render 'form', url: admin_ai_self_hosted_models_path, back_path: admin_ai_self_hosted_models_path
diff --git a/ee/spec/frontend/pages/admin/ai/self_hosted_models/mock_data.js b/ee/spec/frontend/pages/admin/ai/self_hosted_models/mock_data.js
index f2c81b39806bf..740226620b4de 100644
--- a/ee/spec/frontend/pages/admin/ai/self_hosted_models/mock_data.js
+++ b/ee/spec/frontend/pages/admin/ai/self_hosted_models/mock_data.js
@@ -30,3 +30,11 @@ export const mockAiSelfHostedModelsQueryResponse = {
     },
   },
 };
+
+export const SELF_HOSTED_MODEL_OPTIONS = [
+  { modelValue: 'MIXTRAL', modelName: 'Mixtral' },
+  { modelValue: 'MISTRAL', modelName: 'Mistral' },
+  { modelValue: 'CODEGEMMA', modelName: 'Codegemma' },
+  { modelValue: 'CODESTRAL', modelName: 'Codestral' },
+  { modelValue: 'CODELLAMA', modelName: 'Codellama' },
+];
diff --git a/ee/spec/frontend/pages/admin/ai/self_hosted_models/new_self_hosted_model_spec.js b/ee/spec/frontend/pages/admin/ai/self_hosted_models/new_self_hosted_model_spec.js
new file mode 100644
index 0000000000000..c9a506bb01ac4
--- /dev/null
+++ b/ee/spec/frontend/pages/admin/ai/self_hosted_models/new_self_hosted_model_spec.js
@@ -0,0 +1,30 @@
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import NewSelfHostedModel from 'ee/pages/admin/ai/self_hosted_models/components/new_self_hosted_model.vue';
+import { SELF_HOSTED_MODEL_OPTIONS } from './mock_data';
+
+describe('NewSelfHostedModel', () => {
+  let wrapper;
+
+  const basePath = '/admin/ai/self_hosted_models';
+
+  const createComponent = () => {
+    wrapper = mountExtended(NewSelfHostedModel, {
+      propsData: {
+        basePath,
+        modelOptions: SELF_HOSTED_MODEL_OPTIONS,
+      },
+    });
+  };
+
+  beforeEach(() => {
+    createComponent();
+  });
+
+  it('has a title', () => {
+    expect(wrapper.text()).toMatch('Add self-hosted models');
+  });
+
+  it('has a description', () => {
+    expect(wrapper.text()).toMatch('Add a new AI model that can be used for GitLab Duo features.');
+  });
+});
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 061390a3cb11e..fe0826fcd7c5d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3729,9 +3729,15 @@ msgstr ""
 msgid "AdminSelfHostedModels|API token (if needed)"
 msgstr ""
 
+msgid "AdminSelfHostedModels|Add a new AI model that can be used for GitLab Duo features."
+msgstr ""
+
 msgid "AdminSelfHostedModels|Add self-hosted language models to use as backups for GitLab Duo features."
 msgstr ""
 
+msgid "AdminSelfHostedModels|Add self-hosted models"
+msgstr ""
+
 msgid "AdminSelfHostedModels|An API key is set"
 msgstr ""
 
-- 
GitLab