adk-go — Engineering Performance
7 engineers all time · Jun 2025 – Sep 2026 · built 2026-09-30 · GitHub
Performance snapshot
Today's rolling 90-day reading for adk-go, compared with the start of the series. Pick a window to move that comparison point.
Avg. perf / dev / mo
+67.9%
1.00 → 1.68 ETV
Active engineers
−40.0%
5.0 → 3.0
Features
−4.5pp
35.2% → 30.7%
vs. Google
0.60x
1.1x → 0.60x · −40% below
adk-go vs. Google
Per-engineer ETV for adk-go against Google as a whole. Both lines are 90-day rolling averages scaled to a 30-day month, so they share one axis and can be read against each other at any point. Pick a window to zoom the chart to it.
Performance Composition
Each month's output split by type of work: Features (new value), Maintenance (sustaining systems), Tests, Docs, and Fixes (rework). The yellow line is output per engineer, so when it rises each engineer is delivering more, whatever the team size did. Unit: Engineering Throughput Value (ETV).
Engineering capacity
Effective engineers behind adk-go, against its pre-AI baseline. Each subject has its own: adk-go's is 1.00 ETV / dev / mo, its first reading in Q3 2025. Per-engineer ETV divided by that gives a capacity multiple, and that multiple applied to the engineers active in the trailing 90 days turns it into engineer-equivalents. The line is the real headcount, so the gap between line and area is what the leverage is worth. Because each baseline is its own, every subject opens at 1.0x on its first day: multiples measure improvement and are not comparable between subjects.
Knowledge concentration
How dependent is this repo on a small number of engineers? Higher top-1 share = higher key-person risk.
João Westerberg owns 22.8 % of commits.
Behind the numbers
Written summary of the work completed each month.
No monthly reports available yet.
Top engineers
Most impactful commits
Top 10 by ETV in the all-time window.
- 6.7ETVfeat(compaction): summarize completed invocations as the conversation grows (#1232) Adds the compaction library, the engine behind it, and the sliding-window strategy the runner drives once an invocation finishes. compaction.Config is the public surface: which strategy to run, and a Summarizer to run it with. A Summarizer returns content and token usage rather than a finished event, so third-party code cannot declare an authorship, a state delta, an agent transfer or the range of history to delete. The framework builds the event and derives the range from the events it handed over. The default LLMSummarizer carries a timeout, an allow-list of generation settings, and a check that the generation was not truncated or filtered, because a truncated summary stored as a real one silently loses the turns it covers. The engine keeps one definition of coverage: inside the recorded range and not named as a hole. Window selection filters events out of the middle of its own span, by branch, by isolation scope and by what a retained tail holds back, so a range alone would cover gaps that nothing summarized. Prompt assembly substitutes a summary for the events it covers and leaves the rest raw, and an unanswered tool call is never separated from its response. A summarizer sees copies built field by field from what a summarizer is for, not the session's live events, so it cannot rewrite stored history through a pointer it was handed. A race guard re-reads the session before and after the plugin pass and discards a summary if anything landed inside its range while it was being produced. Telemetry is here rather than in a slice of its own because the compactor calls it directly. The span stays open until the caller reports what became of the summary, so the five discard paths no longer report success for an event that reached no session. Compaction never modifies or deletes history. It appends, and only the prompt shrinks.João Westerberg · e9da6686 · 2026-08-31
- 2.4ETVfeat(compaction): compact mid-invocation once the prompt crosses a threshold (#1234) The sliding window replaces each group of invocations with one summary, and summaries are never re-summarized, so it reduces prompt size by a constant factor rather than bounding it. Tail retention is what bounds it. It runs inside an invocation, before a model call, once the prompt passes TokenThreshold. It summarizes everything but the most recent events and seeds each new window with the previous summary, so history stays one rolling summary plus a raw tail however long the conversation runs. Because it runs mid-turn it also catches a single long tool-calling turn that inflates the prompt on its own, which a post-invocation strategy cannot see until the turn is over. The live turn's own question is held back from the window. Summarizing it means summarizing the instruction currently being carried out, and EventRetentionSize cannot protect it, because it counts events and a turn is not a fixed number of them. Enable this or the sliding window, not both. They share a candidate rule: tail retention summarizes the events no compaction already covers, and the sliding window covers everything it reaches, so with both enabled tail retention never finds enough uncovered events to fire. The package documentation says so, with the measurements. adk-python starves its own token-threshold strategy the same way.João Westerberg · 664cec63 · 2026-08-31
- 1.8ETVfeat(adkrest): add authentication and authorization to the REST API (#1561) * feat(adkrest): add authentication and authorization to the REST API * Moved authn/authz to the server folder * Introduced authz.NewNoop() / authz.NewStrict() * Changed Identity to CalllerKarol Droste · fb17148e · 2026-09-11
- 1.7ETVfeat(server): make context compaction reachable from every serving surface (#1235) * feat(session): carry a context-compaction record on a session event Adds session.EventCompaction and session.EventRef, the stored shape a compaction summary takes, and makes every backend keep it faithfully. A summary is an ordinary event whose Actions.Compaction names the timestamp range it stands in for, the events inside that range it does not stand in for, and the summary content itself. Nothing here produces one. This is the record as stored data, so the storage contract is settled before anything writes to it. The record is the framework's alone. A tool handler, an agent callback and a workflow tool node each hold an EventActions that lands on the persisted event, so each clears the field, and the REST mapper drops an inbound one. A record decides which stored events every later prompt drops, so anything able to plant one can erase history and put content of its own in its place. Two obligations land on session.Service, both enforced by the shared conformance suite. An event arriving without an ID is assigned one in place, because a stored event that cannot be named cannot be referred to by a record. And Actions.Compaction survives the round trip: a summary carries its content only there, with no content and no deltas, so a backend that decides what to persist by looking at content or deltas drops it silently and the same range is summarized and billed again every turn. The conformance cases use nanosecond timestamps so a backend that rounds is visible, and one case checks that a hole still names its event after a round trip. That is the property that loses conversation when it fails. * feat(compaction): summarize completed invocations as the conversation grows Adds the compaction library, the engine behind it, and the sliding-window strategy the runner drives once an invocation finishes. compaction.Config is the public surface: which strategy to run, and a Summarizer to run it with. A Summarizer returns content and token usage rather than a finished event, so third-party code cannot declare an authorship, a state delta, an agent transfer or the range of history to delete. The framework builds the event and derives the range from the events it handed over. The default LLMSummarizer carries a timeout, an allow-list of generation settings, and a check that the generation was not truncated or filtered, because a truncated summary stored as a real one silently loses the turns it covers. The engine keeps one definition of coverage: inside the recorded range and not named as a hole. Window selection filters events out of the middle of its own span, by branch, by isolation scope and by what a retained tail holds back, so a range alone would cover gaps that nothing summarized. Prompt assembly substitutes a summary for the events it covers and leaves the rest raw, and an unanswered tool call is never separated from its response. A summarizer sees copies built field by field from what a summarizer is for, not the session's live events, so it cannot rewrite stored history through a pointer it was handed. A race guard re-reads the session before and after the plugin pass and discards a summary if anything landed inside its range while it was being produced. Telemetry is here rather than in a slice of its own because the compactor calls it directly. The span stays open until the caller reports what became of the summary, so the five discard paths no longer report success for an event that reached no session. Compaction never modifies or deletes history. It appends, and only the prompt shrinks. * feat(compaction): compact mid-invocation once the prompt crosses a threshold The sliding window replaces each group of invocations with one summary, and summaries are never re-summarized, so it reduces prompt size by a constant factor rather than bounding it. Tail retention is what bounds it. It runs inside an invocation, before a model call, once the prompt passes TokenThreshold. It summarizes everything but the most recent events and seeds each new window with the previous summary, so history stays one rolling summary plus a raw tail however long the conversation runs. Because it runs mid-turn it also catches a single long tool-calling turn that inflates the prompt on its own, which a post-invocation strategy cannot see until the turn is over. The live turn's own question is held back from the window. Summarizing it means summarizing the instruction currently being carried out, and EventRetentionSize cannot protect it, because it counts events and a turn is not a fixed number of them. Enable this or the sliding window, not both. They share a candidate rule: tail retention summarizes the events no compaction already covers, and the sliding window covers everything it reaches, so with both enabled tail retention never finds enough uncovered events to fire. The package documentation says so, with the measurements. adk-python starves its own token-threshold strategy the same way. * feat(server): make context compaction reachable from every serving surface Threads EventsCompactionConfig through the REST server, the Pub/Sub and Eventarc triggers, the launcher, A2A and Agent Engine, so an application gets the same compaction behaviour whichever way it is served. Every surface refuses a config it cannot actually serve, at startup, by dry-running runner.New per application. The config is validated inside runner.New and a runner is built per request, so without this a process starts cleanly, reports healthy, and fails every request with an error naming nothing an operator can act on. A compaction failure no longer fails the turn that produced it, on any of them. Compaction runs after the agent has answered and after its events are persisted, so a failure there means only that a later prompt will be larger. Each surface recognises ErrCompaction, logs it and carries on, rather than returning a 500 that discards the answer, streaming an error to a client that already has one, or NACKing a Pub/Sub message that was handled. The trigger surfaces warn when a sliding window is configured there, because each delivery gets a session of its own and history never accumulates, so the interval is never reached. The launcher and server config fields say the setting is process-wide: one server serves many applications through its agent loader and they all get the same config and the same summarizer, so applications needing different compaction should run separately.João Westerberg · 96f795c5 · 2026-08-31
- 1.7ETVMove llm agent code to new packages. (#68) * Move llm agent code to new packages. 1. Now it uses new session, llm, agent, llmagent packages. 2. LLMAgent request processors are now moved to the internal package. 3. Test case logic is kept the same everywhere. 4. Diffs with the doc (these are temporary to simplify the migration): * Tool interface has ProcessRequest method and llm.Request has Tools field. This is to keep current request_processors logic for llmagent. * agent.Config accepts Self field to allow setting correct entity as a parent.Dmitry Pasiukevich · 3c040a32 · 2025-08-18
- 1.4ETVRename sessionservice -> session.Service (#133) * Rename sessionservice -> session.Service Removed StoredSession and Session.ID for ADK conformityDmitry Pasiukevich · edad99a0 · 2025-10-02
- 1.4ETVfeat: Add conformance test recording plugin (#890) * Add conformance test recording plugin. * lint fix * lint fix * lint fixJoão Westerberg · 6516f76c · 2026-05-29
- 1.3ETVfix: remoteagent partial response handling leads to data duplication (#545) * set explicit partial event marking and maintain a temporary artifact for streaming events * fix metadata merging * fix duplicating long running function call * fix citations not included * explicit mark as turnComplete on event conversion * fix response duplication for partial and non-partial events * aggregation logic tests * gemini mock streaming test * added failing test cases * lint * fix partial not reset on errors * fix testYaroslav · ab29975f · 2026-02-10
- 1.3ETVfeat: add plugin package (#480) * Add plugin system * Add plugin system * Removed test with redundant check, tool.Context can never be cast to agent.InvocationContext. * lint fixes * Fix tests plugin name * Added symmetrical after callback execution for plugins * Moved pluginManager to internal context value * Fix runOnToolErrorCallbacks for tool not found * Change plugins fields to private * Small test fixJoão Westerberg · 781f76da · 2026-01-20
- 1.2ETVAgent Engine support (#749) * Agent Engine support * Test fix * Fixes * Fixes * Error handling fix * Fixes * Fixes in encode * Fixed encode * sseTimeout fix * reflection fix * sseWriteTime fix * fix * encode fixes * linter fixes * various fixes * encode tests * linter fixes * fix * linter fixes * Fixes in encodeKarol Droste · a586408f · 2026-04-23