uipath-langchain 0.0.101__tar.gz → 0.0.102__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of uipath-langchain might be problematic. Click here for more details.
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/PKG-INFO +1 -1
- uipath_langchain-0.0.101/docs/context_grounding_retriever.md → uipath_langchain-0.0.102/docs/context_grounding.md +43 -7
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/pyproject.toml +1 -1
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_runtime/_runtime.py +0 -3
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/tracers/AsyncUiPathTracer.py +14 -56
- uipath_langchain-0.0.102/tests/tracers/test_async_uipath_tracer.py +366 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/uv.lock +1423 -1423
- uipath_langchain-0.0.101/docs/context_grounding_vector_store.md +0 -35
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.cursorrules +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.editorconfig +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.gitattributes +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.github/workflows/build.yml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.github/workflows/cd.yml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.github/workflows/ci.yml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.github/workflows/commitlint.yml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.github/workflows/lint.yml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.github/workflows/publish-dev.yml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.github/workflows/test.yml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.gitignore +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.pre-commit-config.yaml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.python-version +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.vscode/extensions.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/.vscode/settings.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/CONTRIBUTING.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/LICENSE +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/chat_models.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/human_in_the_loop.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/quick_start.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/quick_start_images/cloud_env_var.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/multi-agent-distributed/coder-agent-package-overview.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/multi-agent-distributed/coder-agent-process-configuration.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/multi-agent-distributed/planner-agent-package-overview.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/multi-agent-distributed/planner-agent-process-configuration.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/multi-agent-distributed/researcher-agent-package-overview.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/multi-agent-distributed/researcher-agent-process-configuration.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/activate-apps.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/activate-deployment.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/copy-folder-path.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/deploy-solution-package-wizard.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/deploy-solution-package.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/monitor-agent.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/navigate-to-solution-folder.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/resume-condition.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/run-agent.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/solution-destination-folder.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/start-job.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/docs/sample_images/ticket-classification/upload-solution-package.png +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/company-research-agent/.env.example +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/company-research-agent/agent.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/company-research-agent/graph.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/company-research-agent/langgraph.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/company-research-agent/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/company-research-agent/uipath.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/company-research-agent/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/hitl-inbox-server/database.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/hitl-inbox-server/main.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/hitl-inbox-server/models.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/hitl-inbox-server/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/hitl-inbox-server/schemas.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/hitl-inbox-server/templates/index.html +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/hitl-inbox-server/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/.env.example +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/coder.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/langgraph.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/planner.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/researcher.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/src/multi-agent-distributed/coder.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/src/multi-agent-distributed/planner.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/src/multi-agent-distributed/researcher.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/uipath.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-planner-researcher-coder-distributed/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/.env.example +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/agent.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/graph.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/langgraph.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/uipath.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/multi-agent-supervisor-researcher-coder/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/retrieval-chain/.env.example +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/retrieval-chain/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/retrieval-chain/main.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/retrieval-chain/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/retrieval-chain/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/.env.example +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/agent.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/langgraph.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/src/simple-local-mcp/graph.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/src/simple-local-mcp/math_server.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/src/simple-local-mcp/weather_server.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/uipath.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-local-mcp/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/.env.example +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/agent.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/langgraph.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/main.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/uipath.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/simple-remote-mcp/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/.env.example +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/README.md +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/agent.mermaid +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/escalation_app_solution/generic-escalation-app-solution-1.0.0.zip +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/langgraph.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/main.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/pyproject.toml +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/uipath.json +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/samples/ticket-classification/uv.lock +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_runtime/_context.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_runtime/_escalation.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_runtime/_exception.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_runtime/_input.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_runtime/_output.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_templates/langgraph.json.template +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_templates/main.py.template +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_utils/_graph.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/cli_init.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/cli_new.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/cli_run.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_utils/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_utils/_request_mixin.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_utils/_settings.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_utils/_sleep_policy.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/chat/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/chat/models.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/chat/utils/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/chat/utils/_chat_types.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/embeddings/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/embeddings/embeddings.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/middlewares.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/retrievers/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/retrievers/context_grounding_retriever.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/tracers/UiPathTracer.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/tracers/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/tracers/_events.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/tracers/_instrument_traceable.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/tracers/_utils.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/utils/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/utils/_request_mixin.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/utils/_settings.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/utils/_sleep_policy.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/vectorstores/context_grounding_vectorstore.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/tests/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/tests/test_dummy.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/tests/test_langchain_client.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/tests/tracers/__init__.py +0 -0
- {uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/tests/tracers/test_instrument_traceable.py +0 -0
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
The `ContextGroundingRetriever` is a document retrieval system that uses vector search to efficiently find and retrieve relevant information from your document store.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
1
|
+
# Context Grounding
|
|
6
2
|
|
|
7
|
-
|
|
3
|
+
Context Grounding Service allows you to:
|
|
8
4
|
- Search through indexed documents using natural language queries
|
|
9
5
|
- Ground LLM responses in your organization's specific information
|
|
10
6
|
- Retrieve context-relevant documents for various applications
|
|
@@ -12,6 +8,11 @@ The `ContextGroundingRetriever` is a document retrieval system that uses vector
|
|
|
12
8
|
|
|
13
9
|
You will need to create an index in `Context Grounding` to use this feature. To create an index go to organization `Admin` -> `AI Trust Layer` -> `Context Grounding`. There you can create a new index and add documents to it. See the full documentation [here](https://docs.uipath.com/automation-cloud/automation-cloud/latest/admin-guide/about-context-grounding) for more details.
|
|
14
10
|
|
|
11
|
+
|
|
12
|
+
# ContextGroundingRetriever
|
|
13
|
+
|
|
14
|
+
The `ContextGroundingRetriever` is a document retrieval system that uses vector search to efficiently find and retrieve relevant information from your document store.
|
|
15
|
+
|
|
15
16
|
## Basic Usage
|
|
16
17
|
|
|
17
18
|
Create a simple retriever by specifying an index name:
|
|
@@ -51,4 +52,39 @@ agent = create_react_agent(model, tools, prompt="Answer user questions as best a
|
|
|
51
52
|
|
|
52
53
|
## Advanced Usage
|
|
53
54
|
|
|
54
|
-
For complex applications, the retriever can be combined with other LangChain components to create robust document QA systems, agents, or knowledge bases.
|
|
55
|
+
For complex applications, the retriever can be combined with other LangChain components to create robust document QA systems, agents, or knowledge bases.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ContextGroundingVectorStore
|
|
60
|
+
|
|
61
|
+
`ContextGroundingVectorStore` is a vector store implementation designed for context-aware document retrieval. It allows you to perform semantic searches and create retrieval chains with language models.
|
|
62
|
+
|
|
63
|
+
## Searching Documents
|
|
64
|
+
|
|
65
|
+
The vector store supports various search methods:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from uipath_langchain.vectorstores.context_grounding_vectorstore import ContextGroundingVectorStore
|
|
69
|
+
|
|
70
|
+
vectorstore = ContextGroundingVectorStore(index_name="Company policy")
|
|
71
|
+
|
|
72
|
+
# Perform semantic searches with distance scores
|
|
73
|
+
docs_with_scores = vectorstore.asimilarity_search_with_score(query="What is the company policy on data storage?", k=5)
|
|
74
|
+
|
|
75
|
+
# Perform a similarity search with relevance scores
|
|
76
|
+
docs_with_relevance_scores = await vectorstore.asimilarity_search_with_relevance_scores(query=query, k=5)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Creating a Retrieval Chain
|
|
80
|
+
|
|
81
|
+
You can integrate the vector store into a retrieval chain with a language model:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# Run a retrieval chain
|
|
85
|
+
model = UiPathAzureChatOpenAI(model="gpt-4o-2024-08-06", max_retries=3)
|
|
86
|
+
retrieval_chain = create_retrieval_chain(vectorstore=vectorstore, model=model)
|
|
87
|
+
|
|
88
|
+
query = "What is the ECCN for a laptop?"
|
|
89
|
+
result = retrieval_chain(query)
|
|
90
|
+
```
|
{uipath_langchain-0.0.101 → uipath_langchain-0.0.102}/src/uipath_langchain/_cli/_runtime/_runtime.py
RENAMED
|
@@ -80,9 +80,6 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
80
80
|
|
|
81
81
|
if self.context.job_id and self.context.tracing_enabled:
|
|
82
82
|
tracer = AsyncUiPathTracer(context=self.context.trace_context)
|
|
83
|
-
await tracer.init_trace(
|
|
84
|
-
self.context.entrypoint, self.context.job_id
|
|
85
|
-
)
|
|
86
83
|
callbacks = [tracer]
|
|
87
84
|
|
|
88
85
|
graph_config: RunnableConfig = {
|
|
@@ -2,7 +2,6 @@ import asyncio
|
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
4
|
import queue
|
|
5
|
-
import re
|
|
6
5
|
import uuid
|
|
7
6
|
import warnings
|
|
8
7
|
from os import environ as env
|
|
@@ -43,9 +42,7 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
43
42
|
|
|
44
43
|
self.context = context or UiPathTraceContext()
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
self.url = llm_ops_pattern.format(orgId=self.context.org_id).rstrip("/")
|
|
45
|
+
self.base_url = self._get_base_url()
|
|
49
46
|
|
|
50
47
|
auth_token = env.get("UNATTENDED_USER_ACCESS_TOKEN") or env.get(
|
|
51
48
|
"UIPATH_ACCESS_TOKEN"
|
|
@@ -103,44 +100,6 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
103
100
|
)
|
|
104
101
|
self._send_span(previous_run)
|
|
105
102
|
|
|
106
|
-
async def init_trace(self, run_name, trace_id=None) -> None:
|
|
107
|
-
if self.context.trace_id:
|
|
108
|
-
# trace id already set no need to do anything
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
# no trace id, start a new trace
|
|
112
|
-
await self.start_trace(run_name, trace_id)
|
|
113
|
-
|
|
114
|
-
async def start_trace(self, run_name, trace_id=None) -> None:
|
|
115
|
-
self.context.trace_id = str(uuid.uuid4())
|
|
116
|
-
|
|
117
|
-
run_name = run_name or f"Job Run: {self.context.trace_id}"
|
|
118
|
-
trace_data = {
|
|
119
|
-
"id": self.context.trace_id,
|
|
120
|
-
"name": re.sub(
|
|
121
|
-
r"[!@#$<>\.]", "", run_name
|
|
122
|
-
), # if we use these characters the Agents UI throws some error (but llmops backend seems fine)
|
|
123
|
-
"referenceId": self.context.reference_id,
|
|
124
|
-
"attributes": "{}",
|
|
125
|
-
"organizationId": self.context.org_id,
|
|
126
|
-
"tenantId": self.context.tenant_id,
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
for attempt in range(self.retries):
|
|
130
|
-
response = await self.client.post(
|
|
131
|
-
f"{self.url}/api/Agent/trace/", headers=self.headers, json=trace_data
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
if response.is_success:
|
|
135
|
-
break
|
|
136
|
-
|
|
137
|
-
await asyncio.sleep(0.5 * (2**attempt)) # Exponential backoff
|
|
138
|
-
|
|
139
|
-
if 400 <= response.status_code < 600:
|
|
140
|
-
logger.warning(
|
|
141
|
-
f"Error when sending trace: {response}. Body is: {response.text}"
|
|
142
|
-
)
|
|
143
|
-
|
|
144
103
|
async def wait_for_all_tracers(self) -> None:
|
|
145
104
|
"""
|
|
146
105
|
Wait for all pending log requests to complete
|
|
@@ -159,11 +118,13 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
159
118
|
|
|
160
119
|
span_data = self.log_queue.get_nowait()
|
|
161
120
|
|
|
121
|
+
url = self._build_url(self.context.trace_id)
|
|
122
|
+
|
|
162
123
|
for attempt in range(self.retries):
|
|
163
124
|
response = await self.client.post(
|
|
164
|
-
|
|
125
|
+
url,
|
|
165
126
|
headers=self.headers,
|
|
166
|
-
json=span_data,
|
|
127
|
+
json=[span_data], # api expects a list of spans
|
|
167
128
|
timeout=10,
|
|
168
129
|
)
|
|
169
130
|
|
|
@@ -189,11 +150,12 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
189
150
|
break
|
|
190
151
|
|
|
191
152
|
span_data = self.log_queue.get_nowait()
|
|
153
|
+
url = self._build_url(self.context.trace_id)
|
|
192
154
|
|
|
193
155
|
response = await self.client.post(
|
|
194
|
-
|
|
156
|
+
url,
|
|
195
157
|
headers=self.headers,
|
|
196
|
-
json=span_data,
|
|
158
|
+
json=[span_data], # api expects a list of spans
|
|
197
159
|
timeout=10,
|
|
198
160
|
)
|
|
199
161
|
except Exception as e:
|
|
@@ -239,6 +201,7 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
239
201
|
"jobKey": self.context.job_id,
|
|
240
202
|
"folderKey": self.context.folder_key,
|
|
241
203
|
"processKey": self.context.folder_key,
|
|
204
|
+
"expiryTimeUtc": None,
|
|
242
205
|
}
|
|
243
206
|
|
|
244
207
|
self.log_queue.put(span_data)
|
|
@@ -293,16 +256,11 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
293
256
|
uipath_url = (
|
|
294
257
|
env.get("UIPATH_URL") or "https://cloud.uipath.com/dummyOrg/dummyTennant/"
|
|
295
258
|
)
|
|
296
|
-
uipath_url = uipath_url.rstrip("/")
|
|
297
259
|
|
|
298
|
-
|
|
299
|
-
parts = uipath_url.split("//")
|
|
300
|
-
|
|
301
|
-
# after splitting by //, the base URL will be at index 1 along with the rest,
|
|
302
|
-
# hence split it again using "/" to get ['https:', 'alpha.uipath.com', 'ada', 'byoa']
|
|
303
|
-
base_url_parts = parts[1].split("/")
|
|
260
|
+
uipath_url = uipath_url.rstrip("/")
|
|
304
261
|
|
|
305
|
-
|
|
306
|
-
base_url = parts[0] + "//" + base_url_parts[0] + "/"
|
|
262
|
+
return uipath_url
|
|
307
263
|
|
|
308
|
-
|
|
264
|
+
def _build_url(self, trace_id: Optional[str]) -> str:
|
|
265
|
+
"""Construct the URL for the API request."""
|
|
266
|
+
return f"{self.base_url}/llmopstenant_/api/Traces/spans?traceId={trace_id}&source=Robots"
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from langchain_core.tracers.schemas import Run
|
|
7
|
+
from uipath._cli._runtime._contracts import UiPathTraceContext
|
|
8
|
+
|
|
9
|
+
from uipath_langchain.tracers._events import CustomTraceEvents, FunctionCallEventData
|
|
10
|
+
from uipath_langchain.tracers.AsyncUiPathTracer import AsyncUiPathTracer, Status
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestAsyncUiPathTracer:
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def mock_response(self):
|
|
16
|
+
response = AsyncMock()
|
|
17
|
+
response.is_success = True
|
|
18
|
+
response.status_code = 200
|
|
19
|
+
response.text = "Success"
|
|
20
|
+
return response
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def mock_client(self, mock_response):
|
|
24
|
+
client = AsyncMock()
|
|
25
|
+
client.post.return_value = mock_response
|
|
26
|
+
return client
|
|
27
|
+
|
|
28
|
+
@pytest.fixture
|
|
29
|
+
def mock_context(self):
|
|
30
|
+
context = UiPathTraceContext()
|
|
31
|
+
context.trace_id = "test-trace-id"
|
|
32
|
+
context.parent_span_id = "test-parent-span-id"
|
|
33
|
+
context.org_id = "test-org-id"
|
|
34
|
+
context.tenant_id = "test-tenant-id"
|
|
35
|
+
context.job_id = "test-job-id"
|
|
36
|
+
context.folder_key = "test-folder-key"
|
|
37
|
+
context.reference_id = "test-reference-id"
|
|
38
|
+
return context
|
|
39
|
+
|
|
40
|
+
@pytest.fixture
|
|
41
|
+
def tracer(self, mock_client, mock_context):
|
|
42
|
+
# Don't create any real asyncio tasks in fixtures
|
|
43
|
+
with patch("asyncio.create_task"):
|
|
44
|
+
with patch.dict("os.environ", {"UIPATH_ACCESS_TOKEN": "test-token"}):
|
|
45
|
+
# Instantiate the tracer but don't start the worker
|
|
46
|
+
tracer = AsyncUiPathTracer(context=mock_context, client=mock_client)
|
|
47
|
+
|
|
48
|
+
# Set worker_task to a mock that can be awaited
|
|
49
|
+
mock_task = AsyncMock()
|
|
50
|
+
tracer.worker_task = mock_task
|
|
51
|
+
|
|
52
|
+
# Set running to False to avoid worker logic
|
|
53
|
+
tracer.running = False
|
|
54
|
+
|
|
55
|
+
yield tracer
|
|
56
|
+
|
|
57
|
+
@pytest.mark.asyncio
|
|
58
|
+
async def test_init(self, tracer, mock_client, mock_context):
|
|
59
|
+
# We only verify non-task related initialization
|
|
60
|
+
assert tracer.client == mock_client
|
|
61
|
+
assert tracer.context == mock_context
|
|
62
|
+
assert tracer.headers == {"Authorization": "Bearer test-token"}
|
|
63
|
+
assert tracer.running is False
|
|
64
|
+
|
|
65
|
+
@pytest.mark.asyncio
|
|
66
|
+
async def test_persist_run(self, tracer):
|
|
67
|
+
# Mock the _send_span method
|
|
68
|
+
tracer._send_span = MagicMock()
|
|
69
|
+
|
|
70
|
+
# Create a run with datetime for start_time
|
|
71
|
+
run = Run(
|
|
72
|
+
id=uuid.uuid4(),
|
|
73
|
+
name="test_run",
|
|
74
|
+
start_time=datetime(2023, 1, 1, 0, 0, 0),
|
|
75
|
+
run_type="llm",
|
|
76
|
+
inputs={"prompt": "test"},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Call _persist_run
|
|
80
|
+
await tracer._persist_run(run)
|
|
81
|
+
|
|
82
|
+
# Verify _send_span was called with the run
|
|
83
|
+
tracer._send_span.assert_called_once_with(run)
|
|
84
|
+
|
|
85
|
+
@pytest.mark.asyncio
|
|
86
|
+
async def test_send_span(self, tracer):
|
|
87
|
+
# Create a run
|
|
88
|
+
run_id = uuid.uuid4()
|
|
89
|
+
run = Run(
|
|
90
|
+
id=run_id,
|
|
91
|
+
name="test_run",
|
|
92
|
+
start_time=datetime(2023, 1, 1, 0, 0, 0),
|
|
93
|
+
end_time=datetime(2023, 1, 1, 0, 1, 0),
|
|
94
|
+
run_type="llm",
|
|
95
|
+
inputs={"prompt": "test"},
|
|
96
|
+
outputs={"result": "output"},
|
|
97
|
+
error=None, # Make it explicit that there's no error
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Call _send_span without mocking its internal methods
|
|
101
|
+
tracer._send_span(run)
|
|
102
|
+
|
|
103
|
+
# Get the item from the queue
|
|
104
|
+
span_data = tracer.log_queue.get_nowait()
|
|
105
|
+
|
|
106
|
+
# Verify expected data was put in the queue
|
|
107
|
+
assert span_data["id"] == str(run_id)
|
|
108
|
+
assert span_data["name"] == "test_run"
|
|
109
|
+
assert span_data["traceId"] == tracer.context.trace_id
|
|
110
|
+
assert span_data["parentId"] == tracer.context.parent_span_id
|
|
111
|
+
assert (
|
|
112
|
+
span_data["startTime"] == "2023-01-01T00:00:00"
|
|
113
|
+
) # Serialized to ISO format
|
|
114
|
+
assert span_data["endTime"] == "2023-01-01T00:01:00" # Serialized to ISO format
|
|
115
|
+
assert span_data["status"] == Status.SUCCESS # Since error is None
|
|
116
|
+
assert (
|
|
117
|
+
"attributes" in span_data
|
|
118
|
+
) # Just verify it exists, don't check exact content
|
|
119
|
+
assert span_data["spanType"] == "LangGraphRun"
|
|
120
|
+
|
|
121
|
+
@pytest.mark.asyncio
|
|
122
|
+
async def test_on_custom_event_function_call(self, tracer):
|
|
123
|
+
# Instead of modifying the Run object, we'll patch the entire Run.create_child method
|
|
124
|
+
# at the module level
|
|
125
|
+
child_run = Run(
|
|
126
|
+
id=uuid.uuid4(),
|
|
127
|
+
name="function_call",
|
|
128
|
+
run_type="tool",
|
|
129
|
+
inputs={"arg": "value"},
|
|
130
|
+
start_time=datetime(2023, 1, 1, 0, 0, 0),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Create a run
|
|
134
|
+
run_id = uuid.uuid4()
|
|
135
|
+
parent_run = Run(
|
|
136
|
+
id=run_id,
|
|
137
|
+
name="parent_run",
|
|
138
|
+
run_type="chain",
|
|
139
|
+
inputs={},
|
|
140
|
+
start_time=datetime(2023, 1, 1, 0, 0, 0),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Add the run to the run_map
|
|
144
|
+
tracer.run_map[str(run_id)] = parent_run
|
|
145
|
+
|
|
146
|
+
# Mock _send_span method
|
|
147
|
+
tracer._send_span = MagicMock()
|
|
148
|
+
|
|
149
|
+
# Patch the RunTree create_child in langsmith
|
|
150
|
+
with patch(
|
|
151
|
+
"langchain_core.tracers.schemas.Run.create_child", return_value=child_run
|
|
152
|
+
) as mock_create_child:
|
|
153
|
+
# Create function call event data
|
|
154
|
+
call_uuid = "test-call-uuid"
|
|
155
|
+
event_data = FunctionCallEventData(
|
|
156
|
+
call_uuid=call_uuid,
|
|
157
|
+
event_type="call",
|
|
158
|
+
function_name="test_function",
|
|
159
|
+
run_type="tool",
|
|
160
|
+
inputs={"arg": "value"},
|
|
161
|
+
tags=["test-tag"],
|
|
162
|
+
metadata={"meta": "data"},
|
|
163
|
+
output=None,
|
|
164
|
+
error=None,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Call on_custom_event
|
|
168
|
+
await tracer.on_custom_event(
|
|
169
|
+
name=CustomTraceEvents.UIPATH_TRACE_FUNCTION_CALL,
|
|
170
|
+
data=event_data,
|
|
171
|
+
run_id=run_id,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Verify the patched create_child was called with correct arguments
|
|
175
|
+
mock_create_child.assert_called_once_with(
|
|
176
|
+
name="test_function",
|
|
177
|
+
run_type="tool",
|
|
178
|
+
tags=["test-tag"],
|
|
179
|
+
inputs={"arg": "value"},
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Verify _send_span was called
|
|
183
|
+
tracer._send_span.assert_called_once()
|
|
184
|
+
|
|
185
|
+
# Verify function_call_run_map was updated
|
|
186
|
+
assert call_uuid in tracer.function_call_run_map
|
|
187
|
+
assert tracer.function_call_run_map[call_uuid] == child_run
|
|
188
|
+
|
|
189
|
+
@pytest.mark.asyncio
|
|
190
|
+
async def test_on_custom_event_function_completion(self, tracer):
|
|
191
|
+
# Create a function call run
|
|
192
|
+
call_uuid = "test-call-uuid"
|
|
193
|
+
child_run = Run(
|
|
194
|
+
id=uuid.uuid4(),
|
|
195
|
+
name="function_call",
|
|
196
|
+
run_type="function",
|
|
197
|
+
inputs={"arg": "value"},
|
|
198
|
+
start_time=datetime(2023, 1, 1, 0, 0, 0),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Add the run to the function_call_run_map
|
|
202
|
+
tracer.function_call_run_map[call_uuid] = child_run
|
|
203
|
+
|
|
204
|
+
# Mock _send_span method and _safe_dict_dump
|
|
205
|
+
tracer._send_span = MagicMock()
|
|
206
|
+
tracer._safe_dict_dump = MagicMock(return_value={"result": "output"})
|
|
207
|
+
|
|
208
|
+
# Create function completion event data
|
|
209
|
+
event_data = FunctionCallEventData(
|
|
210
|
+
call_uuid=call_uuid,
|
|
211
|
+
event_type="completion",
|
|
212
|
+
function_name="test_function",
|
|
213
|
+
output={"result": "output"},
|
|
214
|
+
error=None,
|
|
215
|
+
inputs={},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Patch Run.end method since we can't modify the Run object directly
|
|
219
|
+
with patch("langchain_core.tracers.schemas.Run.end") as mock_end:
|
|
220
|
+
# Call on_custom_event
|
|
221
|
+
await tracer.on_custom_event(
|
|
222
|
+
name=CustomTraceEvents.UIPATH_TRACE_FUNCTION_CALL,
|
|
223
|
+
data=event_data,
|
|
224
|
+
run_id=uuid.uuid4(),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Verify Run.end was called with the right arguments
|
|
228
|
+
mock_end.assert_called_once_with(outputs={"result": "output"}, error=None)
|
|
229
|
+
|
|
230
|
+
# Verify _send_span was called
|
|
231
|
+
tracer._send_span.assert_called_once_with(child_run)
|
|
232
|
+
|
|
233
|
+
# Verify run was removed from function_call_run_map
|
|
234
|
+
assert call_uuid not in tracer.function_call_run_map
|
|
235
|
+
|
|
236
|
+
@pytest.mark.asyncio
|
|
237
|
+
async def test_determine_status(self, tracer):
|
|
238
|
+
# Test success status
|
|
239
|
+
assert tracer._determine_status(None) == Status.SUCCESS
|
|
240
|
+
|
|
241
|
+
# Test error status
|
|
242
|
+
assert tracer._determine_status("Error message") == Status.ERROR
|
|
243
|
+
|
|
244
|
+
# Test interrupted status
|
|
245
|
+
assert tracer._determine_status("GraphInterrupt(reason)") == Status.INTERRUPTED
|
|
246
|
+
|
|
247
|
+
@pytest.mark.asyncio
|
|
248
|
+
async def test_build_url(self, tracer):
|
|
249
|
+
# Mock the _get_base_url method
|
|
250
|
+
tracer.base_url = "https://cloud.uipath.com/org/tenant"
|
|
251
|
+
|
|
252
|
+
# Call _build_url
|
|
253
|
+
url = tracer._build_url("test-trace-id")
|
|
254
|
+
|
|
255
|
+
# Verify the URL
|
|
256
|
+
assert (
|
|
257
|
+
url
|
|
258
|
+
== "https://cloud.uipath.com/org/tenant/llmopstenant_/api/Traces/spans?traceId=test-trace-id&source=Robots"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
@pytest.mark.asyncio
|
|
262
|
+
async def test_wait_for_all_tracers(self, tracer):
|
|
263
|
+
# Instead of trying to await the mock directly, let's patch the wait_for_all_tracers method
|
|
264
|
+
# to avoid awaiting the worker_task
|
|
265
|
+
|
|
266
|
+
# Save the original method
|
|
267
|
+
original_method = tracer.wait_for_all_tracers
|
|
268
|
+
|
|
269
|
+
# Replace with our test version that doesn't await the task
|
|
270
|
+
async def test_wait_for_all_tracers():
|
|
271
|
+
tracer.running = False
|
|
272
|
+
# Just return, don't await the worker_task
|
|
273
|
+
|
|
274
|
+
# Patch the method
|
|
275
|
+
tracer.wait_for_all_tracers = test_wait_for_all_tracers
|
|
276
|
+
|
|
277
|
+
# Call our test method
|
|
278
|
+
await tracer.wait_for_all_tracers()
|
|
279
|
+
|
|
280
|
+
# Verify running is set to False
|
|
281
|
+
assert tracer.running is False
|
|
282
|
+
|
|
283
|
+
# Restore the original method
|
|
284
|
+
tracer.wait_for_all_tracers = original_method
|
|
285
|
+
|
|
286
|
+
@pytest.mark.asyncio
|
|
287
|
+
async def test_worker_functionality(self, mock_client, mock_response, mock_context):
|
|
288
|
+
# For testing the worker functionality, we'll manually set up the tracer
|
|
289
|
+
with patch("asyncio.create_task"):
|
|
290
|
+
with patch.dict("os.environ", {"UIPATH_ACCESS_TOKEN": "test-token"}):
|
|
291
|
+
with patch.dict(
|
|
292
|
+
"os.environ", {"UIPATH_URL": "https://cloud.uipath.com/org/tenant"}
|
|
293
|
+
):
|
|
294
|
+
# Mock the client.post method
|
|
295
|
+
mock_client.post = AsyncMock(return_value=mock_response)
|
|
296
|
+
# Create a tracer without starting the worker
|
|
297
|
+
tracer = AsyncUiPathTracer(context=mock_context, client=mock_client)
|
|
298
|
+
tracer.running = (
|
|
299
|
+
False # Set running to False to ensure worker exits
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Configure test data
|
|
303
|
+
expected_url = "https://cloud.uipath.com/org/tenant/llmopstenant_/api/Traces/spans?traceId=test-trace-id&source=Robots"
|
|
304
|
+
|
|
305
|
+
# Add a test span to the queue
|
|
306
|
+
span_data = {
|
|
307
|
+
"id": "test-id",
|
|
308
|
+
"name": "test-span",
|
|
309
|
+
"traceId": "test-trace-id",
|
|
310
|
+
}
|
|
311
|
+
tracer.log_queue.put(span_data)
|
|
312
|
+
|
|
313
|
+
# Call the worker method directly
|
|
314
|
+
await tracer._worker()
|
|
315
|
+
|
|
316
|
+
# Verify client.post was called correctly
|
|
317
|
+
mock_client.post.assert_called_with(
|
|
318
|
+
expected_url,
|
|
319
|
+
headers=tracer.headers,
|
|
320
|
+
json=[span_data],
|
|
321
|
+
timeout=10,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
@pytest.mark.asyncio
|
|
325
|
+
async def test_httpx_post_with_correct_data(self, mock_client, mock_response):
|
|
326
|
+
# Create a context
|
|
327
|
+
context = UiPathTraceContext()
|
|
328
|
+
context.trace_id = "test-trace-id"
|
|
329
|
+
context.parent_span_id = "test-parent-span-id"
|
|
330
|
+
|
|
331
|
+
# Create a tracer with patch.dict to set the environment variables
|
|
332
|
+
with patch("asyncio.create_task"):
|
|
333
|
+
with patch.dict("os.environ", {"UIPATH_ACCESS_TOKEN": "test-token"}):
|
|
334
|
+
# Fix the patch path - we want to patch the method, not the import
|
|
335
|
+
with patch.object(
|
|
336
|
+
AsyncUiPathTracer,
|
|
337
|
+
"_get_base_url",
|
|
338
|
+
return_value="https://test.uipath.com/org/tenant",
|
|
339
|
+
):
|
|
340
|
+
tracer = AsyncUiPathTracer(context=context, client=mock_client)
|
|
341
|
+
tracer.running = (
|
|
342
|
+
False # Set running to False to ensure worker exits
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
# Create a span that will go in the queue
|
|
346
|
+
span_data = {
|
|
347
|
+
"id": "test-id",
|
|
348
|
+
"name": "test-span",
|
|
349
|
+
"traceId": "test-trace-id",
|
|
350
|
+
"attributes": '{"key": "value"}',
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
# Add span to the queue
|
|
354
|
+
tracer.log_queue.put(span_data)
|
|
355
|
+
|
|
356
|
+
# Execute the worker directly
|
|
357
|
+
await tracer._worker()
|
|
358
|
+
|
|
359
|
+
# Verify the post was called with the right URL and data
|
|
360
|
+
expected_url = "https://test.uipath.com/org/tenant/llmopstenant_/api/Traces/spans?traceId=test-trace-id&source=Robots"
|
|
361
|
+
mock_client.post.assert_called_with(
|
|
362
|
+
expected_url,
|
|
363
|
+
headers={"Authorization": "Bearer test-token"},
|
|
364
|
+
json=[span_data],
|
|
365
|
+
timeout=10,
|
|
366
|
+
)
|