Skip to content

LOU-D10, LOU-D19: lazy provider loading (importing the SDK never loads or requires a provider peer) - #60

Merged
LinuxDevil merged 4 commits into
mainfrom
lou-d10-d19-lazy-providers
Oct 1, 2026
Merged

LinuxDevil merged 4 commits into
mainfrom
lou-d10-d19-lazy-providers

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What

Importing any entry point of the SDK no longer loads an optional peer or a heavy optional dependency. Each is loaded with a dynamic import() on first use:

  • @ai-sdk/openai (OpenAI, OpenRouter), @ai-sdk/anthropic, ollama-ai-provider: inside the provider's generate()/stream() (via createModel()).
  • undici: first request of the built-in http tool.
  • dockerode: first SubprocessSandbox.run().
  • @modelcontextprotocol/sdk: first serveMcp() call (buildServer() is now async; pure helpers moved to toolNames.ts).

A missing package throws MissingPeerDependencyError (packageName, installCommand) on first use, never at import. The install command comes from providerSpec.ts (new peerInstallCommand(), also used by the existing resolveProvider hint), so there is one source. The error is exported from the root and providers barrels.

Provider constructors, resolveProvider() and createAgent({ model }) stay synchronous with unchanged signatures.

Why

LOU-D10 / LOU-D19: createAgent could fail at import when a peer the user never uses was missing, so the LOU-D1 install hint never got to show; every consumer paid the load cost of all provider SDKs, undici, dockerode and the MCP SDK.

Import graph (third-party packages loaded by require('dist/index.js'), measured with Module._load patched)

Before: @ai-sdk/anthropic, @ai-sdk/openai, ollama-ai-provider, undici, dockerode (+ docker-modem, ssh2, protobufjs, @grpc/*, tar-fs, ...), @modelcontextprotocol/sdk (+ hono, @hono/node-server, ajv, ...), plus ai, zod, @opentelemetry/api, nanoid, yaml and their deps (about 75 packages).

After: ai (+ @ai-sdk/provider, provider-utils, ui-utils, secure-json-parse, zod-to-json-schema), zod, @opentelemetry/api, nanoid, yaml. Sub-entries core, testing: only nanoid.

How verified

Run on the result of merging origin/main (b5cd5f5, LOU-X6):

  • npx tsc --noEmit: clean
  • npm run lint: 0 errors (433 warnings, same count as before)
  • npm run build + create-loushy-agent build: success
  • npm run test:coverage: 115 files passed; 1625 passed, 4 skipped; All files 86.93 / 91.4 / 89.9 / 86.93
  • npm run fallow: exit 0
  • npm run docs:verify-snippets: all 52 snippets type-check; 6 run cleanly
  • npm run test:types: no type errors
  • npm run docs:llms:check: ok (regenerated llms.txt/llms-full.txt)

New tests: importGraph.test.ts (built CJS and ESM of 6 entries load none of the six packages; needs dist/, like other dist-dependent tests), missingPeer.test.ts (each provider and SubprocessSandbox with the peer mocked missing: constructs, rejects with the right installCommand; plus loader unit tests). All existing provider tests pass unchanged. Deploy adapter tests (incl. Cloudflare) pass.

Notes for reviewer

  • engines NOT lowered. With undici now lazy, nothing at import time appears to need Node >= 22.19; your call.
  • package.json sideEffects unchanged on purpose: the providers/index side effect is LLMProviderRegistry.register(...) of factories that only construct provider objects, which still works and no longer loads a peer.
  • @opentelemetry/api (optional peer) is still loaded at import by the tracing code; out of scope here, suggest a follow-up ticket.
  • One existing test edited: serveMcp.test.ts now awaits buildServer() (it became async because loading the MCP SDK is lazy).
  • ai stays external; tsup keeps the dynamic imports of externals as real import()/require in both builds (checked in dist). The Worker bundle path is unchanged (esbuild bundles the peers there).

🤖 Generated with Claude Code

LinuxDevil and others added 4 commits October 1, 2026 10:14
…s or requires a provider peer)

Provider peers (@ai-sdk/openai, @ai-sdk/anthropic, ollama-ai-provider) and the heavy
dependencies undici (http tool), dockerode (SubprocessSandbox) and
@modelcontextprotocol/sdk (serveMcp) are now loaded with a dynamic import() on first
use instead of at import time. A missing package throws MissingPeerDependencyError
(packageName, installCommand; install hint shared with providerSpec) on first use.
Constructors and resolveProvider/createAgent stay synchronous.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@LinuxDevil
LinuxDevil merged commit 995f649 into main Oct 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant