Repository navigation
LangChain and LangGraph
-
What each model call was sent, and what came back, lands beside the conversation as a provider
body, cut against what the session already holds bypkg/providerbody— the same mechanism as
Claude Code's bodies. It is what the continuity check between calls runs on, and it shows a
nested agent its own prompt. Measured on the captured corpus, a twenty-turn conversation lands
at 13% of what it was on the wire, bodies included, and a conversation of a few short calls at
about a third.provider_bodies: falseon thelangsmith-ingestadapter turns it off. A
request on this wire names its call, the run's own id, and the assembler now joins a request by
the call it names, when exactly one names it; a request that names none, as Claude Code's do,
joins as before. A nested stream's records name the tool they ran inside as their prompt, not
the trace. -
asz assembles conversations from an agent built on LangChain or LangGraph without changing the
application. Thelangsmith-ingestadapter is a receiver the tracing client is pointed at, and
since that client is a dependency oflangchain-core, every LangChain application already
carries it: four environment variables are the whole integration. See
LangChain and LangGraph. -
What the receiver leaves out of its capability answer is what keeps it simple. The client
compresses only when told the receiver can read zstd, so not advertising it keeps every body
plain and keeps a compression library out of this project. A client that compresses anyway is
refused with the flag named. -
A run arrives more than once: posted when it starts, patched when it ends. Each arrival is its
own record sharing one call, and only the arrival carrying the run's end carries its content —
otherwise one call would emit the assistant message twice, in a round that called itself
verified. Work in progress lands as data, so a slow turn is visible before it finishes and a
process that died mid-turn still leaves what it had reported. -
A graph's own runs land trimmed. They were 30 of 39 runs and 74% of the bytes in one capture,
because each repeats the whole message list its model call already carries. The envelope and the
graph's metadata land, the repeated content does not, and the record says how many bytes went
with it. -
Ownership is a namespace, not a thread key: two applications can both use the project
productionand the thread123. A supplied key is never used as a path either, since
../outside,team/customer,_hiddenand会话-1all arrive as real thread keys. -
A demo application under
tests/apps/langchain, and the fixtures captured from it. It is a real
LangGraph application, one case per shape a conversation can take, driven by a stand-in model
over HTTP so it needs no API key, no network and no provider account.make langchain-capture
runs every case and writesMEASUREMENTS.mdfrom what it captured, so the numbers there never
drift from the fixtures. Fifteen cases cover a plain turn, three turns on one thread, a tool that
fails, parallel tool calls, a graph that loops, two threads at once, a slow tool whose runs are
posted before they finish, a process that dies mid-turn, large content, a sub-agent, a missing
thread key, two applications sharing one, keys that are not safe as paths, and the tracing
client's own decorator with no LangChain in it. -
A LangChain plugin,
apache-skywalking-asz-langchain, records what each of an agent's tool calls
changed on disk. It is thirty lines of glue:asz-changesdoes the scanning and diffing, and the
shim says when a tool call begins and ends, which LangChain has no way to say on its own. The
application imports nothing — a.pthruns a loader at interpreter start, and that loader reads
one environment variable and imports nothing else, so the plugin costs 0.01 s of interpreter
start when it is off. See LangChain Plugin. -
asz-changesis no longer Claude Code's alone. A tool with no shell command is no longer
classified read-only and skipped;tools.scopetakes an exact name, a regular expression
beginningre:, or*for every tool, with anexcludelist in the same three forms; the data
directory can be named withASZ_CHANGES_DATA; and the events have neutral names —tool.begin,
tool.end,tool.failed— meaning exactly what Claude Code's do. -
asz glossarytakes a dialect, andasz conversation -terms nativereads the one a session's
own records were landed in. A root can hold conversations from more than one runtime, and the
runtime's words have to be the ones belonging to what is being read. -
The
changesadapter may be named once per recorder directory, so a machine that runs Claude
Code's plugin and a LangChain shim collects both. Two entries that read one directory are
refused, and the adapter's old name counts as the same adapter; before, a second entry silently
replaced the first. -
claude_code.token.usageis derived from Claude Code's transcripts alone. A LangChain
conversation in the same root carries calls and usage too, and was counted into it. -
A session sent before its transcript landed - the recorder's files often come first - is
attributed by the name asz gave it, so a LangChain session's change records no longer leave
under Claude Code's name. -
The LangChain plugin is published to PyPI after the vote, as the Homebrew tap and the apt
repository are written after it. How to Release has the
step, thepypiskill does it, andpreparewrites the version into the plugin.
Renamed
The change recorder is not Claude Code's: it records what any runtime's tool call changed, and a
LangChain application now produces the same records. The names say so, and every one of them is
carried across rather than dropped.
These are breaking, and deliberately so: the project is before 1.0, and carrying two names for
every artifact would cost more than the upgrade does.
-
The binary
asz-claude-pluginisasz-changes. Reinstall the Claude Code plugin once so its
hooks call the new name;install/claude-code-plugin.shdoes it and carries the plugin's data
across. -
The Debian package
asz-claude-codeisasz-changes. Run
sudo apt install asz-changes && sudo apt remove asz-claude-code. -
One Homebrew formula.
aszinstalls both binaries, where two formulae used to fetch the
identical archive and the identical checksum and install one binary each — which only invited a
machine to end up withaszof one version and the recorder of another. Run
brew uninstall asz-claude-code. -
The Claude Code plugin
asz-changesisfile-changes, so the plugin and the program it runs
no longer share a name. A plugin's data directory is named after the plugin, so the installer
copies the old one across before the first session: the settings, and any change record asz had
not collected yet. Nothing is lost by upgrading. -
The adapter
claude-code-changesischanges. A configuration that still names it is read
as the same adapter, and so is every header landed under it, which keeps its name forever. -
The recorder's data directory can be named with
ASZ_CHANGES_DATA.CLAUDE_PLUGIN_DATAstill
works, because Claude Code sets it for every hook.
Install
- Install tells a Homebrew user to run
brew trust https://github.com/apache/skywalking-ai-sessionizerbefore tapping. Homebrew 7 loads
a tap that is not its own only afterbrew trust, and it matches auser/repositoryentry only
against a tap at its default remote, which this one is not: the repository is not named
homebrew-skywalking-ai-sessionizer. Without the trust,brew tapstops with
Refusing to load formula ... from untrusted tap.
Release
-
The install scripts put
asz-changesbesideasz, as Homebrew and apt do.asz-changessays
its own name in its help and its version, where it still saidasz-claude-plugin. -
A failing test of the LangChain plugin fails
make check; it was reported as Python not
installed. -
asz.yamlwritesheadersout, and the test that holds it to spelling out every value reads
the file's keys rather than the loaded values, which the defaults had filled in. -
A scenario's coverage check judges each change file against the cursor it was landed behind,
as collection does, and a cursor names the recorder root its file is under: two roots can hold
one session under one relative path, and one cursor for both stopped both. A cursor from
before 0.5.0 belongs to the root whose file it was read from, by the file's device and inode
and the bytes before its offset together, and is written with that root on the next pass; one whose
file was copied or restored since is read again from the start, and the index keeps the first
record of an id. A session with two plugin directories was never reported as
covered, so a scenario's removal waited for ever. -
Configuration says which of several agents,
instances and machines one asz serves, and what needs one process each. -
Upgrading from 0.4.0 is one ordered list. The plugin
installer finds the plugin under its old name and uninstalls it with its data kept, copies the
data beside the new directory and renames it into place - so a copy that stops half way is
done again rather than skipped - and finds Claude Code's directory the way asz does. -
The downloads page lists the source package and the binary archives only.
publishlisted the
Debian packages there too, beside the archives of the same binaries. They stay in the release
directory with their signatures and checksums, and apt installs them from the repository on the
website.
Where to get it
- The Apache release of 0.5.0 is the source package. The binary packages for macOS, Linux and Windows, and the Debian packages, are conveniences built from it. The SkyWalking downloads page links each package with its signature and checksum.
- The files attached to this GitHub release are the same signed packages, each with its
.ascsignature and.sha512checksum. Verify them against https://downloads.apache.org/skywalking/KEYS, as Install describes. - To build from the source package, see Install.
- Documentation: https://github.com/apache/skywalking-ai-sessionizer/blob/v0.5.0/docs/README.md
- Full changelog: https://github.com/apache/skywalking-ai-sessionizer/blob/v0.5.0/docs/en/changes/changes.md