From bddbd47f1070306fb9ac7405049b3bcc3b42dd83 Mon Sep 17 00:00:00 2001 From: Seung Hyun Myung <87285309+seung-myung@users.noreply.github.com> Date: Tue, 17 Feb 2026 08:30:14 +1300 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=AA=AA=20fix:=20Pass=20Scope=20in=20O?= =?UTF-8?q?penID=20Refresh=20Token=20Grant=20for=20Azure=20Custom=20API=20?= =?UTF-8?q?(#11770)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(auth): pass scope parameter in OpenID refresh token grant When using Azure Entra ID with a custom API scope (e.g., api://app-id/access_user) and OPENID_REUSE_TOKENS=true, the refresh token exchange fails with AADSTS90009 because the scope parameter is not included in the refresh request. Azure AD v2.0 requires the scope parameter when refreshing tokens issued for custom API audiences. Without it, Azure interprets the request as the app requesting a token for itself and rejects it. This fix passes OPENID_SCOPE as the scope parameter to refreshTokenGrant(), maintaining backward compatibility (no scope sent if OPENID_SCOPE is not set). Fixes: refresh token 400 error with Azure custom API scopes Tested: Azure Entra ID + Token Reuse + SharePoint integration * style(auth): fix ESLint multiline arguments formatting Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- api/server/controllers/AuthController.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/server/controllers/AuthController.js b/api/server/controllers/AuthController.js index 22e53dcfc9..70491dcc61 100644 --- a/api/server/controllers/AuthController.js +++ b/api/server/controllers/AuthController.js @@ -79,7 +79,12 @@ const refreshController = async (req, res) => { try { const openIdConfig = getOpenIdConfig(); - const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken); + const refreshParams = process.env.OPENID_SCOPE ? { scope: process.env.OPENID_SCOPE } : {}; + const tokenset = await openIdClient.refreshTokenGrant( + openIdConfig, + refreshToken, + refreshParams, + ); const claims = tokenset.claims(); const { user, error, migration } = await findOpenIDUser({ findUser, From 3c844c9cc6b7c09c2cde775fcf49675a2dd529e2 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 16 Feb 2026 14:42:19 -0500 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=A5=A0=20refactor:=20Always=20set=20O?= =?UTF-8?q?IDC=20refresh=20token=20cookie=20to=20survive=20session=20expir?= =?UTF-8?q?y=20(#11810)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The express session cookie maxAge (SESSION_EXPIRY, default 15 min) is shorter than the OIDC token lifetime (~1 hour). When OPENID_REUSE_TOKENS is enabled, the refresh token was stored only in the express session (req.session.openidTokens). After the session expired, the refresh token was lost, causing "Refresh token not provided" on the next refresh attempt and signing the user out. Re-login via OIDC would succeed immediately (provider session still active), masking the root cause. The session-only storage was introduced in #11236 to avoid HTTP/2 header size limits from large access_token/id_token JWTs (especially Azure Entra ID with many group claims). The refresh token is a small opaque string and does not contribute to that problem. Move the refreshToken cookie out of the no-session fallback branch so it is always set alongside the session storage. The refreshController already has the fallback logic (req.session?.openidTokens?.refreshToken || parsedCookies.refreshToken) but previously never had a cookie to fall back to. Timeline before fix: T=0 Login, session created (15 min maxAge), id_token valid ~1 hr T=15min Session cookie expires, refresh token lost T=15min+ Page refresh or id_token expiry triggers refresh, fails with "Refresh token not provided", user redirected to /login Timeline after fix: T=0 Login, session created + refreshToken cookie (7 day expiry) T=15min Session cookie expires T=15min+ Refresh reads refreshToken from cookie fallback, succeeds, restores session with fresh tokens --- api/server/services/AuthService.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/api/server/services/AuthService.js b/api/server/services/AuthService.js index 1b4b653c1a..ef50a365b9 100644 --- a/api/server/services/AuthService.js +++ b/api/server/services/AuthService.js @@ -462,6 +462,22 @@ const setOpenIDAuthTokens = (tokenset, req, res, userId, existingRefreshToken) = */ const appAuthToken = tokenset.id_token || tokenset.access_token; + /** + * Always set refresh token cookie so it survives express session expiry. + * The session cookie maxAge (SESSION_EXPIRY, default 15 min) is typically shorter + * than the OIDC token lifetime (~1 hour). Without this cookie fallback, the refresh + * token stored only in the session is lost when the session expires, causing the user + * to be signed out on the next token refresh attempt. + * The refresh token is small (opaque string) so it doesn't hit the HTTP/2 header + * size limits that motivated session storage for the larger access_token/id_token. + */ + res.cookie('refreshToken', refreshToken, { + expires: expirationDate, + httpOnly: true, + secure: shouldUseSecureCookie(), + sameSite: 'strict', + }); + /** Store tokens server-side in session to avoid large cookies */ if (req.session) { req.session.openidTokens = { @@ -472,12 +488,6 @@ const setOpenIDAuthTokens = (tokenset, req, res, userId, existingRefreshToken) = }; } else { logger.warn('[setOpenIDAuthTokens] No session available, falling back to cookies'); - res.cookie('refreshToken', refreshToken, { - expires: expirationDate, - httpOnly: true, - secure: shouldUseSecureCookie(), - sameSite: 'strict', - }); res.cookie('openid_access_token', tokenset.access_token, { expires: expirationDate, httpOnly: true, From 413c2bc0768d27ca1f08d0c31bf2cf2581ddc5a0 Mon Sep 17 00:00:00 2001 From: MyGitHub Date: Mon, 16 Feb 2026 22:23:59 +0100 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=AA=82=20fix:=20Handle=20MongoDB=20Co?= =?UTF-8?q?nnection=20Errors=20to=20Prevent=20Process=20Crashes=20(#11809)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: handle MongoDB connection errors to prevent process crashes Add mongoose.connection.on('error') listener in connect.js to catch connection-level errors emitted by MongoDB driver's SDAM monitoring. Without this listener, these errors become uncaught exceptions per Node.js EventEmitter behavior. Also add MongoDB error patterns to the uncaughtException handler in server/index.js as defense-in-depth, following the same pattern used for GoogleGenerativeAI, Meilisearch, and OpenAI errors. Fixes #11808 * style: fix prettier formatting in uncaughtException handler * refactor: move error listener to module level * fix: use precise MongoDB error matching in uncaughtException handler * fix: replace process.exit(1) with graceful error logging Instead of maintaining a growing list of error patterns that should not crash the process, invert the default behavior: log all unhandled errors and keep running. The existing specific handlers are preserved for their contextual log messages. This prevents process crashes from any transient error (MongoDB timeouts, network issues, third-party library bugs) without needing to add new patterns each time a new error type is encountered. Unnecessary restarts are expensive as they trigger full Meilisearch re-syncs under load. * fix: address review feedback - connect.js: pass full error object to logger instead of just message - server/index.js: add optional chaining for nullish err - server/index.js: make crash-on-unknown-error opt-in via CRASH_ON_UNCAUGHT_EXCEPTION env var (defaults to graceful logging) * fix: rename to CONTINUE_ON_UNCAUGHT_EXCEPTION, default to exit --------- Co-authored-by: Feng Lu --- api/db/connect.js | 4 ++++ api/server/index.js | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/api/db/connect.js b/api/db/connect.js index 26166ccff8..3534884b57 100644 --- a/api/db/connect.js +++ b/api/db/connect.js @@ -40,6 +40,10 @@ if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } +mongoose.connection.on('error', (err) => { + logger.error('[connectDb] MongoDB connection error:', err); +}); + async function connectDb() { if (cached.conn && cached.conn?._readyState === 1) { return cached.conn; diff --git a/api/server/index.js b/api/server/index.js index fcd0229c9f..193eb423ad 100644 --- a/api/server/index.js +++ b/api/server/index.js @@ -251,6 +251,15 @@ process.on('uncaughtException', (err) => { return; } + if (isEnabled(process.env.CONTINUE_ON_UNCAUGHT_EXCEPTION)) { + logger.error('Unhandled error encountered. The app will continue running.', { + name: err?.name, + message: err?.message, + stack: err?.stack, + }); + return; + } + process.exit(1); }); From 35672e0bbb592375c88fe0d6f1f3e778db7d8b8f Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 16 Feb 2026 16:36:32 -0500 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=93=A6=20chore:=20`@librechat/agents`?= =?UTF-8?q?=20to=20v3.1.44=20(#11811)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/package.json | 2 +- package-lock.json | 10 +++++----- packages/api/package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/package.json b/api/package.json index 0c580d012e..fb7efffce8 100644 --- a/api/package.json +++ b/api/package.json @@ -44,7 +44,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.43", + "@librechat/agents": "^3.1.44", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", diff --git a/package-lock.json b/package-lock.json index 0347688c3d..7ac354b2ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.43", + "@librechat/agents": "^3.1.44", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", @@ -11208,9 +11208,9 @@ } }, "node_modules/@librechat/agents": { - "version": "3.1.43", - "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.1.43.tgz", - "integrity": "sha512-KtcBA7b/63RfYAQwVVt3TNAcZHAfmHe8wLNnSjF9NTEXI1EETBHCqh9yuGicjwdIMXVQhi64qmLPDqEFoFr7ww==", + "version": "3.1.44", + "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.1.44.tgz", + "integrity": "sha512-S5hNYTCQqqPfabsdS2nSdGHtcZ4ZvwFnMN11BvJZUEgZ8VHMS9/a+plTSTLqSCoYmUeFI5hIK3pahk5H9vOPug==", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.73.0", @@ -42205,7 +42205,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.43", + "@librechat/agents": "^3.1.44", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.26.0", "@smithy/node-http-handler": "^4.4.5", diff --git a/packages/api/package.json b/packages/api/package.json index 6929d69544..a2bd6c0257 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -87,7 +87,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.43", + "@librechat/agents": "^3.1.44", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.26.0", "@smithy/node-http-handler": "^4.4.5", From b21672335f7f7cad5cca8fed1a8edf9909850934 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 16 Feb 2026 16:47:07 -0500 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=93=8B=20chore:=20Document=20Uncaught?= =?UTF-8?q?=20Exception=20Config=20and=20Fix=20Empty=20Text=20Export=20(#1?= =?UTF-8?q?1812)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: Prevent empty text parts in conversation export function Added a check to return an empty array if the text part of the conversation is empty or consists only of whitespace, ensuring cleaner data handling in the export process. * chore: Update .env.example to include CONTINUE_ON_UNCAUGHT_EXCEPTION variable Added documentation for the CONTINUE_ON_UNCAUGHT_EXCEPTION environment variable, which allows the app to continue running after encountering uncaught exceptions. This change is not recommended for production environments unless necessary. --- .env.example | 4 ++++ client/src/hooks/Conversations/useExportConversation.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.env.example b/.env.example index 4fd526f569..ec848c72a0 100644 --- a/.env.example +++ b/.env.example @@ -47,6 +47,10 @@ TRUST_PROXY=1 # password policies. # MIN_PASSWORD_LENGTH=8 +# When enabled, the app will continue running after encountering uncaught exceptions +# instead of exiting the process. Not recommended for production unless necessary. +# CONTINUE_ON_UNCAUGHT_EXCEPTION=false + #===============# # JSON Logging # #===============# diff --git a/client/src/hooks/Conversations/useExportConversation.ts b/client/src/hooks/Conversations/useExportConversation.ts index 579b5f1cf6..dc352ccab9 100644 --- a/client/src/hooks/Conversations/useExportConversation.ts +++ b/client/src/hooks/Conversations/useExportConversation.ts @@ -106,6 +106,9 @@ export default function useExportConversation({ // TEXT const textPart = content[ContentTypes.TEXT]; const text = typeof textPart === 'string' ? textPart : (textPart?.value ?? ''); + if (text.trim().length === 0) { + return []; + } return [sender, text]; } From be78f8bb86adc87fc42413599b5165537839e93d Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 16 Feb 2026 21:03:21 -0500 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=93=A6=20chore:=20Update=20`@librecha?= =?UTF-8?q?t/agents`=20to=20v3.1.45=20(#11815)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/package.json | 2 +- package-lock.json | 10 +++++----- packages/api/package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/package.json b/api/package.json index fb7efffce8..fa06fc0432 100644 --- a/api/package.json +++ b/api/package.json @@ -44,7 +44,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.44", + "@librechat/agents": "^3.1.45", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", diff --git a/package-lock.json b/package-lock.json index 7ac354b2ce..b1af66176a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.44", + "@librechat/agents": "^3.1.45", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", @@ -11208,9 +11208,9 @@ } }, "node_modules/@librechat/agents": { - "version": "3.1.44", - "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.1.44.tgz", - "integrity": "sha512-S5hNYTCQqqPfabsdS2nSdGHtcZ4ZvwFnMN11BvJZUEgZ8VHMS9/a+plTSTLqSCoYmUeFI5hIK3pahk5H9vOPug==", + "version": "3.1.45", + "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.1.45.tgz", + "integrity": "sha512-izapt5PScVF52xhDH5N5uzCbjK1BMaGsUiKFNVeO20AjDWul+ipDm5zWmXnfX4veD8YHqY5rPnRU0oAecljZIg==", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.73.0", @@ -42205,7 +42205,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.44", + "@librechat/agents": "^3.1.45", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.26.0", "@smithy/node-http-handler": "^4.4.5", diff --git a/packages/api/package.json b/packages/api/package.json index a2bd6c0257..5ee9e3bb70 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -87,7 +87,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.44", + "@librechat/agents": "^3.1.45", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.26.0", "@smithy/node-http-handler": "^4.4.5", From 8dd814d9b73ce81145e6fa579be92dd48e8a163f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:20:21 -0500 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=8C=8D=20i18n:=20Update=20translation?= =?UTF-8?q?.json=20with=20latest=20translations=20(#11813)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- client/src/locales/nb/translation.json | 160 ++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 3 deletions(-) diff --git a/client/src/locales/nb/translation.json b/client/src/locales/nb/translation.json index 15d77af35d..f5b1943b39 100644 --- a/client/src/locales/nb/translation.json +++ b/client/src/locales/nb/translation.json @@ -3,11 +3,13 @@ "chat_direction_right_to_left": "Høyre til venstre", "com_a11y_ai_composing": "KI-en skriver fortsatt.", "com_a11y_end": "KI-en har fullført svaret sitt.", + "com_a11y_selected": "valgt", "com_a11y_start": "KI-en har begynt å svare.", "com_agents_agent_card_label": "{{name}}-agent. {{description}}", "com_agents_all": "Alle agenter", "com_agents_all_category": "Alle", "com_agents_all_description": "Utforsk delte agenter på tvers av alle kategorier", + "com_agents_avatar_upload_error": "Kunne ikke laste opp agentavatar", "com_agents_by_librechat": "av LibreChat", "com_agents_category_aftersales": "Salgsoppfølging", "com_agents_category_aftersales_description": "Agenter for kundeservice, support og oppfølging etter et gjennomført salg.", @@ -26,6 +28,7 @@ "com_agents_category_sales_description": "Agenter som bistår i salgsprosesser og med kundekontakt.", "com_agents_category_tab_label": "Kategorien {{category}}, {{position}} av {{total}}", "com_agents_category_tabs_label": "Agentkategorier", + "com_agents_chat_with": "Chat med {{navn}}", "com_agents_clear_search": "Tøm søket", "com_agents_code_interpreter": "Når aktivert, kan agenten din bruke LibreChat Code Interpreter API for å kjøre generert kode sikkert, inkludert filbehandling. Krever en gyldig API-nøkkel.", "com_agents_code_interpreter_title": "Code Interpreter API", @@ -33,6 +36,7 @@ "com_agents_copy_link": "Kopier lenke", "com_agents_create_error": "Det oppstod en feil under oppretting av agenten.", "com_agents_created_by": "av", + "com_agents_description_card": "Beskrivelse: {{description}}", "com_agents_description_placeholder": "Valgfritt: Beskriv agenten din her.", "com_agents_empty_state_heading": "Ingen agenter funnet", "com_agents_enable_file_search": "Aktiver filsøk", @@ -59,7 +63,9 @@ "com_agents_error_timeout_suggestion": "Sjekk internettforbindelsen din og prøv igjen.", "com_agents_error_timeout_title": "Tidsavbrudd for tilkobling", "com_agents_error_title": "Noe gikk galt", + "com_agents_file_context_description": "Filer lastet opp som \"Kontekst\" er analysert son tekst for å supplementere agenten sine instruksjoner. Dersom OCR er tilgjengelig, eller er konfigurert for den opplastede filtypen, vil prosessen bli brukt til å hente ut tekst. Dette er ideelt for dokumenter, bilder med tekst, eller PDFer som krever det fulle tekstinnholdet i en fil.", "com_agents_file_context_disabled": "Agenten må være opprettet før du kan laste opp filer for filkontekst.", + "com_agents_file_context_label": "Filkontekst", "com_agents_file_search_disabled": "Agenten må være opprettet før du kan laste opp filer for filsøk.", "com_agents_file_search_info": "Når dette er aktivert, vil agenten bruke de eksakte filnavnene listet nedenfor for å hente relevant kontekst fra disse filene.", "com_agents_grid_announcement": "Viser {{count}} agenter i kategorien {{category}}.", @@ -87,7 +93,7 @@ "com_agents_search_empty_heading": "Ingen søkeresultater", "com_agents_search_info": "Når aktivert, kan agenten din søke på nettet for oppdatert informasjon. Krever en gyldig API-nøkkel.", "com_agents_search_instructions": "Skriv for å søke etter agenter etter navn eller beskrivelse.", - "com_agents_search_name": "Søk agenter etter navn", + "com_agents_search_name": "Søk etter agenter ved navn", "com_agents_search_no_results": "Ingen agenter funnet for «{{query}}».", "com_agents_search_placeholder": "Søk agenter ...", "com_agents_see_more": "Se mer", @@ -139,6 +145,7 @@ "com_assistants_update_actions_success": "Handlingen ble opprettet eller oppdatert.", "com_assistants_update_error": "Det oppstod en feil under oppdatering av assistenten.", "com_assistants_update_success": "Oppdatering fullført", + "com_assistants_update_success_name": "Oppdatering av {{name}} vellykket", "com_auth_already_have_account": "Har du allerede en konto?", "com_auth_apple_login": "Logg inn med Apple", "com_auth_back_to_login": "Tilbake til innlogging", @@ -217,10 +224,11 @@ "com_endpoint_agent": "Agent", "com_endpoint_agent_placeholder": "Velg en agent", "com_endpoint_ai": "KI", + "com_endpoint_anthropic_effort": "Kontrollerer hvor mye innsats Claude legger i beregning. Lavere innsats sparer tokens og reduserer treghet, høyere innsats produserer mer gjennom responser. \"Maks\" gir den høyeste graden av resonnering (kun Opus 4.6)", "com_endpoint_anthropic_maxoutputtokens": "Maksimalt antall tokens som kan genereres i svaret. Angi en lavere verdi for kortere svar og en høyere verdi for lengre svar. Merk: Modeller kan stoppe før de når dette maksimumet.", "com_endpoint_anthropic_prompt_cache": "Prompt-mellomlagring gjør det mulig å gjenbruke stor kontekst eller instruksjoner på tvers av API-kall, noe som reduserer kostnader og ventetid.", "com_endpoint_anthropic_temp": "Varierer fra 0 til 1. Bruk en temperatur nærmere 0 for analytiske oppgaver, og nærmere 1 for kreative og generative oppgaver. Vi anbefaler å endre enten denne eller Topp P, men ikke begge.", - "com_endpoint_anthropic_thinking": "Aktiverer intern resonnering for støttede Claude-modeller (f.eks. 3.7 Sonnet). Merk: Krever at \"Tenkebudsjett\" er satt og er lavere enn \"Maks utdata-tokens\".", + "com_endpoint_anthropic_thinking": "Aktiverer intern resonnering for støttede Claude-modeller. For nyere modeller (Opus 4.6+) brukes adaptiv tenkning kontrollert av Effort-parameteren. For eldre modeller kreves det at \"Thinking Budget\" er satt og lavere enn \"Max Output Tokens\".", "com_endpoint_anthropic_thinking_budget": "Bestemmer det maksimale antallet tokens Claude kan bruke for sin interne resonneringsprosess. Et større budsjett kan forbedre svarkvaliteten for komplekse problemer. Denne verdien må være lavere enn \"Maks utdata-tokens\".", "com_endpoint_anthropic_topk": "Top-k endrer hvordan modellen velger tokens for utdata. En top-k på 1 betyr at det valgte tokenet er det mest sannsynlige (grådig dekoding). En top-k på 3 betyr at det neste tokenet velges blant de 3 mest sannsynlige (ved hjelp av temperatur).", "com_endpoint_anthropic_topp": "Top-p endrer hvordan modellen velger tokens for utdata. Tokens velges fra de mest sannsynlige til summen av sannsynlighetene deres er lik top-p-verdien.", @@ -258,6 +266,7 @@ "com_endpoint_default_with_num": "standard: {{0}}", "com_endpoint_disable_streaming": "Deaktiver strømming av svar og motta hele svaret på en gang. Nyttig for modeller som krever organisasjonsverifisering for strømming.", "com_endpoint_disable_streaming_label": "Deaktiver strømming", + "com_endpoint_effort": "Innsats", "com_endpoint_examples": "Forhåndsinnstillinger", "com_endpoint_export": "Eksporter", "com_endpoint_export_share": "Eksporter/Del", @@ -274,7 +283,7 @@ "com_endpoint_instructions_assistants_placeholder": "Overstyrer assistentens instruksjoner. Nyttig for å endre atferden for en enkelt kjøring.", "com_endpoint_max_output_tokens": "Maks utdata-tokens", "com_endpoint_message": "Melding", - "com_endpoint_message_new": "Melding {{0}}", + "com_endpoint_message_new": "Send melding til {{0}}", "com_endpoint_message_not_appendable": "Rediger meldingen din eller regenerer.", "com_endpoint_my_preset": "Min forhåndsinnstilling", "com_endpoint_no_presets": "Ingen forhåndsinnstillinger ennå. Bruk innstillingsknappen for å lage en.", @@ -308,6 +317,7 @@ "com_endpoint_preset_default_removed": "er ikke lenger standard forhåndsinnstilling.", "com_endpoint_preset_delete_confirm": "Er du sikker på at du vil slette denne forhåndsinnstillingen?", "com_endpoint_preset_delete_error": "Det oppstod en feil under sletting av forhåndsinnstillingen. Vennligst prøv igjen.", + "com_endpoint_preset_delete_success": "Sletting av forhåndsinnstilling vellykket", "com_endpoint_preset_import": "Forhåndsinnstilling importert!", "com_endpoint_preset_import_error": "Det oppstod en feil under importering av forhåndsinnstillingen. Vennligst prøv igjen.", "com_endpoint_preset_name": "Navn på forhåndsinnstilling", @@ -348,6 +358,7 @@ "com_error_files_process": "Det oppstod en feil under behandling av filen.", "com_error_files_upload": "Det oppstod en feil under opplasting av filen.", "com_error_files_upload_canceled": "Forespørselen om filopplasting ble avbrutt. Merk: Filopplastingen kan fortsatt behandles og må slettes manuelt.", + "com_error_files_upload_too_large": "Filen er for stor. Vennligst last opp en fil som er mindre enn {{}} MB", "com_error_files_validation": "Det oppstod en feil under validering av filen.", "com_error_google_tool_conflict": "Bruk av innebygde Google-verktøy støttes ikke sammen med eksterne verktøy. Deaktiver enten de innebygde eller de eksterne verktøyene.", "com_error_heic_conversion": "Konvertering av HEIC-bilde til JPEG mislyktes. Prøv å konvertere bildet manuelt eller bruk et annet format.", @@ -360,6 +371,7 @@ "com_error_moderation": "Innholdet du sendte inn ble flagget av vårt moderasjonssystem. Vi kan ikke fortsette med dette emnet. Rediger meldingen din eller start en ny samtale.", "com_error_no_base_url": "Ingen base-URL funnet. Oppgi en og prøv igjen.", "com_error_no_user_key": "Ingen nøkkel funnet. Oppgi en nøkkel og prøv igjen.", + "com_error_refusal": "Responsen ble avslått av sikkerhetsfiltere. Skriv om på meldingen din og prøv igjen. Dersom denne feilmeldingen forekommer ofte imens du bruker Claude Sonnet 4.5 eller Opus 4.1, kan du prøve Sonnet 4, som har andre bruksrestriksjoner.", "com_file_pages": "Sider: {{pages}}", "com_file_source": "Fil", "com_file_unknown": "Ukjent fil", @@ -368,9 +380,12 @@ "com_files_download_progress": "{{0}} av {{1}} filer", "com_files_downloading": "Laster ned filer", "com_files_filter": "Filtrer filer ...", + "com_files_filter_by": "Filtrer filer etter...", "com_files_no_results": "Ingen resultater.", "com_files_number_selected": "{{0}} av {{1}} valgt", "com_files_preparing_download": "Forbereder nedlasting ...", + "com_files_result_found": "{{count}} resultater funnet", + "com_files_results_found": "{{count}} resultater funnet", "com_files_sharepoint_picker_title": "Velg filer", "com_files_table": "Fil-tabell", "com_files_upload_local_machine": "Fra lokal datamaskin", @@ -421,6 +436,7 @@ "com_nav_chat_commands": "Samtalekommandoer", "com_nav_chat_commands_info": "Disse kommandoene aktiveres ved å skrive bestemte tegn i begynnelsen av meldingen din. Hver kommando utløses av sitt angitte prefiks. Du kan deaktivere dem hvis du ofte bruker disse tegnene til å starte meldinger.", "com_nav_chat_direction": "Samtaleretning", + "com_nav_chat_direction_selected": "Chat retning: {{direction}}", "com_nav_clear_all_chats": "Fjern alle samtaler", "com_nav_clear_cache_confirm_message": "Er du sikker på at du vil tømme mellomlageret?", "com_nav_clear_conversation": "Fjern samtaler", @@ -428,9 +444,11 @@ "com_nav_close_sidebar": "Lukk sidefelt", "com_nav_commands": "Kommandoer", "com_nav_confirm_clear": "Bekreft fjerning", + "com_nav_control_panel": "Kontrollpanel", "com_nav_conversation_mode": "Samtalemodus", "com_nav_convo_menu_options": "Samtalemenyvalg", "com_nav_db_sensitivity": "Desibelfølsomhet", + "com_nav_default_temporary_chat": "Midlertidig Chat som standard", "com_nav_delete_account": "Slett konto", "com_nav_delete_account_button": "Slett kontoen min permanent", "com_nav_delete_account_confirm": "Slett konto – er du sikker?", @@ -464,6 +482,7 @@ "com_nav_info_code_artifacts": "Aktiverer visning av eksperimentelle kodeartefakter ved siden av samtalen.", "com_nav_info_code_artifacts_agent": "Aktiverer bruk av kodeartefakter for denne agenten. Som standard legges det til tilleggsinstruksjoner for bruk av artefakter, med mindre \"Egendefinert prompt-modus\" er aktivert.", "com_nav_info_custom_prompt_mode": "Når aktivert, vil standard systemprompt for artefakter ikke bli inkludert. Alle instruksjoner for å generere artefakter må gis manuelt i denne modusen.", + "com_nav_info_default_temporary_chat": "Når dette er påskrudd vil nye chatter starte med \"midlertidig chat\" som standard. Midlertidige chatter blir ikke lagret til historikken din.", "com_nav_info_enter_to_send": "Når aktivert, vil et trykk på `ENTER` sende meldingen din. Når deaktivert, vil et trykk på Enter legge til en ny linje. Du må da trykke `CTRL + ENTER` / `⌘ + ENTER` for å sende.", "com_nav_info_fork_change_default": "`Kun synlige meldinger` inkluderer bare den direkte stien til den valgte meldingen. `Inkluder relaterte grener` legger til grener langs stien. `Inkluder alt til/fra her` inkluderer alle tilknyttede meldinger og grener.", "com_nav_info_fork_split_target_setting": "Når aktivert, vil forgreningen starte fra målmeldingen til den siste meldingen i samtalen, i henhold til den valgte atferden.", @@ -473,6 +492,7 @@ "com_nav_info_save_draft": "Når aktivert, vil teksten og vedleggene du skriver inn bli lagret lokalt som et utkast. Utkastet er tilgjengelig selv om du laster siden på nytt eller bytter samtale. Utkastet slettes når meldingen er sendt.", "com_nav_info_show_thinking": "Når aktivert, vil tenke-nedtrekksmenyene vises som standard, slik at du kan se KI-ens resonnement i sanntid. Når deaktivert, vil de være lukket for et renere grensesnitt.", "com_nav_info_user_name_display": "Når aktivert, vil brukernavnet ditt vises over hver melding du sender. Når deaktivert, vil du bare se \"Du\" over meldingene dine.", + "com_nav_keep_screen_awake": "Hold skjermen på gjennom generering av respons", "com_nav_lang_arabic": "Arabisk (العربية)", "com_nav_lang_armenian": "Armensk (Հայերեն)", "com_nav_lang_auto": "Automatisk gjenkjenning", @@ -491,16 +511,20 @@ "com_nav_lang_german": "Tysk (Deutsch)", "com_nav_lang_hebrew": "Hebraisk (עברית)", "com_nav_lang_hungarian": "Ungarsk (Magyar)", + "com_nav_lang_icelandic": "Islandsk", "com_nav_lang_indonesia": "Indonesisk (Indonesia)", "com_nav_lang_italian": "Italiensk (Italiano)", "com_nav_lang_japanese": "Japansk (日本語)", "com_nav_lang_korean": "Koreansk (한국어)", "com_nav_lang_latvian": "Latvisk (Latviski)", + "com_nav_lang_lithuanian": "Litauisk", "com_nav_lang_norwegian_bokmal": "Norsk bokmål", + "com_nav_lang_norwegian_nynorsk": "Norsk nynorsk", "com_nav_lang_persian": "Persisk (فارسی)", "com_nav_lang_polish": "Polsk (Polski)", "com_nav_lang_portuguese": "Portugisisk (Português)", "com_nav_lang_russian": "Russisk (Русский)", + "com_nav_lang_slovak": "Slovensk", "com_nav_lang_slovenian": "Slovensk", "com_nav_lang_spanish": "Spansk (Español)", "com_nav_lang_swedish": "Svensk (Svenska)", @@ -516,8 +540,18 @@ "com_nav_log_out": "Logg ut", "com_nav_long_audio_warning": "Lengre tekster vil ta lengre tid å behandle.", "com_nav_maximize_chat_space": "Maksimer samtaleplass", + "com_nav_mcp_access_revoked": "Tilbakekalling av MCP servertilgang vellykket.", "com_nav_mcp_configure_server": "Konfigurer {{0}}", + "com_nav_mcp_connect": "Koble til", + "com_nav_mcp_connect_server": "Koble til {{0}}", + "com_nav_mcp_reconnect": "Koble til på nytt", + "com_nav_mcp_status_connected": "Tilkoblet", "com_nav_mcp_status_connecting": "{{0}} - Kobler til", + "com_nav_mcp_status_disconnected": "Frakoblet", + "com_nav_mcp_status_error": "Feil", + "com_nav_mcp_status_initializing": "Starter", + "com_nav_mcp_status_needs_auth": "Trenger Auth", + "com_nav_mcp_status_unknown": "Ukjent", "com_nav_mcp_vars_update_error": "Feil ved oppdatering av egendefinerte MCP-brukervariabler.", "com_nav_mcp_vars_updated": "Egendefinerte MCP-brukervariabler ble oppdatert.", "com_nav_modular_chat": "Aktiver bytte av endepunkter midt i en samtale", @@ -538,6 +572,7 @@ "com_nav_setting_balance": "Saldo", "com_nav_setting_chat": "Samtale", "com_nav_setting_data": "Datakontroll", + "com_nav_setting_delay": "Forsinkelse (s)", "com_nav_setting_general": "Generelt", "com_nav_setting_mcp": "MCP-innstillinger", "com_nav_setting_personalization": "Personalisering", @@ -555,6 +590,7 @@ "com_nav_theme_dark": "Mørkt", "com_nav_theme_light": "Lyst", "com_nav_theme_system": "System", + "com_nav_toggle_sidebar": "Skru sidebar av/på", "com_nav_tool_dialog": "Assistentverktøy", "com_nav_tool_dialog_agents": "Agentverktøy", "com_nav_tool_dialog_description": "Assistenten må lagres for at verktøyvalg skal vedvare.", @@ -605,17 +641,27 @@ "com_ui_action_button": "Handlingsknapp", "com_ui_active": "Aktiv", "com_ui_add": "Legg til", + "com_ui_add_code_interpreter_api_key": "Legg til kodetolk API nøkkel", + "com_ui_add_first_bookmark": "Klikk på en chat for å legge til", + "com_ui_add_first_mcp_server": "Lag din første MCP server for å komme i gang", + "com_ui_add_first_prompt": "Lag din første prompt for å komme i gang", "com_ui_add_mcp": "Legg til MCP", "com_ui_add_mcp_server": "Legg til MCP-server", "com_ui_add_model_preset": "Legg til en modell eller forhåndsinnstilling for et ekstra svar.", "com_ui_add_multi_conversation": "Legg til flersamtale", + "com_ui_add_special_variables": "Legg til spesialvariable", + "com_ui_add_web_search_api_keys": "Legg til nettsøk API-nøkler", "com_ui_adding_details": "Legger til detaljer", + "com_ui_additional_details": "Flere detaljer", "com_ui_admin": "Admin", "com_ui_admin_access_warning": "Deaktivering av admin-tilgang til denne funksjonen kan forårsake uventede UI-problemer. Hvis lagret, kan dette kun tilbakestilles via konfigurasjonsfilen (librechat.yaml).", "com_ui_admin_settings": "Admin-innstillinger", + "com_ui_admin_settings_section": "Admininnstillinger - {{section}}", "com_ui_advanced": "Avansert", "com_ui_advanced_settings": "Avanserte innstillinger", "com_ui_agent": "Agent", + "com_ui_agent_api_keys": "Agent API-nøkler", + "com_ui_agent_api_keys_description": "Lag API-nøkler for å få tilgang til agenter via API", "com_ui_agent_category_aftersales": "Ettersalg", "com_ui_agent_category_finance": "Finans", "com_ui_agent_category_general": "Generelt", @@ -631,6 +677,17 @@ "com_ui_agent_deleted": "Agenten ble slettet.", "com_ui_agent_duplicate_error": "Det oppstod en feil under duplisering av agenten.", "com_ui_agent_duplicated": "Agenten ble duplisert.", + "com_ui_agent_handoff_add": "Legg til overleveringsagent", + "com_ui_agent_handoff_description": "Beskrivelse av overlevering", + "com_ui_agent_handoff_description_placeholder": "f.eks., Overfør til dataanalytiker for statistisk analyse", + "com_ui_agent_handoff_info": "Konfigurer agenter som denne agenten kan overføre samtaler til når spesifikk ekspertise er nødvendig", + "com_ui_agent_handoff_info_2": "Hver overlevering lager et overføringsverktøy som tillater sømløs ruting til spesialistagenter med kontekst.", + "com_ui_agent_handoff_max": "Maksgrensen på {{0}} overleveringsagenter er nådd", + "com_ui_agent_handoff_prompt": "Gjennomføringsinnhold", + "com_ui_agent_handoff_prompt_key": "Innholdsparameter navn (standard: \"instruksjoner\")", + "com_ui_agent_handoff_prompt_key_placeholder": "Merk innholdet som er sendt (standard: \"instruksjoner\")", + "com_ui_agent_handoff_prompt_placeholder": "Fortell denne agenten hvilket innhold den skal generere og videreføre til overleveringsagenten. Du må legge til noe her for å skru på denne funksjonen.", + "com_ui_agent_handoffs": "Agentoverleveringer", "com_ui_agent_name_is_required": "Agentnavn er påkrevd.", "com_ui_agent_recursion_limit": "Maks agentsteg", "com_ui_agent_recursion_limit_info": "Begrenser hvor mange steg agenten kan ta i en kjøring før den gir et endelig svar. Standard er 25 steg. Et steg er enten en API-forespørsel eller bruk av et verktøy.", @@ -652,12 +709,23 @@ "com_ui_agents": "Agenter", "com_ui_agents_allow_create": "Tillat oppretting av agenter", "com_ui_agents_allow_share": "Tillat deling av agenter", + "com_ui_agents_allow_share_public": "Tillat offentlig deling av agenter", "com_ui_agents_allow_use": "Tillat bruk av agenter", "com_ui_all": "alle", "com_ui_all_proper": "Alle", "com_ui_analyzing": "Analyserer", "com_ui_analyzing_finished": "Ferdig med å analysere", "com_ui_api_key": "API-nøkkel", + "com_ui_api_key_copied": "API-nøkler kopiert til utklippstavlen", + "com_ui_api_key_create_error": "Kunne ikke lage API-nøkkel", + "com_ui_api_key_created": "Oppretting av API-nøkkel vellykket", + "com_ui_api_key_delete_error": "Kunne ikke slette API-nøkkel", + "com_ui_api_key_deleted": "Sletting av API-nøkkel vellykket", + "com_ui_api_key_name": "Navn på nøkkel", + "com_ui_api_key_name_placeholder": "Min API-nøkkel", + "com_ui_api_key_name_required": "Navn på API-nøkkel påkrevd", + "com_ui_api_key_warning": "Husk å kopiere API-nøkkelen din nå, du vil ikke kunne se den igjen!", + "com_ui_api_keys_load_error": "Kunne ikke laste inn API-nøkler", "com_ui_archive": "Arkiver", "com_ui_archive_delete_error": "Sletting av arkivert samtale mislyktes.", "com_ui_archive_error": "Arkivering av samtale mislyktes.", @@ -674,6 +742,7 @@ "com_ui_assistants_output": "Assistent-utdata", "com_ui_at_least_one_owner_required": "Minst én eier er påkrevd.", "com_ui_attach_error": "Kan ikke legge ved fil. Opprett eller velg en samtale, eller prøv å laste siden på nytt.", + "com_ui_attach_error_disabled": "FIlopplasting er deaktivert for dette endepunktet", "com_ui_attach_error_openai": "Kan ikke legge ved assistentfiler til andre endepunkter.", "com_ui_attach_error_size": "Filstørrelsesgrensen er overskredet for endepunktet:", "com_ui_attach_error_type": "Filtypen støttes ikke for endepunktet:", @@ -690,6 +759,7 @@ "com_ui_azure": "Azure", "com_ui_azure_ad": "Entra ID", "com_ui_back": "Tilbake", + "com_ui_back_to_builder": "Tilbake til bygger", "com_ui_back_to_chat": "Tilbake til samtale", "com_ui_back_to_prompts": "Tilbake til prompter", "com_ui_backup_code_number": "Kode #{{number}}", @@ -701,10 +771,12 @@ "com_ui_basic": "Grunnleggende", "com_ui_basic_auth_header": "Grunnleggende autorisasjonshode", "com_ui_bearer": "Bearer", + "com_ui_beta": "Beta", "com_ui_bookmark_delete_confirm": "Er du sikker på at du vil slette dette bokmerket?", "com_ui_bookmarks": "Bokmerker", "com_ui_bookmarks_add": "Legg til bokmerker", "com_ui_bookmarks_add_to_conversation": "Legg til i gjeldende samtale", + "com_ui_bookmarks_count_selected": "Bokmerker, {{count}} valgt", "com_ui_bookmarks_create_error": "Det oppstod en feil under oppretting av bokmerket.", "com_ui_bookmarks_create_exists": "Dette bokmerket finnes allerede.", "com_ui_bookmarks_create_success": "Bokmerket ble opprettet.", @@ -719,52 +791,88 @@ "com_ui_bookmarks_title": "Tittel", "com_ui_bookmarks_update_error": "Det oppstod en feil under oppdatering av bokmerket.", "com_ui_bookmarks_update_success": "Bokmerket ble oppdatert.", + "com_ui_branch_created": "Oppretting av gren vellykket", + "com_ui_branch_error": "Kunne ikke opprette gren", + "com_ui_branch_message": "Lag en gren fra denne responsen", + "com_ui_by_author": "av {{0}}", "com_ui_callback_url": "Tilbakekallings-URL", "com_ui_cancel": "Avbryt", "com_ui_cancelled": "Avbrutt", "com_ui_category": "Kategori", + "com_ui_change_version": "Endre versjon", "com_ui_chat": "Samtale", "com_ui_chat_history": "Samtalehistorikk", + "com_ui_chats": "Samtaler", + "com_ui_check_internet": "Sjekk din internettforbindelse", "com_ui_clear": "Fjern", "com_ui_clear_all": "Fjern alle", + "com_ui_clear_browser_cache": "Tøm nettleserbufferen", + "com_ui_clear_presets": "Tøm forhåndsinnstillinger", + "com_ui_clear_search": "Tøm søk", + "com_ui_click_to_close": "Klikk her for å lukke", + "com_ui_click_to_view_var": "Klikk her for å se {{0}}", "com_ui_client_id": "Klient-ID", "com_ui_client_secret": "Klienthemmelighet", "com_ui_close": "Lukk", "com_ui_close_menu": "Lukk meny", + "com_ui_close_settings": "Lukk innstillinger", + "com_ui_close_var": "Lukk {{0}}", "com_ui_close_window": "Lukk vindu", "com_ui_code": "Kode", + "com_ui_collapse": "Skjul", "com_ui_collapse_chat": "Skjul samtale", + "com_ui_collapse_thoughts": "Skjul tanker", "com_ui_command_placeholder": "Valgfritt: Skriv inn en kommando for prompten, ellers vil navnet bli brukt.", "com_ui_command_usage_placeholder": "Velg en prompt med kommando eller navn.", "com_ui_complete_setup": "Fullfør oppsett", "com_ui_concise": "Kortfattet", + "com_ui_configure": "Konfigurer", "com_ui_configure_mcp_variables_for": "Konfigurer variabler for {{0}}", "com_ui_confirm": "Bekreft", "com_ui_confirm_action": "Bekreft handling", "com_ui_confirm_admin_use_change": "Endring av denne innstillingen vil blokkere tilgang for administratorer, inkludert deg selv. Er du sikker på at du vil fortsette?", "com_ui_confirm_change": "Bekreft endring", "com_ui_connecting": "Kobler til", + "com_ui_contact_admin_if_issue_persists": "Kontakt en adiministrator dersom problemet vedvarer", "com_ui_context": "Kontekst", + "com_ui_context_filter_sort": "Filtrer og sortér etter kontekst", "com_ui_continue": "Fortsett", "com_ui_continue_oauth": "Fortsett med OAuth", + "com_ui_control_bar": "Kontroller bar", "com_ui_controls": "Kontroller", + "com_ui_conversation": "samtale", + "com_ui_conversation_label": "{{tittel}} samtale", + "com_ui_conversations": "samtaler", + "com_ui_convo_archived": "Samtale arkivert", "com_ui_convo_delete_error": "Sletting av samtale mislyktes.", + "com_ui_convo_delete_success": "Sletting av samtale vellykket", "com_ui_copied": "Kopiert!", "com_ui_copied_to_clipboard": "Kopiert til utklippstavlen", + "com_ui_copy": "Kopier", "com_ui_copy_code": "Kopier kode", "com_ui_copy_link": "Kopier lenke", + "com_ui_copy_stack_trace": "Kopier stack trace", + "com_ui_copy_thoughts_to_clipboard": "Kopier tanker til utklippstavle", "com_ui_copy_to_clipboard": "Kopier til utklippstavlen", "com_ui_copy_url_to_clipboard": "Kopier URL til utklippstavlen", "com_ui_create": "Opprett", + "com_ui_create_api_key": "Opprett API-nøkkel", + "com_ui_create_assistant": "Lag assistent", "com_ui_create_link": "Opprett lenke", + "com_ui_create_mcp_server": "Lag MCP-server", "com_ui_create_memory": "Opprett minne", + "com_ui_create_new_agent": "L", "com_ui_create_prompt": "Opprett prompt", + "com_ui_create_prompt_page": "ag ", + "com_ui_created": "Opprettet", + "com_ui_creating": "Oppretter...", "com_ui_creating_image": "Oppretter bilde. Dette kan ta et øyeblikk.", "com_ui_current": "Gjeldende", "com_ui_currently_production": "For øyeblikket i produksjon", "com_ui_custom": "Egendefinert", "com_ui_custom_header_name": "Egendefinert overskriftsnavn", "com_ui_custom_prompt_mode": "Egendefinert prompt-modus", + "com_ui_dark_theme_enabled": "Mørkt tema aktivert", "com_ui_dashboard": "Oversikt", "com_ui_date": "Dato", "com_ui_date_april": "April", @@ -781,6 +889,7 @@ "com_ui_date_previous_30_days": "Siste 30 dager", "com_ui_date_previous_7_days": "Siste 7 dager", "com_ui_date_september": "September", + "com_ui_date_sort": "Sorter etter dato", "com_ui_date_today": "I dag", "com_ui_date_yesterday": "I går", "com_ui_decline": "Jeg godtar ikke", @@ -788,19 +897,30 @@ "com_ui_delete": "Slett", "com_ui_delete_action": "Slett handling", "com_ui_delete_action_confirm": "Er du sikker på at du vil slette denne handlingen?", + "com_ui_delete_agent": "Slett agent", "com_ui_delete_agent_confirm": "Er du sikker på at du vil slette denne agenten?", + "com_ui_delete_assistant": "Slett assistent", "com_ui_delete_assistant_confirm": "Er du sikker på at du vil slette denne assistenten? Dette kan ikke angres.", "com_ui_delete_confirm": "Dette vil slette", "com_ui_delete_confirm_prompt_version_var": "Dette vil slette den valgte versjonen for \"{{0}}\". Hvis ingen andre versjoner eksisterer, vil prompten bli slettet.", + "com_ui_delete_confirm_strong": "Dette vil slette {{title}}", "com_ui_delete_conversation": "Slette samtalen?", + "com_ui_delete_conversation_tooltip": "Slett samtale", + "com_ui_delete_mcp_server": "Ønsker du å slette MCP-serveren?", + "com_ui_delete_mcp_server_name": "Slett MCP-server {{0}}", "com_ui_delete_memory": "Slett minne", "com_ui_delete_not_allowed": "Sletteoperasjon er ikke tillatt.", + "com_ui_delete_preset": "Ønsker du å slette forhåndsinnstillingen", "com_ui_delete_prompt": "Slette prompten?", + "com_ui_delete_prompt_name": "Slett prompt - {{name}}", "com_ui_delete_shared_link": "Slette delt lenke?", + "com_ui_delete_shared_link_heading": "Slett delt lenke", "com_ui_delete_success": "Vellykket slettet", "com_ui_delete_tool": "Slett verktøy", "com_ui_delete_tool_confirm": "Er du sikker på at du vil slette dette verktøyet?", + "com_ui_delete_tool_save_reminder": "Verktøy fjernet. Lagre agenten for å ta i bruk endreinger.", "com_ui_deleted": "Slettet", + "com_ui_deleting": "Sletter...", "com_ui_deleting_file": "Sletter fil ...", "com_ui_descending": "Synkende", "com_ui_description": "Beskrivelse", @@ -808,37 +928,52 @@ "com_ui_deselect_all": "Fravelg alle", "com_ui_detailed": "Detaljert", "com_ui_disabling": "Deaktiverer ...", + "com_ui_done": "Ferdig", "com_ui_download": "Last ned", "com_ui_download_artifact": "Last ned artefakt", "com_ui_download_backup": "Last ned reservekoder", "com_ui_download_backup_tooltip": "Før du fortsetter, last ned reservekodene dine. Du vil trenge dem for å få tilgang igjen hvis du mister autentiseringsenheten din.", "com_ui_download_error": "Feil ved nedlasting av fil. Filen kan ha blitt slettet.", + "com_ui_download_error_logs": "Last ned feillogger", "com_ui_drag_drop": "Dra og slipp fil(er) her, eller klikk for å velge.", "com_ui_dropdown_variables": "Nedtrekksvariabler:", "com_ui_dropdown_variables_info": "Opprett egendefinerte nedtrekksmenyer for promptene dine: `{{variabelnavn:valg1|valg2|valg3}}`", "com_ui_duplicate": "Dupliser", + "com_ui_duplicate_agent": "Dupliser Agent", "com_ui_duplication_error": "Det oppstod en feil under duplisering av samtalen.", "com_ui_duplication_processing": "Dupliserer samtale ...", "com_ui_duplication_success": "Samtalen ble duplisert.", "com_ui_edit": "Rediger", "com_ui_edit_editing_image": "Redigerer bilde", "com_ui_edit_mcp_server": "Rediger MCP-server", + "com_ui_edit_mcp_server_dialog_description": "Unik Serveridentifikator: {{serverName}}", "com_ui_edit_memory": "Rediger minne", + "com_ui_edit_preset_title": "Rediger forhåndsinnstilling - {{title}}", + "com_ui_edit_prompt_page": "Rediger promptside", + "com_ui_editable_message": "Redigerbar melding", + "com_ui_editor_instructions": "Dra bildet for å flytte • Bruk zoom slider eller knapper for å justere størrelse", "com_ui_empty_category": "-", "com_ui_endpoint": "Endepunkt", "com_ui_endpoint_menu": "LLM-endepunktmeny", "com_ui_enter": "Enter", "com_ui_enter_api_key": "Skriv inn API-nøkkel", + "com_ui_enter_description": "Angi beskrivelse (valgfritt)", "com_ui_enter_key": "Skriv inn nøkkel", + "com_ui_enter_name": "Angi navn", "com_ui_enter_openapi_schema": "Skriv inn ditt OpenAPI-skjema her.", "com_ui_enter_value": "Skriv inn verdi", "com_ui_error": "Feil", "com_ui_error_connection": "Feil ved tilkobling til serveren, prøv å laste siden på nytt.", + "com_ui_error_message_prefix": "Feilmelding:", "com_ui_error_save_admin_settings": "Det oppstod en feil under lagring av admin-innstillingene.", + "com_ui_error_try_following_prefix": "Vennligst prøv en av de følgende", + "com_ui_error_unexpected": "Oops! Noe uforventet skjedde", "com_ui_error_updating_preferences": "Feil ved oppdatering av preferanser.", "com_ui_everyone_permission_level": "Alles tillatelsesnivå", "com_ui_examples": "Eksempler", + "com_ui_expand": "Utvid", "com_ui_expand_chat": "Utvid samtale", + "com_ui_expand_thoughts": "Utvidede tanker", "com_ui_export_convo_modal": "Eksporter samtale-modal", "com_ui_feedback_more": "Mer ...", "com_ui_feedback_more_information": "Gi ytterligere tilbakemelding", @@ -858,10 +993,12 @@ "com_ui_feedback_tag_unjustified_refusal": "Nektet uten grunn", "com_ui_field_max_length": "{{field}} må inneholde mindre enn {{length}} tegn", "com_ui_field_required": "Dette feltet er påkrevd.", + "com_ui_file_input_avatar_label": "Filinput for avatar", "com_ui_file_size": "Filstørrelse", "com_ui_file_token_limit": "Tokengrense for filer", "com_ui_file_token_limit_desc": "Angir maksimalt antall tokens som kan benyttes for filhåndtering. En høyere grense kan øke behandlingstid og kostnader.", "com_ui_files": "Filer", + "com_ui_filter_mcp_servers": "Filtrer MCP-servere etter navn", "com_ui_filter_prompts": "Filtrer prompter", "com_ui_filter_prompts_name": "Filtrer prompter etter navn", "com_ui_final_touch": "Siste finpuss", @@ -885,6 +1022,7 @@ "com_ui_fork_info_visible": "Dette alternativet forgrener kun de synlige meldingene, altså den direkte stien til målmeldingen, uten noen grener.", "com_ui_fork_more_details_about": "Se tilleggsinformasjon om forgrening-alternativet «{{0}}»", "com_ui_fork_more_info_options": "Se detaljert forklaring av alle forgrening-alternativer.", + "com_ui_fork_open_menu": "Åpne forgreningsmeny", "com_ui_fork_processing": "Forgrener samtale ...", "com_ui_fork_remember": "Husk", "com_ui_fork_remember_checked": "Ditt valg vil bli husket. Endre dette når som helst i innstillingene.", @@ -903,7 +1041,11 @@ "com_ui_good_evening": "God kveld", "com_ui_good_morning": "God morgen", "com_ui_group": "Gruppe", + "com_ui_handoff_instructions": "Overleveringsinstruksjoner", "com_ui_happy_birthday": "Det er min første bursdag!", + "com_ui_header_format": "Overskriftsformat", + "com_ui_hide": "Skjul", + "com_ui_hide_code": "Skjul kode", "com_ui_hide_image_details": "Skjul bildedetaljer", "com_ui_hide_password": "Skjul passord", "com_ui_hide_qr": "Skjul QR-kode", @@ -920,18 +1062,26 @@ "com_ui_import_conversation_file_type_error": "Importtypen støttes ikke.", "com_ui_import_conversation_info": "Importer samtaler fra en JSON-fil.", "com_ui_import_conversation_success": "Samtalene ble importert.", + "com_ui_import_conversation_upload_error": "Feil under opplasting av fil. Vennligst prøv igjen.", + "com_ui_importing": "Importerer", "com_ui_include_shadcnui": "Inkluder instruksjoner for shadcn/ui-komponenter", "com_ui_initializing": "Initialiserer...", "com_ui_input": "Inndata", "com_ui_instructions": "Instruksjoner", "com_ui_key": "Nøkkel", + "com_ui_key_required": "API-nøkkel påkrevd", + "com_ui_last_used": "Sist brukt", "com_ui_late_night": "God senkveld", "com_ui_latest_footer": "Én KI for alle.", "com_ui_latest_production_version": "Siste produksjonsversjon", "com_ui_latest_version": "Siste versjon", + "com_ui_leave_blank_to_keep": "La stå tomt for å beholde eksisterende", "com_ui_librechat_code_api_key": "Få din LibreChat Kodetolk API-nøkkel", "com_ui_librechat_code_api_subtitle": "Sikker. Flerspråklig. Fil-input/output.", "com_ui_librechat_code_api_title": "Kjør KI-kode", + "com_ui_light_theme_enabled": "Lyst tema aktivert", + "com_ui_link_copied": "Lenke kopiert", + "com_ui_link_refreshed": "Lenken er oppdatert", "com_ui_loading": "Laster ...", "com_ui_locked": "Låst", "com_ui_logo": "{{0}}-logo", @@ -939,8 +1089,12 @@ "com_ui_manage": "Administrer", "com_ui_marketplace": "Markedsplass", "com_ui_marketplace_allow_use": "Tillat bruk av markedsplass", + "com_ui_max": "Maks", + "com_ui_max_favorites_reached": "Maksimalt antall festede gjenstander nådd ({{0}}). Fjern noen gjenstander for å legge til flere.", + "com_ui_max_file_size": "PNG, JPG eller JPEG (maks {{0}})", "com_ui_max_tags": "Maksimalt antall er {{0}}. Bruker siste verdier.", "com_ui_mcp_authenticated_success": "MCP-serveren '{{0}}' ble autentisert.", + "com_ui_mcp_click_to_defer": "Klikk for å utsette – verktøyet vil være synlig via søk, men ikke lastet inn før det trengs", "com_ui_mcp_configure_server": "Konfigurer {{0}}", "com_ui_mcp_configure_server_description": "Konfigurer egendefinerte variabler for {{0}}", "com_ui_mcp_enter_var": "Skriv inn verdi for {{0}}", From e710a12bfb0891a3b14f90c0f4763751eb12f415 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 17 Feb 2026 00:53:22 -0500 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=AA=86=20refactor:=20Internalize=20Pr?= =?UTF-8?q?oducer=20Event=20Handling=20into=20Agent=20Graph=20Context=20(#?= =?UTF-8?q?11816)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 refactor: Simplify Event Handling with Consumer Callbacks only Removed direct handling of tool calls from the ModelEndHandler and using ChatModelStreamHandler outside of graph contexts, as are now managed within the graph execution context to maintain it as a producer of events, and the model end handler as a consumer. This change eliminates potential race conditions and streamlines the processing of model end events. /** * handleToolCalls is now called from within the graph execution context * (Graph.createCallModel, after attemptInvoke) rather than here in the * stream consumer. This eliminates the race condition where ToolNode * could read toolCallStepIds before this handler had populated it, * since the stream consumer and graph execution run concurrently. */ * 📦 chore: Update `@librechat/agents` to v3.1.50 --- api/package.json | 2 +- api/server/controllers/agents/callbacks.js | 63 +--------------------- package-lock.json | 10 ++-- packages/api/package.json | 2 +- 4 files changed, 9 insertions(+), 68 deletions(-) diff --git a/api/package.json b/api/package.json index fa06fc0432..bc212227d3 100644 --- a/api/package.json +++ b/api/package.json @@ -44,7 +44,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.45", + "@librechat/agents": "^3.1.50", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", diff --git a/api/server/controllers/agents/callbacks.js b/api/server/controllers/agents/callbacks.js index 163fc4ebba..0bb935795d 100644 --- a/api/server/controllers/agents/callbacks.js +++ b/api/server/controllers/agents/callbacks.js @@ -1,22 +1,13 @@ const { nanoid } = require('nanoid'); -const { Constants } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); +const { Constants, EnvVar, GraphEvents, ToolEndHandler } = require('@librechat/agents'); +const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider'); const { sendEvent, GenerationJobManager, writeAttachmentEvent, createToolExecuteHandler, } = require('@librechat/api'); -const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider'); -const { - EnvVar, - Providers, - GraphEvents, - getMessageId, - ToolEndHandler, - handleToolCalls, - ChatModelStreamHandler, -} = require('@librechat/agents'); const { processFileCitations } = require('~/server/services/Files/Citations'); const { processCodeOutput } = require('~/server/services/Files/Code/process'); const { loadAuthValues } = require('~/server/services/Tools/credentials'); @@ -57,8 +48,6 @@ class ModelEndHandler { let errorMessage; try { const agentContext = graph.getAgentContext(metadata); - const isGoogle = agentContext.provider === Providers.GOOGLE; - const streamingDisabled = !!agentContext.clientOptions?.disableStreaming; if (data?.output?.additional_kwargs?.stop_reason === 'refusal') { const info = { ...data.output.additional_kwargs }; errorMessage = JSON.stringify({ @@ -73,21 +62,6 @@ class ModelEndHandler { }); } - const toolCalls = data?.output?.tool_calls; - let hasUnprocessedToolCalls = false; - if (Array.isArray(toolCalls) && toolCalls.length > 0 && graph?.toolCallStepIds?.has) { - try { - hasUnprocessedToolCalls = toolCalls.some( - (tc) => tc?.id && !graph.toolCallStepIds.has(tc.id), - ); - } catch { - hasUnprocessedToolCalls = false; - } - } - if (isGoogle || streamingDisabled || hasUnprocessedToolCalls) { - await handleToolCalls(toolCalls, metadata, graph); - } - const usage = data?.output?.usage_metadata; if (!usage) { return this.finalize(errorMessage); @@ -98,38 +72,6 @@ class ModelEndHandler { } this.collectedUsage.push(usage); - if (!streamingDisabled) { - return this.finalize(errorMessage); - } - if (!data.output.content) { - return this.finalize(errorMessage); - } - const stepKey = graph.getStepKey(metadata); - const message_id = getMessageId(stepKey, graph) ?? ''; - if (message_id) { - await graph.dispatchRunStep(stepKey, { - type: StepTypes.MESSAGE_CREATION, - message_creation: { - message_id, - }, - }); - } - const stepId = graph.getStepIdByKey(stepKey); - const content = data.output.content; - if (typeof content === 'string') { - await graph.dispatchMessageDelta(stepId, { - content: [ - { - type: 'text', - text: content, - }, - ], - }); - } else if (content.every((c) => c.type?.startsWith('text'))) { - await graph.dispatchMessageDelta(stepId, { - content, - }); - } } catch (error) { logger.error('Error handling model end event:', error); return this.finalize(errorMessage); @@ -200,7 +142,6 @@ function getDefaultHandlers({ const handlers = { [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage), [GraphEvents.TOOL_END]: new ToolEndHandler(toolEndCallback, logger), - [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(), [GraphEvents.ON_RUN_STEP]: { /** * Handle ON_RUN_STEP event. diff --git a/package-lock.json b/package-lock.json index b1af66176a..b3c2b41593 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.45", + "@librechat/agents": "^3.1.50", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", @@ -11208,9 +11208,9 @@ } }, "node_modules/@librechat/agents": { - "version": "3.1.45", - "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.1.45.tgz", - "integrity": "sha512-izapt5PScVF52xhDH5N5uzCbjK1BMaGsUiKFNVeO20AjDWul+ipDm5zWmXnfX4veD8YHqY5rPnRU0oAecljZIg==", + "version": "3.1.50", + "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.1.50.tgz", + "integrity": "sha512-+gdfUJ7X3PJ20/c+8lETY68D6QpxFlCIlGUQBF4A8VKv+Po9J/TO5rWE+OmzmPByYpye7GrcxVCBLfRTvZKraw==", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.73.0", @@ -42205,7 +42205,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.45", + "@librechat/agents": "^3.1.50", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.26.0", "@smithy/node-http-handler": "^4.4.5", diff --git a/packages/api/package.json b/packages/api/package.json index 5ee9e3bb70..107a660315 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -87,7 +87,7 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.45", + "@librechat/agents": "^3.1.50", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.26.0", "@smithy/node-http-handler": "^4.4.5", From 0697e8cd6032ad61089d683564d5298805cbbdd5 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 17 Feb 2026 15:24:03 -0500 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=A4=96=20feat:=20Claude=20Sonnet=204.?= =?UTF-8?q?6=20support=20(#11829)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🤖 feat: Claude Sonnet 4.6 support - Updated .env.example to include claude-sonnet-4-6 in the list of available models. - Enhanced token value assignments in api/models/tx.js and packages/api/src/utils/tokens.ts to accommodate claude-sonnet-4-6. - Added tests in packages/data-provider/specs/bedrock.spec.ts to verify support for claude-sonnet-4-6 in adaptive thinking and context-1m functionalities. - Modified bedrock.ts to correctly parse and identify the version of claude-sonnet-4-6 for adaptive thinking checks. - Included claude-sonnet-4-6 in sharedAnthropicModels and bedrockModels for consistent model availability. * chore: additional Claude Sonnet 4.6 tests - Added unit tests for Claude Sonnet 4.6 in `tokens.spec.js` to verify context length and max output tokens. - Updated `helpers.ts` documentation to reflect adaptive thinking support for Sonnet 4.6. - Enhanced `llm.spec.ts` with tests for context headers and adaptive thinking configurations for Claude Sonnet 4.6. - Improved `bedrock.spec.ts` to ensure correct parsing and handling of Claude Sonnet 4.6 model variations with adaptive thinking. --- .env.example | 6 +- api/models/tx.js | 3 + api/utils/tokens.spec.js | 50 ++++++++++++ .../api/src/endpoints/anthropic/helpers.ts | 2 +- .../api/src/endpoints/anthropic/llm.spec.ts | 78 +++++++++++++++++++ packages/api/src/utils/tokens.ts | 2 + packages/data-provider/specs/bedrock.spec.ts | 66 +++++++++++++++- packages/data-provider/src/bedrock.ts | 25 +++--- packages/data-provider/src/config.ts | 2 + 9 files changed, 218 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index ec848c72a0..a6ff6157ce 100644 --- a/.env.example +++ b/.env.example @@ -135,7 +135,7 @@ PROXY= #============# ANTHROPIC_API_KEY=user_provided -# ANTHROPIC_MODELS=claude-opus-4-6,claude-opus-4-20250514,claude-sonnet-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307 +# ANTHROPIC_MODELS=claude-sonnet-4-6,claude-opus-4-6,claude-opus-4-20250514,claude-sonnet-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307 # ANTHROPIC_REVERSE_PROXY= # Set to true to use Anthropic models through Google Vertex AI instead of direct API @@ -170,8 +170,8 @@ ANTHROPIC_API_KEY=user_provided # BEDROCK_AWS_SESSION_TOKEN=someSessionToken # Note: This example list is not meant to be exhaustive. If omitted, all known, supported model IDs will be included for you. -# BEDROCK_AWS_MODELS=anthropic.claude-opus-4-6-v1,anthropic.claude-3-5-sonnet-20240620-v1:0,meta.llama3-1-8b-instruct-v1:0 -# Cross-region inference model IDs: us.anthropic.claude-opus-4-6-v1,global.anthropic.claude-opus-4-6-v1 +# BEDROCK_AWS_MODELS=anthropic.claude-sonnet-4-6,anthropic.claude-opus-4-6-v1,anthropic.claude-3-5-sonnet-20240620-v1:0,meta.llama3-1-8b-instruct-v1:0 +# Cross-region inference model IDs: us.anthropic.claude-sonnet-4-6,us.anthropic.claude-opus-4-6-v1,global.anthropic.claude-opus-4-6-v1 # See all Bedrock model IDs here: https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns diff --git a/api/models/tx.js b/api/models/tx.js index 959c88e2b4..9a6305ec5c 100644 --- a/api/models/tx.js +++ b/api/models/tx.js @@ -176,6 +176,7 @@ const tokenValues = Object.assign( 'claude-opus-4-5': { prompt: 5, completion: 25 }, 'claude-opus-4-6': { prompt: 5, completion: 25 }, 'claude-sonnet-4': { prompt: 3, completion: 15 }, + 'claude-sonnet-4-6': { prompt: 3, completion: 15 }, 'command-r': { prompt: 0.5, completion: 1.5 }, 'command-r-plus': { prompt: 3, completion: 15 }, 'command-text': { prompt: 1.5, completion: 2.0 }, @@ -309,6 +310,7 @@ const cacheTokenValues = { 'claude-3-haiku': { write: 0.3, read: 0.03 }, 'claude-haiku-4-5': { write: 1.25, read: 0.1 }, 'claude-sonnet-4': { write: 3.75, read: 0.3 }, + 'claude-sonnet-4-6': { write: 3.75, read: 0.3 }, 'claude-opus-4': { write: 18.75, read: 1.5 }, 'claude-opus-4-5': { write: 6.25, read: 0.5 }, 'claude-opus-4-6': { write: 6.25, read: 0.5 }, @@ -337,6 +339,7 @@ const cacheTokenValues = { */ const premiumTokenValues = { 'claude-opus-4-6': { threshold: 200000, prompt: 10, completion: 37.5 }, + 'claude-sonnet-4-6': { threshold: 200000, prompt: 6, completion: 22.5 }, }; /** diff --git a/api/utils/tokens.spec.js b/api/utils/tokens.spec.js index 0cfdc30227..18905d6d18 100644 --- a/api/utils/tokens.spec.js +++ b/api/utils/tokens.spec.js @@ -1162,6 +1162,56 @@ describe('Claude Model Tests', () => { expect(matchModelName(model, EModelEndpoint.anthropic)).toBe('claude-opus-4-6'); }); }); + + it('should return correct context length for Claude Sonnet 4.6 (1M)', () => { + expect(getModelMaxTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe( + maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + expect(getModelMaxTokens('claude-sonnet-4-6')).toBe( + maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + }); + + it('should return correct max output tokens for Claude Sonnet 4.6 (64K)', () => { + const { getModelMaxOutputTokens } = require('@librechat/api'); + expect(getModelMaxOutputTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe( + maxOutputTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + }); + + it('should handle Claude Sonnet 4.6 model name variations', () => { + const modelVariations = [ + 'claude-sonnet-4-6', + 'claude-sonnet-4-6-20260101', + 'claude-sonnet-4-6-latest', + 'anthropic/claude-sonnet-4-6', + 'claude-sonnet-4-6/anthropic', + 'claude-sonnet-4-6-preview', + ]; + + modelVariations.forEach((model) => { + const modelKey = findMatchingPattern(model, maxTokensMap[EModelEndpoint.anthropic]); + expect(modelKey).toBe('claude-sonnet-4-6'); + expect(getModelMaxTokens(model, EModelEndpoint.anthropic)).toBe( + maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + }); + }); + + it('should match model names correctly for Claude Sonnet 4.6', () => { + const modelVariations = [ + 'claude-sonnet-4-6', + 'claude-sonnet-4-6-20260101', + 'claude-sonnet-4-6-latest', + 'anthropic/claude-sonnet-4-6', + 'claude-sonnet-4-6/anthropic', + 'claude-sonnet-4-6-preview', + ]; + + modelVariations.forEach((model) => { + expect(matchModelName(model, EModelEndpoint.anthropic)).toBe('claude-sonnet-4-6'); + }); + }); }); describe('Moonshot/Kimi Model Tests', () => { diff --git a/packages/api/src/endpoints/anthropic/helpers.ts b/packages/api/src/endpoints/anthropic/helpers.ts index d9b1c1ccfe..d33116a2ac 100644 --- a/packages/api/src/endpoints/anthropic/helpers.ts +++ b/packages/api/src/endpoints/anthropic/helpers.ts @@ -65,7 +65,7 @@ function getClaudeHeaders( /** * Configures reasoning-related options for Claude models. - * Models supporting adaptive thinking (Opus 4.6+, Sonnet 5+) use effort control instead of manual budget_tokens. + * Models supporting adaptive thinking (Opus 4.6+, Sonnet 4.6+) use effort control instead of manual budget_tokens. */ function configureReasoning( anthropicInput: AnthropicClientOptions & { max_tokens?: number }, diff --git a/packages/api/src/endpoints/anthropic/llm.spec.ts b/packages/api/src/endpoints/anthropic/llm.spec.ts index 7734097a77..b945eacb34 100644 --- a/packages/api/src/endpoints/anthropic/llm.spec.ts +++ b/packages/api/src/endpoints/anthropic/llm.spec.ts @@ -121,6 +121,39 @@ describe('getLLMConfig', () => { }); }); + it('should add "context-1m" beta header for claude-sonnet-4-6 model', () => { + const modelOptions = { + model: 'claude-sonnet-4-6', + promptCache: true, + }; + const result = getLLMConfig('test-key', { modelOptions }); + const clientOptions = result.llmConfig.clientOptions; + expect(clientOptions?.defaultHeaders).toBeDefined(); + expect(clientOptions?.defaultHeaders).toHaveProperty('anthropic-beta'); + const defaultHeaders = clientOptions?.defaultHeaders as Record; + expect(defaultHeaders['anthropic-beta']).toBe('context-1m-2025-08-07'); + expect(result.llmConfig.promptCache).toBe(true); + }); + + it('should add "context-1m" beta header for claude-sonnet-4-6 model formats', () => { + const modelVariations = [ + 'claude-sonnet-4-6', + 'claude-sonnet-4-6-20260101', + 'anthropic/claude-sonnet-4-6', + ]; + + modelVariations.forEach((model) => { + const modelOptions = { model, promptCache: true }; + const result = getLLMConfig('test-key', { modelOptions }); + const clientOptions = result.llmConfig.clientOptions; + expect(clientOptions?.defaultHeaders).toBeDefined(); + expect(clientOptions?.defaultHeaders).toHaveProperty('anthropic-beta'); + const defaultHeaders = clientOptions?.defaultHeaders as Record; + expect(defaultHeaders['anthropic-beta']).toBe('context-1m-2025-08-07'); + expect(result.llmConfig.promptCache).toBe(true); + }); + }); + it('should pass promptCache boolean for claude-opus-4-5 model (no beta header needed)', () => { const modelOptions = { model: 'claude-opus-4-5', @@ -963,6 +996,51 @@ describe('getLLMConfig', () => { }); }); + it('should use adaptive thinking for Sonnet 4.6 instead of enabled + budget_tokens', () => { + const result = getLLMConfig('test-key', { + modelOptions: { + model: 'claude-sonnet-4-6', + thinking: true, + thinkingBudget: 10000, + }, + }); + + expect((result.llmConfig.thinking as unknown as { type: string }).type).toBe('adaptive'); + expect(result.llmConfig.thinking).not.toHaveProperty('budget_tokens'); + expect(result.llmConfig.maxTokens).toBe(64000); + }); + + it('should set effort via output_config for Sonnet 4.6', () => { + const result = getLLMConfig('test-key', { + modelOptions: { + model: 'claude-sonnet-4-6', + thinking: true, + effort: AnthropicEffort.high, + }, + }); + + expect((result.llmConfig.thinking as unknown as { type: string }).type).toBe('adaptive'); + expect(result.llmConfig.invocationKwargs).toHaveProperty('output_config'); + expect(result.llmConfig.invocationKwargs?.output_config).toEqual({ + effort: AnthropicEffort.high, + }); + }); + + it('should exclude topP/topK for Sonnet 4.6 with adaptive thinking', () => { + const result = getLLMConfig('test-key', { + modelOptions: { + model: 'claude-sonnet-4-6', + thinking: true, + topP: 0.9, + topK: 40, + }, + }); + + expect((result.llmConfig.thinking as unknown as { type: string }).type).toBe('adaptive'); + expect(result.llmConfig).not.toHaveProperty('topP'); + expect(result.llmConfig).not.toHaveProperty('topK'); + }); + it('should NOT set adaptive thinking or effort for non-adaptive models', () => { const nonAdaptiveModels = [ 'claude-opus-4-5', diff --git a/packages/api/src/utils/tokens.ts b/packages/api/src/utils/tokens.ts index 0f19b6eb55..a824afa489 100644 --- a/packages/api/src/utils/tokens.ts +++ b/packages/api/src/utils/tokens.ts @@ -148,6 +148,7 @@ const anthropicModels = { 'claude-3.5-sonnet-latest': 200000, 'claude-haiku-4-5': 200000, 'claude-sonnet-4': 1000000, + 'claude-sonnet-4-6': 1000000, 'claude-4': 200000, 'claude-opus-4': 200000, 'claude-opus-4-5': 200000, @@ -401,6 +402,7 @@ const anthropicMaxOutputs = { 'claude-3-opus': 4096, 'claude-haiku-4-5': 64000, 'claude-sonnet-4': 64000, + 'claude-sonnet-4-6': 64000, 'claude-opus-4': 32000, 'claude-opus-4-5': 64000, 'claude-opus-4-6': 128000, diff --git a/packages/data-provider/specs/bedrock.spec.ts b/packages/data-provider/specs/bedrock.spec.ts index ead41b47fa..55bd0a2e08 100644 --- a/packages/data-provider/specs/bedrock.spec.ts +++ b/packages/data-provider/specs/bedrock.spec.ts @@ -46,6 +46,30 @@ describe('supportsAdaptiveThinking', () => { expect(supportsAdaptiveThinking('claude-opus-4-0')).toBe(false); }); + test('should return true for claude-sonnet-4-6', () => { + expect(supportsAdaptiveThinking('claude-sonnet-4-6')).toBe(true); + }); + + test('should return true for claude-sonnet-4.6', () => { + expect(supportsAdaptiveThinking('claude-sonnet-4.6')).toBe(true); + }); + + test('should return true for claude-sonnet-4-7 (future)', () => { + expect(supportsAdaptiveThinking('claude-sonnet-4-7')).toBe(true); + }); + + test('should return true for anthropic.claude-sonnet-4-6 (Bedrock)', () => { + expect(supportsAdaptiveThinking('anthropic.claude-sonnet-4-6')).toBe(true); + }); + + test('should return true for us.anthropic.claude-sonnet-4-6 (cross-region Bedrock)', () => { + expect(supportsAdaptiveThinking('us.anthropic.claude-sonnet-4-6')).toBe(true); + }); + + test('should return true for claude-4-6-sonnet (alternate naming)', () => { + expect(supportsAdaptiveThinking('claude-4-6-sonnet')).toBe(true); + }); + test('should return false for claude-sonnet-4-5', () => { expect(supportsAdaptiveThinking('claude-sonnet-4-5')).toBe(false); }); @@ -104,6 +128,14 @@ describe('supportsContext1m', () => { expect(supportsContext1m('claude-sonnet-4-5')).toBe(true); }); + test('should return true for claude-sonnet-4-6', () => { + expect(supportsContext1m('claude-sonnet-4-6')).toBe(true); + }); + + test('should return true for anthropic.claude-sonnet-4-6 (Bedrock)', () => { + expect(supportsContext1m('anthropic.claude-sonnet-4-6')).toBe(true); + }); + test('should return true for claude-sonnet-5 (future)', () => { expect(supportsContext1m('claude-sonnet-5')).toBe(true); }); @@ -237,14 +269,42 @@ describe('bedrockInputParser', () => { ]); }); - test('should match anthropic.claude-4-7-sonnet model with 1M context header', () => { + test('should match anthropic.claude-sonnet-4-6 with adaptive thinking and 1M context header', () => { + const input = { + model: 'anthropic.claude-sonnet-4-6', + }; + const result = bedrockInputParser.parse(input) as Record; + const additionalFields = result.additionalModelRequestFields as Record; + expect(additionalFields.thinking).toEqual({ type: 'adaptive' }); + expect(additionalFields.thinkingBudget).toBeUndefined(); + expect(additionalFields.anthropic_beta).toEqual([ + 'output-128k-2025-02-19', + 'context-1m-2025-08-07', + ]); + }); + + test('should match us.anthropic.claude-sonnet-4-6 with adaptive thinking and 1M context header', () => { + const input = { + model: 'us.anthropic.claude-sonnet-4-6', + }; + const result = bedrockInputParser.parse(input) as Record; + const additionalFields = result.additionalModelRequestFields as Record; + expect(additionalFields.thinking).toEqual({ type: 'adaptive' }); + expect(additionalFields.thinkingBudget).toBeUndefined(); + expect(additionalFields.anthropic_beta).toEqual([ + 'output-128k-2025-02-19', + 'context-1m-2025-08-07', + ]); + }); + + test('should match anthropic.claude-4-7-sonnet model with adaptive thinking and 1M context header', () => { const input = { model: 'anthropic.claude-4-7-sonnet', }; const result = bedrockInputParser.parse(input) as Record; const additionalFields = result.additionalModelRequestFields as Record; - expect(additionalFields.thinking).toBe(true); - expect(additionalFields.thinkingBudget).toBe(2000); + expect(additionalFields.thinking).toEqual({ type: 'adaptive' }); + expect(additionalFields.thinkingBudget).toBeUndefined(); expect(additionalFields.anthropic_beta).toEqual([ 'output-128k-2025-02-19', 'context-1m-2025-08-07', diff --git a/packages/data-provider/src/bedrock.ts b/packages/data-provider/src/bedrock.ts index a037245fc0..54f5c8b23f 100644 --- a/packages/data-provider/src/bedrock.ts +++ b/packages/data-provider/src/bedrock.ts @@ -35,27 +35,34 @@ function parseOpusVersion(model: string): { major: number; minor: number } | nul return null; } -/** Extracts sonnet major version from both naming formats */ -function parseSonnetVersion(model: string): number | null { - const nameFirst = model.match(/claude-sonnet[-.]?(\d+)/); +/** Extracts sonnet major/minor version from both naming formats. + * Uses single-digit minor capture to avoid matching date suffixes (e.g., -20250514). */ +function parseSonnetVersion(model: string): { major: number; minor: number } | null { + const nameFirst = model.match(/claude-sonnet[-.]?(\d+)(?:[-.](\d)(?!\d))?/); if (nameFirst) { - return parseInt(nameFirst[1], 10); + return { + major: parseInt(nameFirst[1], 10), + minor: nameFirst[2] != null ? parseInt(nameFirst[2], 10) : 0, + }; } - const numFirst = model.match(/claude-(\d+)(?:[-.]?\d+)?-sonnet/); + const numFirst = model.match(/claude-(\d+)(?:[-.](\d)(?!\d))?-sonnet/); if (numFirst) { - return parseInt(numFirst[1], 10); + return { + major: parseInt(numFirst[1], 10), + minor: numFirst[2] != null ? parseInt(numFirst[2], 10) : 0, + }; } return null; } -/** Checks if a model supports adaptive thinking (Opus 4.6+, Sonnet 5+) */ +/** Checks if a model supports adaptive thinking (Opus 4.6+, Sonnet 4.6+) */ export function supportsAdaptiveThinking(model: string): boolean { const opus = parseOpusVersion(model); if (opus && (opus.major > 4 || (opus.major === 4 && opus.minor >= 6))) { return true; } const sonnet = parseSonnetVersion(model); - if (sonnet != null && sonnet >= 5) { + if (sonnet != null && (sonnet.major > 4 || (sonnet.major === 4 && sonnet.minor >= 6))) { return true; } return false; @@ -64,7 +71,7 @@ export function supportsAdaptiveThinking(model: string): boolean { /** Checks if a model qualifies for the context-1m beta header (Sonnet 4+, Opus 4.6+, Opus 5+) */ export function supportsContext1m(model: string): boolean { const sonnet = parseSonnetVersion(model); - if (sonnet != null && sonnet >= 4) { + if (sonnet != null && sonnet.major >= 4) { return true; } const opus = parseOpusVersion(model); diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 02174b6496..c730e3e8fb 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -1133,6 +1133,7 @@ const sharedOpenAIModels = [ ]; const sharedAnthropicModels = [ + 'claude-sonnet-4-6', 'claude-opus-4-6', 'claude-sonnet-4-5', 'claude-sonnet-4-5-20250929', @@ -1154,6 +1155,7 @@ const sharedAnthropicModels = [ ]; export const bedrockModels = [ + 'anthropic.claude-sonnet-4-6', 'anthropic.claude-opus-4-6-v1', 'anthropic.claude-sonnet-4-5-20250929-v1:0', 'anthropic.claude-haiku-4-5-20251001-v1:0',