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
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CI

on:
push:
branches:
- main
paths-ignore:
- pyproject.toml
pull_request:
branches:
- main

jobs:
commit-lint:
if: ${{ github.event_name == 'pull_request' }}
uses: ./.github/workflows/commitlint.yml

lint:
uses: ./.github/workflows/lint.yml

test:
uses: ./.github/workflows/test.yml
47 changes: 47 additions & 0 deletions .github/workflows/commitlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Commit Lint

on:
workflow_call

jobs:
commitlint:
name: Commit Lint
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 22

- name: Install Git
run: |
if ! command -v git &> /dev/null; then
echo "Git is not installed. Installing..."
sudo apt-get update
sudo apt-get install -y git
else
echo "Git is already installed."
fi

- name: Install commitlint
run: |
npm install conventional-changelog-conventionalcommits
npm install commitlint@latest
npm install @commitlint/config-conventional

- name: Configure
run: |
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

- name: Validate PR commits with commitlint
run: |
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr_branch
npx commitlint --from ${{ github.event.pull_request.base.sha }} --to pr_branch --verbose
38 changes: 38 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Lint

on:
workflow_call

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
permissions:
contents: read

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

- name: Setup uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"

- name: Install dependencies
run: uv sync --all-extras

- name: Check static types
run: uv run mypy --config-file pyproject.toml .

- name: Check linting
run: uv run ruff check .

- name: Check formatting
run: uv run ruff format --check .

28 changes: 28 additions & 0 deletions .github/workflows/publish-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Publish Docs

on:
push:
branches:
- main
paths:
- "docs/**"
- "pyproject.toml"

jobs:
publish-docs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'UiPath/uipath-llamaindex-python' }}
steps:
- name: Trigger Publish Docs
run: |
repo_owner="uipath"
repo_name="uipath-python"
event_type="publish-docs"

curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.REPO_ACCESS }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/$repo_owner/$repo_name/dispatches \
-d "{\"event_type\": \"$event_type\", \"client_payload\": {}}"
9 changes: 9 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,19 @@ on:
- pyproject.toml

jobs:
lint:
uses: ./.github/workflows/lint.yml

test:
uses: ./.github/workflows/test.yml

build:
name: Build
runs-on: ubuntu-latest

needs:
- lint
- test

if: ${{ github.repository == 'UiPath/uipath-llamaindex-python' }}
permissions:
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test

on:
workflow_call:

jobs:
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
os: [ubuntu-latest, windows-latest]

permissions:
contents: read

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

- name: Setup uv
uses: astral-sh/setup-uv@v5

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"

- name: Install dependencies
run: uv sync --all-extras

- name: Run tests
run: uv run pytest

continue-on-error: true

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ line-ending = "auto"
plugins = [
"pydantic.mypy"
]
exclude = [
"samples/.*"
]

follow_imports = "silent"
warn_redundant_casts = true
Expand Down
2 changes: 1 addition & 1 deletion src/uipath_llamaindex/_cli/_runtime/_hitl.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ async def read(cls, resume_trigger: UiPathResumeTrigger) -> Optional[str]:
return job.output_arguments

case UiPathResumeTriggerType.API:
if resume_trigger.api_resume.inbox_id:
if resume_trigger.api_resume and resume_trigger.api_resume.inbox_id:
return await _get_api_payload(resume_trigger.api_resume.inbox_id)

case _:
Expand Down
30 changes: 21 additions & 9 deletions src/uipath_llamaindex/_cli/_runtime/_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
JsonPickleSerializer,
)
from llama_index.core.workflow.handler import WorkflowHandler
from openinference.instrumentation.llama_index import LlamaIndexInstrumentor, get_current_span
from openinference.instrumentation.llama_index import (
LlamaIndexInstrumentor,
get_current_span,
)
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
Expand All @@ -23,14 +26,13 @@
UiPathRuntimeResult,
UiPathRuntimeStatus,
)
from uipath.tracing import TracingManager

from .._tracing._oteladapter import LlamaIndexExporter
from ._context import UiPathLlamaIndexRuntimeContext
from ._exception import UiPathLlamaIndexRuntimeError
from ._hitl import HitlProcessor, HitlReader

from uipath.tracing import TracingManager

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -74,14 +76,20 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
if os.path.exists(self.state_file_path):
os.remove(self.state_file_path)

if self.context.workflow is None:
return None

start_event_class = self.context.workflow._start_event_class
ev = start_event_class(**self.context.input_json)
ev = start_event_class(**(self.context.input_json or {}))
await self.load_workflow_context()

if self.context.workflow_context is None:
return None

handler: WorkflowHandler = self.context.workflow.run(
start_event=ev if self.context.resume else None,
ctx=self.context.workflow_context,
**self.context.input_json,
**(self.context.input_json or {}),
)

resume_trigger: Optional[UiPathResumeTrigger] = None
Expand All @@ -94,9 +102,10 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
if self.context.resume and not response_applied:
# If we are resuming, we need to apply the response to the event stream.
response_applied = True
self.context.workflow_context.send_event(
await self.get_response_event()
)
response_event = await self.get_response_event()
if response_event:
# If we have a response event, send it to the workflow context.
self.context.workflow_context.send_event(response_event)
else:
resume_trigger = await hitl_processor.create_resume_trigger()
break
Expand Down Expand Up @@ -244,6 +253,9 @@ async def load_workflow_context(self):
"""
logger.debug(f"Resumed: {self.context.resume} Input: {self.context.input_json}")

if self.context.workflow is None:
return

if not self.context.resume:
self.context.workflow_context = Context(self.context.workflow)
return
Expand Down Expand Up @@ -277,7 +289,7 @@ async def get_response_event(self) -> Optional[HumanResponseEvent]:
"""
if self.context.input_json:
# If input_json is provided, use it to create a HumanResponseEvent
return HumanResponseEvent(**self.context.input_json)
return HumanResponseEvent(**(self.context.input_json or {}))
# If resumed_trigger is set, fetch the feedback
if self.context.resumed_trigger:
feedback = await HitlReader.read(self.context.resumed_trigger)
Expand Down
23 changes: 13 additions & 10 deletions src/uipath_llamaindex/_cli/cli_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Any, Dict

from llama_index.core.workflow import StopEvent, Workflow
from llama_index.core.workflow.drawing import StepConfig
from llama_index.core.workflow.drawing import StepConfig # type: ignore
from llama_index.core.workflow.events import (
HumanResponseEvent,
InputRequiredEvent,
Expand Down Expand Up @@ -129,11 +129,13 @@ def draw_all_possible_flows_mermaid(

# Track event types to avoid duplicates
event_types = {}
current_stop_event = (
None # Only one kind of `StopEvent` is allowed in a `Workflow`.
)
step_config: StepConfig | None = None

# Only one kind of `StopEvent` is allowed in a `Workflow`.
current_stop_event = None
for _, step_func in steps.items():
step_config: StepConfig = getattr(step_func, "__step_config", None)
step_config = getattr(step_func, "__step_config", None)
if step_config is None:
continue

Expand Down Expand Up @@ -227,12 +229,13 @@ def draw_all_possible_flows_mermaid(
event_id = f"event_{clean_id(event_name)}"

if step_name == "_done" and issubclass(event_type, StopEvent):
stop_event_name = current_stop_event.__name__
stop_event_id = f"event_{clean_id(stop_event_name)}"
edge = f"{stop_event_id} --> {step_id}"
if edge not in edges:
edges.add(edge)
mermaid_diagram.append(f" {edge}")
if current_stop_event:
Comment thread
cristipufu marked this conversation as resolved.
stop_event_name = current_stop_event.__name__
stop_event_id = f"event_{clean_id(stop_event_name)}"
edge = f"{stop_event_id} --> {step_id}"
if edge not in edges:
edges.add(edge)
mermaid_diagram.append(f" {edge}")
else:
edge = f"{event_id} --> {step_id}"
if edge not in edges:
Expand Down
2 changes: 1 addition & 1 deletion src/uipath_llamaindex/_cli/cli_run.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio
import logging
from os import environ as env
from typing import Any, Dict, Optional
from typing import Optional

from dotenv import load_dotenv
from uipath._cli._runtime._contracts import UiPathTraceContext
Expand Down
Loading