@ramarivera/coding-agent-langfuse 0.1.47 → 0.1.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/backfill.js +68 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,10 +97,13 @@ global rule:
|
|
|
97
97
|
|
|
98
98
|
Matched values are emitted on root trace metadata, observation metadata, and
|
|
99
99
|
top-level OTLP attributes such as `project.tags`, `project.group`, and
|
|
100
|
-
`project.owner`.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
`project.owner`. When the session cwd is inside a Git repository, the exporter
|
|
101
|
+
also emits `git.worktree.path`, `git.branch`, and `git.commit` plus matching
|
|
102
|
+
Langfuse metadata fields. The root span also sets `langfuse.trace.tags`, so
|
|
103
|
+
Langfuse custom dashboards can filter/group by tags, metadata, branch, or
|
|
104
|
+
worktree. Historical repair runs use the same OTLP builder as live follow mode,
|
|
105
|
+
so rerunning with `--force` can backfill newly added tags and Git metadata for
|
|
106
|
+
existing sessions.
|
|
104
107
|
|
|
105
108
|
Use a non-default global config path with:
|
|
106
109
|
|
package/dist/backfill.js
CHANGED
|
@@ -35,6 +35,7 @@ const defaultPathTagsConfigPath = join(homedir(), ".config/coding-agent-langfuse
|
|
|
35
35
|
const currentHost = hostname();
|
|
36
36
|
const projectLocalConfigFile = ".langfuse-ca.json";
|
|
37
37
|
const projectLocalConfigCache = new Map();
|
|
38
|
+
const gitMetadataCache = new Map();
|
|
38
39
|
const kimiFirepassRates = {
|
|
39
40
|
input: 2,
|
|
40
41
|
output: 8,
|
|
@@ -244,8 +245,60 @@ function mergeProjectMetadata(cwd, config) {
|
|
|
244
245
|
projectFolder: pathTags.projectFolder ?? project.projectFolder,
|
|
245
246
|
tags: pathTags.tags,
|
|
246
247
|
metadata: pathTags.metadata,
|
|
248
|
+
git: loadGitMetadata(cwd),
|
|
247
249
|
};
|
|
248
250
|
}
|
|
251
|
+
function loadGitMetadata(cwd) {
|
|
252
|
+
const gitCwd = nearestExistingDirectory(cwd);
|
|
253
|
+
if (!gitCwd)
|
|
254
|
+
return {};
|
|
255
|
+
const cached = gitMetadataCache.get(gitCwd);
|
|
256
|
+
if (cached)
|
|
257
|
+
return cached;
|
|
258
|
+
const worktreePath = gitOutput(gitCwd, ["rev-parse", "--show-toplevel"]);
|
|
259
|
+
if (!worktreePath) {
|
|
260
|
+
gitMetadataCache.set(gitCwd, {});
|
|
261
|
+
return {};
|
|
262
|
+
}
|
|
263
|
+
const branch = gitOutput(gitCwd, ["branch", "--show-current"]) ??
|
|
264
|
+
gitOutput(gitCwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
265
|
+
const commit = gitOutput(gitCwd, ["rev-parse", "--verify", "HEAD"]);
|
|
266
|
+
const metadata = {
|
|
267
|
+
worktreePath,
|
|
268
|
+
branch: branch === "HEAD" ? undefined : branch,
|
|
269
|
+
commit,
|
|
270
|
+
};
|
|
271
|
+
gitMetadataCache.set(gitCwd, metadata);
|
|
272
|
+
return metadata;
|
|
273
|
+
}
|
|
274
|
+
function gitOutput(cwd, args) {
|
|
275
|
+
try {
|
|
276
|
+
const output = execFileSync("git", ["-C", cwd, ...args], {
|
|
277
|
+
encoding: "utf8",
|
|
278
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
279
|
+
}).trim();
|
|
280
|
+
return output.length > 0 ? output : undefined;
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function nearestExistingDirectory(path) {
|
|
287
|
+
if (!path)
|
|
288
|
+
return undefined;
|
|
289
|
+
let current = path;
|
|
290
|
+
while (true) {
|
|
291
|
+
try {
|
|
292
|
+
return statSync(current).isDirectory() ? current : dirname(current);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
const parent = dirname(current);
|
|
296
|
+
if (parent === current)
|
|
297
|
+
return undefined;
|
|
298
|
+
current = parent;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
249
302
|
function usage() {
|
|
250
303
|
return `Usage: coding-agent-langfuse-backfill [options]
|
|
251
304
|
|
|
@@ -1854,6 +1907,9 @@ function toOtlp(events, options = {}) {
|
|
|
1854
1907
|
attr("langfuse.trace.metadata.project_folder", firstProject.projectFolder),
|
|
1855
1908
|
attr("langfuse.trace.metadata.project_tags", nonEmptyArray(firstProject.tags)),
|
|
1856
1909
|
attr("langfuse.trace.metadata.path_tags", nonEmptyRecord(firstProject.metadata)),
|
|
1910
|
+
attr("langfuse.trace.metadata.git_worktree_path", firstProject.git.worktreePath),
|
|
1911
|
+
attr("langfuse.trace.metadata.git_branch", firstProject.git.branch),
|
|
1912
|
+
attr("langfuse.trace.metadata.git_commit", firstProject.git.commit),
|
|
1857
1913
|
attr("langfuse.trace.tags", nonEmptyArray(firstProject.tags)),
|
|
1858
1914
|
attr("langfuse.trace.metadata.import_payload_version", payloadVersion(first)),
|
|
1859
1915
|
attr("langfuse.trace.metadata.import_state_identity", importIdentity(first)),
|
|
@@ -1870,6 +1926,9 @@ function toOtlp(events, options = {}) {
|
|
|
1870
1926
|
attr("langfuse.observation.metadata.project_folder", firstProject.projectFolder),
|
|
1871
1927
|
attr("langfuse.observation.metadata.project_tags", nonEmptyArray(firstProject.tags)),
|
|
1872
1928
|
attr("langfuse.observation.metadata.path_tags", nonEmptyRecord(firstProject.metadata)),
|
|
1929
|
+
attr("langfuse.observation.metadata.git_worktree_path", firstProject.git.worktreePath),
|
|
1930
|
+
attr("langfuse.observation.metadata.git_branch", firstProject.git.branch),
|
|
1931
|
+
attr("langfuse.observation.metadata.git_commit", firstProject.git.commit),
|
|
1873
1932
|
attr("langfuse.observation.metadata.import_payload_version", payloadVersion(first)),
|
|
1874
1933
|
attr("langfuse.observation.metadata.import_state_identity", importIdentity(first)),
|
|
1875
1934
|
attr("langfuse.observation.metadata.langfuse_id_identity", langfuseIdIdentity(first)),
|
|
@@ -1881,6 +1940,9 @@ function toOtlp(events, options = {}) {
|
|
|
1881
1940
|
attr("project.tags", nonEmptyArray(firstProject.tags)),
|
|
1882
1941
|
attr("project.group", firstProject.metadata.project_group),
|
|
1883
1942
|
attr("project.owner", firstProject.metadata.project_owner),
|
|
1943
|
+
attr("git.worktree.path", firstProject.git.worktreePath),
|
|
1944
|
+
attr("git.branch", firstProject.git.branch),
|
|
1945
|
+
attr("git.commit", firstProject.git.commit),
|
|
1884
1946
|
].filter((item) => Boolean(item));
|
|
1885
1947
|
const rootSpan = {
|
|
1886
1948
|
traceId: traceId(first),
|
|
@@ -1927,6 +1989,9 @@ function toOtlp(events, options = {}) {
|
|
|
1927
1989
|
attr("langfuse.observation.metadata.project_folder", eventProject.projectFolder),
|
|
1928
1990
|
attr("langfuse.observation.metadata.project_tags", nonEmptyArray(eventProject.tags)),
|
|
1929
1991
|
attr("langfuse.observation.metadata.path_tags", nonEmptyRecord(eventProject.metadata)),
|
|
1992
|
+
attr("langfuse.observation.metadata.git_worktree_path", eventProject.git.worktreePath),
|
|
1993
|
+
attr("langfuse.observation.metadata.git_branch", eventProject.git.branch),
|
|
1994
|
+
attr("langfuse.observation.metadata.git_commit", eventProject.git.commit),
|
|
1930
1995
|
attr("langfuse.observation.metadata.model", modelName ?? event.model),
|
|
1931
1996
|
attr("langfuse.observation.metadata.provider", event.provider),
|
|
1932
1997
|
attr("langfuse.observation.metadata.import_payload_version", payloadVersion(event)),
|
|
@@ -1953,6 +2018,9 @@ function toOtlp(events, options = {}) {
|
|
|
1953
2018
|
attr("project.tags", nonEmptyArray(eventProject.tags)),
|
|
1954
2019
|
attr("project.group", eventProject.metadata.project_group),
|
|
1955
2020
|
attr("project.owner", eventProject.metadata.project_owner),
|
|
2021
|
+
attr("git.worktree.path", eventProject.git.worktreePath),
|
|
2022
|
+
attr("git.branch", eventProject.git.branch),
|
|
2023
|
+
attr("git.commit", eventProject.git.commit),
|
|
1956
2024
|
attr("role", event.role),
|
|
1957
2025
|
attr("agent.model", event.model),
|
|
1958
2026
|
attr("agent.provider", event.provider),
|