cjber commited on
Commit
5767260
·
1 Parent(s): 2b1fd2b

fix(updates to doc format):

Browse files
.streamlit/config.toml CHANGED
@@ -1,6 +1,5 @@
1
  [theme]
2
-
3
- primaryColor="#6eb52f"
4
  backgroundColor="#f0f0f5"
5
  secondaryBackgroundColor="#e0e0ef"
6
  textColor="#262730"
 
1
  [theme]
2
+ primaryColor="#0A3D91"
 
3
  backgroundColor="#f0f0f5"
4
  secondaryBackgroundColor="#e0e0ef"
5
  textColor="#262730"
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import time
2
  from os import getenv
3
 
@@ -5,12 +6,118 @@ import polars as pl
5
  import py7zr
6
  import streamlit as st
7
  import streamlit_authenticator as stauth
 
8
 
9
  from planning_ai.common.utils import Paths
10
  from planning_ai.main import main as report_main
11
  from planning_ai.preprocessing.azure_doc import azure_process_pdfs
12
  from planning_ai.preprocessing.gcpt3 import main as preprocess_main
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  # Load authentication secrets
15
  auth = st.secrets.to_dict()
16
  if "credentials" not in auth:
@@ -32,7 +139,6 @@ if "credentials" not in auth:
32
  "expiry_days": 30,
33
  }
34
 
35
-
36
  # Initialize the authenticator
37
  authenticator = stauth.Authenticate(
38
  auth["credentials"],
@@ -66,66 +172,81 @@ def initialize_session_state():
66
 
67
  def upload_and_extract_files():
68
  """Handle file upload and extraction."""
69
- st.title("Report Builder")
70
- st.write(
71
- """
72
- This program allows you to process JDi `.json` files automatically, to extract detailed information using AI, and produce comprehensive reports. For each _'representation document'_ two AI generated documents are produced.
73
-
74
- 1. **Representation Summary** documents contain automatically generated summaries of each response, these responses are numbered.
75
 
76
- 2. **Executive Report** documents contain first an executive summary of the key points extracted from response documents, following this, a **Profile of Submissions** plots the demographic and geographic distribution of responses. Finally this document details **Themes and Policies**, where key themes and policies by response are highlighted, with notable information from responses bullet-pointed. This document contains inline citations, which relate back to the numbers associated with responses in the **Representation Summary Documents**. Citations are included to allow readers to manually verify the claims and points made by the AI model.
 
 
77
  """
78
- )
79
- st.header("Upload JDi response `.json` files")
80
- st.write("Upload your `.json` files here as a `7zip` file.")
81
- st.write("Please ensure that the `.json` files follow the correct format:")
82
 
83
- with st.expander("**File Format example**"):
 
 
 
 
 
 
 
 
 
84
  st.write(
85
- r"""
86
- ```json
87
- {
88
- "id": 10008,
89
- "method": "Paper",
90
- "respondentpostcode": "CB2 9NE",
91
- "text": "",
92
- "attachments": [
93
- {
94
- "id": 3803,
95
- "url": "http://www.cambridge.gov.uk/public/ldf/localplan2031/15417.pdf",
96
- "published": false
97
- }
98
- ],
99
- "representations": [
100
- {
101
- "id": 15417,
102
- "support/object": "Object",
103
- "document": "Issues and Options Report",
104
- "documentelementid": 29785,
105
- "documentelementtitle": "3 - Spatial Strategy, Question 3.10",
106
- "summary": "No more green belt taken away, which is prime agricultural land. Noise pollution & light pollution for surrounding villages and new houses being built, no bus services either!"
107
- },
108
- ]
109
- }
110
- ```
111
  """
 
 
112
  )
113
- if uploaded_file := st.file_uploader("Choose a `.7z` file:", type="7z"):
114
- with st.spinner("Extracting files...", show_time=True):
115
- try:
116
- # Remove old files
117
- _ = [file.unlink() for file in UPLOAD_DIR.glob("*.json")]
118
-
119
- # Extract new files
120
- with py7zr.SevenZipFile(uploaded_file, mode="r") as archive:
121
- archive.extractall(path=UPLOAD_DIR)
122
- st.session_state["files_extracted"] = True
123
- st.success(f"Extracted `{len(list(UPLOAD_DIR.glob('*.json')))}` files.")
124
- except Exception as e:
125
- st.error(f"Failed to extract files {e}")
126
-
127
-
128
- def build_report():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  """Build the report from extracted files."""
130
  # Remove old files
131
  _ = [file.unlink() for file in (Paths.OUT / "summaries").rglob("*.pdf")]
@@ -134,32 +255,35 @@ def build_report():
134
  "Once the files are extracted, click the button below to build the report.\n\n"
135
  "Do **not** close this page while the report is being built."
136
  )
137
- if st.button("Build Report", type="primary"):
138
- st.session_state["start_time"] = time.time()
139
- with st.spinner("Preprocessing files...", show_time=True):
140
- try:
141
- preprocess_main()
142
- time_taken = time.time() - st.session_state["start_time"]
143
- st.success(
144
- f"Preprocessing completed successfully in {time_taken:.1f} seconds!"
145
- )
146
- except Exception as e:
147
- st.error(f"An error occurred during preprocessing: {e}")
148
- with st.spinner("Extracting text from PDFs...", show_time=True):
149
- try:
150
- azure_process_pdfs()
151
- time_taken = time.time() - st.session_state["start_time"]
152
- st.success(
153
- f"Text extraction completed successfully in {time_taken:.1f} seconds!"
 
 
 
 
 
 
 
 
 
 
154
  )
155
- except Exception as e:
156
- st.error(f"An error occurred during PDF text extraction: {e}")
157
- with st.spinner("Building report...", show_time=True):
158
- report_main()
159
- st.session_state["end_time"] = time.time()
160
- st.session_state["completed"] = True
161
- total_time = st.session_state["end_time"] - st.session_state["start_time"]
162
- st.success(f"Report building completed in {total_time:.1f} seconds!")
163
 
164
 
165
  def display_download_buttons():
@@ -272,20 +396,39 @@ def main():
272
  handle_authentication()
273
  initialize_session_state()
274
 
275
- if st.session_state["authentication_status"]:
276
- authenticator.logout()
277
- if not st.session_state["files_extracted"]:
278
- upload_and_extract_files()
279
- if st.session_state["files_extracted"] and not st.session_state["completed"]:
280
- build_report()
281
- if st.session_state["completed"]:
282
- display_download_buttons()
283
- elif st.session_state["authentication_status"] is False:
284
  st.error("Username/password is incorrect")
285
- reset_session()
286
  elif st.session_state["authentication_status"] is None:
287
  st.warning("Please enter your username and password")
 
 
 
288
  reset_session()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
 
290
 
291
  if __name__ == "__main__":
 
1
+ import base64
2
  import time
3
  from os import getenv
4
 
 
6
  import py7zr
7
  import streamlit as st
8
  import streamlit_authenticator as stauth
9
+ from streamlit_extras.stylable_container import stylable_container
10
 
11
  from planning_ai.common.utils import Paths
12
  from planning_ai.main import main as report_main
13
  from planning_ai.preprocessing.azure_doc import azure_process_pdfs
14
  from planning_ai.preprocessing.gcpt3 import main as preprocess_main
15
 
16
+ st.set_page_config(layout="wide")
17
+ # Inject custom CSS for hiding the default logout button and adding custom footer
18
+ st.markdown(
19
+ """
20
+ <style>
21
+ /* Hide Streamlit's default header, footer, and logout button */
22
+ header {visibility: hidden;}
23
+ footer {visibility: hidden;}
24
+
25
+ /* Custom footer with logo */
26
+ .custom-footer {
27
+ position: fixed;
28
+ bottom: 10px;
29
+ right: 10px;
30
+ z-index: 100;
31
+ }
32
+
33
+ /* Styling the top bar */
34
+ .top-bar {
35
+ background-color: #0A3D91;
36
+ color: white;
37
+ padding: 15px;
38
+ font-size: 32px;
39
+ font-weight: bold;
40
+ position: fixed;
41
+ top: 0;
42
+ left: 0;
43
+ width: 100%;
44
+ z-index: 1000;
45
+ display: flex;
46
+ justify-content: space-between;
47
+ align-items: center;
48
+ }
49
+
50
+ .top-bar .title {
51
+ flex-grow: 1;
52
+ }
53
+
54
+ /* Contact button styling to match the other buttons */
55
+ .top-bar .contact-button {
56
+ background-color: #0A3D91;
57
+ border-radius: 0px;
58
+ color: white;
59
+ border: none;
60
+ padding: 10px 20px;
61
+ font-size: 16px;
62
+ cursor: pointer;
63
+ text-decoration: none; /* Remove underline */
64
+ }
65
+
66
+ .top-bar .contact-button:hover {
67
+ background-color: #045D8C;
68
+ }
69
+
70
+ /* Footer image */
71
+ .footer img {
72
+ height: 40px;
73
+ width: auto;
74
+ }
75
+ .stButton > button {
76
+ background-color: #0A3D91;
77
+ border-radius: 0px;
78
+ color: white;
79
+ border: none;
80
+ }
81
+ .stButton > button:hover {
82
+ background-color: #045D8C;
83
+ }
84
+
85
+ </style>
86
+ """,
87
+ unsafe_allow_html=True,
88
+ )
89
+
90
+
91
+ # Encode the image to base64
92
+ def get_image_base64(path):
93
+ with open(path, "rb") as image_file:
94
+ return base64.b64encode(image_file.read()).decode("utf-8")
95
+
96
+
97
+ logo_base64 = get_image_base64("logo.png")
98
+
99
+ # Add the logo in the footer using base64
100
+ st.markdown(
101
+ f"""
102
+ <div class="custom-footer">
103
+ <img src="data:image/png;base64,{logo_base64}" width="200">
104
+ </div>
105
+ """,
106
+ unsafe_allow_html=True,
107
+ )
108
+
109
+ # Top bar content
110
+ st.markdown(
111
+ """
112
+ <div class="top-bar">
113
+ <div class="title">Planning AI</div>
114
+ <a href="mailto:[email protected]" class="contact-button">Contact</a>
115
+ </div>
116
+ """,
117
+ unsafe_allow_html=True,
118
+ )
119
+
120
+
121
  # Load authentication secrets
122
  auth = st.secrets.to_dict()
123
  if "credentials" not in auth:
 
139
  "expiry_days": 30,
140
  }
141
 
 
142
  # Initialize the authenticator
143
  authenticator = stauth.Authenticate(
144
  auth["credentials"],
 
172
 
173
  def upload_and_extract_files():
174
  """Handle file upload and extraction."""
175
+ main1, main2 = st.columns(2)
 
 
 
 
 
176
 
177
+ with main1:
178
+ st.title("Introduction")
179
+ st.write(
180
  """
181
+ This program allows you to process JDi `.json` files automatically, to extract detailed information using AI, and produce comprehensive reports. For each _'representation document'_ two AI generated documents are produced.
182
+
183
+ 1. **Representation Summary** documents contain automatically generated summaries of each representation, these representations are numbered sequentially, and by their unique ID.
 
184
 
185
+ 2. **Executive Report** documents contain first an executive summary of the key points extracted from response documents, following this, a **Profile of Submissions** plots the demographic and geographic distribution of responses. Finally this document details **Themes and Policies**, where key themes and policies by response are highlighted, with notable information from responses bullet-pointed. This document contains inline citations, which relate back to the numbers associated with responses in the **Representation Summary Documents**. Citations are included to allow readers to manually verify the claims and points made by the AI model.
186
+ """
187
+ )
188
+ st.write("---")
189
+ st.title("Select Document Type")
190
+ doc_type = st.selectbox(
191
+ "Select the type of document:", ["Themes & Policies", "SPT"]
192
+ )
193
+ with main2:
194
+ st.title("Upload JDi files")
195
  st.write(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  """
197
+ 1. Upload your `.json` files here as a `7zip` file.
198
+ 2. Please ensure that the `.json` files follow the correct format:"""
199
  )
200
+
201
+ with st.expander("**File Format example**"):
202
+ st.write(
203
+ r"""
204
+ ```json
205
+ {
206
+ "id": 10008,
207
+ "method": "Paper",
208
+ "respondentpostcode": "CB2 9NE",
209
+ "text": "",
210
+ "attachments": [
211
+ {
212
+ "id": 3803,
213
+ "url": "http://www.cambridge.gov.uk/public/ldf/localplan2031/15417.pdf",
214
+ "published": false
215
+ }
216
+ ],
217
+ "representations": [
218
+ {
219
+ "id": 15417,
220
+ "support/object": "Object",
221
+ "document": "Issues and Options Report",
222
+ "documentelementid": 29785,
223
+ "documentelementtitle": "3 - Spatial Strategy, Question 3.10",
224
+ "summary": "No more green belt taken away, which is prime agricultural land. Noise pollution & light pollution for surrounding villages and new houses being built, no bus services either!"
225
+ },
226
+ ]
227
+ }
228
+ ```
229
+ """
230
+ )
231
+ if uploaded_file := st.file_uploader("Choose a `.7z` file:", type="7z"):
232
+ with st.spinner("Extracting files...", show_time=True):
233
+ try:
234
+ # Remove old files
235
+ _ = [file.unlink() for file in UPLOAD_DIR.glob("*.json")]
236
+
237
+ # Extract new files
238
+ with py7zr.SevenZipFile(uploaded_file, mode="r") as archive:
239
+ archive.extractall(path=UPLOAD_DIR)
240
+ st.session_state["files_extracted"] = True
241
+ st.success(
242
+ f"Extracted `{len(list(UPLOAD_DIR.glob('*.json')))}` files."
243
+ )
244
+ except Exception as e:
245
+ st.error(f"Failed to extract files {e}")
246
+ return doc_type
247
+
248
+
249
+ def build_report(doc_type):
250
  """Build the report from extracted files."""
251
  # Remove old files
252
  _ = [file.unlink() for file in (Paths.OUT / "summaries").rglob("*.pdf")]
 
255
  "Once the files are extracted, click the button below to build the report.\n\n"
256
  "Do **not** close this page while the report is being built."
257
  )
258
+ if not st.session_state["start_time"]:
259
+ if st.button("Build Report", type="primary"):
260
+ st.session_state["start_time"] = time.time()
261
+ with st.spinner("Preprocessing files...", show_time=True):
262
+ try:
263
+ preprocess_main()
264
+ time_taken = time.time() - st.session_state["start_time"]
265
+ st.success(
266
+ f"Preprocessing completed successfully in {time_taken:.1f} seconds!"
267
+ )
268
+ except Exception as e:
269
+ st.error(f"An error occurred during preprocessing: {e}")
270
+ with st.spinner("Extracting text from PDFs...", show_time=True):
271
+ try:
272
+ azure_process_pdfs()
273
+ time_taken = time.time() - st.session_state["start_time"]
274
+ st.success(
275
+ f"Text extraction completed successfully in {time_taken:.1f} seconds!"
276
+ )
277
+ except Exception as e:
278
+ st.error(f"An error occurred during PDF text extraction: {e}")
279
+ with st.spinner("Building report...", show_time=True):
280
+ report_main(doc_type=doc_type)
281
+ st.session_state["end_time"] = time.time()
282
+ st.session_state["completed"] = True
283
+ total_time = (
284
+ st.session_state["end_time"] - st.session_state["start_time"]
285
  )
286
+ st.success(f"Report building completed in {total_time:.1f} seconds!")
 
 
 
 
 
 
 
287
 
288
 
289
  def display_download_buttons():
 
396
  handle_authentication()
397
  initialize_session_state()
398
 
399
+ # Handle authentication states
400
+ if st.session_state["authentication_status"] is False:
 
 
 
 
 
 
 
401
  st.error("Username/password is incorrect")
 
402
  elif st.session_state["authentication_status"] is None:
403
  st.warning("Please enter your username and password")
404
+
405
+ # Reset session if not authenticated
406
+ if not st.session_state["authentication_status"]:
407
  reset_session()
408
+ return
409
+
410
+ # Authenticated user flow
411
+ with stylable_container(
412
+ key="Logout",
413
+ css_styles="""
414
+ button {
415
+ float: right;
416
+ }
417
+ """,
418
+ ):
419
+ authenticator.logout() # show logout button
420
+
421
+ # Step 1: Upload and extract files
422
+ if not st.session_state["files_extracted"]:
423
+ doc_type = upload_and_extract_files()
424
+
425
+ # Step 2: Build report if files are ready
426
+ if st.session_state["files_extracted"]:
427
+ build_report(doc_type)
428
+
429
+ # Step 3: Show download buttons when complete
430
+ if st.session_state["completed"]:
431
+ display_download_buttons()
432
 
433
 
434
  if __name__ == "__main__":
data/covers/cover2 1.pdf ADDED
Binary file (505 kB). View file
 
data/covers/cover_summary_responses.pdf ADDED
Binary file (507 kB). View file
 
data/covers/reference.odt ADDED
Binary file (8.7 kB). View file
 
packages.txt CHANGED
@@ -3,3 +3,5 @@ fonts-liberation
3
  cm-super
4
  dvipng
5
  pandoc
 
 
 
3
  cm-super
4
  dvipng
5
  pandoc
6
+ pdftk
7
+ pandoc-crossref
planning_ai/chains/map_chain.py CHANGED
@@ -52,7 +52,8 @@ def create_brief_summary_model(policy_enum: Enum) -> Type[BaseModel]:
52
  )
53
 
54
 
55
- def create_dynamic_map_chain(themes, prompt: str):
 
56
  policy_groups = []
57
  for theme in themes:
58
  if theme in THEMES_AND_POLICIES:
 
52
  )
53
 
54
 
55
+ def create_dynamic_map_chain(themes, prompt: str, doc_type: str):
56
+
57
  policy_groups = []
58
  for theme in themes:
59
  if theme in THEMES_AND_POLICIES:
planning_ai/chains/prompts/SPTs.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ TODO
planning_ai/chains/prompts/policy.txt CHANGED
@@ -11,22 +11,6 @@ Ensure that all returned details use proper sentence structure. Only include doc
11
 
12
  **Always use British English**
13
 
14
- Use these theme descriptions to help you identify whether a detail is relevant to **both** the theme and policy, if it is not, omit it completely:
15
-
16
- **Climate change:** Help Cambridge transition to net zero carbon by 2050, by ensuring that development is sited in places that help to limit carbon emissions, is designed to the highest achievable standards for energy and water use, and is resilient to current and future climate risks.
17
-
18
- **Biodiversity and green spaces:** Increase and improve our network of habitats for wildlife, and green spaces for people, ensuring that development leaves the natural environment better than it was before.
19
-
20
- **Wellbeing and social inclusion:** Help people in Greater Cambridge to lead healthier and happier lives, ensuring that everyone benefits from the development of new homes and jobs.
21
-
22
- **Great places:** Sustain the unique character of Cambridge and South Cambridgeshire, and complement it with beautiful and distinctive development, creating a place where people want to live, work and play.
23
-
24
- **Jobs:** Encourage a flourishing and mixed economy in Greater Cambridge which includes a wide range of jobs, while maintaining our area's global reputation for innovation.
25
-
26
- **Homes:** Plan for enough housing to meet our needs, including significant quantities of housing that is affordable to rent and buy, and different kinds of homes to suit our diverse communities.
27
-
28
- **Infrastructure:** Plan for transport, water, energy and digital networks; and health, education and cultural facilities; in the right places and built at the right times to serve our growing communities.
29
-
30
  ---
31
 
32
  **Provided information**
 
11
 
12
  **Always use British English**
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  ---
15
 
16
  **Provided information**
planning_ai/chapters.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ "Introduction",
3
+ "Approach to Planning Obligations",
4
+ "How to use this Supplementary Planning Document",
5
+ "Affordable Housing",
6
+ "Green Infrastructure",
7
+ "Biodiversity",
8
+ "Community Facilities",
9
+ "Social and Community Support Services",
10
+ "Libraries and Lifelong Learning",
11
+ "Transport and Highways",
12
+ "Education",
13
+ "Public Art",
14
+ "Burial Space",
15
+ "Public Open Space",
16
+ "Indoor Sports, including Swimming",
17
+ "Public Realm",
18
+ "Waste and Recycling",
19
+ "Emergency Services",
20
+ "Planning Obligations to support local employment and skills",
21
+ "Planning Obligations to support affordable workspace",
22
+ "Public Rights of Way",
23
+ "Healthcare",
24
+ "Other Potential Development Specific Requirements",
25
+ ]
planning_ai/documents/document.py CHANGED
@@ -4,9 +4,11 @@ from collections import Counter
4
 
5
  import geopandas as gpd
6
  import matplotlib.pyplot as plt
 
7
  import numpy as np
8
  import pandas as pd
9
  import polars as pl
 
10
  from matplotlib.patches import Patch
11
  from polars.dependencies import subprocess
12
 
@@ -37,8 +39,18 @@ def _process_postcodes(final):
37
  Paths.RAW / "onspd_cambridge.parquet",
38
  columns=["pcd", "osward", "lsoa11", "oa21"],
39
  ).with_columns(pl.col("pcd").str.replace_all(" ", "").alias("postcode"))
40
- postcodes = postcodes.join(onspd, on="postcode")
41
- return postcodes
 
 
 
 
 
 
 
 
 
 
42
 
43
 
44
  def _process_policies(final):
@@ -246,22 +258,23 @@ def fig_wards(postcodes, rep):
246
  camb_lads = gpd.read_parquet(Paths.RAW / "camb_lads.parquet")
247
  ward_boundaries = gpd.read_parquet(Paths.RAW / "camb_wards.parquet")
248
  ward_pcs = postcodes.group_by("osward").sum()
249
- camb_ward_boundaries = ward_boundaries[
250
- ward_boundaries["WD21CD"].isin(postcodes["osward"].unique())
251
- ]
252
  ward_boundaries_prop = ward_boundaries.merge(
253
  ward_pcs.to_pandas(), left_on="WD21CD", right_on="osward"
254
  )
255
 
256
  _, ax = plt.subplots(figsize=(8, 8))
 
 
257
  ward_boundaries_prop.plot(
258
  ax=ax,
259
  column="count",
 
260
  legend=True,
261
- legend_kwds={"label": "Number of Representations", "fmt": "{:.0f}"},
262
  )
263
- camb_lads.plot(ax=ax, color="none", edgecolor="gray", linewidth=0.5)
264
- camb_ward_boundaries.plot(ax=ax, color="none", edgecolor="black", linewidth=0.5)
 
265
 
266
  plt.axis("off")
267
  plt.tight_layout()
@@ -335,8 +348,44 @@ def build_final_report(out, rep):
335
  .collect()
336
  )
337
  unused_documents = out["generate_final_report"]["unused_documents"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  support_policies, object_policies, other_policies = _process_policies(final)
339
- postcodes = _process_postcodes(final)
340
  stances = _process_stances(responses)
341
  themes = _process_themes(final)
342
 
@@ -344,8 +393,16 @@ def build_final_report(out, rep):
344
  fig_oa(postcodes, rep)
345
  fig_imd(postcodes, rep)
346
 
 
 
 
 
 
 
 
 
347
  quarto_doc = (
348
- f"---\ntitle: '**Summary of Submitted Representations: {rep}**'\n"
349
  r"""
350
  mainfont: Liberation Sans
351
  fontsize: 12pt
@@ -380,20 +437,21 @@ header-includes: |
380
  """
381
  "\n# Executive Summary\n\n"
382
  f"{final['executive']}\n\n"
383
- f"There were a total of {len(responses):,} responses. Of these, submissions indicated "
384
- "the following support and objection of the plan:\n\n"
385
  f"{stances}\n\n"
386
  "# Introduction\n\n"
387
  f"{introduction_paragraph}\n\n"
388
  "\n# Profile of Submissions\n\n"
389
  f"{figures_paragraph}\n\n"
390
- f"![Total number of representations submitted by Ward\\label{{fig-wards}}](./data/out/summary/figs/wards-{rep}.pdf)\n\n"
391
- f"![Proportional frequency of representations submitted by 2021 Output Area\\label{{fig-oas}}](./data/out/summary/figs/oas-{rep}.pdf)\n\n"
392
- f"![Distribution of representations submitted by quintile of index of multiple deprivation (2019)\\label{{fig-imd}}](./data/out/summary/figs/imd_decile-{rep}.pdf)\n\n"
 
393
  r"\newpage"
394
  "\n\n# Themes and Policies\n\n"
395
  f"{themes_paragraph}\n\n"
396
- f"{themes}{{#tbl-themes}}\n\n"
397
  "## Supporting Representations\n\n"
398
  "The following section presents a list of all points raised in representations that support the plan"
399
  ", grouped by theme and policy.\n\n"
@@ -407,13 +465,9 @@ header-includes: |
407
  "or object to the plan, grouped by theme and policy.\n\n"
408
  f"{other_policies or '_No other representations._'}\n\n"
409
  "## Unused Documents\n\n"
410
- "Please note that the following documents were not used to produce this report:\n\n"
411
- f"{str(unused_documents)}\n\n"
412
- "Documents are excluded if they provide no relevant information. These documents "
413
- "are typically very short, and contain information that provides no relation to policies or themes."
414
- "Unused document numbers relate to the 'id' column for 'text' submissions. For pdfs this 'id' "
415
- "is combined with the page number, separated by '999'. For example: 175933-999-12 refers "
416
- "to page 12 of the document attached to representation 175933."
417
  )
418
 
419
  out_path = Paths.SUMMARY / f"Summary_of_Submitted_Representations-{rep}.md"
@@ -427,9 +481,39 @@ header-includes: |
427
  "-o",
428
  f"{out_file}.pdf",
429
  "--pdf-engine=xelatex",
 
430
  ]
431
  subprocess.run(command, check=True, capture_output=True)
432
- command = ["pandoc", f"{out_path}", "-o", f"{out_file}.docx"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  subprocess.run(command, check=True, capture_output=True)
434
  except subprocess.CalledProcessError as e:
435
  logging.error(
@@ -440,15 +524,52 @@ header-includes: |
440
  def build_summaries_document(out, rep):
441
  sub = r"Document ID: \[\d+\]\n\n"
442
  summary_intro = load_txt("planning_ai/documents/summary_intro.txt")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
  full_text = "".join(
444
  f"**Document ID**: {document['doc_id']}\n\n"
445
  f"**Representations ID**: {document['document'].metadata['id']}\n\n"
446
- f"**Summarised Document**\n\n{re.sub(sub, '', document['summary'].summary)}\n\n"
 
447
  "---\n\n"
448
  for document in out["generate_final_report"]["documents"]
449
  )
450
  header = (
451
- f"---\ntitle: '**Summary Documents: {rep}**'\n"
452
  r"""
453
  mainfont: Liberation Sans
454
  fontsize: 12pt
@@ -483,10 +604,17 @@ header-includes: |
483
  """
484
  f"\n{summary_intro}\n\n"
485
  )
 
 
 
 
 
 
 
486
  out_path = Paths.SUMMARY / f"Summary_Documents-{rep}.md"
487
  out_file = Paths.SUMMARY / f"Summary_Documents-{rep}"
488
  with open(out_path, "w") as f:
489
- f.write(f"{header}{full_text}")
490
 
491
  try:
492
  command = [
@@ -495,9 +623,39 @@ header-includes: |
495
  "-o",
496
  f"{out_file}.pdf",
497
  "--pdf-engine=xelatex",
 
498
  ]
499
  subprocess.run(command, check=True, capture_output=True)
500
- command = ["pandoc", f"{out_path}", "-o", f"{out_file}.docx"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  subprocess.run(command, check=True, capture_output=True)
502
  except subprocess.CalledProcessError as e:
503
  logging.error(f"Error during render: {e}")
 
4
 
5
  import geopandas as gpd
6
  import matplotlib.pyplot as plt
7
+ import matplotlib.ticker as mticker
8
  import numpy as np
9
  import pandas as pd
10
  import polars as pl
11
+ import requests
12
  from matplotlib.patches import Patch
13
  from polars.dependencies import subprocess
14
 
 
39
  Paths.RAW / "onspd_cambridge.parquet",
40
  columns=["pcd", "osward", "lsoa11", "oa21"],
41
  ).with_columns(pl.col("pcd").str.replace_all(" ", "").alias("postcode"))
42
+ postcodes = postcodes.join(onspd, on="postcode", how="left")
43
+ outside_pcs = postcodes.filter(pl.col("osward").is_null()).drop_nulls("postcode")
44
+ pcs_url = "https://api.postcodes.io/postcodes"
45
+ outside_pcs = outside_pcs.with_columns(
46
+ pl.col("postcode")
47
+ .map_elements(
48
+ lambda x: requests.get(f"{pcs_url}/{x}").json()["result"]["admin_ward"],
49
+ return_dtype=pl.String,
50
+ )
51
+ .alias("osward")
52
+ )
53
+ return postcodes.drop_nulls(subset=["osward"]), outside_pcs
54
 
55
 
56
  def _process_policies(final):
 
258
  camb_lads = gpd.read_parquet(Paths.RAW / "camb_lads.parquet")
259
  ward_boundaries = gpd.read_parquet(Paths.RAW / "camb_wards.parquet")
260
  ward_pcs = postcodes.group_by("osward").sum()
 
 
 
261
  ward_boundaries_prop = ward_boundaries.merge(
262
  ward_pcs.to_pandas(), left_on="WD21CD", right_on="osward"
263
  )
264
 
265
  _, ax = plt.subplots(figsize=(8, 8))
266
+ ward_boundaries.plot(ax=ax, color="none", edgecolor="black", linewidth=1.5)
267
+ camb_lads.plot(ax=ax, color="white", edgecolor="gray", linewidth=0.5)
268
  ward_boundaries_prop.plot(
269
  ax=ax,
270
  column="count",
271
+ edgecolor="none",
272
  legend=True,
273
+ legend_kwds={"label": "Number of Representations"},
274
  )
275
+ ward_boundaries.plot(ax=ax, color="none", edgecolor="grey", linewidth=0.5)
276
+ cbar = ax.get_figure().axes[-1] # Get the colorbar axis
277
+ cbar.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f"{int(x)}"))
278
 
279
  plt.axis("off")
280
  plt.tight_layout()
 
348
  .collect()
349
  )
350
  unused_documents = out["generate_final_report"]["unused_documents"]
351
+
352
+ unused_pdfs = (
353
+ pl.DataFrame(
354
+ [
355
+ doc["metadata"]
356
+ for doc in unused_documents
357
+ if "representations_id" not in doc["metadata"]
358
+ ]
359
+ )
360
+ .select(["id", "pdf_id", "page_label"])
361
+ .rename({"pdf_id": "representations_id"})
362
+ .with_columns(
363
+ pl.col("representations_id").cast(pl.Int64), pl.lit("PDF").alias("type")
364
+ )
365
+ )
366
+ unused_docs = (
367
+ pl.DataFrame(
368
+ [
369
+ doc["metadata"]
370
+ for doc in unused_documents
371
+ if "representations_id" in doc["metadata"]
372
+ ]
373
+ )
374
+ .select(["id", "representations_id"])
375
+ .with_columns(pl.lit("").alias("page_label"), pl.lit("Text").alias("type"))
376
+ )
377
+ unused_tbl = pl.concat([unused_pdfs, unused_docs]).rename(
378
+ {
379
+ "id": "JDi ID",
380
+ "representations_id": "Representations ID",
381
+ "page_label": "Page Number",
382
+ "type": "Type",
383
+ }
384
+ )
385
+ unused_tbl = unused_tbl.to_pandas().to_markdown(index=False)
386
+
387
  support_policies, object_policies, other_policies = _process_policies(final)
388
+ postcodes, outside_pcs = _process_postcodes(final)
389
  stances = _process_stances(responses)
390
  themes = _process_themes(final)
391
 
 
393
  fig_oa(postcodes, rep)
394
  fig_imd(postcodes, rep)
395
 
396
+ outside_pcs = (
397
+ outside_pcs.group_by("osward")
398
+ .sum()[["osward", "count"]]
399
+ .rename({"osward": "Ward", "count": "Number of Representations"})
400
+ .to_pandas()
401
+ .to_markdown(index=False)
402
+ )
403
+
404
  quarto_doc = (
405
+ f"---\ntitle: '**{rep}**'\n"
406
  r"""
407
  mainfont: Liberation Sans
408
  fontsize: 12pt
 
437
  """
438
  "\n# Executive Summary\n\n"
439
  f"{final['executive']}\n\n"
440
+ f"There were a total of {len(responses):,} responses. Of these, representations left "
441
+ "comment, or indicated the following support and objection of the plan:\n\n"
442
  f"{stances}\n\n"
443
  "# Introduction\n\n"
444
  f"{introduction_paragraph}\n\n"
445
  "\n# Profile of Submissions\n\n"
446
  f"{figures_paragraph}\n\n"
447
+ f"![Total number of representations submitted by Ward within Greater Cambridgeshire\\label{{fig-wards}}](./data/out/summary/figs/wards-{rep}.pdf)\n\n"
448
+ f": Postcodes outside the Greater Cambridge Ward areas {{#tbl:outside}}\n\n{outside_pcs}n\n"
449
+ f"![Proportional frequency of representations submitted by 2021 Output Area relative to the national average\\label{{fig-oas}}](./data/out/summary/figs/oas-{rep}.pdf)\n\n"
450
+ f"![Distribution of representations submitted by the of index of multiple deprivation (2019) relative to the national average\\label{{fig-imd}}](./data/out/summary/figs/imd_decile-{rep}.pdf)\n\n"
451
  r"\newpage"
452
  "\n\n# Themes and Policies\n\n"
453
  f"{themes_paragraph}\n\n"
454
+ f": Breakdown of representation themes {{#tbl:themes}}\n\n{themes}\n\n"
455
  "## Supporting Representations\n\n"
456
  "The following section presents a list of all points raised in representations that support the plan"
457
  ", grouped by theme and policy.\n\n"
 
465
  "or object to the plan, grouped by theme and policy.\n\n"
466
  f"{other_policies or '_No other representations._'}\n\n"
467
  "## Unused Documents\n\n"
468
+ "For full transparency, this section details those documents that were excluded from this report on Table @tbl:unused."
469
+ "These documents are typically very short, and contain information that provides no relation to policies or themes.\n\n TODO: expand."
470
+ f": Unused representations {{#tbl:unused}}\n\n{unused_tbl}\n\n"
 
 
 
 
471
  )
472
 
473
  out_path = Paths.SUMMARY / f"Summary_of_Submitted_Representations-{rep}.md"
 
481
  "-o",
482
  f"{out_file}.pdf",
483
  "--pdf-engine=xelatex",
484
+ "--filter=pandoc-crossref",
485
  ]
486
  subprocess.run(command, check=True, capture_output=True)
487
+ command = [
488
+ "pdftk",
489
+ "data/covers/cover2 1.pdf",
490
+ f"{out_file}.pdf",
491
+ "cat",
492
+ "output",
493
+ f"{out_file}-cover.pdf",
494
+ ]
495
+ subprocess.run(command, check=True, capture_output=True)
496
+ command = ["mv", "-f", f"{out_file}-cover.pdf", f"{out_file}.pdf"]
497
+ subprocess.run(command, check=True, capture_output=True)
498
+
499
+ command = [
500
+ "pandoc",
501
+ f"{out_path}",
502
+ "-o",
503
+ f"{out_file}.docx",
504
+ "--reference-doc=data/covers/reference.docx",
505
+ "--filter=pandoc-crossref",
506
+ ]
507
+ subprocess.run(command, check=True, capture_output=True)
508
+ command = [
509
+ "pandoc",
510
+ "data/covers/Cover2 1.docx",
511
+ f"{out_file}.docx",
512
+ "-o",
513
+ f"{out_file}.docx",
514
+ "--reference-doc=data/covers/reference.docx",
515
+ "--filter=pandoc-crossref",
516
+ ]
517
  subprocess.run(command, check=True, capture_output=True)
518
  except subprocess.CalledProcessError as e:
519
  logging.error(
 
524
  def build_summaries_document(out, rep):
525
  sub = r"Document ID: \[\d+\]\n\n"
526
  summary_intro = load_txt("planning_ai/documents/summary_intro.txt")
527
+ unused_documents = out["generate_final_report"]["unused_documents"]
528
+
529
+ unused_pdfs = (
530
+ pl.DataFrame(
531
+ [
532
+ doc["metadata"]
533
+ for doc in unused_documents
534
+ if "representations_id" not in doc["metadata"]
535
+ ]
536
+ )
537
+ .select(["id", "pdf_id", "page_label"])
538
+ .rename({"pdf_id": "representations_id"})
539
+ .with_columns(
540
+ pl.col("representations_id").cast(pl.Int64), pl.lit("PDF").alias("type")
541
+ )
542
+ )
543
+ unused_docs = (
544
+ pl.DataFrame(
545
+ [
546
+ doc["metadata"]
547
+ for doc in unused_documents
548
+ if "representations_id" in doc["metadata"]
549
+ ]
550
+ )
551
+ .select(["id", "representations_id"])
552
+ .with_columns(pl.lit("").alias("page_label"), pl.lit("Text").alias("type"))
553
+ )
554
+ unused_tbl = pl.concat([unused_pdfs, unused_docs]).rename(
555
+ {
556
+ "id": "JDi ID",
557
+ "representations_id": "Representations ID",
558
+ "page_label": "Page Number",
559
+ "type": "Type",
560
+ }
561
+ )
562
+ unused_tbl = unused_tbl.to_pandas().to_markdown(index=False)
563
  full_text = "".join(
564
  f"**Document ID**: {document['doc_id']}\n\n"
565
  f"**Representations ID**: {document['document'].metadata['id']}\n\n"
566
+ f"**Representations Name**: {document['document'].metadata['representations_document']}\n\n"
567
+ f"\n\n{re.sub(sub, '', document['summary'].summary)}\n\n"
568
  "---\n\n"
569
  for document in out["generate_final_report"]["documents"]
570
  )
571
  header = (
572
+ f"---\ntitle: '**{rep}**'\n"
573
  r"""
574
  mainfont: Liberation Sans
575
  fontsize: 12pt
 
604
  """
605
  f"\n{summary_intro}\n\n"
606
  )
607
+ unused_text = (
608
+ "\n\n## Unused Documents\n\n"
609
+ "For full transparency, this section details those documents that were excluded from this report on Table @tbl:unused.\n\n"
610
+ f": Unused representations {{#tbl:unused}}\n\n{unused_tbl}\n\n"
611
+ "These documents are typically very short, and contain information that provides no relation to policies or themes."
612
+ )
613
+
614
  out_path = Paths.SUMMARY / f"Summary_Documents-{rep}.md"
615
  out_file = Paths.SUMMARY / f"Summary_Documents-{rep}"
616
  with open(out_path, "w") as f:
617
+ f.write(f"{header}{full_text}{unused_text}")
618
 
619
  try:
620
  command = [
 
623
  "-o",
624
  f"{out_file}.pdf",
625
  "--pdf-engine=xelatex",
626
+ "--filter=pandoc-crossref",
627
  ]
628
  subprocess.run(command, check=True, capture_output=True)
629
+ command = [
630
+ "pdftk",
631
+ "data/covers/cover_summary_responses.pdf",
632
+ f"{out_file}.pdf",
633
+ "cat",
634
+ "output",
635
+ f"{out_file}-cover.pdf",
636
+ ]
637
+ subprocess.run(command, check=True, capture_output=True)
638
+ command = ["mv", "-f", f"{out_file}-cover.pdf", f"{out_file}.pdf"]
639
+ subprocess.run(command, check=True, capture_output=True)
640
+
641
+ command = [
642
+ "pandoc",
643
+ f"{out_path}",
644
+ "-o",
645
+ f"{out_file}.docx",
646
+ "--reference-doc=data/covers/reference.docx",
647
+ "--filter=pandoc-crossref",
648
+ ]
649
+ subprocess.run(command, check=True, capture_output=True)
650
+ command = [
651
+ "pandoc",
652
+ "data/covers/Cover 1.docx",
653
+ f"{out_file}.docx",
654
+ "-o",
655
+ f"{out_file}.docx",
656
+ "--reference-doc=data/covers/reference.docx",
657
+ "--filter=pandoc-crossref",
658
+ ]
659
  subprocess.run(command, check=True, capture_output=True)
660
  except subprocess.CalledProcessError as e:
661
  logging.error(f"Error during render: {e}")
planning_ai/documents/introduction.txt CHANGED
@@ -1 +1,7 @@
1
- This report was produced using a generative pre-trained transformer (GPT) large-language model (LLM) to produce a summary of all responses to the related policy document. This model automatically reviews every response in detail, and extracts key information to inform decision making. This document first consolidates this information into a single-page executive summary, highlighting areas of particular interest to consider, and the broad consensus of responses. Figures generated from responses then give both a geographic and statistical overview, highlighting any demographic imbalances in responses. The document then extracts detailed information from responses, grouped by theme and policy. In this section we incorporate citations which relate with the 'Summary Responses' document, to increase transparency.
 
 
 
 
 
 
 
1
+ This report was produced using a generative pre-trained transformer (GPT) large-language model (LLM) to produce a summary of all responses to the related policy document. This model automatically reviews every response in detail and extracts key information to inform decision making.
2
+
3
+ The document is structured to first consolidate all information gathered from the representations to produce a single-page executive summary. This highlights areas of particular interest to consider, and the broad consensus specified by those giving the representations.
4
+
5
+ The postcode location for the member of the public or organisation who submitted a representation are used to produce a map, showing the frequency of the representations by Ward within the Greater Cambridgeshire area. Representations from outside of this area are listed rather than mapped. The postcodes enable a link to the Index of Multiple Deprivation and a geodemographic (Output Area Classification) to create graphs showing how the frequency of representations varies by areas that have different socio-economic and demographic characteristics.
6
+
7
+ The document then presents a summary of information from the responses, grouped by each Theme and Policy as they appear in the Greater Cambridgeshire local plan. In this section we incorporate citations which relate with the ‘Summary Responses’ document, to increase transparency.
planning_ai/documents/summary_intro.txt CHANGED
@@ -1,7 +1,5 @@
1
- This document provides a summary of each representation, along with the **Document ID** which corresponds with the citations in the corresponding **Summary of Submitted Representations** document. Each summary also provides the **Representations ID** which corresponds with the `id` column in the `.json` JDi files. This allows the user to link the produced summaries back to the original documents.
2
 
3
- Note that PDF documents have been split by pages into multiple **Document ID**, but will share the same **Representations ID**.
4
 
5
  ---
6
-
7
-
 
1
+ This document provides a summary of each representation. A Representation ID is provided which corresponds with the ID column in the .json JDi files. A representation contains both a text submission and optionally an attachment which can take varying forms. The tool processes text data only, so does not process images such as photographs, although these can be explored in the original representations submitted. For representations with attachments that are not considered by this summary, these are listed at the end of the report.
2
 
3
+ Every attachment is split into separate pages for processing, with each page given a Document ID. As such, attachments that have been split by pages into multiple Document ID will share the same Representations ID. This specification of both Representation ID and Document ID allows the user to link the produced summaries back to the original documents.
4
 
5
  ---
 
 
planning_ai/documents/themes.txt CHANGED
@@ -1 +1 @@
1
- The following section provides a detailed breakdown of notable details from responses, grouped by themes and policies. Both themes and associated policies are automatically determined through an analysis of the summary content by an LLM agent. Each theme is grouped by whether responses are supporting, opposed, or a general comment. This section aims to give a comprehensive view of the key issues raised by the respondents with respect to the themes and policies outlined. We have incorporated citations into each point (see numbers in square brackets) which relate to the specific document they were made in, to promote the transparency of where information was sourced from. @tbl-themes gives a breakdown of the number of submissions that relate with each theme, submissions may relate to more than one theme.
 
1
+ The following section provides a detailed breakdown of notable details from the **representations**, grouped by the **Themes and Policies** set out in the **Greater Cambridgeshire Local Plan**. Both the Themes and associated Policies are automatically determined through an analysis of the summary content by an LLM agent. Each Theme is organised according to whether representations were specified as supportive, opposed, or provide a general comment. This section offers a comprehensive overview of those key issues raised by members of the public or organisations with respect to these Themes and Policies. We have incorporated citations into each point (see numbers in square brackets) to indicate the specific document^[Each representation made can have a number of documents associated with them. Each document ID is unique, and can be referenced in the **Summary Responses** report.] where each representation was made, thereby promoting transparency of sources. Finally, @tbl-themes provides a breakdown of the number of submissions that relate to each Theme (noting that submissions may be associated with more than one Theme).
planning_ai/graph.py CHANGED
@@ -22,7 +22,6 @@ def create_graph():
22
 
23
  # graph.add_edge(START, "add_entities")
24
  graph.add_conditional_edges(START, map_documents, ["generate_summary"])
25
- # graph.add_conditional_edges("add_entities", map_documents, ["generate_summary"])
26
  graph.add_conditional_edges("generate_summary", map_check, ["check_hallucination"])
27
  graph.add_conditional_edges("check_hallucination", map_fix, ["fix_hallucination"])
28
  graph.add_conditional_edges("fix_hallucination", map_check, ["check_hallucination"])
 
22
 
23
  # graph.add_edge(START, "add_entities")
24
  graph.add_conditional_edges(START, map_documents, ["generate_summary"])
 
25
  graph.add_conditional_edges("generate_summary", map_check, ["check_hallucination"])
26
  graph.add_conditional_edges("check_hallucination", map_fix, ["fix_hallucination"])
27
  graph.add_conditional_edges("fix_hallucination", map_check, ["check_hallucination"])
planning_ai/main.py CHANGED
@@ -13,7 +13,7 @@ from planning_ai.graph import create_graph
13
  from planning_ai.logging import logger
14
 
15
 
16
- def read_docs(representations_document: str):
17
  logger.warning("Reading documents...")
18
  df = (
19
  pl.scan_parquet(Paths.STAGING / "gcpt3.parquet")
@@ -35,7 +35,14 @@ def read_docs(representations_document: str):
35
  continue
36
  meta = (
37
  df.filter(pl.col("attachments_id") == int(pdf.metadata["pdf_id"]))
38
- .select(["id", "respondentpostcode", "representations_support/object"])
 
 
 
 
 
 
 
39
  .to_dict(as_series=False)
40
  )
41
  pdf.metadata = pdf.metadata | {
@@ -48,6 +55,11 @@ def read_docs(representations_document: str):
48
  if meta["representations_support/object"]
49
  else ""
50
  ),
 
 
 
 
 
51
  }
52
  # for now concat page number to keep all pdf pages separate. might want
53
  # to instead combine pdfs somehow
@@ -71,10 +83,13 @@ def read_docs(representations_document: str):
71
  if doc.page_content and len(doc.page_content.split(" ")) > 25
72
  }.values()
73
  )
74
- return [{"document": doc, "filename": doc.metadata["filename"]} for doc in docs]
 
 
 
75
 
76
 
77
- def main():
78
  representations_documents = (
79
  pl.scan_parquet(Paths.STAGING / "gcpt3.parquet")
80
  .select(pl.col("representations_document"))
@@ -83,7 +98,7 @@ def main():
83
  .to_list()
84
  )
85
  for rep in representations_documents:
86
- docs = read_docs(rep)
87
  n_docs = len(docs)
88
 
89
  logger.info(f"{n_docs} documents being processed!")
 
13
  from planning_ai.logging import logger
14
 
15
 
16
+ def read_docs(representations_document: str, doc_type: str):
17
  logger.warning("Reading documents...")
18
  df = (
19
  pl.scan_parquet(Paths.STAGING / "gcpt3.parquet")
 
35
  continue
36
  meta = (
37
  df.filter(pl.col("attachments_id") == int(pdf.metadata["pdf_id"]))
38
+ .select(
39
+ [
40
+ "id",
41
+ "respondentpostcode",
42
+ "representations_support/object",
43
+ "representations_document",
44
+ ]
45
+ )
46
  .to_dict(as_series=False)
47
  )
48
  pdf.metadata = pdf.metadata | {
 
55
  if meta["representations_support/object"]
56
  else ""
57
  ),
58
+ "representations_document": (
59
+ (meta["representations_document"][0])
60
+ if meta["representations_document"]
61
+ else ""
62
+ ),
63
  }
64
  # for now concat page number to keep all pdf pages separate. might want
65
  # to instead combine pdfs somehow
 
83
  if doc.page_content and len(doc.page_content.split(" ")) > 25
84
  }.values()
85
  )
86
+ return [
87
+ {"document": doc, "filename": doc.metadata["filename"], "doc_type": doc_type}
88
+ for doc in docs
89
+ ]
90
 
91
 
92
+ def main(doc_type: str = "Themes & Policies"):
93
  representations_documents = (
94
  pl.scan_parquet(Paths.STAGING / "gcpt3.parquet")
95
  .select(pl.col("representations_document"))
 
98
  .to_list()
99
  )
100
  for rep in representations_documents:
101
+ docs = read_docs(rep, doc_type)
102
  n_docs = len(docs)
103
 
104
  logger.info(f"{n_docs} documents being processed!")
planning_ai/nodes/map_node.py CHANGED
@@ -101,8 +101,13 @@ def generate_summary(state: DocumentState) -> dict:
101
 
102
  logger.info(f"Starting PII removal for: {state['filename']}")
103
  state["document"].page_content = remove_pii(state["document"].page_content)
104
- logger.info(f"Retrieving themes for: {state['filename']}")
105
- state = retrieve_themes(state)
 
 
 
 
 
106
 
107
  if not state["themes"]:
108
  logger.warning(f"No themes found for {state['filename']}")
 
101
 
102
  logger.info(f"Starting PII removal for: {state['filename']}")
103
  state["document"].page_content = remove_pii(state["document"].page_content)
104
+
105
+ if state["doc_type"] == "Themes & Policies":
106
+ logger.info(f"Retrieving themes for: {state['filename']}")
107
+ state = retrieve_themes(state)
108
+ elif state["doc_type"] == "SPT":
109
+ logger.info(f"Retrieving SPT for: {state['filename']}")
110
+ state = retrieve_spt(state)
111
 
112
  if not state["themes"]:
113
  logger.warning(f"No themes found for {state['filename']}")
planning_ai/nodes/reduce_node.py CHANGED
@@ -79,10 +79,10 @@ def batch_generate_executive_summaries(summaries):
79
  f"Document ID: {[s['doc_id']]}\n\n{s['summary'].summary}" for s in summaries
80
  ]
81
  final_responses = []
82
- batch_size = 25
83
  for i in range(0, len(summaries_text), batch_size):
84
  logger.info(
85
- f"Processing batches... {int(i / 25) + 1}/{(len(summaries_text) // batch_size) + 1}"
86
  )
87
  batch = summaries_text[i : i + batch_size]
88
  response = reduce_chain.invoke({"context": batch})
@@ -131,10 +131,7 @@ def generate_final_report(state: OverallState):
131
  def final_output(final_docs):
132
  docs = [doc for doc in final_docs if not doc["failed"]]
133
 
134
- # TODO: say which docs are not considered in the final report
135
- failed_docs = [
136
- doc["document"].metadata["filename"] for doc in final_docs if doc["failed"]
137
- ]
138
  docs = add_doc_id(docs)
139
 
140
  policy_groups = extract_policies_from_docs(docs)
 
79
  f"Document ID: {[s['doc_id']]}\n\n{s['summary'].summary}" for s in summaries
80
  ]
81
  final_responses = []
82
+ batch_size = 50
83
  for i in range(0, len(summaries_text), batch_size):
84
  logger.info(
85
+ f"Processing batches... {int(i / 50) + 1}/{(len(summaries_text) // batch_size) + 1}"
86
  )
87
  batch = summaries_text[i : i + batch_size]
88
  response = reduce_chain.invoke({"context": batch})
 
131
  def final_output(final_docs):
132
  docs = [doc for doc in final_docs if not doc["failed"]]
133
 
134
+ failed_docs = [doc["document"].model_dump() for doc in final_docs if doc["failed"]]
 
 
 
135
  docs = add_doc_id(docs)
136
 
137
  policy_groups = extract_policies_from_docs(docs)
planning_ai/preprocessing/gcpt3.py CHANGED
@@ -4,6 +4,7 @@ from pathlib import Path
4
  from typing import Any
5
 
6
  import polars as pl
 
7
  import requests
8
  from pypdf import PdfReader
9
  from tqdm import tqdm
@@ -61,6 +62,7 @@ def process_files(files: list[Path], schema: dict[str, Any]) -> None:
61
 
62
  def download_attachments():
63
  df = pl.read_parquet(Paths.STAGING / "gcpt3.parquet")
 
64
 
65
  existing_files = {f.stem for f in (Paths.RAW / "pdfs").glob("*.pdf")}
66
 
@@ -87,10 +89,20 @@ def download_attachments():
87
  response = requests.get(row["attachments_url"], timeout=3)
88
  response.raise_for_status()
89
 
90
- PdfReader(BytesIO(response.content)) # check if pdf is valid
91
- with open(file_path, "wb") as f:
92
- f.write(response.content)
93
- print(f"Downloaded {row['attachments_url']} to {file_path}")
 
 
 
 
 
 
 
 
 
 
94
 
95
  except requests.RequestException as e:
96
  logging.error(f"RequestException for {row['attachments_url']}: {e}")
 
4
  from typing import Any
5
 
6
  import polars as pl
7
+ import pypandoc
8
  import requests
9
  from pypdf import PdfReader
10
  from tqdm import tqdm
 
62
 
63
  def download_attachments():
64
  df = pl.read_parquet(Paths.STAGING / "gcpt3.parquet")
65
+ df.columns
66
 
67
  existing_files = {f.stem for f in (Paths.RAW / "pdfs").glob("*.pdf")}
68
 
 
89
  response = requests.get(row["attachments_url"], timeout=3)
90
  response.raise_for_status()
91
 
92
+ response_type = response.headers.get("content-type")
93
+ if response_type == "application/pdf":
94
+ PdfReader(BytesIO(response.content)) # check if pdf is valid
95
+ with open(file_path, "wb") as f:
96
+ f.write(response.content)
97
+ print(f"Downloaded {row['attachments_url']} to {file_path}")
98
+ elif (
99
+ response_type
100
+ == "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
101
+ ):
102
+ pypandoc.convert_file(response.content, "pdf", outputfile=file_path)
103
+ else:
104
+ logging.error(f"Response returned {response_type}, not saving.")
105
+ continue
106
 
107
  except requests.RequestException as e:
108
  logging.error(f"RequestException for {row['attachments_url']}: {e}")
planning_ai/states.py CHANGED
@@ -11,6 +11,7 @@ from planning_ai.common.utils import filename_reducer
11
  class DocumentState(TypedDict):
12
  document: Document
13
  filename: int
 
14
 
15
  entities: list[dict]
16
  themes: list[dict]
 
11
  class DocumentState(TypedDict):
12
  document: Document
13
  filename: int
14
+ doc_type: str
15
 
16
  entities: list[dict]
17
  themes: list[dict]
pyproject.toml CHANGED
@@ -36,8 +36,9 @@ dependencies = [
36
  "streamlit>=1.41.1",
37
  "streamlit-authenticator>=0.4.1",
38
  "py7zr>=0.22.0",
39
- "en_core_web_lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl"
40
-
 
41
  ]
42
 
43
  [tool.uv]
 
36
  "streamlit>=1.41.1",
37
  "streamlit-authenticator>=0.4.1",
38
  "py7zr>=0.22.0",
39
+ "en_core_web_lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl",
40
+ "pypandoc>=1.15",
41
+ "streamlit-extras>=0.5.5",
42
  ]
43
 
44
  [tool.uv]
uv.lock CHANGED
@@ -13,11 +13,11 @@ wheels = [
13
 
14
  [[package]]
15
  name = "aiohappyeyeballs"
16
- version = "2.4.6"
17
  source = { registry = "https://pypi.org/simple" }
18
- sdist = { url = "https://files.pythonhosted.org/packages/08/07/508f9ebba367fc3370162e53a3cfd12f5652ad79f0e0bfdf9f9847c6f159/aiohappyeyeballs-2.4.6.tar.gz", hash = "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0", size = 21726 }
19
  wheels = [
20
- { url = "https://files.pythonhosted.org/packages/44/4c/03fb05f56551828ec67ceb3665e5dc51638042d204983a03b0a1541475b6/aiohappyeyeballs-2.4.6-py3-none-any.whl", hash = "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1", size = 14543 },
21
  ]
22
 
23
  [[package]]
@@ -570,14 +570,14 @@ wheels = [
570
 
571
  [[package]]
572
  name = "cloudpathlib"
573
- version = "0.20.0"
574
  source = { registry = "https://pypi.org/simple" }
575
  dependencies = [
576
  { name = "typing-extensions" },
577
  ]
578
- sdist = { url = "https://files.pythonhosted.org/packages/71/0b/a47d78ed2816db100543b504fdbfc2070f422aac858e6bcf775713e37b8a/cloudpathlib-0.20.0.tar.gz", hash = "sha256:f6ef7ca409a510f7ba4639ba50ab3fc5b6dee82d6dff0d7f5715fd0c9ab35891", size = 45149 }
579
  wheels = [
580
- { url = "https://files.pythonhosted.org/packages/1f/6e/b64600156934dab14cc8b403095a9ea8bd722aad2e775673c68346b76220/cloudpathlib-0.20.0-py3-none-any.whl", hash = "sha256:7af3bcefbf73392ae7f31c08b3660ec31607f8c01b7f6262d4d73469a845f641", size = 52547 },
581
  ]
582
 
583
  [[package]]
@@ -730,15 +730,15 @@ wheels = [
730
 
731
  [[package]]
732
  name = "debugpy"
733
- version = "1.8.12"
734
  source = { registry = "https://pypi.org/simple" }
735
- sdist = { url = "https://files.pythonhosted.org/packages/68/25/c74e337134edf55c4dfc9af579eccb45af2393c40960e2795a94351e8140/debugpy-1.8.12.tar.gz", hash = "sha256:646530b04f45c830ceae8e491ca1c9320a2d2f0efea3141487c82130aba70dce", size = 1641122 }
736
  wheels = [
737
- { url = "https://files.pythonhosted.org/packages/56/19/dd58334c0a1ec07babf80bf29fb8daf1a7ca4c1a3bbe61548e40616ac087/debugpy-1.8.12-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:a2ba7ffe58efeae5b8fad1165357edfe01464f9aef25e814e891ec690e7dd82a", size = 2076091 },
738
- { url = "https://files.pythonhosted.org/packages/4c/37/bde1737da15f9617d11ab7b8d5267165f1b7dae116b2585a6643e89e1fa2/debugpy-1.8.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbbd4149c4fc5e7d508ece083e78c17442ee13b0e69bfa6bd63003e486770f45", size = 3560717 },
739
- { url = "https://files.pythonhosted.org/packages/d9/ca/bc67f5a36a7de072908bc9e1156c0f0b272a9a2224cf21540ab1ffd71a1f/debugpy-1.8.12-cp310-cp310-win32.whl", hash = "sha256:b202f591204023b3ce62ff9a47baa555dc00bb092219abf5caf0e3718ac20e7c", size = 5180672 },
740
- { url = "https://files.pythonhosted.org/packages/c1/b9/e899c0a80dfa674dbc992f36f2b1453cd1ee879143cdb455bc04fce999da/debugpy-1.8.12-cp310-cp310-win_amd64.whl", hash = "sha256:9649eced17a98ce816756ce50433b2dd85dfa7bc92ceb60579d68c053f98dff9", size = 5212702 },
741
- { url = "https://files.pythonhosted.org/packages/38/c4/5120ad36405c3008f451f94b8f92ef1805b1e516f6ff870f331ccb3c4cc0/debugpy-1.8.12-py2.py3-none-any.whl", hash = "sha256:274b6a2040349b5c9864e475284bce5bb062e63dce368a394b8cc865ae3b00c6", size = 5229490 },
742
  ]
743
 
744
  [[package]]
@@ -797,6 +797,15 @@ wheels = [
797
  { url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl", hash = "sha256:293e9547a655b25499198ab15a525b05b9407a75f10255e405e8c3854329ab63" },
798
  ]
799
 
 
 
 
 
 
 
 
 
 
800
  [[package]]
801
  name = "eval-type-backport"
802
  version = "0.2.2"
@@ -836,6 +845,18 @@ wheels = [
836
  { url = "https://files.pythonhosted.org/packages/25/57/1115e9b974478fac83ba9cd79def8b3770a91b7a9001c46a76491071f2fe/extra_streamlit_components-0.1.71-py3-none-any.whl", hash = "sha256:c8e6f98446adecd3002756362e50d0669693b7673afaa89cebfced6415cc6bd3", size = 4858597 },
837
  ]
838
 
 
 
 
 
 
 
 
 
 
 
 
 
839
  [[package]]
840
  name = "fastapi"
841
  version = "0.115.11"
@@ -875,6 +896,19 @@ wheels = [
875
  { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 },
876
  ]
877
 
 
 
 
 
 
 
 
 
 
 
 
 
 
878
  [[package]]
879
  name = "filelock"
880
  version = "3.17.0"
@@ -1062,6 +1096,12 @@ wheels = [
1062
  { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
1063
  ]
1064
 
 
 
 
 
 
 
1065
  [[package]]
1066
  name = "httpcore"
1067
  version = "1.0.7"
@@ -1116,7 +1156,7 @@ wheels = [
1116
 
1117
  [[package]]
1118
  name = "huggingface-hub"
1119
- version = "0.29.1"
1120
  source = { registry = "https://pypi.org/simple" }
1121
  dependencies = [
1122
  { name = "filelock" },
@@ -1127,9 +1167,9 @@ dependencies = [
1127
  { name = "tqdm" },
1128
  { name = "typing-extensions" },
1129
  ]
1130
- sdist = { url = "https://files.pythonhosted.org/packages/22/37/797d6476f13e5ef6af5fc48a5d641d32b39c37e166ccf40c3714c5854a85/huggingface_hub-0.29.1.tar.gz", hash = "sha256:9524eae42077b8ff4fc459ceb7a514eca1c1232b775276b009709fe2a084f250", size = 389776 }
1131
  wheels = [
1132
- { url = "https://files.pythonhosted.org/packages/ae/05/75b90de9093de0aadafc868bb2fa7c57651fd8f45384adf39bd77f63980d/huggingface_hub-0.29.1-py3-none-any.whl", hash = "sha256:352f69caf16566c7b6de84b54a822f6238e17ddd8ae3da4f8f2272aea5b198d5", size = 468049 },
1133
  ]
1134
 
1135
  [[package]]
@@ -1305,14 +1345,14 @@ wheels = [
1305
 
1306
  [[package]]
1307
  name = "jinja2"
1308
- version = "3.1.5"
1309
  source = { registry = "https://pypi.org/simple" }
1310
  dependencies = [
1311
  { name = "markupsafe" },
1312
  ]
1313
- sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
1314
  wheels = [
1315
- { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
1316
  ]
1317
 
1318
  [[package]]
@@ -1667,29 +1707,26 @@ wheels = [
1667
 
1668
  [[package]]
1669
  name = "langchain"
1670
- version = "0.3.19"
1671
  source = { registry = "https://pypi.org/simple" }
1672
  dependencies = [
1673
- { name = "aiohttp" },
1674
  { name = "async-timeout" },
1675
  { name = "langchain-core" },
1676
  { name = "langchain-text-splitters" },
1677
  { name = "langsmith" },
1678
- { name = "numpy" },
1679
  { name = "pydantic" },
1680
  { name = "pyyaml" },
1681
  { name = "requests" },
1682
  { name = "sqlalchemy" },
1683
- { name = "tenacity" },
1684
  ]
1685
- sdist = { url = "https://files.pythonhosted.org/packages/a2/cf/a064ef27d5f3154491c85783590a25d7ae22340cddedf9bf47496044e4eb/langchain-0.3.19.tar.gz", hash = "sha256:b96f8a445f01d15d522129ffe77cc89c8468dbd65830d153a676de8f6b899e7b", size = 10224228 }
1686
  wheels = [
1687
- { url = "https://files.pythonhosted.org/packages/18/7d/0f4cc3317634195381f87c5d90268f29b9a31fda62aa7a7f36a1c27b06f3/langchain-0.3.19-py3-none-any.whl", hash = "sha256:1e16d97db9106640b7de4c69f8f5ed22eeda56b45b9241279e83f111640eff16", size = 1010630 },
1688
  ]
1689
 
1690
  [[package]]
1691
  name = "langchain-community"
1692
- version = "0.3.18"
1693
  source = { registry = "https://pypi.org/simple" }
1694
  dependencies = [
1695
  { name = "aiohttp" },
@@ -1705,14 +1742,14 @@ dependencies = [
1705
  { name = "sqlalchemy" },
1706
  { name = "tenacity" },
1707
  ]
1708
- sdist = { url = "https://files.pythonhosted.org/packages/93/5f/0d81b0299bb8dc797dfc42ed19e7410bfab799eb25107dda5cdf5f703515/langchain_community-0.3.18.tar.gz", hash = "sha256:fa2889a8f0b2d22b5c306fd1b070c0970e1f11b604bf55fad2f4a1d0bf68a077", size = 33216713 }
1709
  wheels = [
1710
- { url = "https://files.pythonhosted.org/packages/f7/95/8e0c46842040e58fc252d82bcddc7d02e1e874aad98fdf729d25978c09ae/langchain_community-0.3.18-py3-none-any.whl", hash = "sha256:0d4a70144a1750045c4f726f9a43379ed2484178f76e4b8295bcef3a7fdf41d5", size = 2520606 },
1711
  ]
1712
 
1713
  [[package]]
1714
  name = "langchain-core"
1715
- version = "0.3.40"
1716
  source = { registry = "https://pypi.org/simple" }
1717
  dependencies = [
1718
  { name = "jsonpatch" },
@@ -1723,9 +1760,9 @@ dependencies = [
1723
  { name = "tenacity" },
1724
  { name = "typing-extensions" },
1725
  ]
1726
- sdist = { url = "https://files.pythonhosted.org/packages/4f/6b/7c3f5fa60639b885d8dbc932c37f0b0584ac5151ac6bdcd00b67b591e5e9/langchain_core-0.3.40.tar.gz", hash = "sha256:893a238b38491967c804662c1ec7c3e6ebaf223d1125331249c3cf3862ff2746", size = 528341 }
1727
  wheels = [
1728
- { url = "https://files.pythonhosted.org/packages/b7/01/b52e43f63261b5aae016cbe170bb5d8e8899770530075d11c1e45a07b97b/langchain_core-0.3.40-py3-none-any.whl", hash = "sha256:9f31358741f10a13db8531e8288b8a5ae91904018c5c2e6f739d6645a98fca03", size = 414346 },
1729
  ]
1730
 
1731
  [[package]]
@@ -1782,7 +1819,7 @@ wheels = [
1782
 
1783
  [[package]]
1784
  name = "langgraph"
1785
- version = "0.3.2"
1786
  source = { registry = "https://pypi.org/simple" }
1787
  dependencies = [
1788
  { name = "langchain-core" },
@@ -1790,9 +1827,9 @@ dependencies = [
1790
  { name = "langgraph-prebuilt" },
1791
  { name = "langgraph-sdk" },
1792
  ]
1793
- sdist = { url = "https://files.pythonhosted.org/packages/82/21/83cc5f941da39ba31b8ff07e5e55b3112839f0bf332d52e6b405ad365ae5/langgraph-0.3.2.tar.gz", hash = "sha256:4712c11a9355e5d15126e15a166807a1d6275d72cf74fd2303609ffc2505e7a8", size = 113460 }
1794
  wheels = [
1795
- { url = "https://files.pythonhosted.org/packages/33/1c/c50719e89442094bace035e632da7be161e5389974f2b805b75fef7aab8f/langgraph-0.3.2-py3-none-any.whl", hash = "sha256:1e17f85e225bf4fc5f0f70bce89d8bc5d1536b7993e39e2e47baa1b1a17a439c", size = 130875 },
1796
  ]
1797
 
1798
  [[package]]
@@ -1810,15 +1847,15 @@ wheels = [
1810
 
1811
  [[package]]
1812
  name = "langgraph-prebuilt"
1813
- version = "0.1.1"
1814
  source = { registry = "https://pypi.org/simple" }
1815
  dependencies = [
1816
  { name = "langchain-core" },
1817
  { name = "langgraph-checkpoint" },
1818
  ]
1819
- sdist = { url = "https://files.pythonhosted.org/packages/22/15/848593ccace12e4f8b80cc0b159b0ba1da17605e1eecbda5f37d891748a3/langgraph_prebuilt-0.1.1.tar.gz", hash = "sha256:420a748ff93842f2b1a345a0c1ca3939d2bc7a2d46c20e9a9a0d8f148152cc47", size = 23257 }
1820
  wheels = [
1821
- { url = "https://files.pythonhosted.org/packages/3c/62/a424fdb892f578fa88b2ff4df0bfdebdc8b89501dacb8ca3b480305cbfef/langgraph_prebuilt-0.1.1-py3-none-any.whl", hash = "sha256:148a9558a36ec7e83cc6512f3521425c862b0463251ae0242ade52a448c54e78", size = 24622 },
1822
  ]
1823
 
1824
  [[package]]
@@ -1877,6 +1914,37 @@ wheels = [
1877
  { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 },
1878
  ]
1879
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1880
  [[package]]
1881
  name = "mapclassify"
1882
  version = "2.8.1"
@@ -1915,6 +1983,15 @@ wheels = [
1915
  { url = "https://files.pythonhosted.org/packages/cc/94/3d619cc82c30daeacd18a88674f4e6540ebfb7b4b7752ca0552793be80cf/marisa_trie-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:5685a14b3099b1422c4f59fa38b0bf4b5342ee6cc38ae57df9666a0b28eeaad3", size = 151891 },
1916
  ]
1917
 
 
 
 
 
 
 
 
 
 
1918
  [[package]]
1919
  name = "markdown-it-py"
1920
  version = "3.0.0"
@@ -1927,6 +2004,24 @@ wheels = [
1927
  { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
1928
  ]
1929
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1930
  [[package]]
1931
  name = "markupsafe"
1932
  version = "3.0.2"
@@ -2266,18 +2361,24 @@ wheels = [
2266
 
2267
  [[package]]
2268
  name = "numpy"
2269
- version = "1.26.4"
2270
  source = { registry = "https://pypi.org/simple" }
2271
- sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 }
2272
  wheels = [
2273
- { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 },
2274
- { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 },
2275
- { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 },
2276
- { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 },
2277
- { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 },
2278
- { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 },
2279
- { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 },
2280
- { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 },
 
 
 
 
 
 
2281
  ]
2282
 
2283
  [[package]]
@@ -2311,7 +2412,7 @@ wheels = [
2311
 
2312
  [[package]]
2313
  name = "openai"
2314
- version = "1.65.2"
2315
  source = { registry = "https://pypi.org/simple" }
2316
  dependencies = [
2317
  { name = "anyio" },
@@ -2323,9 +2424,9 @@ dependencies = [
2323
  { name = "tqdm" },
2324
  { name = "typing-extensions" },
2325
  ]
2326
- sdist = { url = "https://files.pythonhosted.org/packages/f6/03/0bbf201a7e44920d892db0445874c8111be4255cb9495379df18d6d36ea1/openai-1.65.2.tar.gz", hash = "sha256:729623efc3fd91c956f35dd387fa5c718edd528c4bed9f00b40ef290200fb2ce", size = 359185 }
2327
  wheels = [
2328
- { url = "https://files.pythonhosted.org/packages/2c/3b/722ed868cb56f70264190ed479b38b3e46d14daa267d559a3fe3bd9061cf/openai-1.65.2-py3-none-any.whl", hash = "sha256:27d9fe8de876e31394c2553c4e6226378b6ed85e480f586ccfe25b7193fb1750", size = 473206 },
2329
  ]
2330
 
2331
  [[package]]
@@ -2639,6 +2740,7 @@ dependencies = [
2639
  { name = "presidio-analyzer" },
2640
  { name = "presidio-anonymizer" },
2641
  { name = "py7zr" },
 
2642
  { name = "pypdf" },
2643
  { name = "pypdf2" },
2644
  { name = "pyqt6" },
@@ -2647,6 +2749,7 @@ dependencies = [
2647
  { name = "spacytextblob" },
2648
  { name = "streamlit" },
2649
  { name = "streamlit-authenticator" },
 
2650
  { name = "tabulate" },
2651
  { name = "transformers" },
2652
  ]
@@ -2682,6 +2785,7 @@ requires-dist = [
2682
  { name = "presidio-analyzer", specifier = ">=2.2.355" },
2683
  { name = "presidio-anonymizer", specifier = ">=2.2.355" },
2684
  { name = "py7zr", specifier = ">=0.22.0" },
 
2685
  { name = "pypdf", specifier = ">=5.1.0" },
2686
  { name = "pypdf2", specifier = ">=3.0.1" },
2687
  { name = "pyqt6", specifier = ">=6.7.1" },
@@ -2690,6 +2794,7 @@ requires-dist = [
2690
  { name = "spacytextblob", specifier = ">=4.0.0" },
2691
  { name = "streamlit", specifier = ">=1.41.1" },
2692
  { name = "streamlit-authenticator", specifier = ">=0.4.1" },
 
2693
  { name = "tabulate", specifier = ">=0.9.0" },
2694
  { name = "transformers", specifier = ">=4.44.2" },
2695
  ]
@@ -2710,6 +2815,19 @@ wheels = [
2710
  { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
2711
  ]
2712
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2713
  [[package]]
2714
  name = "polars"
2715
  version = "1.24.0"
@@ -2726,7 +2844,7 @@ wheels = [
2726
 
2727
  [[package]]
2728
  name = "posthog"
2729
- version = "3.18.0"
2730
  source = { registry = "https://pypi.org/simple" }
2731
  dependencies = [
2732
  { name = "backoff" },
@@ -2736,9 +2854,9 @@ dependencies = [
2736
  { name = "requests" },
2737
  { name = "six" },
2738
  ]
2739
- sdist = { url = "https://files.pythonhosted.org/packages/02/ca/63096072dc13701a1c8172ec1f7f5f9c7394972df5292262223955fc8c44/posthog-3.18.0.tar.gz", hash = "sha256:8882fe1ee6763bafab0d82a506f52d752a8031c90eea17b4db4331fb33cf1092", size = 65466 }
2740
  wheels = [
2741
- { url = "https://files.pythonhosted.org/packages/fa/ed/3ab148bfbad390777bdf7a724ba9d072a035ef6a2c643d73c671e9f7cdb9/posthog-3.18.0-py2.py3-none-any.whl", hash = "sha256:88f93cc670158ea7a569629d4def77c3b714489a52b0a818b8dd6103669c652d", size = 76648 },
2742
  ]
2743
 
2744
  [[package]]
@@ -3095,6 +3213,19 @@ wheels = [
3095
  { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 },
3096
  ]
3097
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3098
  [[package]]
3099
  name = "pyogrio"
3100
  version = "0.10.0"
@@ -3114,6 +3245,15 @@ wheels = [
3114
  { url = "https://files.pythonhosted.org/packages/75/ca/b31083da2e6c4b598b6609a98c655977189fe8982c36d98ea4789a938045/pyogrio-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:eac90b2501656892c63bc500c12e71f3dbf7d66ddc5a7fb05cd480d25d1b7022", size = 16171065 },
3115
  ]
3116
 
 
 
 
 
 
 
 
 
 
3117
  [[package]]
3118
  name = "pyparsing"
3119
  version = "3.2.1"
@@ -3810,6 +3950,30 @@ wheels = [
3810
  { url = "https://files.pythonhosted.org/packages/d5/b8/3dfed2db5c7ecf275aaddb775e2ae17c576b09c848873188fce91e410129/srsly-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:1a4dddb2edb8f7974c9aa5ec46dc687a75215b3bbdc815ce3fc9ea68fe1e94b5", size = 632267 },
3811
  ]
3812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3813
  [[package]]
3814
  name = "stack-data"
3815
  version = "0.6.3"
@@ -3838,7 +4002,7 @@ wheels = [
3838
 
3839
  [[package]]
3840
  name = "streamlit"
3841
- version = "1.42.2"
3842
  source = { registry = "https://pypi.org/simple" }
3843
  dependencies = [
3844
  { name = "altair" },
@@ -3854,16 +4018,15 @@ dependencies = [
3854
  { name = "pyarrow" },
3855
  { name = "pydeck" },
3856
  { name = "requests" },
3857
- { name = "rich" },
3858
  { name = "tenacity" },
3859
  { name = "toml" },
3860
  { name = "tornado" },
3861
  { name = "typing-extensions" },
3862
  { name = "watchdog", marker = "sys_platform != 'darwin'" },
3863
  ]
3864
- sdist = { url = "https://files.pythonhosted.org/packages/08/58/b1875ccb6901cec7a721059bfd9122107d29e0b0ccfdcea9d353af6b5c52/streamlit-1.42.2.tar.gz", hash = "sha256:62026dbdcb482790933f658b096d7dd58fa70da89c1f06fbc3658b91dcd4dab2", size = 9169615 }
3865
  wheels = [
3866
- { url = "https://files.pythonhosted.org/packages/e4/08/c962e38f4450dafb98af49f8988a736dd67ae9cc26a5704c43269f506bc8/streamlit-1.42.2-py2.py3-none-any.whl", hash = "sha256:e2516c7fcd17a11a85cc1999fae58ace0a6458e2b4c1a411ed3d75b1aee2eb93", size = 9559800 },
3867
  ]
3868
 
3869
  [[package]]
@@ -3883,6 +4046,137 @@ wheels = [
3883
  { url = "https://files.pythonhosted.org/packages/83/47/837b158e1a5b0d187d20c6be22c46d84d12a8d3e8d7113b67ebb33e221c9/streamlit_authenticator-0.4.2-py3-none-any.whl", hash = "sha256:442acccef6af65e2b0feb15d5e9f68707f204c1d31c60673690d87179c7ca5b2", size = 43197 },
3884
  ]
3885
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3886
  [[package]]
3887
  name = "sympy"
3888
  version = "1.13.3"
@@ -4189,7 +4483,7 @@ wheels = [
4189
 
4190
  [[package]]
4191
  name = "unstructured-client"
4192
- version = "0.30.6"
4193
  source = { registry = "https://pypi.org/simple" }
4194
  dependencies = [
4195
  { name = "aiofiles" },
@@ -4203,9 +4497,9 @@ dependencies = [
4203
  { name = "requests-toolbelt" },
4204
  { name = "typing-inspect" },
4205
  ]
4206
- sdist = { url = "https://files.pythonhosted.org/packages/c9/c1/21cf847dd5b525bce5103ada9f60ecb6b3f7f45b4cee02e301a3c5f7a8de/unstructured_client-0.30.6.tar.gz", hash = "sha256:da34e0d3e82724151dd5f87496a84c283c523c135e5d691cfc4a7950b4840dea", size = 75469 }
4207
  wheels = [
4208
- { url = "https://files.pythonhosted.org/packages/b7/04/2f70296b47951ca19467132c38ce12125de59dad8c4a340b7266e6e870ba/unstructured_client-0.30.6-py3-none-any.whl", hash = "sha256:61f68e3f3f8088f623d522c8b5d1915dddc5b5ae27a4a710ab87581ae911a7c8", size = 166394 },
4209
  ]
4210
 
4211
  [[package]]
@@ -4265,6 +4559,15 @@ wheels = [
4265
  { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c", size = 3800523 },
4266
  ]
4267
 
 
 
 
 
 
 
 
 
 
4268
  [[package]]
4269
  name = "wasabi"
4270
  version = "1.1.3"
@@ -4380,28 +4683,28 @@ wheels = [
4380
 
4381
  [[package]]
4382
  name = "websockets"
4383
- version = "15.0"
4384
- source = { registry = "https://pypi.org/simple" }
4385
- sdist = { url = "https://files.pythonhosted.org/packages/2e/7a/8bc4d15af7ff30f7ba34f9a172063bfcee9f5001d7cef04bee800a658f33/websockets-15.0.tar.gz", hash = "sha256:ca36151289a15b39d8d683fd8b7abbe26fc50be311066c5f8dcf3cb8cee107ab", size = 175574 }
4386
- wheels = [
4387
- { url = "https://files.pythonhosted.org/packages/3d/f1/b20cc4c1ff84911c791f36fa511a78203836bb4d603f56290de08c067437/websockets-15.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5e6ee18a53dd5743e6155b8ff7e8e477c25b29b440f87f65be8165275c87fef0", size = 174701 },
4388
- { url = "https://files.pythonhosted.org/packages/f9/e8/4de59ee85ec86052ca574f4e5327ef948e4f77757d3c9c1503f5a0e9c039/websockets-15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ee06405ea2e67366a661ed313e14cf2a86e84142a3462852eb96348f7219cee3", size = 172358 },
4389
- { url = "https://files.pythonhosted.org/packages/2f/ea/b0f95815cdc83d61b1a895858671c6af38a76c23f3ea5d91e2ba11bbedc7/websockets-15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8711682a629bbcaf492f5e0af72d378e976ea1d127a2d47584fa1c2c080b436b", size = 172610 },
4390
- { url = "https://files.pythonhosted.org/packages/09/ed/c5d8f1f296f475c00611a40eff6a952248785efb125f91a0b29575f36ba6/websockets-15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94c4a9b01eede952442c088d415861b0cf2053cbd696b863f6d5022d4e4e2453", size = 181579 },
4391
- { url = "https://files.pythonhosted.org/packages/b7/fc/2444b5ae792d92179f20cec53475bcc25d1d7f00a2be9947de9837ef230a/websockets-15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:45535fead66e873f411c1d3cf0d3e175e66f4dd83c4f59d707d5b3e4c56541c4", size = 180588 },
4392
- { url = "https://files.pythonhosted.org/packages/ff/b5/0945a31562d351cff26d76a2ae9a4ba4536e698aa059a4262afd793b2a1d/websockets-15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e389efe46ccb25a1f93d08c7a74e8123a2517f7b7458f043bd7529d1a63ffeb", size = 180902 },
4393
- { url = "https://files.pythonhosted.org/packages/b6/7c/e9d844b87754bc83b294cc1c695cbc6c5d42e329b85d2bf2d7bb9554d09c/websockets-15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:67a04754d121ea5ca39ddedc3f77071651fb5b0bc6b973c71c515415b44ed9c5", size = 181282 },
4394
- { url = "https://files.pythonhosted.org/packages/9e/6c/6a5d3272f494fa2fb4806b896ecb312bd6c72bab632df4ace19946c079dc/websockets-15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bd66b4865c8b853b8cca7379afb692fc7f52cf898786537dfb5e5e2d64f0a47f", size = 180694 },
4395
- { url = "https://files.pythonhosted.org/packages/b2/32/1fb4b62c2ec2c9844d4ddaa4021d993552c7c493a0acdcec95551679d501/websockets-15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a4cc73a6ae0a6751b76e69cece9d0311f054da9b22df6a12f2c53111735657c8", size = 180631 },
4396
- { url = "https://files.pythonhosted.org/packages/e4/9b/5ef1ddb8857ce894217bdd9572ad98c1cef20d8f9f0f43823b782b7ded6b/websockets-15.0-cp310-cp310-win32.whl", hash = "sha256:89da58e4005e153b03fe8b8794330e3f6a9774ee9e1c3bd5bc52eb098c3b0c4f", size = 175664 },
4397
- { url = "https://files.pythonhosted.org/packages/29/63/c320572ccf813ed2bc3058a0e0291ee95eb258dc5e6b3446ca45dc1af0fd/websockets-15.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ff380aabd7a74a42a760ee76c68826a8f417ceb6ea415bd574a035a111fd133", size = 176109 },
4398
- { url = "https://files.pythonhosted.org/packages/42/52/359467c7ca12721a04520da9ba9fc29da2cd176c30992f6f81fa881bb3e5/websockets-15.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b499caef4bca9cbd0bd23cd3386f5113ee7378094a3cb613a2fa543260fe9506", size = 172384 },
4399
- { url = "https://files.pythonhosted.org/packages/7c/ff/36fd8a45fac404d8f109e03ca06328f49847d71c0c048414c76bb2db91c4/websockets-15.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:17f2854c6bd9ee008c4b270f7010fe2da6c16eac5724a175e75010aacd905b31", size = 172616 },
4400
- { url = "https://files.pythonhosted.org/packages/b1/a8/65496a87984815e2837835d5ac3c9f81ea82031036877e8f80953c59dbd9/websockets-15.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89f72524033abbfde880ad338fd3c2c16e31ae232323ebdfbc745cbb1b3dcc03", size = 173871 },
4401
- { url = "https://files.pythonhosted.org/packages/23/89/9441e1e0818d46fe22d78b3e5c8fe2316516211330e138231c90dce5559e/websockets-15.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1657a9eecb29d7838e3b415458cc494e6d1b194f7ac73a34aa55c6fb6c72d1f3", size = 173477 },
4402
- { url = "https://files.pythonhosted.org/packages/2f/1b/80460b3ac9795ef7bbaa074c603d64e009dbb2ceb11008416efab0dcc811/websockets-15.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e413352a921f5ad5d66f9e2869b977e88d5103fc528b6deb8423028a2befd842", size = 173425 },
4403
- { url = "https://files.pythonhosted.org/packages/56/d1/8da7e733ed266f342e8c544c3b8338449de9b860d85d9a0bfd4fe1857d6e/websockets-15.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8561c48b0090993e3b2a54db480cab1d23eb2c5735067213bb90f402806339f5", size = 176160 },
4404
- { url = "https://files.pythonhosted.org/packages/e8/b2/31eec524b53f01cd8343f10a8e429730c52c1849941d1f530f8253b6d934/websockets-15.0-py3-none-any.whl", hash = "sha256:51ffd53c53c4442415b613497a34ba0aa7b99ac07f1e4a62db5dcd640ae6c3c3", size = 169023 },
4405
  ]
4406
 
4407
  [[package]]
 
13
 
14
  [[package]]
15
  name = "aiohappyeyeballs"
16
+ version = "2.5.0"
17
  source = { registry = "https://pypi.org/simple" }
18
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/0c/458958007041f4b4de2d307e6b75d9e7554dad0baf26fe7a48b741aac126/aiohappyeyeballs-2.5.0.tar.gz", hash = "sha256:18fde6204a76deeabc97c48bdd01d5801cfda5d6b9c8bbeb1aaaee9d648ca191", size = 22494 }
19
  wheels = [
20
+ { url = "https://files.pythonhosted.org/packages/1b/9a/e4886864ce06e1579bd428208127fbdc0d62049c751e4e9e3b509c0059dc/aiohappyeyeballs-2.5.0-py3-none-any.whl", hash = "sha256:0850b580748c7071db98bffff6d4c94028d0d3035acc20fd721a0ce7e8cac35d", size = 15128 },
21
  ]
22
 
23
  [[package]]
 
570
 
571
  [[package]]
572
  name = "cloudpathlib"
573
+ version = "0.21.0"
574
  source = { registry = "https://pypi.org/simple" }
575
  dependencies = [
576
  { name = "typing-extensions" },
577
  ]
578
+ sdist = { url = "https://files.pythonhosted.org/packages/5f/54/71e828c2e415024783f92ee942d3223f6f94cf3fe2e48689b0f3bbb5b608/cloudpathlib-0.21.0.tar.gz", hash = "sha256:fb8f6b890a3d37b35f0eabff86721bb8d35dfc6a6be98c1f4d34b19e989c6641", size = 45271 }
579
  wheels = [
580
+ { url = "https://files.pythonhosted.org/packages/e8/0f/b1a9b09a84ef98b9fc38d50c6b2815cb2256b804a78e7d838ddfbdc035c7/cloudpathlib-0.21.0-py3-none-any.whl", hash = "sha256:657e95ecd2663f1123b6daa95d49aca4b4bc8a9fa90c07930bdba2c5e295e5ef", size = 52744 },
581
  ]
582
 
583
  [[package]]
 
730
 
731
  [[package]]
732
  name = "debugpy"
733
+ version = "1.8.13"
734
  source = { registry = "https://pypi.org/simple" }
735
+ sdist = { url = "https://files.pythonhosted.org/packages/51/d4/f35f539e11c9344652f362c22413ec5078f677ac71229dc9b4f6f85ccaa3/debugpy-1.8.13.tar.gz", hash = "sha256:837e7bef95bdefba426ae38b9a94821ebdc5bea55627879cd48165c90b9e50ce", size = 1641193 }
736
  wheels = [
737
+ { url = "https://files.pythonhosted.org/packages/3f/32/901c7204cceb3262fdf38f4c25c9a46372c11661e8490e9ea702bc4ff448/debugpy-1.8.13-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:06859f68e817966723ffe046b896b1bd75c665996a77313370336ee9e1de3e90", size = 2076250 },
738
+ { url = "https://files.pythonhosted.org/packages/95/10/77fe746851c8d84838a807da60c7bd0ac8627a6107d6917dd3293bf8628c/debugpy-1.8.13-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c2db69fb8df3168bc857d7b7d2494fed295dfdbde9a45f27b4b152f37520", size = 3560883 },
739
+ { url = "https://files.pythonhosted.org/packages/a1/ef/28f8db2070e453dda0e49b356e339d0b4e1d38058d4c4ea9e88cdc8ee8e7/debugpy-1.8.13-cp310-cp310-win32.whl", hash = "sha256:46abe0b821cad751fc1fb9f860fb2e68d75e2c5d360986d0136cd1db8cad4428", size = 5180149 },
740
+ { url = "https://files.pythonhosted.org/packages/89/16/1d53a80caf5862627d3eaffb217d4079d7e4a1df6729a2d5153733661efd/debugpy-1.8.13-cp310-cp310-win_amd64.whl", hash = "sha256:dc7b77f5d32674686a5f06955e4b18c0e41fb5a605f5b33cf225790f114cfeec", size = 5212540 },
741
+ { url = "https://files.pythonhosted.org/packages/37/4f/0b65410a08b6452bfd3f7ed6f3610f1a31fb127f46836e82d31797065dcb/debugpy-1.8.13-py2.py3-none-any.whl", hash = "sha256:d4ba115cdd0e3a70942bd562adba9ec8c651fe69ddde2298a1be296fc331906f", size = 5229306 },
742
  ]
743
 
744
  [[package]]
 
797
  { url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl", hash = "sha256:293e9547a655b25499198ab15a525b05b9407a75f10255e405e8c3854329ab63" },
798
  ]
799
 
800
+ [[package]]
801
+ name = "entrypoints"
802
+ version = "0.4"
803
+ source = { registry = "https://pypi.org/simple" }
804
+ sdist = { url = "https://files.pythonhosted.org/packages/ea/8d/a7121ffe5f402dc015277d2d31eb82d2187334503a011c18f2e78ecbb9b2/entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", size = 13974 }
805
+ wheels = [
806
+ { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294 },
807
+ ]
808
+
809
  [[package]]
810
  name = "eval-type-backport"
811
  version = "0.2.2"
 
845
  { url = "https://files.pythonhosted.org/packages/25/57/1115e9b974478fac83ba9cd79def8b3770a91b7a9001c46a76491071f2fe/extra_streamlit_components-0.1.71-py3-none-any.whl", hash = "sha256:c8e6f98446adecd3002756362e50d0669693b7673afaa89cebfced6415cc6bd3", size = 4858597 },
846
  ]
847
 
848
+ [[package]]
849
+ name = "faker"
850
+ version = "36.2.2"
851
+ source = { registry = "https://pypi.org/simple" }
852
+ dependencies = [
853
+ { name = "tzdata" },
854
+ ]
855
+ sdist = { url = "https://files.pythonhosted.org/packages/ee/6c/412b064e33d11b351ef8945e4cc0ab56aa156e107c71610c4af96bd5d72c/faker-36.2.2.tar.gz", hash = "sha256:758bc63a26dc878fa0d76aa7639b8b65327927980ed0c3683b23bd8a5182f33f", size = 1874990 }
856
+ wheels = [
857
+ { url = "https://files.pythonhosted.org/packages/28/30/3e81fdb631115c37ef81ad8bd342bf3fa52e66366bbed65a367a9137f8b9/faker-36.2.2-py3-none-any.whl", hash = "sha256:14adc340dc8abed5264142ffafe6f1a0f99cf7a7525bc6863755efd5fbbd0692", size = 1918206 },
858
+ ]
859
+
860
  [[package]]
861
  name = "fastapi"
862
  version = "0.115.11"
 
896
  { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 },
897
  ]
898
 
899
+ [[package]]
900
+ name = "favicon"
901
+ version = "0.7.0"
902
+ source = { registry = "https://pypi.org/simple" }
903
+ dependencies = [
904
+ { name = "beautifulsoup4" },
905
+ { name = "requests" },
906
+ ]
907
+ sdist = { url = "https://files.pythonhosted.org/packages/64/68/d2646f40c05d3a501cddd232119f8c087a6fcba3c79255a062c73e80b42a/favicon-0.7.0.tar.gz", hash = "sha256:6d6b5a78de2a0d0084589f687f384b2ecd6a6527093fec564403b1a30605d7a8", size = 9284 }
908
+ wheels = [
909
+ { url = "https://files.pythonhosted.org/packages/93/4c/8baf94bb789972634d933152d27529f2bad4e5d2397b8da9c30f6f5342ce/favicon-0.7.0-py2.py3-none-any.whl", hash = "sha256:7fec0617c73dcb8521ea788e1d38cdc7226c7cb8e28c81e11625d85fa1534880", size = 5921 },
910
+ ]
911
+
912
  [[package]]
913
  name = "filelock"
914
  version = "3.17.0"
 
1096
  { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
1097
  ]
1098
 
1099
+ [[package]]
1100
+ name = "htbuilder"
1101
+ version = "0.9.0"
1102
+ source = { registry = "https://pypi.org/simple" }
1103
+ sdist = { url = "https://files.pythonhosted.org/packages/be/88/0f48d1125168e9eea33879ea820000702533d103f1123d27ca69839a0db4/htbuilder-0.9.0.tar.gz", hash = "sha256:58c0bc5502c1a46b42ae9e074c43ec0f6fdc24ed334936cb17e1ed5a8938aee2", size = 10591 }
1104
+
1105
  [[package]]
1106
  name = "httpcore"
1107
  version = "1.0.7"
 
1156
 
1157
  [[package]]
1158
  name = "huggingface-hub"
1159
+ version = "0.29.2"
1160
  source = { registry = "https://pypi.org/simple" }
1161
  dependencies = [
1162
  { name = "filelock" },
 
1167
  { name = "tqdm" },
1168
  { name = "typing-extensions" },
1169
  ]
1170
+ sdist = { url = "https://files.pythonhosted.org/packages/58/b2/f8b3c9842a794e8203448725aefa02d7c9e0da42d5f22f4ed806057cc36e/huggingface_hub-0.29.2.tar.gz", hash = "sha256:590b29c0dcbd0ee4b7b023714dc1ad8563fe4a68a91463438b74e980d28afaf3", size = 389816 }
1171
  wheels = [
1172
+ { url = "https://files.pythonhosted.org/packages/13/5f/088ff08dc41808fcd99d9972b9bcfa7e3a35e30e8b0a3155b57938f1611c/huggingface_hub-0.29.2-py3-none-any.whl", hash = "sha256:c56f20fca09ef19da84dcde2b76379ecdaddf390b083f59f166715584953307d", size = 468087 },
1173
  ]
1174
 
1175
  [[package]]
 
1345
 
1346
  [[package]]
1347
  name = "jinja2"
1348
+ version = "3.1.6"
1349
  source = { registry = "https://pypi.org/simple" }
1350
  dependencies = [
1351
  { name = "markupsafe" },
1352
  ]
1353
+ sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 }
1354
  wheels = [
1355
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 },
1356
  ]
1357
 
1358
  [[package]]
 
1707
 
1708
  [[package]]
1709
  name = "langchain"
1710
+ version = "0.3.20"
1711
  source = { registry = "https://pypi.org/simple" }
1712
  dependencies = [
 
1713
  { name = "async-timeout" },
1714
  { name = "langchain-core" },
1715
  { name = "langchain-text-splitters" },
1716
  { name = "langsmith" },
 
1717
  { name = "pydantic" },
1718
  { name = "pyyaml" },
1719
  { name = "requests" },
1720
  { name = "sqlalchemy" },
 
1721
  ]
1722
+ sdist = { url = "https://files.pythonhosted.org/packages/2a/b0/5121cdd19cf99e684043f4eae528c893f56bd25e7711d4de89f27832a5f3/langchain-0.3.20.tar.gz", hash = "sha256:edcc3241703e1f6557ef5a5c35cd56f9ccc25ff12e38b4829c66d94971737a93", size = 10225276 }
1723
  wheels = [
1724
+ { url = "https://files.pythonhosted.org/packages/b5/d4/afe8174838bdd3baba5d6a19e9f3af4c54c5db1ab4d66ef0b650c6157919/langchain-0.3.20-py3-none-any.whl", hash = "sha256:273287f8e61ffdf7e811cf8799e6a71e9381325b8625fd6618900faba79cfdd0", size = 1011577 },
1725
  ]
1726
 
1727
  [[package]]
1728
  name = "langchain-community"
1729
+ version = "0.3.19"
1730
  source = { registry = "https://pypi.org/simple" }
1731
  dependencies = [
1732
  { name = "aiohttp" },
 
1742
  { name = "sqlalchemy" },
1743
  { name = "tenacity" },
1744
  ]
1745
+ sdist = { url = "https://files.pythonhosted.org/packages/51/0a/bb9e4dde29003ac7fed35137e045e176b5e5fdd81c893c7cfc14dc5f8f4c/langchain_community-0.3.19.tar.gz", hash = "sha256:fc100b6d4d6523566a957cdc306b0500e4982d5b221b98f67432da18ba5b2bf5", size = 33217868 }
1746
  wheels = [
1747
+ { url = "https://files.pythonhosted.org/packages/2b/a3/6718deba2c30db991c6b000d23fa062441daa576eb1e520cb2edc2729e2f/langchain_community-0.3.19-py3-none-any.whl", hash = "sha256:268ce7b322c0d1961d7bab1a9419d6ff30c99ad09487dca48d47389b69875b16", size = 2523554 },
1748
  ]
1749
 
1750
  [[package]]
1751
  name = "langchain-core"
1752
+ version = "0.3.41"
1753
  source = { registry = "https://pypi.org/simple" }
1754
  dependencies = [
1755
  { name = "jsonpatch" },
 
1760
  { name = "tenacity" },
1761
  { name = "typing-extensions" },
1762
  ]
1763
+ sdist = { url = "https://files.pythonhosted.org/packages/2b/0a/aa5167a1a46094024b8fe50917e37f1df5bcd0034adb25452e121dae60e6/langchain_core-0.3.41.tar.gz", hash = "sha256:d3ee9f3616ebbe7943470ade23d4a04e1729b1512c0ec55a4a07bd2ac64dedb4", size = 528826 }
1764
  wheels = [
1765
+ { url = "https://files.pythonhosted.org/packages/bc/a6/551de93e02b1ef4ec031f6e1c0ff31a70790096c1e7066168a7693e4efe5/langchain_core-0.3.41-py3-none-any.whl", hash = "sha256:1a27cca5333bae7597de4004fb634b5f3e71667a3da6493b94ce83bcf15a23bd", size = 415149 },
1766
  ]
1767
 
1768
  [[package]]
 
1819
 
1820
  [[package]]
1821
  name = "langgraph"
1822
+ version = "0.3.5"
1823
  source = { registry = "https://pypi.org/simple" }
1824
  dependencies = [
1825
  { name = "langchain-core" },
 
1827
  { name = "langgraph-prebuilt" },
1828
  { name = "langgraph-sdk" },
1829
  ]
1830
+ sdist = { url = "https://files.pythonhosted.org/packages/4e/fa/b1ecc95a2464bc7dbe5e67fbd21096013829119899c33236090b98c75508/langgraph-0.3.5.tar.gz", hash = "sha256:7c0d8e61aa02578b41036c9f7a599ccba2562d269f66ef76bacbba47a99a7eca", size = 114020 }
1831
  wheels = [
1832
+ { url = "https://files.pythonhosted.org/packages/a4/5f/1e1d9173b5c41eff54f88d9f4ee82c38eb4928120ab6a21a68a78d1c499e/langgraph-0.3.5-py3-none-any.whl", hash = "sha256:be313ec300633c857873ea3e44aece4dd7d0b11f131d385108b359d377a85bf7", size = 131527 },
1833
  ]
1834
 
1835
  [[package]]
 
1847
 
1848
  [[package]]
1849
  name = "langgraph-prebuilt"
1850
+ version = "0.1.2"
1851
  source = { registry = "https://pypi.org/simple" }
1852
  dependencies = [
1853
  { name = "langchain-core" },
1854
  { name = "langgraph-checkpoint" },
1855
  ]
1856
+ sdist = { url = "https://files.pythonhosted.org/packages/99/68/e1e692dbaeb4e9159b60a585fbfc26fbf073b3bb061caa2ff3153f85121a/langgraph_prebuilt-0.1.2.tar.gz", hash = "sha256:cfa7e54006d45e8f3d034ee88fa1d457c381bf6a2a0de0e64c5d3a776659e6d0", size = 23310 }
1857
  wheels = [
1858
+ { url = "https://files.pythonhosted.org/packages/73/2c/2fd70d557b7343f766f79dc8184b391f3417fc85b34dd04439cdd12dc2e1/langgraph_prebuilt-0.1.2-py3-none-any.whl", hash = "sha256:32028c4c4370576748e6c2e075cab1e13b5e3f2c196a390d71cacfb455212311", size = 24684 },
1859
  ]
1860
 
1861
  [[package]]
 
1914
  { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 },
1915
  ]
1916
 
1917
+ [[package]]
1918
+ name = "lxml"
1919
+ version = "5.3.1"
1920
+ source = { registry = "https://pypi.org/simple" }
1921
+ sdist = { url = "https://files.pythonhosted.org/packages/ef/f6/c15ca8e5646e937c148e147244817672cf920b56ac0bf2cc1512ae674be8/lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8", size = 3678591 }
1922
+ wheels = [
1923
+ { url = "https://files.pythonhosted.org/packages/80/4b/73426192004a643c11a644ed2346dbe72da164c8e775ea2e70f60e63e516/lxml-5.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b", size = 8142766 },
1924
+ { url = "https://files.pythonhosted.org/packages/30/c2/3b28f642b43fdf9580d936e8fdd3ec43c01a97ecfe17fd67f76ce9099752/lxml-5.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b", size = 4422744 },
1925
+ { url = "https://files.pythonhosted.org/packages/1f/a5/45279e464174b99d72d25bc018b097f9211c0925a174ca582a415609f036/lxml-5.3.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5", size = 5229609 },
1926
+ { url = "https://files.pythonhosted.org/packages/f0/e7/10cd8b9e27ffb6b3465b76604725b67b7c70d4e399750ff88de1b38ab9eb/lxml-5.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3", size = 4943509 },
1927
+ { url = "https://files.pythonhosted.org/packages/ce/54/2d6f634924920b17122445136345d44c6d69178c9c49e161aa8f206739d6/lxml-5.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c", size = 5561495 },
1928
+ { url = "https://files.pythonhosted.org/packages/a2/fe/7f5ae8fd1f357fcb21b0d4e20416fae870d654380b6487adbcaaf0df9b31/lxml-5.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2", size = 4998970 },
1929
+ { url = "https://files.pythonhosted.org/packages/af/70/22fecb6f2ca8dc77d14ab6be3cef767ff8340040bc95dca384b5b1cb333a/lxml-5.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac", size = 5114205 },
1930
+ { url = "https://files.pythonhosted.org/packages/63/91/21619cc14f7fd1de3f1bdf86cc8106edacf4d685b540d658d84247a3a32a/lxml-5.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9", size = 4940823 },
1931
+ { url = "https://files.pythonhosted.org/packages/50/0f/27183248fa3cdd2040047ceccd320ff1ed1344167f38a4ac26aed092268b/lxml-5.3.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9", size = 5585725 },
1932
+ { url = "https://files.pythonhosted.org/packages/c6/8d/9b7388d5b23ed2f239a992a478cbd0ce313aaa2d008dd73c4042b190b6a9/lxml-5.3.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79", size = 5082641 },
1933
+ { url = "https://files.pythonhosted.org/packages/65/8e/590e20833220eac55b6abcde71d3ae629d38ac1c3543bcc2bfe1f3c2f5d1/lxml-5.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2", size = 5161219 },
1934
+ { url = "https://files.pythonhosted.org/packages/4e/77/cabdf5569fd0415a88ebd1d62d7f2814e71422439b8564aaa03e7eefc069/lxml-5.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51", size = 5019293 },
1935
+ { url = "https://files.pythonhosted.org/packages/49/bd/f0b6d50ea7b8b54aaa5df4410cb1d5ae6ffa016b8e0503cae08b86c24674/lxml-5.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406", size = 5651232 },
1936
+ { url = "https://files.pythonhosted.org/packages/fa/69/1793d00a4e3da7f27349edb5a6f3da947ed921263cd9a243fab11c6cbc07/lxml-5.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5", size = 5489527 },
1937
+ { url = "https://files.pythonhosted.org/packages/d3/c9/e2449129b6cb2054c898df8d850ea4dadd75b4c33695a6c4b0f35082f1e7/lxml-5.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0", size = 5227050 },
1938
+ { url = "https://files.pythonhosted.org/packages/ed/63/e5da540eba6ab9a0d4188eeaa5c85767b77cafa8efeb70da0593d6cd3b81/lxml-5.3.1-cp310-cp310-win32.whl", hash = "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23", size = 3475345 },
1939
+ { url = "https://files.pythonhosted.org/packages/08/71/853a3ad812cd24c35b7776977cb0ae40c2b64ff79ad6d6c36c987daffc49/lxml-5.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c", size = 3805093 },
1940
+ { url = "https://files.pythonhosted.org/packages/d2/b4/89a68d05f267f05cc1b8b2f289a8242955705b1b0a9d246198227817ee46/lxml-5.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725", size = 3936118 },
1941
+ { url = "https://files.pythonhosted.org/packages/7f/0d/c034a541e7a1153527d7880c62493a74f2277f38e64de2480cadd0d4cf96/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d", size = 4233690 },
1942
+ { url = "https://files.pythonhosted.org/packages/35/5c/38e183c2802f14fbdaa75c3266e11d0ca05c64d78e8cdab2ee84e954a565/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84", size = 4349569 },
1943
+ { url = "https://files.pythonhosted.org/packages/18/5b/14f93b359b3c29673d5d282bc3a6edb3a629879854a77541841aba37607f/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2", size = 4236731 },
1944
+ { url = "https://files.pythonhosted.org/packages/f6/08/8471de65f3dee70a3a50e7082fd7409f0ac7a1ace777c13fca4aea1a5759/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877", size = 4373119 },
1945
+ { url = "https://files.pythonhosted.org/packages/83/29/00b9b0322a473aee6cda87473401c9abb19506cd650cc69a8aa38277ea74/lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499", size = 3487718 },
1946
+ ]
1947
+
1948
  [[package]]
1949
  name = "mapclassify"
1950
  version = "2.8.1"
 
1983
  { url = "https://files.pythonhosted.org/packages/cc/94/3d619cc82c30daeacd18a88674f4e6540ebfb7b4b7752ca0552793be80cf/marisa_trie-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:5685a14b3099b1422c4f59fa38b0bf4b5342ee6cc38ae57df9666a0b28eeaad3", size = 151891 },
1984
  ]
1985
 
1986
+ [[package]]
1987
+ name = "markdown"
1988
+ version = "3.7"
1989
+ source = { registry = "https://pypi.org/simple" }
1990
+ sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 }
1991
+ wheels = [
1992
+ { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 },
1993
+ ]
1994
+
1995
  [[package]]
1996
  name = "markdown-it-py"
1997
  version = "3.0.0"
 
2004
  { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
2005
  ]
2006
 
2007
+ [[package]]
2008
+ name = "markdownlit"
2009
+ version = "0.0.7"
2010
+ source = { registry = "https://pypi.org/simple" }
2011
+ dependencies = [
2012
+ { name = "favicon" },
2013
+ { name = "htbuilder" },
2014
+ { name = "lxml" },
2015
+ { name = "markdown" },
2016
+ { name = "pymdown-extensions" },
2017
+ { name = "streamlit" },
2018
+ { name = "streamlit-extras" },
2019
+ ]
2020
+ sdist = { url = "https://files.pythonhosted.org/packages/13/ab/c6eca20098fc7464df4f1626c65ca07b76903c5e2e9fa60763037e3554a4/markdownlit-0.0.7.tar.gz", hash = "sha256:553e2db454e2be4567caebef5176c98a40a7e24f7ea9c2fe8a1f05c1d9ea4005", size = 14289 }
2021
+ wheels = [
2022
+ { url = "https://files.pythonhosted.org/packages/5a/95/07e666469473043e16f263592c42e966e8b9a61fcc785ed39f48751adeb0/markdownlit-0.0.7-py3-none-any.whl", hash = "sha256:b58bb539dcb52e0b040ab2fed32f1f3146cbb2746dc3812940d9dd359c378bb6", size = 15266 },
2023
+ ]
2024
+
2025
  [[package]]
2026
  name = "markupsafe"
2027
  version = "3.0.2"
 
2361
 
2362
  [[package]]
2363
  name = "numpy"
2364
+ version = "2.2.3"
2365
  source = { registry = "https://pypi.org/simple" }
2366
+ sdist = { url = "https://files.pythonhosted.org/packages/fb/90/8956572f5c4ae52201fdec7ba2044b2c882832dcec7d5d0922c9e9acf2de/numpy-2.2.3.tar.gz", hash = "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020", size = 20262700 }
2367
  wheels = [
2368
+ { url = "https://files.pythonhosted.org/packages/5e/e1/1816d5d527fa870b260a1c2c5904d060caad7515637bd54f495a5ce13ccd/numpy-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cbc6472e01952d3d1b2772b720428f8b90e2deea8344e854df22b0618e9cce71", size = 21232911 },
2369
+ { url = "https://files.pythonhosted.org/packages/29/46/9f25dc19b359f10c0e52b6bac25d3181eb1f4b4d04c9846a32cf5ea52762/numpy-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdfe0c22692a30cd830c0755746473ae66c4a8f2e7bd508b35fb3b6a0813d787", size = 14371955 },
2370
+ { url = "https://files.pythonhosted.org/packages/72/d7/de941296e6b09a5c81d3664ad912f1496a0ecdd2f403318e5e35604ff70f/numpy-2.2.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:e37242f5324ffd9f7ba5acf96d774f9276aa62a966c0bad8dae692deebec7716", size = 5410476 },
2371
+ { url = "https://files.pythonhosted.org/packages/36/ce/55f685995110f8a268fdca0f198c9a84fa87b39512830965cc1087af6391/numpy-2.2.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:95172a21038c9b423e68be78fd0be6e1b97674cde269b76fe269a5dfa6fadf0b", size = 6945730 },
2372
+ { url = "https://files.pythonhosted.org/packages/4f/84/abdb9f6e22576d89c259401c3234d4755b322539491bbcffadc8bcb120d3/numpy-2.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b47c440210c5d1d67e1cf434124e0b5c395eee1f5806fdd89b553ed1acd0a3", size = 14350752 },
2373
+ { url = "https://files.pythonhosted.org/packages/e9/88/3870cfa9bef4dffb3a326507f430e6007eeac258ebeef6b76fc542aef66d/numpy-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0391ea3622f5c51a2e29708877d56e3d276827ac5447d7f45e9bc4ade8923c52", size = 16399386 },
2374
+ { url = "https://files.pythonhosted.org/packages/02/10/3f629682dd0b457525c131945329c4e81e2dadeb11256e6ce4c9a1a6fb41/numpy-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f6b3dfc7661f8842babd8ea07e9897fe3d9b69a1d7e5fbb743e4160f9387833b", size = 15561826 },
2375
+ { url = "https://files.pythonhosted.org/packages/da/18/fd35673ba9751eba449d4ce5d24d94e3b612cdbfba79348da71488c0b7ac/numpy-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1ad78ce7f18ce4e7df1b2ea4019b5817a2f6a8a16e34ff2775f646adce0a5027", size = 18188593 },
2376
+ { url = "https://files.pythonhosted.org/packages/ce/4c/c0f897b580ea59484b4cc96a441fea50333b26675a60a1421bc912268b5f/numpy-2.2.3-cp310-cp310-win32.whl", hash = "sha256:5ebeb7ef54a7be11044c33a17b2624abe4307a75893c001a4800857956b41094", size = 6590421 },
2377
+ { url = "https://files.pythonhosted.org/packages/e5/5b/aaabbfc7060c5c8f0124c5deb5e114a3b413a548bbc64e372c5b5db36165/numpy-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:596140185c7fa113563c67c2e894eabe0daea18cf8e33851738c19f70ce86aeb", size = 12925667 },
2378
+ { url = "https://files.pythonhosted.org/packages/0a/b5/a7839f5478be8f859cb880f13d90fcfe4b0ec7a9ebaff2bcc30d96760596/numpy-2.2.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3c2ec8a0f51d60f1e9c0c5ab116b7fc104b165ada3f6c58abf881cb2eb16044d", size = 21064244 },
2379
+ { url = "https://files.pythonhosted.org/packages/29/e8/5da32ffcaa7a72f7ecd82f90c062140a061eb823cb88e90279424e515cf4/numpy-2.2.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ed2cf9ed4e8ebc3b754d398cba12f24359f018b416c380f577bbae112ca52fc9", size = 6809418 },
2380
+ { url = "https://files.pythonhosted.org/packages/a8/a9/68aa7076c7656a7308a0f73d0a2ced8c03f282c9fd98fa7ce21c12634087/numpy-2.2.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39261798d208c3095ae4f7bc8eaeb3481ea8c6e03dc48028057d3cbdbdb8937e", size = 16215461 },
2381
+ { url = "https://files.pythonhosted.org/packages/17/7f/d322a4125405920401450118dbdc52e0384026bd669939484670ce8b2ab9/numpy-2.2.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:783145835458e60fa97afac25d511d00a1eca94d4a8f3ace9fe2043003c678e4", size = 12839607 },
2382
  ]
2383
 
2384
  [[package]]
 
2412
 
2413
  [[package]]
2414
  name = "openai"
2415
+ version = "1.65.4"
2416
  source = { registry = "https://pypi.org/simple" }
2417
  dependencies = [
2418
  { name = "anyio" },
 
2424
  { name = "tqdm" },
2425
  { name = "typing-extensions" },
2426
  ]
2427
+ sdist = { url = "https://files.pythonhosted.org/packages/fa/8d/1f7aace801afbbe4d6b8c7fa89b76eb9a3a8eeff38b84d4005d47b226b30/openai-1.65.4.tar.gz", hash = "sha256:0b08c58625d556f5c6654701af1023689c173eb0989ce8f73c7fd0eb22203c76", size = 359365 }
2428
  wheels = [
2429
+ { url = "https://files.pythonhosted.org/packages/ba/db/7bab832be24631a793492c1c61ecbf029018b99696f435db3b63d690bf1c/openai-1.65.4-py3-none-any.whl", hash = "sha256:15566d46574b94eae3d18efc2f9a4ebd1366d1d44bfc1bdafeea7a5cf8271bcb", size = 473523 },
2430
  ]
2431
 
2432
  [[package]]
 
2740
  { name = "presidio-analyzer" },
2741
  { name = "presidio-anonymizer" },
2742
  { name = "py7zr" },
2743
+ { name = "pypandoc" },
2744
  { name = "pypdf" },
2745
  { name = "pypdf2" },
2746
  { name = "pyqt6" },
 
2749
  { name = "spacytextblob" },
2750
  { name = "streamlit" },
2751
  { name = "streamlit-authenticator" },
2752
+ { name = "streamlit-extras" },
2753
  { name = "tabulate" },
2754
  { name = "transformers" },
2755
  ]
 
2785
  { name = "presidio-analyzer", specifier = ">=2.2.355" },
2786
  { name = "presidio-anonymizer", specifier = ">=2.2.355" },
2787
  { name = "py7zr", specifier = ">=0.22.0" },
2788
+ { name = "pypandoc", specifier = ">=1.15" },
2789
  { name = "pypdf", specifier = ">=5.1.0" },
2790
  { name = "pypdf2", specifier = ">=3.0.1" },
2791
  { name = "pyqt6", specifier = ">=6.7.1" },
 
2794
  { name = "spacytextblob", specifier = ">=4.0.0" },
2795
  { name = "streamlit", specifier = ">=1.41.1" },
2796
  { name = "streamlit-authenticator", specifier = ">=0.4.1" },
2797
+ { name = "streamlit-extras", specifier = ">=0.5.5" },
2798
  { name = "tabulate", specifier = ">=0.9.0" },
2799
  { name = "transformers", specifier = ">=4.44.2" },
2800
  ]
 
2815
  { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
2816
  ]
2817
 
2818
+ [[package]]
2819
+ name = "plotly"
2820
+ version = "6.0.0"
2821
+ source = { registry = "https://pypi.org/simple" }
2822
+ dependencies = [
2823
+ { name = "narwhals" },
2824
+ { name = "packaging" },
2825
+ ]
2826
+ sdist = { url = "https://files.pythonhosted.org/packages/9c/80/761c14012d6daf18e12b6d1e4f6b218e999bcceb694d7a9b180154f9e4db/plotly-6.0.0.tar.gz", hash = "sha256:c4aad38b8c3d65e4a5e7dd308b084143b9025c2cc9d5317fc1f1d30958db87d3", size = 8111782 }
2827
+ wheels = [
2828
+ { url = "https://files.pythonhosted.org/packages/0e/77/a946f38b57fb88e736c71fbdd737a1aebd27b532bda0779c137f357cf5fc/plotly-6.0.0-py3-none-any.whl", hash = "sha256:f708871c3a9349a68791ff943a5781b1ec04de7769ea69068adcd9202e57653a", size = 14805949 },
2829
+ ]
2830
+
2831
  [[package]]
2832
  name = "polars"
2833
  version = "1.24.0"
 
2844
 
2845
  [[package]]
2846
  name = "posthog"
2847
+ version = "3.18.1"
2848
  source = { registry = "https://pypi.org/simple" }
2849
  dependencies = [
2850
  { name = "backoff" },
 
2854
  { name = "requests" },
2855
  { name = "six" },
2856
  ]
2857
+ sdist = { url = "https://files.pythonhosted.org/packages/a5/1c/aa6bb26491108e9e350cd7af4d4b0a54d48c755cc76b2c2d90ef2916b8b3/posthog-3.18.1.tar.gz", hash = "sha256:ce115b8422f26c57cd4143499115b741f5683c93d0b5b87bab391579aaef084b", size = 65573 }
2858
  wheels = [
2859
+ { url = "https://files.pythonhosted.org/packages/04/c2/407c8cf3edf4fe33b82de3fee11178d083ee0b6e3eb28ff8072caaa85907/posthog-3.18.1-py2.py3-none-any.whl", hash = "sha256:6865104b7cf3a5b13949e2bc2aab9b37b5fbf5f9e045fa55b9eabe21b3850200", size = 76762 },
2860
  ]
2861
 
2862
  [[package]]
 
3213
  { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 },
3214
  ]
3215
 
3216
+ [[package]]
3217
+ name = "pymdown-extensions"
3218
+ version = "10.14.3"
3219
+ source = { registry = "https://pypi.org/simple" }
3220
+ dependencies = [
3221
+ { name = "markdown" },
3222
+ { name = "pyyaml" },
3223
+ ]
3224
+ sdist = { url = "https://files.pythonhosted.org/packages/7c/44/e6de2fdc880ad0ec7547ca2e087212be815efbc9a425a8d5ba9ede602cbb/pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b", size = 846846 }
3225
+ wheels = [
3226
+ { url = "https://files.pythonhosted.org/packages/eb/f5/b9e2a42aa8f9e34d52d66de87941ecd236570c7ed2e87775ed23bbe4e224/pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", size = 264467 },
3227
+ ]
3228
+
3229
  [[package]]
3230
  name = "pyogrio"
3231
  version = "0.10.0"
 
3245
  { url = "https://files.pythonhosted.org/packages/75/ca/b31083da2e6c4b598b6609a98c655977189fe8982c36d98ea4789a938045/pyogrio-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:eac90b2501656892c63bc500c12e71f3dbf7d66ddc5a7fb05cd480d25d1b7022", size = 16171065 },
3246
  ]
3247
 
3248
+ [[package]]
3249
+ name = "pypandoc"
3250
+ version = "1.15"
3251
+ source = { registry = "https://pypi.org/simple" }
3252
+ sdist = { url = "https://files.pythonhosted.org/packages/e1/88/26e650d053df5f3874aa3c05901a14166ce3271f58bfe114fd776987efbd/pypandoc-1.15.tar.gz", hash = "sha256:ea25beebe712ae41d63f7410c08741a3cab0e420f6703f95bc9b3a749192ce13", size = 32940 }
3253
+ wheels = [
3254
+ { url = "https://files.pythonhosted.org/packages/61/06/0763e0ccc81754d3eadb21b2cb86cf21bdedc9b52698c2ad6785db7f0a4e/pypandoc-1.15-py3-none-any.whl", hash = "sha256:4ededcc76c8770f27aaca6dff47724578428eca84212a31479403a9731fc2b16", size = 21321 },
3255
+ ]
3256
+
3257
  [[package]]
3258
  name = "pyparsing"
3259
  version = "3.2.1"
 
3950
  { url = "https://files.pythonhosted.org/packages/d5/b8/3dfed2db5c7ecf275aaddb775e2ae17c576b09c848873188fce91e410129/srsly-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:1a4dddb2edb8f7974c9aa5ec46dc687a75215b3bbdc815ce3fc9ea68fe1e94b5", size = 632267 },
3951
  ]
3952
 
3953
+ [[package]]
3954
+ name = "st-annotated-text"
3955
+ version = "4.0.2"
3956
+ source = { registry = "https://pypi.org/simple" }
3957
+ dependencies = [
3958
+ { name = "htbuilder" },
3959
+ ]
3960
+ sdist = { url = "https://files.pythonhosted.org/packages/4d/76/c0f64a56b779bfb5c87437687a9970d12dad37b5aea2541823d0c5010818/st_annotated_text-4.0.2.tar.gz", hash = "sha256:b0134dcf734697cc3dbdb11862c4174071e7fb6f365af55a37c710d357fd88a5", size = 7915 }
3961
+ wheels = [
3962
+ { url = "https://files.pythonhosted.org/packages/d9/70/f29d4359ddfd1a0afa00065e4dac32bd4931f589185f5f0f499db9aee4fe/st_annotated_text-4.0.2-py3-none-any.whl", hash = "sha256:712c45821f020eccafad3a58c10b9d2d51d449f4db999a06308ecf39a753a4df", size = 9084 },
3963
+ ]
3964
+
3965
+ [[package]]
3966
+ name = "st-theme"
3967
+ version = "1.2.3"
3968
+ source = { registry = "https://pypi.org/simple" }
3969
+ dependencies = [
3970
+ { name = "streamlit" },
3971
+ ]
3972
+ sdist = { url = "https://files.pythonhosted.org/packages/31/84/3e2fd95f5acaf6c92a51e7f038f1120235c65bb0b8be9bf43aa9fb4267e5/st-theme-1.2.3.tar.gz", hash = "sha256:ca97aece1a48ded6e83fd742c27cb0851e1bce2100ab4b6c37c7b6e003b65b42", size = 73873 }
3973
+ wheels = [
3974
+ { url = "https://files.pythonhosted.org/packages/30/09/cd7134f1ed5074a7d456640e7ba9a8c8e68a831837b4e7bfd9f29e5700a4/st_theme-1.2.3-py3-none-any.whl", hash = "sha256:0a54d9817dd5f8a6d7b0d071b25ae72eacf536c63a5fb97374923938021b1389", size = 75205 },
3975
+ ]
3976
+
3977
  [[package]]
3978
  name = "stack-data"
3979
  version = "0.6.3"
 
4002
 
4003
  [[package]]
4004
  name = "streamlit"
4005
+ version = "1.43.0"
4006
  source = { registry = "https://pypi.org/simple" }
4007
  dependencies = [
4008
  { name = "altair" },
 
4018
  { name = "pyarrow" },
4019
  { name = "pydeck" },
4020
  { name = "requests" },
 
4021
  { name = "tenacity" },
4022
  { name = "toml" },
4023
  { name = "tornado" },
4024
  { name = "typing-extensions" },
4025
  { name = "watchdog", marker = "sys_platform != 'darwin'" },
4026
  ]
4027
+ sdist = { url = "https://files.pythonhosted.org/packages/02/05/7b0a78beb4589fa056435fdf2243bc28ba3b320f4bc54b2cbf7680d7848b/streamlit-1.43.0.tar.gz", hash = "sha256:c10c09f9d1251fa7f975dd360572f03cabc82b174f080e323bf7e556103c22e0", size = 9344982 }
4028
  wheels = [
4029
+ { url = "https://files.pythonhosted.org/packages/e9/37/6ae099cf3029990fb2b35420296e3066c649a9dc5c286fa74643b1d7074f/streamlit-1.43.0-py2.py3-none-any.whl", hash = "sha256:cf94b1e9f1de75e4e383df53745230feaac4ac7a7e1f14a3ea362df134db8510", size = 9734392 },
4030
  ]
4031
 
4032
  [[package]]
 
4046
  { url = "https://files.pythonhosted.org/packages/83/47/837b158e1a5b0d187d20c6be22c46d84d12a8d3e8d7113b67ebb33e221c9/streamlit_authenticator-0.4.2-py3-none-any.whl", hash = "sha256:442acccef6af65e2b0feb15d5e9f68707f204c1d31c60673690d87179c7ca5b2", size = 43197 },
4047
  ]
4048
 
4049
+ [[package]]
4050
+ name = "streamlit-camera-input-live"
4051
+ version = "0.2.0"
4052
+ source = { registry = "https://pypi.org/simple" }
4053
+ dependencies = [
4054
+ { name = "jinja2" },
4055
+ { name = "streamlit" },
4056
+ ]
4057
+ sdist = { url = "https://files.pythonhosted.org/packages/91/e1/770277a024a02916120fb396a2ec9524fba2a00d077f98295e71bbb698e7/streamlit-camera-input-live-0.2.0.tar.gz", hash = "sha256:20ceb952b98410084176fcfeb9148e02ea29033a88d4a923161ac7890cedae0f", size = 5761 }
4058
+ wheels = [
4059
+ { url = "https://files.pythonhosted.org/packages/23/9b/1c0ea2b4cc39ff1be6ad7171ac9455203d08e725a9e101cc3e7311589842/streamlit_camera_input_live-0.2.0-py3-none-any.whl", hash = "sha256:dacb56cdedbb0d6c07e35a66b755b9145b5023e5c855c64193c3d3e73198e9be", size = 6586 },
4060
+ ]
4061
+
4062
+ [[package]]
4063
+ name = "streamlit-card"
4064
+ version = "1.0.2"
4065
+ source = { registry = "https://pypi.org/simple" }
4066
+ dependencies = [
4067
+ { name = "streamlit" },
4068
+ ]
4069
+ sdist = { url = "https://files.pythonhosted.org/packages/67/39/765f93dc09cabb8d4762714bf3a0d25892671450eda2c48e4dbdb2292d6b/streamlit_card-1.0.2.tar.gz", hash = "sha256:8001cd5edd8a6e2db36ee81f37dc645f08f78c21a2ba968403176c68b4f33cb1", size = 672326 }
4070
+ wheels = [
4071
+ { url = "https://files.pythonhosted.org/packages/05/22/1d81bd6b2552c67f50a19501c00c7ee08022eacffb21c6900929359f7ce5/streamlit_card-1.0.2-py3-none-any.whl", hash = "sha256:f5d01ce57d6481eb3ba44e504146f56a7b82907d6700f0c19266ed6381a9c58f", size = 680848 },
4072
+ ]
4073
+
4074
+ [[package]]
4075
+ name = "streamlit-embedcode"
4076
+ version = "0.1.2"
4077
+ source = { registry = "https://pypi.org/simple" }
4078
+ dependencies = [
4079
+ { name = "streamlit" },
4080
+ ]
4081
+ sdist = { url = "https://files.pythonhosted.org/packages/61/ef/9c254c44bb5cb276bb2503b1b7101274cdc9f714206fb73d3ec59bec6085/streamlit-embedcode-0.1.2.tar.gz", hash = "sha256:22a50eb43407bab3d0ed2d4b58e89819da477cd0592ef87edbd373c286712e3a", size = 3567 }
4082
+ wheels = [
4083
+ { url = "https://files.pythonhosted.org/packages/72/0b/5c68315b24d7565a1c51611c94124cafce5463ed869270c762552d74d96b/streamlit_embedcode-0.1.2-py3-none-any.whl", hash = "sha256:b3c9520c1b48f2eef3c702b5a967f64c9a8ff2ea8e74ebb26c0e9195965bb923", size = 3534 },
4084
+ ]
4085
+
4086
+ [[package]]
4087
+ name = "streamlit-extras"
4088
+ version = "0.5.5"
4089
+ source = { registry = "https://pypi.org/simple" }
4090
+ dependencies = [
4091
+ { name = "entrypoints" },
4092
+ { name = "htbuilder" },
4093
+ { name = "markdownlit" },
4094
+ { name = "plotly" },
4095
+ { name = "prometheus-client" },
4096
+ { name = "protobuf" },
4097
+ { name = "st-annotated-text" },
4098
+ { name = "st-theme" },
4099
+ { name = "streamlit" },
4100
+ { name = "streamlit-camera-input-live" },
4101
+ { name = "streamlit-card" },
4102
+ { name = "streamlit-embedcode" },
4103
+ { name = "streamlit-faker" },
4104
+ { name = "streamlit-image-coordinates" },
4105
+ { name = "streamlit-keyup" },
4106
+ { name = "streamlit-toggle-switch" },
4107
+ { name = "streamlit-vertical-slider" },
4108
+ { name = "validators" },
4109
+ ]
4110
+ sdist = { url = "https://files.pythonhosted.org/packages/bc/30/bb0241c7418bbc383958c8b3b20fa995c2f7e219fe3cc7fcbe03e21afef9/streamlit_extras-0.5.5.tar.gz", hash = "sha256:3b86e1bb2a626aff981b894f8e29f411e90adcc428f6460b431453f2cde61cbf", size = 55964 }
4111
+ wheels = [
4112
+ { url = "https://files.pythonhosted.org/packages/4a/cd/e47700a0af889604cf16f47306ddfb642decba0909e9871ecdbc694e076e/streamlit_extras-0.5.5-py3-none-any.whl", hash = "sha256:d9dbe58ca774cf969e3f4b1a7746620ef4faa71514359677484ed12dada012e7", size = 78445 },
4113
+ ]
4114
+
4115
+ [[package]]
4116
+ name = "streamlit-faker"
4117
+ version = "0.0.3"
4118
+ source = { registry = "https://pypi.org/simple" }
4119
+ dependencies = [
4120
+ { name = "faker" },
4121
+ { name = "matplotlib" },
4122
+ { name = "streamlit" },
4123
+ { name = "streamlit-extras" },
4124
+ ]
4125
+ sdist = { url = "https://files.pythonhosted.org/packages/bc/14/8ad7e53d381a6c5da920e590e9a7521d542fbae55eb6ec842aaab58f2ff9/streamlit_faker-0.0.3.tar.gz", hash = "sha256:bff0f053aa514a99313a3699746183b41111891c82d6e9b41b1c69a7d719bf2f", size = 12680 }
4126
+ wheels = [
4127
+ { url = "https://files.pythonhosted.org/packages/b0/2e/d60c0214dc6bf626e37b9f37f7ac3a57c311946ebcc12c1fc0e7bcc4b8d6/streamlit_faker-0.0.3-py3-none-any.whl", hash = "sha256:caf410867b55b4877d8fe73cc987d089e1938f8e63594f1eb579e28015844215", size = 14073 },
4128
+ ]
4129
+
4130
+ [[package]]
4131
+ name = "streamlit-image-coordinates"
4132
+ version = "0.1.9"
4133
+ source = { registry = "https://pypi.org/simple" }
4134
+ dependencies = [
4135
+ { name = "jinja2" },
4136
+ { name = "streamlit" },
4137
+ ]
4138
+ sdist = { url = "https://files.pythonhosted.org/packages/36/4f/270c310ec1b200914200bc8387ce166e50c247ae8e4f9e5072a8b674ba37/streamlit_image_coordinates-0.1.9.tar.gz", hash = "sha256:825e1f49053f1363913014a4e9130a03b9ca01fb5f7bd269b17afe8162d2ba37", size = 6494 }
4139
+ wheels = [
4140
+ { url = "https://files.pythonhosted.org/packages/5e/cf/1dba1380eb3b632f1f86c80533a3fca1376a938517044048122adf816a90/streamlit_image_coordinates-0.1.9-py3-none-any.whl", hash = "sha256:e577d475707ce8a3f7be1825027af6b4d7b609a456f4b25b794756ed2436ab06", size = 7049 },
4141
+ ]
4142
+
4143
+ [[package]]
4144
+ name = "streamlit-keyup"
4145
+ version = "0.3.0"
4146
+ source = { registry = "https://pypi.org/simple" }
4147
+ dependencies = [
4148
+ { name = "jinja2" },
4149
+ { name = "streamlit" },
4150
+ ]
4151
+ sdist = { url = "https://files.pythonhosted.org/packages/88/25/d153babd6d8dff72ee2e56976862ec7fdd7856ec7da2cb6ffd4b3b12f336/streamlit_keyup-0.3.0.tar.gz", hash = "sha256:8595a14892423243669e5d50e982853ffb7eb201b65952a48676133ab9bbc937", size = 7387 }
4152
+ wheels = [
4153
+ { url = "https://files.pythonhosted.org/packages/14/ed/facce225c0d360b9bfa053c827029312e1b255e84d79320bda3539417b50/streamlit_keyup-0.3.0-py3-none-any.whl", hash = "sha256:ec7221617b1c832526db52859196c417578d6b4285942fbd10a0b2ff313899b3", size = 7493 },
4154
+ ]
4155
+
4156
+ [[package]]
4157
+ name = "streamlit-toggle-switch"
4158
+ version = "1.0.2"
4159
+ source = { registry = "https://pypi.org/simple" }
4160
+ dependencies = [
4161
+ { name = "streamlit" },
4162
+ ]
4163
+ sdist = { url = "https://files.pythonhosted.org/packages/12/ce/9b8b93bc87223b0680fb73ea3c877d4f02390da12a6a1a561b90ec438d0d/streamlit_toggle_switch-1.0.2.tar.gz", hash = "sha256:991b103cd3448b0f6507f8051777b996a17b4630956d5b6fa13344175b20e572", size = 627850 }
4164
+ wheels = [
4165
+ { url = "https://files.pythonhosted.org/packages/7c/bc/f3ffd4143379756c494b8cb5faf1fffae48b722fe7b05794068d05bec965/streamlit_toggle_switch-1.0.2-py3-none-any.whl", hash = "sha256:0081212d80d178bda337acf2432425e2016d757f57834b18645d4c5b928d4c0f", size = 635443 },
4166
+ ]
4167
+
4168
+ [[package]]
4169
+ name = "streamlit-vertical-slider"
4170
+ version = "2.5.5"
4171
+ source = { registry = "https://pypi.org/simple" }
4172
+ dependencies = [
4173
+ { name = "streamlit" },
4174
+ ]
4175
+ sdist = { url = "https://files.pythonhosted.org/packages/29/b6/c4596692276174e01899e4da3bdcef101451bc45f1a738aad54a0e60bcfe/streamlit_vertical_slider-2.5.5.tar.gz", hash = "sha256:d6854cf81a606f5c021df2037d2c49036df2d03ce5082a5227a2acca8322ca74", size = 635192 }
4176
+ wheels = [
4177
+ { url = "https://files.pythonhosted.org/packages/4e/3f/f16875fa650cc6f6c1bc19ef9ccae5a1a7f434ec08747181743b48c31830/streamlit_vertical_slider-2.5.5-py3-none-any.whl", hash = "sha256:8182e861444fcd69e05c05e7109a636d459560c249f1addf78b58e525a719cb6", size = 1769408 },
4178
+ ]
4179
+
4180
  [[package]]
4181
  name = "sympy"
4182
  version = "1.13.3"
 
4483
 
4484
  [[package]]
4485
  name = "unstructured-client"
4486
+ version = "0.31.0"
4487
  source = { registry = "https://pypi.org/simple" }
4488
  dependencies = [
4489
  { name = "aiofiles" },
 
4497
  { name = "requests-toolbelt" },
4498
  { name = "typing-inspect" },
4499
  ]
4500
+ sdist = { url = "https://files.pythonhosted.org/packages/6e/ff/0a79ca15acee5633a57f21147b3d3bd06a870937176ee20c8b56723df994/unstructured_client-0.31.0.tar.gz", hash = "sha256:6f8ce2cc47e8c79796a33bfb057ca33f1b47e4c43e95be6bbfeb515f43db5ad9", size = 75809 }
4501
  wheels = [
4502
+ { url = "https://files.pythonhosted.org/packages/b8/47/e6c45e30d4cb06f4ebe2699cc17ca95e6e0a942550042215d62aabea6cf0/unstructured_client-0.31.0-py3-none-any.whl", hash = "sha256:6ba9f038ca464fc7d9c5b8228252291af14ed56d7aae7f3136950cc9d9639a16", size = 166495 },
4503
  ]
4504
 
4505
  [[package]]
 
4559
  { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c", size = 3800523 },
4560
  ]
4561
 
4562
+ [[package]]
4563
+ name = "validators"
4564
+ version = "0.34.0"
4565
+ source = { registry = "https://pypi.org/simple" }
4566
+ sdist = { url = "https://files.pythonhosted.org/packages/64/07/91582d69320f6f6daaf2d8072608a4ad8884683d4840e7e4f3a9dbdcc639/validators-0.34.0.tar.gz", hash = "sha256:647fe407b45af9a74d245b943b18e6a816acf4926974278f6dd617778e1e781f", size = 70955 }
4567
+ wheels = [
4568
+ { url = "https://files.pythonhosted.org/packages/6e/78/36828a4d857b25896f9774c875714ba4e9b3bc8a92d2debe3f4df3a83d4f/validators-0.34.0-py3-none-any.whl", hash = "sha256:c804b476e3e6d3786fa07a30073a4ef694e617805eb1946ceee3fe5a9b8b1321", size = 43536 },
4569
+ ]
4570
+
4571
  [[package]]
4572
  name = "wasabi"
4573
  version = "1.1.3"
 
4683
 
4684
  [[package]]
4685
  name = "websockets"
4686
+ version = "15.0.1"
4687
+ source = { registry = "https://pypi.org/simple" }
4688
+ sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 }
4689
+ wheels = [
4690
+ { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423 },
4691
+ { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080 },
4692
+ { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329 },
4693
+ { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312 },
4694
+ { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319 },
4695
+ { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631 },
4696
+ { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016 },
4697
+ { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426 },
4698
+ { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360 },
4699
+ { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388 },
4700
+ { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830 },
4701
+ { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109 },
4702
+ { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343 },
4703
+ { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599 },
4704
+ { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207 },
4705
+ { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155 },
4706
+ { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884 },
4707
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 },
4708
  ]
4709
 
4710
  [[package]]