fix: lint fix

This commit is contained in:
Eric Dong
2025-11-10 15:05:45 -05:00
parent cd54282023
commit cd244b3fe9
11 changed files with 236 additions and 298 deletions
@@ -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);
@@ -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 {
@@ -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);
}
}
@@ -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();
}
}
@@ -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 {
}
}
}
@@ -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");
}
}
@@ -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 {
}
}
}