Skip to content

Commit 399d1a3

Browse files
Update service workflow to handle mcq services (buerokratt#376)
* remove unwanted file * updated changes * fixed requested changes * fixed issue * service workflow implementation without calling service endpoints * fixed requested changes * fixed issues * protocol related requested changes * fixed requested changes * update time tracking * added time tracking and reloacate input guardrail before toolclassifiier * fixed issue * fixed issue * added hybrid search for the service detection * update tool classifier * fixing merge conflicts * fixed issue * optimize first user query response generation time * fixed pr reviewed issues * service integration * context based response generation flow * fixed pr review suggested issues * removed service project layer * fixed issues * delete unnessary files * added requested changes * mcqs flows are completed without dev testing * completed services workflow to handle mcq services * fix pyright issue * fixed pr review commented issues * feat: Mount `tool_classifier` module and add service workflow documentation for TestModel and Production LLM. * payload update --------- Co-authored-by: Thiru Dinesh <56014038+Thirunayan22@users.noreply.github.com>
1 parent 1e6bb54 commit 399d1a3

23 files changed

Lines changed: 2170 additions & 52 deletions

GUI/src/hooks/useStreamingResponse.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState, useRef, useCallback, useEffect } from 'react';
22
import axios from 'axios';
3+
import { ChoiceButton } from 'services/inference';
34

45
const getNotificationNodeUrl = (): string => {
56
const value = import.meta.env.REACT_APP_NOTIFICATION_NODE_URL;
@@ -21,7 +22,7 @@ interface StreamingOptions {
2122
}
2223

2324
interface UseStreamingResponseReturn {
24-
startStreaming: (message: string, options: StreamingOptions, onToken: (token: string) => void, onComplete: () => void, onError: (error: string) => void) => Promise<void>;
25+
startStreaming: (message: string, options: StreamingOptions, onToken: (token: string) => void, onComplete: () => void, onError: (error: string) => void, onButtons?: (buttons: ChoiceButton[]) => void) => Promise<void>;
2526
stopStreaming: () => void;
2627
isStreaming: boolean;
2728
}
@@ -54,7 +55,8 @@ export const useStreamingResponse = (channelId: string): UseStreamingResponseRet
5455
options: StreamingOptions,
5556
onToken: (token: string) => void,
5657
onComplete: () => void,
57-
onError: (error: string) => void
58+
onError: (error: string) => void,
59+
onButtons?: (buttons: ChoiceButton[]) => void
5860
) => {
5961
console.log('[SSE] Starting streaming for channel:', channelId);
6062

@@ -85,6 +87,9 @@ export const useStreamingResponse = (channelId: string): UseStreamingResponseRet
8587
} else if (data.type === 'stream_chunk' && data.content) {
8688
console.log('[SSE] Token:', data.content);
8789
onToken(data.content);
90+
if (data.buttons && data.buttons.length > 0 && onButtons) {
91+
onButtons(data.buttons);
92+
}
8893
} else if (data.type === 'stream_end') {
8994
console.log('[SSE] Stream ended');
9095
setIsStreaming(false);

GUI/src/pages/TestModel/TestLLM.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
margin-top: 30px;
33
}
44

5+
.mcq-buttons {
6+
display: flex;
7+
flex-wrap: wrap;
8+
gap: 0.75rem;
9+
margin-top: 1rem;
10+
}
11+
512
.testModalClassifyButton {
613
text-align: right;
714
margin-top: 20px;

GUI/src/pages/TestModel/index.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import remarkGfm from 'remark-gfm';
88
import './TestLLM.scss';
99
import { useDialog } from 'hooks/useDialog';
1010
import { fetchLLMConnectionsPaginated, LegacyLLMConnectionFilters } from 'services/llmConnections';
11-
import { viewInferenceResult, InferenceRequest, InferenceResponse } from 'services/inference';
11+
import { viewInferenceResult, InferenceRequest, InferenceResponse, ChoiceButton } from 'services/inference';
1212
import { llmConnectionsQueryKeys } from 'utils/queryKeys';
1313
import { ButtonAppearanceTypes } from 'enums/commonEnums';
1414

1515
const TestLLM: FC = () => {
1616
const { t } = useTranslation();
1717
const { open: openDialog, close: closeDialog } = useDialog();
1818
const [inferenceResult, setInferenceResult] = useState<InferenceResponse['response'] | null>(null);
19+
const [pendingButtons, setPendingButtons] = useState<ChoiceButton[]>([]);
1920
const [testLLM, setTestLLM] = useState({
2021
connectionId: null,
2122
text: '',
@@ -49,6 +50,7 @@ const TestLLM: FC = () => {
4950
mutationFn: (request: InferenceRequest) => viewInferenceResult(request),
5051
onSuccess: (data: InferenceResponse) => {
5152
setInferenceResult(data?.response);
53+
setPendingButtons(data?.response?.buttons ?? []);
5254
},
5355
onError: (error: any) => {
5456
console.error('Error getting inference result:', error);
@@ -76,6 +78,15 @@ const TestLLM: FC = () => {
7678
}
7779
};
7880

81+
const handleButtonClick = (payload: string) => {
82+
if (!testLLM.connectionId) return;
83+
setPendingButtons([]);
84+
inferenceMutation.mutate({
85+
llmConnectionId: Number(testLLM.connectionId),
86+
message: payload,
87+
});
88+
};
89+
7990
const handleChange = (key: string, value: string | number) => {
8091
// Prevent changes while inference is loading
8192
if (inferenceMutation.isLoading) {
@@ -158,6 +169,22 @@ const TestLLM: FC = () => {
158169
</div>
159170
</div>
160171

172+
{/* MCQ Buttons */}
173+
{pendingButtons.length > 0 && (
174+
<div className="mcq-buttons">
175+
{pendingButtons.map((btn) => (
176+
<Button
177+
key={btn.payload}
178+
appearance={ButtonAppearanceTypes.SECONDARY}
179+
onClick={() => handleButtonClick(btn.payload)}
180+
disabled={inferenceMutation.isLoading}
181+
>
182+
{btn.title}
183+
</Button>
184+
))}
185+
</div>
186+
)}
187+
161188
{/* Context Section */}
162189
{
163190
sortedContext && sortedContext?.length > 0 && (

GUI/src/pages/TestProductionLLM/TestProductionLLM.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
margin: 0 auto;
44
padding: 2rem;
55

6+
.mcq-buttons {
7+
display: flex;
8+
flex-wrap: wrap;
9+
gap: 0.75rem;
10+
margin-top: 1rem;
11+
}
12+
613
&__header {
714
display: flex;
815
justify-content: space-between;

GUI/src/pages/TestProductionLLM/index.tsx

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
33
import { Button, FormTextarea } from 'components';
44
import { useToast } from 'hooks/useToast';
55
import { useStreamingResponse } from 'hooks/useStreamingResponse';
6+
import { ChoiceButton } from 'services/inference';
67
import './TestProductionLLM.scss';
78
import MessageContent from 'components/MessageContent';
89
interface Message {
@@ -12,6 +13,7 @@ interface Message {
1213
timestamp: string;
1314
hasError?: boolean;
1415
errorMessage?: string;
16+
buttons?: ChoiceButton[];
1517
}
1618

1719
const TestProductionLLM: FC = () => {
@@ -115,6 +117,16 @@ const TestProductionLLM: FC = () => {
115117
});
116118
};
117119

120+
const onButtons = (buttons: ChoiceButton[]) => {
121+
setMessages(prev => {
122+
const botMsgIndex = prev.findIndex(msg => msg.id === botMessageId);
123+
if (botMsgIndex === -1) return prev;
124+
const updated = [...prev];
125+
updated[botMsgIndex] = { ...updated[botMsgIndex], buttons };
126+
return updated;
127+
});
128+
};
129+
118130
const onComplete = () => {
119131
console.log('[Component] Stream completed');
120132
// Always reset loading state on completion
@@ -160,7 +172,7 @@ const TestProductionLLM: FC = () => {
160172

161173
// Start streaming
162174
try {
163-
await startStreaming(userMessageText, streamingOptions, onToken, onComplete, onError);
175+
await startStreaming(userMessageText, streamingOptions, onToken, onComplete, onError, onButtons);
164176
} catch (error) {
165177
console.error('[Component] Failed to start streaming:', error);
166178
// Reset loading state if streaming fails to start
@@ -175,6 +187,62 @@ const TestProductionLLM: FC = () => {
175187
}
176188
};
177189

190+
const handleButtonClick = async (title: string, payload: string) => {
191+
if (isLoading || isStreaming) return;
192+
193+
const userMessage: Message = {
194+
id: `user-${Date.now()}`,
195+
content: title,
196+
isUser: true,
197+
timestamp: new Date().toISOString(),
198+
};
199+
setMessages(prev => [...prev, userMessage]);
200+
setIsLoading(true);
201+
202+
const botMessageId = `bot-${Date.now()}`;
203+
const conversationHistory = messages.map(msg => ({
204+
authorRole: msg.isUser ? 'user' : 'bot',
205+
message: msg.content,
206+
timestamp: msg.timestamp,
207+
}));
208+
const streamingOptions = {
209+
authorId: 'test-user-456',
210+
conversationHistory,
211+
url: 'opensearch-dashboard-test',
212+
};
213+
214+
const onToken = (token: string) => {
215+
setMessages(prev => {
216+
const idx = prev.findIndex(m => m.id === botMessageId);
217+
if (idx === -1) return [...prev, { id: botMessageId, content: token, isUser: false, timestamp: new Date().toISOString() }];
218+
const updated = [...prev];
219+
updated[idx] = { ...updated[idx], content: updated[idx].content + token };
220+
return updated;
221+
});
222+
};
223+
const onButtons = (buttons: ChoiceButton[]) => {
224+
setMessages(prev => {
225+
const idx = prev.findIndex(m => m.id === botMessageId);
226+
if (idx === -1) return prev;
227+
const updated = [...prev];
228+
updated[idx] = { ...updated[idx], buttons };
229+
return updated;
230+
});
231+
};
232+
const onComplete = () => setIsLoading(false);
233+
const onError = (error: string) => {
234+
setIsLoading(false);
235+
toast.open({ type: 'error', title: t('testProductionLLM.streamingErrorTitle'), message: error });
236+
};
237+
238+
try {
239+
await startStreaming(payload, streamingOptions, onToken, onComplete, onError, onButtons);
240+
} catch (error) {
241+
console.error('[Component] Failed to start streaming for button click:', error);
242+
setIsLoading(false);
243+
}
244+
};
245+
178246
const clearChat = () => {
179247
setMessages([]);
180248
stopStreaming();
@@ -215,6 +283,20 @@ const TestProductionLLM: FC = () => {
215283
>
216284
<div className="test-production-llm__message-content">
217285
<MessageContent content={msg.content} />
286+
{!msg.isUser && msg.buttons && msg.buttons.length > 0 && (
287+
<div className="mcq-buttons">
288+
{msg.buttons.map((btn) => (
289+
<Button
290+
key={btn.payload}
291+
onClick={() => handleButtonClick(btn.title, btn.payload)}
292+
disabled={isLoading || isStreaming}
293+
appearance="secondary"
294+
>
295+
{btn.title}
296+
</Button>
297+
))}
298+
</div>
299+
)}
218300
{msg.hasError && (
219301
<div className="test-production-llm__message-error">
220302
<span className="test-production-llm__message-error-icon">⚠️</span>

GUI/src/services/inference.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@ export interface ProductionInferenceRequest {
1919
url: string;
2020
}
2121

22+
export interface ChoiceButton {
23+
title: string;
24+
payload: string;
25+
}
26+
2227
export interface InferenceResponse {
2328
response: {
2429
chatId: number;
2530
llmServiceActive: boolean;
2631
questionOutOfLlmScope: boolean;
2732
content: string;
33+
buttons?: ChoiceButton[];
2834
chunks?: {
2935
rank: number,
3036
chunkRetrieved: string

constants.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ RAG_SEARCH_LLM_ORCHESTRATOR=http://llm-orchestration-service:8100/orchestrate
1010
RAG_SEARCH_PROMPT_REFRESH=http://llm-orchestration-service:8100/prompt-config/refresh
1111
DOMAIN=localhost
1212
DB_PASSWORD=dbadmin
13-
RAG_SEARCH_RUUTER_PUBLIC_INTERNAL_SERVICE=http://ruuter-public:8086/services
13+
RAG_SEARCH_RUUTER_PUBLIC_INTERNAL_SERVICE=http://ruuter:8086/services
1414
SERVICE_DMAPPER_HBS=http://data-mapper:3000/hbs/rag-search
1515
SERVICE_PROJECT_LAYER=services

docker-compose-ec2.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ services:
180180
- ./DSL/CronManager/DSL:/DSL
181181
- ./DSL/CronManager/script:/app/scripts
182182
- ./src/vector_indexer:/app/src/vector_indexer
183+
- ./src/tool_classifier:/app/src/tool_classifier
183184
- ./src/intent_data_enrichment:/app/src/intent_data_enrichment
184185
- ./src/utils/decrypt_vault_secrets.py:/app/src/utils/decrypt_vault_secrets.py:ro # Decryption utility (read-only)
185186
- cron_data:/app/data

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ services:
5252
volumes:
5353
- ./DSL:/data
5454
- ./DSL/DMapper/rag-search/hbs:/workspace/app/views/rag-search
55-
- ./DSL/DMapper/rag-search/lib:/workspace/app/lib
55+
- ./DSL/DMapper/rag-search/js:/workspace/app/js/rag-search
5656
ports:
5757
- 3001:3000
5858
networks:
@@ -87,7 +87,7 @@ services:
8787
volumes:
8888
- ./tim-db:/var/lib/postgresql/data
8989
ports:
90-
- 9876:5432
90+
- 9875:5432
9191
networks:
9292
- bykstack
9393

0 commit comments

Comments
 (0)