Skip to content

Commit b8112e4

Browse files
Merge pull request #28 from UiPath/chore/add-ci-lint-tests
chore: add github actions
2 parents 5eafe12 + 512b56c commit b8112e4

13 files changed

Lines changed: 318 additions & 123 deletions

File tree

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths-ignore:
8+
- pyproject.toml
9+
pull_request:
10+
branches:
11+
- main
12+
13+
jobs:
14+
commit-lint:
15+
if: ${{ github.event_name == 'pull_request' }}
16+
uses: ./.github/workflows/commitlint.yml
17+
18+
lint:
19+
uses: ./.github/workflows/lint.yml
20+
21+
test:
22+
uses: ./.github/workflows/test.yml

.github/workflows/commitlint.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Commit Lint
2+
3+
on:
4+
workflow_call
5+
6+
jobs:
7+
commitlint:
8+
name: Commit Lint
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Setup Node
20+
uses: actions/setup-node@v3
21+
with:
22+
node-version: 22
23+
24+
- name: Install Git
25+
run: |
26+
if ! command -v git &> /dev/null; then
27+
echo "Git is not installed. Installing..."
28+
sudo apt-get update
29+
sudo apt-get install -y git
30+
else
31+
echo "Git is already installed."
32+
fi
33+
34+
- name: Install commitlint
35+
run: |
36+
npm install conventional-changelog-conventionalcommits
37+
npm install commitlint@latest
38+
npm install @commitlint/config-conventional
39+
40+
- name: Configure
41+
run: |
42+
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
43+
44+
- name: Validate PR commits with commitlint
45+
run: |
46+
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr_branch
47+
npx commitlint --from ${{ github.event.pull_request.base.sha }} --to pr_branch --verbose

.github/workflows/lint.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Lint
2+
3+
on:
4+
workflow_call
5+
6+
jobs:
7+
lint:
8+
name: Lint
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Setup uv
18+
uses: astral-sh/setup-uv@v5
19+
with:
20+
enable-cache: true
21+
22+
- name: Setup Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version-file: ".python-version"
26+
27+
- name: Install dependencies
28+
run: uv sync --all-extras
29+
30+
- name: Check static types
31+
run: uv run mypy --config-file pyproject.toml .
32+
33+
- name: Check linting
34+
run: uv run ruff check .
35+
36+
- name: Check formatting
37+
run: uv run ruff format --check .
38+

.github/workflows/publish-docs.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Publish Docs
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- "docs/**"
9+
- "pyproject.toml"
10+
11+
jobs:
12+
publish-docs:
13+
runs-on: ubuntu-latest
14+
if: ${{ github.repository == 'UiPath/uipath-llamaindex-python' }}
15+
steps:
16+
- name: Trigger Publish Docs
17+
run: |
18+
repo_owner="uipath"
19+
repo_name="uipath-python"
20+
event_type="publish-docs"
21+
22+
curl -L \
23+
-X POST \
24+
-H "Accept: application/vnd.github+json" \
25+
-H "Authorization: Bearer ${{ secrets.REPO_ACCESS }}" \
26+
-H "X-GitHub-Api-Version: 2022-11-28" \
27+
https://api.github.com/repos/$repo_owner/$repo_name/dispatches \
28+
-d "{\"event_type\": \"$event_type\", \"client_payload\": {}}"

.github/workflows/publish.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,19 @@ on:
99
- pyproject.toml
1010

1111
jobs:
12+
lint:
13+
uses: ./.github/workflows/lint.yml
14+
15+
test:
16+
uses: ./.github/workflows/test.yml
17+
1218
build:
1319
name: Build
1420
runs-on: ubuntu-latest
1521

22+
needs:
23+
- lint
24+
- test
1625

1726
if: ${{ github.repository == 'UiPath/uipath-llamaindex-python' }}
1827
permissions:

.github/workflows/test.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Test
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
test:
8+
name: Test
9+
runs-on: ${{ matrix.os }}
10+
strategy:
11+
matrix:
12+
python-version: ["3.10", "3.11", "3.12", "3.13"]
13+
os: [ubuntu-latest, windows-latest]
14+
15+
permissions:
16+
contents: read
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Setup uv
23+
uses: astral-sh/setup-uv@v5
24+
25+
- name: Setup Python
26+
uses: actions/setup-python@v5
27+
with:
28+
python-version-file: ".python-version"
29+
30+
- name: Install dependencies
31+
run: uv sync --all-extras
32+
33+
- name: Run tests
34+
run: uv run pytest
35+
36+
continue-on-error: true
37+

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ line-ending = "auto"
6666
plugins = [
6767
"pydantic.mypy"
6868
]
69+
exclude = [
70+
"samples/.*"
71+
]
6972

7073
follow_imports = "silent"
7174
warn_redundant_casts = true

src/uipath_llamaindex/_cli/_runtime/_hitl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async def read(cls, resume_trigger: UiPathResumeTrigger) -> Optional[str]:
9090
return job.output_arguments
9191

9292
case UiPathResumeTriggerType.API:
93-
if resume_trigger.api_resume.inbox_id:
93+
if resume_trigger.api_resume and resume_trigger.api_resume.inbox_id:
9494
return await _get_api_payload(resume_trigger.api_resume.inbox_id)
9595

9696
case _:

src/uipath_llamaindex/_cli/_runtime/_runtime.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
JsonPickleSerializer,
1313
)
1414
from llama_index.core.workflow.handler import WorkflowHandler
15-
from openinference.instrumentation.llama_index import LlamaIndexInstrumentor, get_current_span
15+
from openinference.instrumentation.llama_index import (
16+
LlamaIndexInstrumentor,
17+
get_current_span,
18+
)
1619
from opentelemetry import trace
1720
from opentelemetry.sdk.trace import TracerProvider
1821
from opentelemetry.sdk.trace.export import BatchSpanProcessor
@@ -23,14 +26,13 @@
2326
UiPathRuntimeResult,
2427
UiPathRuntimeStatus,
2528
)
29+
from uipath.tracing import TracingManager
2630

2731
from .._tracing._oteladapter import LlamaIndexExporter
2832
from ._context import UiPathLlamaIndexRuntimeContext
2933
from ._exception import UiPathLlamaIndexRuntimeError
3034
from ._hitl import HitlProcessor, HitlReader
3135

32-
from uipath.tracing import TracingManager
33-
3436
logger = logging.getLogger(__name__)
3537

3638

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

79+
if self.context.workflow is None:
80+
return None
81+
7782
start_event_class = self.context.workflow._start_event_class
78-
ev = start_event_class(**self.context.input_json)
83+
ev = start_event_class(**(self.context.input_json or {}))
7984
await self.load_workflow_context()
8085

86+
if self.context.workflow_context is None:
87+
return None
88+
8189
handler: WorkflowHandler = self.context.workflow.run(
8290
start_event=ev if self.context.resume else None,
8391
ctx=self.context.workflow_context,
84-
**self.context.input_json,
92+
**(self.context.input_json or {}),
8593
)
8694

8795
resume_trigger: Optional[UiPathResumeTrigger] = None
@@ -94,9 +102,10 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
94102
if self.context.resume and not response_applied:
95103
# If we are resuming, we need to apply the response to the event stream.
96104
response_applied = True
97-
self.context.workflow_context.send_event(
98-
await self.get_response_event()
99-
)
105+
response_event = await self.get_response_event()
106+
if response_event:
107+
# If we have a response event, send it to the workflow context.
108+
self.context.workflow_context.send_event(response_event)
100109
else:
101110
resume_trigger = await hitl_processor.create_resume_trigger()
102111
break
@@ -244,6 +253,9 @@ async def load_workflow_context(self):
244253
"""
245254
logger.debug(f"Resumed: {self.context.resume} Input: {self.context.input_json}")
246255

256+
if self.context.workflow is None:
257+
return
258+
247259
if not self.context.resume:
248260
self.context.workflow_context = Context(self.context.workflow)
249261
return
@@ -277,7 +289,7 @@ async def get_response_event(self) -> Optional[HumanResponseEvent]:
277289
"""
278290
if self.context.input_json:
279291
# If input_json is provided, use it to create a HumanResponseEvent
280-
return HumanResponseEvent(**self.context.input_json)
292+
return HumanResponseEvent(**(self.context.input_json or {}))
281293
# If resumed_trigger is set, fetch the feedback
282294
if self.context.resumed_trigger:
283295
feedback = await HitlReader.read(self.context.resumed_trigger)

src/uipath_llamaindex/_cli/cli_init.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import Any, Dict
66

77
from llama_index.core.workflow import StopEvent, Workflow
8-
from llama_index.core.workflow.drawing import StepConfig
8+
from llama_index.core.workflow.drawing import StepConfig # type: ignore
99
from llama_index.core.workflow.events import (
1010
HumanResponseEvent,
1111
InputRequiredEvent,
@@ -129,11 +129,13 @@ def draw_all_possible_flows_mermaid(
129129

130130
# Track event types to avoid duplicates
131131
event_types = {}
132+
current_stop_event = (
133+
None # Only one kind of `StopEvent` is allowed in a `Workflow`.
134+
)
135+
step_config: StepConfig | None = None
132136

133-
# Only one kind of `StopEvent` is allowed in a `Workflow`.
134-
current_stop_event = None
135137
for _, step_func in steps.items():
136-
step_config: StepConfig = getattr(step_func, "__step_config", None)
138+
step_config = getattr(step_func, "__step_config", None)
137139
if step_config is None:
138140
continue
139141

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

229231
if step_name == "_done" and issubclass(event_type, StopEvent):
230-
stop_event_name = current_stop_event.__name__
231-
stop_event_id = f"event_{clean_id(stop_event_name)}"
232-
edge = f"{stop_event_id} --> {step_id}"
233-
if edge not in edges:
234-
edges.add(edge)
235-
mermaid_diagram.append(f" {edge}")
232+
if current_stop_event:
233+
stop_event_name = current_stop_event.__name__
234+
stop_event_id = f"event_{clean_id(stop_event_name)}"
235+
edge = f"{stop_event_id} --> {step_id}"
236+
if edge not in edges:
237+
edges.add(edge)
238+
mermaid_diagram.append(f" {edge}")
236239
else:
237240
edge = f"{event_id} --> {step_id}"
238241
if edge not in edges:

0 commit comments

Comments
 (0)