Skip to content

Commit f763e00

Browse files
committed
feat: add dev terminal
1 parent 41e4dea commit f763e00

7 files changed

Lines changed: 1831 additions & 1735 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-llamaindex"
3-
version = "0.0.32"
3+
version = "0.0.33"
44
description = "UiPath LlamaIndex SDK"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.10"
@@ -9,7 +9,7 @@ dependencies = [
99
"llama-index-embeddings-azure-openai>=0.3.8",
1010
"llama-index-llms-azure-openai>=0.3.2",
1111
"openinference-instrumentation-llama-index>=4.3.0",
12-
"uipath>=2.1.10, <2.2.0",
12+
"uipath>=2.1.30, <2.2.0",
1313
]
1414
classifiers = [
1515
"Development Status :: 3 - Alpha",
Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import os
2-
from typing import List
3-
from dotenv import load_dotenv
42

3+
from dotenv import load_dotenv
54
from llama_index.core.agent.workflow import FunctionAgent
6-
from llama_index.llms.openai import OpenAI
75
from llama_index.indices.managed.llama_cloud import LlamaCloudIndex
6+
from llama_index.llms.openai import OpenAI
87

98
load_dotenv()
109

@@ -13,14 +12,14 @@
1312
name=os.getenv("LLAMACLOUD_INDEX_1_NAME"),
1413
project_name=os.getenv("LLAMACLOUD_PROJECT_NAME"),
1514
organization_id=os.getenv("LLAMACLOUD_ORG_ID"),
16-
api_key=os.getenv("LLAMACLOUD_API_KEY")
15+
api_key=os.getenv("LLAMACLOUD_API_KEY"),
1716
)
1817

1918
personal_preferences_index = LlamaCloudIndex(
2019
name=os.getenv("LLAMACLOUD_INDEX_2_NAME"),
2120
project_name=os.getenv("LLAMACLOUD_PROJECT_NAME"),
2221
organization_id=os.getenv("LLAMACLOUD_ORG_ID"),
23-
api_key=os.getenv("LLAMACLOUD_API_KEY")
22+
api_key=os.getenv("LLAMACLOUD_API_KEY"),
2423
)
2524

2625
llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
@@ -29,34 +28,34 @@
2928
def search_company_policy(query: str) -> str:
3029
"""
3130
Search the company policy index for travel rates, guidelines, and company policies.
32-
31+
3332
Args:
3433
query (str): The search query about company travel policies, rates, or guidelines
35-
34+
3635
Returns:
3736
str: Relevant information from company policy documents
3837
"""
3938
try:
4039
retriever = company_policy_index.as_retriever()
4140
retrieved_nodes = retriever.retrieve(query)
42-
41+
4342
if not retrieved_nodes:
4443
return "No relevant company policy information found for your query."
45-
44+
4645
# Format the search results
4746
results = []
4847
for i, node in enumerate(retrieved_nodes[:3], 1): # Limit to top 3 results
4948
results.append(f"Result {i}:")
5049
results.append(f" Content: {node.text[:300]}...")
5150
results.append(f" Relevance Score: {node.score:.4f}")
52-
if hasattr(node.node, 'metadata'):
51+
if hasattr(node.node, "metadata"):
5352
metadata = node.node.metadata
54-
if 'file_name' in metadata:
53+
if "file_name" in metadata:
5554
results.append(f" Source: {metadata['file_name']}")
56-
if 'page_label' in metadata:
55+
if "page_label" in metadata:
5756
results.append(f" Page: {metadata['page_label']}")
5857
results.append("")
59-
58+
6059
return "\n".join(results)
6160
except Exception as e:
6261
return f"Error searching company policy index: {str(e)}"
@@ -65,34 +64,34 @@ def search_company_policy(query: str) -> str:
6564
def search_personal_preferences(query: str) -> str:
6665
"""
6766
Search the personal preferences index for user's travel preferences and requirements.
68-
67+
6968
Args:
7069
query (str): The search query about personal travel preferences or requirements
71-
70+
7271
Returns:
7372
str: Relevant information from personal preference documents
7473
"""
7574
try:
7675
retriever = personal_preferences_index.as_retriever()
7776
retrieved_nodes = retriever.retrieve(query)
78-
77+
7978
if not retrieved_nodes:
8079
return "No relevant personal preference information found for your query."
81-
80+
8281
# Format the search results
8382
results = []
8483
for i, node in enumerate(retrieved_nodes[:3], 1): # Limit to top 3 results
8584
results.append(f"Result {i}:")
8685
results.append(f" Content: {node.text[:300]}...")
8786
results.append(f" Relevance Score: {node.score:.4f}")
88-
if hasattr(node.node, 'metadata'):
87+
if hasattr(node.node, "metadata"):
8988
metadata = node.node.metadata
90-
if 'file_name' in metadata:
89+
if "file_name" in metadata:
9190
results.append(f" Source: {metadata['file_name']}")
92-
if 'page_label' in metadata:
91+
if "page_label" in metadata:
9392
results.append(f" Page: {metadata['page_label']}")
9493
results.append("")
95-
94+
9695
return "\n".join(results)
9796
except Exception as e:
9897
return f"Error searching personal preferences index: {str(e)}"
@@ -101,18 +100,18 @@ def search_personal_preferences(query: str) -> str:
101100
def get_travel_recommendation(query: str) -> str:
102101
"""
103102
Get travel recommendations based on company policies and personal preferences.
104-
103+
105104
Args:
106105
query (str): The travel-related query or request for recommendations
107-
106+
108107
Returns:
109108
str: Travel recommendations combining policy and preference information
110109
"""
111110
try:
112111
# Search both indexes for comprehensive information
113112
policy_info = search_company_policy(query)
114113
preference_info = search_personal_preferences(query)
115-
114+
116115
recommendation = f"""
117116
Travel Recommendation Analysis:
118117
@@ -124,22 +123,26 @@ def get_travel_recommendation(query: str) -> str:
124123
125124
Based on the above information, here are the key considerations for your travel request.
126125
"""
127-
126+
128127
return recommendation.strip()
129128
except Exception as e:
130129
return f"Error generating travel recommendation: {str(e)}"
131130

132131

133132
# Create the FunctionAgent with our tools
134133
agent = FunctionAgent(
135-
tools=[search_company_policy, search_personal_preferences, get_travel_recommendation],
134+
tools=[
135+
search_company_policy,
136+
search_personal_preferences,
137+
get_travel_recommendation,
138+
],
136139
llm=llm,
137-
system_prompt="""You are a helpful travel assistant that can search through company travel policies and personal preferences to provide comprehensive travel guidance.
140+
system_prompt="""You are a helpful travel assistant that can search through company travel policies and personal preferences to provide comprehensive travel guidance.
138141
139142
You have access to three main functions:
140143
1. search_company_policy - Search for company travel rates, guidelines, and policies
141-
2. search_personal_preferences - Search for user's personal travel preferences and requirements
144+
2. search_personal_preferences - Search for user's personal travel preferences and requirements
142145
3. get_travel_recommendation - Get comprehensive travel recommendations combining both sources
143146
144-
Use these tools to help users with travel-related queries, ensuring you provide accurate information from both company policies and personal preferences when relevant."""
145-
)
147+
Use these tools to help users with travel-related queries, ensuring you provide accurate information from both company policies and personal preferences when relevant.""",
148+
)

src/uipath_llamaindex/_cli/_runtime/_runtime.py

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import logging
33
import os
44
import pickle
5-
from contextlib import suppress
65
from typing import Optional, cast
76

87
from llama_index.core.workflow import (
@@ -13,13 +12,6 @@
1312
WorkflowTimeoutError,
1413
)
1514
from llama_index.core.workflow.handler import WorkflowHandler # type: ignore
16-
from openinference.instrumentation.llama_index import (
17-
LlamaIndexInstrumentor,
18-
get_current_span,
19-
)
20-
from opentelemetry import trace
21-
from opentelemetry.sdk.trace import TracerProvider
22-
from opentelemetry.sdk.trace.export import BatchSpanProcessor
2315
from uipath._cli._runtime._contracts import (
2416
UiPathBaseRuntime,
2517
UiPathErrorCategory,
@@ -28,9 +20,8 @@
2820
UiPathRuntimeStatus,
2921
)
3022
from uipath._cli._runtime._hitl import HitlProcessor, HitlReader
31-
from uipath.tracing import TracingManager
3223

33-
from .._tracing._oteladapter import LlamaIndexExporter
24+
from .._utils._config import LlamaIndexConfig
3425
from ._context import UiPathLlamaIndexRuntimeContext
3526
from ._exception import UiPathLlamaIndexRuntimeError
3627

@@ -58,19 +49,6 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
5849
"""
5950
await self.validate()
6051

61-
self.trace_provider = TracerProvider()
62-
self.tracer = self.trace_provider.get_tracer("uipath.llamaindex.runtime")
63-
64-
with suppress(Exception):
65-
trace.set_tracer_provider(self.trace_provider)
66-
self.trace_provider.add_span_processor(
67-
BatchSpanProcessor(LlamaIndexExporter())
68-
)
69-
70-
LlamaIndexInstrumentor().instrument(tracer_provider=self.trace_provider)
71-
72-
TracingManager.register_current_span_provider(get_current_span)
73-
7452
try:
7553
if self.context.resume is False and self.context.job_id is None:
7654
# Delete the previous graph state file at debug time
@@ -176,8 +154,6 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
176154
detail,
177155
UiPathErrorCategory.USER,
178156
) from e
179-
finally:
180-
self.trace_provider.shutdown()
181157

182158
async def validate(self) -> None:
183159
"""Validate runtime inputs and load Llama agent configuration."""
@@ -193,12 +169,14 @@ async def validate(self) -> None:
193169
) from e
194170

195171
if self.context.config is None:
196-
raise UiPathLlamaIndexRuntimeError(
197-
"CONFIG_MISSING",
198-
"Invalid configuration",
199-
"Failed to load configuration",
200-
UiPathErrorCategory.DEPLOYMENT,
201-
)
172+
self.context.config = LlamaIndexConfig()
173+
if not self.context.config.exists:
174+
raise UiPathLlamaIndexRuntimeError(
175+
"CONFIG_MISSING",
176+
"Invalid configuration",
177+
"Failed to load configuration",
178+
UiPathErrorCategory.DEPLOYMENT,
179+
)
202180

203181
try:
204182
self.context.config.load_config()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import asyncio
2+
from typing import Optional
3+
4+
from openinference.instrumentation.llama_index import (
5+
LlamaIndexInstrumentor,
6+
get_current_span,
7+
)
8+
from uipath._cli._dev._terminal import UiPathDevTerminal
9+
from uipath._cli._runtime._contracts import UiPathRuntimeFactory
10+
from uipath._cli._utils._console import ConsoleLogger
11+
from uipath._cli.middlewares import MiddlewareResult
12+
13+
from ._runtime._context import UiPathLlamaIndexRuntimeContext
14+
from ._runtime._runtime import UiPathLlamaIndexRuntime
15+
16+
console = ConsoleLogger()
17+
18+
19+
def llamaindex_dev_middleware(interface: Optional[str]) -> MiddlewareResult:
20+
"""Middleware to launch the developer terminal"""
21+
22+
try:
23+
if interface == "terminal":
24+
runtime_factory = UiPathRuntimeFactory(
25+
UiPathLlamaIndexRuntime, UiPathLlamaIndexRuntimeContext
26+
)
27+
runtime_factory.add_instrumentor(LlamaIndexInstrumentor, get_current_span)
28+
app = UiPathDevTerminal(runtime_factory)
29+
asyncio.run(app.run_async())
30+
else:
31+
console.error(f"Unknown interface: {interface}")
32+
except KeyboardInterrupt:
33+
console.info("Debug session interrupted by user")
34+
except Exception as e:
35+
console.error(f"Error occurred: {e}")
36+
return MiddlewareResult(
37+
should_continue=False,
38+
should_include_stacktrace=True,
39+
)
40+
41+
return MiddlewareResult(should_continue=False)

src/uipath_llamaindex/_cli/cli_run.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44
from typing import Optional
55

66
from dotenv import load_dotenv
7-
from uipath._cli._runtime._contracts import UiPathTraceContext
7+
from openinference.instrumentation.llama_index import (
8+
LlamaIndexInstrumentor,
9+
get_current_span,
10+
)
11+
from uipath._cli._runtime._contracts import UiPathRuntimeFactory, UiPathTraceContext
812
from uipath._cli.middlewares import MiddlewareResult
913

1014
from ._runtime._context import UiPathLlamaIndexRuntimeContext
1115
from ._runtime._exception import UiPathLlamaIndexRuntimeError
1216
from ._runtime._runtime import UiPathLlamaIndexRuntime
17+
from ._tracing._oteladapter import LlamaIndexExporter
1318
from ._utils._config import LlamaIndexConfig
1419

1520
logger = logging.getLogger(__name__)
@@ -60,8 +65,16 @@ async def execute():
6065
env["UIPATH_REQUESTING_PRODUCT"] = "uipath-python-sdk"
6166
env["UIPATH_REQUESTING_FEATURE"] = "llamaindex"
6267

63-
async with UiPathLlamaIndexRuntime.from_context(context) as runtime:
64-
await runtime.execute()
68+
runtime_factory = UiPathRuntimeFactory(
69+
UiPathLlamaIndexRuntime, UiPathLlamaIndexRuntimeContext
70+
)
71+
72+
if context.job_id:
73+
runtime_factory.add_span_exporter(LlamaIndexExporter())
74+
75+
runtime_factory.add_instrumentor(LlamaIndexInstrumentor, get_current_span)
76+
77+
await runtime_factory.execute(context)
6578

6679
asyncio.run(execute())
6780

src/uipath_llamaindex/middlewares.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from uipath._cli.middlewares import Middlewares
22

3+
from ._cli.cli_dev import llamaindex_dev_middleware
34
from ._cli.cli_init import llamaindex_init_middleware
45
from ._cli.cli_new import llamaindex_new_middleware
56
from ._cli.cli_run import llamaindex_run_middleware
@@ -10,3 +11,4 @@ def register_middleware():
1011
Middlewares.register("init", llamaindex_init_middleware)
1112
Middlewares.register("run", llamaindex_run_middleware)
1213
Middlewares.register("new", llamaindex_new_middleware)
14+
Middlewares.register("dev", llamaindex_dev_middleware)

0 commit comments

Comments
 (0)