v3.24 - Excel cell styling, current-row formula refs, upload regressions fixed
⚠️ Breaking: B[n] in Excel formulas now means "the row I am on"
Previously B[n] and T1.B[n] resolved to the same cell — the first data row of the
table — which made the short form pointless and, worse, wrong: writing =B[0]*C[0] down
a column produced =B4*C4 in every data row, so running totals, per-row products and
growth rates came out silently identical.
B[n] is now relative to the row the formula sits on: B[0] is the current row, B[-1]
the row above, B[1] the row below. T1.B[n] remains the way to point at one fixed cell.
If you have prompts or saved markdown that relied on the old behaviour, replace B[n]
with T1.B[n]. The tool description already documented B[0] as a current-row
reference, so most callers were already writing what they meant.
🩹 Regressions from v3.23, fixed
Template-generated files get their UUID prefix back
Documents produced by the YAML template tools stopped receiving the 8-character UUID
filename prefix on every storage backend, so template output landed under bare,
collision-prone object keys. Files created by the static create_* tools were unaffected.
Both dynamic call sites read the flag as .get("add_unique_prefix", False). Since the
payload is built solely from a template's declared arguments, any template that did not
declare that argument produced an explicit False, which skipped the strategy-based
default resolution entirely. Templates now inherit the same default as every other tool,
and a template that declares add_unique_prefix still controls it outright.
Uploads no longer freeze the event loop
The five static tools used to hand both the document build and the upload to the
background thread pool. Splitting the build out for LibreChat left the upload running
inline on the asyncio event loop — for S3/GCS/Azure that is a network round-trip plus a
signed-URL call, during which no other request is served, health probes included.
Measured with a 500 ms upload: the loop stalled for 517 ms before, 24 ms after.
Anyone running on Kubernetes with RUN_BLOCKING_BY_ASYNCIO_THREAD_ENABLED=true was
exposed to probe timeouts and the pod restarts that follow.
httpx no longer imported on every start
The LibreChat backend was imported at module scope, so every deployment pulled in httpx
regardless of UPLOAD_STRATEGY — and it was not declared in requirements.txt, resolving
only because fastmcp happens to depend on it. It is now imported lazily, like every other
optional backend, and declared explicitly.
✨ New: Excel cell styling
Colour a cell, or apply a named style defined in your own template, with a directive above
the table:
<!-- styles: A[-1]:D[-1]=bg:darkblue;color:white;bold, D[3]=bg:yellow -->
Comma-separated <target>=<attributes> entries, attributes separated by ;. Targets can
be an absolute cell (B2), a table-relative cell (B[0] is the first data row, B[-1]
the header), or a rectangular range of either form. Attributes are bg:, color:, bold,
italic, underline and style:; colours are 6-digit hex or one of 18 names.
Styling runs as a final pass over the finished table, so an explicit instruction beats the
built-in header fill, the formula fill and inline **bold**. It only ever sets colours and
font flags — values, formulas and number formats are left alone.
📈 Excel correctness & robustness
- Number formats follow the source value instead of flattening it.
50.5%now displays as
50.5%, not51%;1500.75keeps its decimals instead of showing1,501. Grouping
applies to negatives too, and formats are still only imposed from 1000 up so small values
keep full visible precision. - Cross-sheet ranges emit the sheet prefix on the first endpoint only.
- Generated table-header names are de-duplicated.
- Workbooks that could not be opened are handled rather than producing a corrupt file, and
the build path is unified so both entry points share one save routine. - Circular-reference detection and typed-column handling corrected.
📄 Documentation
- The health-probe section documented
GET /healthandGET /readiness, neither of which
exists. The real routes are/healthz(startup),/readyz(readiness) and/livez
(liveness), and it is now recorded that they deliberately bypass the API-key middleware
so orchestrators can poll them without credentials. add_unique_prefixis documented as what it actually is: unset follows the backend —
truefor LOCAL/S3/GCS/AZURE/MINIO,falsefor LibreChat.
LibreChat: still not usable
Unchanged from v3.23 — UPLOAD_STRATEGY=LIBRECHAT still depends on a /api/service/files
endpoint that LibreChat has not shipped. Additionally, note that the YAML template tools
fail outright under LIBRECHAT: they call the synchronous upload path, which rejects that
strategy. That will be addressed alongside the rest of the integration. Keep using
LOCAL / S3 / GCS / AZURE / MINIO.