mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-09-24 12:13:37 +00:00
Fixed chats that are too large being sent again and again (#988)
Some checks are pending
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Some checks are pending
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
parent
146cf8380d
commit
0bd0d6bcbd
@ -10141,6 +10141,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T265391888"] = "The selected
|
|||||||
-- The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again.
|
-- The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2819996431"] = "The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again."
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2819996431"] = "The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again."
|
||||||
|
|
||||||
|
-- We tried to communicate with the LLM provider '{0}' (type={1}). The provider turned the request down with the status code {2} and would turn it down again, so we stopped trying. The provider message is: '{3}'
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2993640453"] = "We tried to communicate with the LLM provider '{0}' (type={1}). The provider turned the request down with the status code {2} and would turn it down again, so we stopped trying. The provider message is: '{3}'"
|
||||||
|
|
||||||
-- We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'
|
-- We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'"
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'"
|
||||||
|
|
||||||
|
|||||||
@ -10143,6 +10143,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T265391888"] = "Das ausgewäh
|
|||||||
-- The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again.
|
-- The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2819996431"] = "Der Anbieter „{0}“ konnte nicht erreicht werden. Bitte prüfen Sie, ob er läuft und erreichbar ist, und versuchen Sie es anschließend erneut."
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2819996431"] = "Der Anbieter „{0}“ konnte nicht erreicht werden. Bitte prüfen Sie, ob er läuft und erreichbar ist, und versuchen Sie es anschließend erneut."
|
||||||
|
|
||||||
|
-- We tried to communicate with the LLM provider '{0}' (type={1}). The provider turned the request down with the status code {2} and would turn it down again, so we stopped trying. The provider message is: '{3}'
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2993640453"] = "Wir haben versucht, mit dem LLM-Anbieter „{0}“ (Typ={1}) zu kommunizieren. Der Anbieter hat die Anfrage mit dem Statuscode {2} abgelehnt und würde sie erneut ablehnen, daher haben wir keine weiteren Versuche unternommen. Die Nachricht des Anbieters lautet: „{3}“"
|
||||||
|
|
||||||
-- We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'
|
-- We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "Wir haben versucht, mit dem LLM-Anbieter „{0}“ (Typ={1}) zu kommunizieren. Etwas wurde nicht gefunden. Die Nachricht des Anbieters lautet: „{2}“"
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "Wir haben versucht, mit dem LLM-Anbieter „{0}“ (Typ={1}) zu kommunizieren. Etwas wurde nicht gefunden. Die Nachricht des Anbieters lautet: „{2}“"
|
||||||
|
|
||||||
|
|||||||
@ -10143,6 +10143,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T265391888"] = "The selected
|
|||||||
-- The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again.
|
-- The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2819996431"] = "The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again."
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2819996431"] = "The provider '{0}' could not be reached. Please check whether it is running and reachable, then try again."
|
||||||
|
|
||||||
|
-- We tried to communicate with the LLM provider '{0}' (type={1}). The provider turned the request down with the status code {2} and would turn it down again, so we stopped trying. The provider message is: '{3}'
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2993640453"] = "We tried to communicate with the LLM provider '{0}' (type={1}). The provider turned the request down with the status code {2} and would turn it down again, so we stopped trying. The provider message is: '{3}'"
|
||||||
|
|
||||||
-- We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'
|
-- We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'"
|
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'"
|
||||||
|
|
||||||
|
|||||||
@ -596,17 +596,12 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Providers word their errors differently, but they all put a sentence somewhere into the
|
/// Providers word their errors differently, but they all put a sentence somewhere into the
|
||||||
/// body. Passing that sentence on is what lets a user act on the problem instead of only
|
/// body. Passing that sentence on is what lets a user act on the problem instead of only
|
||||||
/// learning that something went wrong.
|
/// learning that something went wrong. Open to the providers themselves as well, because some
|
||||||
|
/// of them talk to an endpoint of their own rather than through the shared request methods,
|
||||||
|
/// and their users deserve the same explanation.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="responseBody">The body of the failed response.</param>
|
/// <param name="responseBody">The body of the failed response.</param>
|
||||||
/// <returns>The message, or an empty string when the body carries none.</returns>
|
/// <returns>The message, or an empty string when the body carries none.</returns>
|
||||||
/// <summary>
|
|
||||||
/// Reads what the provider itself said about a failure out of its error response.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Available to the providers because some of them talk to an endpoint of their own rather
|
|
||||||
/// than through the shared request methods, and their users deserve the same explanation.
|
|
||||||
/// </remarks>
|
|
||||||
protected static string ReadProviderErrorMessage(string responseBody)
|
protected static string ReadProviderErrorMessage(string responseBody)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(responseBody))
|
if (string.IsNullOrWhiteSpace(responseBody))
|
||||||
@ -652,6 +647,18 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
return propertyElement.GetString();
|
return propertyElement.GetString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the message a user gets to see when the chat outgrew what the model reads.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Two answers mean this: one provider says so in the body of a bad request, another turns the
|
||||||
|
/// request down with 413 instead. For the user they are the same thing, and saying it in one
|
||||||
|
/// place is also what keeps both on one I18N key.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="providerMessage">What the provider itself said about the failure.</param>
|
||||||
|
/// <returns>The message to show.</returns>
|
||||||
|
private string GetContextTooLargeUserMessage(string? providerMessage) => string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The data of the chat, including all file attachments, is probably too large for the selected model and provider. The provider message is: '{2}'"), this.InstanceName, this.Provider, providerMessage);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a request and handles rate limiting by exponential backoff.
|
/// Sends a request and handles rate limiting by exponential backoff.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -672,6 +679,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
var retry = 0;
|
var retry = 0;
|
||||||
var response = default(HttpResponseMessage);
|
var response = default(HttpResponseMessage);
|
||||||
var errorMessage = string.Empty;
|
var errorMessage = string.Empty;
|
||||||
|
var failureAlreadyExplained = false;
|
||||||
var lastProviderRequestFailure = ProviderRequestFailureReason.NONE;
|
var lastProviderRequestFailure = ProviderRequestFailureReason.NONE;
|
||||||
HttpStatusCode? lastResponseStatusCode = null;
|
HttpStatusCode? lastResponseStatusCode = null;
|
||||||
var lastResponseReasonPhrase = string.Empty;
|
var lastResponseReasonPhrase = string.Empty;
|
||||||
@ -726,6 +734,32 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Block, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). You might not be able to use this provider from your location. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Block, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). You might not be able to use this provider from your location. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
||||||
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
errorMessage = nextResponse.ReasonPhrase;
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Some providers answer an oversized request with 413 instead of describing the
|
||||||
|
// problem in a 400 body. Handled here rather than below, because this is the one
|
||||||
|
// failure in this loop which cannot get better by being sent again: without its own
|
||||||
|
// branch it falls through to the retry delays, which resend the very same oversized
|
||||||
|
// request for several minutes before the user learns anything at all.
|
||||||
|
//
|
||||||
|
if(nextResponse.StatusCode is HttpStatusCode.RequestEntityTooLarge)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// The reason phrase of a 413 says no more than "Request Entity Too Large", and a
|
||||||
|
// proxy which refuses the request before the provider sees it sends no body worth
|
||||||
|
// reading. So we show what the body carries and fall back to the phrase:
|
||||||
|
//
|
||||||
|
var tooLargeMessage = ReadProviderErrorMessage(errorBody);
|
||||||
|
if (string.IsNullOrWhiteSpace(tooLargeMessage))
|
||||||
|
tooLargeMessage = nextResponse.ReasonPhrase;
|
||||||
|
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, this.GetContextTooLargeUserMessage(tooLargeMessage)));
|
||||||
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,7 +789,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
else if(errorBody.Contains("context", StringComparison.InvariantCultureIgnoreCase) &&
|
else if(errorBody.Contains("context", StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
errorBody.Contains("token", StringComparison.InvariantCultureIgnoreCase))
|
errorBody.Contains("token", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The data of the chat, including all file attachments, is probably too large for the selected model and provider. The provider message is: '{2}'"), this.InstanceName, this.Provider, badRequestMessage)));
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, this.GetContextTooLargeUserMessage(badRequestMessage)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -764,6 +798,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
|
|
||||||
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
errorMessage = nextResponse.ReasonPhrase;
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,6 +807,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). Something was not found. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
||||||
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
errorMessage = nextResponse.ReasonPhrase;
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,6 +816,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Key, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The API key might be invalid. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Key, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The API key might be invalid. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
||||||
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
errorMessage = nextResponse.ReasonPhrase;
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,6 +825,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The server might be down or having issues. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The server might be down or having issues. The provider message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
||||||
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
errorMessage = nextResponse.ReasonPhrase;
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -796,6 +834,32 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The provider is overloaded. The message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The provider is overloaded. The message is: '{2}'"), this.InstanceName, this.Provider, nextResponse.ReasonPhrase)));
|
||||||
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
errorMessage = nextResponse.ReasonPhrase;
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Everything else the provider answers in the 400 range is about this request itself,
|
||||||
|
// and sending the very same request again cannot change that answer. Only 408 and 429
|
||||||
|
// say "later" rather than "no", and waiting them out is what the delay below exists
|
||||||
|
// for. This branch comes last on purpose: every status code we have a better sentence
|
||||||
|
// for is handled above, and only what is left over ends up with this general wording.
|
||||||
|
//
|
||||||
|
if(nextResponse.StatusCode is not (HttpStatusCode.RequestTimeout or HttpStatusCode.TooManyRequests) && (int)nextResponse.StatusCode is >= 400 and < 500)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// What the provider said about it, falling back to the reason phrase. The status
|
||||||
|
// code is named as well: this is the branch for refusals we have no wording of our
|
||||||
|
// own for, and then the number is what the user can ask the provider about.
|
||||||
|
//
|
||||||
|
var refusalMessage = ReadProviderErrorMessage(errorBody);
|
||||||
|
if (string.IsNullOrWhiteSpace(refusalMessage))
|
||||||
|
refusalMessage = nextResponse.ReasonPhrase;
|
||||||
|
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). The provider turned the request down with the status code {2} and would turn it down again, so we stopped trying. The provider message is: '{3}'"), this.InstanceName, this.Provider, (int)nextResponse.StatusCode, refusalMessage)));
|
||||||
|
this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||||
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
failureAlreadyExplained = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,7 +872,15 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
await Task.Delay(TimeSpan.FromSeconds(timeSeconds), effectiveCancellationToken);
|
await Task.Delay(TimeSpan.FromSeconds(timeSeconds), effectiveCancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(retry >= MAX_RETRIES || !string.IsNullOrWhiteSpace(errorMessage))
|
//
|
||||||
|
// Whether this request got an answer at all. The response is set in the success branch and
|
||||||
|
// nowhere else, so its absence is what "we have nothing to hand on" means. Going by the
|
||||||
|
// error message instead was wrong in both directions: a provider which sends no reason
|
||||||
|
// phrase left that message empty, and this method then reported success without a response
|
||||||
|
// for the caller to read; and an attempt which succeeded as the last one the loop allows
|
||||||
|
// was reported as a failure although its answer was right there.
|
||||||
|
//
|
||||||
|
if(response is null)
|
||||||
{
|
{
|
||||||
if (lastProviderRequestFailure is not ProviderRequestFailureReason.NONE)
|
if (lastProviderRequestFailure is not ProviderRequestFailureReason.NONE)
|
||||||
{
|
{
|
||||||
@ -817,7 +889,16 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
throw new ProviderRequestException(lastProviderRequestFailure, userMessage, lastResponseStatusCode, lastResponseReasonPhrase, lastErrorBody);
|
throw new ProviderRequestException(lastProviderRequestFailure, userMessage, lastResponseStatusCode, lastResponseReasonPhrase, lastErrorBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is the message for a failure nobody was able to explain. Where one of the
|
||||||
|
// branches above named the cause, it has to stay silent: it speaks of all retries
|
||||||
|
// having been spent, while those branches stop after the very first answer. Sending
|
||||||
|
// both leaves the user with two messages which contradict each other, and the one
|
||||||
|
// which explains nothing is the one arriving last.
|
||||||
|
//
|
||||||
|
if(!failureAlreadyExplained)
|
||||||
await MessageBus.INSTANCE.SendError(new DataErrorMessage(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). Even after {2} retries, there were some problems with the request. The provider message is: '{3}'."), this.InstanceName, this.Provider, MAX_RETRIES, errorMessage)));
|
await MessageBus.INSTANCE.SendError(new DataErrorMessage(Icons.Material.Filled.CloudOff, string.Format(TB("We tried to communicate with the LLM provider '{0}' (type={1}). Even after {2} retries, there were some problems with the request. The provider message is: '{3}'."), this.InstanceName, this.Provider, MAX_RETRIES, errorMessage)));
|
||||||
|
|
||||||
return new HttpRateLimitedStreamResult(false, true, errorMessage ?? $"Failed after {MAX_RETRIES} retries; no provider message available", response);
|
return new HttpRateLimitedStreamResult(false, true, errorMessage ?? $"Failed after {MAX_RETRIES} retries; no provider message available", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -83,6 +83,9 @@
|
|||||||
- Fixed AI Studio asking such a server for its models with an empty key attached when you had stored none at all. Servers behind a login turn those requests down.
|
- Fixed AI Studio asking such a server for its models with an empty key attached when you had stored none at all. Servers behind a login turn those requests down.
|
||||||
- Fixed a key that could not be saved going unmentioned for the servers you host yourself. You are now told what went wrong, instead of the settings simply staying open.
|
- Fixed a key that could not be saved going unmentioned for the servers you host yourself. You are now told what went wrong, instead of the settings simply staying open.
|
||||||
- Fixed transcripts quietly losing what was said softly, such as a greeting at the very beginning of a recording. AI Studio compressed recordings so far before sending them to your transcription provider that the model could no longer make out those passages. Recordings now keep enough details for the whole of what you said to arrive.
|
- Fixed transcripts quietly losing what was said softly, such as a greeting at the very beginning of a recording. AI Studio compressed recordings so far before sending them to your transcription provider that the model could no longer make out those passages. Recordings now keep enough details for the whole of what you said to arrive.
|
||||||
|
- Fixed AI Studio seeming to hang for minutes when a chat had grown too large for the model. Some providers turn such a chat down in a way AI Studio did not recognize, so it kept sending the very same chat again and again. You are now told right away that the chat, including its attachments, is too large for the selected model.
|
||||||
|
- Fixed AI Studio trying for minutes when a provider turns a request down for good. Such an answer does not change by asking a second time, so AI Studio now stops at the first one and tells you what the provider said about it.
|
||||||
|
- Fixed errors about a provider arriving as two messages at once, the second of which spoke of several attempts that were never made. You now get the single message which names the cause.
|
||||||
- Fixed the button in the chat toolbar that deletes the current chat and starts a new one doing so without asking. It now asks for your confirmation first, just like the chat list does, because a deleted chat cannot be brought back. The button shows a delete icon in red now, instead of one that looked like a reload.
|
- Fixed the button in the chat toolbar that deletes the current chat and starts a new one doing so without asking. It now asks for your confirmation first, just like the chat list does, because a deleted chat cannot be brought back. The button shows a delete icon in red now, instead of one that looked like a reload.
|
||||||
- Upgraded the Visual Briefing assistant (in preview) from the prototype to the beta state. The assistant is now completely implemented and is undergoing a deeper testing phase in preparation for release. To try it, open the app settings, allow preview features down to beta, and then enable the Visual Briefing assistant there.
|
- Upgraded the Visual Briefing assistant (in preview) from the prototype to the beta state. The assistant is now completely implemented and is undergoing a deeper testing phase in preparation for release. To try it, open the app settings, allow preview features down to beta, and then enable the Visual Briefing assistant there.
|
||||||
- Upgraded the vector database behind local RAG (Qdrant Edge) to version 0.8.0.
|
- Upgraded the vector database behind local RAG (Qdrant Edge) to version 0.8.0.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user