Skip to content

feat(appkit): mlflow tracing for agents (stack 1/5) - #477

Merged
MarioCadenas merged 9 commits into
mainfrom
pr/agent-evals-1-tracing
Aug 13, 2026
Merged

feat(appkit): mlflow tracing for agents (stack 1/5)#477
MarioCadenas merged 9 commits into
mainfrom
pr/agent-evals-1-tracing

Conversation

@MarioCadenas

Copy link
Copy Markdown
Collaborator

Stack 1/5 · targets main.

Adds MLflow tracing to the agents plugin. Agent turns and tool calls are traced to a bound MLflow experiment via the mlflow-tracing SDK (OpenTelemetry under the hood). Tracing is a no-op unless the plugin's optional experiment resource is set (MLFLOW_EXPERIMENT_ID); auth is resolved from the app's own Databricks credentials, so no tokens or OTLP headers are wired by hand.

  • withAgentSpan wraps each turn (AGENT span) and tool dispatch (TOOL span, auto-nested).
  • Sets trace-table Request/Response previews and the mlflow.traceName tag.
  • experiment optional resource in the agents manifest.

This is the base of a 5-PR stack that builds out an agent evaluation framework. Reviewable on its own — touches only the agents plugin.

@MarioCadenas
MarioCadenas requested a review from a team as a code ownerJuly 16, 2026 14:26
@github-actions

github-actionsBot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 840 KB (+5.9 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

distrawgzip
JS (runtime)869 KB (+6.4 KB)303 KB (+2.4 KB)
Type declarations314 KB109 KB (+9 B)
Source maps1.7 MB (+12 KB)566 KB (+4.2 KB)
Other11 KB3.7 KB
Total2.9 MB (+18 KB)981 KB (+6.6 KB)
Per-entry composition (own code — deps external (as shipped))
EntryInitial (gz)Lazy (gz)Total (gz)node_modules (min)Own code (min)
.88 KB2.5 KB91 KBexternal288 KB
./beta49 KB (+702 B)459 B49 KB (+702 B)external143 KB (+2.1 KB)
./type-generator21 KB0 B21 KBexternal60 KB

Chunks:

EntryChunkLoadSize (gz)
.index.jsinitial84 KB
.utils.jsinitial4.0 KB
.remote-tunnel-manager.jslazy2.5 KB
./betabeta.jsinitial33 KB
./betastream-manager.jsinitial5.8 KB
./betawide-event-emitter.jsinitial3.2 KB
./betadatabricks.jsinitial3.0 KB
./betaconfiguration.jsinitial2.1 KB
./betaservice-context.jsinitial1.3 KB
./betaclient.jsinitial431 B
./betaclient-options.jsinitial220 B
./betasupervisor-api.jslazy194 B
./betadatabricks.jslazy142 B
./betaindex.jslazy123 B
./type-generatorindex.jsinitial21 KB

@databricks/appkit-ui

npm tarball (packed): 342 KB (-1 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

distrawgzip
JS (runtime)390 KB130 KB
Type declarations228 KB83 KB (+1 B)
Source maps753 KB248 KB
CSS16 KB3.3 KB
Total1.4 MB464 KB (+1 B)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
EntryInitial (gz)Lazy (gz)Total (gz)node_modules (min)Own code (min)
./js5.3 KB49 KB55 KB208 KB14 KB
./js/beta20 B0 B20 B0 B0 B
./react432 KB49 KB480 KB1.3 MB175 KB
./react/beta1.0 KB0 B1.0 KB0 B1.9 KB

Chunks:

EntryChunkLoadSize (gz)
./jsindex.jsinitial5.2 KB
./jschunkinitial120 B
./jsapache-arrowlazy49 KB
./js/betabeta.jsinitial20 B
./reactindex.jsinitial430 KB
./reacttslibinitial2.1 KB
./reactapache-arrowlazy49 KB
./react/betabeta.jsinitial1.0 KB

Trace agent turns and tool calls to MLflow via the mlflow-tracing SDK.
Adds an optional 'experiment' resource to the agents plugin; when bound
(MLFLOW_EXPERIMENT_ID), each turn opens an AGENT span and each tool call a
nested TOOL span, with auth resolved from the app's Databricks credentials.
A turn's trace can be linked to an evaluation run via mlflow.sourceRun.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
currentTraceId() used getLastActiveTraceId(), which is only set when a
root span ends (on export). Read mid-turn it returned the previous
turn's id — or another turn's under concurrency. Read the context-active
span instead, matching linkTraceToRun.
Also read mlflowRunId from the validated schema (capped at 64 chars)
instead of casting raw req.body, and drop two unused imports. Adds
tracing tests covering trace-id source and output auto-capture.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
/invocations and /responses drove tool calls through traceTool but had
no traceAgent wrapper, so with tracing enabled each tool call became an
orphan root TOOL trace instead of nesting under a turn. Wrap the adapter
run in traceAgent, mirroring the streaming path, so both surfaces produce
the same trace shape. Also drop a dead flushTraces mock left from the
flushAgentTraces removal.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas
MarioCadenasforce-pushed the pr/agent-evals-1-tracing branch from 412b08d to f517306CompareAugust 5, 2026 15:22
@github-actions

github-actionsBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 31686134891 -R databricks/appkit -n appkit-template-0.59.0-pr.a52e202-pr-agent-evals-1-tracing-477 -D appkit-pr-477 \
&& unzip -o "appkit-pr-477/appkit-template-0.59.0-pr.a52e202-pr-agent-evals-1-tracing-477.zip" -d "appkit-pr-477" \
&& databricks apps init --template "appkit-pr-477"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

@MarioCadenas
MarioCadenas enabled auto-merge (squash) August 11, 2026 09:33
Comment threadpackages/appkit/src/plugins/agents/schemas.ts
Comment threadpackages/appkit/src/plugins/agents/agents.ts
/invocations and /responses already produced full agent+tool traces, but
unlike /chat they never linked the trace to an MLflow run or surfaced the
trace id. Add mlflowRunId to invocationsRequestSchema and, in
_runAgentNonStreaming, call linkTraceToRun and return mlflow_trace_id in the
response envelope (only when tracing is enabled) so an eval runner driving
the non-streaming surface can group traces under a run and attach
assessments.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Trim verbose comments to their load-bearing fact, drop duplicated rationale,
and reuse the existing seedPlugin helper for the invoke tracing tests.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas
MarioCadenas merged commit 8dd909d into mainAug 13, 2026
10 checks passed
@MarioCadenas
MarioCadenas deleted the pr/agent-evals-1-tracing branch August 13, 2026 09:27
IamGalymzhan added a commit that referenced this pull request Aug 19, 2026
**Restore three upstream mlflow tests dropped by the merge.** Resolving the
route-handler-errors conflict with `--ours` took the whole file from this branch,
discarding main's non-conflicting additions from PR #477: the `vi.mock("../mlflow")`
hoist, the linkTraceToRun/mockTraceId resets, the parameterised seedPlugin(adapter),
the seedEchoPlugin/invoke helpers, and the three trace tests. 15 upstream tests,
12 here, and nothing failed to say so. Restored from 9538d58 alongside this
branch's createTestPluginContext rewrite of the aliases test — the two changes are
independent.
**close() now memoizes itself, not just the phases.** runOnce() guaranteed the
teardown body ran once, but resetCoreSingletons() sat outside it, so every call
reset again: `await a.close(); createApp(); await a.close()` dropped the second
app's singletons. The reset is also skipped when the budget expired, because the
phases are still running and still own those instances. Only reachable through the
raw AppHandle — createTestApp's wrapper memoizes, which is why the harness-level
test could not see it and the regression test lives in app-close.integration.
**Refcount singleton ownership.** The env baseline was already refcounted so
overlapping harness apps compose, while the singleton layer reset on every boot
and every close — so booting B rebound A's ServiceContext and CacheManager, and
closing A while B was live left B with none at all. claimCoreSingletons/
releaseCoreSingletons now follow the same model as the env baseline: first boot
claims, last close drops.
**Fake the on-behalf-of client.** The kit promised "no workspace, no credentials,
no network", but createApp({ client }) installs only the service principal; an
`obo` request reached ServiceContext.createUserContext, which builds a real SDK
client from process.env.DATABRICKS_HOST. The harness now stubs that for the app's
lifetime and restores it on close, mirroring fixtures.ts's createUserContextSpy.
Every fix has a test verified by reintroducing the bug. Two of those tests needed
a second attempt: asserting the OBO client's host does not discriminate, since a
real client carries the same DATABRICKS_HOST string — the test now asserts the
harness's mock recorded the call. The probe plugin gained a route that calls the
client under asUser, because the existing /as-user route only reads ctx.userId,
which is how this escaped notice.
4517 tests pass; typecheck, lint, and format clean.
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Sign up for freeto 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.

2 participants

@MarioCadenas@atilafassina