google / google/adk-js

Tracking: @google/genai block is 30.4 MB reducible on the byte axis (171 → 87 pkgs, 112 → 63 MB with all in-flight work)

Open
#797 2 comments 0 reactions 1 assignee Claimed by @Varun-S10 View on GitHub
Dominant language
TypeScript
Stars
1.4k
Forks
205
Avg merge
3d 16h
Merged PRs (30d)
92

Description

Tracking issue for the byte-oriented half of the install-size work, alongside #626 (merged), #784 and #785.

## Why a separate tracking issue

#785's attribution table lists:

| Block | In tree | Exclusive |
|---|---:|---:|
| `@google/genai` + `google-auth-library` | 41 | 0 (irreducible) |

That's correct **on the package-count axis**, and for the right reason: `google-auth-library` is independently a direct dependency of `core`, so removing `@google/genai` frees almost no packages exclusively.

But package count and bytes have diverged. On the byte axis the same block is **30.4 MB reducible** — it just can't be fixed by dropping packages from our tree, only by upstream changes and one dedupe. That work is now filed across four repos and needs somewhere to be tracked.

The two efforts are complementary rather than competing:

| | packages | bytes |
|---|---:|---:|
| #784 + #785 (MikroORM, winston) | **−70** | −18.4 MB |
| genai block (this issue) | −14 | **−30.4 MB** |

## Where we are

All figures: clean install of `core`'s production dependencies into an empty directory, warm npm cache, Node v26.7.0, apparent bytes (`du -sb`). Package counts line up with #626's post-merge number (171/172), so this is measuring the same tree.

| milestone | packages | tree |
|---|---:|---:|
| before #626 | 591 | 403 MB |
| **today** (post #626) | **171** | **112.2 MB** |
| + #784 MikroORM optional | 126 | 95.5 MB |
| + #785 winston dropped | 101 | 93.8 MB |
| + genai block (this issue) | **87** | **63.4 MB** |

> **Update.** The genai block's largest single item — the `@google-cloud/vertexai` duplicate — is now split out as **#823** (`migrate to @google-cloud/agentplatform`), measured at **−16.0 MB / −8 packages**. See the update comments below for the revised ladder and the current blocker table.

**End state if everything in flight lands: 87 packages / 63.4 MB** — from 591 / 403 MB originally, and from 171 / 112.2 MB today.

## Condensed research

A clean `npm i @google/genai` is 32 MB / 41 packages. The four runtime dependencies are not where the weight is:

| component | size | note |
|---|---:|---|
| genai's own `dist/` | 15.4 MB | 7.9 MB of it sourcemaps, 2.1 MB `.d.ts` |
| `node-fetch` polyfill chain | 8.8 MB | reaches us via `google-auth-library → gaxios` |
| `protobufjs` chain | 5.7 MB | tokenizer-only; 0 bytes in the main bundle |
| `google-auth-library` (rest) | 1.9 MB | genuinely needed for Vertex |
| `ws`, `p-retry` | 0.2 MB | both fine, leave alone |

Four findings drive the 30.4 MB:

**1. `web-streams-polyfill` (8.8 MB) is never loaded.** It arrives via `gaxios → node-fetch → fetch-blob`. `fetch-blob`'s `streams.cjs` guards it behind `if (!globalThis.ReadableStream)` and prefers `node:stream/web`; Node has had global `ReadableStream` since 16.5. Traced with a `Module._resolveFilename` hook, it resolves **zero times** on Node 20+. It's 8.8 MB for a ~200 KB library because v3.3.3 ships twelve build variants each with a ~400 KB sourcemap. The cause is `gaxios` unconditionally doing `await import('node-fetch')` off-browser despite declaring `engines.node: ">=18"`.

**2. `@google/genai` is installed twice (~32 MB).** `@google-cloud/vertexai@1.12.0` pins `@google/genai: ^1.45.0` against our `^2.9.0`, so npm nests a second full copy. We already pay for this in code — `core/src/sessions/vertex_ai_session_service.ts:306` matches errors structurally because the nested copy's `ApiError` is a different class object. Tracked in #796.

**3. 7.9 MB of genai's tarball is sourcemaps, 70% of that duplicated `sourcesContent`.** The same ~83 source files are embedded three-plus times across six maps. `mappings` alone still resolves stack traces to the correct original file/line — verified with `--enable-source-maps`. For contrast, `gaxios` and `google-auth-library` sit in the same tree and already publish maps at 0% `sourcesContent`.

**4. `ws` and `p-retry` are the healthy ones.** `ws` (143 KB, zero transitive deps) cannot be replaced — Node's global `WebSocket` is WHATWG-spec and cannot send the `Authorization` / `x-goog-api-key` headers the Live API needs. `p-retry` is 4 KB bundled. Neither is worth touching.

Two performance findings surfaced incidentally and are filed separately; neither changes install size.

## Bugs and requests filed along the way

| # | repo | issue | status | adk-js impact |
|---|---|---|---|---|
| 1 | `googleapis/google-cloud-node` | [PR #9101](https://github.com/googleapis/google-cloud-node/pull/9101#issuecomment-5379058848) — gaxios: prefer native `fetch` | open PR, maintainer engaged; added the quantified size case | **−9.4 MB, −8 pkgs** |
| 2 | `googleapis/nodejs-agentplatform` | [#675](https://github.com/googleapis/nodejs-agentplatform/issues/675) — widen `@google/genai` range or make it a peer | open | **−15.2 MB, −6 pkgs** |
| 3 | `google/adk-js` | [#796](https://github.com/google/adk-js/issues/796) — dedupe, with interim root `overrides` | open | (same as #2, landable now) |
| 4 | `googleapis/js-genai` | [#1884](https://github.com/googleapis/js-genai/issues/1884) — strip `sourcesContent` from published maps | open | **−5.9 MB** |
| 5 | `googleapis/js-genai` | [#1883](https://github.com/googleapis/js-genai/issues/1883) — bump `protobufjs` `^7` → `^8.4.0` | open | **+3.8 MB** ⚠️ |
| 6 | `googleapis/js-genai` | [#1885](https://github.com/googleapis/js-genai/issues/1885) — tokenizer discards the `Buffer`, halving parse speed | open | 0 MB (1.93× faster) |
| 7 | `mapbox/pbf` | [#150](https://github.com/mapbox/pbf/issues/150) — `TEXT_DECODER_MIN_LENGTH` below measured crossover | open | 0 MB (we don't use pbf) |

Notes on the two that aren't straightforward wins:

- **#1883 is a net regression for us _today_, and a win once #803 lands.** `@opentelemetry/otlp-transformer@0.205.0` requires `protobufjs@^7.3.0`; if genai moves to `^8` both majors coexist (verified: 3.8 MB + 3.1 MB side by side). Upstream OTel has since moved to protobufjs 8 (0.210.0) and then dropped it entirely (0.220.0+) — we're just pinned to a range that can't reach it. **Bump OTel first (#803), then #1883 is worth −2.0 MB / −11 packages instead of +3.8 MB.**
- **#7 is pure community give-back**, from benchmarking done while evaluating whether genai could drop protobufjs. It doesn't affect ADK.

**Deliberately not filed:** replacing `protobufjs` with a lighter library. `pbf` measured 35× smaller installed and 17× smaller bundled with byte-identical output, but protobufjs is the canonical implementation and the ecosystem cost isn't worth it. Recorded here only so the option isn't rediscovered from scratch.

## What we can land ourselves, today

Only #796's interim workaround. Everything else needs upstream. Note that npm `overrides` are honoured **only in the root project's `package.json`** — verified; a dependency-level override is silently ignored. So this fixes our tree and CI, but **does nothing for people who `npm i @google/adk`**. Only #675 fixes it for consumers.

```json
{
"overrides": {
"@google-cloud/vertexai": { "@google/genai": "$@google/genai" }
}
}
```

Needs the Agent Engine session/memory/sandbox paths exercised first — it forces a library built against genai v1 onto v2. All five entry points we import load and construct correctly; see #796.

## Future work

Exploration paths identified but not yet filed or investigated in depth.

- [ ] **OpenTelemetry block — ~35 MB, 31 packages, the largest remaining after #784/#785.** (See also #803 for the version-bump half of this.) The three `exporter-*-otlp-http` packages, `sdk-trace-node`, `sdk-metrics`, `sdk-logs` and `resource-detector-gcp` are imported in exactly two files (`core/src/telemetry/{google_cloud,setup}.ts`) and `./telemetry/gcp` is already its own export subpath — the same shape #626 used for the four packages it moved. `@opentelemetry/api` should stay a hard dependency; it's the instrumentation seam. Needs a product call on whether telemetry stays on by default.
- [ ] **`lodash-es` — 2.6 MB for three functions.** Only `cloneDeep`, `isEmpty` and `isEqual` are used across `core/src` and `dev/src`. `structuredClone` is a Node global since 17 and covers the first; the other two are a few lines each.
- [ ] **`zod` — 6.3 MB, over half of it not runtime code.** Ships `src/` (2.9 MB of TypeScript sources) and a full `v3/` compat copy (604 KB). Same class of problem as js-genai#1884 and worth the same upstream ask. `zod/mini` exists but only helps bundle size, not install size. Not filed yet.
- [x] ~~**`protobufjs` (3.1 MB) now arrives via otel, not genai.**~~ Resolved: upstream OTel dropped protobufjs entirely in 0.220.0; we just need to bump off the `^0.205.0` line. Filed as **#803**, which also unblocks #1883. Note the bump trades ~33 more packages for ~10.6 MB less, so it needs a call on which axis wins.
- [ ] **genai's triplicate `.d.ts` — ~1.1 MB.** `genai.d.ts`, `node/node.d.ts` and `web/web.d.ts` are ~570 KB each and differ by 46 lines out of ~14,790. Mentioned as an aside in js-genai#1884; not filed separately.
- [ ] **Sourcemaps as an opt-in companion package.** If js-genai#1884 lands as `sourcesContent`-stripping (−5.9 MB) rather than full removal, a `@google/genai-debug` sibling package recovers the remaining ~2 MB while keeping full debuggability opt-in. Verified working: with the companion installed frames resolve to original TypeScript, without it Node falls back silently. Breaks under pnpm's isolated layout and Yarn PnP, so it degrades rather than fails. Lower priority.
- [ ] **`adm-zip` (#601)** has an unpatched DoS advisory and is a hard dependency — separate concern from size, but it's in the same 24-dependency list and worth resolving together.

## Reproduction

```bash
# baseline: production deps only, empty dir, warm cache
npm init -y && npm pkg set dependencies="$(node -p "JSON.stringify(require('./core/package.json').dependencies)")"
npm install && du -sb node_modules && npm ls --all --parseable | wc -l

# with the two landable/upstream fixes simulated
# overrides: node-fetch -> 9-line native-fetch shim, and the vertexai genai dedupe
# then strip sourcesContent from node_modules/@google/genai/**/*.map
```

Every number in this issue was produced by installing the tree and measuring it, not by summing registry metadata.

---

## Update — follow-ups resolved

Two future-work items above were "upgrade protobufjs for `@grpc/proto-loader` and otel". Both turned out not to need new upstream issues:

- **OpenTelemetry** already migrated to protobufjs 8 (0.210.0) and then removed it outright (0.220.0+). We're pinned to `^0.205.0`, which for a `0.x` version resolves to `>=0.205.0 <0.206.0` and can't reach either. Filed as **#803** — an adk-js version bump, not an upstream ask. It unblocks js-genai#1883.
- **`@grpc/proto-loader`** already has an open issue (grpc/grpc-node#3062) *and* an open PR (grpc/grpc-node#3067) awaiting review. Added the missing dependency-weight measurements there rather than filing a duplicate. Note this one is **not in our production tree** — it arrives via the `@google-cloud/opentelemetry-cloud-trace-exporter` optional peer and `@grpc/grpc-js` — so it gates nothing for ADK consumers.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.