Skip to content

62/ai more suggestions 2 #360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
gap: 1em;
}

.literatureSuggestions.collapsed {
.literatureSuggestions[data-collapsed='true'] {
padding: 0;
padding-top: 20px;
pading-top: 20px;
width: 48px;
}

Expand All @@ -45,7 +45,7 @@
font-size: 24px;
}

.literatureSuggestions.collapsed > button.header {
.literatureSuggestions[data-collapsed='true'] > button.header {
flex-direction: column;
padding: 0;
gap: 0.5em;
Expand All @@ -56,7 +56,7 @@
font-weight: bold;
}

.literatureSuggestions.collapsed > button.header > h1 {
.literatureSuggestions[data-collapsed='true'] > button.header > h1 {
order: 9;
writing-mode: vertical-lr;
}
Expand Down
12 changes: 5 additions & 7 deletions src/components/literature-suggestions/literature-suggestions.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
import React from 'react';
import { MinusOutlined, PlusOutlined } from '@ant-design/icons';

import SuggestedQuestions from './suggested-questions';
import MessageItem from './message-item';
Expand Down Expand Up @@ -48,11 +48,8 @@ export default function LiteratureSuggestions({ className }: LiteratureSuggestio

return (
<div
className={classNames(
className,
styles.literatureSuggestions,
collapsedPanel && styles.collapsed
)}
className={classNames(className, styles.literatureSuggestions)}
data-collapsed={collapsedPanel}
>
<button
className={styles.header}
Expand Down Expand Up @@ -99,6 +96,7 @@ export default function LiteratureSuggestions({ className }: LiteratureSuggestio
<footer>
{status === 'ready' && (
<SuggestedQuestions
threadId={threadId}
messagesLength={messages.length}
onClick={(selectedPrompt) => {
setPrompt(selectedPrompt);
Expand All @@ -122,7 +120,7 @@ export default function LiteratureSuggestions({ className }: LiteratureSuggestio
</footer>
</>
) : (
status !== 'error' && !threadError && <Spinner />
status !== 'error' && <Spinner />
)}
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const SuggestionsListPerRegion: Record<string, string[]> = {
],
'Neuron density': [
'Are there publications about determining the neuron density in {brain_region} in {rodents/humans}? ',
'Are there publications determining the quantitative distribution of excitatory versus inhibitory neurons {brain region} of the {rodent/human} brain? ',
'Are there publications determining the quantitative distribution of excitatory versus inhibitory neurons {brain_region} of the {rodent/human} brain? ',
'Are there publications exploring how the density of neurons varies across different layers of the {rodent/human} neocortex? ',
'Are there publications reporting the relationship between neuron density and network connectivity in {rodent/human} models? ',
'Are there publications determining the quantitative distribution of excitatory versus inhibitory neurons in various regions of the rodent brain? ',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import styles from './suggested-questions.module.css';

export interface SuggestedQuestionsProps {
className?: string;
/**
* Suggestions depend on the current chat's thread.
*/
threadId: string
/**
* When there is no message yet, we only use 1 generated suggestion and 2 hard-coded ones.
* Otherwise, we use 3 generated suggestions.
Expand All @@ -21,10 +25,12 @@ export interface SuggestedQuestionsProps {

export default function SuggestedQuestions({
className,
threadId,
messagesLength,
onClick,
}: SuggestedQuestionsProps) {
const [suggestions, clearSuggestions] = useServiceAiAgentSuggestionFromUserJourney(
threadId,
messagesLength === 0 ? 1 : 3
);
const hardcodedSuggestions = useHardcodedSuggestions(messagesLength === 0 ? 2 : 0);
Expand Down
70 changes: 48 additions & 22 deletions src/services/ai-agent/api/suggestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,55 @@ import { fetchJSON } from './util';
import { userJourneyTracker } from '@/components/explore-section/Literature/user-journey';
import { isType } from '@/util/type-guards';

export async function serviceAiAgentSuggestionFromUserJourney(
accessToken: string,
options?: {
virtualLabId?: string | null;
projectId?: string | null;
export const serviceAiAgentSuggestionFromUserJourney = asyncCreateSquash(
async (
accessToken: string,
options?: {
threadId: string;
virtualLabId?: string | null;
projectId?: string | null;
}
): Promise<string[]> => {
const { threadId = null, virtualLabId = null, projectId = null } = options ?? {};
await userJourneyTracker.saveTuple();
const journey = await userJourneyTracker.getLastTuples();
const data = await fetchJSON({
accessToken,
path: 'qa/question_suggestions',
params: {
vlab_id: virtualLabId,
project_id: projectId,
},
query: {
thread_id: threadId,
click_history: journey,
},
typeGuard: isSuggestionFromUserJourneyResponse,
});
return data.suggestions.map((suggestion) => suggestion.question);
}
): Promise<string[]> {
const { virtualLabId = null, projectId = null } = options ?? {};
await userJourneyTracker.saveTuple();
const journey = await userJourneyTracker.getLastTuples();
const data = await fetchJSON({
accessToken,
path: 'qa/question_suggestions',
params: {
vlab_id: virtualLabId,
project_id: projectId,
},
query: {
click_history: journey,
},
typeGuard: isSuggestionFromUserJourneyResponse,
});
return data.suggestions.map((suggestion) => suggestion.question);
);

export type AsyncAction<T extends unknown[], R> = (...args: T) => Promise<R>;

/**
* Transform a async function into a squashable one.
* That means that if you call it but the previous call is still pending,
* you will get the still pending promise and not execute it another time.
* Useful for network calls you don't want to have in parallel.
*/
export function asyncCreateSquash<T extends unknown[], R>(
action: AsyncAction<T, R>
): AsyncAction<T, R> {
let currentAction: Promise<R> | null = null;

return async (...args: T): Promise<R> => {
if (currentAction) return currentAction;
currentAction = action(...args);
const result = await currentAction;
currentAction = null;
return result;
};
}

interface SuggestionFromUserJourneyResponse {
Expand Down
4 changes: 3 additions & 1 deletion src/services/ai-agent/hooks/suggestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useGenericEventListener } from '@/util/generic-event';
import { useParamProjectId, useParamVirtualLabId } from '@/util/params';

export function useServiceAiAgentSuggestionFromUserJourney(
threadId: string,
count: number
): [suggestions: string[], clearSuggestions: () => void] {
const virtualLabId = useParamVirtualLabId();
Expand All @@ -19,6 +20,7 @@ export function useServiceAiAgentSuggestionFromUserJourney(
const data = await serviceAiAgentSuggestionFromUserJourney(
accessToken ?? 'no-access-token',
{
threadId,
virtualLabId,
projectId,
}
Expand All @@ -29,7 +31,7 @@ export function useServiceAiAgentSuggestionFromUserJourney(
}
};
action();
}, [count, accessToken, projectId, virtualLabId]);
}, [threadId, count, accessToken, projectId, virtualLabId]);
React.useEffect(fetchSuggestions, [fetchSuggestions]);
useGenericEventListener(userJourneyTracker.eventChange, fetchSuggestions);
return [suggestions, () => setSuggestions([])];
Expand Down