diff --git a/agent.ipynb b/agent.ipynb index 0b1cf33..a21417e 100644 --- a/agent.ipynb +++ b/agent.ipynb @@ -1,15 +1,5 @@ { "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "0b0e47bb", - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - }, { "cell_type": "markdown", "id": "7bb66df4", @@ -99,15 +89,15 @@ }, { "cell_type": "code", - "execution_count": null, "id": "ae7b7284", "metadata": {}, - "outputs": [], "source": [ "import shutil\n", "\n", "shutil.copy(\"resources/company_policy.txt\", \"company_policy.txt\")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -156,77 +146,77 @@ }, { "cell_type": "code", - "execution_count": null, "id": "51fa0c23", "metadata": {}, - "outputs": [], "source": [ "# check prerequisites in README.md and create the env and the jupyter kernel" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "aa8411bd", "metadata": {}, - "outputs": [], "source": [ "# Install the uipath package\n", "!uv add --active uipath-langchain" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "f3bffb31", "metadata": {}, - "outputs": [], "source": [ "# Verify the uipath installation\n", "!uv run --active uipath -lv" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "d04410a8", "metadata": {}, - "outputs": [], "source": [ "!uv sync --upgrade --active" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "60a30976", "metadata": {}, - "outputs": [], "source": [ "#Initialize the UiPath LangChain agent. replace your name here\n", "!uv run --active uipath new companyAgent-" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "b6a3d346", "metadata": {}, - "outputs": [], "source": [ "!uv add --active langchain-openai" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "961abd73", "metadata": {}, - "outputs": [], "source": [ "# Install the langchain anthropic package\n", "!uv add --active langchain-anthropic" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -240,14 +230,14 @@ }, { "cell_type": "code", - "execution_count": null, "id": "1c497d67", "metadata": {}, - "outputs": [], "source": [ "# Authenticate to UiPath Staging. Make sure to specify the name of the target Tenant \n", "!uv run --active uipath auth --staging --force --tenant \"AgentsProCode\"" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -277,10 +267,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "b1a94d9f", "metadata": {}, - "outputs": [], "source": [ "# %%writefile -a main.py\n", "\n", @@ -289,7 +277,8 @@ "from pydantic import BaseModel\n", "from uipath_langchain.retrievers import ContextGroundingRetriever\n", "from langchain_core.documents import Document\n", - "from uipath.models import InvokeProcess, IngestionInProgressException\n", + "from uipath.platform.common import InvokeProcess\n", + "from uipath.platform.errors import IngestionInProgressException\n", "import httpx\n", "from uipath_langchain.chat.models import UiPathAzureChatOpenAI\n", "\n", @@ -318,7 +307,9 @@ "If not covered:\n", "\n", "\"\"\"" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -330,10 +321,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "5543ff14", "metadata": {}, - "outputs": [], "source": [ "# %%writefile -a main.py\n", "\n", @@ -358,14 +347,14 @@ " if not data_queried:\n", " raise Exception(\"Ingestion is taking too long.\")\n", " return context_data" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "188c0841", "metadata": {}, - "outputs": [], "source": [ "# %%writefile -a main.py\n", "\n", @@ -400,7 +389,9 @@ " # result = await policy_agent.ainvoke(new_state)\n", " result = await llm.ainvoke(messages)\n", " return GraphOutput(response=result.content)" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -409,32 +400,38 @@ "source": [ "**Set the LLM model and its parameters**\n", "\n", + "> **Recommended: Use Option 2 (UiPathAzureChatOpenAI)** - No API key required, uses your existing UiPath authentication.\n", + "\n", + "> **Available Models:** For the list of available models, refer to the [list of available models (under \"agenthub\")](https://github.com/UiPath/gitops-centralized-cluster/blob/master/projects/prod/llms/llmgateway-service/llmgateway-service-values.yaml#L2301) or use `sdk.agenthub.get_available_llm_models()` to fetch available models programmatically.\n", + "\n", + "> **Note:** All `resources/mainX.py` files use `ChatOpenAI` by default. To use UiPath LLM instead (no API key needed), change `llm = ChatOpenAI(...)` to `llm = UiPathAzureChatOpenAI(...)` in the respective file before running.\n", + "\n", + "---\n", + "\n", "**Option 1.** Use generic chat models such as ChatOpenAI. The corresponding API Key needs to be provided and copied in the [`.env`](./.env) file.\n", "\n", "```env\n", - "OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" + "OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ] }, { "cell_type": "code", - "execution_count": null, "id": "a0dc0411", "metadata": {}, - "outputs": [], "source": [ "# TEMPORARY API KEY (FOR TESTING PURPOSES-WILL BE REMOVED AFTER THE SESSION)\n", "\n", "with open(\".env\", \"a\") as f:\n", " f.write(\"\\nOPENAI_API_KEY=****\\n\")\n", " f.write(\"UIPATH_FOLDER_PATH=Shared\\n\")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "f7bfdb78", "metadata": {}, - "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "\n", @@ -449,24 +446,26 @@ " timeout=30,\n", " max_retries=2,\n", ")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", "id": "71fdb175", "metadata": {}, "source": [ - "**Option 2.**, UiPath provides two chat models: `UiPathAzureChatOpenAI` and `UiPathChat`. These are compatible with `LangGraph` as drop-in replacements. \n", + "**Option 2 (Recommended).** UiPath provides two chat models: `UiPathAzureChatOpenAI` and `UiPathChat`. These are compatible with `LangGraph` as drop-in replacements. \n", + "\n", + "**No API Key required** - uses your existing UiPath authentication token.\n", "\n", - "!No need to provide an API Key" + "> **Important:** If you want to use this option, you must also update `resources/main1.py` to use `UiPathAzureChatOpenAI` instead of `ChatOpenAI`, since that file gets copied to `main.py` later in the notebook." ] }, { "cell_type": "code", - "execution_count": null, "id": "4cffe990", "metadata": {}, - "outputs": [], "source": [ "from uipath_langchain.chat.models import UiPathAzureChatOpenAI\n", "\n", @@ -478,7 +477,9 @@ " max_retries=2,\n", " # other params...\n", ")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -490,10 +491,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "96a5414c", "metadata": {}, - "outputs": [], "source": [ "# %%writefile -a main.py\n", "\n", @@ -503,7 +502,9 @@ "\n", "builder.add_edge(START, \"policy\")\n", "builder.add_edge(\"policy\", END)" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -515,40 +516,35 @@ }, { "cell_type": "code", - "execution_count": null, "id": "6370f156", "metadata": {}, - "outputs": [], "source": [ "# %%writefile -a main.py\n", "\n", "# Compile the graph\n", "graph = builder.compile()" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "8c841b19", "metadata": {}, + "source": "# Copy the above python code to the main.py file\n\nimport shutil\nshutil.copyfile(\"resources/main1.py\", \"main.py\")", "outputs": [], - "source": [ - "# Copy the above python code to the main.py file\n", - "\n", - "import shutil\n", - "shutil.copyfile(\"resources/main1.py\", \"main.py\")" - ] + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "4cfa1ed2", "metadata": {}, - "outputs": [], "source": [ "# Initialize the project to make sure all file are up to date\n", "!uv run --active uipath init" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -570,10 +566,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "1738dcaa", "metadata": {}, - "outputs": [], "source": [ "# See the Mermaid diagram\n", "from IPython.display import display, Markdown\n", @@ -582,7 +576,9 @@ " mermaid_code = f.read()\n", "\n", "display(Markdown(f\"```mermaid\\n{mermaid_code}\\n```\"))" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -598,16 +594,16 @@ }, { "cell_type": "code", - "execution_count": null, "id": "24602b54", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", " \"question\": \"What is the PTO policy for taking 3 days off\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -621,25 +617,25 @@ }, { "cell_type": "code", - "execution_count": null, "id": "98f7e1fb", "metadata": {}, - "outputs": [], "source": [ "# Re-initialize the project to make sure all file are up to date\n", "!uv run --active uipath init" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "db556fa4", "metadata": {}, - "outputs": [], "source": [ "# Run the agent locally\n", "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -682,28 +678,28 @@ }, { "cell_type": "code", - "execution_count": null, "id": "9818da8c", "metadata": {}, - "outputs": [], "source": [ "# Auto-increase the version of the package\n", "import re, pathlib\n", "p = pathlib.Path(\"pyproject.toml\"); t = p.read_text()\n", "n = re.sub(r'(?m)^(version\\s*=\\s*\")(\\d+)\\.(\\d+)\\.(\\d+)(\")', lambda m: f'{m.group(1)}{m.group(2)}.{m.group(3)}.{int(m.group(4))+1}{m.group(5)}', t, count=1)\n", "p.write_text(n); print(re.search(r'(?m)^version\\s*=\\s*\"([^\"]+)\"', t).group(1), \"->\", re.search(r'(?m)^version\\s*=\\s*\"([^\"]+)\"', n).group(1))" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "16a9ef22", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath pack\n", "!uv run --active uipath publish --tenant" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -798,25 +794,29 @@ }, { "cell_type": "code", - "execution_count": null, "id": "9a37e909", "metadata": {}, - "outputs": [], "source": [ - "\"CompanyPolicy\": {\n", + "{\n", + " \"mcpServers\": {\n", + " \"CompanyPolicy\": {\n", " \"command\": \"npx\",\n", " \"args\": [\n", " \"-y\",\n", " \"mcp-remote\",\n", - " \"https:< UiPath MCP Server URL>\",\n", + " \"\",\n", " \"--header\",\n", " \"Authorization:${AUTH_HEADER}\"\n", " ],\n", " \"env\": {\n", " \"AUTH_HEADER\": \"Bearer \"\n", " }\n", - " }" - ] + " }\n", + " }\n", + "}" + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -874,18 +874,10 @@ "---" ] }, - { - "cell_type": "markdown", - "id": "74e728e4", - "metadata": {}, - "source": [] - }, { "cell_type": "code", - "execution_count": null, "id": "1ce2859b", "metadata": {}, - "outputs": [], "source": [ "# Added GraphState with fields: question, category\n", "from pydantic import BaseModel\n", @@ -894,14 +886,14 @@ " \"\"\"State for the company policy agent graph.\"\"\"\n", " question: str\n", " category: str | None = None\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "7c387bbb", "metadata": {}, - "outputs": [], "source": [ "# Added supervisor_node to classify into Company Policy or Procurement\n", "from langchain_openai import ChatOpenAI\n", @@ -919,14 +911,14 @@ " normalized = {c.lower(): c for c in categories}\n", " category = normalized.get(label.lower(), \"Company Policy\")\n", " return GraphState(question=state.question, category=category)\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "a49ae672", "metadata": {}, - "outputs": [], "source": [ "# Added procurement_node (initially same prompt shape as policy)\n", "from uipath_langchain.retrievers import ContextGroundingRetriever\n", @@ -956,14 +948,14 @@ " class GraphOutput(BaseModel):\n", " response: str\n", " return GraphOutput(response=result.content)\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "c4a68723", "metadata": {}, - "outputs": [], "source": [ "# Added route_by_category(state) to return the next node key\n", "\n", @@ -971,14 +963,14 @@ " if state.category and state.category.lower() == \"procurement\":\n", " return \"procurement\"\n", " return \"policy\"\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "9a575dfa", "metadata": {}, - "outputs": [], "source": [ "# Rewired graph: START -> supervisor -> (policy | procurement) -> END (demo wiring)\n", "from importlib import reload\n", @@ -1011,7 +1003,9 @@ "builder_demo.add_edge(\"procurement\", END)\n", "\n", "demo_graph = builder_demo.compile()\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1031,36 +1025,27 @@ }, { "cell_type": "code", - "execution_count": null, "id": "08bfc764", "metadata": {}, + "source": "# Update the main.py\nfrom pathlib import Path\nsrc = Path(\"resources/main2_multiple_agents.py\")\ndst = Path(\"main.py\")\ndst.write_text(src.read_text(), encoding=\"utf-8\")\nprint(\"Synchronized main.py from resources/main2_multiple_agents.py\")", "outputs": [], - "source": [ - "# Update the main.py\n", - "from pathlib import Path\n", - "src = Path(\"resources/main2_multiple_agents.py\")\n", - "dst = Path(\"main.py\")\n", - "dst.write_text(src.read_text(), encoding=\"utf-8\")\n", - "print(\"Synchronized main.py from resources/main2_multiple_agents.py\")\n" - ] + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "600974ff", "metadata": {}, - "outputs": [], "source": [ "# Re-initialize the project to make sure all file are up to date\n", "!uv run --active uipath init" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "2a52d9b8", "metadata": {}, - "outputs": [], "source": [ "# See the Mermaid diagram\n", "from IPython.display import display, Markdown\n", @@ -1069,7 +1054,9 @@ " mermaid_code = f.read()\n", "\n", "display(Markdown(f\"```mermaid\\n{mermaid_code}\\n```\"))" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1081,26 +1068,26 @@ }, { "cell_type": "code", - "execution_count": null, "id": "460efe5f", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", " \"question\": \"Can I buy an OpenAI subscription for company use?\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "ccda1b76", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1122,26 +1109,26 @@ }, { "cell_type": "code", - "execution_count": null, "id": "e951ca8e", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", " \"question\": \"What is the PTO policy for taking 3 days off?\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "af599acf", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1192,37 +1179,27 @@ }, { "cell_type": "code", - "execution_count": null, "id": "efd48c77", "metadata": {}, + "source": "# Update main.py with Validation node\n\nfrom pathlib import Path\nsrc = Path(\"resources/main3_validation.py\")\ndst = Path(\"main.py\")\ndst.write_text(src.read_text(), encoding=\"utf-8\")\nprint(\"Synchronized main.py from resources/main3_validation.py\")", "outputs": [], - "source": [ - "# Update main.py with Validation node\n", - "\n", - "from pathlib import Path\n", - "src = Path(\"resources/main3_validation.py\")\n", - "dst = Path(\"main.py\")\n", - "dst.write_text(src.read_text(), encoding=\"utf-8\")\n", - "print(\"Synchronized main.py from resources/main3_validation.py\")\n" - ] + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "3cb685d3", "metadata": {}, - "outputs": [], "source": [ "# Re-initialize the project to make sure all file are up to date\n", "!uv run --active uipath init" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "bd77e103", "metadata": {}, - "outputs": [], "source": [ "# See the Mermaid diagram\n", "from IPython.display import display, Markdown\n", @@ -1231,7 +1208,9 @@ " mermaid_code = f.read()\n", "\n", "display(Markdown(f\"```mermaid\\n{mermaid_code}\\n```\"))\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1243,26 +1222,26 @@ }, { "cell_type": "code", - "execution_count": null, "id": "a60b1e07", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", " \"question\": \"Can I buy an OpenAI subscription for company use?\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "59c13fe5", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1284,10 +1263,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "49f9df01", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", @@ -1295,17 +1272,19 @@ " \"email\": \"eusebiu.jecan@uipath.com\",\n", " \"code\": \"4827\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "b99b8fc6", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1361,37 +1340,27 @@ }, { "cell_type": "code", - "execution_count": null, "id": "2c4d7dcb", "metadata": {}, + "source": "# Update main.py with HR Agent\n\nfrom pathlib import Path\nsrc = Path(\"resources/main4_hr.py\")\ndst = Path(\"main.py\")\ndst.write_text(src.read_text(), encoding=\"utf-8\")\nprint(\"Synchronized main.py from resources/main4_hr.py\")", "outputs": [], - "source": [ - "# Update main.py with Validation node\n", - "\n", - "from pathlib import Path\n", - "src = Path(\"resources/main4_hr.py\")\n", - "dst = Path(\"main.py\")\n", - "dst.write_text(src.read_text(), encoding=\"utf-8\")\n", - "print(\"Synchronized main.py from resources/main4_hr.py\")" - ] + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "51588be7", "metadata": {}, - "outputs": [], "source": [ "# Re-initialize the project to make sure all file are up to date\n", "!uv run --active uipath init" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "77bc25c8", "metadata": {}, - "outputs": [], "source": [ "# See the Mermaid diagram\n", "from IPython.display import display, Markdown\n", @@ -1400,14 +1369,14 @@ " mermaid_code = f.read()\n", "\n", "display(Markdown(f\"```mermaid\\n{mermaid_code}\\n```\"))" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "84cfbb86", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", @@ -1415,17 +1384,19 @@ " \"email\": \"eusebiu.jecan@uipath.com\",\n", " \"code\": \"4827\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "879457a4", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1470,37 +1441,27 @@ }, { "cell_type": "code", - "execution_count": null, "id": "cf539f3a", "metadata": {}, + "source": "# Update main.py with Permission Check for HR\n\nfrom pathlib import Path\nsrc = Path(\"resources/main5_permission_check_hr.py\")\ndst = Path(\"main.py\")\ndst.write_text(src.read_text(), encoding=\"utf-8\")\nprint(\"Synchronized main.py from resources/main5_permission_check_hr.py\")", "outputs": [], - "source": [ - "# Update main.py with Validation node\n", - "\n", - "from pathlib import Path\n", - "src = Path(\"resources/main5_permission_check_hr.py\")\n", - "dst = Path(\"main.py\")\n", - "dst.write_text(src.read_text(), encoding=\"utf-8\")\n", - "print(\"Synchronized main.py from resources/main5_permission_check_hr.py\")" - ] + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "765c2e5e", "metadata": {}, - "outputs": [], "source": [ "# Re-initialize the project to make sure all file are up to date\n", "!uv run --active uipath init" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "91b8ccd6", "metadata": {}, - "outputs": [], "source": [ "# See the Mermaid diagram\n", "from IPython.display import display, Markdown\n", @@ -1509,7 +1470,9 @@ " mermaid_code = f.read()\n", "\n", "display(Markdown(f\"```mermaid\\n{mermaid_code}\\n```\"))" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1521,10 +1484,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "13694a3f", "metadata": {}, - "outputs": [], "source": [ "# Create a local HR authorization file (allow-list)\n", "import json, os, textwrap\n", @@ -1541,14 +1502,14 @@ " json.dump(auth, f, indent=2)\n", "\n", "print(\"Created hr_auth.json with allow-list:\", auth[\"allowed_emails\"])" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "2163b6b2", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", @@ -1556,17 +1517,19 @@ " \"email\": \"eusebiu.jecan@uipath.com\",\n", " \"code\": \"4827\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "e91028f9", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1580,10 +1543,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "d03ca464", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", @@ -1591,17 +1552,19 @@ " \"email\": \"somenone@uipath.com\",\n", " \"code\": \"4827\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "7bcfb714", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1631,27 +1594,11 @@ }, { "cell_type": "code", - "execution_count": 57, "id": "741940fa", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Synchronized main.py from resources/main6_HITL.py\n" - ] - } - ], - "source": [ - "# Update main.py with Human in the loop\n", - "\n", - "from pathlib import Path\n", - "src = Path(\"resources/main6_HITL.py\")\n", - "dst = Path(\"main.py\")\n", - "dst.write_text(src.read_text(), encoding=\"utf-8\")\n", - "print(\"Synchronized main.py from resources/main6_HITL.py\")" - ] + "source": "# Update main.py with Human in the loop\n\nfrom pathlib import Path\nsrc = Path(\"resources/main6_HITL.py\")\ndst = Path(\"main.py\")\ndst.write_text(src.read_text(), encoding=\"utf-8\")\nprint(\"Synchronized main.py from resources/main6_HITL.py\")", + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1663,22 +1610,20 @@ }, { "cell_type": "code", - "execution_count": null, "id": "68e23bbe", "metadata": {}, - "outputs": [], "source": [ "# Re-initialize the project to make sure all file are up to date\n", "\n", "!uv run --active uipath init" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "6edb1216", "metadata": {}, - "outputs": [], "source": [ "# Visualize the current graph (Mermaid)\n", "from IPython.display import display, Markdown\n", @@ -1687,14 +1632,14 @@ " mermaid_code = f.read()\n", "\n", "display(Markdown(f\"```mermaid\\n{mermaid_code}\\n```\"))" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "66d07bdc", "metadata": {}, - "outputs": [], "source": [ "%%writefile input.json\n", "{\n", @@ -1702,14 +1647,14 @@ " \"email\": \"eusebiu.jecan@uipath.com\",\n", " \"code\": \"4827\"\n", "}" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "c15c4ecc", "metadata": {}, - "outputs": [], "source": [ "# Ensure only the correct UIPATH_URL is enabled in .env\n", "lines = []\n", @@ -1725,22 +1670,14 @@ "with open(\".env\", \"w\", encoding=\"utf-8\") as f:\n", " f.writelines(lines)\n", "print(\"Updated .env with the correct UIPATH_URL\")\n" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": 60, "id": "fbf1ac6e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Updated HITL assignee email in main.py to: eusebiu.jecan@uipath.com\n" - ] - } - ], "source": [ "# Set the HITL assignee email \n", "HITL_ASSIGNEE_EMAIL = \"\" # <--- Change this value with teh e-mail you signed up within UiPath\n", @@ -1763,27 +1700,29 @@ "\n", "main_path.write_text(\"\\n\".join(lines), encoding=\"utf-8\")\n", "print(f\"Updated HITL assignee email in main.py to: {HITL_ASSIGNEE_EMAIL}\")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "054bdf9c", "metadata": {}, - "outputs": [], "source": [ "!uv run --active uipath run agent --file input.json" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "15399994", "metadata": {}, - "outputs": [], "source": [ - "!uv run --active uipath run --resume " - ] + "!uv run --active uipath run agent --resume " + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -1807,9 +1746,9 @@ "notebook_metadata_filter": "-all" }, "kernelspec": { - "display_name": ".venv", + "display_name": "FUSION2025", "language": "python", - "name": "python3" + "name": "jupyter-uv-env" }, "language_info": { "codemirror_mode": { @@ -1821,7 +1760,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.4" + "version": "3.11.13" } }, "nbformat": 4, diff --git a/hello-world.ipynb b/hello-world.ipynb index 62984f8..d73e681 100644 --- a/hello-world.ipynb +++ b/hello-world.ipynb @@ -261,7 +261,7 @@ "metadata": {}, "outputs": [], "source": [ - "!uv run uipath run --file input.json" + "!uv run uipath run agent --file input.json" ] }, { diff --git a/resources/main1.py b/resources/main1.py index cbd0408..479a617 100644 --- a/resources/main1.py +++ b/resources/main1.py @@ -4,7 +4,8 @@ from pydantic import BaseModel from uipath_langchain.retrievers import ContextGroundingRetriever from langchain_core.documents import Document -from uipath.models import InvokeProcess, IngestionInProgressException +from uipath.platform.common import InvokeProcess +from uipath.platform.errors import IngestionInProgressException import httpx from uipath_langchain.chat.models import UiPathAzureChatOpenAI from langchain_openai import ChatOpenAI diff --git a/resources/main2_multiple_agents.py b/resources/main2_multiple_agents.py index 1d9949a..b038e3c 100644 --- a/resources/main2_multiple_agents.py +++ b/resources/main2_multiple_agents.py @@ -3,7 +3,8 @@ from pydantic import BaseModel from uipath_langchain.retrievers import ContextGroundingRetriever from langchain_core.documents import Document -from uipath.models import InvokeProcess, IngestionInProgressException +from uipath.platform.common import InvokeProcess +from uipath.platform.errors import IngestionInProgressException import httpx from uipath_langchain.chat.models import UiPathAzureChatOpenAI from langchain_openai import ChatOpenAI diff --git a/resources/main3_validation.py b/resources/main3_validation.py index 5dec6ec..befc1e3 100644 --- a/resources/main3_validation.py +++ b/resources/main3_validation.py @@ -3,7 +3,8 @@ from pydantic import BaseModel from uipath_langchain.retrievers import ContextGroundingRetriever from langchain_core.documents import Document -from uipath.models import InvokeProcess, IngestionInProgressException +from uipath.platform.common import InvokeProcess +from uipath.platform.errors import IngestionInProgressException import httpx from uipath_langchain.chat.models import UiPathAzureChatOpenAI from langchain_openai import ChatOpenAI diff --git a/resources/main4_hr.py b/resources/main4_hr.py index b6aa767..ba9d090 100644 --- a/resources/main4_hr.py +++ b/resources/main4_hr.py @@ -3,7 +3,8 @@ from pydantic import BaseModel from uipath_langchain.retrievers import ContextGroundingRetriever from langchain_core.documents import Document -from uipath.models import InvokeProcess, IngestionInProgressException +from uipath.platform.common import InvokeProcess +from uipath.platform.errors import IngestionInProgressException import httpx from uipath_langchain.chat.models import UiPathAzureChatOpenAI from langchain_openai import ChatOpenAI diff --git a/resources/main5_permission_check_hr.py b/resources/main5_permission_check_hr.py index ec1d861..0dbbc0a 100644 --- a/resources/main5_permission_check_hr.py +++ b/resources/main5_permission_check_hr.py @@ -3,7 +3,8 @@ from pydantic import BaseModel from uipath_langchain.retrievers import ContextGroundingRetriever from langchain_core.documents import Document -from uipath.models import InvokeProcess, IngestionInProgressException +from uipath.platform.common import InvokeProcess +from uipath.platform.errors import IngestionInProgressException import httpx from uipath_langchain.chat.models import UiPathAzureChatOpenAI from langchain_openai import ChatOpenAI diff --git a/resources/main6_HITL.py b/resources/main6_HITL.py index 959d103..1d32a47 100644 --- a/resources/main6_HITL.py +++ b/resources/main6_HITL.py @@ -3,13 +3,14 @@ from pydantic import BaseModel, Field from uipath_langchain.retrievers import ContextGroundingRetriever from langchain_core.documents import Document -from uipath.models import InvokeProcess, IngestionInProgressException +from uipath.platform.common import InvokeProcess +from uipath.platform.errors import IngestionInProgressException import httpx from uipath_langchain.chat.models import UiPathAzureChatOpenAI from langchain_openai import ChatOpenAI import logging import time -from uipath.models import CreateAction +from uipath.platform.common import CreateTask from langgraph.types import interrupt from typing import Literal @@ -180,13 +181,12 @@ async def supervisor_node(state: GraphState) -> GraphState: human_feedback = None if result["confidence"] < 99: - action_data = interrupt(CreateAction(app_name="SimpleApprovalApp", + action_data = interrupt(CreateTask(app_name="SimpleApprovalApp", title="Action Required: Review classification", data={ "Content": f"I classified the question '{state.question}' \n as {result['category']} \n Is this ok? My confidence score is only {result['confidence']}"}, app_folder_path="Shared/ApprovalApp", - assignee="eusebiu.jecan@uipath.com", - app_version=4 + assignee="eusebiu.jecan@uipath.com" )) if not bool(action_data["Approved"]): human_feedback = action_data["Comment"] diff --git a/src/company-agent/.env b/src/company-agent/.env index 4e0ada4..e789f9e 100644 --- a/src/company-agent/.env +++ b/src/company-agent/.env @@ -3,4 +3,3 @@ UIPATH_URL=https://alpha.uipath.com/Ada/GiuliaTenant UIPATH_TENANT_ID=3c05bc70-6a91-4ccd-996a-95b90d11f829 UIPATH_ORGANIZATION_ID=b7006b1c-11c3-4a80-802e-fee0ebf9c360 UIPATH_FOLDER_NAME=Shared -OPENAI_API_KEY=**** diff --git a/src/company-agent/main.py b/src/company-agent/main.py deleted file mode 100644 index 542d958..0000000 --- a/src/company-agent/main.py +++ /dev/null @@ -1,456 +0,0 @@ -# from langchain_anthropic import ChatAnthropic -from langgraph.graph import END, START, StateGraph -from pydantic import BaseModel, Field -from uipath_langchain.retrievers import ContextGroundingRetriever -from langchain_core.documents import Document -from uipath.models import InvokeProcess, IngestionInProgressException -import httpx -from uipath_langchain.chat.models import UiPathAzureChatOpenAI -from langchain_openai import ChatOpenAI -import logging -import time -from uipath.models import CreateAction -from langgraph.types import interrupt -from typing import Literal - -import os -import json - -from dotenv import load_dotenv - -load_dotenv() - - -class IndexNotFound(Exception): - pass - - -logger = logging.getLogger(__name__) - -# Define system prompt for company policy agent -system_prompt = """ -You are an advanced AI assistant specializing in answering questions about internal company policies. -Structure your responses as follows: - -Answer: -<1–3 sentence conclusion> - -Policy evidence: -- "" - Reference:
- -Conditions/Exceptions: -- - -Next steps: -- -- - -If not covered: - -""" - -# llm = ChatAnthropic(model="claude-3-5-sonnet-latest") -llm = UiPathAzureChatOpenAI() -# llm = ChatOpenAI( -# model="gpt-5", -# temperature=0, -# max_tokens=4000, -# timeout=30, -# max_retries=2, -# ) - - -class LlmOutput(BaseModel): - """A model representing the categorization of a user question.""" - - category: Literal["Company Policy", "Procurement", "HR"] = Field( - ..., - description="The category of the user's question. Must be one of 'Company Policy', 'Procurement', or 'HR'." - ) - confidence: float = Field( - ..., - description="Confidence score for the categorization, ranging from 0 (no confidence) to 100 (high confidence).", - ge=0, - le=100, - ) - - -async def get_context_data_async(retriever: ContextGroundingRetriever, question: str) -> list[Document]: - no_of_retries = 5 - context_data = None - data_queried = False - while no_of_retries != 0: - try: - context_data = await retriever.ainvoke(question) - data_queried = True - break - except IngestionInProgressException as ex: - logger.info(ex.message) - no_of_retries -= 1 - logger.info(f"{no_of_retries} retries left") - time.sleep(5) - except httpx.HTTPStatusError as err: - if err.response.status_code == 404: - raise IndexNotFound - raise - if not data_queried: - raise Exception("Ingestion is taking too long.") - return context_data - - -class GraphState(BaseModel): - """State for the company policy agent graph.""" - question: str - category: str | None = None - human_feedback: str | None = None - # Human-in-the-loop fields (used by procurement flow) - email: str | None = None - code: str | None = None - - -class GraphOutput(BaseModel): - """Output for the company policy agent graph.""" - response: str - - -def is_valid_company_email(email: str | None) -> bool: - """Validate that the email is in the uipath.com domain.""" - if not email or "@" not in email: - return False - return email.strip().lower().endswith("@uipath.com") - - -def is_valid_code(code: str | None) -> bool: - """Validate a 4-digit code that is not all the same digit and not consecutive (e.g., 1234).""" - if not code or len(code) != 4 or not code.isdigit(): - return False - # Reject all identical digits (e.g., 1111) - if len(set(code)) == 1: - return False - # Reject strictly ascending consecutive sequences (e.g., 0123, 1234, ..., 6789) - digits = [int(c) for c in code] - if all(digits[i + 1] - digits[i] == 1 for i in range(3)): - return False - return True - - -async def policy_node(state: GraphState) -> GraphOutput: - retriever = ContextGroundingRetriever( - index_name="company-policy-index", - folder_path="Shared", - number_of_results=100, - ) - try: - context_docs = await get_context_data_async(retriever, state.question) - except IndexNotFound: - context_docs = [] - - messages = [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": state.question}, - ] - if state.category: - messages.append({"role": "user", "content": f"Category: {state.category}"}) - if context_docs: - messages.append({"role": "user", "content": context_docs[0].page_content}) - - """Process a company policy question and return a structured answer.""" - result = await llm.ainvoke(messages) - return GraphOutput(response=result.content) - - -async def supervisor_node(state: GraphState) -> GraphState: - import os - - for name, value in os.environ.items(): - print("{0}: {1}".format(name, value)) - """Supervisor: classify the question and store the category for routing.""" - messages = [ - {"role": "system", "content": ( - "Classify the user's question into one of: Company Policy, Procurement, HR. " - "Reply with only the exact label." - )}, - {"role": "user", "content": state.question}, - ] - if state.human_feedback: - messages.append({"role": "user", "content": f"You predicted {state.category} which is not correct. Please consider the following feedback: '{state.human_feedback}'"}) - print(messages) - result = await llm.with_structured_output(LlmOutput).ainvoke(messages) - human_feedback = None - - # Temporarily disable HITL for testing - set to very low threshold - # Original: if result["confidence"] < 99: - if result["confidence"] < 50: - action_data = interrupt(CreateAction(app_name="App", - title="Action Required: Review classification", - data={ - "Content": f"I classified the question '{state.question}' \n as {result['category']} \n Is this ok? My confidence score is only {result['confidence']}"}, - app_folder_path="Shared/ApprovalApp/SimpleApprovalAppSolution", - assignee="cosmin.paunel@uipath.com", - app_version=4 - )) - print(f"Action response: {action_data}") - # Check for approval - action_data might have different key names depending on the app - # Common keys: "Approved", "approved", "IsApproved", "approval", "status" - if action_data: - approved = action_data.get("Approved") or action_data.get("approved") or action_data.get("IsApproved") - if approved is not None and not bool(approved): - human_feedback = action_data.get("Comment", action_data.get("comment", "")) - return GraphState(question=state.question, category=result["category"], human_feedback=human_feedback) - - -def route_by_category(state: GraphState) -> str: - """Return the route key to dispatch to the specialized agent.""" - if state.category and state.category.lower() == "procurement": - return "procurement" - if state.category and state.category.lower() == "hr": - return "hr" - return "policy" - - -async def procurement_node(state: GraphState) -> GraphOutput: - """Procurement specialized agent (assumes prior verification).""" - email = state.email - code = state.code - - retriever = ContextGroundingRetriever( - index_name="procurement-index", - folder_path="Shared", - number_of_results=100, - ) - try: - context_docs = await get_context_data_async(retriever, state.question) - except IndexNotFound: - context_docs = [] - - messages = [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": state.question}, - ] - messages.append({"role": "user", "content": "Agent: Procurement"}) - if state.category: - messages.append({"role": "user", "content": f"Category: {state.category}"}) - # Attach verified email/code context (not used as secrets; only provenance) - if email: - messages.append({"role": "user", "content": f"Verified email: {email}"}) - if code: - messages.append({"role": "user", "content": "Verification code provided (stored transiently)"}) - if context_docs: - messages.append({"role": "user", "content": context_docs[0].page_content}) - - result = await llm.ainvoke(messages) - return GraphOutput(response=result.content) - - -async def hr_node(state: GraphState) -> GraphOutput: - """HR specialized agent (no context grounding). - - Chooses an HR system/tool (e.g., Workday) based on the question using: - - A local JSON catalog file if present (./hr_tools.json or ./resources/hr_tools.json) - - Otherwise a built-in default mapping - The model is prompted to return a compact JSON plan with the chosen tool and action. - """ - - # Load optional local catalog - default_catalog = { - "workday": { - "areas": [ - "pto", "leave", "vacation", "time off", "absence", - "benefits", "payroll", "compensation", "recruiting", "onboarding" - ] - }, - "servicenow_hrsd": { - "areas": ["hr ticket", "policy request", "case", "issue", "access request"] - }, - "bamboohr": { - "areas": ["employee directory", "time off", "basic hr records"] - }, - "greenhouse": { - "areas": ["recruiting", "interview", "schedule", "offer"] - } - } - - catalog = default_catalog - for path in [ - os.path.join(os.getcwd(), "hr_tools.json"), - os.path.join(os.getcwd(), "resources", "hr_tools.json"), - ]: - if os.path.exists(path): - try: - with open(path, "r", encoding="utf-8") as f: - catalog = json.load(f) - break - except Exception: - catalog = default_catalog - - planning_prompt = ( - "You are an HR routing module. Choose the best target system/tool from the provided catalog " - "for the user's request and propose a single concrete action. Respond with ONLY valid JSON " - "in the following schema: {\n" - " \"tool\": ,\n" - " \"action\": ,\n" - " \"parameters\": {},\n" - " \"reason\": \n" - "}. If out of scope, set tool to \"none\" and explain in reason." - ) - - messages = [ - {"role": "system", "content": planning_prompt}, - {"role": "user", "content": f"Catalog: {json.dumps(catalog)}"}, - {"role": "user", "content": f"Question: {state.question}"}, - ] - - result = await llm.ainvoke(messages) - content = result.content or "{}" - - plan = None - try: - plan = json.loads(content) - except Exception: - # Try to recover from responses wrapped in code fences - if "{" in content and "}" in content: - try: - start = content.find("{") - end = content.rfind("}") + 1 - plan = json.loads(content[start:end]) - except Exception: - plan = None - - if not isinstance(plan, dict): - return GraphOutput(response="Tool selected for the ask is: none") - - tool = plan.get("tool", "none") - return GraphOutput(response=f"Tool selected for the ask is: {tool}") - - -async def permission_check_local_DB(state: GraphState): - """HR permission gate using a local JSON DB of allowed emails. - - File lookup order: ./hr_auth.json, ./resources/hr_auth.json - Expected format: {"allowed_emails": ["user@company.com", ...]} - - - If email is missing or not in the allow-list: return GraphOutput("Not authorised") - - If allowed: pass state through unchanged - """ - email = (state.email or "").strip().lower() - allowlist: list[str] = [] - - for path in [ - os.path.join(os.getcwd(), "hr_auth.json"), - os.path.join(os.getcwd(), "resources", "hr_auth.json"), - ]: - if os.path.exists(path): - try: - with open(path, "r", encoding="utf-8") as f: - data = json.load(f) - allowlist = [e.strip().lower() for e in data.get("allowed_emails", []) if isinstance(e, str)] - break - except Exception: - allowlist = [] - - if not email or email not in allowlist: - return GraphOutput(response="Not authorised") - - return state - - -async def verify_credentials_node(state: GraphState): - """Verification gate: validate optional email/code for procurement path. - - - If missing: fail fast with concise message - - If invalid: fail fast with concise message - - If valid: pass state through unchanged - """ - email = state.email - code = state.code - if email is None or code is None: - return GraphOutput(response="Failed: E-mail and CODE are required for procurement.") - if not is_valid_company_email(email) or not is_valid_code(code): - return GraphOutput(response="Failed: E-mail and CODE are invalid.") - return state - - -# Conditional routing after verification to respect LangGraph pattern -def route_after_verification(state) -> str: - """Route based on verification result. - - - If verification failed, prior node returned GraphOutput → end - - If verification passed, prior node returned GraphState → procurement - """ - if isinstance(state, GraphOutput): - return "end" - if isinstance(state, dict) and "response" in state: - return "end" - if isinstance(state, GraphState): - if not is_valid_company_email(state.email) or not is_valid_code(state.code): - return "end" - return "procurement" - -def decide_next_node(state: GraphState) -> Literal["supervisor", "next_node"]: - if state.human_feedback: - return "supervisor" - return "next_node" - - -def route_by_category_node(state: GraphState) -> dict: - category_lower = state.category.lower() - # Map variations to canonical names - category_map = { - "company policy": "policy", - "policy": "policy", - "procurement": "procurement", - "hr": "hr", - "human resources": "hr", - } - - if category_lower not in category_map: - raise ValueError( - f"Invalid category '{state.category}'. Expected one of: 'policy', 'procurement', 'hr'." - ) - return {"route": category_map[category_lower]} - - -# Build the state graph with conditional routing -builder = StateGraph(GraphState, output=GraphOutput) -builder.add_node("supervisor", supervisor_node) -builder.add_node("policy", policy_node) -builder.add_node("verify_credentials", verify_credentials_node) -builder.add_node("procurement", procurement_node) -builder.add_node("hr", hr_node) -builder.add_node("permission_check_local_DB", permission_check_local_DB) -builder.add_node("route_by_category", route_by_category_node) - -builder.add_edge(START, "supervisor") -builder.add_conditional_edges( - "supervisor", - decide_next_node, - { - "supervisor": "supervisor", - "next_node": "route_by_category", - }, -) -builder.add_conditional_edges( - "route_by_category", - route_by_category, - { - "policy": "policy", - "procurement": "verify_credentials", - "hr": "permission_check_local_DB", - }, -) -builder.add_edge("policy", END) -builder.add_conditional_edges( - "verify_credentials", - route_after_verification, - { - "procurement": "procurement", - "end": END, - }, -) -builder.add_edge("procurement", END) -builder.add_edge("permission_check_local_DB", "hr") -builder.add_edge("hr", END) - -# Compile the graph -graph = builder.compile() diff --git a/src/company-agent/pyproject.toml b/src/company-agent/pyproject.toml index b3b22e1..ebcdc8b 100644 --- a/src/company-agent/pyproject.toml +++ b/src/company-agent/pyproject.toml @@ -4,17 +4,17 @@ version = "0.1.0" description = "Company policy agent with supervisor routing and node-level evaluation support" authors = [{ name = "UiPath", email = "support@uipath.com" }] dependencies = [ - "uipath", - "uipath-langchain", - "langchain-core>=0.3.33", - "langchain-openai>=0.3.33", - "langchain-anthropic>=0.3.8", - "langgraph>=0.2.62", - "python-dotenv>=1.0.0", - "pydantic>=2.0.0", - "httpx>=0.27.0", + "uipath~=2.5.30", + "uipath-langchain~=0.4.27", + "langchain-core~=0.3.33", + "langchain-openai~=0.3.33", + "langchain-anthropic~=0.3.8", + "langgraph~=0.2.62", + "python-dotenv~=1.0.0", + "pydantic~=2.0.0", + "httpx~=0.27.0", ] -requires-python = ">=3.10" +requires-python = ">=3.11" [tool.uv.sources] uipath = { path = "../../../uipath-python", editable = true }