2
0
Feroze Mohideen 2 жил өмнө
parent
commit
1ad9fe7e1e

+ 14 - 11
dashboard/package-lock.json

@@ -100,7 +100,7 @@
         "@babel/preset-typescript": "^7.15.0",
         "@ianvs/prettier-plugin-sort-imports": "^4.1.1",
         "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
-        "@porter-dev/api-contracts": "file:../../api-contracts/generated/js",
+        "@porter-dev/api-contracts": "^0.2.166",
         "@testing-library/jest-dom": "^4.2.4",
         "@testing-library/react": "^9.3.2",
         "@testing-library/user-event": "^7.1.2",
@@ -173,14 +173,6 @@
         "npm": "9.7.2"
       }
     },
-    "../../api-contracts/generated/js": {
-      "version": "0.2.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@bufbuild/protobuf": "^1.1.0"
-      }
-    },
     "node_modules/@aashutoshrathi/word-wrap": {
       "version": "1.2.6",
       "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
@@ -2084,6 +2076,12 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@bufbuild/protobuf": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.9.0.tgz",
+      "integrity": "sha512-W7gp8Q/v1NlCZLsv8pQ3Y0uCu/SHgXOVFK+eUluUKWXmsb6VHkpNx0apdOWWcDbB9sJoKeP8uPrjmehJz6xETQ==",
+      "dev": true
+    },
     "node_modules/@discoveryjs/json-ext": {
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@@ -2788,8 +2786,13 @@
       }
     },
     "node_modules/@porter-dev/api-contracts": {
-      "resolved": "../../api-contracts/generated/js",
-      "link": true
+      "version": "0.2.166",
+      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.166.tgz",
+      "integrity": "sha512-189degLVU36wPP2Bd4eSLiVY0fYzZTiCOrPsiUXkXeOVZCMAUO6uRmEf8YQOgNkQ/kVGiUtkG9SfH4sXdvZZHw==",
+      "dev": true,
+      "dependencies": {
+        "@bufbuild/protobuf": "^1.1.0"
+      }
     },
     "node_modules/@react-spring/animated": {
       "version": "9.6.1",

+ 1 - 1
dashboard/package.json

@@ -107,7 +107,7 @@
     "@babel/preset-typescript": "^7.15.0",
     "@ianvs/prettier-plugin-sort-imports": "^4.1.1",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
-    "@porter-dev/api-contracts": "file:../../api-contracts/generated/js",
+    "@porter-dev/api-contracts": "^0.2.166",
     "@testing-library/jest-dom": "^4.2.4",
     "@testing-library/react": "^9.3.2",
     "@testing-library/user-event": "^7.1.2",

+ 16 - 4
dashboard/src/components/porter/Toggle.tsx

@@ -46,14 +46,26 @@ const StyledToggle = styled.div`
   align-items: center;
 `;
 
-const Item = styled.div<{ active: boolean; activeColor?: string; inactiveColor?: string }>`
+const Item = styled.div<{
+  active: boolean;
+  activeColor?: string;
+  inactiveColor?: string;
+}>`
   display: flex;
   align-items: center;
   height: 100%;
   cursor: pointer;
   justify-content: center;
   padding: 10px;
-  opacity: ${(props) => props.active ? "1" : "0.4"};
+  opacity: ${(props) => (props.active ? "1" : "0.4")};
   background: ${(props) =>
-    props.active ? props.activeColor ?? "#ffffff11" : props.inactiveColor ?? "transparent"};
-`;
+    props.active
+      ? props.activeColor ?? "#ffffff11"
+      : props.inactiveColor ?? "transparent"};
+`;
+
+export const ToggleIcon = styled.img`
+  height: 12px;
+  margin: 0 5px;
+  min-width: 12px;
+`;

+ 13 - 0
dashboard/src/lib/addons/deepgram.ts

@@ -0,0 +1,13 @@
+import { z } from "zod";
+
+export const deepgramConfigValidator = z.object({
+  type: z.literal("deepgram"),
+  deepgramAPIKey: z.string().nonempty().default("*********"),
+  quayUsername: z.string().nonempty().default("username"),
+  quaySecret: z.string().nonempty().default("secret"),
+  quayEmail: z.string(),
+  instanceType: z.literal("g4dn.xlarge"),
+  releaseTag: z.string().nonempty().default("release-240426"),
+});
+
+export type DeepgramConfigValidator = z.infer<typeof deepgramConfigValidator>;

+ 11 - 2
dashboard/src/lib/addons/index.ts

@@ -17,15 +17,16 @@ import { z } from "zod";
 import { serviceStringValidator } from "lib/porter-apps/values";
 
 import { datadogConfigValidator } from "./datadog";
+import { deepgramConfigValidator } from "./deepgram";
 import { metabaseConfigValidator } from "./metabase";
 import { mezmoConfigValidator } from "./mezmo";
 import { newrelicConfigValidator } from "./newrelic";
 import { defaultPostgresAddon, postgresConfigValidator } from "./postgres";
 import { redisConfigValidator } from "./redis";
 import { tailscaleConfigValidator } from "./tailscale";
-import { deepgramConfigValidator } from "lib/models/deepgram";
 import {
   ADDON_TEMPLATE_DATADOG,
+  ADDON_TEMPLATE_DEEPGRAM,
   ADDON_TEMPLATE_METABASE,
   ADDON_TEMPLATE_MEZMO,
   ADDON_TEMPLATE_NEWRELIC,
@@ -34,7 +35,6 @@ import {
   ADDON_TEMPLATE_TAILSCALE,
   type AddonTemplate,
 } from "./template";
-import { ADDON_TEMPLATE_DEEPGRAM } from "lib/models/template";
 
 export const clientAddonValidator = z.object({
   expanded: z.boolean().default(false),
@@ -67,6 +67,13 @@ export type ClientAddonType = z.infer<
 export type ClientAddon = z.infer<typeof clientAddonValidator> & {
   template: AddonTemplate<ClientAddonType>;
 };
+export type ClientModelAddon = ClientAddon & {
+  template: AddonTemplate<ClientAddonType> & { isModelTemplate: true };
+};
+export const isClientModelAddon = (
+  addon: ClientAddon
+): addon is ClientModelAddon => addon.template.isModelTemplate ?? false;
+
 export const legacyAddonValidator = z.object({
   name: z.string(),
   namespace: z.string(),
@@ -276,6 +283,7 @@ export function clientAddonToProto(
         ecrPassword: data.quaySecret,
         ecrEmail: data.quayEmail,
         instanceType: data.instanceType,
+        releaseTag: data.releaseTag,
       }),
       case: "deepgram" as const,
     }))
@@ -396,6 +404,7 @@ export function clientAddonFromProto({
       quayUsername: data.value.ecrUsername ?? "",
       quaySecret: data.value.ecrPassword ?? "",
       quayEmail: data.value.ecrEmail ?? "",
+      releaseTag: data.value.releaseTag ?? "",
       instanceType: "g4dn.xlarge" as const,
     }))
     .exhaustive();

+ 30 - 0
dashboard/src/lib/addons/template.ts

@@ -1,6 +1,7 @@
 import Logs from "main/home/add-on-dashboard/common/Logs";
 import Settings from "main/home/add-on-dashboard/common/Settings";
 import DatadogForm from "main/home/add-on-dashboard/datadog/DatadogForm";
+import DeepgramForm from "main/home/add-on-dashboard/deepgram/DeepgramForm";
 import MetabaseForm from "main/home/add-on-dashboard/metabase/MetabaseForm";
 import MezmoForm from "main/home/add-on-dashboard/mezmo/MezmoForm";
 import NewRelicForm from "main/home/add-on-dashboard/newrelic/NewRelicForm";
@@ -47,6 +48,7 @@ export type AddonTemplate<T extends ClientAddonType> = {
   tags: AddonTemplateTag[];
   tabs: AddonTab[]; // this what is rendered on the dashboard after the addon is deployed
   defaultValues: ClientAddon["config"] & { type: T };
+  isModelTemplate?: boolean;
 };
 
 export const ADDON_TEMPLATE_REDIS: AddonTemplate<"redis"> = {
@@ -279,6 +281,30 @@ export const ADDON_TEMPLATE_TAILSCALE: AddonTemplate<"tailscale"> = {
   },
 };
 
+export const ADDON_TEMPLATE_DEEPGRAM: AddonTemplate<"deepgram"> = {
+  type: "deepgram",
+  displayName: "Deepgram",
+  description: "A popular speech-to-text service.",
+  icon: "https://play-lh.googleusercontent.com/wczDL05-AOb39FcL58L32h6j_TrzzGTXDLlOrOmJ-aNsnoGsT1Gkk2vU4qyTb7tGxRw=w240-h480-rw",
+  tags: ["Networking"],
+  tabs: [
+    {
+      name: "configuration",
+      displayName: "Configuration",
+      component: DeepgramForm,
+    },
+  ],
+  defaultValues: {
+    type: "deepgram",
+    deepgramAPIKey: "",
+    quayUsername: "",
+    quaySecret: "",
+    quayEmail: "",
+    instanceType: "g4dn.xlarge",
+  },
+  isModelTemplate: true,
+};
+
 export const SUPPORTED_ADDON_TEMPLATES: Array<AddonTemplate<ClientAddonType>> =
   [
     ADDON_TEMPLATE_DATADOG,
@@ -287,3 +313,7 @@ export const SUPPORTED_ADDON_TEMPLATES: Array<AddonTemplate<ClientAddonType>> =
     ADDON_TEMPLATE_NEWRELIC,
     ADDON_TEMPLATE_TAILSCALE,
   ];
+
+export const SUPPORTED_MODEL_ADDON_TEMPLATES: Array<
+  AddonTemplate<ClientAddonType>
+> = [ADDON_TEMPLATE_DEEPGRAM];

+ 9 - 3
dashboard/src/lib/hooks/useAddon.ts

@@ -25,9 +25,11 @@ import { type DeploymentTarget } from "./useDeploymentTarget";
 export const useAddonList = ({
   projectId,
   deploymentTarget,
+  includeLegacyAddons = true,
 }: {
   projectId?: number;
   deploymentTarget?: DeploymentTarget;
+  includeLegacyAddons?: boolean;
 }): {
   addons: ClientAddon[];
   legacyAddons: LegacyClientAddon[];
@@ -139,12 +141,16 @@ export const useAddonList = ({
               "monitoring",
               "porter-agent-system",
               "external-secrets",
-              "infisical"
+              "infisical",
             ].includes(a.namespace ?? "");
           });
       },
       {
-        enabled: !!projectId && projectId !== -1 && !!deploymentTarget,
+        enabled:
+          !!projectId &&
+          projectId !== -1 &&
+          !!deploymentTarget &&
+          includeLegacyAddons,
         refetchOnWindowFocus: false,
         refetchInterval: 5000,
       }
@@ -552,7 +558,7 @@ export const useAddonLogs = ({
   projectId?: number;
   deploymentTarget: DeploymentTarget;
   addon?: ClientAddon;
-}): { logs: Log[]; refresh: () => void; isInitializing: boolean } => {
+}): { logs: Log[]; refresh: () => Promise<void>; isInitializing: boolean } => {
   const [logs, setLogs] = useState<Log[]>([]);
   const logsBufferRef = useRef<Log[]>([]);
   const { newWebsocket, openWebsocket, closeAllWebsockets } = useWebsockets();

+ 0 - 12
dashboard/src/lib/models/deepgram.tsx

@@ -1,12 +0,0 @@
-import { z } from "zod";
-
-export const deepgramConfigValidator = z.object({
-    type: z.literal("deepgram"),
-    deepgramAPIKey: z.string().nonempty().default("*********"),
-    quayUsername: z.string().nonempty().default("username"),
-    quaySecret: z.string().nonempty().default("secret"),
-    quayEmail: z.string(),
-    instanceType: z.literal("g4dn.xlarge"),
-});
-
-export type DeepgramConfigValidator = z.infer<typeof deepgramConfigValidator>;

+ 0 - 30
dashboard/src/lib/models/template.ts

@@ -1,30 +0,0 @@
-import { ClientAddonType } from "lib/addons";
-import { AddonTemplate } from "lib/addons/template";
-import DeepgramForm from "main/home/inference-dashboard/TemplateForms/DeepgramForm";
-
-export const ADDON_TEMPLATE_DEEPGRAM: AddonTemplate<"deepgram"> = {
-    type: "deepgram",
-    displayName: "Deepgram",
-    description: "A popular speech-to-text service.",
-    icon: "https://play-lh.googleusercontent.com/wczDL05-AOb39FcL58L32h6j_TrzzGTXDLlOrOmJ-aNsnoGsT1Gkk2vU4qyTb7tGxRw=w240-h480-rw",
-    tags: ["Networking"],
-    tabs: [
-      {
-        name: "configuration",
-        displayName: "Configuration",
-        component: DeepgramForm,
-      },
-    ],
-    defaultValues: {
-      type: "deepgram",
-      deepgramAPIKey: "",
-      quayUsername: "",
-      quaySecret: "",
-      quayEmail: "", 
-      instanceType: "g4dn.xlarge",
-    },
-  };
-
-export const SUPPORTED_MODEL_ADDON_TEMPLATES: Array<AddonTemplate<ClientAddonType>> = [
-    ADDON_TEMPLATE_DEEPGRAM,
-]

+ 7 - 18
dashboard/src/main/home/Home.tsx

@@ -41,6 +41,7 @@ import OryLogin from "../auth/OryLogin";
 import AddonDashboard from "./add-on-dashboard/AddOnDashboard";
 import AddonTemplates from "./add-on-dashboard/AddonTemplates";
 import AddonView from "./add-on-dashboard/AddonView";
+import InferenceDashboard from "./add-on-dashboard/InferenceDashboard";
 import LegacyAddOnDashboard from "./add-on-dashboard/legacy_AddOnDashboard";
 import LegacyNewAddOnFlow from "./add-on-dashboard/legacy_NewAddOnFlow";
 import AppView from "./app-dashboard/app-view/AppView";
@@ -57,10 +58,6 @@ import CreateDatastore from "./database-dashboard/forms/CreateDatastore";
 import CreateEnvGroup from "./env-dashboard/CreateEnvGroup";
 import EnvDashboard from "./env-dashboard/EnvDashboard";
 import ExpandedEnv from "./env-dashboard/ExpandedEnv";
-import ExpandedModelTemplate from "./inference-dashboard/ExpandedModelTemplate";
-import InferenceDashboard from "./inference-dashboard/InferenceDashboard";
-import ModelForm from "./inference-dashboard/ModelForm";
-import ModelTemplates from "./inference-dashboard/ModelTemplates";
 import ClusterContextProvider from "./infrastructure-dashboard/ClusterContextProvider";
 import ClusterDashboard from "./infrastructure-dashboard/ClusterDashboard";
 import ClusterView from "./infrastructure-dashboard/ClusterView";
@@ -73,8 +70,6 @@ import { NewProjectFC } from "./new-project/NewProject";
 import Onboarding from "./onboarding/Onboarding";
 import ProjectSettings from "./project-settings/ProjectSettings";
 import Sidebar from "./sidebar/Sidebar";
-import AddonFormContextProvider from "./add-on-dashboard/AddonFormContextProvider";
-import AddonForm from "./add-on-dashboard/AddonForm";
 
 dayjs.extend(relativeTime);
 
@@ -549,20 +544,14 @@ const Home: React.FC<Props> = (props) => {
                   <LegacyAddOnDashboard />
                 )}
               </Route>
-              <Route path="/inference/models">
-                <ModelTemplates />
+              <Route path="/inference/new">
+                <AddonTemplates filterModels />
               </Route>
-              <Route path="/inference/expanded/:modelType">
-                <ExpandedModelTemplate />
+              <Route path="/inference/:addonName">
+                <AddonView filterModels />
               </Route>
-              <Route path="/inference/new/:modelType">
-                <ModelForm />
-              </Route>
-              <Route path="/inference/:modelType">
-                <AddonView />
-              </Route>
-              <Route path="/inference/:modelType/:tab">
-                <AddonView />
+              <Route path="/inference/:addonName/:tab">
+                <AddonView filterModels />
               </Route>
               <Route path="/inference">
                 <InferenceDashboard />

+ 10 - 4
dashboard/src/main/home/add-on-dashboard/AddonForm.tsx

@@ -22,9 +22,11 @@ import Configuration from "./common/Configuration";
 
 type Props<T extends ClientAddonType> = {
   template: AddonTemplate<T>;
+  filterModels?: boolean;
 };
 const AddonForm = <T extends ClientAddonType>({
   template,
+  filterModels,
 }: Props<T>): JSX.Element => {
   const { currentProject, currentCluster } = useContext(Context);
   const { defaultDeploymentTarget, isDefaultDeploymentTargetLoading } =
@@ -72,7 +74,9 @@ const AddonForm = <T extends ClientAddonType>({
       legacyAddons.some((a) => a.name === watchName)
     ) {
       setError("name.value", {
-        message: "An addon with this name already exists",
+        message: filterModels
+          ? "A model with this name already exists"
+          : "An addon with this name already exists",
       });
     } else {
       clearErrors("name.value");
@@ -94,7 +98,7 @@ const AddonForm = <T extends ClientAddonType>({
           <StyledConfigureTemplate>
             <Back
               onClick={() => {
-                history.push(`/addons/new`);
+                history.push(filterModels ? `/inference/new` : `/addons/new`);
               }}
             />
             <DashboardHeader
@@ -108,7 +112,9 @@ const AddonForm = <T extends ClientAddonType>({
               currentStep={currentStep}
               steps={[
                 <>
-                  <Text size={16}>Add-on name</Text>
+                  <Text size={16}>
+                    {filterModels ? "Model name" : "Add-on name"}
+                  </Text>
                   <Spacer y={0.5} />
                   <Text color="helper">
                     Lowercase letters, numbers, and &quot;-&quot; only.
@@ -117,7 +123,7 @@ const AddonForm = <T extends ClientAddonType>({
                   <ControlledInput
                     type="text"
                     width="300px"
-                    placeholder="ex: my-addon"
+                    placeholder={filterModels ? "ex: my-model" : "ex: my-addon"}
                     error={errors.name?.value?.message}
                     {...register("name.value")}
                   />

+ 58 - 38
dashboard/src/main/home/add-on-dashboard/AddonTemplates.tsx

@@ -8,75 +8,91 @@ import { type ClientAddonType } from "lib/addons";
 import {
   AddonTemplateTagColor,
   SUPPORTED_ADDON_TEMPLATES,
+  SUPPORTED_MODEL_ADDON_TEMPLATES,
   type AddonTemplate,
 } from "lib/addons/template";
 
 import { Context } from "shared/Context";
 import addOnGrad from "assets/add-on-grad.svg";
+import inferenceGrad from "assets/inference-grad.svg";
 
 import DashboardHeader from "../cluster-dashboard/DashboardHeader";
 import AddonForm from "./AddonForm";
 import AddonFormContextProvider from "./AddonFormContextProvider";
 
-const AddonTemplates: React.FC = () => {
+type Props = {
+  filterModels?: boolean;
+};
+const AddonTemplates: React.FC<Props> = ({ filterModels }) => {
   const { currentProject } = useContext(Context);
   const { search } = useLocation();
   const queryParams = new URLSearchParams(search);
   const history = useHistory();
 
   const templateMatch = useMemo(() => {
-    const addonName = queryParams.get("addon_name");
-    return SUPPORTED_ADDON_TEMPLATES.find((t) => t.type === addonName);
+    const addonName = filterModels
+      ? queryParams.get("model_name")
+      : queryParams.get("addon_name");
+    return (
+      filterModels ? SUPPORTED_MODEL_ADDON_TEMPLATES : SUPPORTED_ADDON_TEMPLATES
+    ).find((t) => t.type === addonName);
   }, [queryParams]);
 
   if (templateMatch) {
     return (
       <AddonFormContextProvider projectId={currentProject?.id} redirectOnSubmit>
-        <AddonForm template={templateMatch} />
+        <AddonForm template={templateMatch} filterModels={filterModels} />
       </AddonFormContextProvider>
     );
   }
 
   return (
     <StyledTemplateComponent>
-      <Back to="/addons" />
+      <Back to={filterModels ? "/inference" : "/addons"} />
       <DashboardHeader
-        image={addOnGrad}
-        title="Create a new add-on"
+        image={filterModels ? inferenceGrad : addOnGrad}
+        title={filterModels ? "Explore models" : "Create a new add-on"}
         capitalize={false}
-        description="Select an add-on to deploy to this project."
+        description={
+          filterModels
+            ? "Select a model to deploy to this project."
+            : "Select an add-on to deploy to this project."
+        }
         disableLineBreak
       />
       <TemplateListWrapper>
-        {SUPPORTED_ADDON_TEMPLATES.map(
-          (template: AddonTemplate<ClientAddonType>) => {
-            return (
-              <TemplateBlock
-                key={template.type}
-                onClick={() => {
-                  history.push(`/addons/new?addon_name=${template.type}`);
-                }}
-              >
-                <Icon src={template.icon} />
-                <TemplateTitle>{template.displayName}</TemplateTitle>
-                <TemplateDescription>
-                  {template.description}
-                </TemplateDescription>
-                <Spacer y={0.5} />
-                {template.tags.map((t) => (
-                  <Tag
-                    bottom="10px"
-                    left="12px"
-                    style={{ background: AddonTemplateTagColor[t] }}
-                    key={t}
-                  >
-                    {t}
-                  </Tag>
-                ))}
-              </TemplateBlock>
-            );
-          }
-        )}
+        {(filterModels
+          ? SUPPORTED_MODEL_ADDON_TEMPLATES
+          : SUPPORTED_ADDON_TEMPLATES
+        ).map((template: AddonTemplate<ClientAddonType>) => {
+          return (
+            <TemplateBlock
+              key={template.type}
+              onClick={() => {
+                history.push(
+                  filterModels
+                    ? `/inference/new?model_name=${template.type}`
+                    : `/addons/new?addon_name=${template.type}`
+                );
+              }}
+            >
+              <Icon src={template.icon} />
+              <TemplateTitle>{template.displayName}</TemplateTitle>
+              <TemplateDescription>{template.description}</TemplateDescription>
+              <Spacer y={0.5} />
+              {template.tags.map((t) => (
+                <Tag
+                  bottom="10px"
+                  left="12px"
+                  style={{ background: AddonTemplateTagColor[t] }}
+                  key={t}
+                >
+                  {t}
+                </Tag>
+              ))}
+            </TemplateBlock>
+          );
+        })}
       </TemplateListWrapper>
     </StyledTemplateComponent>
   );
@@ -89,7 +105,11 @@ const StyledTemplateComponent = styled.div`
   height: 100%;
 `;
 
-export const Tag = styled.div<{ size?: string; bottom?: string; left?: string }>`
+export const Tag = styled.div<{
+  size?: string;
+  bottom?: string;
+  left?: string;
+}>`
   position: absolute;
   bottom: ${(props) => props.bottom || "auto"};
   left: ${(props) => props.left || "auto"};

+ 5 - 3
dashboard/src/main/home/add-on-dashboard/AddonView.tsx

@@ -14,9 +14,11 @@ import AddonFormContextProvider from "./AddonFormContextProvider";
 import AddonHeader from "./AddonHeader";
 import AddonTabs from "./AddonTabs";
 
-type Props = RouteComponentProps;
+type Props = RouteComponentProps & {
+  filterModels?: boolean;
+};
 
-const AddonView: React.FC<Props> = ({ match }) => {
+const AddonView: React.FC<Props> = ({ match, filterModels }) => {
   const { currentProject, currentCluster } = useContext(Context);
   const params = useMemo(() => {
     const { params } = match;
@@ -41,7 +43,7 @@ const AddonView: React.FC<Props> = ({ match }) => {
       <AddonContextProvider addonName={params.addonName}>
         <AddonFormContextProvider projectId={currentProject?.id}>
           <StyledExpandedAddon>
-            <Back to="/addons" />
+            <Back to={filterModels ? "/inference" : "/addons"} />
             <AddonHeader />
             <Spacer y={1} />
             <AddonTabs tabParam={params.tab} />

+ 232 - 0
dashboard/src/main/home/add-on-dashboard/InferenceDashboard.tsx

@@ -0,0 +1,232 @@
+import React, { useContext, useMemo, useState } from "react";
+import styled from "styled-components";
+import { match, P } from "ts-pattern";
+
+import ClusterProvisioningPlaceholder from "components/ClusterProvisioningPlaceholder";
+import Loading from "components/Loading";
+import Button from "components/porter/Button";
+import Container from "components/porter/Container";
+import DashboardHeader from "components/porter/DashboardHeader";
+import DashboardPlaceholder from "components/porter/DashboardPlaceholder";
+import Icon from "components/porter/Icon";
+import Link from "components/porter/Link";
+import SearchBar from "components/porter/SearchBar";
+import Spacer from "components/porter/Spacer";
+import Text from "components/porter/Text";
+import Toggle, { ToggleIcon } from "components/porter/Toggle";
+import { isClientModelAddon, type ClientModelAddon } from "lib/addons";
+import { useAddonList } from "lib/hooks/useAddon";
+import { useDefaultDeploymentTarget } from "lib/hooks/useDeploymentTarget";
+
+import { Context } from "shared/Context";
+import grid from "assets/grid.png";
+import inferenceGrad from "assets/inference-grad.svg";
+import list from "assets/list.png";
+import healthy from "assets/status-healthy.png";
+
+const InferenceDashboard: React.FC = () => {
+  const { currentProject, currentCluster } = useContext(Context);
+  const { defaultDeploymentTarget, isDefaultDeploymentTargetLoading } =
+    useDefaultDeploymentTarget();
+
+  const [searchValue, setSearchValue] = useState("");
+  const [view, setView] = useState<"grid" | "list">("grid");
+
+  const { addons, isLoading: isAddonListLoading } = useAddonList({
+    projectId: currentProject?.id,
+    deploymentTarget: defaultDeploymentTarget,
+    includeLegacyAddons: false,
+  });
+
+  const models: ClientModelAddon[] = useMemo(() => {
+    const modelAddons = addons.filter(isClientModelAddon);
+
+    return modelAddons;
+  }, [addons, defaultDeploymentTarget]);
+
+  return (
+    <StyledInferenceDashboard>
+      <DashboardHeader
+        image={inferenceGrad}
+        title={
+          <Container row>
+            Inference
+            <Spacer inline x={1} />
+            <Badge>Beta</Badge>
+          </Container>
+        }
+        capitalize={false}
+        description="Run open source ML models in your own cloud."
+      />
+      {match([
+        currentCluster?.status,
+        models.length,
+        isAddonListLoading || isDefaultDeploymentTargetLoading,
+      ])
+        .with(["UPDATING_UNAVAILABLE", P._, P._], () => (
+          <ClusterProvisioningPlaceholder />
+        ))
+        .with([P._, P.number, true], () => <Loading offset="-150px" />)
+        .with([P._, 0, false], () => (
+          <DashboardPlaceholder>
+            <Text size={16}>No ML models have been deployed yet</Text>
+            <Spacer y={0.5} />
+            <Text color={"helper"}>Get started by deploying a model.</Text>
+            <Spacer y={1} />
+            <Link to="/inference/new">
+              <Button onClick={() => ({})} height="35px" alt>
+                Deploy a new model <Spacer inline x={1} />{" "}
+                <i className="material-icons" style={{ fontSize: "18px" }}>
+                  east
+                </i>
+              </Button>
+            </Link>
+          </DashboardPlaceholder>
+        ))
+        .otherwise(() => (
+          <div>
+            <Container row spaced>
+              <SearchBar
+                value={searchValue}
+                setValue={setSearchValue}
+                placeholder="Search add-ons . . ."
+                width="100%"
+              />
+              <Spacer inline x={2} />
+              <Toggle
+                items={[
+                  { label: <ToggleIcon src={grid} />, value: "grid" },
+                  { label: <ToggleIcon src={list} />, value: "list" },
+                ]}
+                active={view}
+                setActive={(s: string) => {
+                  setView(s as "grid" | "list");
+                }}
+              />
+              <Spacer inline x={2} />
+              <Link to="/models/new">
+                <Button onClick={() => {}} height="30px" width="130px">
+                  <I className="material-icons">add</I> New model
+                </Button>
+              </Link>
+            </Container>
+            <Spacer y={1} />
+            {match(view)
+              .with("grid", () => (
+                <div>
+                  {models.map((model) => (
+                    <Block
+                      to={`/inference/${model.name.value}`}
+                      key={model.name.value}
+                    >
+                      <Container row>
+                        <Icon src={model.template.icon} />
+                        <Text size={14}>{model.name.value}</Text>
+                        <Spacer inline x={2} />
+                      </Container>
+                      <StatusIcon src={healthy} />
+                    </Block>
+                  ))}
+                </div>
+              ))
+              .with("list", () => (
+                <div>
+                  {models.map((model) => (
+                    <Row
+                      to={`/inference/${model.name.value}`}
+                      key={model.name.value}
+                    >
+                      <Container row>
+                        <MidIcon src={model.template.icon} />
+                        <Text size={14}>{model.name.value}</Text>
+                        <Spacer inline x={1} />
+                        <MidIcon src={healthy} height="16px" />
+                      </Container>
+                    </Row>
+                  ))}
+                </div>
+              ))
+              .exhaustive()}
+          </div>
+        ))}
+    </StyledInferenceDashboard>
+  );
+};
+
+export default InferenceDashboard;
+
+const Badge = styled.div`
+  background: linear-gradient(60deg, #4b366d 0%, #6475b9 100%);
+  color: white;
+  border-radius: 3px;
+  padding: 2px 5px;
+  margin-right: -5px;
+  font-size: 13px;
+`;
+
+const StyledInferenceDashboard = styled.div`
+  width: 100%;
+  height: 100%;
+`;
+
+const I = styled.i`
+  color: white;
+  font-size: 14px;
+  display: flex;
+  align-items: center;
+  margin-right: 5px;
+  justify-content: center;
+`;
+
+const Block = styled(Link)`
+  height: 75px;
+  flex-direction: column;
+  display: flex;
+  justify-content: space-between;
+  cursor: pointer;
+  padding: 20px;
+  color: ${(props) => props.theme.text.primary};
+  position: relative;
+  border-radius: 5px;
+  background: ${(props) => props.theme.clickable.bg};
+  border: 1px solid #494b4f;
+  :hover {
+    border: 1px solid #7a7b80;
+  }
+
+  animation: fadeIn 0.3s 0s;
+  @keyframes fadeIn {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;
+
+const StatusIcon = styled.img`
+  position: absolute;
+  top: 20px;
+  right: 20px;
+  height: 18px;
+`;
+
+const Row = styled(Link)<{ isAtBottom?: boolean }>`
+  cursor: pointer;
+  display: block;
+  padding: 15px;
+  border-bottom: ${(props) =>
+    props.isAtBottom ? "none" : "1px solid #494b4f"};
+  background: ${(props) => props.theme.clickable.bg};
+  position: relative;
+  border: 1px solid #494b4f;
+  border-radius: 5px;
+  margin-bottom: 15px;
+  animation: fadeIn 0.3s 0s;
+`;
+
+const MidIcon = styled.img<{ height?: string }>`
+  height: ${(props) => props.height || "18px"};
+  margin-right: 11px;
+`;

+ 1 - 1
dashboard/src/main/home/add-on-dashboard/common/Configuration.tsx

@@ -1,6 +1,7 @@
 import React from "react";
 import { match } from "ts-pattern";
 
+import DeepgramForm from "main/home/add-on-dashboard/deepgram/DeepgramForm";
 import { type ClientAddon } from "lib/addons";
 
 import DatadogForm from "../datadog/DatadogForm";
@@ -8,7 +9,6 @@ import MetabaseForm from "../metabase/MetabaseForm";
 import MezmoForm from "../mezmo/MezmoForm";
 import NewRelicForm from "../newrelic/NewRelicForm";
 import TailscaleForm from "../tailscale/TailscaleForm";
-import DeepgramForm from "main/home/inference-dashboard/TemplateForms/DeepgramForm";
 
 type Props = {
   type: ClientAddon["config"]["type"];

+ 106 - 0
dashboard/src/main/home/add-on-dashboard/deepgram/DeepgramForm.tsx

@@ -0,0 +1,106 @@
+import React from "react";
+import { useFormContext } from "react-hook-form";
+
+import { ControlledInput } from "components/porter/ControlledInput";
+import Spacer from "components/porter/Spacer";
+import Text from "components/porter/Text";
+import AddonSaveButton from "main/home/add-on-dashboard/AddonSaveButton";
+import { type ClientAddon } from "lib/addons";
+
+const DeepgramForm: React.FC = () => {
+  const {
+    register,
+    formState: { errors },
+  } = useFormContext<ClientAddon>();
+  return (
+    <div>
+      <Text size={16}> Instance type </Text>
+      <Spacer y={0.5} />
+      <Text color="helper">
+        The instance type to run the model on. Deepgram runs only on T4 GPUs and
+        we have prefilled a preferred instance type for now. Make sure the AWS
+        quota is properly set.{" "}
+      </Text>
+      <Spacer y={0.5} />
+      <ControlledInput
+        type="text"
+        width="300px"
+        {...register("config.instanceType")}
+        placeholder="g4dn-xlarge"
+        error={errors.config?.instanceType?.message}
+      />
+      <Spacer y={1} />
+      <Text size={16}> Release tag </Text>
+      <Spacer y={0.5} />
+      <Text color="helper">
+        The release tag for the specific deepgram model you would like to deploy
+      </Text>
+      <Spacer y={0.5} />
+      <ControlledInput
+        type="text"
+        width="300px"
+        {...register("config.releaseTag")}
+        placeholder="ex: release-240426"
+        error={errors.config?.releaseTag?.message}
+      />
+      <Spacer y={1} />
+      <Text size={16}> Deepgram API Key </Text>
+      <Spacer y={0.5} />
+      <Text color="helper"> The API Key provided to you by Deepgram </Text>
+      <Spacer y={0.5} />
+      <ControlledInput
+        type="password"
+        width="300px"
+        {...register("config.deepgramApiKey")}
+        placeholder="deepgram-api-key"
+        error={errors.config?.deepgramApiKey?.message}
+      />
+      <Spacer y={1} />
+      <Text size={16}> Quay.io Username </Text>
+      <Spacer y={0.5} />
+      <Text color="helper">
+        The username to the container registry provided to you by Deepgram
+      </Text>
+      <Spacer y={0.5} />
+      <ControlledInput
+        type="text"
+        width="300px"
+        {...register("config.quayUsername")}
+        placeholder="ex: release-240426"
+        error={errors.config?.quayUsername?.message}
+      />
+      <Spacer y={1} />
+      <Text size={16}> Quay.io secret </Text>
+      <Spacer y={0.5} />
+      <Text color="helper">
+        The username to the container registry provided to you by Deepgram
+      </Text>
+      <Spacer y={0.5} />
+      <ControlledInput
+        type="password"
+        width="300px"
+        {...register("config.quaySecret")}
+        placeholder="quay.io-secret"
+        error={errors.config?.quaySecret?.message}
+      />
+      <Spacer y={1} />
+      <Text size={16}> Quay.io email </Text>
+      <Spacer y={0.5} />
+      <Text color="helper">
+        The username to the container registry provided to you by Deepgram
+      </Text>
+      <Spacer y={0.5} />
+      <ControlledInput
+        type="text"
+        width="300px"
+        {...register("config.quayEmail")}
+        placeholder="x@y.com"
+        error={errors.config?.quayEmail?.message}
+      />
+      <Spacer y={1} />
+      <AddonSaveButton />
+    </div>
+  );
+};
+
+export default DeepgramForm;

+ 13 - 10
dashboard/src/main/home/inference-dashboard/ExpandedModelTemplate.tsx

@@ -12,16 +12,18 @@ import InfoSection from "components/porter/InfoSection";
 import Line from "components/porter/Line";
 import Link from "components/porter/Link";
 import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text"
+import Text from "components/porter/Text";
+import {
+  AddonTemplateTagColor,
+  SUPPORTED_MODEL_ADDON_TEMPLATES,
+} from "lib/addons/template";
 
-import { Tag } from "../add-on-dashboard/AddonTemplates"
+import { Context } from "shared/Context";
 
+import AddonFormContextProvider from "../add-on-dashboard/AddonFormContextProvider";
+import { Tag } from "../add-on-dashboard/AddonTemplates";
 import DashboardHeader from "../cluster-dashboard/DashboardHeader";
 import { models, tagColor } from "./models";
-import { Context } from "shared/Context";
-import { SUPPORTED_MODEL_ADDON_TEMPLATES } from "lib/models/template";
-import AddonFormContextProvider from "../add-on-dashboard/AddonFormContextProvider";
-import { AddonTemplateTagColor } from "lib/addons/template";
 
 const ExpandedModelTemplate: React.FC = () => {
   const { modelType } = useParams<{
@@ -31,7 +33,9 @@ const ExpandedModelTemplate: React.FC = () => {
   const { currentProject } = useContext(Context);
   const history = useHistory();
 
-  const templateMatch = SUPPORTED_MODEL_ADDON_TEMPLATES.find((t) => t.type === modelType);
+  const templateMatch = SUPPORTED_MODEL_ADDON_TEMPLATES.find(
+    (t) => t.type === modelType
+  );
 
   if (templateMatch === undefined) {
     return null;
@@ -68,9 +72,8 @@ const ExpandedModelTemplate: React.FC = () => {
             >
               {t}
             </Tag>
-            {templateMatch.tags.indexOf(t) !== templateMatch.tags.length - 1 && (
-              <Spacer inline x={0.5} />
-            )}
+            {templateMatch.tags.indexOf(t) !==
+              templateMatch.tags.length - 1 && <Spacer inline x={0.5} />}
           </>
         ))}
       </Container>

+ 0 - 79
dashboard/src/main/home/inference-dashboard/InferenceDashboard.tsx

@@ -1,79 +0,0 @@
-import React, { useContext, useState } from "react";
-import styled from "styled-components";
-
-import ClusterProvisioningPlaceholder from "components/ClusterProvisioningPlaceholder";
-import Button from "components/porter/Button";
-import Container from "components/porter/Container";
-import DashboardHeader from "components/porter/DashboardHeader";
-import DashboardPlaceholder from "components/porter/DashboardPlaceholder";
-import Link from "components/porter/Link";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-
-import { Context } from "shared/Context";
-import inferenceGrad from "assets/inference-grad.svg";
-
-const PreviewEnvs: React.FC = () => {
-  const { currentCluster } = useContext(Context);
-  const [models, setModels] = useState<any[]>([]);
-
-  const renderContents = () => {
-    if (currentCluster?.status === "UPDATING_UNAVAILABLE") {
-      return <ClusterProvisioningPlaceholder />;
-    }
-
-    if (models.length === 0) {
-      return (
-        <DashboardPlaceholder>
-          <Text size={16}>No ML models have been deployed yet</Text>
-          <Spacer y={0.5} />
-
-          <Text color={"helper"}>Get started by deploying a model.</Text>
-          <Spacer y={1} />
-          <Link to="/inference/models">
-            <Button onClick={() => ({})} height="35px" alt>
-              Deploy a new model <Spacer inline x={1} />{" "}
-              <i className="material-icons" style={{ fontSize: "18px" }}>
-                east
-              </i>
-            </Button>
-          </Link>
-        </DashboardPlaceholder>
-      );
-    }
-  };
-
-  return (
-    <StyledAppDashboard>
-      <DashboardHeader
-        image={inferenceGrad}
-        title={
-          <Container row>
-            Inference
-            <Spacer inline x={1} />
-            <Badge>Beta</Badge>
-          </Container>
-        }
-        capitalize={false}
-        description="Run open source ML models in your own cloud."
-      />
-      {renderContents()}
-    </StyledAppDashboard>
-  );
-};
-
-export default PreviewEnvs;
-
-const Badge = styled.div`
-  background: linear-gradient(60deg, #4b366d 0%, #6475b9 100%);
-  color: white;
-  border-radius: 3px;
-  padding: 2px 5px;
-  margin-right: -5px;
-  font-size: 13px;
-`;
-
-const StyledAppDashboard = styled.div`
-  width: 100%;
-  height: 100%;
-`;

+ 0 - 56
dashboard/src/main/home/inference-dashboard/ModelForm.tsx

@@ -1,56 +0,0 @@
-import React, { useContext } from "react";
-import { useHistory, useParams } from "react-router";
-import styled from "styled-components";
-import { match } from "ts-pattern";
-
-import Back from "components/porter/Back";
-import CenterWrapper from "components/porter/CenterWrapper";
-import Container from "components/porter/Container";
-import Image from "components/porter/Image";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-
-import { models } from "./models";
-import Gpt2Form from "./TemplateForms/Gpt2Form";
-import { Context } from "shared/Context";
-import { SUPPORTED_MODEL_ADDON_TEMPLATES } from "lib/models/template";
-import AddonForm from "../add-on-dashboard/AddonForm";
-import AddonFormContextProvider from "../add-on-dashboard/AddonFormContextProvider";
-
-const InferenceForm: React.FC = () => {
-  const { modelType } = useParams<{
-    modelType: string;
-  }>();
-
-  const { currentProject } = useContext(Context);
-  const history = useHistory();
-
-  const templateMatch = SUPPORTED_MODEL_ADDON_TEMPLATES.find((t) => t.type === modelType);
-
-  if (templateMatch === undefined) {
-    return null;
-  }
-
-  return (
-    <AddonFormContextProvider projectId={currentProject?.id} redirectOnSubmit>
-      <AddonForm template={templateMatch} />
-    </AddonFormContextProvider>
-  );
-};
-
-export default InferenceForm;
-
-const FloatIn = styled.div`
-  animation: floatIn 0.5s;
-  animation-fill-mode: forwards;
-  @keyframes floatIn {
-    from {
-      opacity: 0;
-      transform: translateY(20px);
-    }
-    to {
-      opacity: 1;
-      transform: translateY(0px);
-    }
-  }
-`;

+ 0 - 179
dashboard/src/main/home/inference-dashboard/ModelTemplates.tsx

@@ -1,179 +0,0 @@
-import React, { useContext, useMemo } from "react";
-import { useHistory, useLocation } from "react-router";
-import styled from "styled-components";
-
-import Back from "components/porter/Back";
-import Container from "components/porter/Container";
-import Spacer from "components/porter/Spacer";
-
-import inferenceGrad from "assets/inference-grad.svg";
-
-import DashboardHeader from "../cluster-dashboard/DashboardHeader";
-import { Context } from "shared/Context";
-import { SUPPORTED_MODEL_ADDON_TEMPLATES } from "lib/models/template";
-import AddonFormContextProvider from "../add-on-dashboard/AddonFormContextProvider";
-import AddonForm from "../add-on-dashboard/AddonForm";
-import { AddonTemplate, AddonTemplateTagColor } from "lib/addons/template";
-import { ClientAddonType } from "lib/addons";
-
-const ModelTemplates: React.FC = () => {
-  const { currentProject } = useContext(Context);
-  const { search } = useLocation();
-  const queryParams = new URLSearchParams(search);
-  const history = useHistory();
-
-  const templateMatch = useMemo(() => {
-    const addonName = queryParams.get("addon_name");
-    return SUPPORTED_MODEL_ADDON_TEMPLATES.find((t) => t.type === addonName);
-  }, [queryParams]);
-
-  if (templateMatch) {
-    return (
-      <AddonFormContextProvider projectId={currentProject?.id} redirectOnSubmit>
-        <AddonForm template={templateMatch} />
-      </AddonFormContextProvider>
-    );
-  }
-
-  return (
-    <StyledTemplateComponent>
-      <Back to="/inference" />
-      <DashboardHeader
-        image={inferenceGrad}
-        title="Explore models"
-        capitalize={false}
-        description="Select a model to deploy on this project."
-        disableLineBreak
-      />
-      <TemplateListWrapper>
-        {
-          SUPPORTED_MODEL_ADDON_TEMPLATES.map(
-            (template: AddonTemplate<ClientAddonType>) => {
-              return (
-                <TemplateBlock
-                  key={template.type}
-                  onClick={() => {
-                    history.push(`/inference/expanded/${template.type}`);
-                  }}
-                >
-                  <Icon src={template.icon} />
-                  <TemplateTitle>{template.displayName}</TemplateTitle>
-                  <TemplateDescription>{template.description}</TemplateDescription>
-                  <Spacer y={0.25} />
-                  <Container row>
-                    {template.tags?.map((t) => (
-                      <>
-                        <Tag
-                          bottom="10px"
-                          left="12px"
-                          style={{ background: AddonTemplateTagColor[t] }}
-                          key={t}
-                        >
-                          {t}
-                        </Tag>
-                        {template.tags.indexOf(t) !== template.tags.length - 1 && (
-                          <Spacer inline x={0.5} />
-                        )}
-                      </>
-                    ))}
-                  </Container>
-                  <Spacer y={0.5} />
-                </TemplateBlock>
-              );
-            }
-          )}
-      </TemplateListWrapper>
-    </StyledTemplateComponent>
-  );
-};
-
-export default ModelTemplates;
-
-const StyledTemplateComponent = styled.div`
-  width: 100%;
-  height: 100%;
-`;
-
-const TemplateDescription = styled.div`
-  color: #ffffff66;
-  text-align: center;
-  font-weight: default;
-  padding: 0px 25px;
-  line-height: 1.4;
-  font-size: 12px;
-  display: -webkit-box;
-  overflow: hidden;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
-`;
-
-const TemplateTitle = styled.div`
-  width: 80%;
-  text-align: center;
-  font-size: 14px;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-`;
-
-const TemplateBlock = styled.div`
-  align-items: center;
-  user-select: none;
-  display: flex;
-  font-size: 13px;
-  flex-direction: column;
-  align-item: center;
-  justify-content: space-between;
-  height: 180px;
-  cursor: pointer;
-  color: #ffffff;
-  position: relative;
-  border-radius: 5px;
-  background: ${(props) => props.theme.clickable.bg};
-  border: 1px solid #494b4f;
-  :hover {
-    border: 1px solid #7a7b80;
-  }
-
-  animation: fadeIn 0.3s 0s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const TemplateListWrapper = styled.div`
-  overflow: visible;
-  padding-bottom: 50px;
-  display: grid;
-  grid-column-gap: 30px;
-  grid-row-gap: 30px;
-  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
-`;
-
-const Icon = styled.img`
-  height: 25px;
-  margin-top: 20px;
-  margin-bottom: 5px;
-`;
-
-
-const Tag = styled.div<{ size?: string; bottom?: string; left?: string }>`
-  position: absolute;
-  bottom: ${(props) => props.bottom || "auto"};
-  left: ${(props) => props.left || "auto"};
-  font-size: 10px;
-  background: linear-gradient(
-    45deg,
-    rgba(88, 24, 219, 1) 0%,
-    rgba(72, 12, 168, 1) 100%
-  ); // added gradient for shiny effect
-  padding: 10px;
-  border-radius: 4px;
-  opacity: 0.85;
-  box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
-`;

+ 0 - 96
dashboard/src/main/home/inference-dashboard/TemplateForms/DeepgramForm.tsx

@@ -1,96 +0,0 @@
-import React from "react";
-
-import Button from "components/porter/Button";
-import { ControlledInput } from "components/porter/ControlledInput";
-import Spacer from "components/porter/Spacer";
-import Text from "components/porter/Text";
-import VerticalSteps from "components/porter/VerticalSteps";
-import { useFormContext } from "react-hook-form";
-import { ClientAddon } from "lib/addons";
-import AddonSaveButton from "main/home/add-on-dashboard/AddonSaveButton";
-
-const DeepgramForm: React.FC = () => {
-    const {
-        register,
-        control,
-        formState: { errors },
-    } = useFormContext<ClientAddon>();
-    return (
-        <div>
-            <Text size={16} > Instance type </Text>
-            <Spacer y={0.5} />
-            <Text color="helper"> The instance type to run the model on. Deepgram runs only on T4 GPUs and we have prefilled a preferred instance type for now. Make sure the AWS quota is properly set. </Text>
-            <Spacer height="20px" />
-            <ControlledInput
-                type="text"
-                width="300px"
-                {...register("config.instanceType")}
-                placeholder="g4dn-xlarge"
-                error={errors.config?.instanceType?.message}
-            />
-            <Spacer y={0.5} />
-            <Text size={16} > Release tag </Text>
-            <Spacer y={0.5} />
-            <Text color="helper"> The release tag for the specific deepgram model you would like to deploy</Text>
-            <Spacer height="20px" />
-            <ControlledInput
-                type="text"
-                width="300px"
-                {...register("config.releaseTag")}
-                placeholder="ex: release-240426"
-                error={errors.config?.releaseTag?.message}
-            />
-            <Spacer y={0.5} />
-            <Text size={16} > Deepgram API Key </Text>
-            <Spacer y={0.5} />
-            <Text color="helper"> The API Key provided to you by Deepgram </Text>
-            <Spacer height="20px" />
-            <ControlledInput
-                type="password"
-                width="300px"
-                {...register("config.deepgramApiKey")}
-                placeholder="deepgram-api-key"
-                error={errors.config?.deepgramApiKey?.message}
-            />
-            <Spacer y={0.5} />
-            <Text size={16} > Quay.io Username </Text>
-            <Spacer y={0.5} />
-            <Text color="helper"> The username to the container registry provided to you by Deepgram </Text>
-            <Spacer height="20px" />
-            <ControlledInput
-                type="text"
-                width="300px"
-                {...register("config.quayUsername")}
-                placeholder="ex: release-240426"
-                error={errors.config?.quayUsername?.message}
-            />
-            <Spacer y={0.5} />
-            <Text size={16} > Quay.io secret </Text>
-            <Spacer y={0.5} />
-            <Text color="helper"> The username to the container registry provided to you by Deepgram </Text>
-            <Spacer height="20px" />
-            <ControlledInput
-                type="password"
-                width="300px"
-                {...register("config.quaySecret")}
-                placeholder="quay.io-secret"
-                error={errors.config?.quaySecret?.message}
-            />
-            <Text size={16} > Quay.io email </Text>
-            <Spacer y={0.5} />
-            <Text color="helper"> The username to the container registry provided to you by Deepgram </Text>
-            <Spacer height="20px" />
-            <ControlledInput
-                type="text"
-                width="300px"
-                {...register("config.quayEmail")}
-                placeholder="x@y.com"
-                error={errors.config?.quayEmail?.message}
-            />
-            <Spacer y={1} />
-            <AddonSaveButton />
-        </div>
-    );
-};
-
-export default DeepgramForm;

+ 1 - 3
go.mod

@@ -87,7 +87,7 @@ require (
 	github.com/nats-io/nats.go v1.24.0
 	github.com/open-policy-agent/opa v0.44.0
 	github.com/ory/client-go v1.9.0
-	github.com/porter-dev/api-contracts v0.2.158
+	github.com/porter-dev/api-contracts v0.2.166
 	github.com/riandyrn/otelchi v0.5.1
 	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
 	github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d
@@ -387,5 +387,3 @@ require (
 	sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
 )
-
-replace github.com/porter-dev/api-contracts => ../api-contracts

+ 2 - 0
go.sum

@@ -1563,6 +1563,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
+github.com/porter-dev/api-contracts v0.2.166 h1:WF6C5hvyD564lpSrcWtcpiK8Z3fflkH+K4STGJYZfrc=
+github.com/porter-dev/api-contracts v0.2.166/go.mod h1:VV5BzXd02ZdbWIPLVP+PX3GKawJSGQnxorVT2sUZALU=
 github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
 github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=