* feat: add bulk extension field updates with UNSET_VALUE sentinel for key deletion
- Add `UNSET_VALUE` sentinel constant to signal complete field removal from character cards
- Add `writeExtensionFieldBulk()` function to update extension fields across multiple characters in a single API call
- Add `deleteValueByPath()` utility function to remove nested object keys by dot-path
- Update `writeExtensionField()` to support `UNSET_VALUE` for deleting extension keys
- Extend `/api/characters/merge-attributes
* Revert package-lock.json changes
* Allow null values in merge-attributes filter path validation
Change filter.path existence check to only skip on undefined, not null. This allows merging attributes when the existing value is explicitly null, treating null as a valid value rather than absence of a value.
* fix: share forbiddenRegExp between modules
* feat: add writeExtensionFieldBulk and UNSET_VALUE constant to getContext
* Update src/endpoints/characters.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: validate for .png extension
* Update public/scripts/extensions.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* refactor: extract shouldSkip logic as a function param to avoid double parsing
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: extensions OpenRouter model lists
* fix: update JSDoc for optional mapping function parameter in fetchModelsByModality
* fix: update JSDoc to clarify return type of fetchModelsByModality function
* fix: encode output modality in fetchModelsByModality API request
* fix: include model field in sd.cpp SDAPI requests and preserve URL path
The sd.cpp integration overwrites the URL pathname when constructing
requests, which breaks proxy servers like llama-swap that use path-based
routing (e.g. /upstream/model-name). Additionally, the model field was
never included in SDAPI requests, which is required by llama-swap to
route requests to the correct backend.
Changes:
- Server: Append to URL pathname instead of overwriting (same pattern as #5178)
- Server: Pass model field through to sd-server payload
- Client: Add model name text input for sd.cpp source settings
- Client: Send model name in generate request payload
* fix: fetch models from server and populate standard Model dropdown
Instead of a separate text input for the model name, fetch the model
list from the sd.cpp server's /v1/models endpoint and populate the
standard Model dropdown. This provides a seamless experience where
users just pick a model from the dropdown like any other source.
Works with both standalone sd-server and proxy servers like llama-swap
that expose multiple models via the OpenAI-compatible models endpoint.
* fix: don't send clip_skip=1 to sd.cpp, it produces blank images
sd-server generates blank white images when clip_skip is set to 1.
Since clip_skip=1 means 'use all CLIP layers' (the default behavior),
only send the parameter when it's > 1.
* Fix eslint
* Replace string appends with urlJoin
* fix: convert URL strings to URL objects in sdcpp routes
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* feat: add Workers AI text embeddings and multimodal captioning
Extends the Cloudflare Workers AI integration to the vectors and
caption extensions.
Embeddings: adds workers_ai source to the vectors extension using the
OpenAI-compatible /v1/embeddings endpoint, with dynamic model listing
from the Cloudflare model search API.
Captioning: adds workers_ai as a multimodal caption API with dynamic
vision model discovery via the multimodal-models endpoint.
* Add logo svg
* Refactor caption dropdown population
* Fix order of sources
* feat: add error handling for missing Workers AI account ID
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* feat: add Cloudflare Workers AI provider
Adds support for Cloudflare Workers AI using its OpenAI-compatible API.
Workers AI-specific stuff includes:
- Model list fetching and capabilities detection
- Tokenizer auto-detection for typical hosted model families
- Streaming not supported when using structured output
Closes#5305
* Make the entire header clickable
* Add missing samplers
* Fix non-streaming reasoning parsing
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* Use custom init script instead of postinstall
* Revert changes to start scripts in src\electron
* Add global data to content manager
* Add migration for public overrides and user.css location update
* Update npm publish workflow to use 'omit=dev' flag in npm ci commands
* Rename user.css readme file
* Fix indentation in userCssMiddleware function
* Add directory creation for content target
* Restore template compile location
* Move stylesheet up in index.json
* Use path.resolve for user.css file path in userCssMiddleware
* Correct capitalization in "Not Found" error page title and heading
* Remove init run from startup scripts
* Simplify user CSS file path resolution
* Update userCssMiddleware comment
* Use custom init script instead of postinstall
* Revert changes to start scripts in src\electron
* feat: add --ignore-scripts flag to npm install commands in batch and shell scripts
* feat: add --ignore-scripts flag to npm ci in Dockerfile
* feat(secrets): update readSecret function to accept optional secret ID
* add secret_id to ConnectionManagerRequestService payload
* fix: pass secret_id for Text Completion types
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
The delete handler had a missing `return` before `sendStatus(400)`,
causing execution to fall through to `sendStatus(200)`, a double-send
that triggers ERR_HTTP_HEADERS_SENT, which the catch block then
compounds by attempting a third `sendStatus(500)`.
Both the delete and download handlers used callback-based `fs.unlink()`
without awaiting completion. In the download handler, this caused a race
with `createWriteStream({ flags: 'wx' })` (which fails if the file
still exists). In both handlers, `throw err` inside the callback was an
unhandled exception that could never be caught by the outer try/catch.
Replace callback-based `fs.unlink()` with `await fs.promises.unlink()`
and add missing `return` statements to prevent response cascades.
* fix: conditionally include secrets in user data backup
* feat: add full data backup toggle
* 418 -> 403
I'm not a teapot
* Distinguish fails from disabled
* renaming a lorebook prompts to update existing links
* used suggested api and logic
* add world property to shallow function
* Fix type error in assignLorebookToChat invoke
* Remove debug console logs
* Fix activeCharacterUpdated
* Extract updateWorldInfoLinks into a func
* Invert if for an early return
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* Handle port conflicts during server startup
* Fix return type of startHTTPorHTTPS
* Update language in getAddressInUseMessage
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* Fix missing model name in tokenize requests for llama.cpp (fixes#4962)
The new router mode of llama.cpp allows to switch models on the fly,
what is already supported by SillyTavern. The call to the `/tokenize`
endpoint did not contain the model name, and failed in router mode.
This patch adds the `model` parameter similar to the implementation
for other backends.
* fix: migrate vllm and aphrodite to new payload field
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* feat: add SiliconFlow.cn endpoint support and embedding vectors
Chat completion:
- Add endpoint selection dropdown (Global/.com vs China/.cn) to existing
SiliconFlow provider, following the Z.AI endpoint pattern
- Backend switches API URL based on selected endpoint
- Add /api-url slash command support for endpoint switching
Embeddings:
- Add SiliconFlow as a vector/embedding source (OpenAI-compatible)
- Support both .com and .cn endpoints via siliconflow_endpoint setting
borrowed from the main connection panel (Vertex AI pattern)
- Superset model list with platform attribution (.cn) markers
- Models: Qwen3-Embedding (0.6B/4B/8B) + BGE/BCE models (.cn only)
* Add filter by models type
* Load embedding models from endpoint
* Improve api-url command declaration
* Support endpoint override in custom-request service
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
The addOpenRouterSignatures function was previously converting and appending message.signature to reasoning_details unconditionally, ignoring the `enableThoughtSignatures` setting.
This change adds a check for `enableThoughtSignatures` before converting message.signature, while still ensuring the original signature property is deleted to prevent API schema validation errors (HTTP 400).
* Fix AICC direct link import parsing
Update parseAICC in src/endpoints/content-manager.js to dynamically extract the author and character name from the end of the URL path. This resolves a 404 import error caused by AICC adding category subfolders and changing their base URL structure from /character-cards/ to /charactercards/.
* Clean up whitespace in content-manager.js
Remove unnecessary whitespace in URL path processing.
* Use isValidUrl for URL validation
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* Add gpt-5.3-chat-latest model support
- Add to OpenAI model dropdown (index.html)
- Add to captioning multimodal model list (caption/settings.html)
- Add to OPENAI_REASONING_EFFORT_MODELS (constants.js)
- Add OPENAI_FIXED_REASONING_EFFORT map to clamp effort to 'medium' (the only value this model accepts)
- Apply fixed effort override in both Azure and general OpenAI request paths (chat-completions.js)
- Update frontend gpt-5.x regex for parameter handling (openai.js)
* Update public/scripts/openai.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
System messages in the OpenRouter messages array were being counted
toward depth and could receive cache_control breakpoints. Since
OpenRouter hoists system messages into Claude's separate system
parameter, this misplaced breakpoints and could prevent caching
entirely if the hoisted content fell below minimum cache size.
Closes#5227
* Use Ollama /api/embed endpoint for vector embeddings
The deprecated /api/embeddings endpoint does not properly support the
truncate parameter, causing "input length exceeds context length" errors
when vectorizing files. Migrate to /api/embed which correctly handles
truncation and supports native batch input.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Wrap single Ollama vector calculation into batch
Fixes https://github.com/SillyTavern/SillyTavern/pull/5221/changes#r2850052729
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* backend, frontend, bugfixes
* Mobile button and sizing
* lint
* clear folder thumbnailFile on delete, rename thumbnailFile on rename
* use filteredImages when changing sort option
* Address all the review comments
* Fix friendly title generation to handle empty strings
* Move add folder button to the header
* instead of search filtering the backgrounds in a folder and showing the folder if the results > 0, search the folder names.
* Trade button places
* Adjust button text
* feat: restrict folder creation to the Global tab
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* feat(openrouter): disable reasoning if "Request model reasoning" is disabled
* feat(openrouter): map minimum reasoning to none if request reasoning is off
* Add hint how to disable reasoning
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* build llama.cpp embedding url path by appending instead of overwriting
When using a llama-swap frontend, this allows choosing the embedding model
by path; for instance, with the secondary embedding endpoint URL configured
to http://127.0.0.1:5001/upstream/emb-model to pick the "emb-model" model.
With this approach, the same llama-swap instance can be used to serve both
the main LLM and the embedding model from local llama-server commands.
* Trim trailing v1 from URL
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
* check png for apng headers
* isAnimated flag
* refactor: centralize background animation extension checks
* refactor: scope animated extension dedupe to backgrounds
* remove precompute from startup
* Fix animation preference not being loaded, fix type-check of customInputs
* Fix eslint
* Fix sort on removal type
* Update metadata before returning from endpoint on CRUD operations
* Remove race condition in metadata load
* Load metadata after loading the list
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>