Skip to content

Commit 1a9a26b

Browse files
committed
perf: cache parsed CHANGELOG to skip markdown+BS4 on cold start
1 parent 83709f2 commit 1a9a26b

1 file changed

Lines changed: 47 additions & 21 deletions

File tree

backend/open_webui/env.py

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import hashlib
12
import importlib.metadata
23
import json
34
import logging
@@ -186,35 +187,60 @@ def parse_section(section):
186187
except Exception:
187188
changelog_content = (pkgutil.get_data('open_webui', 'CHANGELOG.md') or b'').decode()
188189

189-
# Convert markdown content to HTML
190-
html_content = markdown.markdown(changelog_content)
190+
# Cache parsed CHANGELOG to avoid markdown+BeautifulSoup overhead on every cold
191+
# start. The cache is content-addressed (MD5 of raw markdown) so it invalidates
192+
# automatically whenever CHANGELOG.md changes (e.g. on a version upgrade).
193+
_changelog_cache_dir = Path(os.getenv('DATA_DIR', BACKEND_DIR / 'data'))
194+
_changelog_cache_path = _changelog_cache_dir / 'changelog_cache.json'
195+
_changelog_hash = hashlib.md5(changelog_content.encode()).hexdigest()
191196

192-
# Parse the HTML content
193-
soup = BeautifulSoup(html_content, 'html.parser')
197+
changelog_json = None
198+
try:
199+
_cached = json.loads(_changelog_cache_path.read_text(encoding='utf8'))
200+
if _cached.get('_hash') == _changelog_hash:
201+
changelog_json = _cached['data']
202+
except Exception:
203+
pass
204+
205+
if changelog_json is None:
206+
# Convert markdown content to HTML
207+
html_content = markdown.markdown(changelog_content)
208+
209+
# Parse the HTML content
210+
soup = BeautifulSoup(html_content, 'html.parser')
194211

195-
# Initialize JSON structure
196-
changelog_json = {}
212+
# Initialize JSON structure
213+
changelog_json = {}
197214

198-
# Iterate over each version
199-
for version in soup.find_all('h2'):
200-
version_number = version.get_text().strip().split(' - ')[0][1:-1] # Remove brackets
201-
date = version.get_text().strip().split(' - ')[1]
215+
# Iterate over each version
216+
for version in soup.find_all('h2'):
217+
version_number = version.get_text().strip().split(' - ')[0][1:-1] # Remove brackets
218+
date = version.get_text().strip().split(' - ')[1]
202219

203-
version_data = {'date': date}
220+
version_data = {'date': date}
204221

205-
# Find the next sibling that is a h3 tag (section title)
206-
current = version.find_next_sibling()
222+
# Find the next sibling that is a h3 tag (section title)
223+
current = version.find_next_sibling()
207224

208-
while current and current.name != 'h2':
209-
if current.name == 'h3':
210-
section_title = current.get_text().lower() # e.g., "added", "fixed"
211-
section_items = parse_section(current.find_next_sibling('ul'))
212-
version_data[section_title] = section_items
225+
while current and current.name != 'h2':
226+
if current.name == 'h3':
227+
section_title = current.get_text().lower() # e.g., "added", "fixed"
228+
section_items = parse_section(current.find_next_sibling('ul'))
229+
version_data[section_title] = section_items
213230

214-
# Move to the next element
215-
current = current.find_next_sibling()
231+
# Move to the next element
232+
current = current.find_next_sibling()
216233

217-
changelog_json[version_number] = version_data
234+
changelog_json[version_number] = version_data
235+
236+
try:
237+
_changelog_cache_dir.mkdir(parents=True, exist_ok=True)
238+
_changelog_cache_path.write_text(
239+
json.dumps({'_hash': _changelog_hash, 'data': changelog_json}),
240+
encoding='utf8',
241+
)
242+
except Exception:
243+
pass
218244

219245
CHANGELOG = changelog_json
220246

0 commit comments

Comments
 (0)