Rom89823974978 commited on
Commit
9f543c6
·
1 Parent(s): 5baa7eb

Updates to model, and UI

Browse files
backend/main.py CHANGED
@@ -657,11 +657,11 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
657
  gen_pipe = pipeline(
658
  "text2text-generation",#"text-generation",#"text2text-generation",
659
  model=llm_model,
660
- tokenizer=AutoTokenizer.from_pretrained(settings.llm_model),
661
  device=-1, # force CPU
662
  max_new_tokens=256,
663
  do_sample=True,
664
- temperature=0.5,
665
  )
666
  # Wrap in LangChain's HuggingFacePipeline
667
  llm = HuggingFacePipeline(pipeline=gen_pipe)
@@ -715,7 +715,11 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
715
  fs = gcsfs.GCSFileSystem()
716
  with fs.open(settings.parquet_path, "rb") as f:
717
  df = pl.read_parquet(f)
718
-
 
 
 
 
719
  # lowercase for filtering
720
  for col in ("title", "status", "legalBasis","fundingScheme"):
721
  df = df.with_columns(pl.col(col).str.to_lowercase().alias(f"_{col}_lc"))
 
657
  gen_pipe = pipeline(
658
  "text2text-generation",#"text-generation",#"text2text-generation",
659
  model=llm_model,
660
+ tokenizer=AutoTokenizer.from_pretrained(settings.llm_model,use_fast= False),
661
  device=-1, # force CPU
662
  max_new_tokens=256,
663
  do_sample=True,
664
+ temperature=0.7,
665
  )
666
  # Wrap in LangChain's HuggingFacePipeline
667
  llm = HuggingFacePipeline(pipeline=gen_pipe)
 
715
  fs = gcsfs.GCSFileSystem()
716
  with fs.open(settings.parquet_path, "rb") as f:
717
  df = pl.read_parquet(f)
718
+
719
+ df = df.with_columns(
720
+ pl.col("id").cast(pl.Int64).alias("id")
721
+ )
722
+
723
  # lowercase for filtering
724
  for col in ("title", "status", "legalBasis","fundingScheme"):
725
  df = df.with_columns(pl.col(col).str.to_lowercase().alias(f"_{col}_lc"))
frontend/src/components/ProjectDetails.tsx CHANGED
@@ -97,7 +97,7 @@ export default function ProjectDetails({
97
  : [51.505, -0.09];
98
 
99
  // format date string to YYYY-MM-DD
100
- const fmtDate = (iso?: string) => iso ? new Date(iso).toISOString().split('T')[0] : '-';
101
 
102
  // format numbers with two decimals
103
  const fmtNum = (num: number | null | undefined): string =>
@@ -125,11 +125,11 @@ export default function ProjectDetails({
125
  <Text fontWeight="bold">Acronym</Text>
126
  <Text>{project.acronym}</Text>
127
  </Box>
128
- <Box><Text fontWeight="bold">Start Date</Text><Text>{fmtDate(project.startDate)}</Text></Box>
129
- <Box><Text fontWeight="bold">End Date</Text><Text>{fmtDate(project.endDate)}</Text></Box>
130
  <Box><Text fontWeight="bold">Funding (EC max)</Text><Text>€{fmtNum(project.ecMaxContribution)}</Text></Box>
131
  <Box><Text fontWeight="bold">Total Cost</Text><Text>€{fmtNum(project.totalCost)}</Text></Box>
132
- <Box><Text fontWeight="bold">Funding Scheme</Text><Text>project.fundingScheme</Text></Box>
133
  <Box>
134
  <Text fontWeight="bold">Legal Basis</Text>
135
  <Text>{project.legalBasis}</Text>
 
97
  : [51.505, -0.09];
98
 
99
  // format date string to YYYY-MM-DD
100
+ //const fmtDate = (iso?: string) => iso ? new Date(iso).toISOString().split('T')[0] : '-';
101
 
102
  // format numbers with two decimals
103
  const fmtNum = (num: number | null | undefined): string =>
 
125
  <Text fontWeight="bold">Acronym</Text>
126
  <Text>{project.acronym}</Text>
127
  </Box>
128
+ <Box><Text fontWeight="bold">Start Date</Text><Text>{new Date(project.startDate).toISOString().slice(0,10)}</Text></Box>
129
+ <Box><Text fontWeight="bold">End Date</Text><Text>{new Date(project.endDate).toISOString().slice(0,10)}</Text></Box>
130
  <Box><Text fontWeight="bold">Funding (EC max)</Text><Text>€{fmtNum(project.ecMaxContribution)}</Text></Box>
131
  <Box><Text fontWeight="bold">Total Cost</Text><Text>€{fmtNum(project.totalCost)}</Text></Box>
132
+ <Box><Text fontWeight="bold">Funding Scheme</Text><Text>{project.fundingScheme}</Text></Box>
133
  <Box>
134
  <Text fontWeight="bold">Legal Basis</Text>
135
  <Text>{project.legalBasis}</Text>
frontend/src/components/ProjectExplorer.tsx CHANGED
@@ -63,6 +63,7 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
63
  setQuestion,
64
  chatHistory,
65
  askChatbot,
 
66
  messagesEndRef,
67
  }) => {
68
  const [filterOpts, setFilterOpts] = useState<FilterOptions>({
@@ -84,7 +85,7 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
84
  if (orgFilter) params.set("organization", orgFilter);
85
  if (countryFilter) params.set("country", countryFilter);
86
  if (search) params.set("search", search);
87
- if (idFilter.length >= MIN_SEARCH_LEN) params.set("id", idFilter);
88
  if (fundingSchemeFilter) params.set("fundingScheme", fundingSchemeFilter);
89
  params.set("sortField", sortField);
90
  params.set("sortOrder", sortOrder);
@@ -251,50 +252,74 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
251
 
252
  {/* Right Pane: Assistant */}
253
  <Box
254
- w={{ base: "100%", md: "30%" }}
255
- bg="gray.50"
256
- p={4}
257
- borderRadius="md"
258
- height="500px"
259
- display="flex"
260
- flexDirection="column"
261
- >
262
- <Heading size="sm" mb={2}>
263
- Assistant
264
- </Heading>
265
- <Text fontSize="xs" color="gray.500" mb={3}>
266
- ⚠️ The model may occasionally produce incorrect or misleading answers.
267
- </Text>
268
- <Box flex={1} overflowY="auto" mb={4}>
269
- <VStack spacing={3} align="stretch">
270
- {chatHistory.map((msg: ChatMessage, i: number) => (
271
- <HStack
272
- key={i}
273
- alignSelf={msg.role === "user" ? "flex-end" : "flex-start"}
274
- maxW="90%"
275
- >
276
- {msg.role === "assistant" && <Avatar size="sm" name="Bot" />}
277
- <Box>
278
- <Text fontSize="sm" bg={msg.role === "user" ? "blue.100" : "gray.200"} px={3} py={2} borderRadius="md">
279
- {msg.content}
280
- </Text>
281
- </Box>
282
- {msg.role === "user" && <Avatar size="sm" name="You" bg="blue.300" />}
283
- </HStack>
284
- ))}
285
- <div ref={messagesEndRef} />
286
- </VStack>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  </Box>
288
- <HStack>
289
- <Input
290
- placeholder="Ask something..."
291
- value={question}
292
- onChange={(e) => setQuestion(e.target.value)}
293
- onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); askChatbot(); } }}
294
- />
295
- <Button onClick={askChatbot} colorScheme="blue" aria-label="Ask the chatbot">Send</Button>
296
- </HStack>
297
- </Box>
298
  </Flex>
299
  );
300
  };
 
63
  setQuestion,
64
  chatHistory,
65
  askChatbot,
66
+ loading,
67
  messagesEndRef,
68
  }) => {
69
  const [filterOpts, setFilterOpts] = useState<FilterOptions>({
 
85
  if (orgFilter) params.set("organization", orgFilter);
86
  if (countryFilter) params.set("country", countryFilter);
87
  if (search) params.set("search", search);
88
+ if (idFilter.length >= MIN_SEARCH_LEN) params.set("proj_id", idFilter);
89
  if (fundingSchemeFilter) params.set("fundingScheme", fundingSchemeFilter);
90
  params.set("sortField", sortField);
91
  params.set("sortOrder", sortOrder);
 
252
 
253
  {/* Right Pane: Assistant */}
254
  <Box
255
+ w={{ base: "100%", md: "30%" }}
256
+ bg="gray.50"
257
+ p={4}
258
+ borderRadius="md"
259
+ height="500px"
260
+ display="flex"
261
+ flexDirection="column"
262
+ mt={6} // spacing from above
263
+ >
264
+ <Heading size="sm" mb={2}>Assistant</Heading>
265
+ <Text fontSize="xs" color="gray.500" mb={3}>
266
+ ⚠️ The model may occasionally produce incorrect or misleading answers.
267
+ </Text>
268
+ <Box flex={1} overflowY="auto" mb={4}>
269
+ <VStack spacing={3} align="stretch">
270
+ {chatHistory.map((msg, i) => (
271
+ <HStack
272
+ key={i}
273
+ alignSelf={msg.role === "user" ? "flex-end" : "flex-start"}
274
+ maxW="90%"
275
+ >
276
+ {msg.role === "assistant" && <Avatar size="sm" name="Bot" />}
277
+ <Box>
278
+ <Text
279
+ fontSize="sm"
280
+ bg={msg.role === "user" ? "blue.100" : "gray.200"}
281
+ px={3}
282
+ py={2}
283
+ borderRadius="md"
284
+ >
285
+ {msg.content}
286
+ {msg.content === "Generating answer..." && (
287
+ <Spinner size="xs" ml={2} />
288
+ )}
289
+ </Text>
290
+ </Box>
291
+ {msg.role === "user" && (
292
+ <Avatar size="sm" name="You" bg="blue.300" />
293
+ )}
294
+ </HStack>
295
+ ))}
296
+ <div ref={messagesEndRef} />
297
+ </VStack>
298
+ </Box>
299
+ <HStack>
300
+ <Input
301
+ placeholder="Ask something..."
302
+ value={question}
303
+ onChange={(e) => setQuestion(e.target.value)}
304
+ onKeyDown={(e) => {
305
+ if (e.key === "Enter" && !e.shiftKey) {
306
+ e.preventDefault();
307
+ askChatbot();
308
+ }
309
+ }}
310
+ isDisabled={loading}
311
+ />
312
+ <Button
313
+ onClick={askChatbot}
314
+ colorScheme="blue"
315
+ aria-label="Ask the chatbot"
316
+ isLoading={loading}
317
+ loadingText="Waiting..."
318
+ >
319
+ Send
320
+ </Button>
321
+ </HStack>
322
  </Box>
 
 
 
 
 
 
 
 
 
 
323
  </Flex>
324
  );
325
  };
frontend/src/hooks/types.ts CHANGED
@@ -101,8 +101,9 @@ export interface ProjectExplorerProps {
101
  setPage: React.Dispatch<React.SetStateAction<number>>;
102
  setSelectedProject: (project: Project) => void;
103
  question: string;
104
- setQuestion: (value: string) => void;
105
  chatHistory: ChatMessage[];
106
- askChatbot: () => void;
 
107
  messagesEndRef: React.RefObject<HTMLDivElement>;
108
  }
 
101
  setPage: React.Dispatch<React.SetStateAction<number>>;
102
  setSelectedProject: (project: Project) => void;
103
  question: string;
104
+ setQuestion: (q: string) => void;
105
  chatHistory: ChatMessage[];
106
+ askChatbot: () => Promise<void>;
107
+ loading: boolean;
108
  messagesEndRef: React.RefObject<HTMLDivElement>;
109
  }
frontend/src/hooks/useAppState.ts CHANGED
@@ -38,6 +38,7 @@ export const useAppState = () => {
38
  });
39
 
40
  const [chatHistory, setChatHistory] = useState<ChatMessage[]>([]);
 
41
 
42
  const [availableFilters, setAvailableFilters] = useState<AvailableFilters>({
43
  statuses: ["SIGNED", "CLOSED", "TERMINATED","UNKNOWN"],
@@ -49,7 +50,7 @@ export const useAppState = () => {
49
  const messagesEndRef = useRef<HTMLDivElement | null>(null);
50
 
51
  const fetchProjects = () => {
52
- fetch(`/api/projects?page=${page}&search=${encodeURIComponent(search)}&status=${statusFilter}&legalBasis=${legalFilter}&organization=${orgFilter}&country=${countryFilter}&fundingScheme=${fundingSchemeFilter}&id=${idFilter}&sortField=${sortField}&sortOrder=${sortOrder}`)
53
  .then(res => res.json())
54
  .then((data: Project[]) => setProjects(data))
55
  .catch(console.error);
@@ -84,13 +85,20 @@ export const useAppState = () => {
84
  }
85
 
86
  const askChatbot = async () => {
87
- if (!question.trim()) return;
88
  const newChat: ChatMessage[] = [
89
  ...chatHistory,
90
  { role: "user", content: question },
91
  ];
92
  setChatHistory(newChat);
93
  setQuestion("");
 
 
 
 
 
 
 
94
 
95
  try {
96
  const res = await fetch("/api/rag", {
@@ -99,49 +107,42 @@ export const useAppState = () => {
99
  body: JSON.stringify({ query: question }),
100
  });
101
 
102
- // Log the raw response for debugging
103
- console.log("RAG API status:", res.status, res.statusText);
104
  const text = await res.text();
105
- console.log("RAG API raw body:", text);
106
-
107
  if (!res.ok) {
108
- // Try to parse JSON error, or fall back to raw text
109
- let errDetail: string;
110
  try {
111
- const errJson = JSON.parse(text);
112
- errDetail = errJson.detail || JSON.stringify(errJson);
113
- } catch {
114
- errDetail = text;
115
- }
116
- throw new Error(`API error ${res.status}: ${errDetail}`);
117
  }
118
 
119
- // Now parse the successful JSON
120
  const data: RagResponse = JSON.parse(text);
121
- console.log("RAG API parsed:", data);
122
-
123
  const idList = data.source_ids.join(", ") || "none";
124
  const assistantContent = `${data.answer}
125
 
126
- The output was based on the following Project IDs: ${idList}`;
127
 
128
- setChatHistory([
129
- ...newChat,
 
130
  { role: "assistant", content: assistantContent },
131
  ]);
132
  } catch (err: any) {
133
- console.error("askChatbot error:", err);
134
- setChatHistory([
135
- ...newChat,
136
  {
137
  role: "assistant",
138
  content: `Something went wrong: ${err.message}`,
139
  },
140
  ]);
 
 
 
 
141
  }
142
  };
143
 
144
-
145
  useEffect(fetchProjects, [page, search, statusFilter,legalFilter, orgFilter, countryFilter, fundingSchemeFilter, idFilter, sortField, sortOrder]);
146
  useEffect(() => {
147
  console.log("Updated filters:", filters);
@@ -185,6 +186,7 @@ export const useAppState = () => {
185
  chatHistory,
186
  setChatHistory,
187
  askChatbot,
 
188
  messagesEndRef
189
  },
190
  detailsProps: {
@@ -193,6 +195,7 @@ export const useAppState = () => {
193
  setQuestion,
194
  chatHistory,
195
  askChatbot,
 
196
  messagesEndRef
197
  }
198
  };
 
38
  });
39
 
40
  const [chatHistory, setChatHistory] = useState<ChatMessage[]>([]);
41
+ const [loading, setLoading] = useState<boolean>(false);
42
 
43
  const [availableFilters, setAvailableFilters] = useState<AvailableFilters>({
44
  statuses: ["SIGNED", "CLOSED", "TERMINATED","UNKNOWN"],
 
50
  const messagesEndRef = useRef<HTMLDivElement | null>(null);
51
 
52
  const fetchProjects = () => {
53
+ fetch(`/api/projects?page=${page}&search=${encodeURIComponent(search)}&status=${statusFilter}&legalBasis=${legalFilter}&organization=${orgFilter}&country=${countryFilter}&fundingScheme=${fundingSchemeFilter}&proj_id=${idFilter}&sortField=${sortField}&sortOrder=${sortOrder}`)
54
  .then(res => res.json())
55
  .then((data: Project[]) => setProjects(data))
56
  .catch(console.error);
 
85
  }
86
 
87
  const askChatbot = async () => {
88
+ if (!question.trim() || loading) return;
89
  const newChat: ChatMessage[] = [
90
  ...chatHistory,
91
  { role: "user", content: question },
92
  ];
93
  setChatHistory(newChat);
94
  setQuestion("");
95
+ setLoading(true);
96
+
97
+ // 1) placeholder
98
+ setChatHistory((h) => [
99
+ ...h,
100
+ { role: "assistant", content: "Generating answer..." },
101
+ ]);
102
 
103
  try {
104
  const res = await fetch("/api/rag", {
 
107
  body: JSON.stringify({ query: question }),
108
  });
109
 
 
 
110
  const text = await res.text();
 
 
111
  if (!res.ok) {
112
+ let errDetail = text;
 
113
  try {
114
+ errDetail = JSON.parse(text).detail;
115
+ } catch {}
116
+ throw new Error(errDetail);
 
 
 
117
  }
118
 
 
119
  const data: RagResponse = JSON.parse(text);
 
 
120
  const idList = data.source_ids.join(", ") || "none";
121
  const assistantContent = `${data.answer}
122
 
123
+ The output was based on the following Project IDs: ${idList}`;
124
 
125
+ // 2) replace placeholder with real answer
126
+ setChatHistory((h) => [
127
+ ...h.slice(0, -1),
128
  { role: "assistant", content: assistantContent },
129
  ]);
130
  } catch (err: any) {
131
+ // replace placeholder with error message
132
+ setChatHistory((h) => [
133
+ ...h.slice(0, -1),
134
  {
135
  role: "assistant",
136
  content: `Something went wrong: ${err.message}`,
137
  },
138
  ]);
139
+ } finally {
140
+ setLoading(false);
141
+ // scroll to bottom
142
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
143
  }
144
  };
145
 
 
146
  useEffect(fetchProjects, [page, search, statusFilter,legalFilter, orgFilter, countryFilter, fundingSchemeFilter, idFilter, sortField, sortOrder]);
147
  useEffect(() => {
148
  console.log("Updated filters:", filters);
 
186
  chatHistory,
187
  setChatHistory,
188
  askChatbot,
189
+ loading,
190
  messagesEndRef
191
  },
192
  detailsProps: {
 
195
  setQuestion,
196
  chatHistory,
197
  askChatbot,
198
+ loading,
199
  messagesEndRef
200
  }
201
  };