Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Optional flags when starting the server:
- `historypath=/tmp/mini-a-history` / `historyretention=600` / `historykeep=true` to manage history storage (see comments in `mini-a-web.yaml`)
- `historys3bucket=my-bucket historys3prefix=sessions/` to mirror history JSON files to S3 (supports `historys3url`, `historys3accesskey`, `historys3secret`, `historys3region`, `historys3useversion1`, `historys3ignorecertcheck`). History is uploaded at optimized checkpoints: immediately after user prompts and when final answers are provided, rather than on every interaction event
- `useattach=true` to enable the file attachment button in the browser UI (disabled by default)
- `usekatex=true` (or `usemath=true`) to render LaTeX math expressions in the browser UI using KaTeX

Endpoints used by the UI (served by `mini-a-web.yaml`): `/prompt`, `/result`, `/clear`, and `/ping`.

Expand Down
20 changes: 19 additions & 1 deletion mini-a-web.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ help:
example : "true"
mandatory: false
options : ["true", "false"]
- name : usekatex
desc : "Boolean to enable/disable KaTeX math rendering in the browser UI (default: false)"
example : "true"
mandatory: false
options : ["true", "false"]
- name : usemath
desc : "Alias for usekatex (KaTeX math rendering in the browser UI)"
example : "true"
mandatory: false
options : ["true", "false"]
- name : historys3bucket
desc : "S3 bucket to store conversation history objects"
example : "mini-a-history"
Expand Down Expand Up @@ -168,7 +178,7 @@ todo:
((uri )): /info
((execURI )): | #js
try {
var _res = { status: "ok", usehistory: global.__usehistory, useattach: global.__useattach, historyretention: args.historyretention, showexecs: global.__showexecs, usestream: global.__usestream }
var _res = { status: "ok", usehistory: global.__usehistory, useattach: global.__useattach, usekatex: global.__usekatex, historyretention: args.historyretention, showexecs: global.__showexecs, usestream: global.__usestream }
return ow.server.httpd.reply(_res)
} catch(e) {
printErr(e)
Expand Down Expand Up @@ -808,6 +818,8 @@ jobs:
usediagrams : toBoolean().isBoolean().default(false)
usecharts : toBoolean().isBoolean().default(false)
useascii : toBoolean().isBoolean().default(false)
usekatex : toBoolean().isBoolean().default(false)
usemath : toBoolean().isBoolean().default(__)
historys3bucket : isString().default(__)
historys3prefix : isString().default("")
historys3url : isString().default("https://s3.amazonaws.com")
Expand All @@ -823,6 +835,10 @@ jobs:
format : isString().default(__)
auditch : isString().default(__)
exec : | #js
if (isDef(args.usemath)) {
if (isUnDef(args.usekatex)) args.usekatex = args.usemath
delete args.usemath
}
global.__res = {}
global.__usehistory = args.usehistory
global.__historypath = args.historypath
Expand All @@ -833,6 +849,7 @@ jobs:
global.__usestream = args.usestream
global.__logpromptheaders = args.logpromptheaders.split(",").map(h => h.trim().toLowerCase()).filter(h => h.length > 0)
global.__useattach = args.useattach
global.__usekatex = args.usekatex
global.__planState = {}
global.__sseQueues = {}
if (isUnDef(global.__webMetrics)) {
Expand Down Expand Up @@ -881,6 +898,7 @@ jobs:
}

global.maArgs = merge(args, {})
if (isDef(global.maArgs.usekatex)) delete global.maArgs.usekatex
if (!toBoolean(args.mcplazy)) {
var _ma = new MiniA()
_ma.init( merge( global.maArgs, { mcpdynamic: false } ) )
Expand Down
95 changes: 93 additions & 2 deletions public/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1528,7 +1528,7 @@

<script>
/* ========== CONSTANTS & INITIALIZATION ========== */
const converter = new showdown.Converter({
const MARKDOWN_CONVERTER_OPTIONS = {
"customizedHeaderId" : true,
"parseImgDimensions" : true,
"simplifiedAutoLink" : true,
Expand All @@ -1543,7 +1543,93 @@
"simpleLineBreaks" : true,
"ghCompatibleHeaderId" : true,
"disableForced4SpacesIndentedSublists": true
});
};
const KATEX_CSS_HREF = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css';
const KATEX_EXTENSION_SRC = 'https://cdn.jsdelivr.net/npm/showdown-katex@latest/dist/showdown-katex.min.js';
let converter = null;
let katexEnabled = false;
let katexAssetsReady = false;
let katexAssetsPromise = null;

function buildMarkdownConverter(useKatex) {
const options = Object.assign({}, MARKDOWN_CONVERTER_OPTIONS);
if (useKatex && typeof window.showdownKatex === 'function') {
try {
options.extensions = [window.showdownKatex({ throwOnError: false })];
} catch (error) {
console.warn('KaTeX extension failed to initialize:', error);
}
}
return new showdown.Converter(options);
}

function setMarkdownConverter(useKatex) {
converter = buildMarkdownConverter(useKatex);
katexEnabled = useKatex === true;
}

function ensureStylesheet(href, dataAttr) {
const selector = dataAttr ? `link[${dataAttr}]` : `link[href="${href}"]`;
const existing = document.querySelector(selector);
if (existing) return;
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.crossOrigin = 'anonymous';
if (dataAttr) link.setAttribute(dataAttr, 'true');
document.head.appendChild(link);
}

function loadScriptOnce(src, dataAttr) {
const selector = dataAttr ? `script[${dataAttr}]` : `script[src="${src}"]`;
const existing = document.querySelector(selector);
if (existing) return Promise.resolve();
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
if (dataAttr) script.setAttribute(dataAttr, 'true');
script.onload = resolve;
script.onerror = () => reject(new Error('Failed to load ' + src));
document.head.appendChild(script);
});
}

async function ensureKatexAssets() {
if (katexAssetsReady) return;
if (katexAssetsPromise) {
await katexAssetsPromise;
return;
}
katexAssetsPromise = Promise.resolve()
.then(() => {
ensureStylesheet(KATEX_CSS_HREF, 'data-katex-style');
return loadScriptOnce(KATEX_EXTENSION_SRC, 'data-katex-extension');
})
.then(() => {
katexAssetsReady = true;
});
await katexAssetsPromise;
}

async function configureKatexAvailability(enabled) {
if (!enabled) {
if (katexEnabled) setMarkdownConverter(false);
return;
}
try {
await ensureKatexAssets();
if (typeof window.showdownKatex !== 'function') {
throw new Error('showdown-katex extension not available');
}
setMarkdownConverter(true);
} catch (error) {
console.warn('KaTeX rendering disabled:', error);
setMarkdownConverter(false);
}
}

setMarkdownConverter(false);

const PREVIEW_ID = 'anticipationPreview';
const PING_INTERVAL_MS = 60 * 1000; // 60 seconds
Expand Down Expand Up @@ -3664,6 +3750,7 @@
let shouldEnableHistory = true;
let shouldEnableAttachments = false;
let shouldEnableStream = false;
let shouldEnableKatex = false;

try {
const response = await fetch('/info', {
Expand All @@ -3684,13 +3771,17 @@
if (typeof data.usestream === 'boolean') {
shouldEnableStream = data.usestream;
}
if (typeof data.usekatex === 'boolean') {
shouldEnableKatex = data.usekatex;
}
} catch (error) {
console.error('Unable to determine feature availability:', error);
}

applyHistoryAvailability(shouldEnableHistory);
applyAttachmentAvailability(shouldEnableAttachments);
streamEnabled = shouldEnableStream;
await configureKatexAvailability(shouldEnableKatex);

if (historyEnabled) {
refreshHistoryPanel();
Expand Down