I've been working with this library over the past few days and have encountered several areas where I ran into conflicting behaviors or ambiguous logic—particularly around configuration loading and validation.
I've summarized the questions and observations below for reference and discussion. Most of them center on how api_key, provider, and related fields are validated and normalized, as well as how environment variables are handled.
1. Should we switch to mode="after" for the validator?
Using @model_validator(mode="before") means fields like provider and api_key may still be None when validation runs, because default values and type coercion haven’t been applied yet. Would using mode="after" improve reliability by ensuring that all defaults are applied and types like SecretStr are already in place?
2. Should api_key always be normalized to SecretStr?
We currently assign values to api_key from various sources (explicit input, env vars, fallback lookups), which may result in it being either a str or a SecretStr. Should we always convert non-None values to SecretStr within the validator to ensure consistent internal usage and avoid mistakes when accessing the raw value?
3. Should we stop writing API keys into os.environ?
The current code writes the resolved api_key into os.environ, potentially exposing secrets in the process environment. Is this necessary, or should we keep API keys in memory only and let consumers explicitly manage their own integration logic?
4. Can we simplify API key resolution logic?
Right now we perform multiple checks in several branches to resolve the api_key, including checking explicit input, fallback keys like OPENAI_API_KEY, and possibly logging warnings. Would a clearer priority order (e.g. input > env-prefixed > provider env var) make the logic easier to follow and test?
5. Is the custom from_env() method needed?
Since BaseSettings already handles environment variable parsing (including .env files and prefixes), is our manual from_env() implementation necessary? Or can we just rely on cls() and let Pydantic handle the loading?
6. Should we refine provider/base_url handling?
When base_url is set, we currently override provider to "custom" even if it was originally one of the known providers (e.g., "openai"). This might block valid use cases like Azure/OpenAI-compatible endpoints. Should we be more conservative and only change provider to "custom" if it’s not a known one?
7. Can we downgrade some warnings to info logs?
Certain messages, like detecting a custom base_url, are currently logged at the warning level. Should we downgrade these to info to avoid alarming users for intentional setups?
I've been working with this library over the past few days and have encountered several areas where I ran into conflicting behaviors or ambiguous logic—particularly around configuration loading and validation.
I've summarized the questions and observations below for reference and discussion. Most of them center on how api_key, provider, and related fields are validated and normalized, as well as how environment variables are handled.
1. Should we switch to
mode="after"for the validator?Using
@model_validator(mode="before")means fields likeproviderandapi_keymay still beNonewhen validation runs, because default values and type coercion haven’t been applied yet. Would usingmode="after"improve reliability by ensuring that all defaults are applied and types likeSecretStrare already in place?2. Should
api_keyalways be normalized toSecretStr?We currently assign values to
api_keyfrom various sources (explicit input, env vars, fallback lookups), which may result in it being either astror aSecretStr. Should we always convert non-Nonevalues toSecretStrwithin the validator to ensure consistent internal usage and avoid mistakes when accessing the raw value?3. Should we stop writing API keys into
os.environ?The current code writes the resolved
api_keyintoos.environ, potentially exposing secrets in the process environment. Is this necessary, or should we keep API keys in memory only and let consumers explicitly manage their own integration logic?4. Can we simplify API key resolution logic?
Right now we perform multiple checks in several branches to resolve the
api_key, including checking explicit input, fallback keys likeOPENAI_API_KEY, and possibly logging warnings. Would a clearer priority order (e.g. input > env-prefixed > provider env var) make the logic easier to follow and test?5. Is the custom
from_env()method needed?Since
BaseSettingsalready handles environment variable parsing (including.envfiles and prefixes), is our manualfrom_env()implementation necessary? Or can we just rely oncls()and let Pydantic handle the loading?6. Should we refine provider/base_url handling?
When
base_urlis set, we currently overrideproviderto"custom"even if it was originally one of the known providers (e.g.,"openai"). This might block valid use cases like Azure/OpenAI-compatible endpoints. Should we be more conservative and only changeproviderto"custom"if it’s not a known one?7. Can we downgrade some warnings to info logs?
Certain messages, like detecting a custom
base_url, are currently logged at the warning level. Should we downgrade these toinfoto avoid alarming users for intentional setups?