713 Commits

  • fix: chat reopens on an older turn after an out-of-date window saves an edit (#34160)
    A window that had missed a turn, for example a laptop that just woke up and has not reconnected yet, saved its own, older idea of where the chat was when it saved an edited message. The chat then reopened on that older turn, and the next message started a new branch there, so the newest reply moved behind the branch switch and the model no longer saw it. A save now keeps the chat on the newest turn of the branch it is on, the same turn the chat already shows once it has caught up.
    
    Fixes #34126
  • refac: limit the number of search words used for chat search (#33844)
    Chat search now uses at most the first 12 distinct words of the query.
  • refac: limit size of version history diffs (#33865)
    Diffing two versions of a skill, tool, function or prompt now refuses inputs above 5000 lines per version. All four history diff views share the same check.
  • perf: opening a chat no longer loads the whole conversation to mark it read (#33666)
    Every time a chat is opened, the server marks it as read, and to do so it loaded and parsed the full chat, images included, just to save the last-read time. Marking a chat as unread and removing a tag from a chat did the same. These now touch only the few chat fields they change, which makes them 4 to 6 times faster on large chats.
    
    Measured on SQLite, best of 7 runs, sizes are the chat as stored in the database:
    
    | Chat | Action | Before | After |
    | --- | --- | --- | --- |
    | 7 MB, text only | open (mark read) | 19.8 ms | 3.5 ms |
    | 7 MB, text only | mark as unread | 20.7 ms | 3.5 ms |
    | 7 MB, text only | remove tag | 21.2 ms | 3.4 ms |
    | 49 MB, with images | open (mark read) | 122.2 ms | 30.0 ms |
    | 49 MB, with images | mark as unread | 124.2 ms | 29.1 ms |
    | 49 MB, with images | remove tag | 120.5 ms | 28.6 ms |
  • refac: limit nesting depth of model settings (#33759)
    Model meta and params are now rejected when they are nested too deeply.
  • perf: channel message pages load much faster when many messages are replies (#33691)
    Opening a channel looked up every replied-to message one by one, so a page of 50 mostly-reply messages ran well over a hundred database queries. The replied-to messages and their authors are now fetched together, and the response is unchanged. Measured on a local SQLite database with a 50-message page where 46 messages are replies:
    
    | | before | after |
    |---|---|---|
    | queries | 147 | 3 |
    | time (median) | 46 ms | 1.9 ms |
  • perf: channel list loads much faster with many channels (#33690)
    Opening the channel list ran several database queries for every channel, so the load time grew with the number of channels. The last message time, unread count, mute state and direct message participants are now fetched for all channels at once, so the number of queries stays the same no matter how many channels a user has. The response is unchanged.
    
    Measured on SQLite with an admin user in 23 visible channels (50 seeded), 2000 messages per channel:
    
    | | before | after |
    |---|---|---|
    | database queries | 93 | 9 |
    | response time | 255 ms | 47 ms |
  • perf: chat access checks no longer load the whole conversation (#33667)
    Typing indicators, sending a message, shared links, comments and file access all check whether the user may access the chat, and each check loaded the full chat, images included, only to look at who owns it and which folder it is in. On large chats that delayed every other request on the same server process by up to a few hundred milliseconds per check. The check now reads just those few fields, and who gets access is unchanged.
    
    Measured on SQLite, best of 5 runs, sizes are the chat as stored in the database:
    
    | Chat | Checked for | Before | After |
    | --- | --- | --- | --- |
    | 7 MB, text only | the chat owner | 34.6 ms | 0.36 ms |
    | 7 MB, text only | a user with access through a shared folder | 35.1 ms | 2.58 ms |
    | 57 MB, with images | the chat owner | 249.8 ms | 0.72 ms |
    | 57 MB, with images | a user with access through a shared folder | 249.0 ms | 2.80 ms |
  • perf: knowledge base changes slow down as the knowledge base grows (#33335)
    Adding, updating or removing knowledge base files through the API sent back the metadata of every file in the knowledge base, and so did renaming a knowledge base or changing its access. These calls got slower as the knowledge base grew, so bulk ingestion through the API lost speed with every batch. Adding or updating files now returns only those files, while removing a file, renaming or changing access returns an empty files list. API clients that read the files list from these responses now get only the affected files; the full list stays available, paginated, from `GET /api/v1/knowledge/{id}/files`. Timings with a local mock embedding server that answers instantly, median of 5 runs (at 1,000 files the per-file work of a batch still hides the difference):
    
    | Files already in the knowledge base | Call | Before | After | Response size before | Response size after |
    |---|---|---|---|---|---|
    | 1,000 | batch add of 100 files | 519 ms | 528 ms | 437 KB | 39 KB |
    | 10,000 | batch add of 100 files | 679 ms | 476 ms | 4.0 MB | 39 KB |
    | 10,000 | single file add | 184 ms | 36 ms | 4.0 MB | 0.6 KB |
    
    Closes #31397
  • fix: member's chat loses its pin when the shared folder owner moves it (#33342)
    When the owner of a shared folder moved a chat a member created to another folder inside the shared folder, the chat lost the member's pin and dropped out of their Pinned list. The owner's move now keeps the member's pin, just as it already keeps the chat's unread status for them. Moving your own chat into a folder still unpins it, as before, so it shows up in that folder.
    
    Fixes #33341
  • perf: kb_exec reads the full text of the whole knowledge base on every call (#33332)
    Every kb_exec command (ls, cat, grep and the rest) and the knowledge base sync check listed the knowledge base by loading each file's full extracted text, even though only names, folders, sizes and hashes were used. On large knowledge bases every call took seconds, held hundreds of MB of memory and blocked the server for other users while it ran. The listing now reads only those fields, and file text is still loaded per file when a command needs it, as before. Output is byte-identical.
    
    5,005 files (5,000 of 50 KB in 50 folders plus 5 of 10 MB, 292 MB of text), median of 3 calls, Postgres 17:
    
    | kb_exec command | before | after | peak memory before | after |
    |---|---|---|---|---|
    | `ls` | 2.34 s | 0.20 s | 874 MB | 243 MB |
    | `ls folder_000/` | 2.15 s | 0.20 s | 873 MB | 243 MB |
    | `cat folder_000/doc_000_001.md` | 1.07 s | 0.17 s | 855 MB | 243 MB |
    | `grep -n needle folder_000/` | 1.24 s | 0.31 s | 871 MB | 243 MB |
    | `grep -c needle large_0.md` | 1.19 s | 0.22 s | 874 MB | 306 MB |
    
    On SQLite the same calls drop from 0.50-1.03 s to 0.16-0.29 s.
    
    Fixes #33322
  • feat: choose the Mistral OCR model and pass extra OCR request options (#33333)
    Mistral OCR always sent the mistral-ocr-latest model with no other request options, so admins could not stay on an older OCR model or send the model name a Mistral-compatible endpoint such as Azure AI Foundry expects. A new Parameters field under Admin Panel > Settings > Documents, shown when Mistral OCR is the extraction engine and also available as the MISTRAL_OCR_PARAMS environment variable, takes a JSON object that is merged into every OCR request: {"model": "mistral-ocr-2512"} replaces the default model, and any other field is sent to the OCR endpoint as given. It works the same with and without the Use Base64 switch, and when it is left empty the request is unchanged.
    
    Closes #19707
  • feat: moving a chat into or out of a folder keeps its last activity date (#33327)
    Filing an old chat into a folder made it look recently active: it jumped to the top of the sidebar under Today, and a batch of old chats sorted into folders ended up ordered by when each was moved. Moving a chat into or out of a folder now keeps its last activity date, so it is sorted and grouped by when it was really last active. Moving your own chat still clears its unread dot as before, and when the owner of a shared folder reorganizes your chat within it, the chat no longer turns unread for you.
    
    Closes #30362