Canvas: Support Anthropic Claude based AI agents
Change-Id: Ib74c9672da9a80a4f20d63741a471c728a435b8e
diff --git a/apps/canvas/front/src/components/node-app.tsx b/apps/canvas/front/src/components/node-app.tsx
index fa08977..a01709f 100644
--- a/apps/canvas/front/src/components/node-app.tsx
+++ b/apps/canvas/front/src/components/node-app.tsx
@@ -6,7 +6,7 @@
import { z } from "zod";
import { useForm, EventType, DeepPartial } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
-import { Form, FormControl, FormField, FormItem, FormMessage } from "./ui/form";
+import { Form, FormControl, FormField, FormItem, FormMessage, FormLabel } from "./ui/form";
import { Button } from "./ui/button";
import { Handle, Position, useNodes } from "@xyflow/react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
@@ -80,7 +80,8 @@
});
const agentSchema = z.object({
- geminiApiKey: z.string().optional(),
+ model: z.enum(["gemini", "claude"]),
+ apiKey: z.string().optional(),
});
export function NodeAppDetails({ node, disabled, showName = true, isOverview = false }: NodeDetailsProps<ServiceNode>) {
@@ -253,19 +254,34 @@
resolver: zodResolver(agentSchema),
mode: "onChange",
defaultValues: {
- geminiApiKey: data.agent?.geminiApiKey,
+ apiKey: data.model?.apiKey,
+ model: data.model?.name,
},
});
useEffect(() => {
- const sub = agentForm.watch((value) => {
- store.updateNodeData<"app">(id, {
- agent: {
- geminiApiKey: value.geminiApiKey,
- },
- });
+ const sub = agentForm.watch((value, { name }: { name?: keyof z.infer<typeof agentSchema> | undefined }) => {
+ switch (name) {
+ case "model":
+ agentForm.setValue("apiKey", "", { shouldDirty: true });
+ store.updateNodeData<"app">(id, {
+ model: {
+ name: value.model,
+ apiKey: undefined,
+ },
+ });
+ break;
+ case "apiKey":
+ store.updateNodeData<"app">(id, {
+ model: {
+ name: data.model?.name,
+ apiKey: value.apiKey,
+ },
+ });
+ break;
+ }
});
return () => sub.unsubscribe();
- }, [id, agentForm, store]);
+ }, [id, agentForm, store, data]);
return (
<>
<SourceRepo node={node} disabled={disabled} />
@@ -307,16 +323,41 @@
{node.data.type === "sketch:latest" && (
<Form {...agentForm}>
<form className="space-y-2">
- <Label>Gemini API Key</Label>
<FormField
control={agentForm.control}
- name="geminiApiKey"
+ name="model"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>AI Model</FormLabel>
+ <Select
+ onValueChange={field.onChange}
+ defaultValue={field.value}
+ disabled={disabled}
+ >
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="Select a model" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ <SelectItem value="gemini">Gemini</SelectItem>
+ <SelectItem value="claude">Claude</SelectItem>
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ <Label>API Key</Label>
+ <FormField
+ control={agentForm.control}
+ name="apiKey"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
type="password"
- placeholder="Override Gemini API key"
+ placeholder="Override AI Model API key"
{...field}
value={field.value || ""}
disabled={disabled}