Skip to content

Commit

Permalink
feat: display and select assistant targets in Threads
Browse files Browse the repository at this point in the history
  • Loading branch information
mikbry committed Mar 6, 2024
1 parent d419ec3 commit 5f8f69c
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 49 deletions.
73 changes: 36 additions & 37 deletions webapp/components/views/Threads/Menu/AssistantMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

'use client';

import { useContext, useState } from 'react';
import { useContext, useMemo, useState } from 'react';
import { Archive, Check, MoreHorizontal, Plug, Trash } from 'lucide-react';
// import { useRouter } from 'next/router';
import { Button } from '@/components/ui/button';
Expand All @@ -39,21 +39,30 @@ import {
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Provider, ProviderType, Ui } from '@/types';
import {
AIService,
AIServiceType,
Assistant,
Conversation,
Provider,
ProviderType,
Ui,
} from '@/types';
import useBackend from '@/hooks/useBackendContext';
import useTranslation from '@/hooks/useTranslation';
import { ModalsContext } from '@/context/modals';
import { ModalIds } from '@/modals';
import { AppContext } from '@/context';
import { createProvider, getProviderState } from '@/utils/data/providers';
import OpenAI from '@/utils/providers/openai';
import useShortcuts, { ShortcutIds } from '@/hooks/useShortcuts';
import logger from '@/utils/logger';
import { ShortcutIds } from '@/hooks/useShortcuts';
import { MenuAction } from '@/types/ui';
import { getStateColor } from '@/utils/ui';
import { cn } from '@/lib/utils';
import { useAssistantStore } from '@/stores';
import AssistantIcon from '@/components/common/AssistantIcon';
import { getAssistantTargetsAsItems, getDefaultAssistantService } from '@/utils/data/assistants';
import { updateConversation } from '@/utils/data/conversations';
import { Badge } from '../../../ui/badge';
import { ShortcutBadge } from '../../../common/ShortCut';
import Pastille from '../../../common/Pastille';
Expand All @@ -63,21 +72,23 @@ type AssistantMenuProps = {
selectedAssistantId: string;
selectedTargetId?: string;
selectedConversationId?: string;
onSelectTarget: (model: string, provider: ProviderType) => void;
onSelectMenu: (menu: MenuAction, data: string) => void;
};

export default function AssistantMenu({
selectedAssistantId,
selectedTargetId,
selectedTargetId: _selectedTargetId,
selectedConversationId,
onSelectTarget,
onSelectMenu,
}: AssistantMenuProps) {
const { getAssistant } = useAssistantStore();
const { providers } = useContext(AppContext);
const { conversations, updateConversations, providers } = useContext(AppContext);
const { backendContext } = useBackend();
const assistant = getAssistant(selectedAssistantId);
const conversation = conversations.find((c) => c.id === selectedConversationId) as Conversation;
const assistant = getAssistant(selectedAssistantId) as Assistant;
const service = conversation?.services?.[0] || getDefaultAssistantService(assistant);
const selectedTargetId =
service.type === AIServiceType.Assistant ? service.targetId : _selectedTargetId;
const target = assistant?.targets?.find(
(t) => t.id === selectedTargetId || assistant?.targets?.[0].id,
);
Expand All @@ -86,8 +97,10 @@ export default function AssistantMenu({
const { t } = useTranslation();
const { showModal } = useContext(ModalsContext);
const selectedModelName = target?.models?.[0];
const targetItems: Ui.MenuItem[] = []; // TODO get targets fro assistant
/* const selectedItem = modelItems.find((item) => item.value === selectedModelName); */
const targetItems: Ui.MenuItem[] = useMemo(
() => (assistant ? getAssistantTargetsAsItems(assistant, selectedTargetId) : []),
[assistant, selectedTargetId],
);
const selectedModel = backendContext.config.models.items.find(
(model) => model.name === selectedModelName,
);
Expand Down Expand Up @@ -115,30 +128,16 @@ export default function AssistantMenu({
showModal(ModalIds.OpenAI, { item: chatGPT });
};

const handleNewLocalModel = () => {
showModal(ModalIds.NewLocalModel);
const handleSelectAssistantTarget = async (item: Ui.MenuItem) => {
const targetId = item.value as string;
const newConversation: Conversation = {
...conversation,
services: [{ ...service, targetId } as AIService],
};
const newConversations = updateConversation(newConversation, conversations);
updateConversations(newConversations);
};

const handleNewProviderModel = () => {
showModal(ModalIds.NewProvider);
};

useShortcuts(ShortcutIds.INSTALL_MODEL, (event) => {
event.preventDefault();
logger.info('shortcut install Model');
handleNewLocalModel();
});
useShortcuts(ShortcutIds.NEW_PROVIDER, (event) => {
event.preventDefault();
logger.info('shortcut new provider');
handleNewProviderModel();
});
useShortcuts(ShortcutIds.CONFIG_GPT, (event) => {
event.preventDefault();
logger.info('shortcut configure ChatGPT');
handleSetupChatGPT();
});

return (
<div className="flex w-full flex-col items-start justify-between rounded-md border px-4 py-0 sm:flex-row sm:items-center">
<div className="flex grow items-center justify-between text-sm font-medium leading-none">
Expand Down Expand Up @@ -170,14 +169,14 @@ export default function AssistantMenu({
<DropdownMenuContent align="end" className="w-full">
<DropdownMenuLabel>{t('Target')}</DropdownMenuLabel>
<DropdownMenuGroup>
{targetItems.length < 2 && (
<DropdownMenuItem onSelect={handleNewProviderModel}>
{(!selectedConversationId || targetItems.length < 2) && (
<DropdownMenuItem>
<Check className="mr-2 h-4 w-4" strokeWidth={1.5} />
<span className="capitalize">{target?.name || t('Select a target')}</span>
{selectedModel && <ModelInfos model={selectedModel} stateAsIcon />}
</DropdownMenuItem>
)}
{targetItems.length > 0 && (
{selectedConversationId && targetItems.length > 1 && (
<>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
Expand All @@ -195,7 +194,7 @@ export default function AssistantMenu({
key={item.label}
value={item.value}
onSelect={() => {
onSelectTarget(item.value as string, item.group as ProviderType);
handleSelectAssistantTarget(item);
setOpen(false);
}}
className="flex w-full items-center justify-between"
Expand Down
4 changes: 1 addition & 3 deletions webapp/components/views/Threads/Menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ export default function ThreadMenu({
return selectedAssistantId ? (
<AssistantMenu
selectedAssistantId={selectedAssistantId}
onSelectTarget={() => {
throw new Error('Function not implemented.');
}}
selectedConversationId={selectedConversationId}
onSelectMenu={() => {
throw new Error('Function not implemented.');
}}
Expand Down
2 changes: 1 addition & 1 deletion webapp/types/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type MenuItem = {
label: string;
value?: string;
group?: string;
icon?: unknown;
icon?: LucideIcon | React.Component;
selected?: boolean;
onSelect?: (data: string) => void;
state?: MenuItemState;
Expand Down
46 changes: 46 additions & 0 deletions webapp/utils/data/assistants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2024 mik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { AIService, AIServiceType, Assistant, Ui } from '@/types';

export const getAssistantsAsItems = (assistants: Assistant[], selected?: string): Ui.MenuItem[] =>
assistants.map(
(assistant) =>
({
label: assistant.name,
value: assistant.id,
icon: assistant.icon,
selected: assistant.id === selected,
}) as Ui.MenuItem,
);

export const getAssistantTargetsAsItems = (
assistant: Assistant,
selected?: string,
): Ui.MenuItem[] =>
assistant.targets?.map(
(target) =>
({
label: target.name,
value: target.id,
selected: target.id === selected,
group: target.provider,
}) as Ui.MenuItem,
) ?? [];

export const getDefaultAssistantService = (assistant: Assistant): AIService => ({
type: AIServiceType.Assistant,
assistantId: assistant.id,
targetId: assistant.targets?.[0].id,
});
19 changes: 11 additions & 8 deletions webapp/utils/data/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ export const getLocalModelsAsItems = (
localProvider?: Provider,
): Ui.MenuItem[] => {
const state = getProviderState(localProvider);
return backendContext.config.models.items.map((model) => ({
label: model.title || model.name,
value: model.name,
group: localProvider?.name || OplaProvider.name,
icon: Opla,
selected: model.name === selectedModelname,
state,
}));
return backendContext.config.models.items.map(
(model) =>
({
label: model.title || model.name,
value: model.name,
group: localProvider?.name || OplaProvider.name,
icon: Opla,
selected: model.name === selectedModelname,
state,
}) as Ui.MenuItem,
);
};

export const getProviderModelsAsItems = (
Expand Down

0 comments on commit 5f8f69c

Please sign in to comment.