Commit Graph
18585 Commits
Author SHA1 Message Date
G30 d16fad585d fix: keep a shortcut recording from firing the action already bound to the chord (#30160) 2026-09-19 10:00:18 -05:00
G30 2af297bfcd fix: keep the pinned flag when importing a chat export (#30151) 2026-09-19 09:59:55 -05:00
G30 36276c9ead fix: keep a skill's translations and disabled state when saving it from the editor (#30185) 2026-09-19 09:59:36 -05:00
Classic298 1f8f1f61bb fix: tell ask_user models that the first option is shown as Recommended (#30196)
The ask_user card badges the first option of every question as "Recommended", but nothing ever told the model that. The model picks whatever order it likes, so the badge really means "listed first" and users act on a recommendation the model never made.

The ask_user tool description now states that the first option is labelled Recommended and that the model should list the option it recommends first, so the badge reflects an actual choice.

Kept the badge and instructed the model instead of adding a per-option "recommended" flag: the flag would need a schema change, validation in the request normalizer and a frontend change, for the same result in the common case. Dropping the badge was the other option, but it removes a useful affordance rather than fixing it.

Verified that the added line reaches the model by running the docstring through the tool-spec builder and checking the generated OpenAI function schema.

Fixes #30195
2026-09-19 09:59:18 -05:00
G30 5d7c17f41a fix: stop the Generate Message Pair shortcut from also submitting the message input form (#30167) 2026-09-18 23:54:10 -04:00
Classic298 3d29548716 fix: pgvector reads leak their connection and lose most of their neighbours (#30142)
Two defects on the pgvector read path. A search, query or get that finds nothing returns before the rollback that ends its read-only transaction, so the session keeps the connection it checked out; retrieval fans out over worker threads and the session is thread-local, so every thread that runs an empty read holds a connection for the lifetime of that thread. Users see vector search die after a while with "QueuePool limit of size 5 overflow 10 reached" and no way back other than a restart. Rolling back before the three early returns puts the connection back: measured on PostgreSQL 16, twelve empty reads on a default-shaped pool left all 12 connections checked out before and 0 after.

All collections also share one table under one vector index, and the WHERE collection_name filter is applied after the index walk, so a knowledge base holding a small share of the rows keeps only a small share of its neighbours, silently. pgvector 0.8 added iterative scans for this: the scan keeps going until enough rows pass the filter. This sets it per search so it cannot leak into other sessions, and only when the installed extension supports it, since setting it on pgvector 0.7 would make every search raise. PGVECTOR_ITERATIVE_SCAN turns it off or picks strict_order; it defaults to relaxed_order because the shipped behaviour is silently wrong results, and the cost is about a millisecond per search.

Measured through PgvectorClient.search on PostgreSQL 17 and pgvector 0.8, 101500 rows of 384 dimensions with the knowledge base at 1.5% of the table, hnsw m=16, recall@10 against an exact scan over 40 queries: 0.070 at 4.9 ms before, 0.970 at 7.2 ms after.

Fixes #30133
Fixes #30135
2026-09-18 19:31:49 -04:00
Classic298 52cd298411 fix: allow forking a chat that holds a stale unfinished message (#30131)
Forking returned 409 "Wait for the current response to finish before forking." forever once any assistant message anywhere in the chat was left at done = false, with nothing generating and even when that message sat on a branch that was not being forked. Interrupted turns leave the flag behind and nothing clears it, so an affected chat could never be forked again.

The endpoint now refuses only while a task is actually running, which is the check /compact has always relied on by itself. A message still unfinished on the forked branch is marked done in the copy so the fork does not open showing a spinner, while a turn paused waiting on tool approval keeps its unfinished state and its pending call so the fork still shows the prompt. The source chat is left untouched either way.

The client-side check had the same shape of bug: it read history.currentId instead of the message actually being forked, so a paused last turn also blocked forking earlier, finished messages. It now reads the message it is about to fork.

Fixes #30128
2026-09-18 19:31:20 -04:00
Classic298 fffcb727d2 fix: release the openGauss connection when a read returns no rows (#30144)
An openGauss query or get that finds nothing returns before the rollback that ends its read-only transaction, so the session keeps the connection it checked out of the pool. Retrieval fans out over worker threads and the session is thread-local, so every thread that runs an empty read holds a connection for the lifetime of that thread, and vector search eventually fails with a pool checkout timeout that a restart is the only way out of. Empty reads are routine: an empty knowledge base, a file whose chunks were deleted, or a metadata filter that matches nothing all produce one.

Rolling back before the two early returns puts the connection back. Measured by driving OpenGaussClient itself with a pool of 5: three empty query reads left 3 connections checked out and 0 free before the change, and 0 checked out with 3 free after, same for get. search is already correct, it has no early return.

The pgvector client had the same defect, fixed separately in #30142.
2026-09-18 19:31:01 -04:00
Classic298 6d73780fd1 fix: bundle seaborn and give black its dependencies in the Pyodide lock (#30148)
seaborn was listed among the Pyodide distribution packages, but it is not part of that distribution, so the build wrote no wheel for it. Since seaborn is on the code interpreter's package list, it was quietly downloaded from pypi.org in the user's browser instead, which is what bundling the wheels is meant to avoid. It now takes the PyPI wheel path, like openpyxl.

The code editor's Format button was broken for non-admin users, with or without internet, because black's bundled lock entry declared no dependencies and the hand-written list next to the caller was missing packaging. black's dependencies are now declared in the lock, so that list goes back to black alone and every future caller gets them too.

Both of these shipped unnoticed because nothing checked that a listed package produced a wheel. The build now fails when one did not, naming it.

Verified offline with the network cut at undici's dispatcher: seaborn, black, the Excel roundtrip and the other listed packages all install from the bundled wheels, and a deleted wheel, a missing lock entry or a non-canonical key each fail the build.

Fixes #30145
2026-09-18 19:30:54 -04:00
G30 677341578d fix: stop the model editor's Builtin Tools section from reading its labels before they exist (#30153) 2026-09-18 19:29:41 -04:00
G30 4d01f1ebed fix: keep the archived state and chat variables when importing a chat export (#30155) 2026-09-18 19:29:30 -04:00
Timothy Jaeryang Baek 64bbdf7a73 refac 2026-09-18 19:28:40 -04:00
joaoback f3cf833e54 i18n: add pt-BR translations for newly added UI items and consistency pass (#30158)
New **pt-BR** translations for items introduced in the latest releases, plus a consistency/quality pass across existing strings (grammar, tone, capitalization, pluralization). Placeholders and hotkeys preserved. No logic changes.
2026-09-18 19:18:45 -04:00
Classic298 d5cacb3c0a fix: convert forced tool_choice and non-streaming tool calls for Responses API connections (#30095)
On a connection with API type "responses", a forced tool choice sent in
the Chat Completions shape was forwarded to the provider unchanged, so
providers that validate the Responses API schema rejected the request.
A non-streaming reply that carried a function call was also flattened
to empty text with finish_reason "stop", so API clients never saw the
tool call.

The request converter now flattens a forced function choice to the
Responses API shape, and the result converter turns every function_call
output item into a Chat Completions tool_calls entry, mirroring the
existing Responses output mapping in utils/misc.py.

Fixes #30085
2026-09-18 19:18:26 -04:00
Timothy Jaeryang Baek c82634b9d0 refac 2026-09-18 11:49:56 -04:00
Timothy Jaeryang Baek b513a6ad3b refac 2026-09-18 11:40:44 -04:00
Classic298 9ee810ad09 feat: add Staan as a native web search provider (#30138)
European deployments that need EU data residency have no hosted web search option out of the box: every plug-and-play provider shipped today is US-based, and the only sovereign alternative is self-hosting SearXNG, which means running and maintaining that infrastructure yourself. Staan (staan.ai) is a European search API with EU data residency, so adding it gives those deployments a drop-in choice.

It is configured like any other provider, through the admin UI or STAAN_API_KEY, STAAN_MARKET and STAAN_MAX_SNIPPETS. Market sets the region and language of the results and defaults to en-us. Max snippets asks Staan to fetch each result page and return semantically scored chunks of it, which get merged into that result's snippet so retrieval has more to work with; leaving it at 0 uses the plain search endpoint.

Wired the same way as Tavily and Exa, with domain filtering going through the shared get_filtered_results.

Requested in #26006.
2026-09-18 10:38:36 -05:00
Classic298 655337f7bf fix: vendor openpyxl so Excel I/O works in the Pyodide code interpreter (#30140)
openpyxl has been listed as a Pyodide package since March, but it is not part of the Pyodide distribution, so the build never wrote a wheel for it and never warned about it. In the browser code interpreter every Excel operation failed with ModuleNotFoundError: import openpyxl, pd.read_excel() and DataFrame.to_excel() alike.

It now takes the PyPI wheel path together with its dependency et-xmlfile, and the code interpreter installs it when a snippet imports openpyxl or calls pandas' Excel helpers, which need it without importing it by name.

Injected lock entries are now keyed by the canonical dashed package name, the only spelling pyodide resolves them by. As a side effect black's mypy-extensions now resolves from the bundled wheel too, so formatting Python in the editor no longer reaches out to PyPI at runtime.

Verified offline against a built static/pyodide with every network call blocked: a to_excel then read_excel roundtrip loads openpyxl and et-xmlfile from the local directory and returns the frame.

Fixes #30130
2026-09-18 10:38:02 -05:00
Classic298 95406fd28d fix: build the pgvector ivfflat index once there are rows to cluster on (#30143)
ivfflat places its centroids by clustering the rows it can see when the index is built, so an index built on an empty table gets centroids that mean nothing, and everything inserted afterwards is filed against them. On a fresh install the index is created immediately after the table, before a single chunk exists, and it is never rebuilt, so that install keeps a permanently untrained index and quietly retrieves the wrong chunks. An install that upgraded into the version introducing the index is unaffected, its table already had rows.

The index is now created once the table holds 50 rows per list, the sample size ivfflat itself aims for. Below that, and until the next start, searches fall back to an exact scan, which is correct and costs about a millisecond at that size. hnsw is untouched, it builds its graph as rows are inserted and has nothing to train on. One trade-off: the build moves from the first start to that later one, so an instance that has grown large in between pays a one-time index build during startup.

Measured on PostgreSQL 17 and pgvector 0.8 with the default lists=100 and probes=1, 384 dimensions, recall@10 against an exact scan, varying only how many rows existed when the index was built:

| rows at build | 200 | 1000 | 2500 | 5000 | 20000 |
|---|---|---|---|---|---|
| recall@10 | 0.180 | 0.563 | 0.967 | 1.000 | 1.000 |

Through PgvectorClient.search on a 20000-row table, an index built as it is today scores 0.480 against 1.000 built after the rows arrive, at the same 5 ms. An existing install can repair its index with REINDEX INDEX idx_document_chunk_vector, measured to take it from 0.480 back to 1.000.

Fixes #30134
2026-09-18 10:37:25 -05:00
Timothy Jaeryang Baek 1ddba7e2c6 refac 2026-09-18 11:31:59 -04:00
Timothy Jaeryang Baek ca1eefe293 refac 2026-09-18 11:30:15 -04:00
Timothy Jaeryang Baek 9d98ffcddf refac 2026-09-18 10:55:55 -04:00
Timothy Jaeryang Baek 7cbaabe02f refac 2026-09-18 10:51:00 -04:00
Timothy Jaeryang Baek ad9da98168 refac 2026-09-17 20:11:05 -04:00
Timothy Jaeryang Baek dbb17a5725 refac 2026-09-17 19:49:35 -04:00
Classic298 9923c53c10 fix: treat SVG uploads as documents instead of vision images (#30102)
Uploading an .svg to a chat attached it as a vision image input, so the model received a data URI it could not decode. PIL-backed servers answered "cannot identify image file" and OpenAI answered "The image data you provided does not represent a valid image". No setting made it work.

SVG now takes the ordinary file upload path, so its XML source is extracted and indexed and the model can answer questions about it. Rasterizing was the alternative and it would have discarded the part of an SVG a model reads best, the source itself. Raster formats are untouched and still go up as image inputs.

A shared helper replaces the ad hoc image/ prefix checks at the points that decide image input versus document, on both ends. It normalises the content type first, because a stored "image/SVG+xml" or a trailing charset parameter slipped past a plain comparison.

One behaviour change worth knowing: an SVG now needs the model to have the file upload capability, where before it rode in as an image.

Fixes #30100
2026-09-17 17:40:07 -04:00
Classic298 d08b69025d issue-template: require reproducing on latest AND dev right before submitting (#30104)
Reports for bugs already fixed on dev, sometimes weeks earlier, are common
because reporters check an old version once and never retest. Merges the
two soft checkboxes into one strict, time-bound requirement and warns in
the template body that unreproduced reports get closed without discussion.
2026-09-17 17:39:54 -04:00
Classic298 460f2e7634 fix: surface initial chat title generation failures in the log (#30106)
When title generation for a brand new chat fails, the chat silently keeps the provisional title (the full first user message) and nothing is written to the log at the default level, so there is no way to tell that the feature is broken rather than disabled. The failure was caught by a broad `except Exception` and reported with `log.debug`, which is invisible unless GLOBAL_LOG_LEVEL is set to DEBUG. Issue #29533 describes an outage of this path that survived four releases for exactly that reason.

This logs it with `log.exception` instead, matching how the rest of main.py reports background task failures, so an admin sees one ERROR line plus the traceback naming the real cause.

Nothing else changes: the success path is untouched, the exception is still swallowed so the detached task cannot take anything down with it, and `background_tasks_handler` does not log this exception itself, so there is no duplicate traceback.

Fixes #29533
2026-09-17 17:39:43 -04:00
Classic298 0837f310be fix: reject Docling conversions that failed inside an HTTP 200 response (#30107)
Uploading a file that Docling declines or fails to convert either dies with `TypeError: argument of type 'NoneType' is not iterable`, or silently succeeds and stores the literal string `<No text content found>` as the document's text, which then gets indexed and handed to the model as if it were the file. Docling returns the conversion outcome inside the HTTP 200 body, so checking only the HTTP status made a refused conversion look identical to a successful one, and the `errors` array that says why in plain words was never read.

Failed and skipped conversions are now rejected with the messages Docling returned, so an unsupported format surfaces as "File format not allowed: example.dxf" and the traceback is gone. The markdown field is also read as nullable, because Docling returns JSON `null` for every content format it was not asked to produce, which any Docling Parameters setting `to_formats` without `md` will hit, and that null was what raised the TypeError.

Successful conversions with empty markdown keep the existing `<No text content found>` placeholder, matching what TikaLoader and the Mistral loader already do in the same package.

Fixes #29808
2026-09-17 17:39:35 -04:00
Timothy Jaeryang Baek 58b36765a7 refac 2026-09-16 22:47:37 -04:00
Classic298 fbc4897269 refac: resolve knowledge file access from the file association (#29937)
`has_access_to_file` now derives knowledge-base access purely from the knowledge-file association, instead of also consulting the `collection_name` value stored on the file record.
2026-09-16 15:59:25 -04:00
Classic298 ff7f35a30d fix: stop logging a traceback twice per failed vector search (#29981)
A vector DB outage renders a full traceback twice for every collection and query pair. query_doc and query_doc_with_hybrid_search each log and then re-raise, and the fan-out handler that catches them logs the same exception again, so twenty knowledge bases and five expanded queries turn one outage into hundreds of identical stack traces per message on every replica.

Both helpers lose the try/except that only logged before re-raising. Each fan-out keeps reporting failures through its return value and now carries the collection name back, so one record after the gather names every collection that failed and attaches a single traceback. The hybrid path logs before its existing raise, so a total failure is still reported before the caller falls back to vector search.

Measured with every collection failing, the vector fan-out drops from 1000 traceback records for fifty collections across ten queries to one, and the hybrid fan-out from 100 for ten collections across five queries to one. Results are byte-identical across eighteen and twenty-six scenarios covering healthy, failing, partial and degenerate inputs.

Three limits worth stating. Nine of the fifteen vector clients swallow the error inside search() and return None, chroma and pgvector among them, so those backends see neither the old tracebacks nor the new record. get_sources_from_items queries one attached item at a time, so a chat with twenty knowledge bases still logs twenty records. And with hybrid search on a total outage logs once from each fan-out as the caller falls back, down from two per pair plus one.

The old "All collection queries failed" warning goes with this. The replacement cannot express that case, because a collection can fail one query and succeed another, so naming every failed collection no longer implies an empty payload.
2026-09-16 15:59:05 -04:00
Classic298 556e48e058 fix: honor false defaults on checkbox prompt variables (#30037)
A prompt template with `{{flag | checkbox:default=false}}` opened the input variables form with the checkbox already ticked, and the same happened for `False`, `"false"`, `0` and `"0"`. Template defaults arrive as strings and the checkbox was bound straight to that value, so any non-empty string counted as checked and there was no way to define a checkbox that starts unchecked once a default was given.

The checkbox now reads its ticked state from the value explicitly: it is checked only for `true` (boolean or string) and stays unchecked for anything else, while clicking it still writes a boolean like before. The seeded value itself is left alone, so what gets substituted into the prompt for an untouched form is unchanged, and typing `true` or `false` into the text field next to the checkbox now moves the tick accordingly.

Fixes #30036
2026-09-16 15:44:29 -04:00
Classic298 48fb2b84ba refac: derive a forked chat's folder from the caller's write access (#30069)
A fork copied the source chat's folder id unchanged. It now keeps that folder only when the caller has write access to it, matching what chat creation and chat moves already do, and is created outside any folder otherwise.
2026-09-16 15:42:03 -04:00
Classic298 befdd86eb1 refac: share one guarded helper for chat branch descent (#30070)
The branch navigation handlers each carried their own copy of the loop that walks a message's childrenIds down to the deepest child, eleven copies in total across the single-response view, the multi-response view and Chat.svelte. They now call one getDeepestChildId helper in the frontend utils, which tracks the ids it has already visited and stops when one repeats, the same way the message list build already does.

The helper also absorbs the "start from the last root message" fallback that two of the call sites repeated inline, so every site is now a single call.
2026-09-16 15:41:56 -04:00
Timothy Jaeryang Baek d9c8de9c39 refac 2026-09-16 10:38:46 -04:00
Timothy Jaeryang Baek fd4fc80536 refac 2026-09-16 10:27:38 -04:00
qwist1233-cpuandqwist1233-cpu a0c8593c92 i18n(tr-TR): translate 1056 missing Turkish strings (#30034)
Co-authored-by: qwist1233-cpu <267938209+qwist1233-cpu@users.noreply.github.com>
2026-09-16 10:26:16 -04:00
tsumon e669f8aefe i18n: improve Simplified Chinese translations (#30043) 2026-09-16 10:19:44 -04:00
Classic298 3348f68778 fix: keep attachment-only messages intact when skills are bound (#30045)
Sending an image or file with no typed text to a model with skills
bound replaced the (empty) message text with the list of skill names,
so the model answered with its skill catalog instead of handling the
attachment. The empty-message guard added for #24929 fired on any
empty last user text without checking for attachments.

The guard now skips the fallback when the current message carries
files, read from the user_message object the frontend sends with the
request. Attachment-only messages then go out exactly as they do on
models without skills. A bare skill selection with no attachment still
gets the fallback, so the provider 400 from #24929 stays fixed.

Request-level files were not usable as the signal: that list carries
the model's knowledge and folder files, so a bare skill selection on a
knowledge model would have gone out empty again.

Fixes #30040
2026-09-16 10:19:28 -04:00
Classic298 82da9093aa fix: default converted Responses API function tools to strict=false (#30046)
Fixes #27750

When a model runs on the Responses API, every Chat Completions style tool (workspace tools, MCP and OpenAPI servers) is converted to the Responses tool shape. Only an explicit strict value was carried over, so tools without one were sent with strict omitted. Chat Completions defaults strict to false, the Responses API defaults it to true, so the conversion silently switched loose schemas into strict mode. The model then filled every optional property (empty strings, zeros, false, empty arrays) and combined mutually exclusive filters, and those arguments went to the tool unchanged. Search and filter tools received meaningful values where omission was intended and rejected the call or returned wrong results.

The converter now defaults strict to false when the source tool does not set it. Explicit true or false values are still preserved and already native Responses tools are passed through untouched, matching what the same tool definition gets on Chat Completions.
2026-09-16 10:19:08 -04:00
Classic298 4611394fa6 refac: correct the ENABLE_CHAT_RESPONSE_STREAM_INPLACE_APPEND comments (#30066)
The comments on the opt-in in-place append made it sound like the fast path
can lose streamed text under normal operation. The only way the field can be
emptied is an allocation failure, which means the host is already out of
memory, and the default path (which copies the whole accumulated string on
every chunk) raises in that situation as well. Reword both comments to state
that condition so the flag is not read as unsafe.
2026-09-16 10:08:46 -04:00
Timothy Jaeryang Baek 3394a10b76 refac 2026-09-16 08:51:02 -04:00
Timothy Jaeryang Baek 88e78b7819 refac 2026-09-16 08:35:01 -04:00
Timothy Jaeryang Baek e9a0164690 refac 2026-09-16 00:34:24 -04:00
Timothy Jaeryang Baek 6d8e63e366 refac 2026-09-16 00:02:48 -04:00
Timothy Jaeryang Baek dfde08aa73 refac 2026-09-15 23:53:41 -04:00
Classic298 3d6598fccb fix: steer models to replace_range in the replace_note_content tool description (#30048)
Asked to add a section or change a few lines, models answer with a whole-note
replace_note_content call and the rest of the note is gone, with nothing to
undo because the editor only records versions on chat inserts. The tool
already supports replace_range operations with an expected guard, but its
description never said when to use them or how the offsets work, so models
defaulted to sending the whole note back.

The docstring, which is the description every model receives for this tool
in note chats and normal chats alike, now states the preference for range
edits and the offset, overlap and expected rules the handler enforces.
Verified the text lands in the generated tool spec unchanged and that range
edits, the expected mismatch rejection and whole-note replace behave as
described against a sqlite data dir.
2026-09-15 22:51:49 -04:00
Classic298 06d9d2e7c7 fix: sync Playwright loader spins a CPU core when a page opens a WebSocket (#30050)
Fetching a single URL through the Playwright loader (fetch_url tool, a URL attached to a
chat, the process/web endpoint) never returned when the page opened a WebSocket. The worker
thread stayed at 100% CPU for the life of the process, and every further hit cost another
core, so the whole instance got slow. Web search was unaffected, it uses the async loader.

The sync loader's websocket route handler called the synchronous close(). Playwright runs
websocket route handlers directly on its dispatcher fiber, so that call waited on the very
loop it was blocking and busy-spun forever. The handler is now a no-op: a routed socket only
reaches the network when the handler asks for it, so the page still cannot dial out, and
nothing in the handler waits on the dispatcher any more.

Aborting the upgrade request from the HTTP route handler instead does not work, page.route
never sees WebSocket handshakes and the connection goes through.

Fixes #30024
2026-09-15 22:51:37 -04:00
Timothy Jaeryang Baek 1cdd7aa459 refac 2026-09-15 22:51:15 -04:00