Skip to content

Commit

Permalink
feat: compare with other models (#2712)
Browse files Browse the repository at this point in the history
**Changes:**

This PR enhances the LLM chat functionality and adds model comparison features:

- Adds a "Compare with other models" option in the LLM chat card menu
- Implements URL parameter handling for endpoint and model selection in the LLM playground
- Updates the ChatUIModal to use GraphQL fragments for better data fetching
- Adds translations for the new "Compare with other models" feature
- Refactors the EndpointLLMChatCard and LLMChatCard components to support the new comparison feature

**Rationale:**

These changes improve the user experience by allowing easy comparison between different language models and endpoints. The use of GraphQL fragments and URL parameters enhances data fetching efficiency and enables deep linking to specific model comparisons.

**Effects:**

- Users can now easily compare different models from the chat interface
- The LLM playground can be accessed with pre-selected endpoints and models via URL parameters
- Improved data fetching and component structure for better performance and maintainability

**Screenshots:**
- Added `Compare with other models` button.
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/2HueYSdFvL8pOB5mgrUQ/29a2d9c3-43fc-495b-8e47-f17ab9fbe222.png)

- It will be linked to the default model of the first card in the LLM Playground.
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/2HueYSdFvL8pOB5mgrUQ/3f4477d2-cc16-4bd3-96ad-b8b618bdf67d.png)

**Checklist:**

- [ ] Documentation: Update user guide to explain the new model comparison feature
- [ ] Minimum required manager version: 23.09.8 (due to new GraphQL fields)
- [ ] Test cases:
  1. Verify that the "Compare with other models" option appears in the LLM chat card menu
  2. Test deep linking to the LLM playground with specific endpoint and model parameters
  3. Ensure the ChatUIModal correctly displays endpoint information using the new GraphQL fragment
  • Loading branch information
agatha197 committed Sep 23, 2024
1 parent f07e70d commit 8570e41
Show file tree
Hide file tree
Showing 26 changed files with 123 additions and 36 deletions.
35 changes: 26 additions & 9 deletions react/src/components/lablupTalkativotUI/ChatUIModal.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { useTanQuery } from '../../hooks/reactQueryAlias';
import Flex from '../Flex';
import LLMChatCard from './LLMChatCard';
import { ChatUIModalFragment$key } from './__generated__/ChatUIModalFragment.graphql';
import { Alert, Modal, ModalProps, Skeleton, theme } from 'antd';
import graphql from 'babel-plugin-relay/macro';
import _ from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useFragment } from 'react-relay';

interface ChatUIBasicProps {
endpoint?: string;
endpointFrgmt: ChatUIModalFragment$key | null | undefined;
basePath?: string;
// models?: GetProp<typeof LLMChatCard, 'models'>;
}
interface ChatUIModalProps extends ModalProps, ChatUIBasicProps {}

const ChatUIModal: React.FC<ChatUIModalProps> = ({
endpointFrgmt = null,
basePath,
endpoint,
// models,
...props
}) => {
const { token } = theme.useToken();

return (
<Modal
{...props}
Expand All @@ -38,37 +42,50 @@ const ChatUIModal: React.FC<ChatUIModalProps> = ({
style={{ maxWidth: token.screenLGMax }}
>
<Flex direction="column" align="stretch" style={{ flex: 1 }}>
<EndpointChatContent basePath={basePath} endpoint={endpoint} />
<EndpointChatContent
basePath={basePath}
endpointFrgmt={endpointFrgmt}
/>
</Flex>
</Modal>
);
};

const EndpointChatContent: React.FC<ChatUIBasicProps> = ({
endpointFrgmt,
basePath = 'v1',
endpoint,
}) => {
const { t } = useTranslation();
const endpoint = useFragment(
graphql`
fragment ChatUIModalFragment on Endpoint {
endpoint_id
url
}
`,
endpointFrgmt,
);
const {
data: modelsResult,
// error,
isFetching,
} = useTanQuery<{
data: Array<Model>;
}>({
queryKey: ['models', endpoint],
queryKey: ['models', endpoint?.url],
queryFn: () => {
return fetch(new URL(basePath + '/models', endpoint).toString()).then(
(res) => res.json(),
);
return fetch(
new URL(basePath + '/models', endpoint?.url || '').toString(),
).then((res) => res.json());
},
});

return isFetching ? (
<Skeleton active />
) : (
<LLMChatCard
baseURL={new URL(basePath, endpoint).toString()}
endpointId={endpoint?.endpoint_id || ''}
baseURL={new URL(basePath, endpoint?.url || '').toString()}
models={_.map(modelsResult?.data, (m) => ({
id: m.id,
name: m.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const chatSubmitKeyInfoState = atom<{ id: string; key: string } | undefined>(
interface EndpointLLMChatCardProps extends CardProps {
basePath?: string;
closable?: boolean;
defaultModelId?: string;
defaultEndpoint?: EndpointLLMChatCard_endpoint$key;
fetchKey?: string;
isSynchronous?: boolean;
Expand All @@ -30,6 +31,7 @@ interface EndpointLLMChatCardProps extends CardProps {
const EndpointLLMChatCard: React.FC<EndpointLLMChatCardProps> = ({
basePath = 'v1',
closable,
defaultModelId,
defaultEndpoint,
fetchKey,
isSynchronous,
Expand Down Expand Up @@ -118,7 +120,12 @@ const EndpointLLMChatCard: React.FC<EndpointLLMChatCardProps> = ({
popupMatchSelectWidth={false}
/>
}
modelId={modelsResult?.data?.[0]?.id ?? 'custom'}
modelId={
defaultModelId &&
_.includes(_.map(modelsResult?.data, 'id'), defaultModelId)
? defaultModelId
: (modelsResult?.data?.[0]?.id ?? 'custom')
}
extra={
closable ? (
<Popconfirm
Expand Down
19 changes: 19 additions & 0 deletions react/src/components/lablupTalkativotUI/LLMChatCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { useWebUINavigate } from '../../hooks';
import Flex from '../Flex';
import ChatInput from './ChatInput';
import ModelSelect from './ModelSelect';
Expand All @@ -22,6 +23,7 @@ import {
theme,
} from 'antd';
import _ from 'lodash';
import { Scale } from 'lucide-react';
import React, { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';

Expand All @@ -39,6 +41,7 @@ export interface LLMChatCardProps extends CardProps {
agents?: Array<BAIAgent>;
modelId?: string;
agentId?: string;
endpointId?: string;
baseURL?: string;
apiKey?: string;
headers?: Record<string, string> | Headers;
Expand All @@ -57,6 +60,7 @@ export interface LLMChatCardProps extends CardProps {

const LLMChatCard: React.FC<LLMChatCardProps> = ({
models = [],
endpointId,
baseURL,
headers,
credentials,
Expand All @@ -71,6 +75,8 @@ const LLMChatCard: React.FC<LLMChatCardProps> = ({
onSubmitChange,
...cardProps
}) => {
const webuiNavigate = useWebUINavigate();

const [modelId, setModelId] = useControllableValue(cardProps, {
valuePropName: 'modelId',
trigger: 'onModelChange',
Expand Down Expand Up @@ -157,6 +163,19 @@ const LLMChatCard: React.FC<LLMChatCardProps> = ({
}, [submitKey]);

const items: MenuProps['items'] = [
{
key: 'compare',
label: t('chatui.CompareWithOtherModels'),
icon: <Scale />,
onClick: () => {
webuiNavigate(
`/serving?tab=chatting&endpointId=${endpointId}&modelId=${modelId}`,
);
},
},
{
type: 'divider',
},
{
key: 'clear',
danger: true,
Expand Down
30 changes: 26 additions & 4 deletions react/src/components/lablupTalkativotUI/LLMPlaygroundPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import _ from 'lodash';
import React, { Suspense, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyLoadQuery } from 'react-relay';
import { StringParam, useQueryParam } from 'use-query-params';

interface LLMPlaygroundPageProps {}

Expand All @@ -20,17 +21,31 @@ const LLMPlaygroundPage: React.FC<LLMPlaygroundPageProps> = ({ ...props }) => {

const [isSynchronous, setSynchronous] = useState(false);

const { endpoint_list } = useLazyLoadQuery<LLMPlaygroundPageQuery>(
const [endpointId] = useQueryParam('endpointId', StringParam);
const [modelId] = useQueryParam('modelId', StringParam);
const isEmptyEndpointId = !endpointId;

const { endpoint, endpoint_list } = useLazyLoadQuery<LLMPlaygroundPageQuery>(
graphql`
query LLMPlaygroundPageQuery {
query LLMPlaygroundPageQuery(
$endpointId: UUID!
$isEmptyEndpointId: Boolean!
) {
endpoint(endpoint_id: $endpointId)
@skipOnClient(if: $isEmptyEndpointId) {
...EndpointLLMChatCard_endpoint
}
endpoint_list(limit: 1, offset: 0) {
items {
...EndpointLLMChatCard_endpoint
}
}
}
`,
{},
{
endpointId: endpointId || '',
isEmptyEndpointId: isEmptyEndpointId,
},
);
return (
<>
Expand Down Expand Up @@ -96,7 +111,14 @@ const LLMPlaygroundPage: React.FC<LLMPlaygroundPageProps> = ({ ...props }) => {
key={getKey(index)}
>
<EndpointLLMChatCard
defaultEndpoint={endpoint_list?.items?.[0] || undefined}
defaultModelId={
index === 0 && endpoint && modelId ? modelId : undefined
}
defaultEndpoint={
index === 0 && endpoint
? endpoint
: endpoint_list?.items?.[0] || undefined
}
key={getKey(index)}
style={{ flex: 1 }}
onRequestClose={() => {
Expand Down
3 changes: 2 additions & 1 deletion react/src/pages/EndpointDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ const EndpointDetailPage: React.FC<EndpointDetailPageProps> = () => {
created_user_email @since(version: "23.09.8")
...EndpointOwnerInfoFragment
...EndpointStatusTagFragment
...ChatUIModalFragment
}
endpoint_token_list(
offset: $tokenListOffset
Expand Down Expand Up @@ -705,7 +706,7 @@ const EndpointDetailPage: React.FC<EndpointDetailPageProps> = () => {
endpoint_id={endpoint?.endpoint_id || ''}
></EndpointTokenGenerationModal>
<ChatUIModal
endpoint={endpoint?.url || undefined}
endpointFrgmt={endpoint}
open={openChatModal}
onCancel={() => {
setOpenChatModal(false);
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Chatsitzung löschen",
"DeleteChattingSessionDescription": "Sie sind dabei, dieses Thema zu löschen. \nEinmal gelöscht, kann es nicht wiederhergestellt werden. \nBitte gehen Sie vorsichtig vor.",
"SelectEndpoint": "Wählen Sie Endpunkt aus",
"SyncInput": "Eingang synchronisieren"
"SyncInput": "Eingang synchronisieren",
"CompareWithOtherModels": "Vergleichen Sie mit anderen Modellen"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Διαγραφή συνεδρίας συνομιλίας",
"DeleteChattingSessionDescription": "Πρόκειται να διαγράψετε αυτό το θέμα. \nΑφού διαγραφεί, δεν μπορεί να ανακτηθεί. \nΠαρακαλούμε προχωρήστε με προσοχή.",
"SelectEndpoint": "Επιλέξτε Τελικό σημείο",
"SyncInput": "Συγχρονισμός εισόδου"
"SyncInput": "Συγχρονισμός εισόδου",
"CompareWithOtherModels": "Συγκρίνετε με άλλα μοντέλα"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,7 @@
"DeleteChatSession": "",
"DeleteChattingSession": "Delete chatting session",
"DeleteChattingSessionDescription": "You are about to delete this topic. Once deleted, it cannot be recovered. Please proceed with caution.",
"SyncInput": "Sync input"
"SyncInput": "Sync input",
"CompareWithOtherModels": "Compare with other models"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1709,6 +1709,7 @@
"DeleteChattingSession": "Eliminar sesión de chat",
"DeleteChattingSessionDescription": "Estás a punto de eliminar este tema. \nUna vez eliminado, no se puede recuperar. \nProceda con precaución.",
"SelectEndpoint": "Seleccionar punto final",
"SyncInput": "Entrada de sincronización"
"SyncInput": "Entrada de sincronización",
"CompareWithOtherModels": "Comparar con otros modelos"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,7 @@
"DeleteChattingSessionDescription": "Olet poistamassa tätä aihetta. \nKun se on poistettu, sitä ei voi palauttaa. \nOle hyvä ja jatka varoen.",
"DeleteChatHistory": "Poista keskusteluhistoria",
"SelectModel": "Valitse Malli",
"SyncInput": "Synkronoi sisääntulo"
"SyncInput": "Synkronoi sisääntulo",
"CompareWithOtherModels": "Vertaa muihin malleihin"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Supprimer la session de discussion",
"DeleteChattingSessionDescription": "Vous êtes sur le point de supprimer ce sujet. \nUne fois supprimé, il ne peut pas être récupéré. \nVeuillez procéder avec prudence.",
"SelectEndpoint": "Sélectionnez le point de terminaison",
"SyncInput": "Entrée de synchronisation"
"SyncInput": "Entrée de synchronisation",
"CompareWithOtherModels": "Comparez avec d'autres modèles"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/id.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Hapus sesi obrolan",
"DeleteChattingSessionDescription": "Anda akan menghapus topik ini. \nSetelah dihapus, itu tidak dapat dipulihkan. \nSilakan lanjutkan dengan hati-hati.",
"SelectEndpoint": "Pilih Titik Akhir",
"SyncInput": "Sinkronkan masukan"
"SyncInput": "Sinkronkan masukan",
"CompareWithOtherModels": "Bandingkan dengan model lain"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Elimina la sessione di chat",
"DeleteChattingSessionDescription": "Stai per eliminare questo argomento. \nUna volta eliminato, non può essere recuperato. \nSi prega di procedere con cautela.",
"SelectEndpoint": "Seleziona punto finale",
"SyncInput": "Ingresso sincronizzato"
"SyncInput": "Ingresso sincronizzato",
"CompareWithOtherModels": "Confronta con altri modelli"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,7 @@
"DeleteChattingSession": "チャットセッションを削除する",
"DeleteChattingSessionDescription": "このトピックを削除しようとしています。\n一度削除すると復元することはできません。\n慎重に進めてください。",
"SelectEndpoint": "エンドポイントの選択",
"SyncInput": "同期入力"
"SyncInput": "同期入力",
"CompareWithOtherModels": "他モデルとの比較"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -1709,6 +1709,7 @@
"DeleteChattingSession": "채팅 세션 삭제",
"DeleteChattingSessionDescription": "이 주제를 삭제하려고 합니다. \n삭제한 후에는 복구할 수 없습니다. \n주의해서 진행하시기 바랍니다.",
"SelectEndpoint": "엔드포인트 선택",
"SyncInput": "입력 연동"
"SyncInput": "입력 연동",
"CompareWithOtherModels": "다른 모델과 비교하기"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/mn.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Чатлах сессийг устгах",
"DeleteChattingSessionDescription": "Та энэ сэдвийг устгах гэж байна. \nУстгасны дараа сэргээх боломжгүй. \nБолгоомжтой үргэлжлүүлнэ үү.",
"SelectEndpoint": "Төгсгөлийн цэгийг сонгоно уу",
"SyncInput": "Синкийн оролт"
"SyncInput": "Синкийн оролт",
"CompareWithOtherModels": "Бусад загваруудтай харьцуул"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/ms.json
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,7 @@
"DeleteChattingSession": "Padamkan sesi berbual",
"DeleteChattingSessionDescription": "Anda akan memadamkan topik ini. \nSetelah dipadam, ia tidak boleh dipulihkan. \nSila teruskan dengan berhati-hati.",
"SelectEndpoint": "Pilih Titik Akhir",
"SyncInput": "Input penyegerakan"
"SyncInput": "Input penyegerakan",
"CompareWithOtherModels": "Bandingkan dengan model lain"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Usuń sesję czatu",
"DeleteChattingSessionDescription": "Zamierzasz usunąć ten temat. \nRaz usuniętego nie można odzyskać. \nProszę postępować ostrożnie.",
"SelectEndpoint": "Wybierz Punkt końcowy",
"SyncInput": "Synchronizuj wejście"
"SyncInput": "Synchronizuj wejście",
"CompareWithOtherModels": "Porównaj z innymi modelami"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Excluir sessão de bate-papo",
"DeleteChattingSessionDescription": "Você está prestes a excluir este tópico. \nUma vez excluído, ele não pode ser recuperado. \nPor favor, proceda com cautela.",
"SelectEndpoint": "Selecione o ponto final",
"SyncInput": "Entrada de sincronização"
"SyncInput": "Entrada de sincronização",
"CompareWithOtherModels": "Compare com outros modelos"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Excluir sessão de bate-papo",
"DeleteChattingSessionDescription": "Você está prestes a excluir este tópico. \nUma vez excluído, ele não pode ser recuperado. \nPor favor, proceda com cautela.",
"SelectEndpoint": "Selecione o ponto final",
"SyncInput": "Entrada de sincronização"
"SyncInput": "Entrada de sincronização",
"CompareWithOtherModels": "Compare com outros modelos"
}
}
3 changes: 2 additions & 1 deletion resources/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"DeleteChattingSession": "Удалить сеанс чата",
"DeleteChattingSessionDescription": "Вы собираетесь удалить эту тему. \nПосле удаления его невозможно восстановить. \nПожалуйста, действуйте осторожно.",
"SelectEndpoint": "Выберите конечную точку",
"SyncInput": "Вход синхронизации"
"SyncInput": "Вход синхронизации",
"CompareWithOtherModels": "Сравнить с другими моделями"
}
}
Loading

0 comments on commit 8570e41

Please sign in to comment.