Skip to Content
FrameworksLangChain + Curate-Me

LangChain

LangChain integrates with Curate-Me through the base_url parameter on ChatOpenAI. No middleware, no monkeypatching — the SDK’s existing configuration surface is enough.

Minimal example

import os from langchain_openai import ChatOpenAI llm = ChatOpenAI( base_url=os.environ.get("CM_GATEWAY_URL", "https://api.curate-me.ai/v1/openai"), api_key="unused", model="gpt-4o", default_headers={"X-CM-API-Key": os.environ["CM_API_KEY"]}, ) print(llm.invoke("What is the Curate-Me gateway?").content)

The full example lives at examples/integrations/langchain/main.py. Its smoke test (tests/test_smoke.py) runs on every PR.

What you get for free

  • Cost tracking per call (token-accurate, billed in USD)
  • Rate limiting via IETF RateLimit-* response headers
  • PII scanning on prompts before they reach the upstream provider
  • Model allowlist enforcement
  • HITL approval gates for high-cost calls
  • Full request trace in the Curate-Me dashboard

Supported versions

LibraryTested range
langchain>=0.3.0,<0.5.0
langchain-openai>=0.2.0,<0.4.0
langchain-core>=0.3.0,<0.4.0

Known limitations

  • Streaming: llm.stream(...) works, and per-chunk content is proxied unmodified. The final cost-record event lands when the stream closes — X-CM-Request-Id is in the headers (sent before the stream body) so you can correlate before the stream finishes.
  • Tools: function-calling works via the standard bind_tools(...) API. The gateway tracks tool-call cost as part of the parent request, not as a separate trace row.
  • Embeddings: use OpenAIEmbeddings the same way; route through /v1/openai/embeddings. Verified by the mock gateway in CI.

Troubleshooting

# Sanity check: make a call and inspect the response headers. from openai import OpenAI client = OpenAI( base_url="https://api.curate-me.ai/v1/openai", api_key="cm_sk_test_key", # placeholder — real key from dashboard default_headers={"X-CM-API-Key": os.environ["CM_API_KEY"]}, ) resp = client.with_raw_response.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "ping"}], ) print("request_id:", resp.headers.get("X-CM-Request-Id"))

If X-CM-Request-Id is missing, you’re talking directly to OpenAI — re-check the base_url.