Manaranjan commited on
Commit
c4d2a5f
·
verified ·
1 Parent(s): 9816b32

deploy at 2024-08-20 21:53:26.699758

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+ RUN useradd -m -u 1000 user
3
+ USER user
4
+ ENV HOME=/home/user PATH=/home/user/.local/bin:$PATH
5
+ WORKDIR /code
6
+ COPY --link --chown=1000 . .
7
+ ENV HOME=/home/user
8
+ RUN mkdir -p /tmp/cache/
9
+ RUN chmod a+rwx -R /tmp/cache/
10
+ ENV HF_HUB_CACHE=HF_HOME
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ ENV PYTHONUNBUFFERED=1 PORT=7860
14
+ CMD ["python", "main.py"]
assets/.DS_Store ADDED
Binary file (6.15 kB). View file
 
assets/circle copy.svg ADDED
assets/circle.svg ADDED
assets/icons/circle.svg ADDED
main.py ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml_hf import setup_hf_backup
2
+ from timelinestyle import TimelineStyle
3
+ import os
4
+ import json
5
+ import pandas as pd
6
+ import traceback
7
+ from datetime import datetime
8
+ from typing import Literal
9
+ from pydantic_core import from_json
10
+ from PyPDF2 import PdfReader
11
+ from langchain_core.prompts import PromptTemplate
12
+ from langchain.chains import LLMChain
13
+ from langchain.output_parsers import PydanticOutputParser
14
+ from langchain.chains.summarize import load_summarize_chain
15
+ from langchain_openai import ChatOpenAI
16
+ from langchain_anthropic import ChatAnthropic
17
+ from pydantic import BaseModel, Field, ValidationError
18
+ from langchain_openai import ChatOpenAI
19
+ from fasthtml.common import *
20
+ from fasthtml.components import Svg
21
+ from langchain_community.utilities.wikipedia import WikipediaAPIWrapper
22
+ from langchain_community.tools.wikipedia.tool import WikipediaQueryRun
23
+
24
+ # Set up the app, including daisyui and tailwind for the chat component
25
+ tlink = Script(src="https://cdn.tailwindcss.com"),
26
+ dlink = Link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.min.css")
27
+
28
+ assets_dir = "/Users/manaranjanp/Documents/Work/MyLearnings/fastHTML/llmtimeline/assets"
29
+
30
+ app = FastHTML(hdrs=(tlink, dlink, picolink))
31
+
32
+ svg = Svg(
33
+ Path(fill_rule='evenodd', d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z', clip_rule='evenodd'),
34
+ xmlns='http://www.w3.org/2000/svg',
35
+ viewbox='0 0 20 20',
36
+ fill='currentColor',
37
+ cls='h-5 w-5'
38
+ )
39
+
40
+ print(type(svg))
41
+
42
+ # Pydantic models
43
+ class Event(BaseModel):
44
+ time: datetime = Field(description="When the event occurred")
45
+ description: str = Field(description="A summary of what happened. Not more than 20 words.")
46
+ sentiment: Literal["Positive", "Negative"] = Field(..., description="Categorization of the event sentiment")
47
+
48
+ class EventResponse(BaseModel):
49
+ events: List[Event] = Field(max_length=20, description="List of events extracted from the context")
50
+
51
+ # Set up the Pydantic output parser
52
+ parser = PydanticOutputParser(pydantic_object=EventResponse)
53
+
54
+ # LangChain prompt template with format instructions
55
+ event_extraction_template = """
56
+ Extract the time based informations or events from the context and return a list of events with time, event description and event sentiment type whether it was positive or negative event.
57
+ The context may contain information about people, organization or any other entity. Try to get detailed and unique list of events as possible.
58
+
59
+ <context>
60
+ {context}
61
+ </context>
62
+
63
+ The response must follow the following schema strictly. There will be penalty for not following the schema.
64
+
65
+ <schema>
66
+ {format_instructions}
67
+ </schema>
68
+
69
+ Output:
70
+ """
71
+
72
+ event_prompt = PromptTemplate(
73
+ input_variables=["topic", "context"],
74
+ partial_variables={"format_instructions": parser.get_format_instructions()},
75
+ template=event_extraction_template
76
+ )
77
+
78
+ # Function to get the appropriate language model based on user selection
79
+ def getModel(model, key):
80
+ if(model == 'OpenAI'):
81
+ os.environ['OPENAI_API_KEY'] = key
82
+ return ChatOpenAI(temperature=0, # Set to 0 for deterministic output
83
+ model="gpt-4o-2024-08-06", # Using the GPT-4 Turbo model
84
+ max_tokens=8000) # Limit the response length
85
+ else:
86
+ os.environ['ANTHROPIC_API_KEY'] = key
87
+ return ChatAnthropic(model='claude-3-5-sonnet-20240620') # Limit the response length
88
+
89
+
90
+ # Function to generate an HTML table from the summary object
91
+ #def generate_timeline_html(timeline):
92
+ # rows = []
93
+ # for idx, tline in timeline.iterrows():
94
+ # if(tline['Sentiment'] == "Positive"):
95
+ # rows.append(Div(Div( H2(tline['Time']), P(tline['Event']), cls = 'content'), cls = "container left"))
96
+ # else:
97
+ # rows.append(Div(Div( H2(tline['Time']), P(tline['Event']), cls = 'content'), cls = "container right"))
98
+ #
99
+ # return Div(*rows, cls="timeline")
100
+
101
+
102
+ # Function to generate an HTML table from the summary object
103
+ def generate_timeline_html(timeline):
104
+ rows = []
105
+
106
+ for idx, tline in timeline.iterrows():
107
+ if idx % 2 == 0:
108
+ rows.append(Li(Div(File("./assets/circle.svg"), cls = "timeline-middle"),
109
+ Div(Time(tline['TimeStr'], cls = "font-mono italic"), Div(tline['Event'], cls = 'text-lg font-black'), cls = "timeline-start mb-10 md:text-end"),
110
+ Hr()))
111
+ else:
112
+ rows.append(Li(Div(File("./assets/circle.svg"), cls = "timeline-middle"),
113
+ Div(Time(tline['TimeStr'], cls = "font-mono italic"), Div(tline['Event'], cls = 'text-lg font-black'), cls = "timeline-end mb-10"),
114
+ Hr()))
115
+
116
+ # for idx, tline in timeline.iterrows():
117
+ # if idx % 2 == 0:
118
+ # rows.append(Li(Div(svg, cls = "timeline-middle"),
119
+ # Div(Time(tline['TimeStr'], cls = "font-mono italic"), Div(tline['Event'], cls = 'text-lg font-black'), cls = "timeline-start mb-10 md:text-end"),
120
+ # Hr()))
121
+ # else:
122
+ # rows.append(Li(Div(svg, cls = "timeline-middle"),
123
+ # Div(Time(tline['TimeStr'], cls = "font-mono italic"), Div(tline['Event'], cls = 'text-lg font-black'), cls = "timeline-end mb-10"),
124
+ # Hr()))
125
+
126
+
127
+
128
+
129
+
130
+ # for idx, tline in timeline.iterrows():
131
+ # if idx % 2 == 0:
132
+ # rows.append(Li(#Div(Img(src="/assets/icons/circle.svg", cls="w-5 h-5"), cls = "timeline-middle"),
133
+ # Div(Time(tline['TimeStr'], cls = "font-mono italic"), Div(tline['Event'], cls = 'text-lg font-black'), cls = "timeline-start timeline-box"),
134
+ # Hr()))
135
+ # else:
136
+ # rows.append(Li(#Div(Img(src="/assets/icons/circle.svg", cls="w-5 h-5"), cls = "timeline-middle"),
137
+ # Div(Time(tline['TimeStr'], cls = "font-mono italic"), Div(tline['Event'], cls = 'text-lg font-black'), cls = "timeline-end timeline-box"),
138
+ # Hr()))
139
+
140
+
141
+ return Ul(*rows, cls="timeline timeline-vertical")
142
+
143
+
144
+ def get_timeline_df(result):
145
+
146
+ results_data = []
147
+ # Parse the final result into GradedQAPair objects
148
+ try:
149
+ if not isinstance(result, EventResponse):
150
+ raise ValueError(f"Expected a list, but got {type(result)}")
151
+
152
+ except Exception as e:
153
+ print(f"An error occurred during analysis: {str(e)}")
154
+ raise
155
+
156
+ except Exception as e:
157
+ print(f"An error occurred during analysis: {str(e)}")
158
+ raise
159
+
160
+ if isinstance(result, EventResponse):
161
+ # Create a list to hold the data for the DataFrame
162
+
163
+ for event in result.events:
164
+ results_data.append({
165
+ 'Time': event.time,
166
+ 'Event': event.description,
167
+ 'Sentiment': event.sentiment
168
+ })
169
+
170
+ df = pd.DataFrame(results_data)
171
+ df = df.sort_values("Time", ascending = True).reset_index()
172
+ df['TimeStr'] = df['Time'].map(lambda x: x.strftime('%d/%m/%Y'))
173
+
174
+ return df
175
+
176
+ # Placeholder function for Q&A generation
177
+ def generate_timeline(topic, llm):
178
+ # This function will be implemented later
179
+ # For now, return a sample DataFrame
180
+
181
+ wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
182
+ wiki_content = wikipedia.run(topic)
183
+
184
+ chain = event_prompt | llm | parser
185
+
186
+ result = chain.invoke({"context" : wiki_content})
187
+
188
+ try:
189
+ # Parse the output using PydanticOutputParser
190
+ # response = parser.parse(result)
191
+ # Create the DataFrame
192
+
193
+ print(f"Results: {result}")
194
+
195
+ # timeline = parser.parse(result)
196
+
197
+ df = get_timeline_df(result)
198
+
199
+ # Optionally, save the DataFrame to a CSV file
200
+ df.to_csv(f"timeline.csv", index=True)
201
+ print("Results saved to 'results.csv'")
202
+ except Exception as e:
203
+ print(f"Error parsing LLM output: {str(e)}")
204
+ return None
205
+
206
+ return df
207
+
208
+ # Function to generate the configuration form for the web interface
209
+ def getConfigForm():
210
+ return Card(Form(hx_post="/submit", hx_target="#result", hx_swap_oob="innerHTML", hx_indicator="#indicator")(
211
+ Div(
212
+ Label(Strong("Model and Topic: "), style="color:#3498db; font-size:25px;")
213
+ ),
214
+ Div(
215
+ Span(Strong('Model: '), cls ="badge"),
216
+ Select(Option("OpenAI"), Option("Anthropic"), id="model", cls = 'select w-full max-w-xs')
217
+ ),
218
+ Div(
219
+ Span(Strong('Topic for timeline (Person/Organization/Event): '), cls ="badge"),
220
+ Input(id="secret", type="password", placeholder="Key: "),
221
+ ),
222
+ Div(
223
+ Span(Strong('Provide the topic.: '), cls ="badge"),
224
+ Input(type = 'text',
225
+ id="topic",
226
+ cls = "input w-full max-w-xs",
227
+ placeholder = "Type here")
228
+ ),
229
+ Div(
230
+ Button("Generate Timeline", cls = 'btn')
231
+ ),
232
+ Div(
233
+ Br(),
234
+ A("Developed by Manaranjan Pradhan", href="http://www.manaranjanp.com/",
235
+ target="_blank",
236
+ style = 'color: red; font-size: 16px;')
237
+ )))
238
+
239
+ # Define the route for the homepage
240
+ @app.get('/')
241
+ def homepage():
242
+ return Titled('Generate a timeline ', Grid( getConfigForm(),
243
+ Div(
244
+ Div(id="result"),
245
+ Div(Label(Strong('Generating timeline for the topic.... take a deep breath....')),
246
+ Progress(), id="indicator", cls="htmx-indicator")
247
+ )
248
+ , style="grid-template-columns: 400px 1000px; gap: 50px;"
249
+ ))
250
+
251
+ @app.get('/assets/{fname:path}.{ext}')
252
+ async def get(fname: str, ext: str):
253
+ fpath:str = (assets_dir)+'/'+str(fname)+'.'+str(ext)
254
+ if os.path.isfile(fpath):
255
+ response = FileResponse(fpath, media_type="image/svg")
256
+ print("file sent:"+fpath)
257
+ else:
258
+ print("file failed:"+fpath)
259
+ response = HTTPException(status_code=404, detail="File not found")
260
+
261
+ # Define the route for form submission
262
+ @app.post('/submit')
263
+ async def post(d:dict):
264
+ try:
265
+ # Get the appropriate language model
266
+ model = getModel(d['model'], d['secret'])
267
+
268
+ # Perform one-pass summarization
269
+ timeline_df = generate_timeline(d['topic'], model)
270
+ #qas = pd.read_csv("results_tesla.csv")
271
+
272
+ timeline_df.head(10)
273
+
274
+ # Generate and return the HTML table with the summaries
275
+ return generate_timeline_html(timeline_df)
276
+
277
+ except BaseException as e:
278
+ print(traceback.format_exc())
279
+ return str(e)
280
+
281
+ setup_hf_backup(app)
282
+
283
+ # Start the FastAPI server
284
+ serve()
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wikipedia
2
+ pydantic==2.8.2
3
+ python-fasthtml==0.4.4
4
+ fasthtml-hf==0.1.4
5
+ pandas
6
+ pypdf
7
+ PyPDF2
8
+ langchain
9
+ langchain-community
10
+ langchain-core
11
+ langchain-openai
12
+ openai
13
+ langchain_anthropic
timeline.csv ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ,index,Time,Event,Sentiment,TimeStr
2
+ 0,6,1999-01-01 00:00:00+00:00,Dhoni made his first-class debut for Bihar.,Positive,01/01/1999
3
+ 1,7,2004-12-23 00:00:00+00:00,Dhoni debuted for the Indian cricket team in an ODI.,Positive,23/12/2004
4
+ 2,0,2007-09-24 00:00:00+00:00,India won the 2007 ICC World Twenty20 under Dhoni's captaincy.,Positive,24/09/2007
5
+ 3,18,2008-01-01 00:00:00+00:00,Dhoni received the Major Dhyan Chand Khel Ratna Award.,Positive,01/01/2008
6
+ 4,8,2008-01-01 00:00:00+00:00,Dhoni became captain of the Indian cricket team in all formats.,Positive,01/01/2008
7
+ 5,11,2010-04-25 00:00:00+00:00,CSK won the IPL under Dhoni's captaincy.,Positive,25/04/2010
8
+ 6,3,2010-06-24 00:00:00+00:00,India won the Asia Cup in 2010 under Dhoni's captaincy.,Positive,24/06/2010
9
+ 7,16,2010-09-26 00:00:00+00:00,CSK won the Champions League T20 under Dhoni.,Positive,26/09/2010
10
+ 8,19,2011-01-01 00:00:00+00:00,Dhoni received honorary rank of Lieutenant Colonel.,Positive,01/01/2011
11
+ 9,1,2011-04-02 00:00:00+00:00,India won the 2011 Cricket World Cup under Dhoni's leadership.,Positive,02/04/2011
12
+ 10,12,2011-05-28 00:00:00+00:00,CSK won the IPL again with Dhoni as captain.,Positive,28/05/2011
13
+ 11,2,2013-06-23 00:00:00+00:00,India won the 2013 ICC Champions Trophy with Dhoni as captain.,Positive,23/06/2013
14
+ 12,17,2014-10-04 00:00:00+00:00,CSK won the Champions League T20 again with Dhoni.,Positive,04/10/2014
15
+ 13,9,2014-12-30 00:00:00+00:00,Dhoni retired from Test cricket.,Negative,30/12/2014
16
+ 14,4,2016-03-06 00:00:00+00:00,India won the Asia Cup in 2016 with Dhoni as captain.,Positive,06/03/2016
17
+ 15,13,2018-05-27 00:00:00+00:00,CSK won the IPL for the third time under Dhoni.,Positive,27/05/2018
18
+ 16,5,2018-09-28 00:00:00+00:00,Dhoni was part of the 2018 Asia Cup winning squad.,Positive,28/09/2018
19
+ 17,10,2019-07-10 00:00:00+00:00,Dhoni retired from international limited-overs cricket.,Negative,10/07/2019
20
+ 18,14,2021-10-15 00:00:00+00:00,CSK won the IPL for the fourth time with Dhoni.,Positive,15/10/2021
21
+ 19,15,2023-05-29 00:00:00+00:00,CSK won the IPL for the fifth time under Dhoni.,Positive,29/05/2023
timelinestyle.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class TimelineStyle:
2
+ def __init__(self):
3
+ self.style = """
4
+ * {
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ body {
9
+ background-color: #474e5d;
10
+ font-family: Helvetica, sans-serif;
11
+ }
12
+
13
+ .timeline {
14
+ position: relative;
15
+ max-width: 1200px;
16
+ margin: 0 auto;
17
+ }
18
+
19
+ .timeline::after {
20
+ content: '';
21
+ position: absolute;
22
+ width: 6px;
23
+ background-color: white;
24
+ top: 0;
25
+ bottom: 0;
26
+ left: 50%;
27
+ margin-left: -3px;
28
+ }
29
+
30
+ .container {
31
+ padding: 10px 40px;
32
+ position: relative;
33
+ background-color: inherit;
34
+ width: 50%;
35
+ }
36
+
37
+ .container::after {
38
+ content: '';
39
+ position: absolute;
40
+ width: 25px;
41
+ height: 25px;
42
+ right: -17px;
43
+ background-color: white;
44
+ border: 4px solid #FF9F55;
45
+ top: 15px;
46
+ border-radius: 50%;
47
+ z-index: 1;
48
+ }
49
+
50
+ .left {
51
+ left: 0;
52
+ }
53
+
54
+ .right {
55
+ left: 50%;
56
+ }
57
+
58
+ .left::before {
59
+ content: " ";
60
+ height: 0;
61
+ position: absolute;
62
+ top: 22px;
63
+ width: 0;
64
+ z-index: 1;
65
+ right: 30px;
66
+ border: medium solid white;
67
+ border-width: 10px 0 10px 10px;
68
+ border-color: transparent transparent transparent white;
69
+ }
70
+
71
+ .right::before {
72
+ content: " ";
73
+ height: 0;
74
+ position: absolute;
75
+ top: 22px;
76
+ width: 0;
77
+ z-index: 1;
78
+ left: 30px;
79
+ border: medium solid white;
80
+ border-width: 10px 10px 10px 0;
81
+ border-color: transparent white transparent transparent;
82
+ }
83
+
84
+ .right::after {
85
+ left: -16px;
86
+ }
87
+
88
+ .content {
89
+ padding: 20px 30px;
90
+ background-color: white;
91
+ position: relative;
92
+ border-radius: 6px;
93
+ }
94
+
95
+ @media screen and (max-width: 600px) {
96
+ /* Place the timelime to the left */
97
+ .timeline::after {
98
+ left: 31px;
99
+ }
100
+
101
+ .container {
102
+ width: 100%;
103
+ padding-left: 70px;
104
+ padding-right: 25px;
105
+ }
106
+
107
+ .container::before {
108
+ left: 60px;
109
+ border: medium solid white;
110
+ border-width: 10px 10px 10px 0;
111
+ border-color: transparent white transparent transparent;
112
+ }
113
+
114
+ .left::after, .right::after {
115
+ left: 15px;
116
+ }
117
+
118
+ .right {
119
+ left: 0%;
120
+ }
121
+ }"""
122
+
123
+ def get_style(self):
124
+ return self.style