@traccia2/sdk 0.0.1 → 0.0.3

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.
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Automatic LangChain instrumentation - simple one-line setup.
3
+ *
4
+ * Instead of manually passing callbacks to every component, use these
5
+ * convenience functions for automatic instrumentation with zero boilerplate.
6
+ */
7
+
8
+ import { TraciaCallbackHandler } from './langchain-callback';
9
+
10
+ /**
11
+ * Global handler instance to avoid creating multiple handlers
12
+ */
13
+ let globalTraciaHandler: TraciaCallbackHandler | null = null;
14
+
15
+ /**
16
+ * Get or create the global Traccia callback handler.
17
+ *
18
+ * @example
19
+ * // Instead of:
20
+ * const handler = new TraciaCallbackHandler();
21
+ * const model = new ChatOpenAI({ callbacks: [handler] });
22
+ *
23
+ * // Just do:
24
+ * const model = new ChatOpenAI({ callbacks: [getTraciaHandler()] });
25
+ */
26
+ export function getTraciaHandler(): TraciaCallbackHandler {
27
+ if (!globalTraciaHandler) {
28
+ globalTraciaHandler = new TraciaCallbackHandler();
29
+ }
30
+ return globalTraciaHandler;
31
+ }
32
+
33
+ /**
34
+ * Wrap any LangChain model/chain/agent with automatic tracing.
35
+ *
36
+ * @example
37
+ * const model = new ChatOpenAI({ modelName: 'gpt-4' });
38
+ * const tracedModel = withTracing(model);
39
+ *
40
+ * const response = await tracedModel.invoke({ input: 'Hello' });
41
+ * // Automatically traced!
42
+ */
43
+ export function withTracing<T extends any>(component: T): T {
44
+ const handler = getTraciaHandler();
45
+
46
+ // For models and chains, add the handler to callbacks
47
+ if (component && typeof component === 'object') {
48
+ if ('callbacks' in component) {
49
+ // If callbacks exist, add our handler
50
+ const existing = (component as any).callbacks || [];
51
+ (component as any).callbacks = Array.isArray(existing)
52
+ ? [...existing, handler]
53
+ : [existing, handler];
54
+ } else {
55
+ // Otherwise create callbacks array
56
+ (component as any).callbacks = [handler];
57
+ }
58
+ }
59
+
60
+ return component;
61
+ }
62
+
63
+ /**
64
+ * Create a traced ChatOpenAI model with one line.
65
+ *
66
+ * @example
67
+ * const model = createTracedOpenAI({ modelName: 'gpt-4' });
68
+ * const response = await model.invoke({ input: 'Hello' });
69
+ */
70
+ export async function createTracedOpenAI(config: any): Promise<any> {
71
+ try {
72
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
73
+ const langchainOpenai = require('@langchain/openai');
74
+ const model = new langchainOpenai.ChatOpenAI(config);
75
+ return withTracing(model);
76
+ } catch (error) {
77
+ throw new Error(
78
+ 'Failed to create traced ChatOpenAI. Make sure @langchain/openai is installed.'
79
+ );
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create a traced agent executor with one line.
85
+ *
86
+ * @example
87
+ * const executor = createTracedAgentExecutor({
88
+ * agent,
89
+ * tools,
90
+ * agentExecutorOptions: { maxIterations: 10 }
91
+ * });
92
+ *
93
+ * const result = await executor.invoke({ input: 'What time is it?' });
94
+ */
95
+ export async function createTracedAgentExecutor(options: {
96
+ agent: any;
97
+ tools: any[];
98
+ agentExecutorOptions?: Record<string, any>;
99
+ }): Promise<any> {
100
+ try {
101
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
102
+ const langchainAgents = require('langchain/agents');
103
+ const { agent, tools, agentExecutorOptions = {} } = options;
104
+
105
+ const executor = langchainAgents.AgentExecutor.fromAgentAndTools({
106
+ agent,
107
+ tools,
108
+ ...agentExecutorOptions,
109
+ callbacks: [getTraciaHandler(), ...(agentExecutorOptions.callbacks || [])],
110
+ });
111
+
112
+ return executor;
113
+ } catch (error) {
114
+ throw new Error(
115
+ 'Failed to create traced AgentExecutor. Make sure langchain/agents is installed.'
116
+ );
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Create a traced LLMChain with one line.
122
+ *
123
+ * @example
124
+ * const chain = createTracedLLMChain({
125
+ * llm: new ChatOpenAI(),
126
+ * prompt: chatPrompt
127
+ * });
128
+ *
129
+ * const result = await chain.invoke({ question: 'Hello?' });
130
+ */
131
+ export async function createTracedLLMChain(options: {
132
+ llm: any;
133
+ prompt: any;
134
+ }): Promise<any> {
135
+ try {
136
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
137
+ const langchainChains = require('langchain/chains');
138
+ const { llm, prompt } = options;
139
+
140
+ const chain = new langchainChains.LLMChain({
141
+ llm: withTracing(llm),
142
+ prompt,
143
+ callbacks: [getTraciaHandler()],
144
+ });
145
+
146
+ return chain;
147
+ } catch (error) {
148
+ throw new Error(
149
+ 'Failed to create traced LLMChain. Make sure langchain/chains is installed.'
150
+ );
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Decorator for methods that should be traced.
156
+ *
157
+ * @example
158
+ * class MyAgent {
159
+ * @traced('agent-process')
160
+ * async process(input: string) {
161
+ * return await this.llm.invoke({ input });
162
+ * }
163
+ * }
164
+ *
165
+ * const agent = new MyAgent();
166
+ * const result = await agent.process('Hello'); // Automatically traced!
167
+ */
168
+ export function traced(spanName: string) {
169
+ return function (
170
+ _target: any,
171
+ propertyKey: string,
172
+ descriptor: PropertyDescriptor
173
+ ) {
174
+ const originalMethod = descriptor.value;
175
+
176
+ descriptor.value = async function (...args: any[]) {
177
+ const { getTracer } = await import('../auto');
178
+ const tracer = getTracer('decorated-method');
179
+
180
+ return tracer.startActiveSpan(spanName, async (span) => {
181
+ try {
182
+ span.setAttribute('method', propertyKey);
183
+ span.setAttribute('args_count', args.length);
184
+ const result = await originalMethod.apply(this, args);
185
+ span.setAttribute('success', true);
186
+ return result;
187
+ } catch (error) {
188
+ if (error instanceof Error) {
189
+ span.recordException(error);
190
+ }
191
+ throw error;
192
+ }
193
+ });
194
+ };
195
+
196
+ return descriptor;
197
+ };
198
+ }
199
+
200
+ /**
201
+ * Simple configuration helper for common LangChain setup patterns.
202
+ *
203
+ * @example
204
+ * const { model, executor } = await setupLangChainWithTracing({
205
+ * modelName: 'gpt-4',
206
+ * tools: [weatherTool, calculatorTool],
207
+ * systemPrompt: 'You are a helpful assistant.'
208
+ * });
209
+ *
210
+ * const result = await executor.invoke({ input: 'What is the weather?' });
211
+ */
212
+ export async function setupLangChainWithTracing(options: {
213
+ modelName?: string;
214
+ modelConfig?: Record<string, any>;
215
+ tools?: any[];
216
+ systemPrompt?: string;
217
+ }): Promise<{
218
+ model: any;
219
+ executor: any;
220
+ handler: TraciaCallbackHandler;
221
+ }> {
222
+ try {
223
+ const {
224
+ modelName = 'gpt-4',
225
+ modelConfig = {},
226
+ tools = [],
227
+ systemPrompt,
228
+ } = options;
229
+
230
+ // Create traced model
231
+ const model = await createTracedOpenAI({
232
+ modelName,
233
+ ...modelConfig,
234
+ });
235
+
236
+ // Create agent if tools provided
237
+ let executor = null;
238
+ if (tools.length > 0) {
239
+ try {
240
+ // Try to create agent with tools
241
+ const langchainAgents = require('langchain/agents');
242
+ const langchainCore = require('@langchain/core/prompts');
243
+
244
+ // Create prompt
245
+ const prompt = langchainCore.ChatPromptTemplate.fromMessages([
246
+ ...(systemPrompt
247
+ ? [['system', systemPrompt]]
248
+ : [['system', 'You are a helpful assistant.']]),
249
+ ['human', '{input}'],
250
+ new langchainCore.MessagesPlaceholder('agent_scratchpad'),
251
+ ]);
252
+
253
+ // Create agent
254
+ const agent = await langchainAgents.createOpenAIToolsAgent({
255
+ llmWithTools: model,
256
+ tools,
257
+ prompt,
258
+ callbacks: [getTraciaHandler()],
259
+ });
260
+
261
+ // Create executor
262
+ executor = await createTracedAgentExecutor({
263
+ agent,
264
+ tools,
265
+ });
266
+ } catch (error) {
267
+ // If agent creation fails, just return model without executor
268
+ console.warn('Could not create agent executor:', (error as Error).message);
269
+ }
270
+ }
271
+
272
+ return {
273
+ model,
274
+ executor,
275
+ handler: getTraciaHandler(),
276
+ };
277
+ } catch (error) {
278
+ const err = error as Error;
279
+ throw new Error(
280
+ `Failed to setup LangChain with tracing: ${err.message}`
281
+ );
282
+ }
283
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Automatic LangGraph instrumentation - simple one-line setup.
3
+ *
4
+ * Wrap your LangGraph nodes and conditionals to get tracing without
5
+ * any boilerplate code.
6
+ */
7
+
8
+ import {
9
+ instrumentLangGraph,
10
+ createTracedNode,
11
+ createTracedConditional,
12
+ } from './langgraph-instrumentation';
13
+
14
+ /**
15
+ * Wrap a StateGraph with automatic tracing.
16
+ *
17
+ * @example
18
+ * const graph = new StateGraph(GraphState);
19
+ * graph.addNode('step1', processStep1);
20
+ * graph.addNode('step2', processStep2);
21
+ * graph.addEdge('step1', 'step2');
22
+ *
23
+ * // Just wrap your graph!
24
+ * const traced = wrapGraphWithTracing(graph);
25
+ * const compiled = traced.compile();
26
+ *
27
+ * const result = await compiled.invoke({ input: 'data' });
28
+ * // Fully traced with no other changes!
29
+ */
30
+ export function wrapGraphWithTracing(graph: any, options: any = {}): any {
31
+ return instrumentLangGraph(graph, {
32
+ traceGraphExecution: true,
33
+ traceNodeExecution: true,
34
+ captureGraphState: options.captureState ?? false,
35
+ ...options,
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Create a traced node function with one line.
41
+ *
42
+ * @example
43
+ * // Wrap your existing node:
44
+ * const existingNode = (state) => ({ result: 'done' });
45
+ * const traced = tracedNode('process-step', existingNode);
46
+ * graph.addNode('process', traced);
47
+ */
48
+ export function tracedNode(
49
+ nodeName: string,
50
+ nodeFunction: (state: any) => Promise<any> | any
51
+ ): (state: any) => Promise<any> {
52
+ return createTracedNode(nodeName, nodeFunction);
53
+ }
54
+
55
+ /**
56
+ * Create a traced conditional edge router with one line.
57
+ *
58
+ * @example
59
+ * // Wrap your existing router:
60
+ * const decideRoute = (state) => state.approved ? 'done' : 'review';
61
+ * const traced = tracedConditional('route-check', decideRoute);
62
+ * graph.addConditionalEdges('check', traced);
63
+ */
64
+ export function tracedConditional(
65
+ conditionalName: string,
66
+ routeFunction: (state: Record<string, any>) => string
67
+ ): (state: Record<string, any>) => string {
68
+ return createTracedConditional(conditionalName, routeFunction);
69
+ }
70
+
71
+ /**
72
+ * Factory function to create a traced agent graph from simple config.
73
+ *
74
+ * @example
75
+ * const graph = createSimpleTracedGraph({
76
+ * nodes: {
77
+ * 'process': async (state) => ({ result: await process(state.input) }),
78
+ * 'review': async (state) => ({ reviewed: true }),
79
+ * },
80
+ * edges: [
81
+ * { from: '__start__', to: 'process' },
82
+ * { from: 'process', to: 'review' },
83
+ * { from: 'review', to: '__end__' },
84
+ * ],
85
+ * });
86
+ *
87
+ * const result = await graph.compile().invoke({ input: 'data' });
88
+ */
89
+ export async function createSimpleTracedGraph(options: {
90
+ nodes: Record<string, (state: any) => Promise<any>>;
91
+ edges: Array<{ from: string; to: string }>;
92
+ conditionals?: Record<
93
+ string,
94
+ { router: (state: any) => string; routes: string[] }
95
+ >;
96
+ startState?: Record<string, any>;
97
+ }): Promise<any> {
98
+ try {
99
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
100
+ const langgraphModule = require('@langchain/langgraph');
101
+ const { nodes, edges, conditionals = {}, startState = {} } = options;
102
+
103
+ // Create state graph
104
+ const graph = new langgraphModule.StateGraph({
105
+ channels: startState,
106
+ });
107
+
108
+ // Add traced nodes
109
+ for (const [nodeName, nodeFunc] of Object.entries(nodes)) {
110
+ const tracedNodeFunc = tracedNode(nodeName, nodeFunc);
111
+ graph.addNode(nodeName, tracedNodeFunc);
112
+ }
113
+
114
+ // Add edges
115
+ for (const edge of edges) {
116
+ if (edge.from === '__start__') {
117
+ graph.setEntryPoint(edge.to);
118
+ } else if (edge.to === '__end__') {
119
+ graph.setFinishPoint(edge.from);
120
+ } else {
121
+ graph.addEdge(edge.from, edge.to);
122
+ }
123
+ }
124
+
125
+ // Add conditional edges
126
+ for (const [condName, condConfig] of Object.entries(conditionals)) {
127
+ const tracedRouter = tracedConditional(condName, condConfig.router);
128
+ graph.addConditionalEdges(condName, tracedRouter, condConfig.routes);
129
+ }
130
+
131
+ // Wrap entire graph with tracing
132
+ return wrapGraphWithTracing(graph);
133
+ } catch (error) {
134
+ const err = error as Error;
135
+ throw new Error(
136
+ `Failed to create simple traced graph: ${err.message}. Make sure @langchain/langgraph is installed.`
137
+ );
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Trace a single agent function or tool with minimal setup.
143
+ *
144
+ * @example
145
+ * const tool = traceableFunction('my-tool', async (input) => {
146
+ * return processInput(input);
147
+ * });
148
+ *
149
+ * const result = await tool('data');
150
+ * // Automatically traced!
151
+ */
152
+ export function traceableFunction<T extends (...args: any[]) => any>(
153
+ spanName: string,
154
+ fn: T
155
+ ): T {
156
+ return (async (...args: any[]) => {
157
+ const { getTracer } = await import('../auto');
158
+ const tracer = getTracer('traceable-function');
159
+
160
+ return tracer.startActiveSpan(spanName, async (span) => {
161
+ try {
162
+ span.setAttribute('args_count', args.length);
163
+ if (args[0] && typeof args[0] === 'object') {
164
+ span.setAttribute('input_keys', Object.keys(args[0]).join(','));
165
+ }
166
+
167
+ const result = await fn(...args);
168
+
169
+ if (result && typeof result === 'object') {
170
+ span.setAttribute('output_keys', Object.keys(result).join(','));
171
+ }
172
+ span.setAttribute('success', true);
173
+
174
+ return result;
175
+ } catch (error) {
176
+ if (error instanceof Error) {
177
+ span.recordException(error);
178
+ }
179
+ throw error;
180
+ }
181
+ });
182
+ }) as T;
183
+ }
184
+
185
+ /**
186
+ * Quick setup for a common agent pattern: input → process → route → output.
187
+ *
188
+ * @example
189
+ * const graph = await createAgentWorkflow({
190
+ * processInput: async (state) => ({ processed: await analyze(state.input) }),
191
+ * routeDecision: (state) => state.processed.needsReview ? 'review' : 'done',
192
+ * reviewStep: async (state) => ({ reviewed: true }),
193
+ * });
194
+ *
195
+ * const result = await graph.compile().invoke({ input: 'user input' });
196
+ */
197
+ export async function createAgentWorkflow(options: {
198
+ processInput: (state: Record<string, any>) => Promise<Record<string, any>>;
199
+ routeDecision: (state: Record<string, any>) => string;
200
+ reviewStep?: (state: Record<string, any>) => Promise<Record<string, any>>;
201
+ finalStep?: (state: Record<string, any>) => Promise<Record<string, any>>;
202
+ }): Promise<any> {
203
+ try {
204
+ const {
205
+ processInput,
206
+ routeDecision,
207
+ reviewStep,
208
+ finalStep,
209
+ } = options;
210
+
211
+ const nodes: Record<string, (state: any) => Promise<any>> = {
212
+ process: processInput,
213
+ };
214
+
215
+ if (reviewStep) nodes.review = reviewStep;
216
+ if (finalStep) nodes.final = finalStep;
217
+
218
+ const edges: Array<{ from: string; to: string }> = [
219
+ { from: '__start__', to: 'process' },
220
+ ];
221
+
222
+ const conditionals: Record<string, any> = {
223
+ route: {
224
+ router: routeDecision,
225
+ routes: ['done', ...(reviewStep ? ['review'] : [])],
226
+ },
227
+ };
228
+
229
+ edges.push({ from: 'process', to: 'route' });
230
+
231
+ if (reviewStep) {
232
+ edges.push({ from: 'review', to: finalStep ? 'final' : '__end__' });
233
+ }
234
+
235
+ if (finalStep) {
236
+ edges.push({ from: 'final', to: '__end__' });
237
+ }
238
+
239
+ return createSimpleTracedGraph({
240
+ nodes,
241
+ edges,
242
+ conditionals,
243
+ });
244
+ } catch (error) {
245
+ const err = error as Error;
246
+ throw new Error(`Failed to create agent workflow: ${err.message}`);
247
+ }
248
+ }
@@ -5,9 +5,39 @@
5
5
  * @module integrations
6
6
  */
7
7
 
8
+ // LangChain integrations
8
9
  export { TraciaCallbackHandler } from './langchain-callback';
10
+ export {
11
+ getTraciaHandler,
12
+ withTracing,
13
+ createTracedOpenAI,
14
+ createTracedAgentExecutor,
15
+ createTracedLLMChain,
16
+ traced,
17
+ setupLangChainWithTracing,
18
+ } from './auto-langchain';
19
+
20
+ // LangGraph integrations
9
21
  export {
10
22
  instrumentLangGraph,
11
23
  createTracedNode,
12
24
  createTracedConditional,
13
25
  } from './langgraph-instrumentation';
26
+ export {
27
+ wrapGraphWithTracing,
28
+ tracedNode,
29
+ tracedConditional,
30
+ createSimpleTracedGraph,
31
+ traceableFunction,
32
+ createAgentWorkflow,
33
+ } from './auto-langgraph';
34
+
35
+ // Ollama integrations
36
+ export {
37
+ createOllamaWithTracing,
38
+ setupOllamaWithTracing,
39
+ createOllamaChatbot,
40
+ createOllamaStreamingChatbot,
41
+ getOllamaSetupInstructions,
42
+ POPULAR_OLLAMA_MODELS,
43
+ } from './ollama-integration';