From c6d61c10c0ca1818b811adbd1f4b7a2cc6fb12e1 Mon Sep 17 00:00:00 2001 From: Saqib Ameen Date: Fri, 11 Apr 2025 19:44:45 -0600 Subject: [PATCH 01/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20add=20tracing=20sup?= =?UTF-8?q?port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/nodejs/workflows/workflows.ts | 103 +++++-- packages/langbase/src/langbase/langbase.ts | 5 + packages/langbase/src/langbase/trace.ts | 79 +++++ packages/langbase/src/langbase/workflows.ts | 309 +++++++++++++++++--- 4 files changed, 423 insertions(+), 73 deletions(-) create mode 100644 packages/langbase/src/langbase/trace.ts diff --git a/examples/nodejs/workflows/workflows.ts b/examples/nodejs/workflows/workflows.ts index c99897f..b257817 100644 --- a/examples/nodejs/workflows/workflows.ts +++ b/examples/nodejs/workflows/workflows.ts @@ -1,40 +1,85 @@ -// Experimental upcoming beta AI primitve. -// Please refer to the documentation for more information: https://langbase.com/docs for more information. - +// Test script for the simplified proxy approach import 'dotenv/config'; -import {Langbase, Workflow} from 'langbase'; +import {Langbase} from 'langbase'; +// Create Langbase instance const langbase = new Langbase({ apiKey: process.env.LANGBASE_API_KEY!, }); async function main() { - const {step} = new Workflow({debug: true}); - - const result = await step({ - id: 'sumamrize', - run: async () => { - return langbase.llm.run({ - model: 'openai:gpt-4o-mini', - apiKey: process.env.OPENAI_API_KEY!, - messages: [ - { - role: 'system', - content: - 'You are an expert summarizer. Summarize the user input.', - }, - { - role: 'user', - content: - 'I am testing workflows. I just created an example of summarize workflow. Can you summarize this?', - }, - ], - stream: false, - }); - }, + // Create a workflow with debug mode enabled + const workflow = langbase.workflow({ + name: 'simplified-proxy-test', + debug: true, // Enable debug logging }); - console.log(result['completion']); + try { + // STEP 1: Call langbase.agent.run but don't return its result directly + const step1Result = await workflow.step({ + id: 'call-but-return-custom', + run: async () => { + // Return custom result instead + return { + customField: 'Custom result from simplified proxy', + timestamp: new Date().toISOString(), + }; + }, + }); + + // STEP 2: Return agent.run result directly + const step2Result = await workflow.step({ + id: 'return-agent-run-directly', + run: async () => { + // Call Langbase API and return the result directly + return langbase.agent.run({ + model: 'openai:gpt-4o-mini', + apiKey: process.env.OPENAI_API_KEY!, + instructions: 'Be brief and concise.', + input: 'What is 2+2?', + stream: false, + }); + }, + }); + + // STEP 3: Make multiple Langbase calls in one step + const step3Result = await workflow.step({ + id: 'multiple-calls', + run: async () => { + // First call + const call1 = await langbase.agent.run({ + model: 'openai:gpt-4o-mini', + apiKey: process.env.OPENAI_API_KEY!, + instructions: 'Be brief.', + input: 'First proxy test', + stream: false, + }); + + // Second call with different method + const call2 = await langbase.agent.run({ + model: 'openai:gpt-4o-mini', + apiKey: process.env.OPENAI_API_KEY!, + instructions: 'Be brief.', + input: 'Second proxy test', + stream: false, + }); + + // Return combined result + return { + summary: 'Multiple calls completed with simplified proxy', + calls: 2, + firstOutput: call1.output, + secondOutput: call2.output, + }; + }, + }); + } catch (error) { + console.error('āŒ Workflow error:', error); + } finally { + // End the workflow to show trace report + workflow.end(); + } } -main(); +// Run the test +main().catch(console.error); diff --git a/packages/langbase/src/langbase/langbase.ts b/packages/langbase/src/langbase/langbase.ts index b2c01e5..940592f 100644 --- a/packages/langbase/src/langbase/langbase.ts +++ b/packages/langbase/src/langbase/langbase.ts @@ -1,5 +1,6 @@ import {convertDocToFormData} from '@/lib/utils/doc-to-formdata'; import {Request} from '../common/request'; +import {Workflow} from './workflows'; export type Role = 'user' | 'assistant' | 'system' | 'tool'; @@ -639,6 +640,8 @@ export class Langbase { }; }; + public workflow: (config: {debug?: boolean; name: string}) => Workflow; + constructor(options?: LangbaseOptions) { this.baseUrl = options?.baseUrl ?? 'https://api.langbase.com'; this.apiKey = options?.apiKey ?? ''; @@ -723,6 +726,8 @@ export class Langbase { this.agent = { run: this.runAgent.bind(this), }; + + this.workflow = (config) => new Workflow({...config, langbase: this}); } private async runPipe( diff --git a/packages/langbase/src/langbase/trace.ts b/packages/langbase/src/langbase/trace.ts new file mode 100644 index 0000000..39c1aa6 --- /dev/null +++ b/packages/langbase/src/langbase/trace.ts @@ -0,0 +1,79 @@ +export interface Trace { + name: string; + startTime: number; + endTime?: number; + steps: StepTrace[]; +} + +export interface StepTrace { + name: string; + output: any; + traces: string[] | null; + duration: number; + startTime: number; + endTime: number; +} + +export class TraceManager { + private traces: Map = new Map(); + + createTrace(name: string): string { + const traceId = `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + this.traces.set(traceId, { + name, + startTime: Date.now(), + steps: [], + }); + return traceId; + } + + addStep(traceId: string, step: StepTrace) { + const trace = this.traces.get(traceId); + if (trace) { + trace.steps.push(step); + } + } + + endTrace(traceId: string) { + const trace = this.traces.get(traceId); + if (trace) { + trace.endTime = Date.now(); + } + } + + getTrace(traceId: string): Trace | undefined { + return this.traces.get(traceId); + } + + printTrace(traceId: string) { + const trace = this.traces.get(traceId); + if (trace) { + const duration = trace.endTime + ? trace.endTime - trace.startTime + : Date.now() - trace.startTime; + console.log('\nšŸ“Š Workflow Trace:'); + console.log(`Name: ${trace.name}`); + console.log(`Duration: ${duration}ms`); + console.log( + `Start Time: ${new Date(trace.startTime).toISOString()}`, + ); + if (trace.endTime) { + console.log( + `End Time: ${new Date(trace.endTime).toISOString()}`, + ); + } + console.log('\nSteps:'); + trace.steps.forEach(step => { + console.log(`\n Step: ${step.name}`); + console.log(` Duration: ${step.duration}ms`); + + // Make sure to check for both existence and non-empty array + if (step.traces && step.traces.length > 0) { + console.log(` Traces:`, step.traces); + } + + console.log(` Output:`, step.output); + }); + } + } +} diff --git a/packages/langbase/src/langbase/workflows.ts b/packages/langbase/src/langbase/workflows.ts index 63847c0..f05c20e 100644 --- a/packages/langbase/src/langbase/workflows.ts +++ b/packages/langbase/src/langbase/workflows.ts @@ -1,3 +1,13 @@ +import {TraceManager, StepTrace} from './trace'; +import {Langbase} from './langbase'; + +// Declare the global langbase instance +declare global { + var langbase: Langbase; + var _activeTraceCollector: ((traceId: string) => void) | null; + var _workflowDebugEnabled: boolean; +} + type WorkflowContext = { outputs: Record; }; @@ -15,6 +25,12 @@ type StepConfig = { run: () => Promise; }; +type WorkflowConfig = { + debug?: boolean; + name: string; + langbase: Langbase; +}; + class TimeoutError extends Error { constructor(stepId: string, timeout: number) { super(`Step "${stepId}" timed out after ${timeout}ms`); @@ -22,18 +38,164 @@ class TimeoutError extends Error { } } +// Setup the global trace collector for cross-instance communication +if (typeof global._activeTraceCollector === 'undefined') { + global._activeTraceCollector = null; +} + +// For debug flag +if (typeof global._workflowDebugEnabled === 'undefined') { + global._workflowDebugEnabled = false; +} + export class Workflow { private context: WorkflowContext; private debug: boolean; + private name: string; + private traceManager: TraceManager; + private traceId: string; + private langbase: Langbase; + private originalMethods: Map = new Map(); public readonly step: (config: StepConfig) => Promise; - constructor(config: {debug?: boolean} = {debug: false}) { + constructor(config: WorkflowConfig) { this.context = {outputs: {}}; this.debug = config.debug ?? false; + this.name = config.name; + this.langbase = config.langbase; + this.traceManager = new TraceManager(); + this.traceId = this.traceManager.createTrace(this.name); this.step = this._step.bind(this); + + // Set global debug flag + global._workflowDebugEnabled = this.debug; + } + + /** + * Replace a method in the Langbase instance with a traced version + */ + private interceptMethod(obj: any, method: string, path: string = ''): void { + if (!obj || typeof obj[method] !== 'function') return; + + const fullPath = path ? `${path}.${method}` : method; + const originalMethod = obj[method]; + + // Only replace if not already intercepted + if (!this.originalMethods.has(fullPath)) { + this.originalMethods.set(fullPath, originalMethod); + + const debug = this.debug; + + // Replace with intercepted version + obj[method] = async function (...args: any[]) { + // Call original method with the correct 'this' context + const result = await originalMethod.apply(this, args); + + // Process result for tracing if we have an active collector + if ( + global._activeTraceCollector && + result && + typeof result === 'object' + ) { + // Extract or create traceId + let traceId: string; + + if ('traceId' in result && result.traceId) { + traceId = result.traceId; + global._activeTraceCollector(traceId); + } + } + + return result; + }; + } + } + + /** + * Restore all original methods that were intercepted + */ + private restoreOriginalMethods(): void { + this.originalMethods.forEach((originalMethod, path) => { + // Parse the path to find the object and method + const parts = path.split('.'); + const methodName = parts.pop()!; + let obj: any = this.langbase; + + // Navigate to the correct object + for (const part of parts) { + if (obj && typeof obj === 'object' && part in obj) { + obj = obj[part as keyof typeof obj]; // Type safe access + } else { + return; // Skip if path no longer exists + } + } + + // Restore original method + if ( + obj && + methodName in obj && + typeof obj[methodName] === 'function' + ) { + obj[methodName] = originalMethod; + } + }); + + // Clear the map + this.originalMethods.clear(); + } + + /** + * Intercept all important methods in the Langbase instance + */ + private setupMethodInterceptors(): void { + // Agent methods + this.interceptMethod(this.langbase.agent, 'run', 'agent'); + + // Pipes methods + this.interceptMethod(this.langbase.pipes, 'run', 'pipes'); + this.interceptMethod(this.langbase.pipe, 'run', 'pipe'); + + // Memory methods + if (this.langbase.memories) { + this.interceptMethod( + this.langbase.memories, + 'retrieve', + 'memories', + ); + } + if (this.langbase.memory) { + this.interceptMethod(this.langbase.memory, 'retrieve', 'memory'); + } + + // Tool methods + if (this.langbase.tools) { + this.interceptMethod(this.langbase.tools, 'webSearch', 'tools'); + this.interceptMethod(this.langbase.tools, 'crawl', 'tools'); + } + if (this.langbase.tool) { + this.interceptMethod(this.langbase.tool, 'webSearch', 'tool'); + this.interceptMethod(this.langbase.tool, 'crawl', 'tool'); + } + + // Top-level methods + this.interceptMethod(this.langbase, 'embed'); + this.interceptMethod(this.langbase, 'chunk'); + this.interceptMethod(this.langbase, 'parse'); } private async _step(config: StepConfig): Promise { + const stepStartTime = Date.now(); + // Initialize an array to collect trace IDs + const stepTraces: string[] = []; + + // Function to collect trace IDs + const collectTrace = (traceId: string) => { + if (this.debug) { + console.log(`šŸ“‹ Collected trace ID: ${traceId}`); + } + stepTraces.push(traceId); + }; + if (this.debug) { console.log(`\nšŸ”„ Starting step: ${config.id}`); console.time(`ā±ļø Step ${config.id}`); @@ -48,60 +210,103 @@ export class Workflow { ? config.retries.limit + 1 : 1; - while (attempt <= maxAttempts) { - try { - let stepPromise = config.run(); + // Set up method interceptors before running the step + this.setupMethodInterceptors(); + + // Set the global active trace collector + const previousTraceCollector = global._activeTraceCollector; + global._activeTraceCollector = collectTrace; - if (config.timeout) { - stepPromise = this.withTimeout({ - promise: stepPromise, - timeout: config.timeout, - stepId: config.id, - }); + try { + // Execute the step function directly + let stepPromise: Promise = config.run(); + + // Apply timeout if configured + if (config.timeout) { + stepPromise = this.withTimeout({ + promise: stepPromise, + timeout: config.timeout, + stepId: config.id, + }); + } + + // Wait for the step to complete + const result = await stepPromise; + + // Store step result in context + this.context.outputs[config.id] = result; + + if (this.debug) { + console.timeEnd(`ā±ļø Step ${config.id}`); + console.log(`šŸ“¤ Output:`, result); + + if (stepTraces.length > 0) { + console.log( + `šŸ“‹ Trace IDs (${stepTraces.length}):`, + stepTraces, + ); + } else { + console.log(`šŸ” No trace IDs captured for this step`); } + } + + // Create step trace + const stepEndTime = Date.now(); + const stepTrace: StepTrace = { + name: config.id, + output: result, + traces: stepTraces.length > 0 ? stepTraces : null, + duration: stepEndTime - stepStartTime, + startTime: stepStartTime, + endTime: stepEndTime, + }; - const result = await stepPromise; - this.context.outputs[config.id] = result; + // Add step to trace manager + this.traceManager.addStep(this.traceId, stepTrace); + + // Restore original methods and trace collector + this.restoreOriginalMethods(); + global._activeTraceCollector = previousTraceCollector; + + return result; + } catch (error) { + // Restore original methods and trace collector on error + this.restoreOriginalMethods(); + global._activeTraceCollector = previousTraceCollector; + + // Store error for potential retry or final throw + lastError = error as Error; + + // If retries are configured, try again + if (attempt < maxAttempts) { + const delay = config.retries + ? this.calculateDelay( + config.retries.delay, + attempt, + config.retries.backoff, + ) + : 0; if (this.debug) { - console.timeEnd(`ā±ļø Step ${config.id}`); - console.log(`šŸ“¤ Output:`, result); - console.log(`āœ… Completed step: ${config.id}\n`); + console.log( + `āš ļø Attempt ${attempt} failed, retrying in ${delay}ms...`, + ); + console.error(error); } - return result; - } catch (error) { - lastError = error as Error; - - if (attempt < maxAttempts) { - const delay = config.retries - ? this.calculateDelay( - config.retries.delay, - attempt, - config.retries.backoff, - ) - : 0; - - if (this.debug) { - console.log( - `āš ļø Attempt ${attempt} failed, retrying in ${delay}ms...`, - ); - console.error(error); - } + await this.sleep(delay); + attempt++; - await this.sleep(delay); - attempt++; - } else { - if (this.debug) { - console.timeEnd(`ā±ļø Step ${config.id}`); - console.error(`āŒ Failed step: ${config.id}`, error); - } - throw lastError; + // Try again with the next attempt + return this._step(config); + } else { + if (this.debug) { + console.timeEnd(`ā±ļø Step ${config.id}`); + console.error(`āŒ Failed step: ${config.id}`, error); } + throw lastError; } } - - throw lastError; } private async withTimeout({ @@ -141,4 +346,20 @@ export class Workflow { private async sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } + + public end() { + this.traceManager.endTrace(this.traceId); + this.traceManager.printTrace(this.traceId); + + if (this.debug) { + console.log('\nšŸ” DEBUG: Final trace data:'); + console.log( + JSON.stringify( + this.traceManager.getTrace(this.traceId), + null, + 2, + ), + ); + } + } } From 75df92d8de41cf73c1efa0e11ef136f579503bc3 Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Fri, 25 Apr 2025 20:39:29 +0500 Subject: [PATCH 02/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Extract=20traceid?= =?UTF-8?q?=20from=20response=20headers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/workflows.ts | 53 ++++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/packages/langbase/src/langbase/workflows.ts b/packages/langbase/src/langbase/workflows.ts index f05c20e..1427be0 100644 --- a/packages/langbase/src/langbase/workflows.ts +++ b/packages/langbase/src/langbase/workflows.ts @@ -88,21 +88,50 @@ export class Workflow { // Replace with intercepted version obj[method] = async function (...args: any[]) { - // Call original method with the correct 'this' context - const result = await originalMethod.apply(this, args); + // Add custom arguments for tracing + // Add rawResponse to the options if it's an object + const lastArg = args[args.length - 1]; + const newArgs = [...args]; + + if (lastArg && typeof lastArg === 'object') { + newArgs[newArgs.length - 1] = { + ...lastArg, + rawResponse: true, + }; + } + // Append a new object if the last argument is not an object + else { + newArgs.push({rawResponse: true}); + } + + const result = await originalMethod.apply(this, newArgs); + console.log(`šŸ”„ Intercepted method: ${fullPath}`, result); // Process result for tracing if we have an active collector - if ( - global._activeTraceCollector && - result && - typeof result === 'object' - ) { + if (global._activeTraceCollector) { // Extract or create traceId - let traceId: string; - - if ('traceId' in result && result.traceId) { - traceId = result.traceId; - global._activeTraceCollector(traceId); + let traceId: string | undefined; + + // Check if result is an object with response headers + if (result && typeof result === 'object') { + // Extract from response headers + if ('rawResponse' in result && result.rawResponse) { + // Check for lb-trace-id in headers + if (result.rawResponse.headers['lb-trace-id']) { + // Plain object headers + traceId = + result.rawResponse.headers['lb-trace-id']; + } + } + + // Notify collector if traceId was found + if (traceId && global._activeTraceCollector) { + if (debug) + console.log( + `šŸ” Trace ID extracted: ${traceId}`, + ); + global._activeTraceCollector(traceId); + } } } From d7d3bb997c7f9b96a4977236ecb1dea9100bbaee Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Sat, 26 Apr 2025 02:30:19 +0500 Subject: [PATCH 03/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Error=20and=20durat?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/trace.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/langbase/src/langbase/trace.ts b/packages/langbase/src/langbase/trace.ts index 39c1aa6..4165964 100644 --- a/packages/langbase/src/langbase/trace.ts +++ b/packages/langbase/src/langbase/trace.ts @@ -2,12 +2,15 @@ export interface Trace { name: string; startTime: number; endTime?: number; + duration?: number; steps: StepTrace[]; + error?: string; } export interface StepTrace { name: string; output: any; + error?: string; traces: string[] | null; duration: number; startTime: number; @@ -38,6 +41,7 @@ export class TraceManager { const trace = this.traces.get(traceId); if (trace) { trace.endTime = Date.now(); + trace.duration = trace.endTime - trace.startTime; } } From 5960e906d9ae94d3347052a8cd7f5430dee92ecf Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Sat, 26 Apr 2025 02:30:53 +0500 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Return=20headers=20?= =?UTF-8?q?from=20all=20endpoints=20of=20all=20primitives?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/common/request.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/langbase/src/common/request.ts b/packages/langbase/src/common/request.ts index cf2d817..b6a6a90 100644 --- a/packages/langbase/src/common/request.ts +++ b/packages/langbase/src/common/request.ts @@ -62,6 +62,17 @@ export class Request { const isLllmGenerationEndpoint = GENERATION_ENDPOINTS.includes(endpoint); + // All endpoints should return headers if rawResponse is true + if (!isLllmGenerationEndpoint && options.body?.rawResponse) { + const responseData = await response.json(); + return { + ...responseData, + rawResponse: { + headers: Object.fromEntries(response.headers.entries()), + }, + } as T; + } + if (isLllmGenerationEndpoint) { const threadId = response.headers.get('lb-thread-id'); From 8723fd3eb937e7ef235adc43e555438be0cac33c Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Mon, 23 Jun 2025 16:36:42 +0500 Subject: [PATCH 05/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20traces.create=20fn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/langbase.ts | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/langbase/src/langbase/langbase.ts b/packages/langbase/src/langbase/langbase.ts index 940592f..501c18c 100644 --- a/packages/langbase/src/langbase/langbase.ts +++ b/packages/langbase/src/langbase/langbase.ts @@ -642,6 +642,10 @@ export class Langbase { public workflow: (config: {debug?: boolean; name: string}) => Workflow; + public traces: { + create: (trace: any) => Promise; + }; + constructor(options?: LangbaseOptions) { this.baseUrl = options?.baseUrl ?? 'https://api.langbase.com'; this.apiKey = options?.apiKey ?? ''; @@ -727,7 +731,11 @@ export class Langbase { run: this.runAgent.bind(this), }; - this.workflow = (config) => new Workflow({...config, langbase: this}); + this.workflow = config => new Workflow({...config, langbase: this}); + + this.traces = { + create: this.createTrace.bind(this), + }; } private async runPipe( @@ -1151,4 +1159,17 @@ export class Langbase { }, }); } + + /** + * Creates a new trace on Langbase. + * + * @param {any} trace - The trace data to send. + * @returns {Promise} A promise that resolves to the response of the trace creation. + */ + private async createTrace(trace: any): Promise { + return this.request.post({ + endpoint: '/v1/traces', + body: trace, + }); + } } From 8075ef87e2a39ceae5711cc5cb1e33778f01c73e Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Mon, 23 Jun 2025 16:42:19 +0500 Subject: [PATCH 06/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Use=20traces.creaat?= =?UTF-8?q?e=20fn=20in=20workflow=20end?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/workflows.ts | 29 +++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/langbase/src/langbase/workflows.ts b/packages/langbase/src/langbase/workflows.ts index 1427be0..5ddbb68 100644 --- a/packages/langbase/src/langbase/workflows.ts +++ b/packages/langbase/src/langbase/workflows.ts @@ -55,6 +55,7 @@ export class Workflow { private traceManager: TraceManager; private traceId: string; private langbase: Langbase; + private originalMethods: Map = new Map(); public readonly step: (config: StepConfig) => Promise; @@ -376,19 +377,31 @@ export class Workflow { return new Promise(resolve => setTimeout(resolve, ms)); } - public end() { + public async end(): Promise { + // Finalise and grab the trace this.traceManager.endTrace(this.traceId); this.traceManager.printTrace(this.traceId); + const traceData = this.traceManager.getTrace(this.traceId); + + // --- send to LB API v1/traces/create using SDK method --- + try { + const res = await this.langbase.traces.create(traceData); + + if (!res || res.error) { + console.error( + `āŒ Trace upload failed: ${res?.status || ''} ${res?.statusText || ''}`, + ); + } else if (this.debug) { + console.log(`āœ… Trace ${this.traceId} sent to collector`); + } + } catch (err) { + console.error('āŒ Error while sending trace', err); + } + // ------------------------------------------------------------------------- if (this.debug) { console.log('\nšŸ” DEBUG: Final trace data:'); - console.log( - JSON.stringify( - this.traceManager.getTrace(this.traceId), - null, - 2, - ), - ); + console.log(JSON.stringify(traceData, null, 2)); } } } From 759bff767fa8a2c2277882aec6b16d9e248a9248 Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Mon, 23 Jun 2025 17:25:43 +0500 Subject: [PATCH 07/16] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Trace=20collector?= =?UTF-8?q?=20according=20to=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/trace.ts | 108 ++++++++++++++------ packages/langbase/src/langbase/workflows.ts | 4 +- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/packages/langbase/src/langbase/trace.ts b/packages/langbase/src/langbase/trace.ts index 4165964..74f6cae 100644 --- a/packages/langbase/src/langbase/trace.ts +++ b/packages/langbase/src/langbase/trace.ts @@ -17,67 +17,117 @@ export interface StepTrace { endTime: number; } +export type TraceType = + | 'workflow' + | 'agent' + | 'chunk' + | 'memory' + | 'parse' + | 'embed'; + +export type PrimitiveTrace = + | {chunk: any} + | {agent: any} + | {memory: any} + | {parse: any} + | {embed: any} + | {workflow: WorkflowTrace; entityAuthId: string}; + +type WorkflowTrace = { + createdAt: string; + id: string; + agentWorkflowId: string; + name: string; + startTime: number; + endTime?: number; + duration?: number; + steps: StepTrace[]; + error?: string; +}; + export class TraceManager { - private traces: Map = new Map(); + private traces: Map = new Map(); - createTrace(name: string): string { + createTrace(type: TraceType, traceData: any = {}): string { const traceId = `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - this.traces.set(traceId, { - name, - startTime: Date.now(), - steps: [], - }); + let trace: PrimitiveTrace; + const createdAt = new Date().toISOString(); + if (type === 'workflow') { + trace = { + workflow: { + createdAt, + id: traceId, + agentWorkflowId: traceData.agentWorkflowId || '', + name: traceData.name || '', + startTime: Date.now(), + steps: [], + }, + entityAuthId: '', + }; + } else if (type === 'agent') { + trace = {agent: {...traceData, createdAt, id: traceId}}; + } else if (type === 'chunk') { + trace = {chunk: {...traceData, createdAt, id: traceId}}; + } else if (type === 'memory') { + trace = {memory: {...traceData, createdAt, id: traceId}}; + } else if (type === 'parse') { + trace = {parse: {...traceData, createdAt, id: traceId}}; + } else if (type === 'embed') { + trace = {embed: {...traceData, createdAt, id: traceId}}; + } else { + throw new Error('Unknown trace type'); + } + this.traces.set(traceId, trace); return traceId; } addStep(traceId: string, step: StepTrace) { const trace = this.traces.get(traceId); - if (trace) { - trace.steps.push(step); + if (trace && 'workflow' in trace) { + trace.workflow.steps.push(step); } } endTrace(traceId: string) { const trace = this.traces.get(traceId); - if (trace) { - trace.endTime = Date.now(); - trace.duration = trace.endTime - trace.startTime; + if (trace && 'workflow' in trace) { + trace.workflow.endTime = Date.now(); + trace.workflow.duration = + trace.workflow.endTime - trace.workflow.startTime; } } - getTrace(traceId: string): Trace | undefined { + getTrace(traceId: string): PrimitiveTrace | undefined { return this.traces.get(traceId); } printTrace(traceId: string) { const trace = this.traces.get(traceId); - if (trace) { - const duration = trace.endTime - ? trace.endTime - trace.startTime - : Date.now() - trace.startTime; + if (!trace) return; + if ('workflow' in trace) { + const wf = trace.workflow; + const duration = wf.endTime + ? wf.endTime - wf.startTime + : Date.now() - wf.startTime; console.log('\nšŸ“Š Workflow Trace:'); - console.log(`Name: ${trace.name}`); + console.log(`Name: ${wf.name}`); console.log(`Duration: ${duration}ms`); - console.log( - `Start Time: ${new Date(trace.startTime).toISOString()}`, - ); - if (trace.endTime) { - console.log( - `End Time: ${new Date(trace.endTime).toISOString()}`, - ); + console.log(`Start Time: ${new Date(wf.startTime).toISOString()}`); + if (wf.endTime) { + console.log(`End Time: ${new Date(wf.endTime).toISOString()}`); } console.log('\nSteps:'); - trace.steps.forEach(step => { + wf.steps.forEach(step => { console.log(`\n Step: ${step.name}`); console.log(` Duration: ${step.duration}ms`); - - // Make sure to check for both existence and non-empty array if (step.traces && step.traces.length > 0) { console.log(` Traces:`, step.traces); } - console.log(` Output:`, step.output); }); + } else { + console.log('\nšŸ“Š Primitive Trace:'); + console.dir(trace, {depth: 4}); } } } diff --git a/packages/langbase/src/langbase/workflows.ts b/packages/langbase/src/langbase/workflows.ts index 5ddbb68..d48a070 100644 --- a/packages/langbase/src/langbase/workflows.ts +++ b/packages/langbase/src/langbase/workflows.ts @@ -65,7 +65,9 @@ export class Workflow { this.name = config.name; this.langbase = config.langbase; this.traceManager = new TraceManager(); - this.traceId = this.traceManager.createTrace(this.name); + this.traceId = this.traceManager.createTrace('workflow', { + name: this.name, + }); this.step = this._step.bind(this); // Set global debug flag From b01acd5c611e31196df94b7392b6a51b5bac7b8b Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Mon, 23 Jun 2025 20:49:54 +0500 Subject: [PATCH 08/16] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Trace=20id=20creati?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/trace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/langbase/src/langbase/trace.ts b/packages/langbase/src/langbase/trace.ts index 74f6cae..e53d7b0 100644 --- a/packages/langbase/src/langbase/trace.ts +++ b/packages/langbase/src/langbase/trace.ts @@ -49,7 +49,7 @@ export class TraceManager { private traces: Map = new Map(); createTrace(type: TraceType, traceData: any = {}): string { - const traceId = `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + const traceId = crypto.randomUUID(); let trace: PrimitiveTrace; const createdAt = new Date().toISOString(); if (type === 'workflow') { From 2cde3a41779fbee23d58589e5bba9b0599f2939e Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Mon, 30 Jun 2025 20:20:20 +0500 Subject: [PATCH 09/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Connect=20trace=20w?= =?UTF-8?q?ith=20agent=20id=20from=20deployment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/trace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/langbase/src/langbase/trace.ts b/packages/langbase/src/langbase/trace.ts index e53d7b0..e97f47b 100644 --- a/packages/langbase/src/langbase/trace.ts +++ b/packages/langbase/src/langbase/trace.ts @@ -57,7 +57,7 @@ export class TraceManager { workflow: { createdAt, id: traceId, - agentWorkflowId: traceData.agentWorkflowId || '', + agentWorkflowId: process.env.LANGBASE_AGENT_ID || '', name: traceData.name || '', startTime: Date.now(), steps: [], From 364fd69a2bb7a0aedf2ae2cfcbf6ab9e06b624ac Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Thu, 3 Jul 2025 20:40:20 +0500 Subject: [PATCH 10/16] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Global=20trace=20co?= =?UTF-8?q?llector=20for=20serverless?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/workflows.ts | 46 ++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/langbase/src/langbase/workflows.ts b/packages/langbase/src/langbase/workflows.ts index d48a070..7ce6032 100644 --- a/packages/langbase/src/langbase/workflows.ts +++ b/packages/langbase/src/langbase/workflows.ts @@ -1,12 +1,22 @@ import {TraceManager, StepTrace} from './trace'; import {Langbase} from './langbase'; +// Cross-platform global object +const _global: any = + typeof global !== 'undefined' + ? global + : typeof window !== 'undefined' + ? window + : typeof self !== 'undefined' + ? self + : typeof globalThis !== 'undefined' + ? globalThis + : {}; + // Declare the global langbase instance -declare global { - var langbase: Langbase; - var _activeTraceCollector: ((traceId: string) => void) | null; - var _workflowDebugEnabled: boolean; -} +_global.langbase = _global.langbase || new Langbase(); +_global._activeTraceCollector = _global._activeTraceCollector || null; +_global._workflowDebugEnabled = _global._workflowDebugEnabled || false; type WorkflowContext = { outputs: Record; @@ -38,16 +48,6 @@ class TimeoutError extends Error { } } -// Setup the global trace collector for cross-instance communication -if (typeof global._activeTraceCollector === 'undefined') { - global._activeTraceCollector = null; -} - -// For debug flag -if (typeof global._workflowDebugEnabled === 'undefined') { - global._workflowDebugEnabled = false; -} - export class Workflow { private context: WorkflowContext; private debug: boolean; @@ -71,7 +71,7 @@ export class Workflow { this.step = this._step.bind(this); // Set global debug flag - global._workflowDebugEnabled = this.debug; + _global._workflowDebugEnabled = this.debug; } /** @@ -111,7 +111,7 @@ export class Workflow { console.log(`šŸ”„ Intercepted method: ${fullPath}`, result); // Process result for tracing if we have an active collector - if (global._activeTraceCollector) { + if (_global._activeTraceCollector) { // Extract or create traceId let traceId: string | undefined; @@ -128,12 +128,12 @@ export class Workflow { } // Notify collector if traceId was found - if (traceId && global._activeTraceCollector) { + if (traceId && _global._activeTraceCollector) { if (debug) console.log( `šŸ” Trace ID extracted: ${traceId}`, ); - global._activeTraceCollector(traceId); + _global._activeTraceCollector(traceId); } } } @@ -246,8 +246,8 @@ export class Workflow { this.setupMethodInterceptors(); // Set the global active trace collector - const previousTraceCollector = global._activeTraceCollector; - global._activeTraceCollector = collectTrace; + const previousTraceCollector = _global._activeTraceCollector; + _global._activeTraceCollector = collectTrace; try { // Execute the step function directly @@ -298,13 +298,13 @@ export class Workflow { // Restore original methods and trace collector this.restoreOriginalMethods(); - global._activeTraceCollector = previousTraceCollector; + _global._activeTraceCollector = previousTraceCollector; return result; } catch (error) { // Restore original methods and trace collector on error this.restoreOriginalMethods(); - global._activeTraceCollector = previousTraceCollector; + _global._activeTraceCollector = previousTraceCollector; // Store error for potential retry or final throw lastError = error as Error; From 816e9a2d8086879162e9d72f7a3f87224164845a Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Fri, 11 Jul 2025 16:41:03 +0500 Subject: [PATCH 11/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Make=20workflow=20n?= =?UTF-8?q?ame=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/workflows.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/langbase/src/langbase/workflows.ts b/packages/langbase/src/langbase/workflows.ts index 7ce6032..1e6e9ce 100644 --- a/packages/langbase/src/langbase/workflows.ts +++ b/packages/langbase/src/langbase/workflows.ts @@ -13,8 +13,7 @@ const _global: any = ? globalThis : {}; -// Declare the global langbase instance -_global.langbase = _global.langbase || new Langbase(); +// Global state for tracing _global._activeTraceCollector = _global._activeTraceCollector || null; _global._workflowDebugEnabled = _global._workflowDebugEnabled || false; @@ -37,7 +36,7 @@ type StepConfig = { type WorkflowConfig = { debug?: boolean; - name: string; + name?: string; langbase: Langbase; }; @@ -62,7 +61,7 @@ export class Workflow { constructor(config: WorkflowConfig) { this.context = {outputs: {}}; this.debug = config.debug ?? false; - this.name = config.name; + this.name = config.name ?? 'workflow'; this.langbase = config.langbase; this.traceManager = new TraceManager(); this.traceId = this.traceManager.createTrace('workflow', { From 8a08c1a6b1dc4bcc2f490a4a2ea7a605930db382 Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Fri, 11 Jul 2025 17:10:42 +0500 Subject: [PATCH 12/16] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Backward=20compat?= =?UTF-8?q?=20for=20tracing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/workflows.ts | 81 ++++++++++++--------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/packages/langbase/src/langbase/workflows.ts b/packages/langbase/src/langbase/workflows.ts index 1e6e9ce..9c6cb15 100644 --- a/packages/langbase/src/langbase/workflows.ts +++ b/packages/langbase/src/langbase/workflows.ts @@ -37,7 +37,7 @@ type StepConfig = { type WorkflowConfig = { debug?: boolean; name?: string; - langbase: Langbase; + langbase?: Langbase; // Now optional for backward compatibility }; class TimeoutError extends Error { @@ -51,9 +51,9 @@ export class Workflow { private context: WorkflowContext; private debug: boolean; private name: string; - private traceManager: TraceManager; - private traceId: string; - private langbase: Langbase; + private traceManager?: TraceManager; // Optional + private traceId?: string; // Optional + private langbase?: Langbase; // Optional private originalMethods: Map = new Map(); public readonly step: (config: StepConfig) => Promise; @@ -63,20 +63,24 @@ export class Workflow { this.debug = config.debug ?? false; this.name = config.name ?? 'workflow'; this.langbase = config.langbase; - this.traceManager = new TraceManager(); - this.traceId = this.traceManager.createTrace('workflow', { - name: this.name, - }); - this.step = this._step.bind(this); - // Set global debug flag - _global._workflowDebugEnabled = this.debug; + // Only initialize tracing if langbase is provided + if (this.langbase) { + this.traceManager = new TraceManager(); + this.traceId = this.traceManager.createTrace('workflow', { + name: this.name, + }); + // Set global debug flag + _global._workflowDebugEnabled = this.debug; + } + this.step = this._step.bind(this); } /** * Replace a method in the Langbase instance with a traced version */ private interceptMethod(obj: any, method: string, path: string = ''): void { + if (!this.langbase) return; // Skip if no langbase instance provided (no tracing) if (!obj || typeof obj[method] !== 'function') return; const fullPath = path ? `${path}.${method}` : method; @@ -146,6 +150,7 @@ export class Workflow { * Restore all original methods that were intercepted */ private restoreOriginalMethods(): void { + if (!this.langbase) return; // Skip if no langbase (no tracing) this.originalMethods.forEach((originalMethod, path) => { // Parse the path to find the object and method const parts = path.split('.'); @@ -179,6 +184,7 @@ export class Workflow { * Intercept all important methods in the Langbase instance */ private setupMethodInterceptors(): void { + if (!this.langbase) return; // Skip if no langbase (no tracing) // Agent methods this.interceptMethod(this.langbase.agent, 'run', 'agent'); @@ -241,12 +247,12 @@ export class Workflow { ? config.retries.limit + 1 : 1; - // Set up method interceptors before running the step - this.setupMethodInterceptors(); + // Set up method interceptors before running the step (only if tracing) + if (this.langbase) this.setupMethodInterceptors(); - // Set the global active trace collector + // Set the global active trace collector (only if tracing) const previousTraceCollector = _global._activeTraceCollector; - _global._activeTraceCollector = collectTrace; + if (this.langbase) _global._activeTraceCollector = collectTrace; try { // Execute the step function directly @@ -281,29 +287,32 @@ export class Workflow { } } - // Create step trace - const stepEndTime = Date.now(); - const stepTrace: StepTrace = { - name: config.id, - output: result, - traces: stepTraces.length > 0 ? stepTraces : null, - duration: stepEndTime - stepStartTime, - startTime: stepStartTime, - endTime: stepEndTime, - }; - - // Add step to trace manager - this.traceManager.addStep(this.traceId, stepTrace); - - // Restore original methods and trace collector - this.restoreOriginalMethods(); - _global._activeTraceCollector = previousTraceCollector; + // Create step trace (only if tracing) + if (this.langbase && this.traceManager && this.traceId) { + const stepEndTime = Date.now(); + const stepTrace: StepTrace = { + name: config.id, + output: result, + traces: stepTraces.length > 0 ? stepTraces : null, + duration: stepEndTime - stepStartTime, + startTime: stepStartTime, + endTime: stepEndTime, + }; + this.traceManager.addStep(this.traceId, stepTrace); + } + // Restore original methods and trace collector (only if tracing) + if (this.langbase) { + this.restoreOriginalMethods(); + _global._activeTraceCollector = previousTraceCollector; + } return result; } catch (error) { - // Restore original methods and trace collector on error - this.restoreOriginalMethods(); - _global._activeTraceCollector = previousTraceCollector; + // Restore original methods and trace collector on error (only if tracing) + if (this.langbase) { + this.restoreOriginalMethods(); + _global._activeTraceCollector = previousTraceCollector; + } // Store error for potential retry or final throw lastError = error as Error; @@ -379,6 +388,8 @@ export class Workflow { } public async end(): Promise { + // If tracing is not enabled, do nothing (no-op for backward compatibility) + if (!this.langbase || !this.traceManager || !this.traceId) return; // Finalise and grab the trace this.traceManager.endTrace(this.traceId); this.traceManager.printTrace(this.traceId); From 3e63082a32e530f0bb6e5f6e35f3d9045bae1338 Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Fri, 11 Jul 2025 17:14:00 +0500 Subject: [PATCH 13/16] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Make=20workflow?= =?UTF-8?q?=20name=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/langbase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/langbase/src/langbase/langbase.ts b/packages/langbase/src/langbase/langbase.ts index 501c18c..3176569 100644 --- a/packages/langbase/src/langbase/langbase.ts +++ b/packages/langbase/src/langbase/langbase.ts @@ -640,7 +640,7 @@ export class Langbase { }; }; - public workflow: (config: {debug?: boolean; name: string}) => Workflow; + public workflow: (config: {debug?: boolean; name?: string}) => Workflow; public traces: { create: (trace: any) => Promise; From 7c4546b24907e10fb08f081af55138ec2ca9d47b Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Fri, 11 Jul 2025 17:52:24 +0500 Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/nodejs/workflows/workflows.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/nodejs/workflows/workflows.ts b/examples/nodejs/workflows/workflows.ts index b257817..31d5994 100644 --- a/examples/nodejs/workflows/workflows.ts +++ b/examples/nodejs/workflows/workflows.ts @@ -1,6 +1,5 @@ -// Test script for the simplified proxy approach import 'dotenv/config'; -import {Langbase} from 'langbase'; +import {Langbase, Workflow} from 'langbase'; // Create Langbase instance const langbase = new Langbase({ @@ -10,9 +9,15 @@ const langbase = new Langbase({ async function main() { // Create a workflow with debug mode enabled const workflow = langbase.workflow({ - name: 'simplified-proxy-test', - debug: true, // Enable debug logging + name: 'Test Agent Workflow', + debug: true, }); + // OR + // const workflow = new Workflow({ + // name: 'Test Agent Workflow', + // debug: true, + // langbase, + // }); try { // STEP 1: Call langbase.agent.run but don't return its result directly @@ -76,7 +81,7 @@ async function main() { } catch (error) { console.error('āŒ Workflow error:', error); } finally { - // End the workflow to show trace report + // End the workflow to send trace workflow.end(); } } From 7abe0d0f2a7ef432b89171d851658052894370ae Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Fri, 11 Jul 2025 19:09:11 +0500 Subject: [PATCH 15/16] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Error=20handle?= =?UTF-8?q?=20=20envs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/trace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/langbase/src/langbase/trace.ts b/packages/langbase/src/langbase/trace.ts index e97f47b..443c9c9 100644 --- a/packages/langbase/src/langbase/trace.ts +++ b/packages/langbase/src/langbase/trace.ts @@ -57,7 +57,7 @@ export class TraceManager { workflow: { createdAt, id: traceId, - agentWorkflowId: process.env.LANGBASE_AGENT_ID || '', + agentWorkflowId: process?.env?.LANGBASE_AGENT_ID || '', name: traceData.name || '', startTime: Date.now(), steps: [], From 1e137c0dec2ea13a9cae2390a7e7205fb5c4dc0c Mon Sep 17 00:00:00 2001 From: Ahmad Bilal Date: Fri, 11 Jul 2025 19:27:13 +0500 Subject: [PATCH 16/16] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20process?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/langbase/src/langbase/trace.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/langbase/src/langbase/trace.ts b/packages/langbase/src/langbase/trace.ts index 443c9c9..2ef90b0 100644 --- a/packages/langbase/src/langbase/trace.ts +++ b/packages/langbase/src/langbase/trace.ts @@ -52,12 +52,17 @@ export class TraceManager { const traceId = crypto.randomUUID(); let trace: PrimitiveTrace; const createdAt = new Date().toISOString(); + const agentWorkflowId = + typeof process !== 'undefined' && process.env?.LANGBASE_AGENT_ID + ? process.env.LANGBASE_AGENT_ID + : ''; + if (type === 'workflow') { trace = { workflow: { createdAt, id: traceId, - agentWorkflowId: process?.env?.LANGBASE_AGENT_ID || '', + agentWorkflowId, name: traceData.name || '', startTime: Date.now(), steps: [],