Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Integration testing

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
integration-tests:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

strategy:
matrix:
include:
- build-dir: quickstart-agent
- build-dir: simple-hitl-agent

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Integration testing' step
Uses Step
uses 'docker/setup-buildx-action' with ref 'v3', not a pinned commit hash

- name: Build Docker image (${{ matrix.build-dir }})
run: |
docker build -f testcases/${{ matrix.build-dir }}/Dockerfile \
-t ${{ matrix.build-dir }}:test \
--build-arg CLIENT_ID="${{ secrets.ALPHA_TEST_CLIENT_ID }}" \
--build-arg CLIENT_SECRET="${{ secrets.ALPHA_TEST_CLIENT_SECRET }}" \
--build-arg BASE_URL="${{ secrets.ALPHA_BASE_URL }}" \
.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ plugins = [
"pydantic.mypy"
]
exclude = [
"samples/.*"
"samples/.*", "testcases/.*"
]

follow_imports = "silent"
Expand Down
4 changes: 4 additions & 0 deletions testcases/quickstart-agent/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
UIPATH_URL=xxx
UIPATH_TENANT_ID=xxx
UIPATH_ORGANIZATION_ID=xxx
UIPATH_ACCESS_TOKEN=xxx
35 changes: 35 additions & 0 deletions testcases/quickstart-agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM ghcr.io/astral-sh/uv:python3.12-bookworm

WORKDIR /app

COPY . .

WORKDIR /app/testcases/quickstart-agent

RUN uv sync

ARG CLIENT_ID
ARG CLIENT_SECRET
ARG BASE_URL

RUN if [ -z "$CLIENT_ID" ]; then echo "CLIENT_ID build arg is required" && exit 1; fi
RUN if [ -z "$CLIENT_SECRET" ]; then echo "CLIENT_SECRET build arg is required" && exit 1; fi
RUN if [ -z "$BASE_URL" ]; then echo "BASE_URL build arg is required" && exit 1; fi

# Set environment variables for runtime
ENV CLIENT_ID=$CLIENT_ID
ENV CLIENT_SECRET=$CLIENT_SECRET
ENV BASE_URL=$BASE_URL
ENV TAVILY_API_KEY=${TAVILY_API_KEY:-""}
ENV UIPATH_TENANT_ID=${UIPATH_TENANT_ID:-""}
ENV UIPATH_JOB_KEY=8c6a342e-036e-492c-a3d2-99e66f6554ce

# Authenticate with UiPath during build
RUN uv run uipath auth --client-id="$CLIENT_ID" --client-secret="$CLIENT_SECRET" --base-url="$BASE_URL"

RUN uv run uipath pack

RUN AGENT_INPUT=$(cat input.json) && uv run uipath run agent "$AGENT_INPUT"

# Run the Python assert script to validate output
RUN python src/assert.py
17 changes: 17 additions & 0 deletions testcases/quickstart-agent/agent.mermaid
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
flowchart TD
step__done["_done"]:::stepStyle
step_critique_joke["critique_joke"]:::stepStyle
step_generate_joke["generate_joke"]:::stepStyle
event_JokeEvent([<p>JokeEvent</p>]):::defaultEventStyle
event_CritiqueEvent([<p>CritiqueEvent</p>]):::stopEventStyle
event_TopicEvent([<p>TopicEvent</p>]):::defaultEventStyle
event_CritiqueEvent --> step__done
step_critique_joke --> event_CritiqueEvent
event_JokeEvent --> step_critique_joke
step_generate_joke --> event_JokeEvent
event_TopicEvent --> step_generate_joke
classDef stepStyle fill:#f2f0ff,line-height:1.2
classDef externalStyle fill:#f2f0ff,line-height:1.2
classDef defaultEventStyle fill-opacity:0
classDef stopEventStyle fill:#bfb6fc
classDef inputRequiredStyle fill:#f2f0ff,line-height:1.2
1 change: 1 addition & 0 deletions testcases/quickstart-agent/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"topic": "cats"}
7 changes: 7 additions & 0 deletions testcases/quickstart-agent/llama_index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"dependencies": ["."],
"workflows": {
"agent": "./src/main.py:agent"
},
"env": ".env"
}
15 changes: 15 additions & 0 deletions testcases/quickstart-agent/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[project]
name = "llama-quickstart-agent"
version = "0.0.6"
description = "UiPath LlamaIndex Quickstart Agent"
authors = [{ name = "John Doe", email = "john.doe@myemail.com" }]
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.10"
dependencies = [
"uipath-llamaindex",
"llama-index-llms-openai>=0.2.2",
"uipath>=2.0.82",
]

[tool.uv.sources]
uipath-llamaindex = { path = "../../", editable = true }
73 changes: 73 additions & 0 deletions testcases/quickstart-agent/src/assert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import json
import os
import sys

print("Checking quickstart agent output...")

# Check NuGet package
uipath_dir = ".uipath"
if not os.path.exists(uipath_dir):
print("NuGet package directory (.uipath) not found")
sys.exit(1)

nupkg_files = [f for f in os.listdir(uipath_dir) if f.endswith(".nupkg")]
if not nupkg_files:
print("NuGet package file (.nupkg) not found in .uipath directory")
sys.exit(1)

print(f"NuGet package found: {nupkg_files[0]}")

# Check agent output file
output_file = "__uipath/output.json"
if not os.path.isfile(output_file):
print("Agent output file not found")
sys.exit(1)

print("Agent output file found")

# Check status and required fields
try:
with open(output_file, "r", encoding="utf-8") as f:
output_data = json.load(f)

# Check status
status = output_data.get("status")
if status != "successful":
print(f"Agent execution failed with status: {status}")
sys.exit(1)

print("Agent execution status: successful")

# Check required fields for quickstart agent
if "output" not in output_data:
print("Missing 'output' field in agent response")
sys.exit(1)

output_content = output_data["output"]
if "joke" not in output_content:
print("Missing 'joke' field in output")
sys.exit(1)

joke = output_content["joke"]
if not joke or joke.strip() == "":
print("Joke field is empty")
sys.exit(1)

if "critique" not in output_content:
print("Missing 'critique' field in output")
sys.exit(1)

critique = output_content["critique"]
if not critique or critique.strip() == "":
print("Critique field is empty")
sys.exit(1)

print(f"Joke: {joke}")
print(f"Critique: {critique}")

print("Required fields validation passed")
print("Quickstart agent working correctly.")

except Exception as e:
print(f"Error checking output: {e}")
sys.exit(1)
45 changes: 45 additions & 0 deletions testcases/quickstart-agent/src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from llama_index.core.workflow import (
Event,
StartEvent,
StopEvent,
Workflow,
step,
)

from uipath_llamaindex.llms import UiPathOpenAI


class TopicEvent(StartEvent):
topic: str


class JokeEvent(Event):
joke: str


class CritiqueEvent(StopEvent):
joke: str
critique: str


class JokeFlow(Workflow):
llm = UiPathOpenAI(model="gpt-4o-mini-2024-07-18")

@step
async def generate_joke(self, ev: TopicEvent) -> JokeEvent:
topic = ev.topic

prompt = f"Write your best joke about {topic}."
response = await self.llm.acomplete(prompt)
return JokeEvent(joke=str(response))

@step
async def critique_joke(self, ev: JokeEvent) -> CritiqueEvent:
joke = ev.joke

prompt = f"Give a thorough analysis and critique of the following joke: {joke}"
response = await self.llm.acomplete(prompt)
return CritiqueEvent(joke=joke, critique=str(response))


agent = JokeFlow(timeout=60, verbose=False)
42 changes: 42 additions & 0 deletions testcases/quickstart-agent/uipath.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"entryPoints": [
{
"filePath": "agent",
"uniqueId": "a1ac234f-75bc-4f3a-9bae-514f53cd86db",
"type": "agent",
"input": {
"type": "object",
"properties": {
"topic": {
"title": "Topic",
"type": "string"
}
},
"required": [
"topic"
]
},
"output": {
"type": "object",
"properties": {
"joke": {
"title": "Joke",
"type": "string"
},
"critique": {
"title": "Critique",
"type": "string"
}
},
"required": [
"joke",
"critique"
]
}
}
],
"bindings": {
"version": "2.0",
"resources": []
}
}
Loading