Repository navigation
http_client_proxies is silently dropped by every provider except the three Azure paths (docs promise All) #7492
Description
Component
sdk-python (mem0/llms/*, mem0/embeddings/* — http_client_proxies plumbing)
Description
TL;DR: http_client_proxies is documented as available for "All" providers, but it is actually wired through to the underlying SDK client only in the three Azure paths. On 17 other LLM/embedding providers the proxy is silently dropped — traffic (prompts and API keys included) goes out direct, with no warning, no error, and a config.http_client that looks configured if you inspect it. This extends #6651 (which covered the OpenAI paths and was closed as not_planned) to the full provider matrix.
What the docs promise
docs/components/llms/config.mdx:102 and docs/components/embedders/config.mdx:79 both list http_client_proxies as available for All providers. The parameter sits on the shared BaseLlmConfig / BaseEmbedderConfig signatures and both factories pass it through uniformly, so at the API level it is a whole-surface promise. #5447 shipped it as a general feature.
What the code does
build_http_client() correctly builds a proxied httpx.Client into config.http_client for every provider. The drop happens one step later: most provider constructors build their SDK client without passing that client along.
Verified on current origin/main (94c3fe9f) by inspecting the real httpx transport pool type (transport._pool being httpcore.HTTPProxy) of the constructed client — not by looking at config objects:
| Outcome | Providers |
|---|---|
| Proxy applied (3) | llms/azure_openai, llms/azure_openai_structured, embeddings/azure_openai |
| Proxy dropped (17) | LLMs: openai, openai_structured, anthropic, deepseek, groq, together, xai, minimax, vllm, lmstudio, ollama, gemini · Embeddings: openai, together, lmstudio, ollama, gemini |
| Not applicable (no httpx client at all) | llms/aws_bedrock (boto3), llms/litellm, llms/sarvam (requests), embeddings/aws_bedrock |
End-to-end check with a local API and a dead proxy port: OpenAILLM configured with the dead proxy succeeds by going direct; AzureOpenAILLM with the same config fails with APIConnectionError (the proxy is honored). So this is not "proxy support partially implemented" — the requests genuinely bypass the configured proxy.
There is no warning, no log line, and no validation. The failure mode is fully silent; the only mitigations are ambient (trust_env=True still honors HTTP_PROXY/HTTPS_PROXY environment variables, so users who only use env vars are unaffected).
For users behind a corporate proxy this is both an availability bug (requests fail to reach the API) and a compliance one (prompts and credentials leave through a path the operator believes is proxied).
Two adjacent defects found while mapping this
mem0/llms/xai.py:31passeshttp_client_proxies=config.http_client— the wrong object entirely; constructingXAILLMwithhttp_client_proxiesset raisesAttributeError: 'Client' object has no attribute 'url'. This is what XAILLM forwards the built http client as http_client_proxies, crashing when a proxy is set #6230 (open) reports.mem0/llms/gemini.py:23-32converts aBaseLlmConfigwithout carryinghttp_client_proxies, so it isNoneeven on the base config. Same shape as bug(llms/gemini): BaseLlmConfig→GeminiConfig conversion drops http_client_proxies #6385 (closed as not_planned).
Why a new issue rather than reopening #6651
#6651 reported the OpenAI LLM + embedder paths and was closed as not_planned with the diagnosis confirmed but nothing fixed; the three fix PRs that followed (#6653, #6661, #6737) were all closed unmerged. The increments this issue adds beyond it:
- the full provider matrix (17 dropped paths, 3 working, 4 not applicable) — the fix needs a per-provider plan, not a one-liner for OpenAI;
- the existing test suite (
tests/test_http_client_proxies.py) only asserts at the config layer (config.http_clientexists), which is exactly why this has stayed green in CI while broken in production — the coverage gap is part of the bug; - a constraint any fix must know: anthropic 1.9.0 depends on httpx2 and rejects an
httpx.Clientwith aTypeError, so "passhttp_client=everywhere like Azure does" does not work for the Anthropic provider as-is; - ollama's client takes no injected client but forwards kwargs (
proxy=/mounts=work), and google-genai needstypes.HttpOptions(httpx_client=...)— per-provider wiring is genuinely heterogeneous.
Happy to contribute the fix once this is accepted; a draft with per-provider tests that fail on current main is ready.
How You Verified This
On origin/main (94c3fe9f238f3dbf29c9ce98643bd71eb13077cd), Python 3.12:
- For every LLM and embedding provider registered in both factories, constructed the provider with
http_client_proxiesset (string form and dict form, both), then inspected the constructed SDK client's real transport: a working proxy showshttpcore.HTTPProxyintransport._pool. Two deliberate guards against false results:config.http_clientitself is excluded from the check (it always exists and always has the proxy — counting it is how a whole-matrix false pass happens), and httpx-family clients are recognized structurally (openai>=3's_DefaultHttpxClientis not anhttpx.Clientsubclass, soisinstancewould under-count). The 17/3/4 split above is from that run. - End-to-end differential: pointed
OpenAILLMat a local API through a proxy port that is deliberately dead. Direct connection succeeds (proving the request ignored the proxy); the same setup onAzureOpenAILLMfails withAPIConnectionError(proving Azure honors it). - Confirmed the drop points by reading each constructor (
llms/openai.py:43,53,anthropic.py:45,deepseek.py:41,groq.py:26,together.py:23,xai.py:41,minimax.py:45,vllm.py:40,lmstudio.py:41,ollama.py:41,gemini.py:40,43, plus the five embedding paths) — none pass the built client into the SDK client constructor. - Confirmed the anthropic/httpx2 constraint by constructing an Anthropic client with an
httpx.Client: it raisesTypeError. - Confirmed the ollama kwargs path (
proxy=/mounts=forwarded to the underlyinghttpx.Client) and the google-genaiHttpOptions(httpx_client=...)path construct working proxied clients. - A parametrized regression test that asserts the transport pool type for every provider runs 21 cases: 17 fail on current main; the 4 passing cases are exactly the three Azure paths plus the
build_http_clientself-check. Test file available on request — it deliberately imports the repo'smem0(not a PyPI-installed copy). - Prior art checked: bug(llms+embeddings/openai): http_client_proxies builds a client that is never passed to OpenAI() #6651 (closed not_planned, OpenAI paths), fix(openai): forward http_client into OpenAI SDK clients #6653/fix(openai): pass configured proxy clients #6661/fix(llms,embeddings): pass http_client to OpenAI client when proxies set #6737 (fix PRs, closed unmerged), LlmFactory.create forwards a constructed httpx.Client as http_client_proxies, raising TypeError when a proxy is set #5648/fix(llms,embeddings): repair HTTP proxy support (httpx>=0.28) and preserve proxies in LlmFactory #5447 (merged, config-layer only), XAILLM forwards the built http client as http_client_proxies, crashing when a proxy is set #6230 (open, xai), bug(llms/gemini): BaseLlmConfig→GeminiConfig conversion drops http_client_proxies #6385 (closed, gemini). None covers the full matrix, the test-coverage gap, or the httpx2 constraint.
AI Assistance
AI-generated (an agent wrote most or all of this report): the provider matrix, the transport-level verification, the end-to-end differential, and the prior-art scan were produced by an AI agent against a local clone at 94c3fe9f; every claim under "How You Verified This" comes from real runs logged locally, reviewed and confirmed by the contributor before filing. No external API keys were used — the dead-proxy differential runs against a local endpoint.
Activity
chenhz01 commentedon Sep 30, 2026
Hi @passionworkeer - this extends #6651 (OpenAI paths, closed as not_planned) to the full provider matrix, and the symptom is sneaky:
build_http_client()builds the proxiedhttpx.Clientfor every provider, but most constructors don't forwardconfig.http_client, so 17 providers go direct with no warning - prompts and keys included.Here is a probe that proves whether a provider's client is actually proxied:
proxy_applied()inspects the transport's pool type;Falsemeans the proxy was silently dropped. If useful I can script this across all 20 providers to produce the exact pass/fail table.bluetomlee commentedon Oct 1, 2026
Confirmed the impact on current main (
94c3fe9):build_http_client()builds the proxiedhttpx.Clientintoconfig.http_clientfor every provider, but only the three Azure constructors forward it to their SDK client — on the other 17 the proxy is silently dropped while the config still looks wired.One question before anyone invests in a PR, given #6651 was closed as not_planned: is wiring the remaining providers something you want to take on now?
If yes, I'd suggest keeping the fix in one place rather than touching 17 constructors — a shared wiring helper each provider constructor calls (passing
config.http_clientto the SDK client where the SDK accepts one), and an explicit warn-on-drop for the few SDKs that can't take an httpx client, so the failure mode stops being silent. Regression test would be parametrized over the provider matrix and assert on the transport pool type (httpcore.HTTPProxy), same inspection method the report used.Happy to take this on if it's agreed.
Varshith-Kali commentedon Oct 8, 2026
Confirming the report on current main:
build_http_client()inmem0/configs/llms/base.pybuilds the proxiedhttpx.Clientintoconfig.http_clientfor every provider, but only the three Azure constructors (azure_openai.py, and the two other Azure paths) forward it to their SDK clients. The other ~17 provider constructors (OpenAI, LiteLLM, Gemini, etc.) construct their SDK clients withouthttp_client=..., so the proxy config — and anything relying on it for egress control — is silently dropped.Before writing code, I wanted to check the preferred approach, since #6651 (the OpenAI-only subset) was closed as not planned:
config.http_clientto its SDK client where the SDK accepts anhttpxclient (most OpenAI-compatible SDKs do), via a small shared helper to avoid 17 copy-pasted edits.httpxclient, log an explicit warning at construction time so the dropped proxy stops being silent.Is the full-matrix version of this in scope, and would approach (1)+(2) be welcome? Happy to implement it that way if so.
chenhz01 commentedon Oct 8, 2026
Thanks @Varshith-Kali for the independent confirmation — and with @bluetomlee asking essentially the same "should we fix this?" back on Oct 1, this thread now has two confirmations and two nearly identical questions waiting on the same answer. The bug's whole trick is failing silently; the fix decision shouldn't have to die silently too. 🙂
On approach: (1) and (2) are complementary, not either/or. (1) is the actual fix — a shared wiring helper means one fix point instead of ~17 copy-pasted edits, which is almost exactly what @bluetomlee proposed on Oct 1. Two independent reads landing on the same shape is usually a good sign. (2) is the safety net for the few SDKs that can't accept an
httpxclient — given the failure mode here is silence, the loud warning is what prevents the next variant of this bug.One honest caveat on "in scope": that call belongs to the mem0 maintainers, not me — I'm a reporter in this thread (my Sep 30 comment is a transport-level probe confirming the drop). But given #6651 was closed as not_planned, a PR is probably the cheapest way to force a maintainer decision: if it merges, the full provider matrix is fixed; if it doesn't, it still serves as the reference implementation for whoever picks this up next.
If you do open the PR, two concrete offers:
transport._poolishttpcore.HTTPProxy, not just thatconfig.http_clientlooks wired. Config-level assertions would have missed this exact bug, and that's the entire lesson of this issue.Varshith-Kali commentedon Oct 9, 2026
@chenhz01 the fix is built and the branch is up if you'd like to run your transport probe against it before anything is opened:
fix/http-client-proxies-all-providerson my fork.What it does: a shared wiring helper routes
config.http_clientinto every LLM and embedding provider's SDK client where the SDK accepts anhttpxclient (15 providers), plus an explicit warning at construction time for the SDKs that can't take one — so the dropped proxy stops being silent. 35 regression tests, including transport-level assertions (transport._poolishttpcore.HTTPProxy) per your suggestion rather than config-level ones.One process note for the maintainers: I'm holding the PR itself until this issue carries the
acceptedlabel (per the repo's PR gate) — happy to open it as soon as that's in place.chenhz01 commentedon Oct 15, 2026
@Varshith-Kali ran your branch through the transport probe as offered — baseline vs
fix/http-client-proxies-all-providers(81ca368), same probe, two runs, side by side.Environment
Windows, Python 3.13.14, httpx 0.28.1 / httpcore 1.0.9. Baseline = PyPI
mem0ai 2.2.1(which is upstream release commit94c3fe9f, the exact commit the original report was verified on). Your branch verified via the compare API: 1 commit ahead ofmain, 0 behind, and none of the 22 source files your commit touches changed between94c3fe9fandmain— so the branch was exercised by overlaying your files onto a stock 2.2.1 install (import path confirmed per run). AmbientHTTP_PROXY/HTTPS_PROXYwere stripped from the probe environment — see the second method note below for why that matters.Result matrix
Every one of the 17 paths the original report marked as dropped now honors the proxy on your branch — in both string and dict config forms (34/34 cases inspectable, 32 proxied + 2 environment-blocked):
openai,openai_structured,anthropic,deepseek,groq,together,xai,minimax,vllm,lmstudio,ollama,gemini) × both forms:dropped → proxiedollama(openai,together,lmstudio,gemini) × both forms:dropped → proxiedembeddings/ollama× both forms: constructor performs a live call to the local Ollama host, which does not exist on this machine, so construction fails before the transport can be inspected — identical behavior on baseline and branch, so it is environment-blocked rather than a finding either way.proxiedon baseline and on your branch — no regression.sarvam,litellm: still no httpx client surface (as in the original report's N/A rows).aws_bedrock(both LLM and embeddings): still classified N/A by transport inspection (botocore has no httpx client) — but yourbuild_botocore_configwiring produced a nice piece of behavioral evidence: constructingAWSBedrockLLMon your branch logsFailed to connect to proxy URL: "http://127.0.0.1:9"(the probe's dead-proxy URL), while baseline reaches the real AWS endpoint and fails on the dummy credentials instead. So on your branch the original N/A boto3 paths are behaviorally proxied too — worth a line in the PR description.embeddings/huggingface: not run —sentence_transformersis not installed in this environment; recording as an honest gap rather than a pass.Your own
tests/test_http_client_proxies.pyfrom the branch: 39/39 passed on this environment. (It does not collect on stock 2.2.1 since it imports your newmem0.utils.httphelper, so the baseline side of the comparison comes from the probe instead.)Two probe-method notes you may want in the PR tests
httpx 0.28 changed where the proxy lives. The original report's detection —
transport._poolbeinghttpcore.HTTPProxy— is blind on httpx 0.28.1:httpx.Client(proxy=...)no longer builds an HTTPProxy pool on the main transport; the proxied transports now sit inclient._mounts(URLPattern →HTTPTransportwhose_poolishttpcore.HTTPProxy). A probe that only checks the legacy location reports every path as "dropped" even when the proxy is honored. The version-adaptive check that produced the matrix above uses three evidence channels: legacytransport._pool, any_mountstransport pool, and object-identity withconfig.http_client(the strongest signal where the SDK accepts an injected client — that is how the openai/deepseek/groq/together/xai/minimax/vllm/lmstudio/gemini paths verified). Your 39 tests evidently already handle this correctly on 0.28 — worth pinning explicitly in case CI pins a different httpx.Strip ambient proxy env vars or every path looks proxied. The first probe run in this environment carried
HTTP_PROXY/HTTPS_PROXYin the process env, and withtrust_env=True(the SDK default) every SDK client — including all 17 baseline paths — showed proxied mounts. That is the env-layer twin of the config-layer false pass the original report warned about: ambient env proxies make the whole matrix pass regardless of mem0's wiring. The clean run stripsHTTP_PROXY/HTTPS_PROXY/ALL_PROXYbefore constructing anything; the dead proxy is passed explicitly viahttp_client_proxiesonly. After stripping, baseline cleanly reproduces 32/32 dropped and the branch cleanly reproduces 32/32 proxied.One discrepancy recorded for completeness: the
xaiconstruction crash from the original report (AttributeError: 'Client' object has no attribute 'url', still open as #6230) did not reproduce on the PyPI 2.2.1 wheel used here —XAILLMconstructs cleanly in both forms (and is proxied on your branch). Wheel-vs-main drift or an environment difference; noting it rather than quietly dropping it.No ask attached to this — the
acceptedlabel decision is the maintainers' and you are already holding the PR per the repo gate. The probe script (single file, stdlib + the provider SDKs, per-path evidence records included) is available if it is useful for the PR's test suite.