StefanoDUrso commited on
Commit
6e5b27a
Β·
0 Parent(s):

first commit

Browse files
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ .env
2
+ .*
3
+ data/mp4
4
+ data/mp3
5
+
6
+ # Except .gitignore itself
7
+ !.gitignore
8
+
9
+ # Except .gitkeep files
10
+ !.gitkeep
__pycache__/config.cpython-312.pyc ADDED
Binary file (2.47 kB). View file
 
app.backup ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import time
3
+ import gradio as gr
4
+
5
+ from config import initialize, check_user
6
+
7
+
8
+ llm_manager, qdrant_manager = initialize()
9
+ if llm_manager is None:
10
+ print("Error: Failed to initialize configuration: llm_manager. Exiting application.", flush=True)
11
+ sys.exit(1)
12
+ if qdrant_manager is None:
13
+ print("Error: Failed to initialize configuration: qdrant_manager. Exiting application.", flush=True)
14
+ sys.exit(1)
15
+
16
+ def reset_textbox():
17
+ """Clears the textbox after sending a message."""
18
+ return gr.update(value="")
19
+
20
+ def slow_echo(message, history):
21
+ if history is None:
22
+ history = [] # Ensure history is initialized
23
+
24
+ # Append user message with role "user"
25
+ history.append({"role": "user", "content": message})
26
+
27
+ # Placeholder for assistant response
28
+ bot_entry = {"role": "assistant", "content": ""}
29
+ history.append(bot_entry)
30
+
31
+ response = "You typed: "
32
+
33
+ for i in range(len(message)):
34
+ time.sleep(0.05)
35
+ response += message[i]
36
+ bot_entry["content"] = response # Update assistant's response progressively
37
+ yield history # Yield updated history in the correct format
38
+
39
+ yield history # Final yield with full message
40
+
41
+ def llm_send_message(message, history):
42
+ if history is None:
43
+ history = []
44
+
45
+ # Append user message to history
46
+ history.append({"role": "user", "content": message})
47
+
48
+ yield history
49
+
50
+ # Placeholder for assistant response
51
+ bot_entry = {"role": "assistant", "content": ""}
52
+ history.append(bot_entry)
53
+
54
+ # Send message to LLM and stream response
55
+ response = ""
56
+ for chunk in llm_manager.send_message(message): # Streaming response
57
+ time.sleep(0.01) # Simulate gradual output
58
+ response += chunk
59
+ bot_entry["content"] = response # Update assistant response progressively
60
+ yield history # Yield updated history
61
+
62
+ yield history # Final yield
63
+
64
+ def authenticate(username, password):
65
+ if check_user(username, password):
66
+ print("πŸ”‘ Login successful!")
67
+ return gr.update(visible=False), gr.update(visible=True), gr.update(value="", visible=False) # Hide login, show chatbot, clear error
68
+ else:
69
+ print("❌ Incorrect username or password")
70
+ return gr.update(visible=True), gr.update(visible=True), gr.update(value="❌ Incorrect username or password", visible=True) # Show error
71
+
72
+ with gr.Blocks(fill_height=True) as demo:
73
+
74
+ with gr.Column(visible=True) as login_section:
75
+ gr.Markdown("### πŸ”’ Login Required")
76
+ username_input = gr.Textbox(label="Username")
77
+ password_input = gr.Textbox(label="Password", type="password")
78
+ login_button = gr.Button("Login")
79
+ error_message = gr.Text("", visible=False)
80
+
81
+ with gr.Column(visible=False) as chat_section:
82
+
83
+ chat_configuration = gr.Markdown("")
84
+
85
+ chat = gr.Chatbot(
86
+ label="Video Helper",
87
+ type="messages"
88
+ )
89
+
90
+ input = gr.Textbox(
91
+ label="Input",
92
+ placeholder="Type something here..."
93
+ )
94
+
95
+ stored_message = gr.State()
96
+
97
+ input.submit(
98
+ fn=lambda text: (text, ""),
99
+ inputs=[input],
100
+ outputs=[stored_message, input]
101
+ ).then(
102
+ #fn=llm_send_message,
103
+ fn=slow_echo,
104
+ inputs=[stored_message, chat],
105
+ outputs=chat
106
+ )
107
+
108
+ send_btn = gr.Button("Send")
109
+ send_btn.click(
110
+ fn=lambda text: (text, ""),
111
+ inputs=[input],
112
+ outputs=[stored_message, input]
113
+ ).then(
114
+ fn=llm_send_message,
115
+ inputs=[stored_message, chat],
116
+ outputs=chat
117
+ )
118
+
119
+ login_button.click(
120
+ authenticate,
121
+ [username_input, password_input],
122
+ [login_section, chat_section, error_message]
123
+ )
124
+
125
+ if __name__ == "__main__":
126
+ demo.launch()
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import time
3
+ import gradio as gr
4
+
5
+ from config import initialize, check_user
6
+
7
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
8
+
9
+ llm_manager, qdrant_manager = initialize()
10
+ if llm_manager is None or qdrant_manager is None:
11
+ print("❌ Failed to initialize. Exiting.")
12
+ sys.exit(1)
13
+
14
+ collection_name = "key_statistics"
15
+ if qdrant_manager.get_collection(collection_name):
16
+ llm_manager.set_qdrant_manager(qdrant_manager)
17
+ print(f"βœ… Collection '{collection_name}' loaded and linked to LLM.")
18
+ else:
19
+ print(f"⚠️ Collection '{collection_name}' not found or empty.")
20
+
21
+ def reset_textbox():
22
+ return gr.update(value="")
23
+
24
+ def show_spinner():
25
+ return gr.update(visible=True)
26
+
27
+ def hide_spinner():
28
+ return gr.update(visible=False)
29
+
30
+ def set_interactive_state(interactive: bool):
31
+ return (
32
+ gr.update(interactive=interactive), # input
33
+ gr.update(interactive=interactive) # send_btn
34
+ )
35
+
36
+ def _add_trust_icon(text, level):
37
+ icons = {
38
+ "high": "🟒", # alta affidabilità
39
+ "medium": "🟑", # simile ma non perfetto
40
+ "summary": "🟠", # riassunto usato
41
+ "low": "πŸ”΄", # basato solo sulla chat
42
+ "no_context": "βšͺ️"
43
+ }
44
+ label = {
45
+ "high": "Reliable",
46
+ "medium": "Moderate Similarity",
47
+ "summary": "Summary Used",
48
+ "low": "No Source Match",
49
+ "no_context": "No Context"
50
+ }
51
+
52
+ icon = icons.get(level, "βšͺ️")
53
+ tooltip = label.get(level, "Unknown")
54
+
55
+ # Prepend semaforo + etichetta (puoi anche usare HTML per Gradio futuro)
56
+ return f"{icon} *{tooltip}*\n\n{text}"
57
+
58
+ def get_summary(summary_type, history):
59
+ if history is None:
60
+ history = []
61
+
62
+ if summary_type == "map":
63
+ content = llm_manager.get_map_summary()
64
+ label = "🧾 Map Summary"
65
+ elif summary_type == "stuff":
66
+ content = llm_manager.get_stuff_summary()
67
+ label = "πŸ“š Stuff Summary"
68
+ else:
69
+ content = None
70
+ label = "❓ Unknown Summary"
71
+
72
+ if content:
73
+ history.append({"role": "assistant", "content": f"{label}\n\n{content}"})
74
+ else:
75
+ history.append({"role": "assistant", "content": f"⚠️ No {label.lower()} available."})
76
+
77
+ return history
78
+
79
+ def send_chat_message(message, history):
80
+ if history is None:
81
+ history = []
82
+
83
+ history.append({"role": "user", "content": message})
84
+ bot_entry = {"role": "assistant", "content": ""}
85
+ history.append(bot_entry)
86
+
87
+ response = ""
88
+ context_level = "high" # default in caso non venga restituito
89
+
90
+ # Supporta nuova struttura restituita da stream_message
91
+ stream = llm_manager.stream_message(message, contextualize=True)
92
+
93
+ for chunk in stream:
94
+ if isinstance(chunk, dict): # nuova versione con chunk + livello
95
+ response += chunk["content"]
96
+ context_level = chunk.get("context_level", "high")
97
+ else: # retrocompatibilitΓ 
98
+ response += chunk
99
+
100
+ # aggiorna contenuto in tempo reale
101
+ bot_entry["content"] = _add_trust_icon(response, context_level)
102
+ yield history
103
+
104
+ yield history
105
+
106
+ def authenticate(username, password):
107
+ if check_user(username, password):
108
+ llm_manager.initialize_conversation()
109
+
110
+ # Solo messaggi assistant (il primo dovrebbe essere l’initial summary)
111
+ assistant_msgs = [
112
+ {"role": "assistant", "content": msg.content}
113
+ for msg in llm_manager.messages if msg.type == "ai"
114
+ ]
115
+
116
+ return (
117
+ gr.update(visible=False), # Hide login
118
+ gr.update(visible=True), # Show chat
119
+ gr.update(value="", visible=False), # Clear error
120
+ assistant_msgs # Initial chat history
121
+ )
122
+ else:
123
+ return (
124
+ gr.update(visible=True),
125
+ gr.update(visible=False),
126
+ gr.update(value="❌ Incorrect username or password", visible=True),
127
+ []
128
+ )
129
+
130
+ with gr.Blocks(fill_height=True) as demo:
131
+
132
+ with gr.Column(visible=True) as login_section:
133
+ gr.Markdown("### πŸ”’ Login Required")
134
+ username_input = gr.Textbox(label="Username")
135
+ password_input = gr.Textbox(label="Password", type="password")
136
+ login_button = gr.Button("Login")
137
+ error_message = gr.Text("", visible=False)
138
+
139
+ with gr.Column(visible=False) as chat_section:
140
+
141
+ chat_configuration = gr.Markdown("")
142
+
143
+ spinner = gr.Markdown("⏳ Sto pensando...", visible=False)
144
+
145
+ with gr.Row():
146
+ map_btn = gr.Button("🧾 Map Summary")
147
+ stuff_btn = gr.Button("πŸ“š Stuff Summary")
148
+
149
+ chat = gr.Chatbot(
150
+ label="Video Helper",
151
+ type="messages"
152
+ )
153
+
154
+ input = gr.Textbox(
155
+ label="Input",
156
+ placeholder="Type something here..."
157
+ )
158
+ send_btn = gr.Button("Send")
159
+
160
+ stored_message = gr.State()
161
+ chat_history = gr.State(value=[])
162
+
163
+ map_btn.click(
164
+ fn=lambda history: get_summary("map", history),
165
+ inputs=[chat_history],
166
+ outputs=[chat]
167
+ )
168
+
169
+ stuff_btn.click(
170
+ fn=lambda history: get_summary("stuff", history),
171
+ inputs=[chat_history],
172
+ outputs=[chat]
173
+ )
174
+
175
+ input.submit(
176
+ fn=lambda x: (x, ""),
177
+ inputs=input,
178
+ outputs=[stored_message, input]
179
+ ).then(
180
+ fn=show_spinner,
181
+ outputs=[spinner]
182
+ ).then(
183
+ fn=send_chat_message,
184
+ inputs=[stored_message, chat_history],
185
+ outputs=chat
186
+ ).then(
187
+ fn=hide_spinner,
188
+ outputs=[spinner]
189
+ )
190
+
191
+ send_btn.click(
192
+ fn=lambda x: (x, ""),
193
+ inputs=input,
194
+ outputs=[stored_message, input]
195
+ ).then(
196
+ fn=show_spinner,
197
+ outputs=[spinner]
198
+ ).then(
199
+ fn=send_chat_message,
200
+ inputs=[stored_message, chat_history],
201
+ outputs=chat
202
+ ).then(
203
+ fn=hide_spinner,
204
+ outputs=[spinner]
205
+ )
206
+
207
+ login_button.click(
208
+ authenticate,
209
+ [username_input, password_input],
210
+ [login_section, chat_section, error_message, chat_history]
211
+ ).then(
212
+ fn=lambda history: history,
213
+ inputs=[chat_history],
214
+ outputs=[chat]
215
+ )
216
+
217
+ if __name__ == "__main__":
218
+ demo.launch()
config.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from utilities.llm.LlmManager import LlmManager
4
+ from utilities.vectorstore.QdrantLangchainManager import QdrantLangchainManager
5
+ from langchain_openai import ChatOpenAI
6
+ from sentence_transformers import CrossEncoder
7
+
8
+ MODEL = "gpt-4o-mini"
9
+ LANGUAGE = "en"
10
+
11
+ crossencoder_model = None
12
+ credentials = {}
13
+
14
+ def check_user(username, password):
15
+ if username in credentials and credentials[username] == password:
16
+ print("πŸ”‘ Login successful!")
17
+ return True
18
+ else:
19
+ print("❌ Incorrect username or password")
20
+ return False
21
+
22
+ def initialize(app=None):
23
+ global crossencoder_model
24
+
25
+ try:
26
+ load_dotenv()
27
+
28
+ if crossencoder_model is None:
29
+ print("Loading CrossEncoder model...")
30
+ crossencoder_model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
31
+ print("CrossEncoder model loaded!")
32
+
33
+ credentials[os.getenv("USERNAME")] = os.getenv("PASSWORD")
34
+ i = 1
35
+ while os.getenv(f"USERNAME_{i}") and os.getenv(f"PASSWORD_{i}"):
36
+ credentials[os.getenv(f"USERNAME_{i}")] = os.getenv(f"PASSWORD_{i}")
37
+ i += 1
38
+
39
+ qdrant_manager = QdrantLangchainManager(
40
+ qdrant_url=os.getenv("QDRANT_URL"),
41
+ qdrant_api_key=os.getenv("QDRANT_API_KEY"),
42
+ crossencoder_model=crossencoder_model
43
+ )
44
+
45
+ llm_manager = LlmManager(
46
+ qdrant_manager=qdrant_manager,
47
+ model=MODEL,
48
+ language=LANGUAGE
49
+ )
50
+
51
+ return llm_manager, qdrant_manager
52
+
53
+ except Exception as e:
54
+ print("Error initializing configuration:", e, flush=True)
55
+ return None
56
+
57
+
58
+ # Qdrant
59
+ # try:
60
+ # qdrant_manager = QdrantLangchainManager(
61
+ # qdrant_url=os.getenv("QDRANT_URL"),
62
+ # qdrant_api_key=os.getenv("QDRANT_API_KEY"),
63
+ # llm=ChatOpenAI(model="gpt-4o-mini", streaming=True),
64
+ # crossencoder_model=crossencoder_model
65
+ # )
66
+ # config['qdrant_manager'] = qdrant_manager
67
+ # config['qdrant_connected'] = True
68
+ # except Exception as e:
69
+ # config['qdrant_connected'] = False
70
+ # print(f"Error connecting to Qdrant: {e}", flush=True)
71
+
72
+ return config
data/txt/Key statisitcs startups.txt ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
2
+
3
+ Hi everyone and welcome to this video lecture from the Entrepreneurial Literacy Initiative. I am Martti Wask and today we're going to present to you some brief key statistics about entrepreneurship. Before we go into the mud, let me give you some brief definition of what I understand about a startup.
4
+
5
+ So to some extent startups are just like any other business, right, or more specifically small businesses because they're small at the beginning. But there are three characteristics that make them unique. Startups are young, high growth orientated businesses.
6
+
7
+ Look at these two GIFs. So on the left hand side we've got the small or the traditional business, right. You see how the kid is turning into a woman or even an elder woman at a very low pass.
8
+
9
+ In contrast the startup is making it up to the adult life very suddenly. A second feature or a third feature that is characteristic of startups is that they are disruptive in nature. They're here to disrupt traditional industries.
10
+
11
+ Think about this small business about two or three taxes, right. And then Uber comes or Lyft comes and they put some technology to make everything much more efficient from the customer standpoint. So they just break the traditional industries who are just left with policies and regulations to get rid of these new innovations.
12
+
13
+ Same thing for CDs which is something that you may not have used in your life but perhaps you've seen it at home with your parents. So there used to be these CD stores in the cities where you just went there and buy some CDs. Now the music industry is much more on streaming almost for free or these freemium business models and going into concerts.
14
+
15
+ Now let's jump on to some statistics about startups. We're going to start from very macro type of things and just I don't want to call it startup dynamics but rather firm dynamics because this is the firm births and firm deaths from Eurostat in year 2019. So this is just to give you an idea of the number of companies that are born every year which is more than a quarter of a million for bigger countries such as Spain, Italy, France, Germany, Poland and Turkey.
16
+
17
+ And as for firm deaths we've got in those same countries something north of 130 000 companies that die. So there is a positive net effect of more and more businesses being created every year. You can check out more on these statistics at the European Commission website.
18
+
19
+ Some other and perhaps more relevant to us statistics are the number of high growth firms in each country. If you are increasing your employee base by 10 percent or higher you're going to be considered as per Eurostat a high growth company in the year 2018 which is this specific statistic. And what we can see is that the number of firms that are considered high growth firms are a little over 15 percent in countries such as Greece, Netherlands, Spain and Ireland.
20
+
21
+ Another important statistic is the context. So to become an entrepreneur it is fundamental the context the ecosystem in which you live. And the Global Entrepreneurship Monitor the GEM report which is very widely known it's a worldwide survey of adult population regarding entrepreneurship topics.
22
+
23
+ And so it essentially tracks four different dimensions. First the attitudes and perceptions of adults towards entrepreneurship, second the motivational aspects towards entrepreneurship, then the actual activity of entrepreneurialism and the impact of this entrepreneurship. As per the Netherlands we observe how 60.8 percent of the adults who are surveyed consider to know someone who has started a new business recently.
24
+
25
+ Another remarkable statistic is that 82.9 percent of Dutch people consider to be very easy or easy to start a business. And this makes the Netherlands to rank number three in the list of 43 countries surveyed. As per the entrepreneurial intentions which is the question of whether people are expected to become entrepreneurs in the next three years, 13.1 percent of adults say so in their responses to the question.
26
+
27
+ And this ranks the Netherlands number 28 out of 43. Some other important statistics are the total early stage entrepreneurial activity. This is a statistic that is created on purpose by the GEM report and it essentially tells you the number of adults who are nascent entrepreneurs or who recently became business owners.
28
+
29
+ In the case of the Netherlands 11.5 percent of the surveyed adults did so. So there's a tear ratio of 11.5 percent. Seven percent are established business owners so they became business owners longer than say 42 months I believe it is.
30
+
31
+ And another bucket that is relevant for the context of entrepreneurship in the Netherlands is the motivations. So Dutch people become entrepreneurs to make a difference and to build great wealth which means a lot about the optimism of Dutch adults. So these are kind of the top two reasons by which Dutch people Dutch individuals become entrepreneurs.
32
+
33
+ Another important dimension that the GEM report tracks is the entrepreneurship impact. So 1.5 percent of Dutch adults consider to be giving employment to more than six people within the next five years or in five years time. And two percent consider to be selling their products and services to an international base.
34
+
35
+ International meaning the revenues of the firm having 25 percent or more coming from international markets. And these ranks the Netherlands number 11 out of a list of 43 surveyed countries. In comparison we've got also the Swedish statistics.
36
+
37
+ And so in comparison to the Netherlands a little less people know someone who has started a business only 48.5 percent as opposed to 60.8 percent for the Netherlands. 80 percent 80.1 percent considers that it is easy to start a business and 8.3 percent has entrepreneurial intentions. So a little lower than the Netherlands in all these variables.
38
+
39
+ As per the motivation to start a business these are the two top reasons again to make a difference and to build great wealth. This is similar to the Dutch case. For the entrepreneurship impact job expectations just 1.1 percent of adults consider to be providing employment to more than six people in five years time.
40
+
41
+ And only 1.4 percent or 1.4 percent of adults consider to be selling their products and business to international markets as opposed to two percent for the Netherlands. In the slide deck I provide some other countries that you can check if you like. But interestingly and talking again about context or ecosystems the Global Startup Ecosystem Record 2021 record offers a ranking of entrepreneurial haves.
42
+
43
+ So where there is the ecosystem or context to start a business. Of course no surprise that Silicon Valley comes in number one. Then New York City and London and Beijing being number four.
44
+
45
+ So the Chinese hubs are hitting strong in this rankings more and more. Then we have some other US cities like Boston or Los Angeles. Israel is number seven.
46
+
47
+ Shanghai and other Chinese have their Tokyo and Seattle making it to the top 10. Some other important cities European cities aside from London and if you will Tel Aviv are Paris and Amsterdam or the Delta region let's say Utrecht Rotterdam etc. Then we've got Stockholm tying in number 17 with Singapore.
48
+
49
+ The Swedish ecosystem is very noteworthy. There are some very recent a billion dollar startups such as Spotify or Klarna which are very remarkable cases. And then we've got Berlin making to the bottom of the top 25 list for the best entrepreneurial ecosystems.
50
+
51
+ Now if we go on to firm survival just to make you an idea of you starting up a business what are your chances to make it to the next year. So Eurostat here provides statistics of survival rates for one year the light blue, three years the orange ones and five years the dark blue. So these are for 2018 and if we take into account the dark blue we observe that more or less they go on to 40 to 60 percent of survival rate meaning that you have let's say make an average of 50 percent one out of two businesses wind down after five years of operations.
52
+
53
+ So you have a 50 percent chances of success if you will. Finally and now going a little bit more into the micro level into the firm level and analysing startup profitability. So most of startups are not profitable right from the beginning.
54
+
55
+ So here you have on the right side the pre-seed and so the different life cycle of the firm pre-seed and startups. You can see an orange coloured and red coloured that they are not profitable yet. So the orange colour is that they expect to break even they expect to be profitable in more than two years.
56
+
57
+ So still a long road to profitability and the red colour is they expect to become profitable to break even in less than two years. So essentially in the pre-seed and the startup stage is very common to be unprofitable and once you go a little bit more on to more mature firms the profitability turns into something more normal so to speak. So this lack of profitability means that the startups really require of external financing to get their operations going and I want to give you because financing is kind of a key topic a hot topic when we talk about startups just a very brief idea of what we're talking about when we're dealing with financing in the startup world.
58
+
59
+ So essentially you have four ways in which you can finance the business revenue which is the preferred one it's essentially your customers are providing you the money to run your operations and this is by far the best way to finance your business. Then you've got the debt that is a bit delicate because banks or whoever who lends you money can lend you the money but sometimes they ask you for personal guarantees. So think about it a startup you just created the startup there's no track record there are no assets that you can use as collateral there so the debt holders will really ask you for guarantees.
60
+
61
+ Watch out what type of guarantees you provide. I've seen entrepreneurs actually bucking up the money that they use for the business with their personal wealth and this is extremely dangerous. If the company eventually goes south then you will have to pay with your assets be it a car be it a motorbike be it your future salary to the debt holders and so that's why we created the limited liability company to limit the liability of the entrepreneur in this case.
62
+
63
+ So try to avoid as much as you can backing up some money into the into the business with your personal wealth and be aware that a business sucks money burns cash very easily just by having a couple of employees only two paying some office rents some supplies some marketing ads that you want to do on Facebook, Google, Instagram etc. This all can easily eat you up to a hundred a hundred fifty thousand euros in a year. So you really have to watch out what you back out with your personal wealth.
64
+
65
+ So having to return a hundred fifty thousand to the bank right when you're 25 or 30 is not the best thing you want to do with a death company. Third you have the grants the grants are eventually like free money so it's they're wonderful but the problem is time with there you cannot count on them to run your operations they're going to come in one year time they're going to come in 1.5 year you will always be waiting for that ministry signature that needs to be given before the money can be released so it's really tiring long processes a lot of justifications they're wonderful because it's free financing and you always need to do especially if some there are some lines that really match your startup or the innovation that you're trying to pursue but you cannot count on them. And finally you're left with equity which is the traditional type of financing a startup that aims to scale up very fast and so with the equity you have different instruments or you have different actors at the different stages of the firm you can start at the pre-seed and seed stage with angels business angels well you really start with the family and the friends right once you've used up those funds then you have to go with angels business angels which are the first type of professional investor that you're going to get for some venture capital funds they like to go and really have an eye at pre-seed and the seed stage even though they're more they're more acting when when there's some sort of growth there's some sort of metrics going on there is when you can attract venture capital and eventually if there's strong growth the venture capital will sell their stake into the private equity actors so the expansion or the maturity and the private equity together with the venture capital will take the business either to an m&a to a big m&a transaction or to uh to ipo if successful or to another private equity fund that is bigger than the one you just received so there are several exit strategies for them let me finally give you some personal reflections on what it takes to be an entrepreneur and if you're lucky enough a successful entrepreneur first thing that i would advise you to do try to focus on your customer try to focus on your product and how to reach the customer make the customer love your product because if you have some traction then things come and things i mean financing i mean uh a lower marketing expenses because you're going to get known through your customer base i mean things will happen if the customer loves the product so the very first thing to do is to build up a nice product it doesn't need to be developed to a great extent but some product that really tackles some issue some societal issue and therefore it's bought right that's the best thing to do and don't worry about the tricks and about the abc of how to raise financing etc if you have a steep revenue line things will come to your company second reflect about the difficult side of being a successful entrepreneur if you're lucky enough to get into a successful company you are the dad of the business which means that everyone expects a lot from you you cannot show any weakness you have to be there 24 7 so if you go on holidays and there's any fire any alarm there it's going to be you that have to wipe that out also you can get easily tired with the project so once you've done two three four years of the same stuff you can get tired of the project but guess what you cannot just abandon you have an employee base of 10 15 people say 40 50 or hundreds if you're successful enough and so they count on you you are the leader and this is a burden that not so many people think when they start into entrepreneurship but you need to think about that you're going to be the leader and it's not that easy to jump down from the bandwagon especially if you have venture capitals on board because then there will be ownership contracts there will be some contractual ties that will tie you to the business for several years and you cannot undo those contracts so there are some things that you need to think about because before you get an into a entrepreneurship of course if you don't get into venture capital you just start your nice idea that you want to scale up you have this growth adventure ahead of you or you expect to have it and eventually it doesn't work you can always transition into a small business and make a very good and decent living out of that have your work-life balance there so it's not that one thing goes without the other you can always transition from having a startup idea and then eventually transitioning to small business because all that traction that you expected is not there or the opposite you can start as a small business I've known some consultants that eventually think about how to provide consulting on a high scale level and then from this consulting a small business they turn into startup so you can actually go the two ways but think reflect before beginning with an entrepreneurial career there are some contractual ties you can get tired of the project and this is something that it commonly happens I would say and no one talks it openly it doesn't make it to the headlines for no reason entrepreneurs have high degrees of depression and mental problems so there is a study by professors at Berkeley showing that entrepreneurs have a 30 percent more probability to be depressed as opposed to other adults another tip do not hurry to start an entrepreneurial career you don't have to start right when you're 20 21 in fact you will see in future modules that you will increase the odds of success if you wait a little longer so unless you have the next Facebook and you experience that your side projects that you're studying as a student as a graduate student or whatever or early employee traction a lot such as Facebook etc etc then is when you're going to face a decision to make am I turning an entrepreneur right now at the age of 20 or do I wait a little longer then it's it's kind of a live decision but before if you're thinking about an idea do not hesitate to take some some working experience at good companies if it's a startup company perhaps even better and also just a final note as entrepreneur and especially if you are successful enough you're going to be surrounded by a lot of people by a lot of people that will tell you how charming they are and how good they've been in advising that other company in being a mentor of that other company by the way in exchange of one percent or two percent of the company so just trust your intuition this is nothing new you've been dealing with people for all your life so try to smell how the person him or herself is before engaging further with him or her there are tonnes of people that will try to convince you that it's worth engaging further with them watch out them because there are many well I hope you enjoyed the video lecture just nothing else stay tuned for the next modules of A.L.I. that will be as exciting as this video lecture bye-bye
66
+
67
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
data/txt/Risk and Return part 1 v1.txt ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
2
+
3
+ Hello everyone and welcome to this video lecture from the Entrepreneurship Literacy Initiative. I am Marco Darin and today we are going to look at risk and return in entrepreneurship. This is actually the first of two video lectures on the topic that will look at both the facts and interpretation of how entrepreneurs think and act about the risk and the returns that they can reap from their entrepreneurial endeavours.
4
+
5
+ Now let's start with something that you have already seen in a previous video lecture with Marti. The fact number one that you have looked at is the high default rate which is typically over 50 percent in the first few years after the creation of a company. And this means that companies are often left stranded and don't yield any return to their initiators, to their founders.
6
+
7
+ And this is a picture which tries to convey this image of total dismay and failure. Second fact is that at the same time while entrepreneurial companies often fail, they have a high potential for achieving extremely high returns. And sometimes these are really huge and in fact as an illustration we can think of the Elon Musk IPO of Tesla a few years ago and we know that the sky is the limit even though SpaceX is still very far away from its IPO.
8
+
9
+ Now what are the risks and returns to entrepreneurship really? These two facts, high default rates and high potential, seem to be at odds among themselves because it's very risky and often it doesn't yield much but it could even be a big lottery ticket. Now let's look more at what economic and finance research has dealt and and digged out from many different studies. Now the first thing that we want to bring you is some key facts.
10
+
11
+ And here is what we are going to see today. In the second lecture we'll try to make sense of these facts and provide some deeper interpretation to think how to act about it. Now let me give you a preview today.
12
+
13
+ What are these key results? The first is that there is a high dispersion of returns across different ventures which means that some of them are extremely unsuccessful, typically fail, typically fail quickly, sometimes fail painfully after quite a few years. But at the same time on the other hand, which means on the other extreme of the distribution, we see that some ventures achieve very high returns. Now the second key result is that this dispersion is then compounded in the fact that the average entrepreneur does not really earn a high return and they return enough to especially compensate for the risk of failure which is very high.
14
+
15
+ It's a little bit like trying to get through a fire and knowing that you are easily burnt a little bit and so the typical fire worker is not going to enjoy it very much. Now this is something which is documented by many studies across different periods and countries. Don't worry, I'm not going to bore you with a full review of this, but I'll just like to look at three of them which bring out some very interesting evidence.
16
+
17
+ Now let's start with this study published in the Journal of Economic Perspectives which tries to go back to what it calls the roots of entrepreneurship, trying to understand why entrepreneurs seemingly take actions which don't square very well with our view of what rational agents often do. Now this, which is basically a survey of previous studies, provides this very interesting picture and the picture shows the pattern of failures and successes across about half a million firms founded in the United States in 1996. So it looks at a cohort, it's a full population of these companies and then it looks at sales six years afterwards in 2002 and now the y-axis provides the share of firms which experience a certain outcome and this outcome in terms of sales is put as buckets in the x-axis.
18
+
19
+ Now what do we see here? It's very interesting because if you notice almost exactly 50 percent of the companies fail, so they experience zero sales. Failure is extremely common, it's the most common outcome. But then if we go to the next buckets, what do we see? Well we see that another about 10 percent achieves very low sales, less than a hundred thousand dollars and that may seem a large sum to you but if we think that these are sales, not profits, they are after all not so much for a business after six years of its creation.
20
+
21
+ Probably they just barely helped the founder make it. Then if we go further in sales, we see that a number of another 30-20 percent of firms achieves returns which could be kind of decent if it's a small corner shop but they're not very good if it's a more ambitious business and these are kind of between 100 and 500 thousand and they can go up to a million. And so we see that there are also about eight percent of companies which reach very good sales, one to five millions.
22
+
23
+ But then the other very interesting fact is that as we go to really really good outcomes, let's say above five millions, between five and ten, we get just two percent, about two percent of these companies. And if we think about the big earners, more than ten millions, and again these are sales not profits, well we have about one percent. So this is basically what I was saying before, putting together the two facts.
24
+
25
+ First that failure is extremely common and that these small results are the norm. Second, that there is a wide dispersion because if we could really kind of put not just buckets but points here, we would go extremely far away on the y-axis and we see that this is a very small number of firms which really make it okay. Now this is clearly just the observation about companies founded in one year, 1996, so one could think that this is kind of a non-representative sample.
26
+
27
+ No, it is because many other studies find very similar patterns across different times and countries. So this is really a key feature of entrepreneurship. The second study looks a little bit more closer at US private companies and it's a study by Tobias Moskowitz and Annette Bissing-Jorgensen, which are two very successful financial economists, which looks at what they call the private equity premium puzzle.
28
+
29
+ So they say that entrepreneurs invest in their companies which are private, mostly remain private, just a very very tiny number goes public, and so this is private equity in that sense, equity which is privately held. And where does this puzzle stand? The puzzle stands in the following facts. So they look at a representative sample of 4,000 US households which own a business and they follow it through a decade from 1989 through 1999.
30
+
31
+ Interestingly, first of all, these households hold the vast majority of their companies, so they get somebody else to invest in the company. In a few cases, it's a business angel or a venture capitalist or another company or sometimes even the state. Most other cases, it's other people like friends, families or acquaintances.
32
+
33
+ And therefore, they hold most of their wealth into this company, which could be their corner shop or it could be a small business or could be a more ambitious venture. A key feature is here that these companies are highly liquid, which means that it's very difficult to sell this equity. So if for some reason you need to sell part of your holdings, it's very difficult because it's a private company and it's not transparent and very few people would have the guts to invest in something which is so risky because they find it very difficult to understand it, to know it and to run it.
34
+
35
+ And so beyond one key point of this paper is that beyond failure risk, there is also the risk of illiquidity that you need to get some money because maybe you need it for medical reasons or for sending your kids to school or for moving to a different location and you find it very difficult. So that's another dimension which entrepreneurs should take into account and we are going to see this more into the second video lecture about this topic. Now, having introduced illiquidity risk, they find that the return to private equity, equity held in private businesses is not statistically higher than that to public equity.
36
+
37
+ So engaging in an entrepreneurial venture or being an employee, developing your own human capital, getting a good job and investing your savings in public equity in the stock market, you have very similar returns. And these are similar in level but also in volatility. And so the puzzle here is that these two authors estimate a loss of about 10% per year if adjusted for failure and illiquidity risk to holders of this equity, to entrepreneurs.
38
+
39
+ And clearly, this is quite puzzling also because it's not a mistake. These people keep investing in their company over the decade over which they are followed by this survey. And so this to economists brings a puzzle.
40
+
41
+ Why do people behave like this? Now, a third paper adds another piece to the puzzle. And this is a paper by Bob Hall and his wife and non-academic economist, Susan Woodward. Bob Hall has been the doyen of American macroeconomists for several decades.
42
+
43
+ And he has been the chair of this committee which declared the start and the end of recessions in the US. And so he knows a little bit about kind of how the vagaries of the economy, the downturns and upturns work. And he applied this knowledge by looking at a sample of a venture-backed company.
44
+
45
+ So this is not just the whole population of private businesses, but it's the subset which is funded by venture capital first, which in a way we could think is different from the others because these are more ambitious companies. And they also call their contribution, their study, the burden of non-diversifiable risk of entrepreneurship. And they also focus on the fact that there is something more to failure risk and illiquidity risk.
46
+
47
+ And in particular, they look at 22,000 VC-backed US businesses between about 1987 and 2007. And what do they find? Is that entrepreneurs are highly diversified. This is true both for the general population of entrepreneurs and for venture-backed entrepreneurs who put not only all their financial capital, but also all their human capital, their reputation into play into one single business.
48
+
49
+ Now, they also find that there is, and that's an interesting fact, a negative correlation between exit value and time to exit. So in most cases, the most successful exits, the most successful returns to entrepreneurs and investors are made when a company is exiting through an acquisition or an IPO in a very short time. So in that sense, being fast is being good.
50
+
51
+ And last but not least, and very important for our attempt to understand risk and reward, is that about three quarters, 75% of these entrepreneurs reap no rewards. Part of it is done to the type of contrast that venture capitalists give when they provide financing, and venture capitalists are very savvy investors. So they are able to make sure that they recoup most of their investment, even if the company doesn't do very well, which means that in most cases, if the company doesn't do extremely well, the entrepreneurs don't get any rewards.
52
+
53
+ And again, this is puzzling because why do these very smart, well-equipped, very educated people engage into activities which are highly undiversified? Okay, so they get a lot of risk, a lot of failure and illiquidity risk. And unless they really do very well, they will not gain much in terms of financial rewards. So this brings us to our preliminary conclusion, preliminary because it's the first of two video lectures, which is to bring up these facts.
54
+
55
+ Entrepreneurship is highly risky, it has strong variation in returns, some people do extremely well, most people don't do well at all. There is substantive failure risk and holding equity in your company is highly liquid because it's difficult to sell it to outsiders. And if we compute returns and adjust them for these two types of risk, especially, then the results are disappointing.
56
+
57
+ Still, entrepreneurs keep opening businesses. Why? This is an interesting puzzle that all these empirical set of studies, literature brings us. And so I'll leave you with a little bit of curiosity.
58
+
59
+ And we'll pick this up again in the next video lecture, where we provide some additional facts and some conceptual framework to understand what is going on and make sense of this appearing puzzle. Entrepreneurs certainly are not stupid. They're not reckless people.
60
+
61
+ They mostly know what they're doing, but they seem to defy standard economic views. Why? Well, stay tuned and in the next video lecture, we'll explain you why. Thank you and see you soon.
62
+
63
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
data/txt/Risk and Return part 2 v1.txt ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
2
+
3
+ Hello everyone and welcome to this video lecture from the Entrepreneurship Literacy Initiative. I am Marco Darin and today we are going to talk about risk and return in entrepreneurship. This is the second part in a two video lecture series which tries to understand the risk and return of entrepreneurial activities.
4
+
5
+ Now let's pick up where we left last time. So we have shown that entrepreneurship is a highly risky business and it exhibits a strong variation in returns with most ventures fading miserably and a few of them, very few of them doing well or extremely well. So failure risk and liquidity risk are particularly high in this realm and we have shown that risk adjusted returns to entrepreneurship seem to disappoint.
6
+
7
+ And so we cannot think really that entrepreneurs are solely driven by financial returns because that is not the case. Still, entrepreneurs keep opening businesses. Why? Today we are going to explain.
8
+
9
+ So there are three main explanations that could reconcile the evidence and our economic intuition. The first one is that entrepreneurs are different. So they have different risk preferences than the general population and in particular they are more risk tolerant.
10
+
11
+ They engage in entrepreneurship because they can bear more risk. Second potential explanation is that there are not only financial returns but other types of private benefits in the sense that entrepreneurs receive some benefit which is private, which is not financially in monetary terms and therefore they are compensated with more than just financial returns. Third potential explanation is that entrepreneurs are different because they are overconfident.
12
+
13
+ They are too optimistic about the future of their ventures. Now let's see a little bit more of concepts and evidence about each of these potential explanations. First of all, let's look at risk preferences.
14
+
15
+ Now to understand well the importance of risk preferences, let me introduce the risk-return frontier, which is a key tool in financial economics. Now consider the following example. A project is offered to you which has an expected return of 10% with a risk of 20%.
16
+
17
+ As you can see here, the risk means that the return will fall between 9 and 11%. It's 20% of the 10% which is expected. Now any point in this segment between 9 and 11% is possible and this is what is offered to you.
18
+
19
+ Now consider an alternative. Consider now that risk goes to 30% and so the expected return is always 10% but now the possible outcome has a wider range between 8.5 and 11.5 which is 3 percentage points now. Now let me give you a few seconds to think.
20
+
21
+ Which one would you prefer between these two projects? Think about it. Make up your mind. Okay, let's see.
22
+
23
+ Clearly there is no right or wrong answer. It depends on your preferences towards risk and a lot of empirical evidence tells us that most people, most individuals behave in a risk-averse way so they need to be compensated to bear more risk. So if the risk moves from 20 to 30% of the expected return, then you need to be compensated because there is a possibility, yes, that you can do better, 11.5 instead of 11, for example, but you can also do worse, 8.7 instead of 9.5. So the extremes are pushed out, the boundaries are pushed out, and so that is the fact that there is more risk.
24
+
25
+ And most people don't like this. So a typical person with a risk of 30% would not be willing to accept this second project, would stick with the first one or would accept a higher risk if the expected return is higher. In this case, 12%, it's just an example.
26
+
27
+ So with 20%, 12%, sorry, the 30% risk is equivalent to 3.6 percentage points, and so the range of possible values goes from 10.2 to 13.8, so 1.8% above or below 12%, which is the expected value. Some people may be even more risk averse and require 13% or 14% of compensation, pushing the boundaries further away. Now, why is this important? This is important because we can also express this through what we call the risk-return frontier, which is simply the set of all points which link the accepted return to different levels of risk.
28
+
29
+ In this case, we see the two points that we have seen before, and we can think that they are on a concave trajectory. This could also be linear or convex. There is a lot of work on this, but the essential point for us is that more risk must be compensated by a higher expected return.
30
+
31
+ That's what we need to know to understand the entrepreneurial choice. Now, what are the data about this? Well, a conjecture is that, as I said before, entrepreneurs are more risk tolerant than the general population, so they're willing to start riskier businesses than the average person. Now, this is confirmed by quite some literature, in particular by two studies that we briefly look at now.
32
+
33
+ The first study is by Bob Hall and Susan Woodward. It's the one that you have seen in the first video lecture. What do they do in the second part of their article is to consider what they call the certainty equivalent of entrepreneurial opportunity.
34
+
35
+ For each project in those 22,000 venture-backed startups that they consider, they estimate how much the entrepreneur, based on the returns that they receive in the actual data, would be willing to pay to own the project. Clearly, if this is a positive sum, it's the value of the project to the entrepreneur. If it is a negative sum, this is how much the entrepreneur needs to be compensated to undertake the project.
36
+
37
+ They do this for a number of different parameters. In the first column here, we see different degrees of risk aversion. Then for each degree of risk aversion, they see what is the yearly income of the entrepreneur per year in his or her previous job.
38
+
39
+ Then they assess, they look at how much wealth accumulated the entrepreneur has at the beginning of the project. Why? That's very interesting. You will probably wonder why.
40
+
41
+ Well, we know that risk aversion depends a lot on our wealth. Wealthier people tend to be more risk tolerant. Why? Because they have a cushion, so they can take more risk than somebody who needs just from hand to mouth.
42
+
43
+ Therefore, here they consider four possible different wealths. 100,000, 1 million, 5 million, or 20 million at the beginning of the venture. Now let's see how these combinations work out.
44
+
45
+ We see that for very low risk aversion, this is a relative coefficient, basically all entrepreneurs are willing to pay some money to own their project. This clearly is something which decreases with their pre-tax income. Why? Because this is a better outside option.
46
+
47
+ If I'm earning, like here, 2 million, I will be only willing to engage in a project if it has a really high expected payoff. Otherwise, I'm not going to take it up, because my current job is already quite lucrative. Now we see that what is interesting, the key message is that the higher the risk aversion, the less the entrepreneur is willing to pay, which is what we would expect based on economic reasoning.
48
+
49
+ In particular, we see that already with an intermediate level of risk aversion, well, many entrepreneurs are not willing to pay much. They're willing to pay little, or they sometimes need even to be compensated. This is certainly true when risk aversion becomes fairly high.
50
+
51
+ Two is a relatively high coefficient of relative risk aversion. What does it mean? It means that, yes, risk aversion plays a role, and less risk averse people tend to be willing to engage in entrepreneurship more often. This is more true if their worth is higher and if their current employment is lower.
52
+
53
+ Then you can go back to this slide and see all the possible combinations. This is a very interesting result, because it comes out of actual data. A similar result is obtained by a Norwegian and Greek team of economists, which look at Norwegian data, at a cohort of 400,000 Norwegian people, and they obtain this interesting result.
54
+
55
+ They test the joint hypothesis that entrepreneurs are more risk tolerant, and therefore they found businesses more often. Second, these businesses perform worse. Why? Because they are willing to accept, for a given level of risk, a lower expected value.
56
+
57
+ That's the risk-return frontier that we have looked at before. Notice that this contradicts the prediction of the risk-return frontier. The fact that you will be willing to engage in entrepreneurship in risky business only if you are compensated with a higher return.
58
+
59
+ Here, if you are very risk tolerant, that frontier will tend to be flat, or almost flat. It's not upward sloping as we have seen before, which is a general attitude of people. In that sense, yes, entrepreneurs tend to be slightly different than the general population.
60
+
61
+ They find in this Norwegian data evidence that confirms these hypotheses. These preferences appear to provide some explanation to our puzzle. This is the first learning point from this video lecture.
62
+
63
+ Now, let's turn to see how this plays in our previous example, so that we can relate to the little theory that I proposed to you. We see that instead of going for a 12% expected return to accept a riskier project, what both these studies tell us is that probably entrepreneurs will be willing to accept a very small, only say 11% increase in expected return. Some of them may even be willing to go for higher risk with the same expected return, because they are not just risk tolerant, but they are risk loving.
64
+
65
+ They like the fact that they are engaging in a risky business. This clearly is an empirical question. It depends on how individuals behave.
66
+
67
+ Now, let's move to the second potential explanation. The existence of private benefits which are not factored into the financial returns of the venture. Here we look at just one study.
68
+
69
+ There are many, but time is clearly pressing. We look at one very recent and intriguing study, which looks at entrepreneurship as a way to keep options open. Sylvain Catherine, a French economist who works at the Wharton School at the University of Pennsylvania, looks at French administrative data, very detailed data, and explores the idea that clearly funders may reap different types of unmeasured benefits.
70
+
71
+ There are three possible ones. One is non-pecuniary. For example, they enjoy the entrepreneurial experience.
72
+
73
+ They really like running a company. They get this out of their experience. That doesn't have a monetary measure.
74
+
75
+ It's enjoyment, exactly as much as you like studying. You just like it. You don't need to be compensated for it.
76
+
77
+ You just go for it. You're just listening to this video lecture. Very good.
78
+
79
+ The second possibility is that people like independence. That's also been documented, that people don't like to have a boss, and so they like to be their own masters. That is, in itself, something which gives them utility, satisfaction.
80
+
81
+ A second line of private benefit is legal tax avoidance, so unreported earnings. The fact that many small businesses manage to obtain favourable tax treatments or avoid taxes very legally, sometimes also illegally, but that is clearly very difficult for us to measure. But even legal tax avoidance has been shown to be contributing a bit to the creation of small businesses.
82
+
83
+ Not so much ambitious ventures, because those will operate in a very transparent way as being incorporated and then being acquired or IPOed. The third dimension is that founders may enjoy the experience that leads to better post-failure employment. Even if you fail or if you sell your company, you may get back to the job market and obtain an employment which is as good or even better than the one that you had before.
84
+
85
+ This is the one dimension where this economist, Catherine, focusses on. He focusses in particular on the option to return to the job market. The finding is that this is material.
86
+
87
+ I have to drop down to show this to you. We are also learning how to do these video lectures, you see. The result is that for the average French entrepreneur, the option is worth, over the typically six years of engagement in the venture, about €137,000, which is quite some money.
88
+
89
+ What does it mean? It means that the experience that is accumulated in these years is worth €137,000 on average when they go back to employment. The other interesting result is that the option to return as an employee, the fact that you know that you can count on going back, that if you sell your company or even if it fails, you are not going to be on the door. You will find an employment.
90
+
91
+ This accounts for about 40% of new business creation in France in about 20 years that Catherine explores in the data. This is also something very interesting. People get private benefits of different sorts, and this motivates them to jump into entrepreneurship.
92
+
93
+ Last but not least, overconfidence. Entrepreneurs are different from the general population because they are more optimistic. They see the reality as rosier than it is.
94
+
95
+ Well, as this highly cited psychology paper tells us, this is not so easy to find out. The title, The Trouble with Overconfidence, says it all. These two US psychologists argue the following.
96
+
97
+ They say that there are three concepts which are actually hidden into this overconfidence. One is overestimation of one's ability. The fact that we think that we are better than we are.
98
+
99
+ The second is what they call overplacement. The fact that we tend to place our own skills above those of others. The first one is an absolute, and this is a relative type of concept when we interact with other people.
100
+
101
+ Third, overprecision. We tend to have an excessive confidence on the accuracy on our own beliefs about the future. Now, much of their analysis is conceptual and shows that it is difficult to build and disentangle these concepts in an empirical setting.
102
+
103
+ So it is very difficult to do some research in this direction and find out compelling results. So in that sense, we should be cautious. There is one interesting study, though, which brings up quite an interesting result.
104
+
105
+ This is by Manju Puri and David Robinson, both at Duke University in the US. Now, what they find, they do something which is quite smart. So they build a measure of optimism which bypasses the criticism of the psychologists.
106
+
107
+ And they say, hey, let's build a measure which is not something which relates to your entrepreneurial experience or to the realm that you are going to investigate, but it's something which is orthogonal to it, different. And so they ask people, they ask people in the sense they use a vast survey of US adults, what is their expected life expectancy? So how long will you live? Now, for each individual, they are able to build a very good measure because they use demographic and personal information which is picked up in the survey. And they use actual techniques which tell us that depending on some general coordinates of our life, health, and habits, they can get a very confident measure of our lifespan.
108
+
109
+ Now, using this measure of optimism, they find that optimism positively correlates with entrepreneurial activity. So it's true in the sense that people who have a positive way of thinking tend to engage in entrepreneurial activity more often. At the same time, they find that extreme optimism leads to hazardous choices.
110
+
111
+ So this doesn't make for good entrepreneurs. So it's not that the wild guys are wild in the sense of wildly optimistic guys are better entrepreneurs. No, but entrepreneurs tend to be people who see things positively and so probably this also helps them overcome difficulties.
112
+
113
+ This is clearly the conjecture which would need more research to be validated. And so there are also studies which show that moderate optimism is correlated with lower forecast errors of the consequences of one's actions. So if you are moderately positive, you are also more precise in forecasting the future of your actions' consequences.
114
+
115
+ And so this also makes for good entrepreneurs. So how do we put all these things together? Well, what we can conclude from this review of the excellent literature is that first of all, entrepreneurship cannot be explained only by the search for returns because we know that from the previous video lecture, this does not happen. Second, that several elements play an important role in this decision.
116
+
117
+ And risk attitudes, private benefits are certainly important. And in particular, personality traits and own preferences play a role. So one conclusion that we draw which we think is interesting for you guys is learn who you are and what your life goals are is important to understand whether entrepreneurship is something which is good.
118
+
119
+ It could be a good choice for you or not. So we leave you with this thought and interesting result and we conclude the video lecture here. So I hope you enjoyed it, that you find something interesting and stay tuned for more material coming from Eli.
120
+
121
+ See you. Bye bye.
122
+
123
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
data/txt/VL Myths about entrepreneurs.txt ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
2
+
3
+ Hi everyone, and welcome to this video lecture from the Entrepreneurial Literacy Initiative. I'm Martti Wask, and today we're going to talk about myths about entrepreneurs. So many times when we think about entrepreneurship, we think about young college dropouts, right? Hi Mark, love you too.
4
+
5
+ So we have the number of stereotypes that are mainly driven by the headlines that we read in newspapers, by blog posts that we consult every now and then, and essentially we see these young people making millions of dollars. Is that true? Well, that's what we are here for. Some of the stereotypes is that you have to go through well-established business accelerator programmes, such as Y Combinators.
6
+
7
+ Many entrepreneurs getting out of the Y Combinator, also known as YC, were able to fund their businesses with millions of dollars. Some of the stereotypes is that you need to solve a founder personal problem, so you as a founder really know how to tackle the problem. You can also think of being the first to market, and so you get out or you get most of the customer base.
8
+
9
+ You could also think of being placed or located in San Francisco, New York, Boston, these places, right? Where you can raise like a lot of money. So nothing far from reality. We're here to talk to you about two studies that will really give us some facts and knowledge about the main characteristics of successful entrepreneurs.
10
+
11
+ And you'll see that most of them are not really young precisely. The first study that we're going to consult is done by Ali Tamaseb, who wrote this book, Superfounders, very recently, 2021. So he's got a sample of entrepreneurs from 2005 to 2018, and he differentiates which one made it to a multi-billion dollar valuations and which ones did not.
12
+
13
+ And he tries to find out the different characteristics that make specific entrepreneurs to make it to the more than $1 billion valuation. So he run hundreds of interviews. He consulted thousands of LinkedIn profiles, crunch based profiles, et cetera, et cetera.
14
+
15
+ Overall, he got 30,000 data points and he evaluates more than 60 characteristics of entrepreneurs. The second study that we're going to look at is done by Azule, Johns, Kim, and Miranda in 2020, published in the American Economic Review Insights. More academic, but don't be scary.
16
+
17
+ We're only going to show some descriptive statistics tables. So they analyse every single startup that was founded in the U.S. between 2007 and 2009. And they track those startups for five years, employment and revenue, to be able to discern which ones were successful startups from those that did not grow that much.
18
+
19
+ The good thing of this study is that they analyse the population. So they really get to the administrative data they're able to extract from the U.S. Census Bureau. So it's really not a representative sample, but instead the population as a whole.
20
+
21
+ So let's start by the first myth, founder's age. Again, we all think about young college dropouts like Mark Zuckerberg or this Aaron Levy that I feature here, which is kind of this stereotypical entrepreneur. So he's the founder of Box.
22
+
23
+ And back in 2005, when he was studying at University of Southern California, he was running an internship at Paramount Pictures. What else? Entertainment in California, right? Hollywood. And so what he experienced there is that people went with these huge files, huge video files.
24
+
25
+ It's an entertainment company with a USB stick. And they were going office in, office out with these USB sticks. So what he did in his next college project was to gather some students around him.
26
+
27
+ And they started or they presented this cloud storage platform that they eventually called it Box. So because the demand soared that much, he dropped out from college at the age 19 years old. And just 10 years later, 2015, the company was going IPO, achieving $175 million in proceeds and a valuation a bit north of $1.6 billion.
28
+
29
+ So kind of the typical story, success story about entrepreneurs. Now, is this really true or not? So let's get into Azulay et al. 2020.
30
+
31
+ What they did is they run different columns. So the first columns is all startups in the U.S. between 2007 and 2009. What is the average age of the founders? And what you can see is that the entire U.S. average age is 41.8. So rather old.
32
+
33
+ And you can partition it by tech employment, by B.C.-backed firms, by firms that eventually register a patent, etc., etc. You see how the age is more or less the same. It's over 40.
34
+
35
+ The columns to the right are those firms that grow the most. The top 10% firms, the top 5%, top 1%, and top 0.1%. Interestingly, what you can see is that the more to the right you go, the higher the age, the mean age of the founder is. So for all startups, it's 41.8. If you go to the right for the United States, the top 0.1% average 45 years of age for the main founder.
36
+
37
+ The very last column, successfully exit startups, are those startups that make it to IPOs, so to the public markets, or to an M&A, so an acquisition by a big player. So as you can see again, the average age of these successfully exited startups is relatively higher than the normal age, than the average age of 41.8, by being at 46.7. So in essence, successful startups still do not have these typical college dropouts, which are rather an outlier. You can partition the sample by US region, and it doesn't matter whether you are in California, Massachusetts, New York, Silicon Valley, or some other entrepreneurial hubs like Texas, or Durham, Raleigh, and North Carolina, that the age is more or less the same thing.
38
+
39
+ Myth number two, the number of founders. So it is said that you really need a team to found a successful company. And nothing far from reality.
40
+
41
+ What you can see is that almost 20% of the successful startups, and this is from Ali Tamaseb, from the multi-billion dollar startups, are founded by a solo founder. This has some advantages. So there are no conflicts of ownership between the co-founders, conflicts about personalities, different visions, different goals, etc., that many times are one main cause of failure for startups.
42
+
43
+ Now, one warning. Solo founders companies typically have founders that already co-founded a firm before. So they know what the process is about, and now they just want to run it solo.
44
+
45
+ Perhaps to make most of the profits, right? Some examples of it is Langley Steinhardt from KarGurus, which IPO-ed in 2017, valued at more than 1.5 billion. So he was a former co-founder of TripAdvisor. Or Zhang Yimin from ByteDance.
46
+
47
+ ByteDance is the company running TikTok. And so he is the main co-founder, and he already co-founded several other companies before, 99Fang and Kuxun. Myth number three, about education.
48
+
49
+ And again, here I put you a list of startup founders that eventually dropped out from college. And look how successful they were. Mark Zuckerberg from Facebook.
50
+
51
+ Michael Dell from Dell Computers. Bill Gates. Spiegel from Snapchat.
52
+
53
+ Elon Musk. All these guys never graduated from college. And so these are the ones that are making the headlines most of the times.
54
+
55
+ But how about the average successful entrepreneur? Are these outliers? And it seems that the answer is yes. So look at the distribution of how educated founding CEOs are. Again, successful founders that made it to more than a billion-dollar valuation.
56
+
57
+ This is the study by Ali Tamaseb. A very high proportion of them have at least a bachelor. If not bachelor plus MBA or master studies.
58
+
59
+ So again, founders' education seemed to matter. So the more educated you are, the more chances of success you're going to have. Yet, some of the most successful entrepreneurs are concentrated at top U.S. schools.
60
+
61
+ You have to think that this is coming from Ali Tamaseb. And Ali was analysing U.S. startups. So this is totally biassed towards the U.S. But we can take it with the European mentality too.
62
+
63
+ The fact of going to a top school, there's no denial that you have more chances to get into or to have found a successful startup. Here we've got a strong list of Stanford, Harvard, MIT, UC Berkeley, etc., etc. But there are some other universities that are not top Ivy League schools.
64
+
65
+ Such as the University of Illinois, Chicago, UIUC. We've got University of Waterloo, the Brigham Young University, BYU, etc., etc. So there are some founders of very successful startups that never made it to a top school.
66
+
67
+ Myth number four, work experience. Well, again, if you're a young college dropout, it doesn't matter whether you have some work experience that you're going to nail it anyways. And this doesn't seem to be the case.
68
+
69
+ So what is the corporate experience before founding this successful startup? Well, 50% of the founders seem to have more than 10 years of experience. And now you may say, okay, but are these 50% of co-founders the ones that make it to the multibillion dollars valuation? And the answer is, well, yes. 30% of multibillion dollar founders didn't work for anyone else before, but the 70% remaining did.
70
+
71
+ And if they worked before co-founding a successful startup, they made it at a tier one company, typically. And the tier one, again, we are in the U.S. market. This is the Google, Oracle, IBM, Yahoo, etc., etc.
72
+
73
+ You can take it into your own country and think about what types of companies are the tier one companies. Also, work experience seems to pay off. What you have here with the red box is the probability of a successful exit.
74
+
75
+ And this chart tells you whether the entrepreneur has experience in the same industry, considering industry as a NAICS industry classification code. So two digit NAICS, four digits NAICS, and six digit NAICS is panel C. So essentially what you have here is that if you have never, never, ever touched the same industry to which you're starting up the company, you have a 0.13% of probability of a successful exit. If you had one to two years of experience, this probability remains more or less the same at 0.13%. However, if you've been for more than three years in the same six digit NAICS code industry, then the probability of success raises almost double to 0.21%. So just to wrap up, most of the times you see a lot of stereotyping things that you see on the news, on blogs, on the web in general, etc., etc.
76
+
77
+ These might be outliers, and we try to show you that in fact they are outliers. In this video lecture, we've been showing you that being educated, that having work experience, especially in the industry, that not necessarily being a young entrepreneur can really make up for a successful career in entrepreneurship. So as a matter of proof, we're attaching this podcast, this featured story about Ari Beldegrun.
78
+
79
+ He is an oncology professor at UCLA, who eventually built up a couple of startups that make it to NASDAQ. So with this, we finish the video lecture. Hope you enjoyed it, and just stay tuned for the next materials from Eli.
80
+
81
+ Bye bye.
82
+
83
+ (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
readme.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ **ENV**
2
+ - ***create*** python -m venv .venv
3
+ - ***activate*** .venv\Scripts\activate
4
+
5
+ ---
6
+
7
+ **REQUIREMENTS.TXT**
8
+ - ***generate minimal*** pipreqs . --force --ignore .venv
9
+ - ***install*** pip install -r requirements.txt
10
+ - ***re-generate*** pip freeze > requirements.txt
11
+ - ***run application*** gradio app.py o python app.py
12
+ - ***manually install missing packages*** pip install package_name
13
+
14
+ ---
15
+
16
+ **PUBBLICAZIONE SU HUGGING FACE**
17
+ - Create a new Space SDK GRADIO BLANK
18
+ - git clone https://huggingface.co/spaces/StefanoDUrso/ELI #clone the EMPTY gradio project on a local HIGGINGFACE folder
19
+ - cd into the new HUGGINGFACE folder
20
+ - git remote add github https://github.com/paisleypark3121/ELI.git #add the GITHUB repository as new REMOTE
21
+ - git remote -v #this to verify the remotes; this is the output:
22
+ - origin https://huggingface.co/spaces/StefanoDUrso/ELI (fetch)
23
+ - origin https://huggingface.co/spaces/StefanoDUrso/ELI (push)
24
+ - github https://github.com/paisleypark3121/ELI.git (fetch)
25
+ - github https://github.com/paisleypark3121/ELI.git (push)
26
+ - git pull github main --allow-unrelated-histories #downloads all GITHUB code into the HUGGINGFACE folder
27
+ - git add .
28
+ - git commit -m "Sync GitHub with Hugging Face"
29
+ - git push origin main
30
+
31
+ Everytime we want to sync the code that we pushed into GITHUB we need to:
32
+ - cd into the HUGGINGFACE folder
33
+ - git pull github main #it pulls all the new code pushed into GITHUB
34
+ - git push origin main #it pushes the code into HUGGINGFACE space
35
+
36
+ ---
requirements.txt ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiofiles==24.1.0
2
+ aiohappyeyeballs==2.6.1
3
+ aiohttp==3.11.16
4
+ aiosignal==1.3.2
5
+ annotated-types==0.7.0
6
+ anyio==4.9.0
7
+ attrs==25.3.0
8
+ certifi==2025.1.31
9
+ charset-normalizer==3.4.1
10
+ click==8.1.8
11
+ colorama==0.4.6
12
+ dataclasses-json==0.6.7
13
+ distro==1.9.0
14
+ fastapi==0.115.12
15
+ ffmpy==0.5.0
16
+ filelock==3.18.0
17
+ frozenlist==1.5.0
18
+ fsspec==2025.3.2
19
+ gradio==5.25.2
20
+ gradio_client==1.8.0
21
+ greenlet==3.1.1
22
+ groovy==0.1.2
23
+ grpcio==1.71.0
24
+ grpcio-tools==1.71.0
25
+ h11==0.14.0
26
+ h2==4.2.0
27
+ hpack==4.1.0
28
+ httpcore==1.0.8
29
+ httpx==0.28.1
30
+ httpx-sse==0.4.0
31
+ huggingface-hub==0.30.2
32
+ hyperframe==6.1.0
33
+ idna==3.10
34
+ Jinja2==3.1.6
35
+ jiter==0.9.0
36
+ joblib==1.4.2
37
+ jsonpatch==1.33
38
+ jsonpointer==3.0.0
39
+ langchain==0.3.23
40
+ langchain-community==0.3.21
41
+ langchain-core==0.3.51
42
+ langchain-openai==0.3.12
43
+ langchain-qdrant==0.2.0
44
+ langchain-text-splitters==0.3.8
45
+ langsmith==0.3.30
46
+ markdown-it-py==3.0.0
47
+ MarkupSafe==3.0.2
48
+ marshmallow==3.26.1
49
+ mdurl==0.1.2
50
+ mpmath==1.3.0
51
+ multidict==6.4.3
52
+ mypy-extensions==1.0.0
53
+ networkx==3.4.2
54
+ numpy==2.2.4
55
+ openai==1.73.0
56
+ orjson==3.10.16
57
+ packaging==24.2
58
+ pandas==2.2.3
59
+ pillow==11.2.1
60
+ portalocker==2.10.1
61
+ propcache==0.3.1
62
+ protobuf==5.29.4
63
+ pydantic==2.11.3
64
+ pydantic-settings==2.8.1
65
+ pydantic_core==2.33.1
66
+ pydub==0.25.1
67
+ Pygments==2.19.1
68
+ python-dateutil==2.9.0.post0
69
+ python-dotenv==1.1.0
70
+ python-multipart==0.0.20
71
+ pytz==2025.2
72
+ pywin32==310
73
+ PyYAML==6.0.2
74
+ qdrant-client==1.13.3
75
+ regex==2024.11.6
76
+ requests==2.32.3
77
+ requests-toolbelt==1.0.0
78
+ rich==14.0.0
79
+ ruff==0.11.5
80
+ safehttpx==0.1.6
81
+ safetensors==0.5.3
82
+ scikit-learn==1.6.1
83
+ scipy==1.15.2
84
+ semantic-version==2.10.0
85
+ sentence-transformers==4.1.0
86
+ setuptools==78.1.0
87
+ shellingham==1.5.4
88
+ six==1.17.0
89
+ sniffio==1.3.1
90
+ SQLAlchemy==2.0.40
91
+ starlette==0.46.2
92
+ sympy==1.13.1
93
+ tenacity==9.1.2
94
+ threadpoolctl==3.6.0
95
+ tiktoken==0.9.0
96
+ tokenizers==0.21.1
97
+ tomlkit==0.13.2
98
+ torch==2.6.0
99
+ tqdm==4.67.1
100
+ transformers==4.51.3
101
+ typer==0.15.2
102
+ typing-inspect==0.9.0
103
+ typing-inspection==0.4.0
104
+ typing_extensions==4.13.2
105
+ tzdata==2025.2
106
+ urllib3==2.4.0
107
+ uvicorn==0.34.1
108
+ websockets==15.0.1
109
+ yarl==1.19.0
110
+ zstandard==0.23.0
utilities/llm/LlmManager.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_openai import ChatOpenAI
2
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
3
+ from utilities.vectorstore.SummaryManager import SummaryManager
4
+
5
+ MAX_MESSAGES = 50
6
+
7
+ system_message_it = """
8
+ Sei ELI, un assistente che aiuta gli studenti ad analizzare un video e rispondere a domande su di esso.
9
+ Basandoti sul contesto fornito, rispondi alla domanda dell'utente.
10
+
11
+ - Contesto: {context}
12
+ - Domanda: {question}
13
+ """
14
+
15
+ system_message_en = """
16
+ You are ELI, an assistant that helps students analyze a video and answer questions about it.
17
+ Based on the provided context, answer the user's question.
18
+
19
+ - Context: {context}
20
+ - Question: {question}
21
+ """
22
+
23
+ def get_system_message(language="en"):
24
+ if language == "it":
25
+ return system_message_it
26
+ else:
27
+ return system_message_en
28
+
29
+ fallback_prompt_en = """
30
+ You are ELI, an assistant that helps students analyze a video and answer questions about it.
31
+ Unfortunately, no relevant context could be found in the provided material.
32
+
33
+ - If possible, try to answer based on the previous conversation.
34
+ - Otherwise, inform the user that no verified information is available to answer the question reliably.
35
+
36
+ - Question: {question}
37
+ """
38
+
39
+ fallback_prompt_it = """
40
+ Sei ELI, un assistente che aiuta gli studenti ad analizzare un video e rispondere a domande su di esso.
41
+ Purtroppo, non Γ¨ stato trovato alcun contesto rilevante nei materiali forniti.
42
+
43
+ - Se possibile, prova a rispondere basandoti sulla conversazione precedente.
44
+ - Altrimenti, informa l’utente che non hai informazioni verificate per rispondere in modo affidabile.
45
+
46
+ - Domanda: {question}
47
+ """
48
+
49
+ def get_fallback_prompt(language="en"):
50
+ fallback_prompts = {
51
+ "en": fallback_prompt_en,
52
+ "it": fallback_prompt_it
53
+ }
54
+ return fallback_prompts.get(language, fallback_prompts["en"])
55
+
56
+
57
+ def get_disclaimer(context_level, language="en"):
58
+ disclaimers = {
59
+ "en": {
60
+ "medium": "\n\n⚠️ Note: the retrieved context has moderate similarity. The answer may not be fully reliable.",
61
+ "summary": "\n\n🟠 Note: the context is based on a general summary of the content. Please verify the information if needed.",
62
+ "low": "\n\n⚠️ No reliable information was found in the source material. The answer may rely only on the conversation.",
63
+ "no_context": "\n\n⚠️ No context available. The assistant will try to respond based on previous conversation, if possible."
64
+ },
65
+ "it": {
66
+ "medium": "\n\n⚠️ Nota: il contesto recuperato ha una similarità moderata. La risposta potrebbe non essere pienamente affidabile.",
67
+ "summary": "\n\n🟠 Nota: il contesto usato è un riassunto generale del contenuto. Verifica le fonti se necessario.",
68
+ "low": "\n\n⚠️ Nessuna informazione affidabile trovata nei materiali. La risposta potrebbe basarsi solo sulla conversazione.",
69
+ "no_context": "\n\n⚠️ Nessun contesto disponibile. L’assistente proverΓ  a rispondere in base alla conversazione, se possibile."
70
+ }
71
+ }
72
+
73
+ return disclaimers.get(language, {}).get(context_level, "")
74
+
75
+ class LlmManager():
76
+
77
+ def __init__(self, qdrant_manager=None, model="gpt-4o-mini", language="en"):
78
+ self.model=model
79
+ self.llm = ChatOpenAI(model=model, streaming=True)
80
+ self.language = language
81
+ self.system_message = get_system_message(language)
82
+ self.messages = []
83
+ self.qdrant_manager = qdrant_manager
84
+
85
+ def set_qdrant_manager(self, qdrant_manager):
86
+ self.qdrant_manager = qdrant_manager
87
+
88
+ def reset_messages(self, context, question):
89
+ self.messages = [SystemMessage(content=self.system_message.format(context=context, question=question))]
90
+
91
+ def stream_message(self, message, contextualize=False):
92
+ """
93
+ Streaming equivalente a send_message, con gestione dinamica del contesto e localizzazione.
94
+ """
95
+ if contextualize:
96
+ context = ""
97
+ context_level = "no_context"
98
+
99
+ if self.qdrant_manager and self.qdrant_manager.is_loaded():
100
+ context, context_level = self.qdrant_manager.get_context_for_query(message)
101
+ print(f"πŸ“š Contesto recuperato [{context_level}]: {context[:100]}")
102
+
103
+ disclaimer = get_disclaimer(context_level, self.language)
104
+ base_prompt = get_system_message(self.language)
105
+
106
+ # Caso: contesto assente (no_context/low) ➝ prompt fallback
107
+ if not context.strip() and context_level in ["low", "no_context"]:
108
+ formatted_message = get_fallback_prompt(self.language).format(question=message)
109
+ else:
110
+ formatted_message = base_prompt.format(context=context, question=message) + disclaimer
111
+
112
+ # Inserisci o aggiorna il SystemMessage
113
+ if self.messages and isinstance(self.messages[0], SystemMessage):
114
+ self.messages[0] = SystemMessage(content=formatted_message)
115
+ else:
116
+ self.messages.insert(0, SystemMessage(content=formatted_message))
117
+
118
+ # Aggiungi il messaggio utente
119
+ self.messages.append(HumanMessage(content=message))
120
+
121
+ else:
122
+ self.messages.append(HumanMessage(content=message))
123
+
124
+ self._roll_messages()
125
+
126
+ response = ""
127
+ for chunk in self.llm.stream(self.messages):
128
+ response += chunk.content
129
+ yield {"content": chunk.content, "context_level": context_level}
130
+
131
+ self.messages.append(AIMessage(content=response))
132
+ return response, context_level
133
+
134
+ def send_message(self, message, contextualize=False):
135
+ if contextualize:
136
+ if not self.messages:
137
+ # Primo messaggio: contestualizzo
138
+ context = ""
139
+ if self.qdrant_manager and self.qdrant_manager.is_loaded():
140
+ context = self.qdrant_manager.get_context_for_query(message) or ""
141
+ self.reset_messages(context=context, question=message)
142
+ else:
143
+ self.messages.append(HumanMessage(content=message))
144
+ else:
145
+ self.messages.append(HumanMessage(content=message))
146
+
147
+ self._roll_messages()
148
+
149
+ print("\n--- MESSAGES ---")
150
+ for i, msg in enumerate(self.messages):
151
+ role = msg.type.upper()
152
+ preview = msg.content.strip().replace("\n", " ")[:120]
153
+ print(f"{i+1:02d}. [{role}] {preview}...")
154
+ print("--- END ---\n")
155
+
156
+ response = ""
157
+ for chunk in self.llm.stream(self.messages):
158
+ response += chunk.content
159
+ yield chunk.content # STREAM output to Gradio
160
+
161
+ # Alla fine, salva l’output completo nei messaggi
162
+ self.messages.append(AIMessage(content=response))
163
+
164
+ def initialize_conversation(self):
165
+ if self.qdrant_manager and self.qdrant_manager.is_loaded():
166
+ summary_manager = SummaryManager(
167
+ language=self.language,
168
+ qdrant_manager=self.qdrant_manager,
169
+ model=self.model
170
+ )
171
+ summary = summary_manager.do_initial_summary()
172
+ if summary:
173
+ preamble = {
174
+ "it": "Questo contenuto tratta i seguenti temi principali:",
175
+ "en": "This content covers the following main topics:"
176
+ }
177
+ follow_up = {
178
+ "it": "Vuoi approfondire qualche aspetto in particolare?",
179
+ "en": "Would you like to explore any of these points further?"
180
+ }
181
+
182
+ intro_message = f"{preamble.get(self.language)}\n\n{summary}\n\n{follow_up.get(self.language)}"
183
+ self.messages = [
184
+ SystemMessage(content=self.system_message.format(context="", question="")),
185
+ AIMessage(content=intro_message)
186
+ ]
187
+
188
+ def get_map_summary(self):
189
+ summary_manager = SummaryManager(
190
+ language=self.language,
191
+ qdrant_manager=self.qdrant_manager,
192
+ model=self.model
193
+ )
194
+ summary, _, _ = summary_manager.do_summary_map_reduce()
195
+ return summary
196
+
197
+ def get_stuff_summary(self):
198
+ summary_manager = SummaryManager(
199
+ language=self.language,
200
+ qdrant_manager=self.qdrant_manager,
201
+ model=self.model
202
+ )
203
+ summary, _, _ = summary_manager.do_summary_stuff()
204
+ return summary
205
+
206
+ def _roll_messages(self):
207
+ """
208
+ Keeps only the last `MAX_MESSAGES` from `messages`, excluding the first (SystemMessage).
209
+ The first message (SystemMessage) is always preserved.
210
+ """
211
+ if len(self.messages) > MAX_MESSAGES + 1: # +1 to account for the SystemMessage
212
+ self.messages = [self.messages[0]] + self.messages[-MAX_MESSAGES:] # Keep SystemMessage + last MAX_MESSAGES
utilities/llm/__pycache__/LlmManager.cpython-312.pyc ADDED
Binary file (10.9 kB). View file
 
utilities/vectorstore/QdrantLangchainManager.py ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ import tiktoken
4
+
5
+ from qdrant_client import QdrantClient
6
+ from qdrant_client.models import Distance, VectorParams, Filter, FieldCondition, MatchValue
7
+
8
+ import torch
9
+ from torch.nn.functional import sigmoid
10
+
11
+ from langchain_openai import ChatOpenAI, OpenAIEmbeddings
12
+ from langchain_qdrant import QdrantVectorStore
13
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
14
+ from langchain_community.document_loaders import TextLoader, PyPDFLoader
15
+ from langchain_core.documents import Document
16
+ from langchain_core.messages import SystemMessage, HumanMessage
17
+
18
+ ENCODING_NAME='o200k_base'
19
+
20
+ class QdrantLangchainManager:
21
+
22
+ def __init__(self, qdrant_url, qdrant_api_key,
23
+ system_message=None, crossencoder_model=None,
24
+ batch_size=500, chunk_size=2000, chunk_overlap=50,
25
+ vector_size=1536, re_ranking_threshold=0.7):
26
+
27
+ self.qdrant_url = qdrant_url
28
+ self.qdrant_api_key = qdrant_api_key
29
+ self.client = QdrantClient(url=qdrant_url, api_key=qdrant_api_key)
30
+
31
+ self.batch_size = batch_size
32
+ self.chunk_size = chunk_size
33
+ self.chunk_overlap = chunk_overlap
34
+ self.vector_size = vector_size
35
+ self.re_ranking_threshold = re_ranking_threshold
36
+
37
+ self.reranker = crossencoder_model
38
+
39
+ self.collection_name = None
40
+ self.vectorstore = None
41
+
42
+ def is_loaded(self):
43
+ if self.collection_name is None:
44
+ return False
45
+
46
+ if self.vectorstore is None:
47
+ return False
48
+
49
+ return True
50
+
51
+ def create_collection(self, collection_name):
52
+ try:
53
+ if not self.client.collection_exists(collection_name):
54
+ self.client.create_collection(
55
+ collection_name=collection_name,
56
+ vectors_config=VectorParams(size=self.vector_size, distance=Distance.COSINE)
57
+ )
58
+ print(f"βœ… Collection '{collection_name}' created successfully.")
59
+ else:
60
+ print(f"⚠️ Collection '{collection_name}' already exists.")
61
+
62
+ self.vectorstore = QdrantVectorStore(
63
+ client=self.client,
64
+ collection_name=collection_name,
65
+ embedding=OpenAIEmbeddings(),
66
+ )
67
+ self.collection_name = collection_name
68
+ return True
69
+ except Exception as e:
70
+ print(f"❌ Error creating collection '{collection_name}': {e}")
71
+ return False
72
+
73
+ def get_collection(self, collection_name):
74
+ try:
75
+ if not self.client.collection_exists(collection_name):
76
+ print(f"⚠️ Collection '{collection_name}' doesn't exist.")
77
+ return False
78
+ self.vectorstore = QdrantVectorStore(
79
+ client=self.client,
80
+ collection_name=collection_name,
81
+ embedding=OpenAIEmbeddings(),
82
+ )
83
+ self.collection_name = collection_name
84
+ return True
85
+ except Exception as e:
86
+ print(f"❌ Error getting collection '{collection_name}': {e}")
87
+ return False
88
+
89
+ def insert_document(self, file_path, max_tokens=None):
90
+ if not self.vectorstore:
91
+ print("⚠️ No collection initialized. Please create or load a collection first.")
92
+ return False, 0, None
93
+
94
+ try:
95
+ file_extension = os.path.splitext(file_path)[-1].lower()
96
+ file_name = os.path.basename(file_path)
97
+
98
+ # Select the appropriate loader
99
+ if file_extension == ".txt":
100
+ loader = TextLoader(file_path)
101
+ elif file_extension == ".pdf":
102
+ loader = PyPDFLoader(file_path)
103
+ else:
104
+ raise ValueError(f"Unsupported file type: {file_extension}")
105
+
106
+ # Load and split documents
107
+ docs = loader.load()
108
+
109
+ full_text = "\n".join([doc.page_content for doc in docs])
110
+ total_tokens = self._calculate_tokens(full_text)
111
+ print(f"πŸ“„ Total tokens: {total_tokens}")
112
+
113
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=self.chunk_size, chunk_overlap=self.chunk_overlap)
114
+ chunks = text_splitter.split_documents(docs)
115
+
116
+ # Merge small chunks if needed
117
+ max_iterations = 3
118
+ iteration = 0
119
+ merge_performed = True
120
+ while merge_performed and iteration < max_iterations:
121
+ chunks, merge_performed = self._merge_chunks(chunks)
122
+ iteration += 1
123
+ print(f"πŸ”„ Merge Iteration {iteration}: {len(chunks)} chunks remain.")
124
+
125
+ global_idx = 0
126
+ for i in range(0, len(chunks), self.batch_size):
127
+ batch = chunks[i:i + self.batch_size]
128
+
129
+ batch_docs = [
130
+ Document(
131
+ page_content=chunk.page_content,
132
+ metadata={
133
+ "type": "content",
134
+ "file_name": file_name,
135
+ "chunk_index": global_idx + j # globale!
136
+ }
137
+ )
138
+ for j, chunk in enumerate(batch)
139
+ ]
140
+
141
+ global_idx += len(batch)
142
+
143
+ self.vectorstore.add_documents(batch_docs)
144
+ print(f"βœ… Inserted {len(batch_docs)} documents (Batch {i // self.batch_size + 1})")
145
+
146
+ return True, total_tokens, full_text if max_tokens is not None and total_tokens <= max_tokens else None
147
+
148
+ except Exception as e:
149
+ print(f"❌ Error inserting documents: {e}")
150
+ return False, 0, None
151
+
152
+ def insert_text(self, text, metadata=None):
153
+ """
154
+ Insert a single block of text into the vectorstore, with optional metadata.
155
+ """
156
+ if not self.vectorstore:
157
+ print("⚠️ No collection initialized. Please create or load a collection first.")
158
+ return False
159
+
160
+ try:
161
+ doc = Document(
162
+ page_content=text,
163
+ metadata=metadata or {}
164
+ )
165
+ self.vectorstore.add_documents([doc])
166
+ print("βœ… Text inserted into vector store.")
167
+ return True
168
+ except Exception as e:
169
+ print(f"❌ Failed to insert text into vector store: {e}")
170
+ return False
171
+
172
+ def get_documents(self):
173
+ try:
174
+ total_points = self.client.count(self.collection_name, exact=True).count
175
+ if total_points == 0:
176
+ print("Qdrant collection is empty.")
177
+ return []
178
+ print(total_points)
179
+
180
+ results = self.client.scroll(
181
+ collection_name=self.collection_name,
182
+ limit=total_points,
183
+ with_vectors=True,
184
+ with_payload=True
185
+ )
186
+
187
+ return [
188
+ {
189
+ "id": point.id,
190
+ "vector": point.vector,
191
+ "payload": {
192
+ "page_content": point.payload.get("page_content", ""),
193
+ **point.payload.get("metadata", {}) # <--- merge dei metadati qui!
194
+ }
195
+ }
196
+ for point in results[0]
197
+ ]
198
+
199
+ except Exception as e:
200
+ print(f"Error fetching documents from Qdrant: {e}")
201
+ return []
202
+
203
+ def get_context_for_query(self, query, top_k=8):
204
+ print("πŸ”Ž get_context_for_query:", query)
205
+
206
+ if not self.vectorstore:
207
+ print("⚠️ No collection initialized.")
208
+ return "", "no_context"
209
+
210
+ try:
211
+ docs_with_scores_raw = self.vectorstore.similarity_search_with_score(query, k=top_k + 3)
212
+ docs_with_scores = [
213
+ (doc, score)
214
+ for doc, score in docs_with_scores_raw
215
+ if doc.metadata.get("type") == "content"
216
+ ][:top_k]
217
+
218
+ if not docs_with_scores:
219
+ print("⚠️ Nessun documento con type='content' trovato.")
220
+ return "", "no_context"
221
+
222
+ print(f"πŸ“¦ Trovati {len(docs_with_scores)} documenti candidati.")
223
+
224
+ # Se non c'Γ¨ reranker, usa soglia base
225
+ if not self.reranker:
226
+ valid_docs = [
227
+ doc.page_content for doc, score in docs_with_scores
228
+ if score > self.re_ranking_threshold
229
+ ]
230
+ if valid_docs:
231
+ return "\n\n".join(valid_docs), "high"
232
+ else:
233
+ return "", "low"
234
+
235
+ # Con reranker: calcolo probabilitΓ  normalizzate
236
+ reranked = self._reranking(query, docs_with_scores)
237
+ if not reranked:
238
+ print("⚠️ Reranker ha restituito risultati vuoti.")
239
+ return "", "no_context"
240
+
241
+ high_conf, medium_conf = [], []
242
+
243
+ for (doc, _), score in reranked:
244
+ if score > 0.7:
245
+ high_conf.append(doc.page_content)
246
+ elif score > 0.3:
247
+ medium_conf.append(doc.page_content)
248
+
249
+ if high_conf:
250
+ print(f"βœ… {len(high_conf)} documenti con alta confidenza.")
251
+ return "\n\n".join(high_conf), "high"
252
+
253
+ elif medium_conf:
254
+ print(f"🟑 {len(medium_conf)} documenti con confidenza media.")
255
+ return "\n\n".join(medium_conf), "medium"
256
+
257
+ # πŸ” Fallback: cerca se esistono summary
258
+ print("🟠 Nessun chunk valido. Provo a usare i summary.")
259
+
260
+ summaries = [
261
+ d for d in self.get_documents()
262
+ if d["payload"].get("type") in ["map_summary", "stuff_summary", "initial_summary"]
263
+ ]
264
+
265
+ if not summaries:
266
+ print("⚠️ Nessun summary trovato.")
267
+ return "", "low"
268
+
269
+ summary_pairs = [(query, d["payload"]["page_content"]) for d in summaries]
270
+ raw_scores = self.reranker.predict(summary_pairs)
271
+ prob_scores = sigmoid(torch.tensor(raw_scores)).tolist()
272
+
273
+ summary_candidates = [
274
+ d["payload"]["page_content"]
275
+ for d, score in zip(summaries, prob_scores)
276
+ if score > 0.5
277
+ ]
278
+
279
+ if summary_candidates:
280
+ print("🟠 Uso il/i summary come contesto.")
281
+ return "\n\n".join(summary_candidates), "summary"
282
+
283
+ print("❌ Nessun summary supera la soglia.")
284
+ return "", "low"
285
+
286
+ except Exception as e:
287
+ print(f"❌ Errore nel recupero del contesto: {e}")
288
+ return "", "no_context"
289
+
290
+
291
+ def delete_collection(self, collection_name):
292
+ try:
293
+ self.client.delete_collection(collection_name)
294
+ print(f"🚨 Collection '{collection_name}' has been deleted.")
295
+ self.vectorstore = None
296
+ return True
297
+ except Exception as e:
298
+ print(f"❌ Error deleting collection '{collection_name}': {e}")
299
+ return False
300
+
301
+ def _merge_chunks(self, chunks, min_size=500, max_size=2000):
302
+ if not chunks:
303
+ return [], False
304
+
305
+ merged_chunks = []
306
+ temp_text = ""
307
+ merge_performed = False
308
+
309
+ if len(chunks[0].page_content) < min_size and len(chunks) > 1:
310
+ chunks[1] = Document(page_content=chunks[0].page_content + " " + chunks[1].page_content)
311
+ chunks = chunks[1:]
312
+ merge_performed = True
313
+
314
+ for chunk in chunks:
315
+ text = chunk.page_content
316
+ if not temp_text:
317
+ temp_text = text
318
+ continue
319
+ if len(text) < min_size:
320
+ temp_text += " " + text
321
+ merge_performed = True
322
+ else:
323
+ while len(temp_text) > max_size:
324
+ merged_chunks.append(Document(page_content=temp_text[:max_size]))
325
+ temp_text = temp_text[max_size:]
326
+ merged_chunks.append(Document(page_content=temp_text))
327
+ temp_text = text
328
+
329
+ if temp_text:
330
+ merged_chunks.append(Document(page_content=temp_text))
331
+
332
+ return merged_chunks, merge_performed
333
+
334
+ # def _reranking(self, query, docs_with_scores):
335
+ # if not self.reranker:
336
+ # print("⚠️ Reranker not initialized. Skipping reranking.")
337
+ # return docs_with_scores
338
+
339
+ # query_pairs = [(query, doc.page_content) for doc, _ in docs_with_scores]
340
+ # new_scores = self.reranker.predict(query_pairs)
341
+ # return sorted(zip(docs_with_scores, new_scores), key=lambda x: x[1], reverse=True)
342
+
343
+ def _reranking(self, query, docs_with_scores):
344
+ if not self.reranker:
345
+ print("⚠️ Reranker not initialized. Skipping reranking.")
346
+ return docs_with_scores
347
+
348
+ query_pairs = [(query, doc.page_content) for doc, _ in docs_with_scores]
349
+
350
+ # Calcola i logit grezzi
351
+ raw_scores = self.reranker.predict(query_pairs)
352
+
353
+ # Applica sigmoid per ottenere probabilitΓ  tra 0 e 1
354
+ prob_scores = sigmoid(torch.tensor(raw_scores)).tolist()
355
+
356
+ # Log per debugging
357
+ print("πŸ“ˆ Reranking scores (sigmoid-normalized):")
358
+ for i, ((doc, old_score), prob) in enumerate(zip(docs_with_scores, prob_scores)):
359
+ print(f"{i+1:02d}. chunk_index={doc.metadata.get('chunk_index', '-')}, score={prob:.3f}")
360
+
361
+ return sorted(zip(docs_with_scores, prob_scores), key=lambda x: x[1], reverse=True)
362
+
363
+ def _calculate_tokens(self, text):
364
+ """Calculate the number of tokens in the given text."""
365
+ try:
366
+ encoding = tiktoken.get_encoding(ENCODING_NAME)
367
+ return len(encoding.encode(text))
368
+ except Exception as e:
369
+ print(f"Error calculating tokens: {e}", flush=True)
370
+ return 0
utilities/vectorstore/SummaryManager.py ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import requests
4
+ from dotenv import load_dotenv
5
+
6
+ import tiktoken
7
+
8
+ #from langchain_openai import OpenAI
9
+ #from langchain_community.llms import OpenAI
10
+ from langchain_openai import ChatOpenAI, OpenAIEmbeddings
11
+ from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
12
+ from langchain_community.callbacks.manager import get_openai_callback
13
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
14
+ from langchain.chains.summarize import load_summarize_chain
15
+ from langchain.chains.combine_documents import create_stuff_documents_chain
16
+ from langchain.schema import Document
17
+
18
+ from sklearn.cluster import KMeans
19
+ from sklearn.manifold import TSNE
20
+ #import matplotlib.pyplot as plt
21
+ import warnings
22
+ from warnings import simplefilter
23
+ import numpy as np
24
+
25
+ #default_model='gpt-4o-mini'
26
+ #default_embedding_model='text-embedding-3-small'
27
+ #default_temperature=0
28
+ #default_chunk_size=5000
29
+ #default_chunk_overlap=1000
30
+ #default_max_lenght=5000
31
+ #default_separator="\n\n\n\n\n"
32
+
33
+ '''
34
+ On average each token contains 4 characters; 1000 tokens is about 750 words
35
+ - chunk size 1000 -> 250 tokens
36
+ - chunk size 2000 -> 500 tokens
37
+ - chunk size 5000 -> 1250 tokens
38
+ - chunk size 10000 -> 2500 tokens
39
+ On GPT-4o-mini:
40
+ - the price for 10k tokens in input is about 0.001€ (1k -> 0.00015)
41
+ - the price for 10k tokens in output is about 0.006€ (1k -> 0.0006)
42
+ - the input limit is 128k tokens
43
+ - the output limit is 16k tokens
44
+ '''
45
+
46
+ map_prompt_en = """
47
+ Please provide a summary of the following text:
48
+
49
+ {text}
50
+ """
51
+
52
+ map_prompt_it = """
53
+ Per favore, fornisci un riassunto del seguente testo:
54
+
55
+ {text}
56
+ """
57
+
58
+ combine_prompt_en = """
59
+ Based on the key points and ideas provided in the previous summaries, craft a final, cohesive summary that presents the information as if it were an original piece of content.
60
+ Ensure that the summary flows smoothly, maintaining a consistent tone and style throughout. Avoid explicitly referencing the structure or order of the previous summaries, and focus on creating a well-organized and comprehensive narrative that conveys the central themes and insights clearly and naturally.
61
+ Return the summary in Markdown format, using `###` for headings, `-` for lists, and keeping lines separated for better readability. Make sure that each item in a list starts on a new line, and that each sentence in a paragraph is separated by a blank line. Never write multiple sentences on the same line, unless they are parts of the same continuous paragraph. Make sure that each mathematical formula is written in LaTeX and enclosed in the delimiters \\( \\) for inline formulas and \\[ \\] for block formulas. Each sentence or idea should be separated by a carriage return for better readability. When displaying code, always use triple backticks (```) and specify the language (e.g. ```html, ```python, ```javascript) to ensure proper formatting and maintain proper indentation and layout.
62
+
63
+ {text}
64
+ """
65
+
66
+ combine_prompt_it = """
67
+ Basandoti sui punti chiave e le idee fornite nei riassunti precedenti, crea un riassunto finale e coeso che presenti le informazioni come se fossero un contenuto originale.
68
+ Assicurati che il riassunto scorra in modo fluido, mantenendo un tono e uno stile coerenti in tutto. Evita di fare riferimento esplicito alla struttura o all'ordine dei riassunti precedenti e concentrati sulla creazione di una narrazione ben organizzata e completa che trasmetta chiaramente e naturalmente i temi e le intuizioni centrali.
69
+ Restituisci il riassunto in formato Markdown, utilizzando `###` per i titoli, `-` per gli elenchi e mantenendo le linee separate per una migliore leggibilitΓ . Assicurati che ogni elemento di un elenco inizi su una nuova riga, e che ogni frase di un paragrafo sia separata da una riga vuota. Non scrivere mai piΓΉ frasi sulla stessa riga, a meno che non siano parti dello stesso paragrafo continuo. Assicurati che ogni formula matematica sia scritta in LaTeX e racchiusa tra i delimitatori \\( \\) per le formule inline e \\[ \\] per le formule di blocco. Ogni frase o idea deve essere separata da un ritorno a capo per una migliore leggibilitΓ . Quando mostri del codice, usa sempre i backtick tripli (```) e specifica la lingua (es. ```html, ```python, ```javascript) per garantire la corretta formattazione e mantenere l'indentazione e il layout corretti.
70
+
71
+ {text}
72
+ """
73
+
74
+ initial_summary_prompt_intro_en = """
75
+ Read the following introductory text and write a brief summary of the main themes.
76
+
77
+ Start with the sentence: "The content is about..." or a similar phrase, followed by a list of 3–5 key points. Use a clear and informative style.
78
+ """
79
+
80
+ initial_summary_prompt_intro_it = """
81
+ Leggi il seguente testo introduttivo e scrivi un riassunto breve dei temi principali.
82
+
83
+ Inizia con la frase: "Il contenuto tratta di..." oppure una formula equivalente, e segui con un elenco di 3–5 punti chiave. Usa uno stile chiaro e informativo.
84
+ """
85
+
86
+ initial_summary_prompt_closing_en = """
87
+ End with a sentence indicating that this is just an introduction and the content continues.
88
+ """
89
+
90
+ initial_summary_prompt_closing_it = """
91
+ Concludi con una frase che segnali che il contenuto continua oltre questo estratto.
92
+ """
93
+
94
+ initial_summary_prompt_formatting = """
95
+ Return the summary in Markdown format, using `###` for headings, `-` for lists, and keeping lines separated for better readability. Make sure that each item in a list starts on a new line, and that each sentence in a paragraph is separated by a blank line. Never write multiple sentences on the same line, unless they are parts of the same continuous paragraph. Make sure that each mathematical formula is written in LaTeX and enclosed in the delimiters \\( \\) for inline formulas and \\[ \\] for block formulas. Each sentence or idea should be separated by a carriage return for better readability. When displaying code, always use triple backticks (```) and specify the language (e.g. ```html, ```python, ```javascript) to ensure proper formatting and maintain proper indentation and layout.
96
+ """
97
+
98
+
99
+ def get_map_prompt(language):
100
+ if language=='it':
101
+ return map_prompt_it
102
+ else:
103
+ return map_prompt_en
104
+
105
+ def get_combine_prompt(language):
106
+ if language=='it':
107
+ return combine_prompt_it
108
+ else:
109
+ return combine_prompt_en
110
+
111
+ def get_initial_summary_prompt(language, is_partial=False):
112
+ if language == 'it':
113
+ prompt = initial_summary_prompt_intro_it
114
+ if is_partial:
115
+ prompt += "\n" + initial_summary_prompt_closing_it
116
+ else:
117
+ prompt = initial_summary_prompt_intro_en
118
+ if is_partial:
119
+ prompt += "\n" + initial_summary_prompt_closing_en
120
+
121
+ # Aggiungi sempre la parte sulla formattazione Markdown
122
+ prompt += "\n" + initial_summary_prompt_formatting
123
+
124
+ # Appendice per il blocco di testo
125
+ prompt += "\n\nText:\n{combined_text}"
126
+
127
+ return prompt
128
+
129
+
130
+ # Constants
131
+ MAX_TOTAL_TOKENS = 6000 # Safe token limit for summarization
132
+ CHUNK_SIZE = 2000 # Define a fixed chunk size
133
+ MAX_SELECTED_DOCS = 5
134
+
135
+ class SummaryManager:
136
+
137
+ def __init__(self, language, qdrant_manager, model='gpt-4o-mini', temperature=0, max_tokens=MAX_TOTAL_TOKENS):
138
+ self.language = language
139
+ self.qdrant_manager = qdrant_manager
140
+ self.model = model
141
+ self.temperature = temperature
142
+ self.max_tokens = max_tokens
143
+
144
+ print("Model:", self.model)
145
+ self.llm = ChatOpenAI(model=model, temperature=temperature)
146
+
147
+ def do_initial_summary(self):
148
+ """
149
+ Builds a lightweight initial summary using the first 2-3 chunks of the document.
150
+ If an initial summary already exists in the vector store, it returns that instead.
151
+ """
152
+
153
+ # STEP 1: cerca se giΓ  esiste
154
+ print("πŸ”Ž Checking for existing initial summary...", flush=True)
155
+ all_docs = self.qdrant_manager.get_documents()
156
+ for doc in all_docs:
157
+ if doc["payload"].get("type") == "initial_summary" and doc["payload"].get("language") == self.language:
158
+ print("βœ… Found existing initial summary in vector store.")
159
+ return doc["payload"]["page_content"]
160
+
161
+ # STEP 2: genera il riassunto se non esiste
162
+ print("πŸš€ Generating initial summary from early chunks...", flush=True)
163
+
164
+ if not all_docs:
165
+ print("⚠️ No documents available for summary.")
166
+ return None
167
+
168
+ # Filtra solo i documenti con chunk_index
169
+ chunk_docs = [doc for doc in all_docs if 'chunk_index' in doc["payload"]]
170
+ total_chunks = len(chunk_docs)
171
+
172
+ # Prendi i primi 3 chunk ordinati
173
+ selected_chunks = sorted(chunk_docs, key=lambda d: d["payload"]["chunk_index"])[:3]
174
+ is_partial = len(selected_chunks) < total_chunks
175
+
176
+ combined_text = "\n".join([doc["payload"]["page_content"] for doc in selected_chunks])[:3000]
177
+
178
+ # Prompt dinamico
179
+ prompt_template = get_initial_summary_prompt(self.language, is_partial=is_partial)
180
+ prompt = prompt_template.format(combined_text=combined_text)
181
+
182
+ try:
183
+ from langchain_core.messages import HumanMessage
184
+ response = self.llm.invoke([HumanMessage(content=prompt)])
185
+ summary = response.content
186
+
187
+ # STEP 3: salva il riassunto nel vector store
188
+ inserted = self.qdrant_manager.insert_text(
189
+ text=summary,
190
+ metadata={
191
+ "type": "initial_summary",
192
+ "file_name": self.qdrant_manager.collection_name,
193
+ "language": self.language
194
+ }
195
+ )
196
+ if inserted:
197
+ print("πŸ“ Initial summary saved to vector store.")
198
+
199
+ return summary
200
+
201
+ except Exception as e:
202
+ print(f"❌ Error generating initial summary: {e}")
203
+ return None
204
+
205
+ def do_summary_map_reduce(self):
206
+ """
207
+ Returns a full summary using Map-Reduce summarization.
208
+ If a final summary already exists in the vector store, returns that instead.
209
+ """
210
+
211
+ # STEP 1: check if summary already exists
212
+ print("πŸ”Ž Checking for existing final summary...", flush=True)
213
+ all_documents = self.qdrant_manager.get_documents()
214
+ for doc in all_documents:
215
+ if doc["payload"].get("type") == "map_summary" and doc["payload"].get("language") == self.language:
216
+ print("βœ… Found existing final summary in vector store.")
217
+ return doc["payload"]["page_content"], 0, 0
218
+
219
+ if not all_documents:
220
+ print("❌ No documents found in collection.")
221
+ return None, 0, 0
222
+
223
+ print(len(all_documents), flush=True)
224
+
225
+ # STEP 2: extract vectors & text
226
+ embeddings = [doc["vector"] for doc in all_documents]
227
+ documents = [doc["payload"]["page_content"] for doc in all_documents]
228
+ metadata = [doc["payload"] for doc in all_documents]
229
+
230
+ # STEP 3: select up to MAX_SELECTED_DOCS chunks via KMeans
231
+ MAX_SELECTED_DOCS = 5
232
+ selected_docs = self._select_best_chunks(
233
+ documents=documents,
234
+ metadata=metadata,
235
+ embeddings=embeddings,
236
+ max_chunks=MAX_SELECTED_DOCS
237
+ )
238
+
239
+ total_tokens = sum(self.llm.get_num_tokens(doc.page_content) for doc in selected_docs)
240
+ print(f"βœ… Selected {len(selected_docs)} docs with total tokens: {total_tokens}")
241
+
242
+ # STEP 4: load LangChain prompts
243
+ map_prompt_template = PromptTemplate(template=get_map_prompt(self.language), input_variables=["text"])
244
+ combine_prompt_template = PromptTemplate(template=get_combine_prompt(self.language), input_variables=["text"])
245
+
246
+ print("πŸ”„ Loading summarization chain...")
247
+ summary_chain = load_summarize_chain(
248
+ llm=self.llm,
249
+ chain_type='map_reduce',
250
+ map_prompt=map_prompt_template,
251
+ combine_prompt=combine_prompt_template,
252
+ verbose=False
253
+ )
254
+
255
+ print("πŸ“Š Checking token size of each formatted_doc...")
256
+ for i, doc in enumerate(selected_docs):
257
+ token_count = self.llm.get_num_tokens(doc.page_content)
258
+ print(f"Chunk {i+1}: {token_count} tokens")
259
+
260
+ # STEP 5: run the chain with token tracking
261
+ with get_openai_callback() as cb:
262
+ result = summary_chain.invoke({"input_documents": selected_docs})
263
+ input_tokens_used = cb.prompt_tokens
264
+ output_tokens_used = cb.completion_tokens
265
+ total_tokens = cb.total_tokens
266
+ print(f"🧾 Token usage: total={total_tokens}")
267
+
268
+ full_summary = result['output_text']
269
+ print("βœ… Map-reduce summary generated.")
270
+
271
+ # STEP 6: store the final summary
272
+ inserted = self.qdrant_manager.insert_text(
273
+ text=full_summary,
274
+ metadata={
275
+ "type": "map_summary",
276
+ "file_name": self.qdrant_manager.collection_name,
277
+ "language": self.language
278
+ }
279
+ )
280
+ if inserted:
281
+ print("πŸ“ Final summary saved to vector store.")
282
+
283
+ return full_summary, input_tokens_used, output_tokens_used
284
+
285
+ def do_summary_stuff(self):
286
+ """
287
+ Returns a full summary using STUFF summarization strategy.
288
+ Uses all documents if total token count is within limits,
289
+ otherwise selects the best subset under token budget.
290
+ """
291
+
292
+ # STEP 1: check if summary already exists
293
+ print("πŸ”Ž Checking for existing final summary...", flush=True)
294
+ all_documents = self.qdrant_manager.get_documents()
295
+ for doc in all_documents:
296
+ if doc["payload"].get("type") == "stuff_summary" and doc["payload"].get("language") == self.language:
297
+ print("βœ… Found existing final summary in vector store.")
298
+ return doc["payload"]["page_content"], 0, 0
299
+
300
+ if not all_documents:
301
+ print("❌ No documents found in collection.")
302
+ return None, 0, 0
303
+
304
+ #print(len(all_documents), flush=True)
305
+
306
+ # STEP 2: extract vectors & text
307
+ embeddings = [doc["vector"] for doc in all_documents]
308
+ documents = [doc["payload"]["page_content"] for doc in all_documents]
309
+ metadata = [doc["payload"] for doc in all_documents]
310
+
311
+ # STEP 3: selezione intelligente con fallback a clustering
312
+ selected_docs = self._get_chunks_for_stuff(
313
+ documents=documents,
314
+ metadata=metadata,
315
+ embeddings=embeddings,
316
+ llm=self.llm,
317
+ max_tokens=self.max_tokens,
318
+ fallback_max_chunks=5
319
+ )
320
+
321
+ total_tokens = sum(self.llm.get_num_tokens(doc.page_content) for doc in selected_docs)
322
+ print(f"βœ… Selected {len(selected_docs)} docs with total tokens: {total_tokens}")
323
+
324
+ # STEP 4: load chain
325
+ combine_prompt_template = PromptTemplate(
326
+ template=get_combine_prompt(self.language),
327
+ input_variables=["text"]
328
+ )
329
+
330
+ print("πŸ”„ Running summarization with 'stuff' strategy...")
331
+ summary_chain = load_summarize_chain(
332
+ llm=self.llm,
333
+ chain_type='stuff',
334
+ prompt=combine_prompt_template,
335
+ verbose=False
336
+ )
337
+
338
+ # STEP 5: run the chain with token tracking
339
+ with get_openai_callback() as cb:
340
+ result = summary_chain.invoke({"input_documents": selected_docs})
341
+ input_tokens_used = cb.prompt_tokens
342
+ output_tokens_used = cb.completion_tokens
343
+ total_tokens = cb.total_tokens
344
+ print(f"🧾 Token usage: total={total_tokens}")
345
+
346
+ full_summary = result['output_text']
347
+ print("βœ… Stuff summary generated.")
348
+
349
+ # STEP 6: store the final summary
350
+ inserted = self.qdrant_manager.insert_text(
351
+ text=full_summary,
352
+ metadata={
353
+ "type": "stuff_summary",
354
+ "file_name": self.qdrant_manager.collection_name,
355
+ "language": self.language
356
+ }
357
+ )
358
+ if inserted:
359
+ print("πŸ“ Final summary saved to vector store.")
360
+
361
+ return full_summary, input_tokens_used, output_tokens_used
362
+
363
+ def _find_closest_embeddings(self,vectors, num_clusters, kmeans):
364
+ closest_indices = []
365
+ for i in range(num_clusters):
366
+ distances = np.linalg.norm(vectors - kmeans.cluster_centers_[i], axis=1)
367
+ closest_index = np.argmin(distances)
368
+ closest_indices.append(closest_index)
369
+
370
+ selected_indices = sorted(closest_indices)
371
+ return selected_indices
372
+
373
+ def _count_tokens(text,model):
374
+ if not text:
375
+ return 0
376
+ encoding = tiktoken.encoding_for_model(model)
377
+ num_tokens = len(encoding.encode(text))
378
+ return num_tokens
379
+
380
+ def _select_best_chunks(self, documents, metadata, embeddings, max_chunks=5):
381
+ assert len(documents) == len(metadata) == len(embeddings)
382
+
383
+ num_clusters = min(max_chunks, len(documents))
384
+ print(f"πŸ“Œ Selecting {num_clusters} best chunks (max={max_chunks})")
385
+
386
+ if num_clusters > 1:
387
+ kmeans = KMeans(n_clusters=num_clusters, random_state=42).fit(embeddings)
388
+ indices = self._find_closest_embeddings(embeddings, num_clusters, kmeans)
389
+ else:
390
+ indices = list(range(min(len(documents), max_chunks)))
391
+
392
+ selected = [(documents[i], metadata[i]) for i in indices[:max_chunks]]
393
+
394
+ # Ordina per chunk_index
395
+ selected = sorted(selected, key=lambda x: x[1].get("chunk_index", 0))
396
+
397
+ return [Document(page_content=d, metadata=m) for d, m in selected]
398
+
399
+ def _select_best_chunks_under_token_budget(self, documents, metadata, embeddings, llm, max_tokens, max_chunks=10):
400
+ assert len(documents) == len(metadata) == len(embeddings)
401
+
402
+ if max_chunks > len(documents):
403
+ max_chunks = len(documents)
404
+
405
+ kmeans = KMeans(n_clusters=max_chunks, random_state=42).fit(embeddings)
406
+ indices = self._find_closest_embeddings(embeddings, max_chunks, kmeans)
407
+
408
+ selected = []
409
+ total_tokens = 0
410
+
411
+ for i in indices:
412
+ doc_text = documents[i]
413
+ token_count = llm.get_num_tokens(doc_text)
414
+ if total_tokens + token_count > max_tokens:
415
+ break
416
+ selected.append((doc_text, metadata[i]))
417
+ total_tokens += token_count
418
+
419
+ selected = sorted(selected, key=lambda x: x[1].get("chunk_index", 0))
420
+
421
+ return [Document(page_content=d, metadata=m) for d, m in selected]
422
+
423
+ def _get_chunks_for_stuff(self, documents, metadata, embeddings, llm, max_tokens, fallback_max_chunks=5):
424
+ total_tokens = sum(llm.get_num_tokens(d) for d in documents)
425
+
426
+ if total_tokens <= max_tokens:
427
+ print(f"βœ… Using ALL documents ({total_tokens} tokens)")
428
+ ordered = sorted(zip(documents, metadata), key=lambda x: x[1].get("chunk_index", 0))
429
+ return [Document(page_content=d, metadata=m) for d, m in ordered]
430
+
431
+ else:
432
+ print(f"⚠️ Total tokens {total_tokens} exceed max {max_tokens} β€” selecting best subset")
433
+ return self._select_best_chunks_under_token_budget(
434
+ documents=documents,
435
+ metadata=metadata,
436
+ embeddings=embeddings,
437
+ llm=llm,
438
+ max_tokens=max_tokens,
439
+ max_chunks=fallback_max_chunks
440
+ )
utilities/vectorstore/__pycache__/QdrantLangchainManager.cpython-312.pyc ADDED
Binary file (16.1 kB). View file
 
utilities/vectorstore/__pycache__/SummaryManager.cpython-312.pyc ADDED
Binary file (21.5 kB). View file
 
utils.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from config import initialize
2
+ from utilities.vectorstore.SummaryManager import SummaryManager
3
+
4
+ def manage_collection(file_name, collection_name):
5
+
6
+ if not qdrant_manager.get_collection(collection_name):
7
+ if not qdrant_manager.create_collection(collection_name):
8
+ print("❌ Error: Failed to create collection in Qdrant. Exiting application.", flush=True)
9
+ return
10
+
11
+ success, total_tokens, text = qdrant_manager.insert_document(file_name)
12
+ if success:
13
+ print(f"βœ… Documento inserito correttamente. Token totali: {total_tokens}")
14
+ if text:
15
+ print(f"βœ… Testo completo disponibile (entro il limite token): {text[:100]}...")
16
+ else:
17
+ print("❌ Errore durante l'inserimento del documento.")
18
+
19
+ #qdrant_manager.delete_collection(collection_name)
20
+
21
+ def get_initial_summary(collection_name):
22
+ """Retrieve initial summary from a Qdrant collection."""
23
+
24
+ # Carica la collection se esiste
25
+ if not qdrant_manager.get_collection(collection_name):
26
+ print(f"❌ Collection '{collection_name}' non trovata.")
27
+ return None
28
+
29
+ # Inizializza il SummaryManager
30
+ summary_manager = SummaryManager(language="en", qdrant_manager=qdrant_manager)
31
+
32
+ # Genera il riassunto iniziale
33
+ return summary_manager.do_initial_summary()
34
+
35
+ def get_summary(collection_name, type="map_reduce"):
36
+ """Retrieve initial summary from a Qdrant collection."""
37
+
38
+ # Carica la collection se esiste
39
+ if not qdrant_manager.get_collection(collection_name):
40
+ print(f"❌ Collection '{collection_name}' non trovata.")
41
+ return None
42
+
43
+ # Inizializza il SummaryManager
44
+ summary_manager = SummaryManager(language="en", qdrant_manager=qdrant_manager)
45
+
46
+ if type == "map_reduce":
47
+ return summary_manager.do_summary_map_reduce()
48
+ elif type == "stuff":
49
+ print("Using stuff method")
50
+ return summary_manager.do_summary_stuff()
51
+ else:
52
+ return None
53
+
54
+ def chat_with_bot(llm_manager, contextualize=True):
55
+ print("πŸ€– Chatbot! Write 'exit' or 'quit' to close the conversation.\n")
56
+
57
+ try:
58
+ if contextualize:
59
+ llm_manager.initialize_conversation()
60
+ print(f"πŸ€– ELI: {llm_manager.messages[-1].content}\n")
61
+ except Exception as e:
62
+ print(f"⚠️ Could not load initial summary: {e}\n")
63
+
64
+ while True:
65
+ try:
66
+ user_input = input("πŸ‘€ You: ")
67
+ if user_input.lower() in ["exit", "quit"]:
68
+ print("πŸ‘‹ End of conversation.")
69
+ break
70
+
71
+ response = llm_manager.send_message(user_input, contextualize=contextualize)
72
+ print(f"πŸ€– ELI: {response}\n")
73
+
74
+ except KeyboardInterrupt:
75
+ print("\nπŸ‘‹ Conversation stopped.")
76
+ break
77
+
78
+ except Exception as e:
79
+ print(f"⚠️ Error: {e}\n")
80
+
81
+
82
+
83
+ llm_manager, qdrant_manager = initialize()
84
+
85
+ # file_name="data/txt/Key statisitcs startups.txt"
86
+ collection_name="key_statistics"
87
+
88
+ llm_manager, qdrant_manager = initialize()
89
+ if qdrant_manager.get_collection(collection_name):
90
+ llm_manager.set_qdrant_manager(qdrant_manager)
91
+
92
+ chat_with_bot(llm_manager)
93
+
94
+ #manage_collection(file_name, collection_name)
95
+ #summary=get_initial_summary(collection_name)
96
+ #summary=get_summary(collection_name,"map_reduce")
97
+ #summary=get_summary(collection_name,type="stuff")
98
+
99
+ # if summary:
100
+ # print(f"βœ… Summary:\n{summary}")
101
+ # else:
102
+ # print("⚠️ Nessun riassunto generato.")