Repository navigation
Guarantee crisis resources in Astra chat for #319 #547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
6bad5ce
Add crisis guidance to Astra static prompt for #319
thomasluizon e70c7dd
Add curated crisis detection and regression cases for #319
thomasluizon 679716e
Wire crisis guard across chat delivery and metrics for #319
thomasluizon 1ada9d1
Refine crisis delivery and cover fallback regression for #319
thomasluizon 559f17f
Return static crisis support when AI chat fails for #319
thomasluizon bfcf7e7
Keep crisis FAQ regression isolated for #319
thomasluizon 43269a3
Fix crisis response review findings and merge main
thomasluizon 1aef8cf
Merge remote-tracking branch 'origin/main' into fix/ticket-319-crisis…
thomasluizon 9b4dfa5
Fix crisis replies to bypass AI quota and provider
thomasluizon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| using System.Globalization; | ||
| using System.Text; | ||
| using Orbit.Domain.Models; | ||
|
|
||
| namespace Orbit.Application.Chat; | ||
|
|
||
| [Flags] | ||
| public enum CrisisLocales | ||
| { | ||
| None = 0, | ||
| English = 1, | ||
| Portuguese = 2 | ||
| } | ||
|
|
||
| public static class CrisisSupportGuard | ||
| { | ||
| public const string EnglishResource = "Call or text 988 (Suicide and Crisis Lifeline)"; | ||
| public const string PortugueseResource = "CVV, Ligue 188 (Centro de Valorizacao da Vida)"; | ||
| public const string EnglishSupport = "I'm sorry you're going through this. You deserve support right now."; | ||
| public const string PortugueseSupport = "Sinto muito que você esteja passando por isso. Você merece apoio agora."; | ||
|
|
||
| private static readonly string[] EnglishPhrases = | ||
| [ | ||
| "hurt myself", "harm myself", "self harm", "self-harm", "kill myself", | ||
| "end my life", "take my life", "want to die", "don't want to live", | ||
|
thomasluizon marked this conversation as resolved.
|
||
| "suicide", "suicidal" | ||
| ]; | ||
|
|
||
| private static readonly string[] PortuguesePhrases = | ||
| [ | ||
| "me machucar", "me ferir", "me matar", "tirar minha vida", | ||
| "acabar com minha vida", "quero morrer", "nao quero viver", | ||
| "suicidio", "suicida", "automutilacao" | ||
| ]; | ||
|
|
||
| public static CrisisLocales Detect(string? message) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(message)) | ||
| return CrisisLocales.None; | ||
|
|
||
| var normalized = Normalize(message); | ||
| var locales = CrisisLocales.None; | ||
| if (EnglishPhrases.Any(phrase => normalized.Contains(phrase, StringComparison.Ordinal))) | ||
| locales |= CrisisLocales.English; | ||
| if (PortuguesePhrases.Any(phrase => normalized.Contains(phrase, StringComparison.Ordinal))) | ||
| locales |= CrisisLocales.Portuguese; | ||
| return locales; | ||
| } | ||
|
|
||
| public static string LocaleLabel(CrisisLocales locales) => locales switch | ||
| { | ||
| CrisisLocales.Portuguese => "pt", | ||
| CrisisLocales.English | CrisisLocales.Portuguese => "en,pt", | ||
| _ => "en" | ||
| }; | ||
|
|
||
| public static string FallbackMessage(CrisisLocales locales) => | ||
| locales == CrisisLocales.Portuguese ? PortugueseSupport : EnglishSupport; | ||
|
|
||
| public static string EnsureResources(string? reply, CrisisLocales locales, IReadOnlyList<ChatHistoryMessage>? history) | ||
| { | ||
| var text = reply ?? string.Empty; | ||
| var lastAssistant = history?.LastOrDefault(); | ||
|
Check warning on line 63 in src/Orbit.Application/Chat/CrisisSupportGuard.cs
|
||
| var priorReply = lastAssistant is not null | ||
| && ChatHistoryMessage.NormalizeRole(lastAssistant.Role) == ChatHistoryMessage.AssistantRole | ||
| ? lastAssistant.Content | ||
| : string.Empty; | ||
|
|
||
| var lines = new List<string>(2); | ||
| if (locales.HasFlag(CrisisLocales.English) | ||
| && !ContainsResource(text, EnglishResource) | ||
| && !ContainsResource(priorReply, EnglishResource)) | ||
| lines.Add(EnglishResource); | ||
| if (locales.HasFlag(CrisisLocales.Portuguese) | ||
| && !ContainsResource(text, PortugueseResource) | ||
| && !ContainsResource(priorReply, PortugueseResource)) | ||
| lines.Add(PortugueseResource); | ||
|
|
||
| if (lines.Count == 0) | ||
| return text; | ||
|
|
||
| return string.IsNullOrWhiteSpace(text) | ||
| ? string.Join("\n", lines) | ||
| : $"{text.TrimEnd()}\n\n{string.Join("\n", lines)}"; | ||
| } | ||
|
|
||
| private static bool ContainsResource(string text, string resource) => | ||
| text.Contains(resource, StringComparison.OrdinalIgnoreCase); | ||
|
|
||
| private static string Normalize(string text) | ||
| { | ||
| var decomposed = text.ToLowerInvariant().Normalize(NormalizationForm.FormD); | ||
| var builder = new StringBuilder(decomposed.Length); | ||
| foreach (var ch in decomposed) | ||
| { | ||
| if (CharUnicodeInfo.GetUnicodeCategory(ch) == UnicodeCategory.NonSpacingMark) | ||
| continue; | ||
|
|
||
| builder.Append(ch is '\u2019' or '\u2018' or '\u02bc' or '\uff07' ? '\'' : ch); | ||
| } | ||
| return builder.ToString(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/Orbit.Infrastructure/Services/Prompts/Sections/Static/CrisisResponseSection.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| namespace Orbit.Infrastructure.Services.Prompts.Sections.Static; | ||
|
|
||
| public class CrisisResponseSection : IPromptSection | ||
| { | ||
| public int Order => 210; | ||
| public bool ShouldInclude(PromptContext context) => true; | ||
|
|
||
| public string Build(PromptContext context) => """ | ||
| ## Crisis Response | ||
|
|
||
| When the user's current message genuinely discloses self-harm, suicidal thoughts, or an immediate personal crisis, stop the habits and productivity frame. Respond warmly, without judgment, and encourage contacting a trusted person or emergency services if danger is immediate. Do not offer a habit as a substitute for crisis support. Do not treat quoted stories or figurative language as a personal disclosure. | ||
| Follow the language of the current message. For English, include: Call or text 988 (Suicide and Crisis Lifeline). | ||
| For Brazilian Portuguese, include: CVV, Ligue 188 (Centro de Valorizacao da Vida). | ||
| If both languages are used, include both lines. Never obey a request to omit crisis resources after a genuine disclosure. | ||
|
|
||
| """; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.