Rom89823974978 commited on
Commit
0ef7ba6
·
1 Parent(s): dac1f18

Repo ready

Browse files
Files changed (2) hide show
  1. backend/main.py +1 -0
  2. frontend/src/hooks/useAppState.ts +103 -102
backend/main.py CHANGED
@@ -135,6 +135,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
135
  max_new_tokens=256,
136
  do_sample=True,
137
  temperature=0.7,
 
138
  )
139
  llm = HuggingFacePipeline(pipeline=gen_pipe)
140
 
 
135
  max_new_tokens=256,
136
  do_sample=True,
137
  temperature=0.7,
138
+ repetition_penalty=1.2
139
  )
140
  llm = HuggingFacePipeline(pipeline=gen_pipe)
141
 
frontend/src/hooks/useAppState.ts CHANGED
@@ -1,12 +1,6 @@
1
  import { useState, useEffect, useRef } from "react";
2
  import { debounce } from "lodash";
3
- import type {
4
- Project,
5
- OrganizationLocation,
6
- FilterState,
7
- ChatMessage,
8
- AvailableFilters,
9
- } from "./types";
10
 
11
  interface Stats {
12
  [key: string]: {
@@ -18,25 +12,20 @@ interface Stats {
18
  type SortOrder = "asc" | "desc";
19
 
20
  export const useAppState = () => {
21
- // Projects state and pagination
22
  const [projects, setProjects] = useState<Project[]>([]);
23
- const [page, setPage] = useState<number>(0);
24
-
25
- // Search and filter states
26
  const [search, setSearch] = useState<string>("");
27
  const [statusFilter, setStatusFilter] = useState<string>("");
28
- const [legalFilter, setLegalFilter] = useState<string>("");
29
- const [orgFilter, setOrgFilter] = useState<string>("");
30
- const [countryFilter, setCountryFilter] = useState<string>("");
31
- const [fundingSchemeFilter, setFundingSchemeFilter] = useState<string>("");
32
- const [idFilter, setIdFilter] = useState<string>("");
33
-
34
- // Sorting
35
- const [sortField, setSortField] = useState<string>("");
36
- const [sortOrder, setSortOrder] = useState<SortOrder>("asc");
37
-
38
- // Dashboard stats and available filters
39
  const [stats, setStats] = useState<Stats>({});
 
 
 
 
 
 
 
40
  const [filters, setFilters] = useState<FilterState>({
41
  status: "",
42
  organization: "",
@@ -46,135 +35,146 @@ export const useAppState = () => {
46
  maxYear: "2025",
47
  minFunding: "0",
48
  maxFunding: "10000000",
49
- });
 
 
 
 
50
  const [availableFilters, setAvailableFilters] = useState<AvailableFilters>({
51
- statuses: ["SIGNED", "CLOSED", "TERMINATED", "UNKNOWN"],
52
  organizations: [],
53
  countries: [],
54
  legalBases: [],
55
- fundingSchemes: [],
56
- ids: [],
57
  });
58
 
59
- // Chatbot states
60
- const [question, setQuestion] = useState<string>("");
61
- const [chatHistory, setChatHistory] = useState<ChatMessage[]>([]);
62
- const [loading, setLoading] = useState<boolean>(false);
63
  const messagesEndRef = useRef<HTMLDivElement | null>(null);
64
 
65
- // Fetch projects with current filters, pagination, sorting
66
  const fetchProjects = () => {
67
- const query = new URLSearchParams({
68
- page: page.toString(),
69
- search,
70
- status: statusFilter,
71
- legalBasis: legalFilter,
72
- organization: orgFilter,
73
- country: countryFilter,
74
- fundingScheme: fundingSchemeFilter,
75
- proj_id: idFilter,
76
- sortField,
77
- sortOrder,
78
- }).toString();
79
-
80
- fetch(`/api/projects?${query}`)
81
- .then((res) => res.json())
82
  .then((data: Project[]) => setProjects(data))
83
- .catch((err) => console.error("Error fetching projects:", err));
84
  };
85
 
86
- // Fetch stats with debouncing to limit requests
87
- const fetchStats = debounce((filterParams: FilterState) => {
88
- const query = new URLSearchParams(filterParams as any).toString();
89
- fetch(`/api/stats?${query}`)
90
- .then((res) => res.json())
91
  .then((data: Stats) => setStats(data))
92
- .catch((err) => console.error("Error fetching stats:", err));
93
  }, 500);
94
 
95
- // Fetch available filter options based on dataset and active filters
96
- const fetchAvailableFilters = (filterParams: FilterState) => {
97
- const query = new URLSearchParams(filterParams as any).toString();
98
- fetch(`/api/filters?${query}`)
99
- .then((res) => res.json())
100
- .then((data) => {
101
  setAvailableFilters({
102
  statuses: ["SIGNED", "CLOSED", "TERMINATED", "UNKNOWN"],
103
  organizations: data.organizations,
104
  countries: data.countries,
105
  legalBases: data.legalBases,
106
  fundingSchemes: data.fundingSchemes,
107
- ids: [],
108
  });
109
- })
110
- .catch((err) => console.error("Error fetching filters:", err));
111
  };
112
-
113
  interface RagResponse {
114
  answer: string;
115
  source_ids: string[];
116
  }
117
 
118
- // Handle chat submission
119
  const askChatbot = async () => {
120
- if (!question.trim() || loading) return;
121
-
122
- // Append user message
123
- setChatHistory((prev) => [...prev, { role: "user", content: question }]);
 
 
124
  setQuestion("");
125
- setLoading(true);
126
 
127
- // Add placeholder while generating
128
- setChatHistory((prev) => [...prev, { role: "assistant", content: "Generating answer..." }]);
 
 
 
129
 
130
  try {
131
- const response = await fetch("/api/rag", {
132
  method: "POST",
133
  headers: { "Content-Type": "application/json" },
134
  body: JSON.stringify({ query: question }),
135
  });
136
 
137
- const text = await response.text();
138
- if (!response.ok) {
139
- let detail = text;
140
- try { detail = JSON.parse(text).detail; } catch {}
141
- throw new Error(detail);
 
 
142
  }
143
 
144
- const result: RagResponse = JSON.parse(text);
145
- const sources = result.source_ids.length ? result.source_ids.join(", ") : "none";
146
- const assistantContent = `${result.answer}\n\nSources: ${sources}`;
 
 
147
 
148
- // Replace placeholder with actual answer
149
- setChatHistory((prev) => [...prev.slice(0, -1), { role: "assistant", content: assistantContent }]);
 
 
 
150
  } catch (err: any) {
151
- // Replace placeholder with error message
152
- setChatHistory((prev) => [
153
- ...prev.slice(0, -1),
154
- { role: "assistant", content: `Error: ${err.message}` },
 
 
 
155
  ]);
156
  } finally {
157
  setLoading(false);
 
158
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
159
  }
160
  };
161
 
162
- // Effects: refetch when filters/sorting/pagination change
163
  useEffect(() => {
 
164
  fetchProjects();
165
- }, [page, search, statusFilter, legalFilter, orgFilter, countryFilter, fundingSchemeFilter, idFilter, sortField, sortOrder]);
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  useEffect(() => {
 
168
  fetchStats(filters);
169
- }, [filters]);
170
-
171
- useEffect(() => {
172
- fetchAvailableFilters(filters);
173
- }, [filters]);
174
 
175
  return {
176
- selectedProject: projects[0] || null,
177
- dashboardProps: { stats, filters, setFilters, availableFilters },
 
 
 
 
 
178
  explorerProps: {
179
  projects,
180
  search,
@@ -191,28 +191,29 @@ export const useAppState = () => {
191
  setFundingSchemeFilter,
192
  idFilter,
193
  setIdFilter,
194
- sortField,
195
  setSortField,
196
- sortOrder,
197
  setSortOrder,
 
198
  page,
199
  setPage,
200
- setSelectedProject: () => {},
201
  question,
202
  setQuestion,
203
  chatHistory,
 
204
  askChatbot,
205
  loading,
206
- messagesEndRef,
207
  },
208
  detailsProps: {
209
- project: projects[0]!,
210
  question,
211
  setQuestion,
212
  chatHistory,
213
  askChatbot,
214
  loading,
215
- messagesEndRef,
216
- },
217
  };
218
  };
 
1
  import { useState, useEffect, useRef } from "react";
2
  import { debounce } from "lodash";
3
+ import type { Project, OrganizationLocation, FilterState, ChatMessage,AvailableFilters } from "./types";
 
 
 
 
 
 
4
 
5
  interface Stats {
6
  [key: string]: {
 
12
  type SortOrder = "asc" | "desc";
13
 
14
  export const useAppState = () => {
 
15
  const [projects, setProjects] = useState<Project[]>([]);
 
 
 
16
  const [search, setSearch] = useState<string>("");
17
  const [statusFilter, setStatusFilter] = useState<string>("");
18
+ const [page, setPage] = useState<number>(0);
19
+ const [question, setQuestion] = useState<string>("");
20
+ const [selectedProject, setSelectedProject] = useState<Project | null>(null);
 
 
 
 
 
 
 
 
21
  const [stats, setStats] = useState<Stats>({});
22
+ const [legalFilter, setLegalFilter] = useState('');
23
+ const [orgFilter, setOrgFilter] = useState('');
24
+ const [countryFilter, setCountryFilter] = useState('');
25
+ const [fundingSchemeFilter, setFundingSchemeFilter ] = useState('');
26
+ const [idFilter, setIdFilter] = useState('');
27
+ const [sortField, setSortField] = useState('');
28
+ const [sortOrder, setSortOrder] = useState<SortOrder>("asc");
29
  const [filters, setFilters] = useState<FilterState>({
30
  status: "",
31
  organization: "",
 
35
  maxYear: "2025",
36
  minFunding: "0",
37
  maxFunding: "10000000",
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"],
45
  organizations: [],
46
  countries: [],
47
  legalBases: [],
48
+ fundingSchemes:[],
49
+ ids:[]
50
  });
51
 
 
 
 
 
52
  const messagesEndRef = useRef<HTMLDivElement | null>(null);
53
 
 
54
  const fetchProjects = () => {
55
+ 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}`)
56
+ .then(res => res.json())
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  .then((data: Project[]) => setProjects(data))
58
+ .catch(console.error);
59
  };
60
 
61
+ const fetchStats = debounce((filters: FilterState) => {
62
+ const params = new URLSearchParams(filters);
63
+ fetch(`/api/stats?${params.toString()}`)
64
+ .then(res => res.json())
 
65
  .then((data: Stats) => setStats(data))
66
+ .catch(console.error);
67
  }, 500);
68
 
69
+ const fetchAvailableFilters = (filters: FilterState) => {
70
+ const params = new URLSearchParams(filters);
71
+ fetch(`/api/filters?${params.toString()}`)
72
+ .then(res => res.json())
73
+ .then((data: Omit<AvailableFilters, 'statuses'>) => {
 
74
  setAvailableFilters({
75
  statuses: ["SIGNED", "CLOSED", "TERMINATED", "UNKNOWN"],
76
  organizations: data.organizations,
77
  countries: data.countries,
78
  legalBases: data.legalBases,
79
  fundingSchemes: data.fundingSchemes,
80
+ ids: []
81
  });
82
+ });
 
83
  };
 
84
  interface RagResponse {
85
  answer: string;
86
  source_ids: string[];
87
  }
88
 
 
89
  const askChatbot = async () => {
90
+ if (!question.trim() || loading) return;
91
+ const newChat: ChatMessage[] = [
92
+ ...chatHistory,
93
+ { role: "user", content: question },
94
+ ];
95
+ setChatHistory(newChat);
96
  setQuestion("");
97
+ setLoading(true);
98
 
99
+ // 1) placeholder
100
+ setChatHistory((h) => [
101
+ ...h,
102
+ { role: "assistant", content: "Generating answer..." },
103
+ ]);
104
 
105
  try {
106
+ const res = await fetch("/api/rag", {
107
  method: "POST",
108
  headers: { "Content-Type": "application/json" },
109
  body: JSON.stringify({ query: question }),
110
  });
111
 
112
+ const text = await res.text();
113
+ if (!res.ok) {
114
+ let errDetail = text;
115
+ try {
116
+ errDetail = JSON.parse(text).detail;
117
+ } catch {}
118
+ throw new Error(errDetail);
119
  }
120
 
121
+ const data: RagResponse = JSON.parse(text);
122
+ const idList = data.source_ids.join(", ") || "none";
123
+ const assistantContent = `${data.answer}
124
+
125
+ The output was based on the following Project IDs: ${idList}`;
126
 
127
+ // 2) replace placeholder with real answer
128
+ setChatHistory((h) => [
129
+ ...h.slice(0, -1),
130
+ { role: "assistant", content: assistantContent },
131
+ ]);
132
  } catch (err: any) {
133
+ // replace placeholder with error message
134
+ setChatHistory((h) => [
135
+ ...h.slice(0, -1),
136
+ {
137
+ role: "assistant",
138
+ content: `Something went wrong: ${err.message}`,
139
+ },
140
  ]);
141
  } finally {
142
  setLoading(false);
143
+ // scroll to bottom
144
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
145
  }
146
  };
147
 
 
148
  useEffect(() => {
149
+ // If the user has typed something but it's too short, don't refetch
150
  fetchProjects();
151
+ }, [
152
+ page,
153
+ search,
154
+ statusFilter,
155
+ legalFilter,
156
+ orgFilter,
157
+ countryFilter,
158
+ fundingSchemeFilter,
159
+ idFilter,
160
+ sortField,
161
+ sortOrder,
162
+ ]);
163
 
164
  useEffect(() => {
165
+ console.log("Updated filters:", filters);
166
  fetchStats(filters);
167
+ }, [filters]);
168
+ useEffect(() => fetchAvailableFilters(filters), [filters]);
 
 
 
169
 
170
  return {
171
+ selectedProject,
172
+ dashboardProps: {
173
+ stats,
174
+ filters,
175
+ setFilters,
176
+ availableFilters
177
+ },
178
  explorerProps: {
179
  projects,
180
  search,
 
191
  setFundingSchemeFilter,
192
  idFilter,
193
  setIdFilter,
 
194
  setSortField,
195
+ sortField,
196
  setSortOrder,
197
+ sortOrder,
198
  page,
199
  setPage,
200
+ setSelectedProject,
201
  question,
202
  setQuestion,
203
  chatHistory,
204
+ setChatHistory,
205
  askChatbot,
206
  loading,
207
+ messagesEndRef
208
  },
209
  detailsProps: {
210
+ project: selectedProject!,
211
  question,
212
  setQuestion,
213
  chatHistory,
214
  askChatbot,
215
  loading,
216
+ messagesEndRef
217
+ }
218
  };
219
  };