namespace AIStudio.Provider.Reasoning; /// /// Reading the values a person wrote into their additional API parameters. /// /// /// Every dialect ends up asking the same two questions: is this key there, and does this value mean /// yes or no. The answers are the same whoever asks them -- "off" is off at every provider -- so /// they live here rather than once per dialect. /// public static class ReasoningParameters { /// /// Try to read a parameter, matching the key regardless of how it was capitalized. /// /// The parsed parameter dictionary. /// The parameter name to find. /// The matched parameter value, if found. /// True, when a matching key was found. public static bool TryGet(IDictionary parameters, string key, out object? value) { value = null; if (parameters.Count is 0) return false; var foundKey = parameters.Keys.FirstOrDefault(candidate => string.Equals(candidate, key, StringComparison.OrdinalIgnoreCase)); if (foundKey is null) return false; value = parameters[foundKey]; return true; } /// /// Reads a value which is written as a boolean, a number, or a level. /// /// The raw parsed parameter value. /// What the value says. public static ReasoningConfigurationState LevelOf(object? value) => value switch { bool booleanValue => booleanValue ? ReasoningConfigurationState.EXPLICITLY_ENABLED : ReasoningConfigurationState.EXPLICITLY_DISABLED, int i => i is 0 ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, long l => l is 0 ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, double d => Math.Abs(d) < double.Epsilon ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, decimal m => m is 0 ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, string text when IsDisabledText(text) => ReasoningConfigurationState.EXPLICITLY_DISABLED, string text when IsEnabledText(text) => ReasoningConfigurationState.EXPLICITLY_ENABLED, _ => ReasoningConfigurationState.NOT_CONFIGURED, }; /// /// Reads a token budget, which several providers use to say the same thing with a number. /// /// /// A budget of zero switches thinking off. Everything else, negative budgets included, leaves it /// available -- a negative one usually means "as much as it takes". /// /// The configured budget value. /// What the budget says. public static ReasoningConfigurationState BudgetOf(object? value) => value switch { int i => i is 0 ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, long l => l is 0 ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, double d => Math.Abs(d) < double.Epsilon ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, decimal m => m is 0 ? ReasoningConfigurationState.EXPLICITLY_DISABLED : ReasoningConfigurationState.EXPLICITLY_ENABLED, _ => LevelOf(value), }; /// /// Puts several answers together into one. /// /// /// A "no" wins over a "yes", wherever the two stand. Somebody who switched thinking off in one /// place meant to switch it off, and an indicator lighting up anyway because another parameter /// could be read as a yes would be the app arguing with them. /// /// What the dialects found. /// The one answer. public static ReasoningConfigurationState Merge(IEnumerable states) { var result = ReasoningConfigurationState.NOT_CONFIGURED; foreach (var state in states) { if (state is ReasoningConfigurationState.EXPLICITLY_DISABLED) return ReasoningConfigurationState.EXPLICITLY_DISABLED; if (state is ReasoningConfigurationState.EXPLICITLY_ENABLED) result = ReasoningConfigurationState.EXPLICITLY_ENABLED; } return result; } /// /// Puts several answers together into one. /// /// What the dialects found. /// The one answer. public static ReasoningConfigurationState Merge(params ReasoningConfigurationState[] states) => Merge(states.AsEnumerable()); /// /// Whether a text means yes. /// /// The string value to inspect. /// True, when the value switches reasoning on. public static bool IsEnabledText(string text) => text.Equals("true", StringComparison.OrdinalIgnoreCase) || text.Equals("yes", StringComparison.OrdinalIgnoreCase) || text.Equals("on", StringComparison.OrdinalIgnoreCase) || text.Equals("enabled", StringComparison.OrdinalIgnoreCase) || text.Equals("low", StringComparison.OrdinalIgnoreCase) || text.Equals("minimal", StringComparison.OrdinalIgnoreCase) || text.Equals("medium", StringComparison.OrdinalIgnoreCase) || text.Equals("high", StringComparison.OrdinalIgnoreCase) || text.Equals("max", StringComparison.OrdinalIgnoreCase); /// /// Whether a text means no. /// /// The string value to inspect. /// True, when the value switches reasoning off. public static bool IsDisabledText(string text) => string.IsNullOrWhiteSpace(text) || text.Equals("false", StringComparison.OrdinalIgnoreCase) || text.Equals("no", StringComparison.OrdinalIgnoreCase) || text.Equals("off", StringComparison.OrdinalIgnoreCase) || text.Equals("none", StringComparison.OrdinalIgnoreCase) || text.Equals("disabled", StringComparison.OrdinalIgnoreCase); }