ws-bom-robot-app 0.0.40__tar.gz → 0.0.42__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.
- {ws_bom_robot_app-0.0.40/ws_bom_robot_app.egg-info → ws_bom_robot_app-0.0.42}/PKG-INFO +1 -1
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/setup.py +1 -1
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/main.py +40 -16
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/providers/llm_manager.py +7 -5
- ws_bom_robot_app-0.0.42/ws_bom_robot_app/llm/vector_store/integration/jira.py +118 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42/ws_bom_robot_app.egg-info}/PKG-INFO +1 -1
- ws_bom_robot_app-0.0.40/ws_bom_robot_app/llm/vector_store/integration/jira.py +0 -114
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/MANIFEST.in +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/README.md +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/pyproject.toml +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/requirements.txt +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/setup.cfg +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/auth.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/config.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/cron_manager.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/agent_description.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/agent_handler.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/agent_lcel.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/api.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/defaut_prompt.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/models/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/models/api.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/models/base.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/models/kb.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/providers/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/settings.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/models/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/models/main.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/tool_builder.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/tool_manager.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/utils.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/agent.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/chunker.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/download.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/kb.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/print.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/secrets.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/utils/webhooks.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/base.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/chroma.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/faiss.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/manager.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/qdrant.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/generator.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/azure.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/base.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/confluence.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/dropbox.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/gcs.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/github.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/googledrive.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/manager.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/s3.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/sftp.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/sharepoint.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/sitemap.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/integration/slack.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/loader/__init__.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/loader/base.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/loader/docling.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/loader/json_loader.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/main.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/task_manager.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/util.py +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app.egg-info/SOURCES.txt +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app.egg-info/dependency_links.txt +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app.egg-info/requires.txt +0 -0
- {ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ _requirements = [line.split('#')[0].strip() for line in open("requirements.txt")
|
|
|
4
4
|
|
|
5
5
|
setup(
|
|
6
6
|
name="ws_bom_robot_app",
|
|
7
|
-
version="0.0.
|
|
7
|
+
version="0.0.42",
|
|
8
8
|
description="A FastAPI application serving ws bom/robot/llm platform ai.",
|
|
9
9
|
long_description=open("README.md", encoding='utf-8').read(),
|
|
10
10
|
long_description_content_type="text/markdown",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from asyncio import Queue
|
|
2
|
-
import asyncio, json, logging, os, traceback
|
|
2
|
+
import asyncio, json, logging, os, traceback, re
|
|
3
3
|
from fastapi import Request
|
|
4
4
|
from langchain.callbacks.tracers import LangChainTracer
|
|
5
5
|
from langchain_core.callbacks.base import AsyncCallbackHandler
|
|
@@ -28,6 +28,18 @@ async def invoke(rq: InvokeRequest) -> str:
|
|
|
28
28
|
result: AIMessage = await processor.run_agent(_msg)
|
|
29
29
|
return {"result": result.content}
|
|
30
30
|
|
|
31
|
+
def _parse_formatted_message(message: str) -> str:
|
|
32
|
+
try:
|
|
33
|
+
text_fragments = []
|
|
34
|
+
quoted_strings = re.findall(r'"([^"\\]*(?:\\.[^"\\]*)*)"', message)
|
|
35
|
+
for string in quoted_strings:
|
|
36
|
+
if not string.startswith(('threadId', 'type')) and len(string) > 1:
|
|
37
|
+
text_fragments.append(string)
|
|
38
|
+
result = ''.join(text_fragments)
|
|
39
|
+
result = result.replace('\\n', '\n')
|
|
40
|
+
except:
|
|
41
|
+
result = message
|
|
42
|
+
return result
|
|
31
43
|
async def __stream(rq: StreamRequest, ctx: Request, queue: Queue,formatted: bool = True) -> None:
|
|
32
44
|
await rq.initialize()
|
|
33
45
|
#os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
|
|
@@ -41,21 +53,33 @@ async def __stream(rq: StreamRequest, ctx: Request, queue: Queue,formatted: bool
|
|
|
41
53
|
|
|
42
54
|
#CREATION OF CHAT HISTORY FOR AGENT
|
|
43
55
|
for message in rq.messages:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
if message.role in ["human","user"]:
|
|
57
|
+
settings.chat_history.append(HumanMessage(content=message.content))
|
|
58
|
+
elif message.role in ["ai","assistant"]:
|
|
59
|
+
message_content = ""
|
|
60
|
+
if formatted:
|
|
61
|
+
if '{\"type\":\"string\"' in message.content:
|
|
62
|
+
try:
|
|
63
|
+
json_msg = json.loads('[' + message.content[:-1] + ']')
|
|
64
|
+
for msg in json_msg:
|
|
65
|
+
if msg.get("content"):
|
|
66
|
+
message_content += msg["content"]
|
|
67
|
+
except:
|
|
68
|
+
message_content = _parse_formatted_message(message.content)
|
|
69
|
+
elif '{\"type\":\"text\"' in message.content:
|
|
70
|
+
try:
|
|
71
|
+
json_msg = json.loads('[' + message.content[:-1] + ']')
|
|
72
|
+
for msg in json_msg:
|
|
73
|
+
if msg.get("text"):
|
|
74
|
+
message_content += msg["text"]
|
|
75
|
+
except:
|
|
76
|
+
message_content = _parse_formatted_message(message.content)
|
|
77
|
+
else:
|
|
78
|
+
message_content = _parse_formatted_message(message.content)
|
|
79
|
+
else:
|
|
80
|
+
message_content = message.content
|
|
81
|
+
if message_content:
|
|
82
|
+
settings.chat_history.append(AIMessage(content=message_content))
|
|
59
83
|
|
|
60
84
|
if rq.lang_chain_tracing:
|
|
61
85
|
client = LangSmithClient(
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/providers/llm_manager.py
RENAMED
|
@@ -44,7 +44,9 @@ class OpenAI(LlmInterface):
|
|
|
44
44
|
|
|
45
45
|
def get_llm(self):
|
|
46
46
|
from langchain_openai import ChatOpenAI
|
|
47
|
-
chat = ChatOpenAI(
|
|
47
|
+
chat = ChatOpenAI(
|
|
48
|
+
api_key=self.config.api_key or os.getenv("OPENAI_API_KEY"),
|
|
49
|
+
model=self.config.model)
|
|
48
50
|
if not any(self.config.model.startswith(prefix) for prefix in ["o1", "o3"]):
|
|
49
51
|
chat.temperature = self.config.temperature
|
|
50
52
|
chat.streaming = True
|
|
@@ -60,7 +62,7 @@ class DeepSeek(LlmInterface):
|
|
|
60
62
|
def get_llm(self):
|
|
61
63
|
from langchain_openai import ChatOpenAI
|
|
62
64
|
return ChatOpenAI(
|
|
63
|
-
api_key=self.config.api_key,
|
|
65
|
+
api_key=self.config.api_key or os.getenv("DEEPSEEK_API_KEY"),
|
|
64
66
|
model=self.config.model,
|
|
65
67
|
base_url="https://api.deepseek.com/v1",
|
|
66
68
|
max_tokens=8192,
|
|
@@ -79,7 +81,7 @@ class Google(LlmInterface):
|
|
|
79
81
|
from langchain_google_genai.chat_models import ChatGoogleGenerativeAI
|
|
80
82
|
return ChatGoogleGenerativeAI(
|
|
81
83
|
name="chat",
|
|
82
|
-
api_key=self.config.api_key,
|
|
84
|
+
api_key=self.config.api_key or os.getenv("GOOGLE_API_KEY"),
|
|
83
85
|
model=self.config.model,
|
|
84
86
|
temperature=self.config.temperature,
|
|
85
87
|
disable_streaming=False
|
|
@@ -131,7 +133,7 @@ class Anthropic(LlmInterface):
|
|
|
131
133
|
def get_llm(self):
|
|
132
134
|
from langchain_anthropic import ChatAnthropic
|
|
133
135
|
return ChatAnthropic(
|
|
134
|
-
api_key=self.config.api_key,
|
|
136
|
+
api_key=self.config.api_key or os.getenv("ANTHROPIC_API_KEY"),
|
|
135
137
|
model=self.config.model,
|
|
136
138
|
temperature=self.config.temperature,
|
|
137
139
|
streaming=True,
|
|
@@ -156,7 +158,7 @@ class Groq(LlmInterface):
|
|
|
156
158
|
def get_llm(self):
|
|
157
159
|
from langchain_groq import ChatGroq
|
|
158
160
|
return ChatGroq(
|
|
159
|
-
api_key=self.config.api_key,
|
|
161
|
+
api_key=self.config.api_key or os.getenv("GROQ_API_KEY"),
|
|
160
162
|
model=self.config.model,
|
|
161
163
|
#max_tokens=8192,
|
|
162
164
|
temperature=self.config.temperature,
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import asyncio, os
|
|
2
|
+
from ws_bom_robot_app.llm.vector_store.integration.base import IntegrationStrategy
|
|
3
|
+
from langchain_core.documents import Document
|
|
4
|
+
from ws_bom_robot_app.llm.vector_store.loader.base import Loader
|
|
5
|
+
from pydantic import BaseModel, Field, AliasChoices
|
|
6
|
+
from typing import Any, Optional, Union
|
|
7
|
+
from unstructured_ingest.interfaces import ProcessorConfig, ReadConfig
|
|
8
|
+
from unstructured_ingest.connector.jira import SimpleJiraConfig, JiraAccessConfig, JiraSourceConnector, JiraIngestDoc, nested_object_to_field_getter, _get_id_fields_for_issue, _get_project_fields_for_issue
|
|
9
|
+
from unstructured_ingest.runner import JiraRunner
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JiraParams(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
JiraParams is a Pydantic model that represents the parameters required to interact with a Jira instance.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
url (str): The URL of the Jira instance, e.g., 'https://example.atlassian.net'.
|
|
18
|
+
access_token (str): The access token for authenticating with the Jira API.
|
|
19
|
+
user_email (str): The email address of the Jira user.
|
|
20
|
+
projects (list[str]): A list of project keys or IDs to interact with, e.g., ['SCRUM', 'PROJ1'].
|
|
21
|
+
boards (Optional[list[str]]): An optional list of board IDs to interact with. Defaults to None, e.g., ['1', '2'].
|
|
22
|
+
issues (Optional[list[str]]): An optional list of issue keys or IDs to interact with. Defaults to None, e.g., ['SCRUM-1', 'PROJ1-1'].
|
|
23
|
+
"""
|
|
24
|
+
url: str = Field(..., pattern=r'^https?:\/\/.+')
|
|
25
|
+
access_token: str = Field(..., validation_alias=AliasChoices("accessToken","access_token"), min_length=1)
|
|
26
|
+
user_email: str = Field(validation_alias=AliasChoices("userEmail","user_email"), min_length=1)
|
|
27
|
+
projects: list[str]
|
|
28
|
+
boards: Optional[list[str]] | None = None
|
|
29
|
+
issues: Optional[list[str]] | None = None
|
|
30
|
+
|
|
31
|
+
class Jira(IntegrationStrategy):
|
|
32
|
+
def __init__(self, knowledgebase_path: str, data: dict[str, Union[str,int,list]]):
|
|
33
|
+
super().__init__(knowledgebase_path, data)
|
|
34
|
+
self.__data = JiraParams.model_validate(self.data)
|
|
35
|
+
def working_subdirectory(self) -> str:
|
|
36
|
+
return 'jira'
|
|
37
|
+
def run(self) -> None:
|
|
38
|
+
access_config = JiraAccessConfig(
|
|
39
|
+
api_token=self.__data.access_token
|
|
40
|
+
)
|
|
41
|
+
config = SimpleJiraConfig(
|
|
42
|
+
user_email=self.__data.user_email,
|
|
43
|
+
url = self.__data.url,
|
|
44
|
+
access_config=access_config,
|
|
45
|
+
projects=self.__data.projects,
|
|
46
|
+
boards=self.__data.boards,
|
|
47
|
+
issues=self.__data.issues
|
|
48
|
+
)
|
|
49
|
+
# runner override: waiting for v2 migration https://github.com/Unstructured-IO/unstructured-ingest/issues/106
|
|
50
|
+
runner = _JiraRunner(
|
|
51
|
+
connector_config=config,
|
|
52
|
+
processor_config=ProcessorConfig(reprocess=False,verbose=False,num_processes=2,raise_on_error=False),
|
|
53
|
+
read_config=ReadConfig(download_dir=self.working_directory,re_download=True,preserve_downloads=True,download_only=True),
|
|
54
|
+
partition_config=None,
|
|
55
|
+
retry_strategy_config=None
|
|
56
|
+
)
|
|
57
|
+
runner.run()
|
|
58
|
+
async def load(self) -> list[Document]:
|
|
59
|
+
await asyncio.to_thread(self.run)
|
|
60
|
+
await asyncio.sleep(1)
|
|
61
|
+
return await Loader(self.working_directory).load()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# region override
|
|
65
|
+
class _JiraIngestDoc(JiraIngestDoc):
|
|
66
|
+
def _get_dropdown_custom_fields_for_issue(issue: dict, c_sep=" " * 5, r_sep="\n") -> str:
|
|
67
|
+
def _parse_value(value: Any) -> Any:
|
|
68
|
+
if isinstance(value, dict):
|
|
69
|
+
_candidate = ["displayName", "name", "value"]
|
|
70
|
+
for item in _candidate:
|
|
71
|
+
if item in value:
|
|
72
|
+
return value[item]
|
|
73
|
+
return value
|
|
74
|
+
def _remap_custom_fields(fields: dict):
|
|
75
|
+
remapped_fields = {}
|
|
76
|
+
for field_key, field_value in fields.items():
|
|
77
|
+
new_key = next((map_item["name"] for map_item in _JiraSourceConnector.CUSTOM_FIELDS if field_key == map_item["id"]), field_key)
|
|
78
|
+
if new_key != field_value:
|
|
79
|
+
remapped_fields[new_key] = field_value
|
|
80
|
+
return remapped_fields
|
|
81
|
+
filtered_fields = {key: _parse_value(value) for key, value in issue.items() if value is not None and type(value) not in [list]}
|
|
82
|
+
custom_fields =_remap_custom_fields(filtered_fields)
|
|
83
|
+
return (r_sep + c_sep ).join([f"{key}: {value}{r_sep}" for key, value in custom_fields.items()])
|
|
84
|
+
def __init__(self, *args, **kwargs):
|
|
85
|
+
super().__init__(*args, **kwargs)
|
|
86
|
+
_issue = self.issue
|
|
87
|
+
_nested: dict = nested_object_to_field_getter(_issue["fields"])
|
|
88
|
+
document = "\n\n\n".join(
|
|
89
|
+
[
|
|
90
|
+
_get_id_fields_for_issue(_issue),
|
|
91
|
+
_get_project_fields_for_issue(_nested),
|
|
92
|
+
_JiraIngestDoc._get_dropdown_custom_fields_for_issue(_nested)
|
|
93
|
+
],
|
|
94
|
+
)
|
|
95
|
+
_full_filename = str(self.filename)
|
|
96
|
+
_file_extension = _full_filename.split(".")[-1]
|
|
97
|
+
_file_without_extension = _full_filename.replace(f".{_file_extension}","")
|
|
98
|
+
os.makedirs(os.path.dirname(_file_without_extension), exist_ok=True)
|
|
99
|
+
with open(f"{_file_without_extension}_extra.{_file_extension}", "w", encoding="utf8") as f:
|
|
100
|
+
f.write(document)
|
|
101
|
+
|
|
102
|
+
class _JiraSourceConnector(JiraSourceConnector):
|
|
103
|
+
CUSTOM_FIELDS: list | None = None
|
|
104
|
+
def __set_custom_fields(self) -> None:
|
|
105
|
+
_custom_fields = self.jira.get_all_custom_fields()
|
|
106
|
+
_JiraSourceConnector.CUSTOM_FIELDS = [{"id":item["id"],"name":item["name"]} for item in _custom_fields]
|
|
107
|
+
self._jira = None # fix serialization
|
|
108
|
+
def __init__(self, *args, **kwargs):
|
|
109
|
+
super().__init__(*args, **kwargs)
|
|
110
|
+
if not _JiraSourceConnector.CUSTOM_FIELDS:
|
|
111
|
+
self.__set_custom_fields()
|
|
112
|
+
def get_ingest_docs(self) -> list[_JiraIngestDoc]:
|
|
113
|
+
return [_JiraIngestDoc(**item.__dict__) for item in super().get_ingest_docs()]
|
|
114
|
+
|
|
115
|
+
class _JiraRunner(JiraRunner):
|
|
116
|
+
def get_source_connector_cls(self):
|
|
117
|
+
return _JiraSourceConnector
|
|
118
|
+
# endregion
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from ws_bom_robot_app.llm.vector_store.integration.base import IntegrationStrategy
|
|
3
|
-
from unstructured_ingest.interfaces import ProcessorConfig, ReadConfig
|
|
4
|
-
from unstructured_ingest.connector.jira import SimpleJiraConfig, JiraAccessConfig
|
|
5
|
-
from unstructured_ingest.runner import JiraRunner
|
|
6
|
-
from langchain_core.documents import Document
|
|
7
|
-
from ws_bom_robot_app.llm.vector_store.loader.base import Loader
|
|
8
|
-
from pydantic import BaseModel, Field, AliasChoices
|
|
9
|
-
from typing import Optional, Union
|
|
10
|
-
import requests
|
|
11
|
-
import unstructured_ingest.connector.jira
|
|
12
|
-
|
|
13
|
-
class JiraParams(BaseModel):
|
|
14
|
-
"""
|
|
15
|
-
JiraParams is a Pydantic model that represents the parameters required to interact with a Jira instance.
|
|
16
|
-
|
|
17
|
-
Attributes:
|
|
18
|
-
url (str): The URL of the Jira instance, e.g., 'https://example.atlassian.net'.
|
|
19
|
-
access_token (str): The access token for authenticating with the Jira API.
|
|
20
|
-
user_email (str): The email address of the Jira user.
|
|
21
|
-
projects (list[str]): A list of project keys or IDs to interact with, e.g., ['SCRUM', 'PROJ1'].
|
|
22
|
-
boards (Optional[list[str]]): An optional list of board IDs to interact with. Defaults to None, e.g., ['1', '2'].
|
|
23
|
-
issues (Optional[list[str]]): An optional list of issue keys or IDs to interact with. Defaults to None, e.g., ['SCRUM-1', 'PROJ1-1'].
|
|
24
|
-
"""
|
|
25
|
-
url: str
|
|
26
|
-
access_token: str = Field(validation_alias=AliasChoices("accessToken","access_token"))
|
|
27
|
-
user_email: str = Field(validation_alias=AliasChoices("userEmail","user_email"))
|
|
28
|
-
projects: list[str]
|
|
29
|
-
boards: Optional[list[str]] | None = None
|
|
30
|
-
issues: Optional[list[str]] | None = None
|
|
31
|
-
fieldsMappingUrl: Optional[str] | None = None
|
|
32
|
-
|
|
33
|
-
class Jira(IntegrationStrategy):
|
|
34
|
-
DEFAULT_C_SEP = " " * 5
|
|
35
|
-
DEFAULT_R_SEP = "\n"
|
|
36
|
-
def __init__(self, knowledgebase_path: str, data: dict[str, Union[str,int,list]]):
|
|
37
|
-
super().__init__(knowledgebase_path, data)
|
|
38
|
-
self.__data = JiraParams.model_validate(self.data)
|
|
39
|
-
def working_subdirectory(self) -> str:
|
|
40
|
-
return 'jira'
|
|
41
|
-
def run(self) -> None:
|
|
42
|
-
unstructured_ingest.connector.jira._get_dropdown_fields_for_issue = self._get_dropdown_fields_for_issue
|
|
43
|
-
access_config = JiraAccessConfig(
|
|
44
|
-
api_token=self.__data.access_token
|
|
45
|
-
)
|
|
46
|
-
config = SimpleJiraConfig(
|
|
47
|
-
user_email=self.__data.user_email,
|
|
48
|
-
url = self.__data.url,
|
|
49
|
-
access_config=access_config,
|
|
50
|
-
projects=self.__data.projects,
|
|
51
|
-
boards=self.__data.boards,
|
|
52
|
-
issues=self.__data.issues
|
|
53
|
-
)
|
|
54
|
-
runner = JiraRunner(
|
|
55
|
-
connector_config=config,
|
|
56
|
-
processor_config=ProcessorConfig(reprocess=False,verbose=False,num_processes=2,raise_on_error=False),
|
|
57
|
-
read_config=ReadConfig(download_dir=self.working_directory,re_download=True,preserve_downloads=True,download_only=True),
|
|
58
|
-
partition_config=None,
|
|
59
|
-
retry_strategy_config=None
|
|
60
|
-
)
|
|
61
|
-
runner.run()
|
|
62
|
-
async def load(self) -> list[Document]:
|
|
63
|
-
await asyncio.to_thread(self.run)
|
|
64
|
-
await asyncio.sleep(1)
|
|
65
|
-
return await Loader(self.working_directory).load()
|
|
66
|
-
|
|
67
|
-
def _remap_custom_fields(self, field_list):
|
|
68
|
-
auth = (self.__data.user_email, self.__data.access_token)
|
|
69
|
-
response = requests.get(self.__data.fieldsMappingUrl, auth=auth)
|
|
70
|
-
|
|
71
|
-
if response.status_code == 200:
|
|
72
|
-
mapper: dict = response.json()
|
|
73
|
-
remapped_field_list = {}
|
|
74
|
-
for field_key, field_value in field_list.items():
|
|
75
|
-
new_key = None
|
|
76
|
-
for map_item in mapper:
|
|
77
|
-
if field_key == map_item["id"]:
|
|
78
|
-
# Usa il nome mappato come nuova chiave
|
|
79
|
-
new_key = map_item["name"]
|
|
80
|
-
break
|
|
81
|
-
|
|
82
|
-
if new_key is None:
|
|
83
|
-
new_key = field_key
|
|
84
|
-
|
|
85
|
-
remapped_field_list[new_key] = field_value
|
|
86
|
-
|
|
87
|
-
return remapped_field_list
|
|
88
|
-
|
|
89
|
-
def _get_dropdown_fields_for_issue(self, issue, c_sep=DEFAULT_C_SEP, r_sep=DEFAULT_R_SEP):
|
|
90
|
-
all_fields = {}
|
|
91
|
-
for key, value in issue.items():
|
|
92
|
-
if value is not None:
|
|
93
|
-
if isinstance(value, list) and (len(value) > 0):
|
|
94
|
-
all_fields[key] = value
|
|
95
|
-
else:
|
|
96
|
-
all_fields[key] = value
|
|
97
|
-
mapped_fields = self._remap_custom_fields(all_fields)
|
|
98
|
-
return f"""
|
|
99
|
-
IssueType:{issue["issuetype"]["name"]}
|
|
100
|
-
{r_sep}
|
|
101
|
-
Status:{issue["status"]["name"]}
|
|
102
|
-
{r_sep}
|
|
103
|
-
Priority:{issue["priority"]}
|
|
104
|
-
{r_sep}
|
|
105
|
-
AssigneeID_Name:{issue["assignee"]["accountId"]}{c_sep}{issue["assignee"]["displayName"]}
|
|
106
|
-
{r_sep}
|
|
107
|
-
ReporterAdr_Name:{issue["reporter"]["emailAddress"]}{c_sep}{issue["reporter"]["displayName"]}
|
|
108
|
-
{r_sep}
|
|
109
|
-
Labels:{c_sep.join(issue["labels"])}
|
|
110
|
-
{r_sep}
|
|
111
|
-
Components:{c_sep.join([component["name"] for component in issue["components"]])}
|
|
112
|
-
{r_sep}
|
|
113
|
-
{(r_sep + c_sep ).join([f"{key}:{value}{r_sep}" for key, value in mapped_fields.items()])}
|
|
114
|
-
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/agent_description.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/providers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/models/__init__.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/models/main.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/tool_builder.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/tools/tool_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/__init__.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/__init__.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/base.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/chroma.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/faiss.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/manager.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/db/qdrant.py
RENAMED
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/generator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app/llm/vector_store/loader/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ws_bom_robot_app-0.0.40 → ws_bom_robot_app-0.0.42}/ws_bom_robot_app.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|