use dynamo_runtime::config::{
env_is_truthy,
environment_names::llm::{self as env_llm, metrics as env_metrics},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetricsConfig {
prefix: Option<String>,
}
impl MetricsConfig {
pub fn new(prefix: Option<String>) -> Self {
Self { prefix }
}
pub fn prefix(&self) -> Option<String> {
self.prefix.clone()
}
pub fn set_prefix(&mut self, prefix: Option<String>) {
self.prefix = prefix;
}
}
impl Default for MetricsConfig {
fn default() -> Self {
Self {
prefix: std::env::var(env_metrics::DYN_METRICS_PREFIX).ok(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AnthropicApiConfig {
enabled: bool,
strip_preamble: bool,
}
impl AnthropicApiConfig {
pub fn new(enabled: bool, strip_preamble: bool) -> Self {
Self {
enabled,
strip_preamble,
}
}
pub fn enabled(&self) -> bool {
self.enabled
}
pub fn strip_preamble(&self) -> bool {
self.strip_preamble
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn set_strip_preamble(&mut self, strip_preamble: bool) {
self.strip_preamble = strip_preamble;
}
}
impl Default for AnthropicApiConfig {
fn default() -> Self {
Self {
enabled: env_is_truthy(env_llm::DYN_ENABLE_ANTHROPIC_API),
strip_preamble: env_is_truthy(env_llm::DYN_STRIP_ANTHROPIC_PREAMBLE),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamingDispatchConfig {
tool_dispatch: bool,
reasoning_dispatch: bool,
}
impl StreamingDispatchConfig {
pub fn new(tool_dispatch: bool, reasoning_dispatch: bool) -> Self {
Self {
tool_dispatch,
reasoning_dispatch,
}
}
pub fn tool_dispatch(&self) -> bool {
self.tool_dispatch
}
pub fn reasoning_dispatch(&self) -> bool {
self.reasoning_dispatch
}
pub fn set_tool_dispatch(&mut self, tool_dispatch: bool) {
self.tool_dispatch = tool_dispatch;
}
pub fn set_reasoning_dispatch(&mut self, reasoning_dispatch: bool) {
self.reasoning_dispatch = reasoning_dispatch;
}
}
impl Default for StreamingDispatchConfig {
fn default() -> Self {
Self {
tool_dispatch: env_is_truthy(env_llm::DYN_ENABLE_STREAMING_TOOL_DISPATCH),
reasoning_dispatch: env_is_truthy(env_llm::DYN_ENABLE_STREAMING_REASONING_DISPATCH),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FrontendApiConfig {
anthropic: AnthropicApiConfig,
streaming_dispatch: StreamingDispatchConfig,
}
impl FrontendApiConfig {
pub fn new(anthropic: AnthropicApiConfig, streaming_dispatch: StreamingDispatchConfig) -> Self {
Self {
anthropic,
streaming_dispatch,
}
}
pub fn from_flags(
enable_anthropic_api: bool,
strip_anthropic_preamble: bool,
enable_streaming_tool_dispatch: bool,
enable_streaming_reasoning_dispatch: bool,
) -> Self {
Self {
anthropic: AnthropicApiConfig::new(enable_anthropic_api, strip_anthropic_preamble),
streaming_dispatch: StreamingDispatchConfig::new(
enable_streaming_tool_dispatch,
enable_streaming_reasoning_dispatch,
),
}
}
pub fn from_optional_flags(
enable_anthropic_api: Option<bool>,
strip_anthropic_preamble: Option<bool>,
enable_streaming_tool_dispatch: Option<bool>,
enable_streaming_reasoning_dispatch: Option<bool>,
) -> Option<Self> {
if enable_anthropic_api.is_none()
&& strip_anthropic_preamble.is_none()
&& enable_streaming_tool_dispatch.is_none()
&& enable_streaming_reasoning_dispatch.is_none()
{
return None;
}
let defaults = Self::default();
Some(Self::from_flags(
enable_anthropic_api.unwrap_or_else(|| defaults.anthropic().enabled()),
strip_anthropic_preamble.unwrap_or_else(|| defaults.anthropic().strip_preamble()),
enable_streaming_tool_dispatch
.unwrap_or_else(|| defaults.streaming_dispatch().tool_dispatch()),
enable_streaming_reasoning_dispatch
.unwrap_or_else(|| defaults.streaming_dispatch().reasoning_dispatch()),
))
}
pub fn anthropic(&self) -> &AnthropicApiConfig {
&self.anthropic
}
pub fn anthropic_mut(&mut self) -> &mut AnthropicApiConfig {
&mut self.anthropic
}
pub fn streaming_dispatch(&self) -> &StreamingDispatchConfig {
&self.streaming_dispatch
}
pub fn streaming_dispatch_mut(&mut self) -> &mut StreamingDispatchConfig {
&mut self.streaming_dispatch
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn optional_flags_return_none_when_all_values_are_unspecified() {
let config = FrontendApiConfig::from_optional_flags(None, None, None, None);
assert_eq!(config, None);
}
#[test]
fn optional_flags_preserve_explicit_false_values() {
let config = FrontendApiConfig::from_optional_flags(
Some(false),
Some(true),
Some(false),
Some(true),
)
.expect("explicit flags should produce a config");
assert!(!config.anthropic().enabled());
assert!(config.anthropic().strip_preamble());
assert!(!config.streaming_dispatch().tool_dispatch());
assert!(config.streaming_dispatch().reasoning_dispatch());
}
#[test]
fn optional_flags_use_env_defaults_for_unspecified_values() {
temp_env::with_vars(
[
(env_llm::DYN_ENABLE_ANTHROPIC_API, Some("1")),
(env_llm::DYN_STRIP_ANTHROPIC_PREAMBLE, Some("1")),
(env_llm::DYN_ENABLE_STREAMING_TOOL_DISPATCH, Some("1")),
(env_llm::DYN_ENABLE_STREAMING_REASONING_DISPATCH, Some("1")),
],
|| {
let config =
FrontendApiConfig::from_optional_flags(Some(false), None, None, Some(false))
.expect("partial flags should produce a config");
assert!(!config.anthropic().enabled());
assert!(config.anthropic().strip_preamble());
assert!(config.streaming_dispatch().tool_dispatch());
assert!(!config.streaming_dispatch().reasoning_dispatch());
},
);
}
}