diff --git a/.github/workflows/integration-quarkus-langchain4j.yml b/.github/workflows/integration-quarkus-langchain4j.yml new file mode 100644 index 00000000..8ee6e900 --- /dev/null +++ b/.github/workflows/integration-quarkus-langchain4j.yml @@ -0,0 +1,260 @@ +name: Integration Quarkus-LangChain4j + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + types: [opened, synchronize, reopened] + schedule: + # Run every Saturday at 02:30 UTC to catch dependency breakages + - cron: '30 2 * * 6' + workflow_dispatch: + inputs: + quarkus_langchain4j_version: + description: 'Quarkus LangChain4j version to test against' + required: false + type: string + quarkus_version: + description: 'Quarkus platform version to test against' + required: false + type: string + +env: + JAVA_HOME: /opt/jenkins/jdks/graal-23.1.0/jdk-21.0.3 + TORNADO_ROOT: ${{ github.workspace }}/GPULlama3.java/external/tornadovm + GRAAL_JARS: /opt/graalJars + QUARKUS_PORT: 8081 + +jobs: + quarkus-integration-test: + if: github.repository == 'beehive-lab/GPULlama3.java' + runs-on: [self-hosted] + timeout-minutes: 30 + strategy: + fail-fast: true + matrix: + backend: + - name: opencl + - name: ptx + + steps: + - name: Checkout GPULlama3 + uses: actions/checkout@v4 + + # Step 1: Clone and build TornadoVM + - name: Clone TornadoVM master + run: | + git clone --depth 1 --branch master \ + https://github.com/beehive-lab/TornadoVM.git \ + $TORNADO_ROOT + - name: Set up Python venv for TornadoVM + run: | + python3 -m venv $TORNADO_ROOT/venv + source $TORNADO_ROOT/venv/bin/activate + python --version + - name: Build TornadoVM + run: | + cd $TORNADO_ROOT + mkdir -p graalJars && cp $GRAAL_JARS/* graalJars/ + source venv/bin/activate + echo "=== Building TornadoVM ===" + + make BACKEND=${{ matrix.backend.name }} + + echo "=== Searching for TornadoVM SDK directory ===" + SDK_DIR=$(find dist -type d -maxdepth 3 -path "*/tornadovm-*-${{ matrix.backend.name }}" | head -n 1) + if [ -z "$SDK_DIR" ]; then + echo "::error::Could not locate TornadoVM SDK directory!" + find dist -maxdepth 5 -type d + exit 1 + fi + FULL_SDK="${PWD}/${SDK_DIR}" + echo "Detected TornadoVM SDK: $FULL_SDK" + + # Export for current shell session + export TORNADOVM_HOME="$FULL_SDK" + export PATH="$FULL_SDK/bin:$JAVA_HOME/bin:$PATH" + + # Save for subsequent steps + echo "TORNADOVM_HOME=$FULL_SDK" >> $GITHUB_ENV + echo "PATH=$PATH" >> $GITHUB_ENV + + echo "=== Checking tornado CLI ===" + which tornado || { echo "::error::tornado not in PATH"; exit 1; } + tornado --devices + + # Step 2: Build GPULlama3.java + - name: Build GPULlama3.java + run: | + cd ${{ github.workspace }} + echo "Using TORNADOVM_HOME=$TORNADOVM_HOME" + export PATH="$TORNADOVM_HOME/bin:$JAVA_HOME/bin:$PATH" + tornado --version + + # Append SNAPSHOT to GPULlama3 version + GPULLAMA3_VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout) + GPULLAMA3_VERSION="${GPULLAMA3_VERSION}-SNAPSHOT" + echo "GPULlama3 version: $GPULLAMA3_VERSION" + ./mvnw versions:set -DnewVersion=$GPULLAMA3_VERSION + + # Build + ./mvnw clean install -DskipTests + + # Save GPULlama3.java version for subsequent steps + echo "GPULLAMA3_VERSION=$GPULLAMA3_VERSION" >> $GITHUB_ENV + + # Step 3: Clone Quarkus LangChain4j + - name: Clone Quarkus LangChain4j + run: | + cd ${{ github.workspace }} + git clone https://github.com/quarkiverse/quarkus-langchain4j.git + + # Step 4: Build Quarkus LangChain4j with current GPULlama3.java + - name: Build Quarkus LangChain4j + run: | + cd ${{ github.workspace }}/quarkus-langchain4j + export PATH="$TORNADOVM_HOME/bin:$JAVA_HOME/bin:$PATH" + + # Update the GPULlama3 version used by quarkus-langchain4j + POM="pom.xml" + sed -i 's/.*<\/gpu-llama3\.version>/'$GPULLAMA3_VERSION'<\/gpu-llama3.version>/' "$POM" + + # Use reactor to build *only *GPULlama3 integration test + dependencies + # This recompiles everything with the same Java version, avoiding compatibility issues + # The -Dtornado flag activates the TornadoVM profile which includes gpu-llama3 module + mvn clean install -pl integration-tests/gpu-llama3 -am -DskipTests -Dtornado + + # Step 4.5: Verify GPULlama3 JAR + - name: Verify GPULlama3 Dependency + run: | + cd ${{ github.workspace }}/quarkus-langchain4j/integration-tests/gpu-llama3 + echo "GPULlama3 dependency used by Quarkus-LangChain4j:" + mvn dependency:tree | grep "io.github.beehive-lab:gpu-llama3" + + # Step 5: Start Quarkus Application and Wait for Startup + - name: Start Quarkus Application and Wait for Startup + run: | + cd ${{ github.workspace }}/quarkus-langchain4j/integration-tests/gpu-llama3 + export PATH="$TORNADOVM_HOME/bin:$JAVA_HOME/bin:$PATH" + + echo "Starting Quarkus application on port $QUARKUS_PORT..." + + # Start the Quarkus application in the background + java @"$TORNADOVM_HOME/tornado-argfile" \ + -Dtornado.device.memory=8GB \ + -Dquarkus.http.port=$QUARKUS_PORT \ + -jar target/quarkus-app/quarkus-run.jar & + APP_PID=$! + + if [ -z "$APP_PID" ]; then + echo "ERROR: Failed to start Quarkus application" + exit 1 + fi + + echo "Waiting for Quarkus application to start..." + + # Wait for application to be ready + for i in {1..30}; do + if curl -s http://localhost:$QUARKUS_PORT/q/health > /dev/null 2>&1; then + echo "Application ready after ${i} seconds" + echo "Health endpoint: http://localhost:$QUARKUS_PORT/q/health" + break + elif [ $i -eq 30 ]; then + echo "ERROR: Application failed to start within 30 seconds" + echo "Debugging info:" + echo "- Port: $QUARKUS_PORT" + echo "- Process ID: $APP_PID" + echo "- Health URL: http://localhost:$QUARKUS_PORT/q/health" + kill $APP_PID || true + exit 1 + else + [ $((i % 5)) -eq 0 ] && echo "Still waiting... (${i}s)" + sleep 1 + fi + done + + # Step 6: Run test 1 + - name: Trigger Blocking Endpoint + run: | + MAX_ATTEMPTS=3 + ATTEMPT=1 + SUCCESS=false + + while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do + echo "Attempt $ATTEMPT of $MAX_ATTEMPTS for blocking endpoint..." + + # Trigger endpoint + HTTP_RESPONSE=$(curl -s -w "%{http_code}" http://localhost:$QUARKUS_PORT/chat/blocking) + HTTP_RESPONSE_CODE="${HTTP_RESPONSE: -3}" + HTTP_RESPONSE_BODY="${HTTP_RESPONSE%???}" + + # Check response code + if [ "$HTTP_RESPONSE_CODE" = "200" ]; then + echo "SUCCESS: Blocking endpoint returned HTTP code: $HTTP_RESPONSE_CODE" + echo "HTTP Response body: $HTTP_RESPONSE_BODY" + SUCCESS=true + break + else + echo "Attempt $ATTEMPT failed: Blocking endpoint returned HTTP code $HTTP_RESPONSE_CODE" + echo "Response body: $HTTP_RESPONSE_BODY" + + if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then + echo "Retrying in 2 seconds..." + sleep 2 + fi + fi + + ATTEMPT=$((ATTEMPT + 1)) + done + + if [ "$SUCCESS" = false ]; then + echo "ERROR: Blocking endpoint failed after $MAX_ATTEMPTS attempts" + exit 1 + fi + + # Step 7: Run test 2 + - name: Trigger Streaming Endpoint + run: | + MAX_ATTEMPTS=3 + ATTEMPT=1 + SUCCESS=false + + while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do + echo "Attempt $ATTEMPT of $MAX_ATTEMPTS for streaming endpoint..." + + # Trigger endpoint + HTTP_RESPONSE=$(timeout 10s curl -s -w "%{http_code}" http://localhost:$QUARKUS_PORT/chat/streaming) + HTTP_RESPONSE_CODE="${HTTP_RESPONSE: -3}" + #HTTP_RESPONSE_BODY="$HTTP_RESPONSE%???}" + + # Check response code + if [ "$HTTP_RESPONSE_CODE" == "200" ]; then + echo "SUCCESS: Streaming endpoint returned HTTP code: ${HTTP_RESPONSE: -3}" + # do not show ugly streaming response body + #echo "HTTP Response body: $HTTP_RESPONSE_BODY" + SUCCESS=true + break + else + echo "Attempt $ATTEMPT failed: Streaming endpoint returned HTTP code $HTTP_RESPONSE_CODE" + + if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then + echo "Retrying in 2 seconds..." + sleep 2 + fi + fi + + ATTEMPT=$((ATTEMPT + 1)) + done + + if [ "$SUCCESS" = false ]; then + echo "ERROR: Streaming endpoint failed after $MAX_ATTEMPTS attempts" + exit 1 + fi + + # Step 8: Cleanup & Shutdown + - name: Cleanup & Shutdown + run: | + # Clean shutdown + kill $APP_PID || true + wait $APP_PID 2>/dev/null || true