explorbot 0.1.9 → 0.1.11
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 +27 -1
- package/bin/explorbot-cli.ts +86 -15
- package/boat/api-tester/src/ai/curler-tools.ts +3 -3
- package/boat/api-tester/src/ai/curler.ts +1 -1
- package/boat/api-tester/src/apibot.ts +2 -2
- package/boat/api-tester/src/config.ts +1 -1
- package/dist/bin/explorbot-cli.js +85 -14
- package/dist/boat/api-tester/src/ai/curler-tools.js +2 -2
- package/dist/boat/api-tester/src/apibot.js +2 -2
- package/dist/package.json +2 -2
- package/dist/rules/navigator/output.md +9 -0
- package/dist/rules/navigator/verification-actions.md +2 -0
- package/dist/src/action-result.js +23 -1
- package/dist/src/action.js +46 -38
- package/dist/src/ai/bosun.js +16 -2
- package/dist/src/ai/conversation.js +39 -0
- package/dist/src/ai/experience-compactor.js +235 -50
- package/dist/src/ai/historian/codeceptjs.js +109 -0
- package/dist/src/ai/historian/experience.js +320 -0
- package/dist/src/ai/historian/mixin.js +2 -0
- package/dist/src/ai/historian/playwright.js +145 -0
- package/dist/src/ai/historian/utils.js +18 -0
- package/dist/src/ai/historian.js +19 -398
- package/dist/src/ai/navigator.js +133 -80
- package/dist/src/ai/pilot.js +254 -13
- package/dist/src/ai/planner/subpages.js +1 -30
- package/dist/src/ai/planner.js +33 -13
- package/dist/src/ai/provider.js +55 -18
- package/dist/src/ai/rerunner.js +3 -3
- package/dist/src/ai/researcher/deep-analysis.js +1 -1
- package/dist/src/ai/researcher/fingerprint-worker.js +1 -1
- package/dist/src/ai/researcher/locators.js +1 -1
- package/dist/src/ai/researcher/sections.js +8 -1
- package/dist/src/ai/researcher.js +43 -41
- package/dist/src/ai/rules.js +26 -14
- package/dist/src/ai/tester.js +90 -26
- package/dist/src/ai/tools.js +18 -10
- package/dist/src/api/request-store.js +20 -0
- package/dist/src/api/xhr-capture.js +19 -3
- package/dist/src/browser-server.js +16 -3
- package/dist/src/command-handler.js +1 -1
- package/dist/src/commands/add-rule-command.js +12 -9
- package/dist/src/commands/base-command.js +20 -0
- package/dist/src/commands/clean-command.js +3 -2
- package/dist/src/commands/compact-command.js +138 -0
- package/dist/src/commands/context-command.js +7 -1
- package/dist/src/commands/drill-command.js +4 -1
- package/dist/src/commands/experience-command.js +104 -0
- package/dist/src/commands/explore-command.js +54 -19
- package/dist/src/commands/freesail-command.js +2 -0
- package/dist/src/commands/index.js +7 -3
- package/dist/src/commands/init-command.js +11 -10
- package/dist/src/commands/learn-command.js +1 -1
- package/dist/src/commands/navigate-command.js +4 -1
- package/dist/src/commands/plan-clear-command.js +4 -1
- package/dist/src/commands/plan-command.js +43 -4
- package/dist/src/commands/plan-edit-command.js +1 -1
- package/dist/src/commands/plan-load-command.js +4 -1
- package/dist/src/commands/plan-reload-command.js +4 -1
- package/dist/src/commands/plan-save-command.js +20 -8
- package/dist/src/commands/rerun-command.js +4 -0
- package/dist/src/commands/research-command.js +5 -2
- package/dist/src/commands/start-command.js +5 -1
- package/dist/src/commands/test-command.js +7 -1
- package/dist/src/components/App.js +15 -5
- package/dist/src/execution-controller.js +13 -2
- package/dist/src/experience-tracker.js +174 -83
- package/dist/src/explorbot.js +31 -22
- package/dist/src/explorer.js +12 -5
- package/dist/src/observability.js +50 -99
- package/dist/src/playwright-recorder.js +309 -0
- package/dist/src/reporter.js +17 -2
- package/dist/src/stats.js +2 -0
- package/dist/src/suite.js +1 -1
- package/dist/src/test-plan.js +12 -0
- package/dist/src/utils/aria.js +37 -1
- package/dist/src/utils/error-page.js +30 -7
- package/dist/src/utils/logger.js +1 -1
- package/dist/src/utils/next-steps.js +37 -0
- package/dist/src/utils/rules-loader.js +1 -1
- package/dist/src/utils/test-files.js +1 -1
- package/dist/src/utils/url-matcher.js +50 -0
- package/package.json +2 -2
- package/rules/navigator/output.md +9 -0
- package/rules/navigator/verification-actions.md +2 -0
- package/src/action-result.ts +26 -1
- package/src/action.ts +44 -37
- package/src/ai/bosun.ts +16 -2
- package/src/ai/conversation.ts +37 -0
- package/src/ai/experience-compactor.ts +270 -63
- package/src/ai/historian/codeceptjs.ts +130 -0
- package/src/ai/historian/experience.ts +383 -0
- package/src/ai/historian/mixin.ts +4 -0
- package/src/ai/historian/playwright.ts +169 -0
- package/src/ai/historian/utils.ts +23 -0
- package/src/ai/historian.ts +35 -468
- package/src/ai/navigator.ts +140 -85
- package/src/ai/pilot.ts +259 -14
- package/src/ai/planner/subpages.ts +1 -24
- package/src/ai/planner.ts +34 -14
- package/src/ai/provider.ts +52 -18
- package/src/ai/rerunner.ts +3 -3
- package/src/ai/researcher/deep-analysis.ts +1 -1
- package/src/ai/researcher/fingerprint-worker.ts +1 -1
- package/src/ai/researcher/locators.ts +2 -2
- package/src/ai/researcher/sections.ts +7 -1
- package/src/ai/researcher.ts +47 -42
- package/src/ai/rules.ts +27 -14
- package/src/ai/task-agent.ts +1 -1
- package/src/ai/tester.ts +94 -26
- package/src/ai/tools.ts +53 -29
- package/src/api/request-store.ts +22 -0
- package/src/api/xhr-capture.ts +21 -3
- package/src/browser-server.ts +17 -3
- package/src/command-handler.ts +1 -1
- package/src/commands/add-rule-command.ts +13 -9
- package/src/commands/base-command.ts +26 -1
- package/src/commands/clean-command.ts +4 -3
- package/src/commands/compact-command.ts +156 -0
- package/src/commands/context-command.ts +8 -2
- package/src/commands/drill-command.ts +5 -2
- package/src/commands/experience-command.ts +125 -0
- package/src/commands/explore-command.ts +58 -21
- package/src/commands/freesail-command.ts +2 -0
- package/src/commands/index.ts +7 -3
- package/src/commands/init-command.ts +11 -10
- package/src/commands/learn-command.ts +2 -2
- package/src/commands/navigate-command.ts +5 -2
- package/src/commands/plan-clear-command.ts +5 -2
- package/src/commands/plan-command.ts +47 -5
- package/src/commands/plan-edit-command.ts +2 -2
- package/src/commands/plan-load-command.ts +5 -2
- package/src/commands/plan-reload-command.ts +5 -2
- package/src/commands/plan-save-command.ts +20 -9
- package/src/commands/rerun-command.ts +5 -0
- package/src/commands/research-command.ts +6 -3
- package/src/commands/start-command.ts +6 -2
- package/src/commands/test-command.ts +8 -2
- package/src/components/App.tsx +16 -5
- package/src/config.ts +6 -1
- package/src/execution-controller.ts +14 -3
- package/src/experience-tracker.ts +198 -100
- package/src/explorbot.ts +33 -23
- package/src/explorer.ts +14 -5
- package/src/observability.ts +50 -109
- package/src/playwright-recorder.ts +305 -0
- package/src/reporter.ts +17 -3
- package/src/stats.ts +4 -0
- package/src/suite.ts +1 -1
- package/src/test-plan.ts +12 -0
- package/src/utils/aria.ts +38 -1
- package/src/utils/error-page.ts +32 -7
- package/src/utils/logger.ts +1 -1
- package/src/utils/next-steps.ts +51 -0
- package/src/utils/rules-loader.ts +1 -1
- package/src/utils/test-files.ts +1 -1
- package/src/utils/url-matcher.ts +43 -0
package/dist/src/explorbot.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { ActionResult } from "./action-result.js";
|
|
4
|
-
import { ApiClient } from "./api/api-client.js";
|
|
5
|
-
import { RequestStore } from "./api/request-store.js";
|
|
6
|
-
import { loadSpec } from "./api/spec-reader.js";
|
|
7
4
|
import { Bosun } from "./ai/bosun.js";
|
|
8
5
|
import { Captain } from "./ai/captain.js";
|
|
9
6
|
import { ExperienceCompactor } from "./ai/experience-compactor.js";
|
|
@@ -14,11 +11,15 @@ import { Pilot } from "./ai/pilot.js";
|
|
|
14
11
|
import { Planner } from "./ai/planner.js";
|
|
15
12
|
import { AIProvider } from "./ai/provider.js";
|
|
16
13
|
import { Quartermaster } from "./ai/quartermaster.js";
|
|
17
|
-
import { Researcher } from "./ai/researcher.js";
|
|
18
14
|
import { Rerunner } from "./ai/rerunner.js";
|
|
15
|
+
import { Researcher } from "./ai/researcher.js";
|
|
19
16
|
import { Tester } from "./ai/tester.js";
|
|
20
17
|
import { createAgentTools } from "./ai/tools.js";
|
|
18
|
+
import { ApiClient } from "./api/api-client.js";
|
|
19
|
+
import { RequestStore } from "./api/request-store.js";
|
|
20
|
+
import { loadSpec } from "./api/spec-reader.js";
|
|
21
21
|
import { ConfigParser } from "./config.js";
|
|
22
|
+
import { ExperienceTracker } from "./experience-tracker.js";
|
|
22
23
|
import Explorer from "./explorer.js";
|
|
23
24
|
import { KnowledgeTracker } from "./knowledge-tracker.js";
|
|
24
25
|
import { Plan } from "./test-plan.js";
|
|
@@ -34,6 +35,8 @@ export class ExplorBot {
|
|
|
34
35
|
needsInput = false;
|
|
35
36
|
currentPlan;
|
|
36
37
|
planFeature;
|
|
38
|
+
lastPlanError = null;
|
|
39
|
+
lastSavedPlanPath = null;
|
|
37
40
|
agents = {};
|
|
38
41
|
constructor(options = {}) {
|
|
39
42
|
this.options = options;
|
|
@@ -54,13 +57,11 @@ export class ExplorBot {
|
|
|
54
57
|
return;
|
|
55
58
|
}
|
|
56
59
|
try {
|
|
57
|
-
|
|
58
|
-
this.provider = new AIProvider(this.config.ai);
|
|
59
|
-
await this.provider.validateConnection();
|
|
60
|
+
await this.startProviderOnly();
|
|
60
61
|
this.explorer = new Explorer(this.config, this.provider, this.options);
|
|
61
62
|
await this.explorer.start();
|
|
62
63
|
if (!this.options.incognito) {
|
|
63
|
-
await this.agentExperienceCompactor().
|
|
64
|
+
await this.agentExperienceCompactor().autocompact();
|
|
64
65
|
}
|
|
65
66
|
if (this.userResolveFn)
|
|
66
67
|
this.explorer.setUserResolve(this.userResolveFn);
|
|
@@ -71,9 +72,16 @@ export class ExplorBot {
|
|
|
71
72
|
process.exit(1);
|
|
72
73
|
}
|
|
73
74
|
}
|
|
75
|
+
async startProviderOnly() {
|
|
76
|
+
if (this.provider)
|
|
77
|
+
return;
|
|
78
|
+
this.config = await this.configParser.loadConfig(this.options);
|
|
79
|
+
this.provider = new AIProvider(this.config.ai);
|
|
80
|
+
await this.provider.validateConnection();
|
|
81
|
+
}
|
|
74
82
|
async stop() {
|
|
75
83
|
this.agents.quartermaster?.stop();
|
|
76
|
-
await this.explorer
|
|
84
|
+
await this.explorer?.stop();
|
|
77
85
|
}
|
|
78
86
|
async visitInitialState() {
|
|
79
87
|
const url = this.options.from || '/';
|
|
@@ -97,6 +105,12 @@ export class ExplorBot {
|
|
|
97
105
|
}
|
|
98
106
|
return new KnowledgeTracker();
|
|
99
107
|
}
|
|
108
|
+
getExperienceTracker() {
|
|
109
|
+
if (this.explorer) {
|
|
110
|
+
return this.explorer.getStateManager().getExperienceTracker();
|
|
111
|
+
}
|
|
112
|
+
return new ExperienceTracker();
|
|
113
|
+
}
|
|
100
114
|
getConfig() {
|
|
101
115
|
return this.config;
|
|
102
116
|
}
|
|
@@ -186,10 +200,7 @@ export class ExplorBot {
|
|
|
186
200
|
return this.agents.captain;
|
|
187
201
|
}
|
|
188
202
|
agentExperienceCompactor() {
|
|
189
|
-
return (this.agents.experienceCompactor ||= this.
|
|
190
|
-
const experienceTracker = explorer.getStateManager().getExperienceTracker();
|
|
191
|
-
return new ExperienceCompactor(ai, experienceTracker);
|
|
192
|
-
}));
|
|
203
|
+
return (this.agents.experienceCompactor ||= new ExperienceCompactor(this.provider, this.getExperienceTracker()));
|
|
193
204
|
}
|
|
194
205
|
agentQuartermaster() {
|
|
195
206
|
const config = this.config.ai?.agents?.quartermaster;
|
|
@@ -204,10 +215,10 @@ export class ExplorBot {
|
|
|
204
215
|
return this.agents.quartermaster;
|
|
205
216
|
}
|
|
206
217
|
agentHistorian() {
|
|
207
|
-
return (this.agents.historian ||= this.createAgent(({ ai, explorer }) => {
|
|
218
|
+
return (this.agents.historian ||= this.createAgent(({ ai, explorer, config }) => {
|
|
208
219
|
const experienceTracker = explorer.getStateManager().getExperienceTracker();
|
|
209
220
|
const reporter = explorer.getReporter();
|
|
210
|
-
return new Historian(ai, experienceTracker, reporter, explorer.getStateManager());
|
|
221
|
+
return new Historian(ai, experienceTracker, reporter, explorer.getStateManager(), config, explorer.getPlaywrightRecorder());
|
|
211
222
|
}));
|
|
212
223
|
}
|
|
213
224
|
agentRerunner() {
|
|
@@ -293,21 +304,18 @@ export class ExplorBot {
|
|
|
293
304
|
if (this.currentPlan) {
|
|
294
305
|
planner.setPlan(this.currentPlan);
|
|
295
306
|
}
|
|
307
|
+
this.lastPlanError = null;
|
|
296
308
|
try {
|
|
297
309
|
this.currentPlan = await planner.plan(feature, opts.style, opts.extend, opts.completedPlans);
|
|
298
310
|
}
|
|
299
311
|
catch (err) {
|
|
300
|
-
|
|
312
|
+
this.lastPlanError = err instanceof Error ? err : new Error(String(err));
|
|
313
|
+
tag('warning').log(`Planning failed: ${this.lastPlanError.message}`);
|
|
301
314
|
if (!this.currentPlan)
|
|
302
315
|
return undefined;
|
|
303
316
|
return this.currentPlan;
|
|
304
317
|
}
|
|
305
|
-
|
|
306
|
-
if (savedPath) {
|
|
307
|
-
const relativePath = path.relative(process.cwd(), savedPath);
|
|
308
|
-
tag('info').log(`Plan saved to: ${relativePath}`);
|
|
309
|
-
tag('info').log(`Edit the plan file and run /plan:load ${relativePath} to reload it`);
|
|
310
|
-
}
|
|
318
|
+
this.savePlan();
|
|
311
319
|
return this.currentPlan;
|
|
312
320
|
}
|
|
313
321
|
getPlansDir() {
|
|
@@ -329,6 +337,7 @@ export class ExplorBot {
|
|
|
329
337
|
const planFilename = filename || this.generatePlanFilename();
|
|
330
338
|
const planPath = path.join(plansDir, planFilename);
|
|
331
339
|
Plan.saveMultipleToMarkdown(plans, planPath);
|
|
340
|
+
this.lastSavedPlanPath = planPath;
|
|
332
341
|
return planPath;
|
|
333
342
|
}
|
|
334
343
|
generatePlanFilename() {
|
package/dist/src/explorer.js
CHANGED
|
@@ -8,12 +8,13 @@ import { createTest } from 'codeceptjs/lib/mocha/test';
|
|
|
8
8
|
import { ActionResult } from "./action-result.js";
|
|
9
9
|
import Action from './action.js';
|
|
10
10
|
import { visuallyAnnotateContainers } from "./ai/researcher/coordinates.js";
|
|
11
|
+
import { RequestStore } from "./api/request-store.js";
|
|
12
|
+
import { XhrCapture } from "./api/xhr-capture.js";
|
|
11
13
|
import { ConfigParser, outputPath } from './config.js';
|
|
12
14
|
import { KnowledgeTracker } from './knowledge-tracker.js';
|
|
15
|
+
import { PlaywrightRecorder } from "./playwright-recorder.js";
|
|
13
16
|
import { Reporter } from "./reporter.js";
|
|
14
17
|
import { StateManager } from './state-manager.js';
|
|
15
|
-
import { RequestStore } from "./api/request-store.js";
|
|
16
|
-
import { XhrCapture } from "./api/xhr-capture.js";
|
|
17
18
|
import { createDebug, log, tag } from './utils/logger.js';
|
|
18
19
|
import { WebElement, extractElementData } from "./utils/web-element.js";
|
|
19
20
|
const debugLog = createDebug('explorbot:explorer');
|
|
@@ -35,6 +36,7 @@ class Explorer {
|
|
|
35
36
|
_activeTest = null;
|
|
36
37
|
xhrCapture = null;
|
|
37
38
|
requestStore = null;
|
|
39
|
+
playwrightRecorder = new PlaywrightRecorder();
|
|
38
40
|
constructor(config, aiProvider, options) {
|
|
39
41
|
this.config = config;
|
|
40
42
|
this.aiProvider = aiProvider;
|
|
@@ -42,7 +44,7 @@ class Explorer {
|
|
|
42
44
|
this.initializeContainer();
|
|
43
45
|
this.stateManager = new StateManager({ incognito: this.options?.incognito });
|
|
44
46
|
this.knowledgeTracker = new KnowledgeTracker();
|
|
45
|
-
this.reporter = new Reporter(config.reporter);
|
|
47
|
+
this.reporter = new Reporter(config.reporter, this.stateManager);
|
|
46
48
|
}
|
|
47
49
|
initializeContainer() {
|
|
48
50
|
try {
|
|
@@ -89,7 +91,7 @@ class Explorer {
|
|
|
89
91
|
tag('substep').log(debugInfo);
|
|
90
92
|
}
|
|
91
93
|
const PlaywrightConfig = {
|
|
92
|
-
timeout:
|
|
94
|
+
timeout: 3000,
|
|
93
95
|
highlightElement: true,
|
|
94
96
|
waitForAction: 500,
|
|
95
97
|
...playwrightConfig,
|
|
@@ -188,6 +190,7 @@ class Explorer {
|
|
|
188
190
|
const hasSession = this.options?.session && existsSync(this.options.session);
|
|
189
191
|
const contextOptions = hasSession ? { storageState: this.options.session } : undefined;
|
|
190
192
|
await this.playwrightHelper._createContextPage(contextOptions);
|
|
193
|
+
await this.playwrightRecorder.start(this.playwrightHelper.browserContext);
|
|
191
194
|
this.setupXhrCapture();
|
|
192
195
|
if (hasSession) {
|
|
193
196
|
tag('info').log(`Session restored from ${path.relative(process.cwd(), this.options.session)}`);
|
|
@@ -216,7 +219,10 @@ class Explorer {
|
|
|
216
219
|
await this.playwrightHelper._startBrowser();
|
|
217
220
|
}
|
|
218
221
|
createAction() {
|
|
219
|
-
return new Action(this.actor, this.stateManager);
|
|
222
|
+
return new Action(this.actor, this.stateManager, this.playwrightRecorder);
|
|
223
|
+
}
|
|
224
|
+
getPlaywrightRecorder() {
|
|
225
|
+
return this.playwrightRecorder;
|
|
220
226
|
}
|
|
221
227
|
async visit(url) {
|
|
222
228
|
await this.closeOtherTabs();
|
|
@@ -411,6 +417,7 @@ class Explorer {
|
|
|
411
417
|
if (this.xhrCapture && this.playwrightHelper?.page) {
|
|
412
418
|
this.xhrCapture.detach(this.playwrightHelper.page);
|
|
413
419
|
}
|
|
420
|
+
await this.playwrightRecorder.stop();
|
|
414
421
|
if (this.options?.session && this.playwrightHelper?.browserContext) {
|
|
415
422
|
const dir = path.dirname(this.options.session);
|
|
416
423
|
if (!existsSync(dir))
|
|
@@ -1,125 +1,76 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { context, trace } from '@opentelemetry/api';
|
|
1
|
+
import { trace } from '@opentelemetry/api';
|
|
3
2
|
let current = null;
|
|
4
|
-
let depth = 0;
|
|
5
3
|
export const Observability = {
|
|
6
4
|
async run(name, metadata, fn) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const savedName = current.name;
|
|
17
|
-
current.span = childSpan;
|
|
18
|
-
current.name = name;
|
|
19
|
-
return await context.with(trace.setSpan(context.active(), childSpan), async () => {
|
|
20
|
-
try {
|
|
21
|
-
return await fn();
|
|
22
|
-
}
|
|
23
|
-
finally {
|
|
24
|
-
childSpan.end();
|
|
25
|
-
current.span = savedSpan;
|
|
26
|
-
current.name = savedName;
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
const tracer = trace.getTracer('ai');
|
|
31
|
-
const spanContext = {
|
|
32
|
-
traceId: current?.traceId || randomBytes(16).toString('hex'),
|
|
33
|
-
spanId: randomBytes(8).toString('hex'),
|
|
34
|
-
traceFlags: 1,
|
|
35
|
-
};
|
|
36
|
-
const rootContext = trace.setSpanContext(context.active(), spanContext);
|
|
37
|
-
const initSpan = tracer.startSpan(name, undefined, rootContext);
|
|
38
|
-
initSpan.setAttribute('langfuse.trace.name', name);
|
|
39
|
-
initSpan.setAttribute('langfuse.trace.id', current?.traceId || '');
|
|
40
|
-
if (current?.metadata?.sessionId) {
|
|
41
|
-
initSpan.setAttribute('langfuse.trace.session_id', String(current.metadata.sessionId));
|
|
42
|
-
}
|
|
43
|
-
if (current?.metadata?.userId) {
|
|
44
|
-
initSpan.setAttribute('langfuse.trace.user_id', String(current.metadata.userId));
|
|
45
|
-
}
|
|
46
|
-
if (current?.metadata?.tags && Array.isArray(current.metadata.tags)) {
|
|
47
|
-
initSpan.setAttribute('langfuse.trace.tags', current.metadata.tags);
|
|
48
|
-
}
|
|
49
|
-
if (current?.metadata?.input) {
|
|
50
|
-
initSpan.setAttribute('langfuse.trace.input', JSON.stringify(current.metadata.input));
|
|
51
|
-
}
|
|
52
|
-
initSpan.end();
|
|
53
|
-
const span = tracer.startSpan(name, undefined, rootContext);
|
|
54
|
-
current.span = span;
|
|
55
|
-
return await context.with(trace.setSpan(rootContext, span), async () => {
|
|
5
|
+
const tracer = trace.getTracer('ai');
|
|
6
|
+
if (current) {
|
|
7
|
+
return await tracer.startActiveSpan(name, {}, async (span) => {
|
|
8
|
+
const saved = current;
|
|
9
|
+
current = {
|
|
10
|
+
metadata: { ...saved.metadata, ...metadata },
|
|
11
|
+
name,
|
|
12
|
+
span,
|
|
13
|
+
};
|
|
56
14
|
try {
|
|
57
15
|
return await fn();
|
|
58
16
|
}
|
|
59
17
|
finally {
|
|
60
18
|
span.end();
|
|
61
|
-
current
|
|
19
|
+
current = saved;
|
|
62
20
|
}
|
|
63
21
|
});
|
|
64
22
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
metadata: {
|
|
77
|
-
...metadata,
|
|
78
|
-
langfuseTraceId,
|
|
79
|
-
},
|
|
80
|
-
traceId: langfuseTraceId,
|
|
81
|
-
updateParent: true,
|
|
82
|
-
name,
|
|
83
|
-
};
|
|
84
|
-
depth = 1;
|
|
85
|
-
return true;
|
|
86
|
-
},
|
|
87
|
-
endTrace(started) {
|
|
88
|
-
if (!current) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (!started) {
|
|
92
|
-
depth -= 1;
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
depth -= 1;
|
|
96
|
-
if (depth <= 0) {
|
|
97
|
-
current = null;
|
|
98
|
-
depth = 0;
|
|
99
|
-
}
|
|
23
|
+
const attributes = buildRootSpanAttributes(name, metadata);
|
|
24
|
+
return await tracer.startActiveSpan(name, { attributes }, async (span) => {
|
|
25
|
+
current = { metadata, name, span };
|
|
26
|
+
try {
|
|
27
|
+
return await fn();
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
span.end();
|
|
31
|
+
current = null;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
100
34
|
},
|
|
101
35
|
getTelemetry() {
|
|
102
36
|
if (!current) {
|
|
103
37
|
return undefined;
|
|
104
38
|
}
|
|
105
|
-
const
|
|
39
|
+
const metadata = {};
|
|
40
|
+
if (current.metadata.sessionId)
|
|
41
|
+
metadata.sessionId = current.metadata.sessionId;
|
|
42
|
+
if (current.metadata.userId)
|
|
43
|
+
metadata.userId = current.metadata.userId;
|
|
44
|
+
if (Array.isArray(current.metadata.tags))
|
|
45
|
+
metadata.tags = current.metadata.tags;
|
|
46
|
+
return {
|
|
106
47
|
isEnabled: true,
|
|
107
48
|
functionId: current.name,
|
|
108
|
-
metadata
|
|
109
|
-
...current.metadata,
|
|
110
|
-
langfuseTraceId: current.traceId,
|
|
111
|
-
langfuseUpdateParent: current.updateParent,
|
|
112
|
-
},
|
|
49
|
+
metadata,
|
|
113
50
|
};
|
|
114
|
-
if (current.updateParent) {
|
|
115
|
-
current.updateParent = false;
|
|
116
|
-
}
|
|
117
|
-
return telemetry;
|
|
118
51
|
},
|
|
119
52
|
isTracing() {
|
|
120
53
|
return Boolean(current);
|
|
121
54
|
},
|
|
122
55
|
getSpan() {
|
|
123
|
-
return current?.span;
|
|
56
|
+
return current?.span ?? trace.getActiveSpan();
|
|
124
57
|
},
|
|
125
58
|
};
|
|
59
|
+
function buildRootSpanAttributes(name, metadata) {
|
|
60
|
+
const attributes = {
|
|
61
|
+
'langfuse.trace.name': name,
|
|
62
|
+
};
|
|
63
|
+
if (metadata.sessionId) {
|
|
64
|
+
attributes['session.id'] = String(metadata.sessionId);
|
|
65
|
+
}
|
|
66
|
+
if (metadata.userId) {
|
|
67
|
+
attributes['user.id'] = String(metadata.userId);
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(metadata.tags)) {
|
|
70
|
+
attributes['langfuse.trace.tags'] = metadata.tags;
|
|
71
|
+
}
|
|
72
|
+
if (metadata.input !== undefined) {
|
|
73
|
+
attributes['langfuse.trace.input'] = JSON.stringify(metadata.input);
|
|
74
|
+
}
|
|
75
|
+
return attributes;
|
|
76
|
+
}
|