Merge pull request #329 from abi/add-anthropic-key

Add field for Anthropic API key in settings
This commit is contained in:
Abi Raja 2024-05-31 14:35:47 -04:00 committed by GitHub
commit 22e45cc566
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 93 additions and 30 deletions

View File

@ -120,6 +120,17 @@ async def stream_code(websocket: WebSocket):
) )
return return
# Get the Anthropic API key from the request. Fall back to environment variable if not provided.
# If neither is provided, we throw an error later only if Claude is used.
anthropic_api_key = None
if "anthropicApiKey" in params and params["anthropicApiKey"]:
anthropic_api_key = params["anthropicApiKey"]
print("Using Anthropic API key from client-side settings dialog")
else:
anthropic_api_key = ANTHROPIC_API_KEY
if anthropic_api_key:
print("Using Anthropic API key from environment variable")
# Get the OpenAI Base URL from the request. Fall back to environment variable if not provided. # Get the OpenAI Base URL from the request. Fall back to environment variable if not provided.
openai_base_url = None openai_base_url = None
# Disable user-specified OpenAI Base URL in prod # Disable user-specified OpenAI Base URL in prod
@ -219,31 +230,31 @@ async def stream_code(websocket: WebSocket):
else: else:
try: try:
if validated_input_mode == "video": if validated_input_mode == "video":
if not ANTHROPIC_API_KEY: if not anthropic_api_key:
await throw_error( await throw_error(
"Video only works with Anthropic models. No Anthropic API key found. Please add the environment variable ANTHROPIC_API_KEY to backend/.env" "Video only works with Anthropic models. No Anthropic API key found. Please add the environment variable ANTHROPIC_API_KEY to backend/.env or in the settings dialog"
) )
raise Exception("No Anthropic key") raise Exception("No Anthropic key")
completion = await stream_claude_response_native( completion = await stream_claude_response_native(
system_prompt=VIDEO_PROMPT, system_prompt=VIDEO_PROMPT,
messages=prompt_messages, # type: ignore messages=prompt_messages, # type: ignore
api_key=ANTHROPIC_API_KEY, api_key=anthropic_api_key,
callback=lambda x: process_chunk(x), callback=lambda x: process_chunk(x),
model=Llm.CLAUDE_3_OPUS, model=Llm.CLAUDE_3_OPUS,
include_thinking=True, include_thinking=True,
) )
exact_llm_version = Llm.CLAUDE_3_OPUS exact_llm_version = Llm.CLAUDE_3_OPUS
elif code_generation_model == Llm.CLAUDE_3_SONNET: elif code_generation_model == Llm.CLAUDE_3_SONNET:
if not ANTHROPIC_API_KEY: if not anthropic_api_key:
await throw_error( await throw_error(
"No Anthropic API key found. Please add the environment variable ANTHROPIC_API_KEY to backend/.env" "No Anthropic API key found. Please add the environment variable ANTHROPIC_API_KEY to backend/.env or in the settings dialog"
) )
raise Exception("No Anthropic key") raise Exception("No Anthropic key")
completion = await stream_claude_response( completion = await stream_claude_response(
prompt_messages, # type: ignore prompt_messages, # type: ignore
api_key=ANTHROPIC_API_KEY, api_key=anthropic_api_key,
callback=lambda x: process_chunk(x), callback=lambda x: process_chunk(x),
) )
exact_llm_version = code_generation_model exact_llm_version = code_generation_model

View File

@ -46,7 +46,8 @@
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"thememirror": "^2.0.1", "thememirror": "^2.0.1",
"vite-plugin-checker": "^0.6.2", "vite-plugin-checker": "^0.6.2",
"webm-duration-fix": "^1.0.4" "webm-duration-fix": "^1.0.4",
"zustand": "^4.5.2"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",

View File

@ -59,6 +59,7 @@ function App() {
{ {
openAiApiKey: null, openAiApiKey: null,
openAiBaseURL: null, openAiBaseURL: null,
anthropicApiKey: null,
screenshotOneApiKey: null, screenshotOneApiKey: null,
isImageGenerationEnabled: true, isImageGenerationEnabled: true,
editorTheme: EditorTheme.COBALT, editorTheme: EditorTheme.COBALT,
@ -378,7 +379,10 @@ function App() {
/> />
)} )}
<div className="lg:fixed lg:inset-y-0 lg:z-40 lg:flex lg:w-96 lg:flex-col"> <div className="lg:fixed lg:inset-y-0 lg:z-40 lg:flex lg:w-96 lg:flex-col">
<div className="flex grow flex-col gap-y-2 overflow-y-auto border-r border-gray-200 bg-white px-6 dark:bg-zinc-950 dark:text-white"> <div
className="flex grow flex-col gap-y-2 overflow-y-auto border-r
border-gray-200 bg-white px-6 dark:bg-zinc-950 dark:text-white"
>
<div className="flex items-center justify-between mt-10 mb-2"> <div className="flex items-center justify-between mt-10 mb-2">
<h1 className="text-2xl ">Screenshot to Code</h1> <h1 className="text-2xl ">Screenshot to Code</h1>
<SettingsDialog settings={settings} setSettings={setSettings} /> <SettingsDialog settings={settings} setSettings={setSettings} />

View File

@ -49,7 +49,7 @@ function SettingsDialog({ settings, setSettings }: Props) {
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Label htmlFor="image-generation"> <Label htmlFor="image-generation">
<div>DALL-E Placeholder Image Generation</div> <div>DALL-E Placeholder Image Generation</div>
<div className="font-light mt-2"> <div className="font-light mt-2 text-xs">
More fun with it but if you want to save money, turn it off. More fun with it but if you want to save money, turn it off.
</div> </div>
</Label> </Label>
@ -64,29 +64,31 @@ function SettingsDialog({ settings, setSettings }: Props) {
} }
/> />
</div> </div>
<div className="flex flex-col space-y-4"> <div className="flex flex-col space-y-6">
<Label htmlFor="openai-api-key"> <div>
<div>OpenAI API key</div> <Label htmlFor="openai-api-key">
<div className="font-light mt-2 leading-relaxed"> <div>OpenAI API key</div>
Only stored in your browser. Never stored on servers. Overrides <div className="font-light mt-1 mb-2 text-xs leading-relaxed">
your .env config. Only stored in your browser. Never stored on servers. Overrides
</div> your .env config.
</Label> </div>
</Label>
<Input <Input
id="openai-api-key" id="openai-api-key"
placeholder="OpenAI API key" placeholder="OpenAI API key"
value={settings.openAiApiKey || ""} value={settings.openAiApiKey || ""}
onChange={(e) => onChange={(e) =>
setSettings((s) => ({ setSettings((s) => ({
...s, ...s,
openAiApiKey: e.target.value, openAiApiKey: e.target.value,
})) }))
} }
/> />
</div>
{!IS_RUNNING_ON_CLOUD && ( {!IS_RUNNING_ON_CLOUD && (
<> <div>
<Label htmlFor="openai-api-key"> <Label htmlFor="openai-api-key">
<div>OpenAI Base URL (optional)</div> <div>OpenAI Base URL (optional)</div>
<div className="font-light mt-2 leading-relaxed"> <div className="font-light mt-2 leading-relaxed">
@ -105,9 +107,31 @@ function SettingsDialog({ settings, setSettings }: Props) {
})) }))
} }
/> />
</> </div>
)} )}
<div>
<Label htmlFor="anthropic-api-key">
<div>Anthropic API key</div>
<div className="font-light mt-1 text-xs leading-relaxed">
Only stored in your browser. Never stored on servers. Overrides
your .env config.
</div>
</Label>
<Input
id="anthropic-api-key"
placeholder="Anthropic API key"
value={settings.anthropicApiKey || ""}
onChange={(e) =>
setSettings((s) => ({
...s,
anthropicApiKey: e.target.value,
}))
}
/>
</div>
<Accordion type="single" collapsible className="w-full"> <Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1"> <AccordionItem value="item-1">
<AccordionTrigger>Screenshot by URL Config</AccordionTrigger> <AccordionTrigger>Screenshot by URL Config</AccordionTrigger>

View File

@ -0,0 +1,10 @@
import { create } from "zustand";
// Store for app-wide state
interface AppStore {
inputMode: "image" | "video";
}
export const useStore = create<AppStore>(() => ({
inputMode: "image",
}));

View File

@ -16,6 +16,7 @@ export interface Settings {
codeGenerationModel: CodeGenerationModel; codeGenerationModel: CodeGenerationModel;
// Only relevant for hosted version // Only relevant for hosted version
isTermOfServiceAccepted: boolean; isTermOfServiceAccepted: boolean;
anthropicApiKey: string | null; // Added property for anthropic API key
} }
export enum AppState { export enum AppState {

View File

@ -5642,6 +5642,11 @@ use-sidecar@^1.1.2:
detect-node-es "^1.1.0" detect-node-es "^1.1.0"
tslib "^2.0.0" tslib "^2.0.0"
use-sync-external-store@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
util-deprecate@^1.0.2: util-deprecate@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
@ -5937,3 +5942,10 @@ zod@3.22.4:
version "3.22.4" version "3.22.4"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==
zustand@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"
integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==
dependencies:
use-sync-external-store "1.2.0"