mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-02 12:53:26 +03:00
feat(admin): add prompt management page (#7611)
close AF-907 Supports online modification of prompt, but does not support custom ai key yet ![CleanShot 2024-07-29 at 22 12 39@2x](https://github.com/user-attachments/assets/c67ad0d0-3e5b-44ff-b7db-d07dd11c19e2)
This commit is contained in:
parent
bf6e36de37
commit
b214003968
@ -4,6 +4,7 @@ import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
Args,
|
||||
Field,
|
||||
Float,
|
||||
ID,
|
||||
InputType,
|
||||
Mutation,
|
||||
@ -205,16 +206,16 @@ class CopilotPromptConfigType {
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
jsonMode!: boolean | null;
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
@Field(() => Float, { nullable: true })
|
||||
frequencyPenalty!: number | null;
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
@Field(() => Float, { nullable: true })
|
||||
presencePenalty!: number | null;
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
@Field(() => Float, { nullable: true })
|
||||
temperature!: number | null;
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
@Field(() => Float, { nullable: true })
|
||||
topP!: number | null;
|
||||
}
|
||||
|
||||
@ -238,8 +239,8 @@ class CopilotPromptType {
|
||||
@Field(() => String)
|
||||
name!: string;
|
||||
|
||||
@Field(() => AvailableModels)
|
||||
model!: AvailableModels;
|
||||
@Field(() => String)
|
||||
model!: string;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
action!: string | null;
|
||||
|
@ -61,19 +61,19 @@ enum CopilotModels {
|
||||
}
|
||||
|
||||
input CopilotPromptConfigInput {
|
||||
frequencyPenalty: Int
|
||||
frequencyPenalty: Float
|
||||
jsonMode: Boolean
|
||||
presencePenalty: Int
|
||||
temperature: Int
|
||||
topP: Int
|
||||
presencePenalty: Float
|
||||
temperature: Float
|
||||
topP: Float
|
||||
}
|
||||
|
||||
type CopilotPromptConfigType {
|
||||
frequencyPenalty: Int
|
||||
frequencyPenalty: Float
|
||||
jsonMode: Boolean
|
||||
presencePenalty: Int
|
||||
temperature: Int
|
||||
topP: Int
|
||||
presencePenalty: Float
|
||||
temperature: Float
|
||||
topP: Float
|
||||
}
|
||||
|
||||
input CopilotPromptMessageInput {
|
||||
@ -102,7 +102,7 @@ type CopilotPromptType {
|
||||
action: String
|
||||
config: CopilotPromptConfigType
|
||||
messages: [CopilotPromptMessageType!]!
|
||||
model: CopilotModels!
|
||||
model: String!
|
||||
name: String!
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,10 @@ export const router = _createBrowserRouter(
|
||||
path: '/admin/auth',
|
||||
lazy: () => import('./modules/auth'),
|
||||
},
|
||||
{
|
||||
path: '/admin/ai',
|
||||
lazy: () => import('./modules/ai'),
|
||||
},
|
||||
{
|
||||
path: '/admin/setup',
|
||||
lazy: () => import('./modules/setup'),
|
||||
|
44
packages/frontend/admin/src/modules/ai/discard-changes.tsx
Normal file
44
packages/frontend/admin/src/modules/ai/discard-changes.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { Button } from '@affine/admin/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@affine/admin/components/ui/dialog';
|
||||
|
||||
export const DiscardChanges = ({
|
||||
open,
|
||||
onClose,
|
||||
onConfirm,
|
||||
onOpenChange,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}) => {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:w-[460px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="leading-7">Discard Changes</DialogTitle>
|
||||
<DialogDescription className="leading-6">
|
||||
Changes to this prompt will not be saved.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<div className="flex justify-end items-center w-full space-x-4">
|
||||
<Button type="button" onClick={onClose} variant="outline">
|
||||
<span>Cancel</span>
|
||||
</Button>
|
||||
<Button type="button" onClick={onConfirm} variant="destructive">
|
||||
<span>Discard</span>
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
146
packages/frontend/admin/src/modules/ai/edit-prompt.tsx
Normal file
146
packages/frontend/admin/src/modules/ai/edit-prompt.tsx
Normal file
@ -0,0 +1,146 @@
|
||||
import { Button } from '@affine/admin/components/ui/button';
|
||||
import { ScrollArea } from '@affine/admin/components/ui/scroll-area';
|
||||
import { Separator } from '@affine/admin/components/ui/separator';
|
||||
import { Textarea } from '@affine/admin/components/ui/textarea';
|
||||
import { CheckIcon, XIcon } from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { useRightPanel } from '../layout';
|
||||
import type { Prompt } from './prompts';
|
||||
import { usePrompt } from './use-prompt';
|
||||
|
||||
export function EditPrompt({ item }: { item: Prompt }) {
|
||||
const { closePanel } = useRightPanel();
|
||||
|
||||
const [messages, setMessages] = useState(item.messages);
|
||||
const { updatePrompt } = usePrompt();
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLTextAreaElement>, index: number) => {
|
||||
const newMessages = [...messages];
|
||||
newMessages[index] = {
|
||||
...newMessages[index],
|
||||
content: e.target.value,
|
||||
};
|
||||
setMessages(newMessages);
|
||||
},
|
||||
[messages]
|
||||
);
|
||||
const handleClose = useCallback(() => {
|
||||
setMessages(item.messages);
|
||||
closePanel();
|
||||
}, [closePanel, item.messages]);
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
updatePrompt({ name: item.name, messages });
|
||||
handleClose();
|
||||
}, [handleClose, item.name, messages, updatePrompt]);
|
||||
|
||||
const disableSave = useMemo(
|
||||
() => JSON.stringify(messages) === JSON.stringify(item.messages),
|
||||
[item.messages, messages]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setMessages(item.messages);
|
||||
}, [item.messages]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-1">
|
||||
<div className="flex-grow-0 flex-shrink-0 h-[56px] flex justify-between items-center py-[10px] px-6 ">
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
className="w-7 h-7"
|
||||
variant="ghost"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<XIcon size={20} />
|
||||
</Button>
|
||||
<span className="text-base font-medium">Edit Prompt</span>
|
||||
<Button
|
||||
type="submit"
|
||||
size="icon"
|
||||
className="w-7 h-7"
|
||||
variant="ghost"
|
||||
onClick={onConfirm}
|
||||
disabled={disableSave}
|
||||
>
|
||||
<CheckIcon size={20} />
|
||||
</Button>
|
||||
</div>
|
||||
<Separator />
|
||||
<ScrollArea>
|
||||
<div className="px-5 py-4 overflow-y-auto space-y-[10px] flex flex-col gap-5">
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Name</div>
|
||||
<div className="text-sm font-normal text-zinc-500">{item.name}</div>
|
||||
</div>
|
||||
{item.action ? (
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Action</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{item.action}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Model</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{item.model}
|
||||
</div>
|
||||
</div>
|
||||
{item.config ? (
|
||||
<div className="flex flex-col border rounded p-3">
|
||||
<div className="text-sm font-medium">Config</div>
|
||||
{Object.entries(item.config).map(([key, value], index) => (
|
||||
<div key={key} className="flex flex-col">
|
||||
{index !== 0 && <Separator />}
|
||||
<span className="text-sm font-normal">{key}</span>
|
||||
<span className="text-sm font-normal text-zinc-500">
|
||||
{value?.toString()}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="px-5 py-4 overflow-y-auto space-y-[10px] flex flex-col">
|
||||
<div className="text-sm font-medium">Messages</div>
|
||||
{messages.map((message, index) => (
|
||||
<div key={index} className="flex flex-col gap-3">
|
||||
{index !== 0 && <Separator />}
|
||||
<div>
|
||||
<div className="text-sm font-normal">Role</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{message.role}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.params ? (
|
||||
<div>
|
||||
<div className="text-sm font-medium">Params</div>
|
||||
{Object.entries(message.params).map(([key, value], index) => (
|
||||
<div key={key} className="flex flex-col">
|
||||
{index !== 0 && <Separator />}
|
||||
<span className="text-sm font-normal">{key}</span>
|
||||
<span className="text-sm font-normal text-zinc-500">
|
||||
{value.toString()}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="text-sm font-normal">Content</div>
|
||||
<Textarea
|
||||
className=" min-h-48"
|
||||
value={message.content}
|
||||
onChange={e => handleChange(e, index)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
41
packages/frontend/admin/src/modules/ai/index.tsx
Normal file
41
packages/frontend/admin/src/modules/ai/index.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { Separator } from '@affine/admin/components/ui/separator';
|
||||
import { cn } from '@affine/admin/utils';
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
||||
|
||||
import { Prompts } from './prompts';
|
||||
|
||||
export function Ai() {
|
||||
return null;
|
||||
|
||||
// hide ai config in admin until it's ready
|
||||
// return <Layout content={<AiPage />} />;
|
||||
}
|
||||
|
||||
export function AiPage() {
|
||||
return (
|
||||
<div className=" h-screen flex-1 flex-col flex">
|
||||
<div className="flex items-center justify-between px-6 py-3 max-md:ml-9 max-md:mt-[2px]">
|
||||
<div className="text-base font-medium">AI</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<ScrollAreaPrimitive.Root
|
||||
className={cn('relative overflow-hidden w-full')}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit] [&>div]:!block">
|
||||
<Prompts />
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
className={cn(
|
||||
'flex touch-none select-none transition-colors',
|
||||
|
||||
'h-full w-2.5 border-l border-l-transparent p-[1px]'
|
||||
)}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export { Ai as Component };
|
69
packages/frontend/admin/src/modules/ai/keys.tsx
Normal file
69
packages/frontend/admin/src/modules/ai/keys.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import { Button } from '@affine/admin/components/ui/button';
|
||||
import { Input } from '@affine/admin/components/ui/input';
|
||||
import { Label } from '@affine/admin/components/ui/label';
|
||||
import { Separator } from '@affine/admin/components/ui/separator';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function Keys() {
|
||||
const [openAIKey, setOpenAIKey] = useState('');
|
||||
const [falAIKey, setFalAIKey] = useState('');
|
||||
const [unsplashKey, setUnsplashKey] = useState('');
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-3 py-5 px-6 w-full">
|
||||
<div className="flex items-center">
|
||||
<span className="text-xl font-semibold">Keys</span>
|
||||
</div>
|
||||
<div className="flex-grow overflow-y-auto space-y-[10px]">
|
||||
<div className="flex flex-col rounded-md border py-4 gap-4">
|
||||
<div className="px-5 space-y-3">
|
||||
<Label className="text-sm font-medium">OpenAI Key</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
className="py-2 px-3 text-base font-normal placeholder:opacity-50"
|
||||
value={openAIKey}
|
||||
placeholder="sk-xxxxxxxxxxxxx-xxxxxxxxxxxxxx"
|
||||
onChange={e => setOpenAIKey(e.target.value)}
|
||||
/>
|
||||
<Button disabled>Save</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="px-5 space-y-3">
|
||||
<Label className="text-sm font-medium">Fal.AI Key</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
type="email"
|
||||
className="py-2 px-3 ext-base font-normal placeholder:opacity-50"
|
||||
value={falAIKey}
|
||||
placeholder="00000000-0000-0000-00000000:xxxxxxxxxxxxxxxxx"
|
||||
onChange={e => setFalAIKey(e.target.value)}
|
||||
/>
|
||||
<Button disabled>Save</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="px-5 space-y-3">
|
||||
<Label className="text-sm font-medium">Unsplash Key</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
type="password"
|
||||
className="py-2 px-3 ext-base font-normal placeholder:opacity-50"
|
||||
value={unsplashKey}
|
||||
placeholder="00000000-0000-0000-00000000:xxxxxxxxxxxxxxxxx"
|
||||
onChange={e => setUnsplashKey(e.target.value)}
|
||||
/>
|
||||
<Button disabled>Save</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="px-5 space-y-3 text-sm font-normal text-gray-500">
|
||||
Custom API keys may not perform as expected. AFFiNE does not
|
||||
guarantee results when using custom API keys.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
113
packages/frontend/admin/src/modules/ai/prompts.tsx
Normal file
113
packages/frontend/admin/src/modules/ai/prompts.tsx
Normal file
@ -0,0 +1,113 @@
|
||||
import { Button } from '@affine/admin/components/ui/button';
|
||||
import { Separator } from '@affine/admin/components/ui/separator';
|
||||
import type { CopilotPromptMessageRole } from '@affine/graphql';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { useRightPanel } from '../layout';
|
||||
import { DiscardChanges } from './discard-changes';
|
||||
import { EditPrompt } from './edit-prompt';
|
||||
import { usePrompt } from './use-prompt';
|
||||
|
||||
export type Prompt = {
|
||||
__typename?: 'CopilotPromptType';
|
||||
name: string;
|
||||
model: string;
|
||||
action: string | null;
|
||||
config: {
|
||||
__typename?: 'CopilotPromptConfigType';
|
||||
jsonMode: boolean | null;
|
||||
frequencyPenalty: number | null;
|
||||
presencePenalty: number | null;
|
||||
temperature: number | null;
|
||||
topP: number | null;
|
||||
} | null;
|
||||
messages: Array<{
|
||||
__typename?: 'CopilotPromptMessageType';
|
||||
role: CopilotPromptMessageRole;
|
||||
content: string;
|
||||
params: Record<string, string> | null;
|
||||
}>;
|
||||
};
|
||||
|
||||
export function Prompts() {
|
||||
const { prompts: list } = usePrompt();
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-3 py-5 px-6 w-full">
|
||||
<div className="flex items-center">
|
||||
<span className="text-xl font-semibold">Prompts</span>
|
||||
</div>
|
||||
<div className="flex-grow overflow-y-auto space-y-[10px]">
|
||||
<div className="flex flex-col rounded-md border w-full">
|
||||
{list.map((item, index) => (
|
||||
<PromptRow
|
||||
key={item.name.concat(index.toString())}
|
||||
item={item}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const PromptRow = ({ item, index }: { item: Prompt; index: number }) => {
|
||||
const { setRightPanelContent, openPanel, isOpen } = useRightPanel();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
|
||||
const handleDiscardChangesCancel = useCallback(() => {
|
||||
setDialogOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleConfirm = useCallback(
|
||||
(item: Prompt) => {
|
||||
setRightPanelContent(<EditPrompt item={item} />);
|
||||
if (dialogOpen) {
|
||||
handleDiscardChangesCancel();
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
openPanel();
|
||||
}
|
||||
},
|
||||
[
|
||||
dialogOpen,
|
||||
handleDiscardChangesCancel,
|
||||
isOpen,
|
||||
openPanel,
|
||||
setRightPanelContent,
|
||||
]
|
||||
);
|
||||
|
||||
const handleEdit = useCallback(
|
||||
(item: Prompt) => {
|
||||
if (isOpen) {
|
||||
setDialogOpen(true);
|
||||
} else {
|
||||
handleConfirm(item);
|
||||
}
|
||||
},
|
||||
[handleConfirm, isOpen]
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
{index !== 0 && <Separator />}
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex flex-col gap-1 w-full items-start px-6 py-[14px] h-full "
|
||||
onClick={() => handleEdit(item)}
|
||||
>
|
||||
<div>{item.name}</div>
|
||||
<div className="text-left w-full opacity-50 overflow-hidden text-ellipsis whitespace-nowrap break-words text-nowrap">
|
||||
{item.messages.flatMap(message => message.content).join(' ')}
|
||||
</div>
|
||||
</Button>
|
||||
<DiscardChanges
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
onClose={handleDiscardChangesCancel}
|
||||
onConfirm={() => handleConfirm(item)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
51
packages/frontend/admin/src/modules/ai/use-prompt.ts
Normal file
51
packages/frontend/admin/src/modules/ai/use-prompt.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import {
|
||||
useMutateQueryResource,
|
||||
useMutation,
|
||||
} from '@affine/core/hooks/use-mutation';
|
||||
import { useQuery } from '@affine/core/hooks/use-query';
|
||||
import { getPromptsQuery, updatePromptMutation } from '@affine/graphql';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import type { Prompt } from './prompts';
|
||||
|
||||
export const usePrompt = () => {
|
||||
const { data } = useQuery({
|
||||
query: getPromptsQuery,
|
||||
});
|
||||
|
||||
const { trigger } = useMutation({
|
||||
mutation: updatePromptMutation,
|
||||
});
|
||||
|
||||
const revalidate = useMutateQueryResource();
|
||||
|
||||
const updatePrompt = useAsyncCallback(
|
||||
async ({
|
||||
name,
|
||||
messages,
|
||||
}: {
|
||||
name: string;
|
||||
messages: Prompt['messages'];
|
||||
}) => {
|
||||
await trigger({
|
||||
name,
|
||||
messages,
|
||||
})
|
||||
.then(async () => {
|
||||
await revalidate(getPromptsQuery);
|
||||
toast.success('Prompt updated successfully');
|
||||
})
|
||||
.catch(e => {
|
||||
toast(e.message);
|
||||
console.error(e);
|
||||
});
|
||||
},
|
||||
[revalidate, trigger]
|
||||
);
|
||||
|
||||
return {
|
||||
prompts: data.listCopilotPrompts,
|
||||
updatePrompt,
|
||||
};
|
||||
};
|
@ -81,7 +81,7 @@ export function Layout({ content }: LayoutProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const rightPanelRef = useRef<ImperativePanelHandle>(null);
|
||||
|
||||
const [activeTab, setActiveTab] = useState('Accounts');
|
||||
const [activeTab, setActiveTab] = useState('');
|
||||
const [activeSubTab, setActiveSubTab] = useState('auth');
|
||||
const [currentModule, setCurrentModule] = useState('auth');
|
||||
|
||||
|
@ -7,12 +7,7 @@ import {
|
||||
import { buttonVariants } from '@affine/admin/components/ui/button';
|
||||
import { cn } from '@affine/admin/utils';
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
||||
import {
|
||||
ClipboardListIcon,
|
||||
CpuIcon,
|
||||
SettingsIcon,
|
||||
UsersIcon,
|
||||
} from 'lucide-react';
|
||||
import { ClipboardListIcon, SettingsIcon, UsersIcon } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
@ -28,8 +23,6 @@ const TabsMap: { [key: string]: string } = {
|
||||
settings: 'Settings',
|
||||
};
|
||||
|
||||
const defaultTab = 'Accounts';
|
||||
|
||||
export function Nav() {
|
||||
const { moduleList } = useGetServerRuntimeConfig();
|
||||
const { activeTab, setActiveTab, setCurrentModule } = useNav();
|
||||
@ -42,7 +35,6 @@ export function Nav() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
setActiveTab(defaultTab);
|
||||
}, [setActiveTab]);
|
||||
|
||||
return (
|
||||
@ -64,7 +56,8 @@ export function Nav() {
|
||||
<UsersIcon className="mr-2 h-4 w-4" />
|
||||
Accounts
|
||||
</Link>
|
||||
<Link
|
||||
{/* hide ai config in admin until it's ready */}
|
||||
{/* <Link
|
||||
to={'/admin/ai'}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
@ -79,7 +72,7 @@ export function Nav() {
|
||||
>
|
||||
<CpuIcon className="mr-2 h-4 w-4" />
|
||||
AI
|
||||
</Link>
|
||||
</Link> */}
|
||||
<Link
|
||||
to={'/admin/config'}
|
||||
className={cn(
|
||||
|
19
packages/frontend/graphql/src/graphql/get-prompts.gql
Normal file
19
packages/frontend/graphql/src/graphql/get-prompts.gql
Normal file
@ -0,0 +1,19 @@
|
||||
query getPrompts {
|
||||
listCopilotPrompts {
|
||||
name
|
||||
model
|
||||
action
|
||||
config {
|
||||
jsonMode
|
||||
frequencyPenalty
|
||||
presencePenalty
|
||||
temperature
|
||||
topP
|
||||
}
|
||||
messages {
|
||||
role
|
||||
content
|
||||
params
|
||||
}
|
||||
}
|
||||
}
|
@ -496,6 +496,33 @@ query oauthProviders {
|
||||
}`,
|
||||
};
|
||||
|
||||
export const getPromptsQuery = {
|
||||
id: 'getPromptsQuery' as const,
|
||||
operationName: 'getPrompts',
|
||||
definitionName: 'listCopilotPrompts',
|
||||
containsFile: false,
|
||||
query: `
|
||||
query getPrompts {
|
||||
listCopilotPrompts {
|
||||
name
|
||||
model
|
||||
action
|
||||
config {
|
||||
jsonMode
|
||||
frequencyPenalty
|
||||
presencePenalty
|
||||
temperature
|
||||
topP
|
||||
}
|
||||
messages {
|
||||
role
|
||||
content
|
||||
params
|
||||
}
|
||||
}
|
||||
}`,
|
||||
};
|
||||
|
||||
export const getServerRuntimeConfigQuery = {
|
||||
id: 'getServerRuntimeConfigQuery' as const,
|
||||
operationName: 'getServerRuntimeConfig',
|
||||
@ -1057,6 +1084,33 @@ mutation updateAccount($id: String!, $input: ManageUserInput!) {
|
||||
}`,
|
||||
};
|
||||
|
||||
export const updatePromptMutation = {
|
||||
id: 'updatePromptMutation' as const,
|
||||
operationName: 'updatePrompt',
|
||||
definitionName: 'updateCopilotPrompt',
|
||||
containsFile: false,
|
||||
query: `
|
||||
mutation updatePrompt($name: String!, $messages: [CopilotPromptMessageInput!]!) {
|
||||
updateCopilotPrompt(name: $name, messages: $messages) {
|
||||
name
|
||||
model
|
||||
action
|
||||
config {
|
||||
jsonMode
|
||||
frequencyPenalty
|
||||
presencePenalty
|
||||
temperature
|
||||
topP
|
||||
}
|
||||
messages {
|
||||
role
|
||||
content
|
||||
params
|
||||
}
|
||||
}
|
||||
}`,
|
||||
};
|
||||
|
||||
export const updateServerRuntimeConfigsMutation = {
|
||||
id: 'updateServerRuntimeConfigsMutation' as const,
|
||||
operationName: 'updateServerRuntimeConfigs',
|
||||
|
22
packages/frontend/graphql/src/graphql/update-prompt.gql
Normal file
22
packages/frontend/graphql/src/graphql/update-prompt.gql
Normal file
@ -0,0 +1,22 @@
|
||||
mutation updatePrompt(
|
||||
$name: String!
|
||||
$messages: [CopilotPromptMessageInput!]!
|
||||
) {
|
||||
updateCopilotPrompt(name: $name, messages: $messages) {
|
||||
name
|
||||
model
|
||||
action
|
||||
config {
|
||||
jsonMode
|
||||
frequencyPenalty
|
||||
presencePenalty
|
||||
temperature
|
||||
topP
|
||||
}
|
||||
messages {
|
||||
role
|
||||
content
|
||||
params
|
||||
}
|
||||
}
|
||||
}
|
@ -104,20 +104,20 @@ export enum CopilotModels {
|
||||
}
|
||||
|
||||
export interface CopilotPromptConfigInput {
|
||||
frequencyPenalty: InputMaybe<Scalars['Int']['input']>;
|
||||
frequencyPenalty: InputMaybe<Scalars['Float']['input']>;
|
||||
jsonMode: InputMaybe<Scalars['Boolean']['input']>;
|
||||
presencePenalty: InputMaybe<Scalars['Int']['input']>;
|
||||
temperature: InputMaybe<Scalars['Int']['input']>;
|
||||
topP: InputMaybe<Scalars['Int']['input']>;
|
||||
presencePenalty: InputMaybe<Scalars['Float']['input']>;
|
||||
temperature: InputMaybe<Scalars['Float']['input']>;
|
||||
topP: InputMaybe<Scalars['Float']['input']>;
|
||||
}
|
||||
|
||||
export interface CopilotPromptConfigType {
|
||||
__typename?: 'CopilotPromptConfigType';
|
||||
frequencyPenalty: Maybe<Scalars['Int']['output']>;
|
||||
frequencyPenalty: Maybe<Scalars['Float']['output']>;
|
||||
jsonMode: Maybe<Scalars['Boolean']['output']>;
|
||||
presencePenalty: Maybe<Scalars['Int']['output']>;
|
||||
temperature: Maybe<Scalars['Int']['output']>;
|
||||
topP: Maybe<Scalars['Int']['output']>;
|
||||
presencePenalty: Maybe<Scalars['Float']['output']>;
|
||||
temperature: Maybe<Scalars['Float']['output']>;
|
||||
topP: Maybe<Scalars['Float']['output']>;
|
||||
}
|
||||
|
||||
export interface CopilotPromptMessageInput {
|
||||
@ -149,7 +149,7 @@ export interface CopilotPromptType {
|
||||
action: Maybe<Scalars['String']['output']>;
|
||||
config: Maybe<CopilotPromptConfigType>;
|
||||
messages: Array<CopilotPromptMessageType>;
|
||||
model: CopilotModels;
|
||||
model: Scalars['String']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
}
|
||||
|
||||
@ -1684,6 +1684,32 @@ export type OauthProvidersQuery = {
|
||||
};
|
||||
};
|
||||
|
||||
export type GetPromptsQueryVariables = Exact<{ [key: string]: never }>;
|
||||
|
||||
export type GetPromptsQuery = {
|
||||
__typename?: 'Query';
|
||||
listCopilotPrompts: Array<{
|
||||
__typename?: 'CopilotPromptType';
|
||||
name: string;
|
||||
model: string;
|
||||
action: string | null;
|
||||
config: {
|
||||
__typename?: 'CopilotPromptConfigType';
|
||||
jsonMode: boolean | null;
|
||||
frequencyPenalty: number | null;
|
||||
presencePenalty: number | null;
|
||||
temperature: number | null;
|
||||
topP: number | null;
|
||||
} | null;
|
||||
messages: Array<{
|
||||
__typename?: 'CopilotPromptMessageType';
|
||||
role: CopilotPromptMessageRole;
|
||||
content: string;
|
||||
params: Record<string, string> | null;
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type GetServerRuntimeConfigQueryVariables = Exact<{
|
||||
[key: string]: never;
|
||||
}>;
|
||||
@ -2186,6 +2212,35 @@ export type UpdateAccountMutation = {
|
||||
};
|
||||
};
|
||||
|
||||
export type UpdatePromptMutationVariables = Exact<{
|
||||
name: Scalars['String']['input'];
|
||||
messages: Array<CopilotPromptMessageInput> | CopilotPromptMessageInput;
|
||||
}>;
|
||||
|
||||
export type UpdatePromptMutation = {
|
||||
__typename?: 'Mutation';
|
||||
updateCopilotPrompt: {
|
||||
__typename?: 'CopilotPromptType';
|
||||
name: string;
|
||||
model: string;
|
||||
action: string | null;
|
||||
config: {
|
||||
__typename?: 'CopilotPromptConfigType';
|
||||
jsonMode: boolean | null;
|
||||
frequencyPenalty: number | null;
|
||||
presencePenalty: number | null;
|
||||
temperature: number | null;
|
||||
topP: number | null;
|
||||
} | null;
|
||||
messages: Array<{
|
||||
__typename?: 'CopilotPromptMessageType';
|
||||
role: CopilotPromptMessageRole;
|
||||
content: string;
|
||||
params: Record<string, string> | null;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
|
||||
export type UpdateServerRuntimeConfigsMutationVariables = Exact<{
|
||||
updates: Scalars['JSONObject']['input'];
|
||||
}>;
|
||||
@ -2432,6 +2487,11 @@ export type Queries =
|
||||
variables: OauthProvidersQueryVariables;
|
||||
response: OauthProvidersQuery;
|
||||
}
|
||||
| {
|
||||
name: 'getPromptsQuery';
|
||||
variables: GetPromptsQueryVariables;
|
||||
response: GetPromptsQuery;
|
||||
}
|
||||
| {
|
||||
name: 'getServerRuntimeConfigQuery';
|
||||
variables: GetServerRuntimeConfigQueryVariables;
|
||||
@ -2729,6 +2789,11 @@ export type Mutations =
|
||||
variables: UpdateAccountMutationVariables;
|
||||
response: UpdateAccountMutation;
|
||||
}
|
||||
| {
|
||||
name: 'updatePromptMutation';
|
||||
variables: UpdatePromptMutationVariables;
|
||||
response: UpdatePromptMutation;
|
||||
}
|
||||
| {
|
||||
name: 'updateServerRuntimeConfigsMutation';
|
||||
variables: UpdateServerRuntimeConfigsMutationVariables;
|
||||
|
Loading…
Reference in New Issue
Block a user