diff --git a/applications/quarkus-langchain4j-demo/.mvn/wrapper/maven-wrapper.properties b/applications/quarkus-langchain4j-demo/.mvn/wrapper/maven-wrapper.properties index d58dfb7..5291372 100644 --- a/applications/quarkus-langchain4j-demo/.mvn/wrapper/maven-wrapper.properties +++ b/applications/quarkus-langchain4j-demo/.mvn/wrapper/maven-wrapper.properties @@ -1,19 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -wrapperVersion=3.3.2 +wrapperVersion=3.3.4 distributionType=only-script -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.15/apache-maven-3.9.15-bin.zip diff --git a/applications/quarkus-langchain4j-demo/README.md b/applications/quarkus-langchain4j-demo/README.md index ea80fa8..1276971 100644 --- a/applications/quarkus-langchain4j-demo/README.md +++ b/applications/quarkus-langchain4j-demo/README.md @@ -3,21 +3,27 @@ ## Prerequisites - Java 21+ -- Langfuse stack ([Cloud](https://cloud.langfuse.com/) or [Self-Hosted](https://langfuse.com/docs/deployment/self-host)) -- Langfuse API Keys - An OpenAI Api Key ## How to run -1. Configure environment variables to connect Spring AI demo app with Langfuse. +1. Configure environment variables. + ``` - export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=sk-xxx-... - export QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel" # πŸ‡ͺπŸ‡Ί EU data region - # export QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT="https://us.cloud.langfuse.com/api/public/otel" # πŸ‡ΊπŸ‡Έ US data region - # export QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:3000/api/public/otel" # 🏠 Local deployment (>= v3.22.0) - export QUARKUS_OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $(echo -n "pk-lf-xxx:sk-lf-xxx" | base64)" + export OPENAI_API_KEY=sk-xxx-... ``` 2. Run the sample application via `./mvnw quarkus:dev`. -3. Observe the new trace in the Langfuse web UI. +3. You should see something in the logs like this, indicating the URL/credentials of Langfuse: + + ``` + Dev Services for Langfuse started. + Other applications in dev mode will find it automatically. + For Quarkus applications in production mode, you can connect to this instance by starting you application with -Dquarkus.langfuse.base-url=http://localhost:42971. + Log in with: + Email: quarkus@quarkus.io + Password: quarkuslangfuse + ``` + +4. Observe the new trace in the Langfuse web UI (indicated at the URL in the above message). ![sample-trace](./screenshots/quarkus-langchain4j-demo-trace.png) diff --git a/applications/quarkus-langchain4j-demo/mvnw b/applications/quarkus-langchain4j-demo/mvnw index 19529dd..bd8896b 100755 --- a/applications/quarkus-langchain4j-demo/mvnw +++ b/applications/quarkus-langchain4j-demo/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.3.2 +# Apache Maven Wrapper startup batch script, version 3.3.4 # # Optional ENV vars # ----------------- @@ -105,14 +105,17 @@ trim() { printf "%s" "${1}" | tr -d '[:space:]' } +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties while IFS="=" read -r key value; do case "${key-}" in distributionUrl) distributionUrl=$(trim "${value-}") ;; distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; esac -done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" -[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" case "${distributionUrl##*/}" in maven-mvnd-*bin.*) @@ -130,7 +133,7 @@ maven-mvnd-*bin.*) distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" ;; maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; -*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; esac # apply MVNW_REPOURL and calculate MAVEN_HOME @@ -227,7 +230,7 @@ if [ -n "${distributionSha256Sum-}" ]; then echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 exit 1 elif command -v sha256sum >/dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then distributionSha256Result=true fi elif command -v shasum >/dev/null; then @@ -252,8 +255,41 @@ if command -v unzip >/dev/null; then else tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" fi -printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" -mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" clean || : exec_maven "$@" diff --git a/applications/quarkus-langchain4j-demo/mvnw.cmd b/applications/quarkus-langchain4j-demo/mvnw.cmd index 249bdf3..92450f9 100644 --- a/applications/quarkus-langchain4j-demo/mvnw.cmd +++ b/applications/quarkus-langchain4j-demo/mvnw.cmd @@ -19,7 +19,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM Apache Maven Wrapper startup batch script, version 3.3.4 @REM @REM Optional ENV vars @REM MVNW_REPOURL - repo url base for downloading maven distribution @@ -40,7 +40,7 @@ @SET __MVNW_ARG0_NAME__= @SET MVNW_USERNAME= @SET MVNW_PASSWORD= -@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) @echo Cannot start maven from wrapper >&2 && exit /b 1 @GOTO :EOF : end batch / begin powershell #> @@ -73,16 +73,30 @@ switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { # apply MVNW_REPOURL and calculate MAVEN_HOME # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ if ($env:MVNW_REPOURL) { - $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } - $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" } $distributionUrlName = $distributionUrl -replace '^.*/','' $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' -$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" + +$MAVEN_M2_PATH = "$HOME/.m2" if ($env:MAVEN_USER_HOME) { - $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" } -$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { @@ -134,7 +148,33 @@ if ($distributionSha256Sum) { # unzip and move Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null -Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null try { Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null } catch { diff --git a/applications/quarkus-langchain4j-demo/pom.xml b/applications/quarkus-langchain4j-demo/pom.xml index e21d8a7..d962e7d 100644 --- a/applications/quarkus-langchain4j-demo/pom.xml +++ b/applications/quarkus-langchain4j-demo/pom.xml @@ -2,12 +2,6 @@ 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.4.3 - - com.langfuse quarkus-langchain4j-demo 0.0.1-SNAPSHOT @@ -16,10 +10,16 @@ - 21 - quarkus-bom - io.quarkus.platform - 3.19.2 + 21 + 3.15.0 + 21 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.36.1 + true + 3.5.6 @@ -31,6 +31,13 @@ pom import + + io.quarkiverse.langchain4j + quarkus-langchain4j-bom + 1.10.0 + pom + import + @@ -38,41 +45,102 @@ io.quarkiverse.langchain4j quarkus-langchain4j-openai - 0.26.0 io.quarkus quarkus-opentelemetry + + io.opentelemetry + opentelemetry-exporter-otlp + + + io.quarkiverse.langfuse + quarkus-langfuse + 0.1.1 + io.quarkus quarkus-rest-jackson io.quarkus - quarkus-junit5 + quarkus-junit test - - - ${quarkus.platform.group-id} - quarkus-maven-plugin - ${quarkus.platform.version} - true - - - - build - generate-code - generate-code-tests - native-image-agent - - - - - + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + native-image-agent + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + + maven-surefire-plugin + ${surefire-plugin.version} + + @{argLine} + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + @{argLine} + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + false + false + true + + + diff --git a/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/MyAiService.java b/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/PoemService.java similarity index 93% rename from applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/MyAiService.java rename to applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/PoemService.java index 3dd7602..5019fe9 100644 --- a/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/MyAiService.java +++ b/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/PoemService.java @@ -1,11 +1,12 @@ package com.langfuse.quarkus; +import io.quarkiverse.langchain4j.RegisterAiService; + import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; -import io.quarkiverse.langchain4j.RegisterAiService; -@RegisterAiService() -public interface MyAiService { +@RegisterAiService +public interface PoemService { /** * Ask the LLM to create a poem about the given topic. diff --git a/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/Startup.java b/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/Startup.java index 73923de..6d364fc 100644 --- a/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/Startup.java +++ b/applications/quarkus-langchain4j-demo/src/main/java/com/langfuse/quarkus/Startup.java @@ -1,12 +1,20 @@ package com.langfuse.quarkus; -import io.quarkus.runtime.StartupEvent; -import jakarta.enterprise.event.Observes; -import jakarta.inject.Singleton; - -@Singleton -public class Startup { - public void writeAPoem(@Observes StartupEvent event, MyAiService service) { - System.out.println(service.writeAPoem("LangFuse", 4)); - } +import jakarta.enterprise.context.control.ActivateRequestContext; +import jakarta.inject.Inject; + +import io.quarkus.runtime.QuarkusApplication; +import io.quarkus.runtime.annotations.QuarkusMain; + +@QuarkusMain +public class Startup implements QuarkusApplication { + @Inject + PoemService service; + + @Override + @ActivateRequestContext + public int run(String... args) throws Exception { + System.out.println(service.writeAPoem("LangFuse", 4)); + return 0; + } } \ No newline at end of file diff --git a/applications/quarkus-langchain4j-demo/src/main/resources/application.properties b/applications/quarkus-langchain4j-demo/src/main/resources/application.properties index b6d631b..fe8e268 100644 --- a/applications/quarkus-langchain4j-demo/src/main/resources/application.properties +++ b/applications/quarkus-langchain4j-demo/src/main/resources/application.properties @@ -1,3 +1,3 @@ -quarkus.otel.exporter.otlp.traces.protocol=http/protobuf -quarkus.langchain4j.tracing.include-completion=true -quarkus.langchain4j.tracing.include-prompt=true \ No newline at end of file +quarkus.langchain4j.log-requests=true +quarkus.langfuse.log-responses=true +quarkus.langchain4j.openai.api-key=${OPENAI_API_KEY} \ No newline at end of file