diff --git a/.gradle/8.0.2/executionHistory/executionHistory.bin b/.gradle/8.0.2/executionHistory/executionHistory.bin index b9f31a4..836ab63 100644 Binary files a/.gradle/8.0.2/executionHistory/executionHistory.bin and b/.gradle/8.0.2/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.0.2/executionHistory/executionHistory.lock b/.gradle/8.0.2/executionHistory/executionHistory.lock index 30a82c7..2c17ba2 100644 Binary files a/.gradle/8.0.2/executionHistory/executionHistory.lock and b/.gradle/8.0.2/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.0.2/fileHashes/fileHashes.bin b/.gradle/8.0.2/fileHashes/fileHashes.bin index abcd0b8..cafd793 100644 Binary files a/.gradle/8.0.2/fileHashes/fileHashes.bin and b/.gradle/8.0.2/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.0.2/fileHashes/fileHashes.lock b/.gradle/8.0.2/fileHashes/fileHashes.lock index 55496bf..f268446 100644 Binary files a/.gradle/8.0.2/fileHashes/fileHashes.lock and b/.gradle/8.0.2/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.0.2/fileHashes/resourceHashesCache.bin b/.gradle/8.0.2/fileHashes/resourceHashesCache.bin index 1446b76..37ff1a1 100644 Binary files a/.gradle/8.0.2/fileHashes/resourceHashesCache.bin and b/.gradle/8.0.2/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 59e9d35..0aa5c46 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index f5447a8..f82c777 100644 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/README.md b/README.md index 52a9215..1247bc9 100644 --- a/README.md +++ b/README.md @@ -74,68 +74,8 @@ If unspecified, runs will be posted to the `default` project. ### Example: using the RunTree API (experimental) The RunTree API is currently the recommended way to post runs to LangSmith. -```java -package com.langsmith.example; - -import com.langsmith.runtree.RunTree; -import com.langsmith.runtree.RunTreeConfigBuilder; -import com.theokanning.openai.service.OpenAiService; -import com.theokanning.openai.completion.chat.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class OpenAiExample { - public static void main(String[] args) { - String apiKey = System.getenv("OPENAI_API_KEY"); - OpenAiService service = new OpenAiService(apiKey); - - String question = "Can you summarize this morning's meetings?"; - - // Create a top-level run - RunTreeConfigBuilder pipelineConfigBuilder = new RunTreeConfigBuilder() - .setName("Chat Pipeline") - .setRunType("chain") - .setInputs(Collections.singletonMap("question", question)); - - RunTree pipeline = new RunTree(pipelineConfigBuilder.build()); - - var context = "During this morning's meeting, we solved all world conflict."; - - List messages = new ArrayList<>(); - var systemMessage = new ChatMessage(ChatMessageRole.SYSTEM.value(), "You are a helpful assistant. Please respond to the user's request only based on the given context."); - messages.add(systemMessage); - var userMessage = new ChatMessage(ChatMessageRole.USER.value(), "Question: " + question + "\nContext: " + context); - messages.add(userMessage); - - var completionRequest = ChatCompletionRequest - .builder() - .model("gpt-3.5-turbo") - .messages(messages) - .build(); - - // Create a child run - RunTreeConfigBuilder childConfigBuilder = new RunTreeConfigBuilder() - .setName("OpenAI Call") - .setRunType("llm") - .setInputs(Collections.singletonMap("messages", messages)) - .setParentRun(pipeline); - - RunTree childTree = pipeline.createChild(childConfigBuilder.build()); - - ChatMessage responseMessage = service.createChatCompletion(completionRequest).getChoices().get(0).getMessage(); - System.out.println("Response: " + responseMessage.getContent()); - - // End the child run - childTree.end(Collections.singletonMap("response", responseMessage), null, null); - - // End the parent run - pipeline.end(Collections.singletonMap("response", responseMessage.getContent()), null, null); - childTree.postRunAsync().join(); - pipeline.postRunAsync().join(); - } -} -``` + +See a full example in the [here](./examples/src/main/java/com/langsmith/example/OpenAiExample.java). ### Example: creating a resource diff --git a/buildSrc/.gradle/8.0.2/executionHistory/executionHistory.lock b/buildSrc/.gradle/8.0.2/executionHistory/executionHistory.lock index 6e75298..2548a5c 100644 Binary files a/buildSrc/.gradle/8.0.2/executionHistory/executionHistory.lock and b/buildSrc/.gradle/8.0.2/executionHistory/executionHistory.lock differ diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 3db8d72..d5c92f2 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -32,7 +32,7 @@ tasks.withType { } // Dynamically create a task for each example -val examples = listOf("RunTreeExample", "OpenAiExample") // Add the names of your example classes here +val examples = listOf("OpenAiExample") // Add the names of your example classes here examples.forEach { example -> tasks.create("run$example", org.gradle.api.tasks.JavaExec::class.java) { diff --git a/examples/src/main/java/com/langsmith/example/OpenAiExample.java b/examples/src/main/java/com/langsmith/example/OpenAiExample.java index fdd5c6b..9d68d6d 100644 --- a/examples/src/main/java/com/langsmith/example/OpenAiExample.java +++ b/examples/src/main/java/com/langsmith/example/OpenAiExample.java @@ -1,9 +1,11 @@ package com.langsmith.example; +import com.langsmith.runtree.EndOptions; import com.langsmith.runtree.RunTree; -import com.langsmith.runtree.RunTreeConfigBuilder; +import com.langsmith.runtree.RunTreeConfig; import com.theokanning.openai.service.OpenAiService; import com.theokanning.openai.completion.chat.*; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -16,12 +18,14 @@ public static void main(String[] args) { String question = "Can you summarize this morning's meetings?"; // Create a top-level run - RunTreeConfigBuilder pipelineConfigBuilder = new RunTreeConfigBuilder() + RunTreeConfig pipelineConfig = RunTreeConfig + .builder() .setName("Chat Pipeline") .setRunType("chain") - .setInputs(Collections.singletonMap("question", question)); + .setInputs(Collections.singletonMap("question", question)) + .build(); - RunTree pipeline = new RunTree(pipelineConfigBuilder.build()); + RunTree pipeline = new RunTree(pipelineConfig); var context = "During this morning's meeting, we solved all world conflict."; @@ -38,22 +42,30 @@ public static void main(String[] args) { .build(); // Create a child run - RunTreeConfigBuilder childConfigBuilder = new RunTreeConfigBuilder() + RunTreeConfig childConfig = RunTreeConfig + .builder() .setName("OpenAI Call") .setRunType("llm") .setInputs(Collections.singletonMap("messages", messages)) - .setParentRun(pipeline); + .setParentRun(pipeline) + .build(); - RunTree childTree = pipeline.createChild(childConfigBuilder.build()); + RunTree childTree = pipeline.createChild(childConfig); ChatMessage responseMessage = service.createChatCompletion(completionRequest).getChoices().get(0).getMessage(); System.out.println("Response: " + responseMessage.getContent()); // End the child run - childTree.end(Collections.singletonMap("response", responseMessage), null, null); + childTree.end(EndOptions + .builder() + .setOutputs(Collections.singletonMap("response", responseMessage)) + .build()); // End the parent run - pipeline.end(Collections.singletonMap("response", responseMessage.getContent()), null, null); + pipeline.end(EndOptions + .builder() + .setOutputs(Collections.singletonMap("response", responseMessage.getContent())) + .build()); childTree.postRunAsync().join(); pipeline.postRunAsync().join(); } diff --git a/examples/src/main/java/com/langsmith/example/RunTreeExample.java b/examples/src/main/java/com/langsmith/example/RunTreeExample.java deleted file mode 100644 index 52dfe12..0000000 --- a/examples/src/main/java/com/langsmith/example/RunTreeExample.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.langsmith.example; - -import com.langsmith.api.client.LangSmithClientAsync; -import com.langsmith.api.client.okhttp.LangSmithOkHttpClientAsync; -import com.langsmith.runtree.RunTree; -import com.langsmith.runtree.RunTreeConfigBuilder; - -import java.util.Map; - -public class RunTreeExample { - public static void main(String[] args) { - System.out.println("Hello, World!"); - - LangSmithClientAsync client = LangSmithOkHttpClientAsync.fromEnv(); - - // Create a RunTreeConfig object - var builder = new RunTreeConfigBuilder(); - builder.setName("Test Run"); - builder.setRunType("llm"); - builder.setInputs(Map.of("input", "Testing!")); - var config = builder.build(); - - // Create a RunTree object - RunTree runTree = new RunTree(config); - - var childBuilder = new RunTreeConfigBuilder(); - childBuilder.setName("Test Child Run"); - childBuilder.setInputs(Map.of("input", "Test2!")); - var childConfig = childBuilder.build(); - - // Time it - var startTime = System.currentTimeMillis(); - - // Using TLS - RunTree childTree = RunTree.fromCurrentSpan(childConfig); - var parentFuture = runTree.postRunAsync(); - parentFuture.join(); -// var endTime = System.currentTimeMillis(); -// var elapsedTime = endTime - startTime; -// System.out.println("Post1 elapsed time: " + elapsedTime + " milliseconds"); -// var childFuture = childTree.postRunAsync(); -// endTime = System.currentTimeMillis(); -// elapsedTime = endTime - startTime; -// System.out.println("Post2 time: " + elapsedTime + " milliseconds"); -// var childEndFuture = childTree.endAsync(Map.of("output", "Test Output"), null, null); -// endTime = System.currentTimeMillis(); -// elapsedTime = endTime - startTime; -// var endTime = System.currentTimeMillis(); -// var elapsedTime = endTime - startTime; -// System.out.println("Post1 elapsed time: " + elapsedTime + " milliseconds"); -// var childFuture = childTree.postRunAsync(true); -// endTime = System.currentTimeMillis(); -// elapsedTime = endTime - startTime; -// System.out.println("Post2 time: " + elapsedTime + " milliseconds"); -// var childEndFuture = childTree.endAsync(Map.of("output", "Test Output"), null, null); -// endTime = System.currentTimeMillis(); -// elapsedTime = endTime - startTime; -// System.out.println("End1 elapsed time: " + elapsedTime + " milliseconds"); -// var parentEndFuture = runTree.endAsync(Map.of("output", "Test Output Parent"), null, null); -// endTime = System.currentTimeMillis(); -// elapsedTime = endTime - startTime; -// System.out.println("End parent elapsed time: " + elapsedTime + " milliseconds"); -// -// // Calculate elapsed time -// endTime = System.currentTimeMillis(); -// elapsedTime = endTime - startTime; -// System.out.println("Total lapsed time: " + elapsedTime + " milliseconds"); - } -} diff --git a/langsmith-java-trace/src/main/kotlin/com/langsmith/runtree/RunTree.kt b/langsmith-java-trace/src/main/kotlin/com/langsmith/runtree/RunTree.kt index 418249a..aa5c939 100644 --- a/langsmith-java-trace/src/main/kotlin/com/langsmith/runtree/RunTree.kt +++ b/langsmith-java-trace/src/main/kotlin/com/langsmith/runtree/RunTree.kt @@ -70,6 +70,30 @@ fun getTracerProject(): String { ?: "default" } +data class EndOptions( + val outputs: Map? = null, + val errorMessage: String? = null, + val endTime: Long? = null +) { + companion object { + @JvmStatic fun builder() = Builder() + } + + class Builder { + private var outputs: Map? = null + private var errorMessage: String? = null + private var endTime: Long? = null + + fun setOutputs(value: Map?) = apply { outputs = value } + + fun setErrorMessage(value: String?) = apply { errorMessage = value } + + fun setEndTime(value: Long?) = apply { endTime = value } + + fun build(): EndOptions = EndOptions(outputs, errorMessage, endTime) + } +} + data class RunTreeConfig( val name: String, val runType: String = "chain", @@ -89,87 +113,91 @@ data class RunTreeConfig( val referenceExampleId: String? = null, val client: LangSmithClientAsync = LangSmithOkHttpClientAsync.fromEnv(), val objectMapper: ObjectMapper = jacksonObjectMapper() -) +) { + companion object { + @JvmStatic fun builder() = Builder() + } -class RunTreeConfigBuilder { - private var name: String = "" - private var runType: String = "chain" - private var id: String = UUID.randomUUID().toString() - private var projectName: String = getTracerProject() - private var parentRun: RunTree? = null - private var parentRunId: String? = null - private var childRuns: MutableList = mutableListOf() - private var startTime: Long = System.currentTimeMillis() - private var endTime: Long? = null - private var extra: Map = emptyMap() - private var tags: List = ArrayList() - private var error: String? = null - private var serialized: Map = emptyMap() - private var inputs: Map = emptyMap() - private var outputs: Map? = emptyMap() - private var referenceExampleId: String? = null - private var client: LangSmithClientAsync? = null - private var objectMapper: ObjectMapper = jacksonObjectMapper() + class Builder { + private var name: String = "" + private var runType: String = "chain" + private var id: String = UUID.randomUUID().toString() + private var projectName: String = getTracerProject() + private var parentRun: RunTree? = null + private var parentRunId: String? = null + private var childRuns: MutableList = mutableListOf() + private var startTime: Long = System.currentTimeMillis() + private var endTime: Long? = null + private var extra: Map = emptyMap() + private var tags: List = ArrayList() + private var error: String? = null + private var serialized: Map = emptyMap() + private var inputs: Map = emptyMap() + private var outputs: Map? = emptyMap() + private var referenceExampleId: String? = null + private var client: LangSmithClientAsync? = null + private var objectMapper: ObjectMapper = jacksonObjectMapper() - fun setName(value: String) = apply { name = value } + fun setName(value: String) = apply { name = value } - fun setRunType(value: String) = apply { runType = value } + fun setRunType(value: String) = apply { runType = value } - fun setId(value: String) = apply { id = value } + fun setId(value: String) = apply { id = value } - fun setProjectName(value: String) = apply { projectName = value } + fun setProjectName(value: String) = apply { projectName = value } - fun setParentRun(value: RunTree?) = apply { parentRun = value } + fun setParentRun(value: RunTree?) = apply { parentRun = value } - fun setParentRunId(value: String?) = apply { parentRunId = value } + fun setParentRunId(value: String?) = apply { parentRunId = value } - fun addChildRun(value: RunTree) = apply { childRuns.add(value) } + fun addChildRun(value: RunTree) = apply { childRuns.add(value) } - fun setStartTime(value: Long) = apply { startTime = value } + fun setStartTime(value: Long) = apply { startTime = value } - fun setEndTime(value: Long?) = apply { endTime = value } + fun setEndTime(value: Long?) = apply { endTime = value } - fun setExtra(value: Map) = apply { extra = value } + fun setExtra(value: Map) = apply { extra = value } - fun setTags(value: List) = apply { tags = value } + fun setTags(value: List) = apply { tags = value } - fun setError(value: String?) = apply { error = value } + fun setError(value: String?) = apply { error = value } - fun setSerialized(value: Map) = apply { serialized = value } + fun setSerialized(value: Map) = apply { serialized = value } - fun setInputs(value: Map) = apply { inputs = value } + fun setInputs(value: Map) = apply { inputs = value } - fun setOutputs(value: Map?) = apply { outputs = value } + fun setOutputs(value: Map?) = apply { outputs = value } - fun setReferenceExampleId(value: String?) = apply { referenceExampleId = value } + fun setReferenceExampleId(value: String?) = apply { referenceExampleId = value } - fun setClient(value: LangSmithClientAsync?) = apply { client = value } + fun setClient(value: LangSmithClientAsync?) = apply { client = value } - fun setObjectMapper(value: ObjectMapper) = apply { objectMapper = value } + fun setObjectMapper(value: ObjectMapper) = apply { objectMapper = value } - fun addExtra(key: String, value: Any) = apply { extra = extra.plus(key to value) } + fun addExtra(key: String, value: Any) = apply { extra = extra.plus(key to value) } - fun build(): RunTreeConfig = - RunTreeConfig( - name = name, - runType = runType, - id = id, - projectName = projectName, - parentRun = parentRun, - parentRunId = parentRunId, - childRuns = childRuns, - startTime = startTime, - endTime = endTime, - extra = extra, - tags = tags, - error = error, - serialized = serialized, - inputs = inputs, - outputs = outputs, - referenceExampleId = referenceExampleId, - client = client ?: LangSmithOkHttpClientAsync.fromEnv(), - objectMapper = objectMapper - ) + fun build(): RunTreeConfig = + RunTreeConfig( + name = name, + runType = runType, + id = id, + projectName = projectName, + parentRun = parentRun, + parentRunId = parentRunId, + childRuns = childRuns, + startTime = startTime, + endTime = endTime, + extra = extra, + tags = tags, + error = error, + serialized = serialized, + inputs = inputs, + outputs = outputs, + referenceExampleId = referenceExampleId, + client = client ?: LangSmithOkHttpClientAsync.fromEnv(), + objectMapper = objectMapper + ) + } } class RunTree(config: RunTreeConfig) { @@ -284,9 +312,9 @@ class RunTree(config: RunTreeConfig) { return client.runs().update(updateParams).thenApply { _ -> Unit } } - fun end(outputs: Map? = null, error: String? = null, endTime: Long? = null) { - this.outputs = outputs - this.error = error - this.endTime = endTime ?: System.currentTimeMillis() + fun end(options: EndOptions = EndOptions()) { + this.outputs = options.outputs + this.error = options.errorMessage + this.endTime = options.endTime ?: System.currentTimeMillis() } } diff --git a/langsmith-java-trace/src/main/kotlin/com/langsmith/traceable/Traceable.kt b/langsmith-java-trace/src/main/kotlin/com/langsmith/traceable/Traceable.kt index 490eac1..283f8bf 100644 --- a/langsmith-java-trace/src/main/kotlin/com/langsmith/traceable/Traceable.kt +++ b/langsmith-java-trace/src/main/kotlin/com/langsmith/traceable/Traceable.kt @@ -1,8 +1,9 @@ package com.langsmith.traceable import com.fasterxml.jackson.databind.ObjectMapper +import com.langsmith.runtree.EndOptions import com.langsmith.runtree.RunTree -import com.langsmith.runtree.RunTreeConfigBuilder +import com.langsmith.runtree.RunTreeConfig import java.util.concurrent.CompletableFuture import java.util.function.Function @@ -71,7 +72,7 @@ class Traceable { ): R { val traceConfig = config ?: TraceConfig() val builder = - RunTreeConfigBuilder() + RunTreeConfig.builder() .setName(traceConfig.name ?: function::class.simpleName ?: "Anonymous") .setRunType(traceConfig.runType ?: "chain") .setTags(traceConfig.tags ?: emptyList()) @@ -95,11 +96,11 @@ class Traceable { try { val result = function.apply(traceableInput.input) - runTree.end(convertToMap(result), null, null) + runTree.end(EndOptions(outputs = convertToMap(result))) CompletableFuture.allOf(postFuture).join() return result } catch (e: Throwable) { - runTree.end(null, e.toString(), null) + runTree.end(EndOptions(errorMessage = e.toString())) CompletableFuture.allOf(postFuture).join() throw e }