From 214546399a89a5e3ce50a424b017b2b4aad895f1 Mon Sep 17 00:00:00 2001 From: Hwang In Tak Date: Tue, 24 Sep 2024 16:58:15 +0900 Subject: [PATCH 01/12] fix: fix katex rendering --- src/lib/utils/marked/katex-extension.ts | 163 +++++++++++++----------- 1 file changed, 92 insertions(+), 71 deletions(-) diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts index c90736462..2c5186dac 100644 --- a/src/lib/utils/marked/katex-extension.ts +++ b/src/lib/utils/marked/katex-extension.ts @@ -1,8 +1,8 @@ import katex from 'katex'; const DELIMITER_LIST = [ - { left: '$$', right: '$$', display: false }, { left: '$', right: '$', display: false }, + { left: '$$', right: '$$', display: true }, { left: '\\pu{', right: '}', display: false }, { left: '\\ce{', right: '}', display: false }, { left: '\\(', right: '\\)', display: false }, @@ -28,24 +28,24 @@ function escapeRegex(string) { function generateRegexRules(delimiters) { delimiters.forEach((delimiter) => { - const { left, right } = delimiter; + const { left, right, display } = delimiter; // Ensure regex-safe delimiters const escapedLeft = escapeRegex(left); const escapedRight = escapeRegex(right); - // Inline pattern - Capture group $1, token content, followed by end delimiter and normal punctuation marks. - // Example: $text$ - inlinePatterns.push( - `${escapedLeft}((?:\\\\.|[^\\\\\\n])*?(?:\\\\.|[^\\\\\\n${escapedRight}]))${escapedRight}` - ); - - // Block pattern - Starts and ends with the delimiter on new lines. Example: - // $$\ncontent here\n$$ - blockPatterns.push(`${escapedLeft}\n((?:\\\\[^]|[^\\\\])+?)\n${escapedRight}`); + if (!display) { + inlinePatterns.push( + `${escapedLeft}((?:\\\\.|[^\\\\\\n])*?(?:\\\\.|[^\\\\\\n${escapedRight}]))${escapedRight}` + ); + } else { + blockPatterns.push( + `${escapedLeft}((?:\\\\.|[^\\\\\\n])*?(?:\\\\.|[^\\\\\\n${escapedRight}]))${escapedRight}` + ); + } }); const inlineRule = new RegExp(`^(${inlinePatterns.join('|')})(?=[\\s?!.,:?!。,:]|$)`, 'u'); - const blockRule = new RegExp(`^(${blockPatterns.join('|')})(?:\n|$)`, 'u'); + const blockRule = new RegExp(`^(${blockPatterns.join('|')})(?=[\\s?!.,:?!。,:]|$)`, 'u'); return { inlineRule, blockRule }; } @@ -55,84 +55,105 @@ const { inlineRule, blockRule } = generateRegexRules(DELIMITER_LIST); export default function (options = {}) { return { extensions: [ - inlineKatex(options, createRenderer(options, false)), - blockKatex(options, createRenderer(options, true)) + inlineKatex(options), + blockKatex(options), ] }; } -function createRenderer(options, newlineAfter) { - return (token) => - katex.renderToString(token.text, { ...options, displayMode: token.displayMode }) + - (newlineAfter ? '\n' : ''); +function katexStart(src, displayMode: boolean) { + let ruleReg = displayMode ? blockRule : inlineRule; + + let indexSrc = src; + + while (indexSrc) { + let index = -1; + let startIndex = -1; + let startDelimiter = ''; + let endDelimiter = ''; + for (let delimiter of DELIMITER_LIST) { + if (delimiter.display !== displayMode) { + continue; + } + + startIndex = indexSrc.indexOf(delimiter.left); + if (startIndex === -1) { + continue; + } + + index = startIndex; + startDelimiter = delimiter.left; + endDelimiter = delimiter.right; + } + + if (index === -1) { + return; + } + + const f = index === 0 || indexSrc.charAt(index - 1) === ' '; + if (f) { + const possibleKatex = indexSrc.substring(index); + + if (possibleKatex.match(ruleReg)) { + return index; + } + } + + indexSrc = indexSrc.substring(index + startDelimiter.length).replace(endDelimiter, ''); + } } -function inlineKatex(options, renderer) { - const ruleReg = inlineRule; +function katexTokenizer(src, tokens, displayMode: boolean) { + let ruleReg = displayMode ? blockRule : inlineRule; + let type = displayMode ? 'blockKatex' : 'inlineKatex'; + + const match = src.match(ruleReg); + + if (match) { + const text = match + .slice(2) + .filter((item) => item) + .find((item) => item.trim()); + + if (displayMode) { + console.log("block matched", match[0]); + } else { + console.log("inline matched", match[0]); + } + + return { + type, + raw: match[0], + text: text, + displayMode, + }; + } +} + + + +function inlineKatex(options) { return { name: 'inlineKatex', level: 'inline', start(src) { - let index; - let indexSrc = src; - - while (indexSrc) { - index = indexSrc.indexOf('$'); - if (index === -1) { - return; - } - const f = index === 0 || indexSrc.charAt(index - 1) === ' '; - if (f) { - const possibleKatex = indexSrc.substring(index); - - if (possibleKatex.match(ruleReg)) { - return index; - } - } - - indexSrc = indexSrc.substring(index + 1).replace(/^\$+/, ''); - } + return katexStart(src, false); }, tokenizer(src, tokens) { - const match = src.match(ruleReg); - - if (match) { - const text = match - .slice(2) - .filter((item) => item) - .find((item) => item.trim()); - - return { - type: 'inlineKatex', - raw: match[0], - text: text - }; - } + return katexTokenizer(src, tokens, false); }, - renderer }; } -function blockKatex(options, renderer) { +function blockKatex(options) { return { name: 'blockKatex', level: 'block', - tokenizer(src, tokens) { - const match = src.match(blockRule); - - if (match) { - const text = match - .slice(2) - .filter((item) => item) - .find((item) => item.trim()); - - return { - type: 'blockKatex', - raw: match[0], - text: text - }; - } + start(src) { + return katexStart(src, true); + }, + tokenizer(src, tokens) { + return katexTokenizer(src, tokens, true); }, - renderer }; } From 377cc427b6701708a38ce5af01508cc649e27ce2 Mon Sep 17 00:00:00 2001 From: Hwang In Tak Date: Tue, 24 Sep 2024 20:40:50 +0900 Subject: [PATCH 02/12] fix: Remove unnecessary logging --- src/lib/utils/marked/katex-extension.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts index 2c5186dac..755519566 100644 --- a/src/lib/utils/marked/katex-extension.ts +++ b/src/lib/utils/marked/katex-extension.ts @@ -115,12 +115,6 @@ function katexTokenizer(src, tokens, displayMode: boolean) { .filter((item) => item) .find((item) => item.trim()); - if (displayMode) { - console.log("block matched", match[0]); - } else { - console.log("inline matched", match[0]); - } - return { type, raw: match[0], From 0bfbace9aa38ec93e99b6d48d14e3f75acaa8428 Mon Sep 17 00:00:00 2001 From: Hwang In Tak Date: Tue, 24 Sep 2024 22:00:01 +0900 Subject: [PATCH 03/12] fix: Simplify regex --- src/lib/utils/index.ts | 18 ------------------ src/lib/utils/marked/katex-extension.ts | 6 ++++-- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index bb553c57e..cea7f6e64 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -8,23 +8,6 @@ import { TTS_RESPONSE_SPLIT } from '$lib/types'; // Helper functions ////////////////////////// -const convertLatexToSingleLine = (content) => { - // Patterns to match multiline LaTeX blocks - const patterns = [ - /(\$\$\s[\s\S]*?\s\$\$)/g, // Match $$ ... $$ - /(\\\[[\s\S]*?\\\])/g, // Match \[ ... \] - /(\\begin\{[a-z]+\}[\s\S]*?\\end\{[a-z]+\})/g // Match \begin{...} ... \end{...} - ]; - - patterns.forEach((pattern) => { - content = content.replace(pattern, (match) => { - return match.replace(/\s*\n\s*/g, ' ').trim(); - }); - }); - - return content; -}; - export const replaceTokens = (content, char, user) => { const charToken = /{{char}}/gi; const userToken = /{{user}}/gi; @@ -68,7 +51,6 @@ export const sanitizeResponseContent = (content: string) => { }; export const processResponseContent = (content: string) => { - content = convertLatexToSingleLine(content); return content.trim(); }; diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts index 755519566..371c4c932 100644 --- a/src/lib/utils/marked/katex-extension.ts +++ b/src/lib/utils/marked/katex-extension.ts @@ -35,11 +35,11 @@ function generateRegexRules(delimiters) { if (!display) { inlinePatterns.push( - `${escapedLeft}((?:\\\\.|[^\\\\\\n])*?(?:\\\\.|[^\\\\\\n${escapedRight}]))${escapedRight}` + `${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}` ); } else { blockPatterns.push( - `${escapedLeft}((?:\\\\.|[^\\\\\\n])*?(?:\\\\.|[^\\\\\\n${escapedRight}]))${escapedRight}` + `${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}` ); } }); @@ -109,6 +109,8 @@ function katexTokenizer(src, tokens, displayMode: boolean) { const match = src.match(ruleReg); + console.log("searching:", src); + if (match) { const text = match .slice(2) From e48d66f918d61440ebd3cff950004709f83a0c3b Mon Sep 17 00:00:00 2001 From: Hwang In Tak Date: Tue, 24 Sep 2024 22:11:05 +0900 Subject: [PATCH 04/12] fix: Remove unnecessary logging --- src/lib/utils/marked/katex-extension.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts index 371c4c932..96ce9e28c 100644 --- a/src/lib/utils/marked/katex-extension.ts +++ b/src/lib/utils/marked/katex-extension.ts @@ -109,8 +109,6 @@ function katexTokenizer(src, tokens, displayMode: boolean) { const match = src.match(ruleReg); - console.log("searching:", src); - if (match) { const text = match .slice(2) From ff651ddc3680e8fcf11e3b5368f4208ce7299df4 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 24 Sep 2024 16:07:49 +0200 Subject: [PATCH 05/12] fix: dev1 --- backend/open_webui/__init__.py | 2 +- package-lock.json | 4 ++-- package.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/__init__.py b/backend/open_webui/__init__.py index 167f0fb60..7f808ae70 100644 --- a/backend/open_webui/__init__.py +++ b/backend/open_webui/__init__.py @@ -54,7 +54,7 @@ def serve( os.environ["LD_LIBRARY_PATH"] = ":".join(LD_LIBRARY_PATH) import open_webui.main # we need set environment variables before importing main - uvicorn.run(open_webui.main.app, host=host, port=port, forwarded_allow_ips="*") + uvicorn.run("open_webui.main:app", host=host, port=port, forwarded_allow_ips="*") @app.command() diff --git a/package-lock.json b/package-lock.json index 2ec000ba2..3d8bb8d14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-webui", - "version": "0.3.26", + "version": "0.3.27.dev1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.3.26", + "version": "0.3.27.dev1", "dependencies": { "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-python": "^6.1.6", diff --git a/package.json b/package.json index 19f941ac5..f278210ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "0.3.26", + "version": "0.3.27.dev1", "private": true, "scripts": { "dev": "npm run pyodide:fetch && vite dev --host", @@ -85,4 +85,4 @@ "node": ">=18.13.0 <=21.x.x", "npm": ">=6.0.0" } -} +} \ No newline at end of file From 3cee50768789ac8f759c7483b0676ed22f61c39e Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 24 Sep 2024 16:19:24 +0200 Subject: [PATCH 06/12] fix: dev2 --- backend/open_webui/__init__.py | 3 ++- backend/open_webui/apps/socket/main.py | 6 +++++- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/__init__.py b/backend/open_webui/__init__.py index 7f808ae70..de34a8bc7 100644 --- a/backend/open_webui/__init__.py +++ b/backend/open_webui/__init__.py @@ -52,9 +52,10 @@ def serve( ) os.environ["USE_CUDA_DOCKER"] = "false" os.environ["LD_LIBRARY_PATH"] = ":".join(LD_LIBRARY_PATH) + import open_webui.main # we need set environment variables before importing main - uvicorn.run("open_webui.main:app", host=host, port=port, forwarded_allow_ips="*") + uvicorn.run(open_webui.main.app, host=host, port=port, forwarded_allow_ips="*") @app.command() diff --git a/backend/open_webui/apps/socket/main.py b/backend/open_webui/apps/socket/main.py index 1c9ff505c..09eb91a6e 100644 --- a/backend/open_webui/apps/socket/main.py +++ b/backend/open_webui/apps/socket/main.py @@ -90,10 +90,14 @@ async def periodic_usage_pool_cleanup(): await asyncio.sleep(TIMEOUT_DURATION) +async def on_startup(): + asyncio.create_task(periodic_usage_pool_cleanup()) + + app = socketio.ASGIApp( sio, socketio_path="/ws/socket.io", - on_startup=asyncio.create_task(periodic_usage_pool_cleanup()), + on_startup=on_startup(), ) diff --git a/package-lock.json b/package-lock.json index 3d8bb8d14..44dc5017b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-webui", - "version": "0.3.27.dev1", + "version": "0.3.27.dev2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.3.27.dev1", + "version": "0.3.27.dev2", "dependencies": { "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-python": "^6.1.6", diff --git a/package.json b/package.json index f278210ed..f7cf4d635 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "0.3.27.dev1", + "version": "0.3.27.dev2", "private": true, "scripts": { "dev": "npm run pyodide:fetch && vite dev --host", From 71743b25fe5cc7a0042116c5a292f989032c3142 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 24 Sep 2024 16:42:42 +0200 Subject: [PATCH 07/12] chore: format --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 44dc5017b..93cdb36c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-webui", - "version": "0.3.27.dev2", + "version": "0.3.27", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.3.27.dev2", + "version": "0.3.27", "dependencies": { "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-python": "^6.1.6", diff --git a/package.json b/package.json index f7cf4d635..acca68ce3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "0.3.27.dev2", + "version": "0.3.27", "private": true, "scripts": { "dev": "npm run pyodide:fetch && vite dev --host", @@ -85,4 +85,4 @@ "node": ">=18.13.0 <=21.x.x", "npm": ">=6.0.0" } -} \ No newline at end of file +} From 3f1255b39e8cef8ee37ab147d107bda67549c27b Mon Sep 17 00:00:00 2001 From: Hwang In Tak Date: Wed, 25 Sep 2024 00:10:49 +0900 Subject: [PATCH 08/12] fix: Change inline and block delimiters --- src/lib/utils/marked/katex-extension.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts index 96ce9e28c..e530ace10 100644 --- a/src/lib/utils/marked/katex-extension.ts +++ b/src/lib/utils/marked/katex-extension.ts @@ -1,14 +1,14 @@ import katex from 'katex'; const DELIMITER_LIST = [ + { left: '$$\n', right: '\n$$', display: true }, + { left: '$$', right: '$$', display: false }, // This should be on top to prevent conflict with $ delimiter { left: '$', right: '$', display: false }, - { left: '$$', right: '$$', display: true }, { left: '\\pu{', right: '}', display: false }, { left: '\\ce{', right: '}', display: false }, { left: '\\(', right: '\\)', display: false }, - { left: '( ', right: ' )', display: false }, - { left: '\\[', right: '\\]', display: true }, - { left: '[ ', right: ' ]', display: true } + { left: '\\[\n', right: '\n\\]', display: true }, + { left: '\\[', right: '\\]', display: false }, ]; // const DELIMITER_LIST = [ @@ -55,8 +55,8 @@ const { inlineRule, blockRule } = generateRegexRules(DELIMITER_LIST); export default function (options = {}) { return { extensions: [ - inlineKatex(options), blockKatex(options), + inlineKatex(options), ] }; } From 30e65b33f6ca25b462d0657171d949286d19c45d Mon Sep 17 00:00:00 2001 From: Hwang In Tak Date: Wed, 25 Sep 2024 00:41:08 +0900 Subject: [PATCH 09/12] fix: Add comments --- src/lib/utils/marked/katex-extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts index e530ace10..6acbcd922 100644 --- a/src/lib/utils/marked/katex-extension.ts +++ b/src/lib/utils/marked/katex-extension.ts @@ -55,7 +55,7 @@ const { inlineRule, blockRule } = generateRegexRules(DELIMITER_LIST); export default function (options = {}) { return { extensions: [ - blockKatex(options), + blockKatex(options), // This should be on top to prevent conflict with inline delimiters. inlineKatex(options), ] }; From e19406cdd7672b5328c489541de5fc9e69243829 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 24 Sep 2024 17:43:43 +0200 Subject: [PATCH 10/12] fix --- backend/open_webui/apps/socket/main.py | 5 ----- backend/open_webui/main.py | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/apps/socket/main.py b/backend/open_webui/apps/socket/main.py index 09eb91a6e..fca268a6b 100644 --- a/backend/open_webui/apps/socket/main.py +++ b/backend/open_webui/apps/socket/main.py @@ -90,14 +90,9 @@ async def periodic_usage_pool_cleanup(): await asyncio.sleep(TIMEOUT_DURATION) -async def on_startup(): - asyncio.create_task(periodic_usage_pool_cleanup()) - - app = socketio.ASGIApp( sio, socketio_path="/ws/socket.io", - on_startup=on_startup(), ) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 2ded77f2f..d190b916e 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -8,6 +8,8 @@ import shutil import sys import time import uuid +import asyncio + from contextlib import asynccontextmanager from typing import Optional @@ -31,7 +33,7 @@ from open_webui.apps.openai.main import ( from open_webui.apps.openai.main import get_all_models as get_openai_models from open_webui.apps.rag.main import app as rag_app from open_webui.apps.rag.utils import get_rag_context, rag_template -from open_webui.apps.socket.main import app as socket_app +from open_webui.apps.socket.main import app as socket_app, periodic_usage_pool_cleanup from open_webui.apps.socket.main import get_event_call, get_event_emitter from open_webui.apps.webui.internal.db import Session from open_webui.apps.webui.main import app as webui_app @@ -184,6 +186,8 @@ https://github.com/open-webui/open-webui @asynccontextmanager async def lifespan(app: FastAPI): run_migrations() + + asyncio.create_task(periodic_usage_pool_cleanup()) yield @@ -851,7 +855,6 @@ async def inspect_websocket(request: Request, call_next): app.mount("/ws", socket_app) - app.mount("/ollama", ollama_app) app.mount("/openai", openai_app) From e703e172e25f642915bc103e25bd2ecf55c38e70 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 24 Sep 2024 18:10:14 +0200 Subject: [PATCH 11/12] chore: format --- src/lib/utils/marked/katex-extension.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts index 6acbcd922..e99fcad31 100644 --- a/src/lib/utils/marked/katex-extension.ts +++ b/src/lib/utils/marked/katex-extension.ts @@ -8,7 +8,7 @@ const DELIMITER_LIST = [ { left: '\\ce{', right: '}', display: false }, { left: '\\(', right: '\\)', display: false }, { left: '\\[\n', right: '\n\\]', display: true }, - { left: '\\[', right: '\\]', display: false }, + { left: '\\[', right: '\\]', display: false } ]; // const DELIMITER_LIST = [ @@ -34,13 +34,9 @@ function generateRegexRules(delimiters) { const escapedRight = escapeRegex(right); if (!display) { - inlinePatterns.push( - `${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}` - ); + inlinePatterns.push(`${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}`); } else { - blockPatterns.push( - `${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}` - ); + blockPatterns.push(`${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}`); } }); @@ -56,7 +52,7 @@ export default function (options = {}) { return { extensions: [ blockKatex(options), // This should be on top to prevent conflict with inline delimiters. - inlineKatex(options), + inlineKatex(options) ] }; } @@ -119,13 +115,11 @@ function katexTokenizer(src, tokens, displayMode: boolean) { type, raw: match[0], text: text, - displayMode, + displayMode }; } } - - function inlineKatex(options) { return { name: 'inlineKatex', @@ -135,7 +129,7 @@ function inlineKatex(options) { }, tokenizer(src, tokens) { return katexTokenizer(src, tokens, false); - }, + } }; } @@ -148,6 +142,6 @@ function blockKatex(options) { }, tokenizer(src, tokens) { return katexTokenizer(src, tokens, true); - }, + } }; } From 7bbc57f22599c47041ecde9340d26a3713ae2267 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 24 Sep 2024 18:12:47 +0200 Subject: [PATCH 12/12] doc: changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb7d12c9e..90608807d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.27] - 2024-09-24 + +### Fixed + +- **🔄 Periodic Cleanup Error Resolved**: Fixed a critical RuntimeError related to the 'periodic_usage_pool_cleanup' coroutine, ensuring smooth and efficient performance post-pip install, correcting a persisting issue from version 0.3.26. +- **📊 Enhanced LaTeX Rendering**: Improved rendering for LaTeX content, enhancing clarity and visual presentation in documents and mathematical models. + ## [0.3.26] - 2024-09-24 ### Fixed