Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multiple LLMs keys implemented #790

Merged
merged 24 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
864d8c4
feat: multiple LLMs keys implemented
diegolikescode Oct 23, 2023
7c3cb56
fix: prettier
diegolikescode Oct 23, 2023
5416a55
fix: references from getOpenAIKey to get the value of OpenAI inside t…
diegolikescode Oct 23, 2023
9c1890d
fix: GitHub Action workflow
diegolikescode Oct 23, 2023
dbfa6e1
fix: remove unused import
diegolikescode Oct 25, 2023
486ca00
rollback .gitignore
diegolikescode Oct 25, 2023
7032dd5
review code and apply necessary changes
diegolikescode Oct 25, 2023
88b4ce7
fix: PR review
diegolikescode Nov 1, 2023
f3dd4f2
feat: LLM providers as env_vars
diegolikescode Nov 10, 2023
b64c358
Merge branch 'main' into feature/multiple-llm-api-keys
diegolikescode Nov 16, 2023
c81da8b
feat: Modify createAndStartTemplate to accept env_vars parameter
bekossy Nov 17, 2023
d1ac920
format fix
bekossy Nov 17, 2023
1257a44
Merge branch 'main' into pr/790
bekossy Nov 20, 2023
4cf2fdc
Merge branch 'main' into agenta/feature/multiple-llm-api-keys
aakrem Nov 27, 2023
24974aa
format
aakrem Nov 27, 2023
710ad20
fixes cypress error
bekossy Nov 27, 2023
6bb2880
adds removeLlmProviderKey func
bekossy Nov 27, 2023
3fc6d16
feat: Add function for retrieving api key
bekossy Nov 27, 2023
3841e70
removes unused imports
bekossy Nov 27, 2023
bf05a18
Merge branch 'main' into feature/multiple-llm-api-keys
aakrem Nov 27, 2023
b944eb8
format
aakrem Nov 27, 2023
849e0d3
Merge branch 'agenta/feature/multiple-llm-api-keys' into pr/790
bekossy Nov 27, 2023
ae36b0b
add div
aakrem Nov 27, 2023
5bd3487
add key
aakrem Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ docker-compose.cloud.prod.yml
agenta-web/src/pages/auth/[[...path]].tsx
agenta-web/cypress/screenshots/
agenta-web/cypress/videos/
.nextjs_cache/
.nextjs_cache/

5 changes: 3 additions & 2 deletions agenta-web/src/components/AppSelector/AppSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {useAppTheme} from "../Layout/ThemeContextProvider"
import {CloseCircleFilled} from "@ant-design/icons"
import TipsAndFeatures from "./TipsAndFeatures"
import Welcome from "./Welcome"
import {getOpenAIKey, isAppNameInputValid, isDemo} from "@/lib/helpers/utils"
import {getLlmProviderKey, isAppNameInputValid, isDemo} from "@/lib/helpers/utils"
import {
createAndStartTemplate,
getTemplates,
Expand Down Expand Up @@ -178,7 +178,8 @@ const AppSelector: React.FC = () => {
handleCreateAppModalCancel()

// warn the user and redirect if openAI key is not present
const openAIKey = getOpenAIKey()
// TODO: must be changed for multiples LLM keys
const openAIKey = getLlmProviderKey("OpenAI")
if (!openAIKey && !isDemo()) {
notification.error({
message: "OpenAI API Key Missing",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import {useVariants} from "@/lib/hooks/useVariant"
import {useRouter} from "next/router"
import {EvaluationFlow, EvaluationType} from "@/lib/enums"
import {getOpenAIKey} from "@/lib/helpers/utils"
import {getLlmProviderKey} from "@/lib/helpers/utils"
import {createUseStyles} from "react-jss"
import {exportAICritiqueEvaluationData} from "@/lib/helpers/evaluate"
import SecondaryButton from "../SecondaryButton/SecondaryButton"
Expand Down Expand Up @@ -272,7 +272,7 @@ Answer ONLY with one of the given grading or evaluation options.
inputs: rows[rowNumber].inputs,
outputs: data.outputs,
evaluation_prompt_template: evaluationPromptTemplate,
open_ai_key: getOpenAIKey(),
open_ai_key: getLlmProviderKey("OpenAI"),
})

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
import {useVariants} from "@/lib/hooks/useVariant"
import {useRouter} from "next/router"
import {EvaluationFlow, EvaluationType} from "@/lib/enums"
import {getOpenAIKey} from "@/lib/helpers/utils"
import {getLlmProviderKey} from "@/lib/helpers/utils"
import {createUseStyles} from "react-jss"
import SecondaryButton from "../SecondaryButton/SecondaryButton"
import {exportCustomCodeEvaluationData} from "@/lib/helpers/evaluate"
Expand Down Expand Up @@ -292,7 +292,7 @@ const CustomCodeRunEvaluationTable: React.FC<CustomCodeEvaluationTableProps> = (
outputs: [{variant_id: variants[0].variantId, variant_output: outputVariantX}],
inputs: rows[rowNumber].inputs,
correct_answer: correctAnswer(rows[rowNumber].inputs),
open_ai_key: getOpenAIKey(),
open_ai_key: getLlmProviderKey("OpenAI"),
}

try {
Expand Down
7 changes: 5 additions & 2 deletions agenta-web/src/components/Evaluations/Evaluations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
useLoadTestsetsList,
fetchCustomEvaluations,
} from "@/lib/services/api"
import {dynamicComponent, getOpenAIKey, isDemo} from "@/lib/helpers/utils"
import {dynamicComponent, getLlmProviderKey, isDemo} from "@/lib/helpers/utils"
import {useRouter} from "next/router"
import {Variant, Parameter, GenericObject, SingleCustomEvaluation} from "@/lib/Types"
import {EvaluationType} from "@/lib/enums"
Expand Down Expand Up @@ -337,7 +337,10 @@ export default function Evaluations() {
} else if (selectedTestset?.name === "Select a Test set") {
message.error("Please select a testset")
return
} else if (!getOpenAIKey() && selectedEvaluationType === EvaluationType.auto_ai_critique) {
} else if (
!getLlmProviderKey("OpenAI") &&
selectedEvaluationType === EvaluationType.auto_ai_critique
) {
setError({
message:
"In order to run an AI Critique evaluation, please set your OpenAI API key in the API Keys page.",
Expand Down
93 changes: 57 additions & 36 deletions agenta-web/src/components/pages/settings/Secrets/Secrets.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import {getOpenAIKey, removeOpenAIKey, saveOpenAIKey} from "@/lib/helpers/utils"
import {
getLlmProviderKey,
saveLlmProviderKey,
removeSingleLlmProviderKey,
getAllProviderLlmKeys,
LlmProvider,
} from "@/lib/helpers/utils"
import {Button, Input, Space, Typography, message} from "antd"
import {useState} from "react"
import {createUseStyles} from "react-jss"
Expand All @@ -16,18 +22,19 @@ const useStyles = createUseStyles({
margin: "0px 0",
},
input: {
minWidth: 400,
display: "flex",
alignItems: "center",
width: 420,
marginBottom: 8,
marginLeft: 8,
},
})

export default function Secrets() {
const classes = useStyles()
const savedOpenAiKey = getOpenAIKey()
const [openAiKey, setOpenAiKey] = useState(savedOpenAiKey)
const [llmProviderKeys, setLlmProviderKeys] = useState(getAllProviderLlmKeys())
const [messageAPI, contextHolder] = message.useMessage()

const saveDisabled = openAiKey === savedOpenAiKey

return (
<div data-cy="secrets">
{contextHolder}
Expand All @@ -41,38 +48,52 @@ export default function Secrets() {
</Text>

<div className={classes.container}>
<Title level={5}>Providers</Title>
<Title level={5}>Available Providers</Title>

<div className={classes.apiContainer}>
<Space direction="horizontal">
<Input.Password
data-cy="openai-api-input"
value={openAiKey}
onChange={(e) => setOpenAiKey(e.target.value)}
addonBefore="OpenAI API Key"
visibilityToggle={false}
className={classes.input}
/>
<Button
data-cy="openai-api-save"
disabled={saveDisabled}
onClick={() => {
saveOpenAIKey(openAiKey)
messageAPI.success("The secret is saved")
}}
>
Save
</Button>
<Button
onClick={() => {
removeOpenAIKey()
setOpenAiKey("")
messageAPI.warning("The secret is deleted")
}}
>
Delete
</Button>
</Space>
{llmProviderKeys.map(({title, key}: LlmProvider, i: number) => (
<Space direction="horizontal" key={i}>
<Input.Password
data-cy="openai-api-input"
value={key}
onChange={(e) => {
const newLlmProviderKeys = [...llmProviderKeys]
newLlmProviderKeys[i].key = e.target.value
setLlmProviderKeys(newLlmProviderKeys)
}}
addonBefore={`${title}`}
visibilityToggle={false}
className={classes.input}
/>
<Button
data-cy="openai-api-save"
disabled={
llmProviderKeys[i].key ===
getLlmProviderKey(llmProviderKeys[i].key) ||
!llmProviderKeys[i].key
}
diegolikescode marked this conversation as resolved.
Show resolved Hide resolved
onClick={() => {
saveLlmProviderKey(i, key)
messageAPI.success("The secret is saved")
}}
>
Save
</Button>
<Button
onClick={() => {
removeSingleLlmProviderKey(i)

const newLlmProviderKeys = [...llmProviderKeys]
newLlmProviderKeys[i].key = ""
setLlmProviderKeys(newLlmProviderKeys)

messageAPI.warning("The secret is deleted")
}}
>
Delete
</Button>
</Space>
))}
</div>
</div>
</div>
Expand Down
56 changes: 47 additions & 9 deletions agenta-web/src/lib/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
import dynamic from "next/dynamic"
import {EvaluationType} from "../enums"

const openAItoken = "openAiToken"
const llmAvailableProvidersToken = "llmAvailableProvidersToken"

export type LlmProvider = {
title: string
key: string
}

export const llmAvailableProviders: LlmProvider[] = [
diegolikescode marked this conversation as resolved.
Show resolved Hide resolved
"OpenAI",
"Replicate",
"Hugging Face",
"Cohere",
"Anthropic",
"Azure",
"TogetherAI",
].map((provider) => ({
title: provider,
key: "",
}))

export const renameVariables = (name: string) => {
return name.charAt(0).toUpperCase() + name.slice(1).replace(/_/g, " ")
Expand All @@ -26,25 +44,45 @@ export const EvaluationTypeLabels: Record<EvaluationType, string> = {
[EvaluationType.auto_webhook_test]: "Webhook Test",
}

export const saveOpenAIKey = (key: string) => {
export const saveLlmProviderKey = (providerIdx: number, keyValue: string) => {
if (typeof window !== "undefined") {
// TODO: add encryption here
localStorage.setItem(openAItoken, key)
const keys = JSON.parse(localStorage.getItem(llmAvailableProvidersToken) ?? "[{}]")
keys[providerIdx].key = keyValue
localStorage.setItem(llmAvailableProvidersToken, JSON.stringify(keys))
}
}

export const getOpenAIKey = (): string => {
export const getLlmProviderKey = (providerName: string) => {
diegolikescode marked this conversation as resolved.
Show resolved Hide resolved
// precedence order: local storage, env variable, empty string
let key
if (typeof window !== "undefined") {
key = localStorage.getItem(openAItoken)
const inStorage = localStorage.getItem(llmAvailableProvidersToken)
if (inStorage) {
return JSON.parse(inStorage).find((prov: LlmProvider) => prov.title == providerName)
}
}
// the "NEXT_PUBLIC_OPENIA_API_KEY logic here must be rethought
diegolikescode marked this conversation as resolved.
Show resolved Hide resolved
// return key ?? process.env.NEXT_PUBLIC_OPENAI_API_KEY ?? ""
return ""
}

export const getAllProviderLlmKeys = () => {
if (typeof window !== "undefined") {
const inStorage = localStorage.getItem(llmAvailableProvidersToken)
if (inStorage) {
return JSON.parse(inStorage)
}
// if doesn't have the localStorage variable
localStorage.setItem(llmAvailableProvidersToken, JSON.stringify(llmAvailableProviders))
}
return key ?? process.env.NEXT_PUBLIC_OPENAI_API_KEY ?? ""
return llmAvailableProviders
}

export const removeOpenAIKey = () => {
export const removeSingleLlmProviderKey = (providerIdx: number) => {
if (typeof window !== "undefined") {
localStorage.removeItem(openAItoken)
const keys = JSON.parse(localStorage.getItem(llmAvailableProvidersToken) ?? "[{}]")
keys[providerIdx].key = ""
localStorage.setItem(llmAvailableProvidersToken, JSON.stringify(keys))
}
}

Expand Down
Loading