Repository navigation
Description
Component
Python SDK
Summary
AzureOpenAIStructuredLLM.__init__ crashes on every config form except an already-built AzureOpenAIConfig: the class's own declared default (config: Optional[BaseLlmConfig] = None), any dict config, and any plain BaseLlmConfig all raise — AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs' (for dicts containing the documented azure_kwargs key: TypeError: BaseLlmConfig.__init__() got an unexpected keyword argument 'azure_kwargs').
The constructor passes the raw config to super().__init__() (mem0/llms/azure_openai_structured.py:18), where LLMBase builds a plain BaseLlmConfig from None/dict (mem0/llms/base.py:19-25), and then immediately reads self.config.azure_kwargs (mem0/llms/azure_openai_structured.py:24) — an attribute only AzureOpenAIConfig has. The sibling class AzureOpenAILLM converts None/dict/BaseLlmConfig to AzureOpenAIConfig before calling super() (mem0/llms/azure_openai.py:20-38), so the exact same inputs work there.
Steps to Reproduce
No Azure credentials or network needed — every crash happens before the AzureOpenAI client is built (the api_key="k" value also steers away from the DefaultAzureCredential branch).
from mem0.configs.llms.base import BaseLlmConfig
from mem0.llms.azure_openai import AzureOpenAILLM
from mem0.llms.azure_openai_structured import AzureOpenAIStructuredLLM
AZURE_DICT = {
"model": "gpt-4o",
"api_key": "k",
"azure_kwargs": {
"azure_deployment": "dep",
"azure_endpoint": "https://x.openai.azure.com",
"api_version": "2024-10-21",
},
}
for label, arg in [
("no-arg (declared default None)", None),
("dict with azure_kwargs", AZURE_DICT),
("dict without azure_kwargs", {"model": "gpt-4o", "api_key": "k"}),
("plain BaseLlmConfig", BaseLlmConfig(model="gpt-4o", api_key="k")),
]:
try:
AzureOpenAIStructuredLLM(arg)
print(f"AzureOpenAIStructuredLLM({label}): OK")
except Exception as e:
print(f"AzureOpenAIStructuredLLM({label}): CRASH: {type(e).__name__}: {e}")
# sibling contrast: same dict works for AzureOpenAILLM
try:
AzureOpenAILLM(AZURE_DICT)
print("AzureOpenAILLM(dict with azure_kwargs): OK")
except Exception as e:
print(f"AzureOpenAILLM(dict with azure_kwargs): CRASH: {type(e).__name__}: {e}")Expected Behavior
AzureOpenAIStructuredLLM should accept the config forms its own signature and its base class declare, converting them to AzureOpenAIConfig the way the sibling AzureOpenAILLM does:
AzureOpenAIStructuredLLM()— the declared defaultconfig: Optional[BaseLlmConfig] = None(mem0/llms/azure_openai_structured.py:17) should not crash on its own default.- dict configs —
LLMBase.__init__documents and implements dict configs: "Handle dict-based configuration (backward compatibility):self.config = BaseLlmConfig(**config)" with the docstring "config: LLM configuration option class or dict, defaults to None" (mem0/llms/base.py:16-23).AzureOpenAIConfigitself is a dict-friendly dataclass carryingazure_kwargs, andazure_kwargsis the documented key for Azure options. - plain
BaseLlmConfig— accepted by the declared signature's type annotation.
Concrete basis: the sibling class in the same provider family, AzureOpenAILLM.__init__ (mem0/llms/azure_openai.py:20-38):
if config is None:
config = AzureOpenAIConfig()
elif isinstance(config, dict):
config = AzureOpenAIConfig(**config)
elif isinstance(config, BaseLlmConfig) and not isinstance(config, AzureOpenAIConfig):
config = AzureOpenAIConfig(model=..., temperature=..., ...)With that conversion in place, the identical dict constructs AzureOpenAILLM successfully (verified below).
Actual Behavior
Verifier 1:
AzureOpenAIStructuredLLM(no-arg): CRASH AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs' [azure_openai_structured.py:24 in __init__]
AzureOpenAIStructuredLLM(dict with azure_kwargs): CRASH TypeError: BaseLlmConfig.__init__() got an unexpected keyword argument 'azure_kwargs'
AzureOpenAIStructuredLLM(dict without azure_kwargs): CRASH AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
AzureOpenAIStructuredLLM(plain BaseLlmConfig): CRASH AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
AzureOpenAILLM(dict with azure_kwargs): OK
Verifier 2:
AzureOpenAIStructuredLLM(no-arg (declared default None)): CRASH: AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
AzureOpenAIStructuredLLM(dict with azure_kwargs): CRASH: TypeError: BaseLlmConfig.__init__() got an unexpected keyword argument 'azure_kwargs'
AzureOpenAIStructuredLLM(plain BaseLlmConfig): CRASH: AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
AzureOpenAILLM(dict with azure_kwargs): OK
AzureOpenAIStructuredLLM(full AzureOpenAIConfig): OK, model = gpt-4o
Re-run on c7ee362aff94a369af70f13f2b4f853f6793ff4c before filing — same result:
AzureOpenAIStructuredLLM(no-arg (declared default None)): CRASH: AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
AzureOpenAIStructuredLLM(dict with azure_kwargs): CRASH: TypeError: BaseLlmConfig.__init__() got an unexpected keyword argument 'azure_kwargs'
AzureOpenAIStructuredLLM(dict without azure_kwargs): CRASH: AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
AzureOpenAIStructuredLLM(plain BaseLlmConfig): CRASH: AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
AzureOpenAILLM(dict with azure_kwargs): OK
Tracebacks (same re-run) — the two distinct failure points:
File "mem0/llms/azure_openai_structured.py", line 24, in __init__
api_key = self.config.azure_kwargs.api_key or os.getenv("LLM_AZURE_OPENAI_API_KEY")
^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'BaseLlmConfig' object has no attribute 'azure_kwargs'
File "mem0/llms/base.py", line 23, in __init__
self.config = BaseLlmConfig(**config)
^^^^^^^^^^^^^^^^^^^^^^^
TypeError: BaseLlmConfig.__init__() got an unexpected keyword argument 'azure_kwargs'
Only passing an already-built AzureOpenAIConfig works.
Environment
- mem0 version: 2.0.20 (
pyproject.toml), commitc7ee362aff94a369af70f13f2b4f853f6793ff4c - Python 3.12.14
- macOS, Darwin 25.6.0
- No credentials / offline — the crash precedes any client construction or network call
Root Cause
mem0/llms/azure_openai_structured.py:17-24 — __init__ forwards the raw config to super().__init__(config) without converting it to AzureOpenAIConfig. LLMBase.__init__ then turns None/dict into a plain BaseLlmConfig (mem0/llms/base.py:19-25), which has no azure_kwargs attribute, so the first access at line 24 raises AttributeError; a dict that does contain azure_kwargs instead fails earlier inside super() with TypeError, because BaseLlmConfig(**config) does not accept that keyword. The sibling AzureOpenAILLM (mem0/llms/azure_openai.py:20-38) does the conversion first and is unaffected.
How You Verified This
What I Ran
The script in Steps to Reproduce against a checkout of c7ee362aff94a369af70f13f2b4f853f6793ff4c (editable install, Python 3.12.14, azure-identity installed for the module imports), offline. Independently reproduced by two verifiers on the same commit with the same script.
What I Saw
The verbatim outputs in Actual Behavior: all four documented/declared config forms crash AzureOpenAIStructuredLLM.__init__ (AttributeError at azure_openai_structured.py:24, or TypeError at base.py:23 for a dict carrying azure_kwargs), while the identical dict constructs AzureOpenAILLM fine and a full AzureOpenAIConfig constructs AzureOpenAIStructuredLLM fine.
Why This Is a Bug
The constructor violates its own declared contract: the signature is config: Optional[BaseLlmConfig] = None (azure_openai_structured.py:17), so AzureOpenAIStructuredLLM() must work, and LLMBase explicitly documents and supports dict configs "backward compatibility" (base.py:16-23). The direct fix already exists in the same provider family — AzureOpenAILLM.__init__ (azure_openai.py:20-38) converts None → AzureOpenAIConfig(), dict → AzureOpenAIConfig(**config), and plain BaseLlmConfig → AzureOpenAIConfig(...); AzureOpenAIStructuredLLM is the only Azure class missing that conversion. The class is a registered provider ("azure_openai_structured": (..., AzureOpenAIConfig) at mem0/utils/factory.py:52), so anyone instantiating it directly — the documented pattern for the LLM classes — hits the crash.
What I Ruled Out
- The
Memory/factory path —LlmFactory.createpre-builds the provider config class before instantiating (mem0/utils/factory.py:80-88), sofrom_config-style usage works; only direct construction with the class's own declared signature crashes. - Missing environment variables — the
os.getenvfallbacks at azure_openai_structured.py:24-27 are only reached after theself.config.azure_kwargsaccess; no env setting can avoid the AttributeError. - Test coverage —
tests/llms/test_azure_openai_structured.pyonly passes aDummyConfigduck-type that happens to carry anazure_kwargsattribute (tests/llms/test_azure_openai_structured.py:26-49), soNone/dict/BaseLlmConfiginputs are never exercised. - PR fix(llms): honor reasoning-model params in AzureOpenAIStructuredLLM #5548 (merged, touches this file) — fixes reasoning-model params in
generate_response; different function, different defect.
Related
Searched issues/PRs for azure_kwargs, AzureOpenAIStructuredLLM, azure_openai_structured: nothing covers this constructor crash. Nearest, none duplicates: #4164 (closed; AzureOpenAIStructuredLLM missing _parse_response method — different defect), merged PR #5548 (same file, generate_response reasoning-model params — different function), PR #2914 (closed; original structured Azure LLM addition).
Happy to open a PR mirroring the sibling conversion from azure_openai.py:20-38 at the top of __init__ (None → AzureOpenAIConfig(), dict → AzureOpenAIConfig(**config), plain BaseLlmConfig → AzureOpenAIConfig(...)) plus tests for the three currently-crashing forms — say the word (per CONTRIBUTING I'll wait for the accepted label first).
AI Assistance
AI helped me find it, and I reproduced it myself afterwards (the re-run in Actual Behavior is my own; two independent verifiers reproduced it on the same commit before filing).
Activity
BlueX888 commentedon Sep 23, 2026
Reproduced this. Root cause: mem0/llms/azure_openai_structured.py:18 passes the raw config to super().init(), so None/dict inputs become a plain BaseLlmConfig (mem0/llms/base.py:19-25), and line 24 then reads self.config.azure_kwargs — an attribute only AzureOpenAIConfig has; the sibling AzureOpenAILLM converts first (mem0/llms/azure_openai.py:20-38).
Minimal reproduction:
Observed output:
Happy to open a PR with this fix, or to be assigned if you prefer to take it.
chenhz01 commentedon Oct 1, 2026
Hi @BlueX888 — the crash is that
AzureOpenAIStructuredLLM.__init__readsself.config.azure_kwargsright aftersuper().__init__(), but the base builds a plainBaseLlmConfigforNone/dict inputs, which has noazure_kwargs. The siblingAzureOpenAILLMconvertsNone/dict/BaseLlmConfigtoAzureOpenAIConfigfirst — exactly what's missing here. The fix is to normalize the config toAzureOpenAIConfigbefore touching provider-specific attributes.This exact fix pattern — auditing the config/credential path the constructor will touch before the client is built — is what I'd reach for; here's a working reference implementation from a drop-in fix library I maintain (published, runnable):
Ref: https://github.com/chenhz01/llm_agent_dropin_fixes/blob/main/src/llm_agent_dropin_fixes/state_persist/mem0_cred_audit.py