Skip to content

Commit ffeb4d5

Browse files
ewilliams-clouderabaasitshariefjkwatsonmliu-cloudera
authored
add some simple alerts when a model does not exist (#229)
* add some simple alerts when a model does not exist * add nav buttons * switch to using suspense query for amp config * better error handling for streaming * bumping crewai configs to be more lenient * bump max iter time to 120 seconds * fixed merge conflicts * bump back to 120 * use the proper model name for the crew planner agent * changed to runtime error * unquote crew ai errors * added more info in tips * remove unused import * Update llm-service/app/services/query/agents/crewai_querier.py Co-authored-by: mliu-cloudera <mliu@cloudera.com> * remove unquote --------- Co-authored-by: Baasit Sharief <baasitsharief@gmail.com> Co-authored-by: jwatson <jkwatson@gmail.com> Co-authored-by: mliu-cloudera <mliu@cloudera.com>
1 parent c1aecd4 commit ffeb4d5

File tree

13 files changed

+200
-73
lines changed

13 files changed

+200
-73
lines changed

llm-service/app/services/query/agents/crewai_querier.py

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,10 @@ def assemble_crew(
151151
step_callback=lambda output: step_callback(
152152
output, "Tool Result", crew_events_queue
153153
),
154-
max_execution_time=30,
155-
max_iter=5,
154+
max_execution_time=120,
155+
max_iter=15,
156+
max_rpm=10,
157+
max_retry_limit=5,
156158
)
157159

158160
# Define tasks for the researcher agents
@@ -236,7 +238,7 @@ def assemble_crew(
236238
output, "Research Complete", crew_events_queue
237239
),
238240
tools=mcp_tools,
239-
max_retries=3,
241+
max_retries=5,
240242
guardrail=validate_with_context,
241243
)
242244

@@ -256,8 +258,10 @@ def assemble_crew(
256258
output, "Response Computed", crew_events_queue
257259
),
258260
verbose=True,
259-
max_execution_time=30,
260-
max_iter=5,
261+
max_execution_time=120,
262+
max_iter=15,
263+
max_rpm=10,
264+
max_retry_limit=5,
261265
)
262266

263267
response_context = [research_task, calculation_task]
@@ -309,31 +313,35 @@ def launch_crew(
309313
query_str: str,
310314
) -> Tuple[str, list[Tuple[str, float]]]:
311315
# Run the crew to get the enhanced response
312-
crew_result: CrewOutput = crew.kickoff()
313-
314-
source_node_ids_w_score = extract_node_ids_from_crew_result(crew_result)
315-
316-
# Create an enhanced query that includes the CrewAI insights
317-
return (
318-
f"""
319-
Original query: {query_str}
320-
321-
Research insights: {crew_result}
322-
323-
Please provide a response to the original query, incorporating the insights from research with in-line citations. \
324-
325-
Adhere to the following guidelines:
326-
* If you cannot find relevant information in the research insights, answer the question directly and indicate that \
327-
you don't have enough information.
328-
* If citations from the research insights are used, use the in-line links and \
329-
citations from the research insights as is. Keep markdown formatted links as is i.e. [<text>](<web_link>). \
330-
Keep the in-line citations of format `<a class='rag_citation' href='node_id'>node_id</a>` as is.
331-
* Do not make up any links or citations of the form `<a class='rag_citation' href='node_id'>node_id</a>` \
332-
that are not present in the research insights. Do not make up any markdown links as well. Only use the \
333-
links and citations from the research insights.
334-
""",
335-
source_node_ids_w_score,
336-
)
316+
try:
317+
crew_result: CrewOutput = crew.kickoff()
318+
319+
source_node_ids_w_score = extract_node_ids_from_crew_result(crew_result)
320+
321+
# Create an enhanced query that includes the CrewAI insights
322+
return (
323+
f"""
324+
Original query: {query_str}
325+
326+
Research insights: {crew_result}
327+
328+
Please provide a response to the original query, incorporating the insights from research with in-line citations. \
329+
330+
Adhere to the following guidelines:
331+
* If you cannot find relevant information in the research insights, answer the question directly and indicate that \
332+
you don't have enough information.
333+
* If citations from the research insights are used, use the in-line links and \
334+
citations from the research insights as is. Keep markdown formatted links as is i.e. [<text>](<web_link>). \
335+
Keep the in-line citations of format `<a class='rag_citation' href='node_id'>node_id</a>` as is.
336+
* Do not make up any links or citations of the form `<a class='rag_citation' href='node_id'>node_id</a>` \
337+
that are not present in the research insights. Do not make up any markdown links as well. Only use the \
338+
links and citations from the research insights.
339+
""",
340+
source_node_ids_w_score,
341+
)
342+
except Exception as e:
343+
logger.exception("Error running CrewAI crew")
344+
raise RuntimeError("Error running CrewAI crew: %s" % str(e)) from e
337345

338346

339347
def extract_node_ids_from_crew_result(

llm-service/app/services/query/agents/planner_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def decide_retrieval_strategy(
121121
role="Planner",
122122
goal="Decide whether to use retrieval or answer directly",
123123
backstory="You are an expert planner who decides the most efficient way to answer a query.",
124-
llm=get_crewai_llm_object_direct(self.llm, self.configuration.model_name),
124+
llm=get_crewai_llm_object_direct(self.llm, getattr(self.llm, "model", "")),
125125
# verbose=True,
126126
)
127127

ui/src/api/ampMetadataApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export const useGetAmpConfig = () => {
185185
};
186186

187187
export const useGetPollingAmpConfig = (poll?: boolean) => {
188-
return useQuery({
188+
return queryOptions({
189189
queryKey: [QueryKeys.getPollingAmpConfig],
190190
queryFn: getAmpConfig,
191191
refetchInterval: () => {

ui/src/api/chatApi.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -452,24 +452,32 @@ const streamChatMutation = async (
452452
}),
453453
signal: ctrl.signal,
454454
onmessage(msg: EventSourceMessage) {
455-
const data = JSON.parse(msg.data) as ChatMutationResponse;
456-
457-
if (data.error) {
458-
onError(data.error);
455+
try {
456+
const data = JSON.parse(msg.data) as ChatMutationResponse;
457+
458+
if (data.error) {
459+
onError(data.error);
460+
ctrl.abort();
461+
}
462+
463+
if (data.text) {
464+
onChunk(data.text);
465+
}
466+
467+
if (data.event) {
468+
onEvent(data.event);
469+
}
470+
471+
if (data.response_id) {
472+
responseId = data.response_id;
473+
}
474+
} catch (error) {
475+
console.error("Error parsing message data:", error);
476+
onError(
477+
`An error occurred while processing the response. Original error message: ${JSON.stringify(msg)}. Error in parsing: ${JSON.stringify(error)}`,
478+
);
459479
ctrl.abort();
460480
}
461-
462-
if (data.text) {
463-
onChunk(data.text);
464-
}
465-
466-
if (data.event) {
467-
onEvent(data.event);
468-
}
469-
470-
if (data.response_id) {
471-
responseId = data.response_id;
472-
}
473481
},
474482
onerror(err: unknown) {
475483
ctrl.abort();

ui/src/layout/Sidebar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import "./style.css";
6161
import AmpUpdateBanner from "src/components/AmpUpdate/AmpUpdateBanner.tsx";
6262
import { useGetPollingAmpConfig } from "src/api/ampMetadataApi.ts";
6363
import { getItem } from "./TopNav";
64+
import { useSuspenseQuery } from "@tanstack/react-query";
6465

6566
const { Sider } = Layout;
6667

@@ -71,7 +72,7 @@ const Sidebar: React.FC = () => {
7172
const matchRoute = useMatchRoute();
7273
const navigate = useNavigate();
7374
const ref = useRef<HTMLDivElement>(null);
74-
const { data: config } = useGetPollingAmpConfig();
75+
const { data: config } = useSuspenseQuery(useGetPollingAmpConfig());
7576

7677
const navToRagApp = () => {
7778
navigate({ to: "/chats" }).catch(() => null);

ui/src/layout/TopNav.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,12 @@ import {
5454
ProjectConfig,
5555
useGetPollingAmpConfig,
5656
} from "src/api/ampMetadataApi.ts";
57+
import { useSuspenseQuery } from "@tanstack/react-query";
5758

5859
const TopNav: React.FC = () => {
5960
const matchRoute = useMatchRoute();
6061
const navigate = useNavigate();
61-
const { data: config } = useGetPollingAmpConfig();
62+
const { data: config } = useSuspenseQuery(useGetPollingAmpConfig());
6263

6364
const navigateTo = (path: string) => () => {
6465
navigate({ to: path }).catch(() => null);

ui/src/pages/DataSources/DataSourcesManagement/DataSourcesForm.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
******************************************************************************/
3838

3939
import {
40+
Alert,
41+
Button,
4042
Collapse,
4143
Divider,
4244
Form,
@@ -51,6 +53,8 @@ import { ConnectionType, DataSourceBaseType } from "src/api/dataSourceApi";
5153
import { useGetEmbeddingModels, useGetLlmModels } from "src/api/modelsApi.ts";
5254
import { useEffect } from "react";
5355
import { transformModelOptions } from "src/utils/modelUtils.ts";
56+
import { useNavigate } from "@tanstack/react-router";
57+
import messageQueue from "src/utils/messageQueue.ts";
5458

5559
export const distanceMetricOptions = [
5660
{
@@ -143,6 +147,7 @@ const DataSourcesForm = ({
143147
}: DataSourcesFormProps) => {
144148
const embeddingsModels = useGetEmbeddingModels();
145149
const llmModels = useGetLlmModels();
150+
const navigate = useNavigate();
146151

147152
useEffect(() => {
148153
if (initialValues.embeddingModel) {
@@ -160,6 +165,30 @@ const DataSourcesForm = ({
160165
style={{ width: "100%" }}
161166
{...layout}
162167
>
168+
{embeddingsModels.isSuccess && embeddingsModels.data.length === 0 ? (
169+
<Alert
170+
type="warning"
171+
showIcon={true}
172+
message={
173+
"One embedding model must be available to create a knowledge base"
174+
}
175+
style={{ marginBottom: 16 }}
176+
action={
177+
<Button
178+
onClick={() => {
179+
navigate({
180+
to: "/settings",
181+
hash: "modelConfiguration",
182+
}).catch(() => {
183+
messageQueue.error("Failed to navigate to models page");
184+
});
185+
}}
186+
>
187+
Model Config
188+
</Button>
189+
}
190+
/>
191+
) : null}
163192
<Form.Item
164193
name="name"
165194
label="Name"

ui/src/pages/Models/ModelTips.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ const helpText = {
8484
<li>Cohere Rerank v3.5</li>
8585
<li>Amazon Rerank v1</li>
8686
</li>
87+
<li>
88+
For model capabilities including tool calling, refer to the Amazon
89+
Bedrock User Guide.
90+
</li>
8791
</ul>
8892
</Typography>
8993
),

ui/src/pages/RagChatTab/ChatOutput/ChatMessages/ChatMessageController.tsx

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,9 @@ import { useContext, useEffect, useRef } from "react";
4040
import { useInView } from "react-intersection-observer";
4141
import ChatMessage from "pages/RagChatTab/ChatOutput/ChatMessages/ChatMessage.tsx";
4242
import { RagChatContext } from "pages/RagChatTab/State/RagChatContext.tsx";
43-
import { Image, Skeleton, Typography } from "antd";
44-
import Images from "src/components/images/Images.ts";
43+
import { Skeleton } from "antd";
4544
import PendingRagOutputSkeleton from "pages/RagChatTab/ChatOutput/Loaders/PendingRagOutputSkeleton.tsx";
4645
import { ChatLoading } from "pages/RagChatTab/ChatOutput/Loaders/ChatLoading.tsx";
47-
import SuggestedQuestionsCards from "pages/RagChatTab/ChatOutput/Placeholders/SuggestedQuestionsCards.tsx";
4846
import { useSearch } from "@tanstack/react-router";
4947
import messageQueue from "src/utils/messageQueue.ts";
5048
import {
@@ -54,7 +52,7 @@ import {
5452
useStreamingChatMutation,
5553
} from "src/api/chatApi.ts";
5654
import { useRenameNameMutation } from "src/api/sessionApi.ts";
57-
import NoDataSourcesState from "pages/RagChatTab/ChatOutput/Placeholders/NoDataSourcesState.tsx";
55+
import EmptyChatState from "pages/RagChatTab/ChatOutput/ChatMessages/EmptyChatState.tsx";
5856

5957
const ChatMessageController = () => {
6058
const {
@@ -166,21 +164,7 @@ const ChatMessageController = () => {
166164
if (isFetchingHistory) {
167165
return <ChatLoading />;
168166
}
169-
return (
170-
<>
171-
<Image
172-
src={Images.BrandTalking}
173-
alt="Machines Chatting"
174-
style={{ width: 80 }}
175-
preview={false}
176-
/>
177-
<Typography.Title level={4} style={{ fontWeight: 300, margin: 0 }}>
178-
Welcome to RAG Studio
179-
</Typography.Title>
180-
<SuggestedQuestionsCards />
181-
<NoDataSourcesState />
182-
</>
183-
);
167+
return <EmptyChatState />;
184168
}
185169

186170
return (
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* CLOUDERA APPLIED MACHINE LEARNING PROTOTYPE (AMP)
3+
* (C) Cloudera, Inc. 2025
4+
* All rights reserved.
5+
*
6+
* Applicable Open Source License: Apache 2.0
7+
*
8+
* NOTE: Cloudera open source products are modular software products
9+
* made up of hundreds of individual components, each of which was
10+
* individually copyrighted. Each Cloudera open source product is a
11+
* collective work under U.S. Copyright Law. Your license to use the
12+
* collective work is as provided in your written agreement with
13+
* Cloudera. Used apart from the collective work, this file is
14+
* licensed for your use pursuant to the open source license
15+
* identified above.
16+
*
17+
* This code is provided to you pursuant a written agreement with
18+
* (i) Cloudera, Inc. or (ii) a third-party authorized to distribute
19+
* this code. If you do not have a written agreement with Cloudera nor
20+
* with an authorized and properly licensed third party, you do not
21+
* have any rights to access nor to use this code.
22+
*
23+
* Absent a written agreement with Cloudera, Inc. ("Cloudera") to the
24+
* contrary, A) CLOUDERA PROVIDES THIS CODE TO YOU WITHOUT WARRANTIES OF ANY
25+
* KIND; (B) CLOUDERA DISCLAIMS ANY AND ALL EXPRESS AND IMPLIED
26+
* WARRANTIES WITH RESPECT TO THIS CODE, INCLUDING BUT NOT LIMITED TO
27+
* IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND
28+
* FITNESS FOR A PARTICULAR PURPOSE; (C) CLOUDERA IS NOT LIABLE TO YOU,
29+
* AND WILL NOT DEFEND, INDEMNIFY, NOR HOLD YOU HARMLESS FOR ANY CLAIMS
30+
* ARISING FROM OR RELATED TO THE CODE; AND (D)WITH RESPECT TO YOUR EXERCISE
31+
* OF ANY RIGHTS GRANTED TO YOU FOR THE CODE, CLOUDERA IS NOT LIABLE FOR ANY
32+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE OR
33+
* CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, DAMAGES
34+
* RELATED TO LOST REVENUE, LOST PROFITS, LOSS OF INCOME, LOSS OF
35+
* BUSINESS ADVANTAGE OR UNAVAILABILITY, OR LOSS OR CORRUPTION OF
36+
* DATA.
37+
*/
38+
39+
import { Alert, Button, Image, Typography } from "antd";
40+
import Images from "src/components/images/Images.ts";
41+
import SuggestedQuestionsCards from "pages/RagChatTab/ChatOutput/Placeholders/SuggestedQuestionsCards.tsx";
42+
import NoDataSourcesState from "pages/RagChatTab/ChatOutput/Placeholders/NoDataSourcesState.tsx";
43+
import { useGetLlmModels } from "src/api/modelsApi.ts";
44+
import messageQueue from "src/utils/messageQueue.ts";
45+
import { useNavigate } from "@tanstack/react-router";
46+
47+
const EmptyChatState = () => {
48+
const { data: llmModels, isSuccess } = useGetLlmModels();
49+
const navigate = useNavigate();
50+
51+
return (
52+
<>
53+
{isSuccess && llmModels.length === 0 ? (
54+
<Alert
55+
type="warning"
56+
showIcon
57+
message={"One inference model must be available to chat"}
58+
action={
59+
<Button
60+
style={{ marginLeft: 12 }}
61+
onClick={() => {
62+
navigate({
63+
to: "/settings",
64+
hash: "modelConfiguration",
65+
}).catch(() => {
66+
messageQueue.error("Failed to navigate to models page");
67+
});
68+
}}
69+
>
70+
Model Config
71+
</Button>
72+
}
73+
/>
74+
) : null}
75+
<Image
76+
src={Images.BrandTalking}
77+
alt="Machines Chatting"
78+
style={{ width: 80 }}
79+
preview={false}
80+
/>
81+
<Typography.Title level={4} style={{ fontWeight: 300, margin: 0 }}>
82+
Welcome to RAG Studio
83+
</Typography.Title>
84+
<SuggestedQuestionsCards />
85+
<NoDataSourcesState />
86+
</>
87+
);
88+
};
89+
90+
export default EmptyChatState;

0 commit comments

Comments
 (0)