Skip to main content
The SDK patches your LLM client the moment initialize() runs, so it must run before the LLM SDK is imported. In Next.js the only place that’s guaranteed is the instrumentation.ts hook at your project root:
Do not call initialize() from inside a route file. By then the LLM SDK is already imported and the patch won’t take. In Next.js, always pass instrumentModules (only the SDKs you actually use): bundlers can load LLM SDKs in ways auto-instrumentation can’t see, which silently drops all LLM spans.

Keep LLM SDKs external

Also keep the LLM SDKs external so every bundle shares one runtime copy. If they get inlined, each bundle has its own private copy and instrumentModules patches one your routes never use. List belvedir itself as well: a route or server action that bundles its own copy calls withSession()/flush() on an uninitialized duplicate, so flush silently no-ops and spans drop on serverless. Mind the version-specific config key: on Next 15+ it’s top-level serverExternalPackages; on Next 13/14 it’s experimental.serverComponentsExternalPackages. The Next 15 name is silently ignored there (the build prints an Unrecognized key(s) warning; don’t ignore it). Requires belvedir. The legacy @belvedir/loop@0.2.0 and @fractalresearch/loop packages still install and run, but they are frozen and predate several fixes (raw-fetch capture, the string withSession form, a dependency-conflict crash fixed in 0.3.2); migrate to belvedir.