From 8e89d2227d8213d285da839b130ee657cff6435e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=A6Ltorio?= Date: Fri, 4 Oct 2024 16:07:31 +0200 Subject: [PATCH] Refactor AIPrompt and App components --- src/aipane/AIPrompt.ts | 9 ++++ src/aipane/aipane.ts | 2 +- src/aipane/components/App.tsx | 13 ++++- src/aipane/components/HeroModels.tsx | 5 +- src/aipane/components/HeroProviders.tsx | 70 +++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 src/aipane/components/HeroProviders.tsx diff --git a/src/aipane/AIPrompt.ts b/src/aipane/AIPrompt.ts index 246b8ed..07286e6 100644 --- a/src/aipane/AIPrompt.ts +++ b/src/aipane/AIPrompt.ts @@ -121,3 +121,12 @@ export function getProvider(providerName: string): AIProvider { export function getModel(provider: AIProvider, modelId: string): AIModel { return provider.models.filter((model: AIModel) => model.id === modelId)[0]; } + +/** + * Retrieves the AI model from config.json for the given provider. + * @param {AIProvider} provider - The AI provider containing the model. + * @returns {AIModel} The AI model with the specified ID. + */ +export function getDefaultModel(provider: AIProvider): AIModel { + return provider.models.filter((model: AIModel) => model.default)[0]; +} diff --git a/src/aipane/aipane.ts b/src/aipane/aipane.ts index 4b096bd..447948a 100644 --- a/src/aipane/aipane.ts +++ b/src/aipane/aipane.ts @@ -69,7 +69,7 @@ export async function insertText(provider: AIProvider, model: AIModel, apiKey: s console.log(`User: ${user}`); console.log(`User text: \n${userText}`); let aiText = await groqRequest(provider, model, apiKey, system, `${user}\n${userText}`); - console.log(`AI response (${model}): \n${aiText}`); + console.log(`AI provider: ${provider.name} AI model: ${model.name}: \n${aiText}`); aiText = aiText.replace(/\n/g, "
"); Office.context.mailbox.item?.body.setSelectedDataAsync( aiText, diff --git a/src/aipane/components/App.tsx b/src/aipane/components/App.tsx index b232a07..826aef1 100644 --- a/src/aipane/components/App.tsx +++ b/src/aipane/components/App.tsx @@ -6,6 +6,7 @@ import * as React from "react"; import { useState, useEffect } from "react"; import Header from "./Header"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars import HeroList, { HeroListItem } from "./HeroList"; import TextInsertion from "./TextInsertion"; import { makeStyles } from "@fluentui/react-components"; @@ -15,6 +16,7 @@ import HeroApiKey from "./HeroApiKey"; import HeroComboPrompts from "./HeroComboPrompts"; import HeroModels from "./HeroModels"; import { AIModel, AIProvider, getDefaultProvider, getModel } from "../AIPrompt"; +import HeroProviders from "./HeroProviders"; interface AppProps { title: string; @@ -36,6 +38,7 @@ const App: React.FC = (props: AppProps) => { // The list items are static and won't change at runtime, // so this should be an ordinary const, not a part of state. + // eslint-disable-next-line @typescript-eslint/no-unused-vars const listItems: HeroListItem[] = [ { icon: , @@ -79,6 +82,13 @@ const App: React.FC = (props: AppProps) => { insertText(provider, model, apiKey, prompt, `${userText}`); }; + const handleProviderChange = (provider: AIProvider) => { + setProvider(provider); + if (!localStorage.getItem(provider.apiKey)) { + setShowApiKeyInput(true); + } + }; + return (
@@ -91,7 +101,8 @@ const App: React.FC = (props: AppProps) => { /> ) : ( <> - + {/* */} + diff --git a/src/aipane/components/HeroModels.tsx b/src/aipane/components/HeroModels.tsx index 54b7eac..08ca81a 100644 --- a/src/aipane/components/HeroModels.tsx +++ b/src/aipane/components/HeroModels.tsx @@ -6,7 +6,7 @@ import * as React from "react"; import { Dropdown, Label, makeStyles, Option, useId } from "@fluentui/react-components"; import { useState, useEffect } from "react"; -import { type AIModel, type AIProvider } from "../AIPrompt"; +import { getDefaultModel, type AIModel, type AIProvider } from "../AIPrompt"; interface HeroModelsProps { onChange: (selectedValue: string) => void; @@ -14,9 +14,6 @@ interface HeroModelsProps { //readValue: () => string; } -function getDefaultModel(provider: AIProvider): AIModel { - return provider.models.filter((model: AIModel) => model.default)[0]; -} const useStyles = makeStyles({ root: { // Stack the label above the field diff --git a/src/aipane/components/HeroProviders.tsx b/src/aipane/components/HeroProviders.tsx new file mode 100644 index 0000000..40d5e9b --- /dev/null +++ b/src/aipane/components/HeroProviders.tsx @@ -0,0 +1,70 @@ +/* +========================================================= +* © 2024 Ronan LE MEILLAT for SCTG Development +========================================================= +*/ +import * as React from "react"; +import { Dropdown, Label, makeStyles, Option, useId } from "@fluentui/react-components"; +import { useState, useEffect } from "react"; +import { getDefaultProvider, getProvider, type AIProvider } from "../AIPrompt"; +import config from "../../config.json"; + +interface HeroProvidersProps { + onChange: (provider: AIProvider) => void; +} + +const useStyles = makeStyles({ + root: { + // Stack the label above the field + display: "flex", + flexDirection: "column", + // Use 2px gap below the label (per the design system) + gap: "2px", + margin: "10px", + }, + combobox: { + marginTop: "20px", + marginBottom: "20px", + }, +}); + +const HeroProviders: React.FC = ({ onChange }) => { + const styles = useStyles(); + const selectId = useId("select"); + const [selectedValue, setSelectedValue] = useState(getDefaultProvider().name); + + const handleChange = (event: React.FormEvent, option?: any) => { + event.preventDefault(); + const newValue = option.nextOption?.value || getDefaultProvider().name; + setSelectedValue(newValue); + onChange(getProvider(newValue)); + }; + + useEffect(() => { + onChange(getProvider(selectedValue)); + }, [selectedValue]); + + return ( +
+ + + {config.providers.map((option: AIProvider) => ( + + ))} + +
+ ); +}; + +export default HeroProviders;