mirror of
https://github.com/langchain-ai/langsmith-java.git
synced 2026-08-24 12:22:55 -04:00
fix: lint fix
This commit is contained in:
+15
-18
@@ -11,7 +11,7 @@ import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Example: Send live OpenTelemetry traces to Jaeger.
|
||||
*
|
||||
*
|
||||
* Start Jaeger: docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest
|
||||
* Run: ./gradlew :langsmith-java-example:run -Pexample=OtelJaegerExample
|
||||
* View: http://localhost:16686
|
||||
@@ -38,14 +38,13 @@ public class OtelJaegerExample {
|
||||
System.out.println("→ Root span: langchain.chain started");
|
||||
|
||||
// CHILD 1: First LLM call
|
||||
Span llmSpan1 = OtelSpanCreator.createLlmSpan(tracer, "openai.chat",
|
||||
"openai", "gpt-4", projectName, null);
|
||||
Span llmSpan1 = OtelSpanCreator.createLlmSpan(tracer, "openai.chat", "openai", "gpt-4", projectName, null);
|
||||
try (Scope scope = llmSpan1.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(llmSpan1, "What's the weather?");
|
||||
|
||||
|
||||
System.out.println(" → Child span 1: openai.chat started");
|
||||
Thread.sleep(500);
|
||||
|
||||
|
||||
OtelSpanCreator.setOutput(llmSpan1, "I'll check the weather for you.");
|
||||
OtelSpanCreator.setTokenUsage(llmSpan1, 10, 8);
|
||||
llmSpan1.setStatus(StatusCode.OK);
|
||||
@@ -55,10 +54,9 @@ public class OtelJaegerExample {
|
||||
}
|
||||
|
||||
// CHILD 2: Tool call
|
||||
Span toolSpan = OtelSpanCreator.createToolSpan(tracer, "weather.tool",
|
||||
"get_weather", projectName, null);
|
||||
Span toolSpan = OtelSpanCreator.createToolSpan(tracer, "weather.tool", "get_weather", projectName, null);
|
||||
try (Scope scope = toolSpan.makeCurrent()) {
|
||||
|
||||
|
||||
System.out.println(" → Child span 2: weather.tool started");
|
||||
Thread.sleep(300);
|
||||
toolSpan.setStatus(StatusCode.OK);
|
||||
@@ -68,33 +66,32 @@ public class OtelJaegerExample {
|
||||
}
|
||||
|
||||
// CHILD 3: Second LLM call with nested database query
|
||||
Span llmSpan2 = OtelSpanCreator.createLlmSpan(tracer, "openai.chat",
|
||||
"openai", "gpt-4", projectName, null);
|
||||
Span llmSpan2 = OtelSpanCreator.createLlmSpan(tracer, "openai.chat", "openai", "gpt-4", projectName, null);
|
||||
try (Scope scope2 = llmSpan2.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(llmSpan2, "Provide a detailed weather summary.");
|
||||
|
||||
|
||||
System.out.println(" → Child span 3: openai.chat started");
|
||||
|
||||
|
||||
// NESTED CHILD: Database query
|
||||
Span dbSpan = OtelSpanCreator.createToolSpan(tracer, "database.query",
|
||||
"postgresql_query", projectName, null);
|
||||
Span dbSpan =
|
||||
OtelSpanCreator.createToolSpan(tracer, "database.query", "postgresql_query", projectName, null);
|
||||
try (Scope dbScope = dbSpan.makeCurrent()) {
|
||||
dbSpan.setAttribute(AttributeKey.stringKey("db.system"), "postgresql");
|
||||
OtelSpanCreator.setInput(dbSpan, "SELECT * FROM weather_data WHERE city='SF'");
|
||||
|
||||
|
||||
System.out.println(" → Nested span: database.query started");
|
||||
Thread.sleep(200);
|
||||
|
||||
|
||||
// Simulate error
|
||||
dbSpan.setStatus(StatusCode.ERROR, "Connection timeout");
|
||||
dbSpan.setAttribute(AttributeKey.booleanKey("error"), true);
|
||||
dbSpan.setAttribute(AttributeKey.stringKey("error.type"), "timeout");
|
||||
|
||||
|
||||
System.out.println(" ← Nested span: database.query failed");
|
||||
} finally {
|
||||
dbSpan.end();
|
||||
}
|
||||
|
||||
|
||||
Thread.sleep(400);
|
||||
OtelSpanCreator.setOutput(llmSpan2, "Unable to retrieve detailed data due to database error.");
|
||||
OtelSpanCreator.setTokenUsage(llmSpan2, 20, 15);
|
||||
|
||||
+12
-13
@@ -8,7 +8,6 @@ import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.StatusCode;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -16,10 +15,10 @@ import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Example: Send OpenTelemetry traces to LangSmith UI.
|
||||
*
|
||||
*
|
||||
* This is a mock/demo example that simulates LLM calls without requiring API keys.
|
||||
* It demonstrates the tracing structure and waterfall visualization.
|
||||
*
|
||||
*
|
||||
* Usage:
|
||||
* export LANGSMITH_API_KEY=your_api_key
|
||||
* ./gradlew :langsmith-java-example:run -Pexample=OtelLangSmith
|
||||
@@ -81,8 +80,8 @@ public class OtelLangSmithExample {
|
||||
|
||||
try (Scope rootScope = rootSpan.makeCurrent()) {
|
||||
// CHILD 1: First LLM call
|
||||
Span llmSpan1 = OtelSpanCreator.createLlmSpan(tracer, "openai.llm.call",
|
||||
"openai", "gpt-4", projectName, sessionId);
|
||||
Span llmSpan1 =
|
||||
OtelSpanCreator.createLlmSpan(tracer, "openai.llm.call", "openai", "gpt-4", projectName, sessionId);
|
||||
try (Scope llmScope1 = llmSpan1.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(llmSpan1, "What's the weather in San Francisco?");
|
||||
Thread.sleep(500);
|
||||
@@ -94,8 +93,8 @@ public class OtelLangSmithExample {
|
||||
}
|
||||
|
||||
// CHILD 2: Tool call
|
||||
Span toolSpan = OtelSpanCreator.createToolSpan(tracer, "weather.tool",
|
||||
"get_weather", projectName, sessionId);
|
||||
Span toolSpan =
|
||||
OtelSpanCreator.createToolSpan(tracer, "weather.tool", "get_weather", projectName, sessionId);
|
||||
try (Scope toolScope = toolSpan.makeCurrent()) {
|
||||
toolSpan.setAttribute(AttributeKey.stringKey("tool.input"), "{\"location\":\"San Francisco\"}");
|
||||
Thread.sleep(300);
|
||||
@@ -105,14 +104,14 @@ public class OtelLangSmithExample {
|
||||
}
|
||||
|
||||
// CHILD 3: Second LLM call with nested retriever
|
||||
Span llmSpan2 = OtelSpanCreator.createLlmSpan(tracer, "openai.llm.final",
|
||||
"openai", "gpt-4", projectName, sessionId);
|
||||
Span llmSpan2 = OtelSpanCreator.createLlmSpan(
|
||||
tracer, "openai.llm.final", "openai", "gpt-4", projectName, sessionId);
|
||||
try (Scope llmScope2 = llmSpan2.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(llmSpan2, "Based on the weather data, provide a summary.");
|
||||
|
||||
// NESTED CHILD: Retriever call inside LLM
|
||||
Span retrieverSpan = OtelSpanCreator.createRetrievalSpan(tracer, "database.retriever",
|
||||
projectName, sessionId);
|
||||
Span retrieverSpan =
|
||||
OtelSpanCreator.createRetrievalSpan(tracer, "database.retriever", projectName, sessionId);
|
||||
try (Scope retrieverScope = retrieverSpan.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(retrieverSpan, "weather forecast data");
|
||||
Thread.sleep(200);
|
||||
@@ -123,8 +122,8 @@ public class OtelLangSmithExample {
|
||||
}
|
||||
|
||||
Thread.sleep(400);
|
||||
OtelSpanCreator.setOutput(llmSpan2,
|
||||
"The weather in San Francisco is sunny with a temperature of 72°F.");
|
||||
OtelSpanCreator.setOutput(
|
||||
llmSpan2, "The weather in San Francisco is sunny with a temperature of 72°F.");
|
||||
OtelSpanCreator.setTokenUsage(llmSpan2, 25, 18);
|
||||
llmSpan2.setStatus(StatusCode.OK);
|
||||
} finally {
|
||||
|
||||
+7
-8
@@ -5,22 +5,22 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Spring Boot example: Send OpenTelemetry traces to LangSmith.
|
||||
*
|
||||
*
|
||||
* Usage:
|
||||
* export LANGSMITH_API_KEY=your_api_key
|
||||
* export LANGSMITH_PROJECT=my-project # optional, defaults to "default"
|
||||
* ./gradlew :langsmith-java-example:run -Pexample=SpringBootLangSmith
|
||||
*
|
||||
*
|
||||
* Then make requests to:
|
||||
* http://localhost:8080/api/chat
|
||||
* http://localhost:8080/api/analyze?text=hello
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SpringBootLangSmithExample {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== Spring Boot + LangSmith OpenTelemetry Example ===\n");
|
||||
|
||||
|
||||
// Check required environment variables
|
||||
String apiKey = System.getenv("LANGSMITH_API_KEY");
|
||||
if (apiKey == null || apiKey.isEmpty()) {
|
||||
@@ -31,12 +31,12 @@ public class SpringBootLangSmithExample {
|
||||
System.err.println(" ./gradlew :langsmith-java-example:run -Pexample=SpringBootLangSmith");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
|
||||
String projectName = System.getenv("LANGSMITH_PROJECT");
|
||||
if (projectName == null || projectName.isEmpty()) {
|
||||
projectName = "default";
|
||||
}
|
||||
|
||||
|
||||
System.out.println("Configuration:");
|
||||
System.out.println(" Project: " + projectName);
|
||||
System.out.println(" Endpoint: https://api.smith.langchain.com/otel/v1/traces");
|
||||
@@ -45,8 +45,7 @@ public class SpringBootLangSmithExample {
|
||||
System.out.println(" POST http://localhost:8080/api/chat");
|
||||
System.out.println(" GET http://localhost:8080/api/analyze?text=hello");
|
||||
System.out.println();
|
||||
|
||||
|
||||
SpringApplication.run(SpringBootLangSmithExample.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-9
@@ -3,19 +3,18 @@ package com.langchain.smith.example.config;
|
||||
import com.langchain.smith.otel.OtelConfig;
|
||||
import com.langchain.smith.otel.OtelTraceExporter;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Spring configuration for OpenTelemetry integration with LangSmith.
|
||||
*/
|
||||
@Configuration
|
||||
public class OtelConfiguration {
|
||||
|
||||
|
||||
@Bean
|
||||
public OtelTraceExporter otelTraceExporter() {
|
||||
String apiKey = System.getenv("LANGSMITH_API_KEY");
|
||||
@@ -23,11 +22,11 @@ public class OtelConfiguration {
|
||||
if (projectName == null || projectName.isEmpty()) {
|
||||
projectName = "default";
|
||||
}
|
||||
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("x-api-key", apiKey);
|
||||
headers.put("Langsmith-Project", projectName);
|
||||
|
||||
|
||||
OtelConfig config = OtelConfig.builder()
|
||||
.enabled(true)
|
||||
.endpoint("https://api.smith.langchain.com/otel/v1/traces")
|
||||
@@ -35,13 +34,12 @@ public class OtelConfiguration {
|
||||
.timeout(Duration.ofSeconds(30))
|
||||
.serviceName("spring-boot-langsmith")
|
||||
.build();
|
||||
|
||||
|
||||
return OtelTraceExporter.fromConfig(config);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public Tracer tracer(OtelTraceExporter exporter) {
|
||||
return exporter.getTracer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -10,14 +10,14 @@ import org.springframework.stereotype.Component;
|
||||
*/
|
||||
@Component
|
||||
public class OtelShutdownHook {
|
||||
|
||||
|
||||
private final OtelTraceExporter exporter;
|
||||
|
||||
|
||||
@Autowired
|
||||
public OtelShutdownHook(OtelTraceExporter exporter) {
|
||||
this.exporter = exporter;
|
||||
}
|
||||
|
||||
|
||||
@PreDestroy
|
||||
public void onShutdown() {
|
||||
System.out.println("\n→ Flushing OpenTelemetry traces...");
|
||||
@@ -29,4 +29,3 @@ public class OtelShutdownHook {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-45
@@ -6,59 +6,56 @@ import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.StatusCode;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* REST controller demonstrating OpenTelemetry tracing with LangSmith.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class ChatController {
|
||||
|
||||
|
||||
private final Tracer tracer;
|
||||
private final LlmService llmService;
|
||||
|
||||
|
||||
@Autowired
|
||||
public ChatController(Tracer tracer, LlmService llmService) {
|
||||
this.tracer = tracer;
|
||||
this.llmService = llmService;
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/chat")
|
||||
public Map<String, Object> chat(@RequestBody Map<String, String> request) {
|
||||
String userMessage = request.getOrDefault("message", "Hello!");
|
||||
|
||||
|
||||
// Create a root span for the entire request
|
||||
Span rootSpan = OtelSpanCreator.createChainSpan(
|
||||
tracer,
|
||||
"chat_request",
|
||||
"spring-boot-langsmith",
|
||||
null
|
||||
);
|
||||
|
||||
Span rootSpan = OtelSpanCreator.createChainSpan(tracer, "chat_request", "spring-boot-langsmith", null);
|
||||
|
||||
try (Scope scope = rootSpan.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(rootSpan, userMessage);
|
||||
|
||||
|
||||
System.out.println("→ Processing chat request: " + userMessage);
|
||||
|
||||
|
||||
// Call the LLM service (which creates its own span)
|
||||
String response = llmService.generateResponse(userMessage);
|
||||
|
||||
|
||||
OtelSpanCreator.setOutput(rootSpan, response);
|
||||
rootSpan.setStatus(StatusCode.OK);
|
||||
|
||||
|
||||
System.out.println("← Chat response generated");
|
||||
|
||||
|
||||
return Map.of(
|
||||
"request", userMessage,
|
||||
"response", response,
|
||||
"model", "gpt-4",
|
||||
"trace_id", rootSpan.getSpanContext().getTraceId()
|
||||
);
|
||||
|
||||
"request",
|
||||
userMessage,
|
||||
"response",
|
||||
response,
|
||||
"model",
|
||||
"gpt-4",
|
||||
"trace_id",
|
||||
rootSpan.getSpanContext().getTraceId());
|
||||
|
||||
} catch (Exception e) {
|
||||
rootSpan.setStatus(StatusCode.ERROR, e.getMessage());
|
||||
throw e;
|
||||
@@ -66,39 +63,33 @@ public class ChatController {
|
||||
rootSpan.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/analyze")
|
||||
public Map<String, Object> analyze(@RequestParam String text) {
|
||||
// Create a span for the analysis operation
|
||||
Span analysisSpan = OtelSpanCreator.createChainSpan(
|
||||
tracer,
|
||||
"text_analysis",
|
||||
"spring-boot-langsmith",
|
||||
null
|
||||
);
|
||||
|
||||
Span analysisSpan = OtelSpanCreator.createChainSpan(tracer, "text_analysis", "spring-boot-langsmith", null);
|
||||
|
||||
try (Scope scope = analysisSpan.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(analysisSpan, text);
|
||||
|
||||
|
||||
System.out.println("→ Analyzing text: " + text);
|
||||
|
||||
|
||||
// Simulate analysis with nested operations
|
||||
int wordCount = text.split("\\s+").length;
|
||||
String sentiment = llmService.analyzeSentiment(text);
|
||||
|
||||
|
||||
String result = String.format("Word count: %d, Sentiment: %s", wordCount, sentiment);
|
||||
OtelSpanCreator.setOutput(analysisSpan, result);
|
||||
analysisSpan.setStatus(StatusCode.OK);
|
||||
|
||||
|
||||
System.out.println("← Analysis complete");
|
||||
|
||||
|
||||
return Map.of(
|
||||
"text", text,
|
||||
"word_count", wordCount,
|
||||
"sentiment", sentiment,
|
||||
"trace_id", analysisSpan.getSpanContext().getTraceId()
|
||||
);
|
||||
|
||||
"text", text,
|
||||
"word_count", wordCount,
|
||||
"sentiment", sentiment,
|
||||
"trace_id", analysisSpan.getSpanContext().getTraceId());
|
||||
|
||||
} catch (Exception e) {
|
||||
analysisSpan.setStatus(StatusCode.ERROR, e.getMessage());
|
||||
throw e;
|
||||
@@ -106,10 +97,9 @@ public class ChatController {
|
||||
analysisSpan.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/health")
|
||||
public Map<String, String> health() {
|
||||
return Map.of("status", "healthy", "service", "spring-boot-langsmith");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-36
@@ -13,45 +13,39 @@ import org.springframework.stereotype.Service;
|
||||
*/
|
||||
@Service
|
||||
public class LlmService {
|
||||
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
|
||||
@Autowired
|
||||
public LlmService(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulates an LLM API call with tracing.
|
||||
*/
|
||||
public String generateResponse(String input) {
|
||||
Span llmSpan = OtelSpanCreator.createLlmSpan(
|
||||
tracer,
|
||||
"openai.chat",
|
||||
"openai",
|
||||
"gpt-4",
|
||||
"spring-boot-langsmith",
|
||||
null
|
||||
);
|
||||
|
||||
Span llmSpan =
|
||||
OtelSpanCreator.createLlmSpan(tracer, "openai.chat", "openai", "gpt-4", "spring-boot-langsmith", null);
|
||||
|
||||
try (Scope scope = llmSpan.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(llmSpan, input);
|
||||
|
||||
|
||||
System.out.println(" → Calling OpenAI API...");
|
||||
|
||||
|
||||
// Simulate LLM processing time
|
||||
Thread.sleep(500);
|
||||
|
||||
|
||||
String response = "I received your message: '" + input + "'. How can I help you today?";
|
||||
|
||||
|
||||
OtelSpanCreator.setOutput(llmSpan, response);
|
||||
OtelSpanCreator.setTokenUsage(llmSpan, 15, 20);
|
||||
llmSpan.setStatus(StatusCode.OK);
|
||||
|
||||
|
||||
System.out.println(" ← OpenAI API response received");
|
||||
|
||||
|
||||
return response;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
llmSpan.setStatus(StatusCode.ERROR, e.getMessage());
|
||||
throw new RuntimeException("LLM call failed", e);
|
||||
@@ -59,28 +53,22 @@ public class LlmService {
|
||||
llmSpan.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulates sentiment analysis with tracing.
|
||||
*/
|
||||
public String analyzeSentiment(String text) {
|
||||
Span sentimentSpan = OtelSpanCreator.createLlmSpan(
|
||||
tracer,
|
||||
"sentiment_analysis",
|
||||
"openai",
|
||||
"gpt-4",
|
||||
"spring-boot-langsmith",
|
||||
null
|
||||
);
|
||||
|
||||
tracer, "sentiment_analysis", "openai", "gpt-4", "spring-boot-langsmith", null);
|
||||
|
||||
try (Scope scope = sentimentSpan.makeCurrent()) {
|
||||
OtelSpanCreator.setInput(sentimentSpan, text);
|
||||
|
||||
|
||||
System.out.println(" → Analyzing sentiment...");
|
||||
|
||||
|
||||
// Simulate analysis time
|
||||
Thread.sleep(300);
|
||||
|
||||
|
||||
// Simple sentiment detection
|
||||
String sentiment;
|
||||
if (text.toLowerCase().contains("good") || text.toLowerCase().contains("great")) {
|
||||
@@ -90,15 +78,15 @@ public class LlmService {
|
||||
} else {
|
||||
sentiment = "neutral";
|
||||
}
|
||||
|
||||
|
||||
OtelSpanCreator.setOutput(sentimentSpan, sentiment);
|
||||
OtelSpanCreator.setTokenUsage(sentimentSpan, 8, 2);
|
||||
sentimentSpan.setStatus(StatusCode.OK);
|
||||
|
||||
|
||||
System.out.println(" ← Sentiment: " + sentiment);
|
||||
|
||||
|
||||
return sentiment;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
sentimentSpan.setStatus(StatusCode.ERROR, e.getMessage());
|
||||
throw new RuntimeException("Sentiment analysis failed", e);
|
||||
@@ -107,4 +95,3 @@ public class LlmService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user