ws-bom-robot-app 0.0.68__py3-none-any.whl → 0.0.70__py3-none-any.whl
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/llm/providers/llm_manager.py +27 -17
- ws_bom_robot_app/llm/vector_store/integration/manager.py +2 -2
- ws_bom_robot_app/llm/vector_store/integration/thron.py +123 -0
- {ws_bom_robot_app-0.0.68.dist-info → ws_bom_robot_app-0.0.70.dist-info}/METADATA +10 -4
- {ws_bom_robot_app-0.0.68.dist-info → ws_bom_robot_app-0.0.70.dist-info}/RECORD +7 -6
- {ws_bom_robot_app-0.0.68.dist-info → ws_bom_robot_app-0.0.70.dist-info}/WHEEL +0 -0
- {ws_bom_robot_app-0.0.68.dist-info → ws_bom_robot_app-0.0.70.dist-info}/top_level.txt +0 -0
|
@@ -107,9 +107,8 @@ class Google(LlmInterface):
|
|
|
107
107
|
def get_llm(self):
|
|
108
108
|
from langchain_google_genai.chat_models import ChatGoogleGenerativeAI
|
|
109
109
|
return ChatGoogleGenerativeAI(
|
|
110
|
-
name="chat",
|
|
111
|
-
api_key=self.config.api_key or os.getenv("GOOGLE_API_KEY"),
|
|
112
110
|
model=self.config.model,
|
|
111
|
+
google_api_key=self.config.api_key or os.getenv("GOOGLE_API_KEY"),
|
|
113
112
|
temperature=self.config.temperature,
|
|
114
113
|
disable_streaming=False,
|
|
115
114
|
)
|
|
@@ -117,8 +116,8 @@ class Google(LlmInterface):
|
|
|
117
116
|
def get_embeddings(self):
|
|
118
117
|
from langchain_google_genai.embeddings import GoogleGenerativeAIEmbeddings
|
|
119
118
|
return GoogleGenerativeAIEmbeddings(
|
|
120
|
-
google_api_key=self.config.api_key,
|
|
121
|
-
model="models/text-embedding-
|
|
119
|
+
google_api_key=self.config.api_key or os.getenv("GOOGLE_API_KEY"),
|
|
120
|
+
model="models/text-embedding-004")
|
|
122
121
|
|
|
123
122
|
def get_models(self):
|
|
124
123
|
import google.generativeai as genai
|
|
@@ -140,22 +139,33 @@ class Gvertex(LlmInterface):
|
|
|
140
139
|
temperature=self.config.temperature
|
|
141
140
|
)
|
|
142
141
|
def get_embeddings(self):
|
|
143
|
-
from langchain_google_vertexai import VertexAIEmbeddings
|
|
144
|
-
|
|
142
|
+
from langchain_google_vertexai.embeddings import VertexAIEmbeddings
|
|
143
|
+
embeddings = VertexAIEmbeddings(model_name="text-embedding-004")
|
|
144
|
+
#fix gemini-embedding-001 batch size
|
|
145
|
+
#embeddings.instance["max_batch_size"] = 1
|
|
146
|
+
#embeddings.instance["batch_size"] = 1
|
|
147
|
+
return embeddings
|
|
145
148
|
def get_models(self):
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
+
_models = [
|
|
150
|
+
{"id":"gemini-2.5-pro"},
|
|
151
|
+
{"id":"gemini-2.5-flash"},
|
|
152
|
+
{"id":"gemini-2.0-flash"},
|
|
153
|
+
{"id":"gemini-2.0-flash-lite"}
|
|
154
|
+
]
|
|
155
|
+
try:
|
|
156
|
+
from google.cloud import aiplatform
|
|
157
|
+
aiplatform.init()
|
|
158
|
+
_list = aiplatform.Model.list()
|
|
159
|
+
if _list:
|
|
160
|
+
_models = list([{"id": model.name} for model in _list])
|
|
149
161
|
# removed due issue: https://github.com/langchain-ai/langchain-google/issues/733
|
|
150
162
|
# Message type "google.cloud.aiplatform.v1beta1.GenerateContentResponse" has no field named "createTime" at "GenerateContentResponse". Available Fields(except extensions): "['candidates', 'modelVersion', 'promptFeedback', 'usageMetadata']"
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
{"id":"gemini-1.5-pro-002"}
|
|
158
|
-
]
|
|
163
|
+
except Exception as e:
|
|
164
|
+
print(f"Error fetching models from Gvertex: {e}")
|
|
165
|
+
# fallback to hardcoded models
|
|
166
|
+
#see https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations#united-states for available models
|
|
167
|
+
finally:
|
|
168
|
+
return _models
|
|
159
169
|
|
|
160
170
|
class Groq(LlmInterface):
|
|
161
171
|
def get_llm(self):
|
|
@@ -12,7 +12,7 @@ from ws_bom_robot_app.llm.vector_store.integration.sftp import Sftp
|
|
|
12
12
|
from ws_bom_robot_app.llm.vector_store.integration.sharepoint import Sharepoint
|
|
13
13
|
from ws_bom_robot_app.llm.vector_store.integration.sitemap import Sitemap
|
|
14
14
|
from ws_bom_robot_app.llm.vector_store.integration.slack import Slack
|
|
15
|
-
|
|
15
|
+
from ws_bom_robot_app.llm.vector_store.integration.thron import Thron
|
|
16
16
|
class IntegrationManager:
|
|
17
17
|
_list: dict[str, Type[IntegrationStrategy]] = {
|
|
18
18
|
"llmkbazure": Azure,
|
|
@@ -27,7 +27,7 @@ class IntegrationManager:
|
|
|
27
27
|
"llmkbsharepoint": Sharepoint,
|
|
28
28
|
"llmkbsitemap": Sitemap,
|
|
29
29
|
"llmkbslack": Slack,
|
|
30
|
-
|
|
30
|
+
"llmkbthron": Thron,
|
|
31
31
|
}
|
|
32
32
|
@classmethod
|
|
33
33
|
def get_strategy(cls, name: str, knowledgebase_path: str, data: dict[str, str]) -> IntegrationStrategy:
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import asyncio, logging, aiohttp
|
|
2
|
+
from ws_bom_robot_app.llm.vector_store.integration.base import IntegrationStrategy, UnstructuredIngest
|
|
3
|
+
from unstructured_ingest.v2.processes.connectors.fsspec.sftp import SftpConnectionConfig, SftpAccessConfig, SftpDownloaderConfig, SftpIndexerConfig
|
|
4
|
+
from langchain_core.documents import Document
|
|
5
|
+
from ws_bom_robot_app.llm.vector_store.loader.base import Loader
|
|
6
|
+
from typing import List, Union, Optional
|
|
7
|
+
from pydantic import BaseModel, Field, AliasChoices
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
class ThronParams(BaseModel):
|
|
12
|
+
"""
|
|
13
|
+
ThronParams is a model that defines the parameters required for Thron integration.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
app_id (str): The application ID for Thron.
|
|
17
|
+
client_id (str): The client ID for Thron.
|
|
18
|
+
client_secret (str): The client secret for Thron.
|
|
19
|
+
"""
|
|
20
|
+
organization_name: str = Field(validation_alias=AliasChoices("organizationName","organization_name"))
|
|
21
|
+
attribute_fields: Optional[List[str]] = Field(default=None, validation_alias=AliasChoices("attributeFields","attribute_fields"))
|
|
22
|
+
client_id: str = Field(validation_alias=AliasChoices("clientId","client_id"))
|
|
23
|
+
client_secret: str = Field(validation_alias=AliasChoices("clientSecret","client_secret"))
|
|
24
|
+
|
|
25
|
+
class Thron(IntegrationStrategy):
|
|
26
|
+
def __init__(self, knowledgebase_path: str, data: dict[str, Union[str,int,list]]):
|
|
27
|
+
super().__init__(knowledgebase_path, data)
|
|
28
|
+
self.__data = ThronParams.model_validate(self.data)
|
|
29
|
+
|
|
30
|
+
def working_subdirectory(self) -> str:
|
|
31
|
+
return 'thron'
|
|
32
|
+
|
|
33
|
+
async def run(self) -> None:
|
|
34
|
+
_data = await self.__get_data()
|
|
35
|
+
transformed_data = self.__transform_data(_data)
|
|
36
|
+
json_file_path = os.path.join(self.working_directory, 'thron_data.json')
|
|
37
|
+
with open(json_file_path, 'w', encoding='utf-8') as f:
|
|
38
|
+
json.dump(transformed_data, f, indent=2, ensure_ascii=False)
|
|
39
|
+
|
|
40
|
+
async def load(self) -> list[Document]:
|
|
41
|
+
await self.run()
|
|
42
|
+
await asyncio.sleep(1)
|
|
43
|
+
return await Loader(self.working_directory).load()
|
|
44
|
+
|
|
45
|
+
async def __get_auth_token(self) -> str:
|
|
46
|
+
try:
|
|
47
|
+
async with aiohttp.ClientSession() as session:
|
|
48
|
+
auth_data = {
|
|
49
|
+
"grant_type": "client_credentials",
|
|
50
|
+
"client_id": self.__data.client_id,
|
|
51
|
+
"client_secret": self.__data.client_secret
|
|
52
|
+
}
|
|
53
|
+
headers = {
|
|
54
|
+
"accept": "application/json",
|
|
55
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
56
|
+
}
|
|
57
|
+
async with session.post("https://websolute.thron.com/api/v1/authentication/oauth2/token", data=auth_data, headers=headers) as response:
|
|
58
|
+
result = await response.json()
|
|
59
|
+
return result.get("access_token", "")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logging.error(f"Error fetching Thron auth token: {e}")
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
async def __get_data(self) -> dict:
|
|
65
|
+
try:
|
|
66
|
+
token = await self.__get_auth_token()
|
|
67
|
+
if not token:
|
|
68
|
+
logging.error("Failed to obtain Thron authentication token.")
|
|
69
|
+
return {}
|
|
70
|
+
attribute_fields = ",".join(self.__data.attribute_fields) if self.__data.attribute_fields else ""
|
|
71
|
+
async with aiohttp.ClientSession() as session:
|
|
72
|
+
headers = {
|
|
73
|
+
"accept": "application/json",
|
|
74
|
+
"Authorization": f"Bearer {token}"
|
|
75
|
+
}
|
|
76
|
+
async with session.get(f"https://{self.__data.organization_name}.thron.com/api/v1/product-data/products?attributeFields=product_id,{attribute_fields}", headers=headers) as response:
|
|
77
|
+
result = await response.json()
|
|
78
|
+
return result.get("items", {})
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logging.error(f"Error fetching Thron product data: {e}")
|
|
81
|
+
return {}
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def __transform_data(self, data: dict) -> dict:
|
|
87
|
+
transformed_data = []
|
|
88
|
+
|
|
89
|
+
# First pass: collect all MASTER items
|
|
90
|
+
master_items = {item.get("id"): item for item in data if item.get("hierarchyLevel") == "MASTER"}
|
|
91
|
+
|
|
92
|
+
# Second pass: process items
|
|
93
|
+
for item in data:
|
|
94
|
+
hierarchy_level = item.get("hierarchyLevel")
|
|
95
|
+
|
|
96
|
+
if hierarchy_level == "MASTER":
|
|
97
|
+
# Find matching variants for this master
|
|
98
|
+
master_id = item.get("id")
|
|
99
|
+
item_copy = item.copy()
|
|
100
|
+
item_copy["variants"] = []
|
|
101
|
+
|
|
102
|
+
# Look for variants that belong to this master
|
|
103
|
+
for variant_item in data:
|
|
104
|
+
if (variant_item.get("hierarchyLevel") == "VARIANT" and
|
|
105
|
+
variant_item.get("variation", {}).get("master")):
|
|
106
|
+
|
|
107
|
+
variant_master_id = variant_item.get("variation").get("master").split(":")[-1]
|
|
108
|
+
|
|
109
|
+
# Check if this variant belongs to current master by comparing product_ids
|
|
110
|
+
for attr in item.get("attributes", []):
|
|
111
|
+
if (attr.get("code") == "product_id" and
|
|
112
|
+
attr.get("identifier") == variant_master_id):
|
|
113
|
+
item_copy["variants"].append(variant_item)
|
|
114
|
+
break
|
|
115
|
+
|
|
116
|
+
# Only add master items that have variants
|
|
117
|
+
if item_copy["variants"]:
|
|
118
|
+
transformed_data.append(item_copy)
|
|
119
|
+
|
|
120
|
+
elif hierarchy_level == "SIMPLE":
|
|
121
|
+
transformed_data.append(item)
|
|
122
|
+
|
|
123
|
+
return transformed_data
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ws_bom_robot_app
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.70
|
|
4
4
|
Summary: A FastAPI application serving ws bom/robot/llm platform ai.
|
|
5
5
|
Home-page: https://github.com/websolutespa/bom
|
|
6
6
|
Author: Websolute Spa
|
|
@@ -258,11 +258,17 @@ docker build -f Dockerfile-robot-base-gpu -t ghcr.io/websolutespa/ws-bom-robot-b
|
|
|
258
258
|
docker push ghcr.io/websolutespa/ws-bom-robot-base:gpu
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
-
dockerize app
|
|
261
|
+
dockerize app
|
|
262
262
|
|
|
263
263
|
```pwsh
|
|
264
|
-
docker build -f Dockerfile
|
|
265
|
-
docker run --name ws-bom-robot-app
|
|
264
|
+
docker build -f Dockerfile -t ws-bom-robot-app .
|
|
265
|
+
docker run --rm --name ws-bom-robot-app -d -p 6001:6001 ws-bom-robot-app
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
docker run mounted to src
|
|
269
|
+
|
|
270
|
+
```pwsh
|
|
271
|
+
docker run --rm --name ws-bom-robot-app-src -d -v "$(pwd)/ws_bom_robot_app:/app/ws_bom_robot_app" -v "$(pwd)/.data:/app/.data" -v "$(pwd)/tmp:/tmp" -p 6001:6001 ws-bom-robot-app
|
|
266
272
|
```
|
|
267
273
|
|
|
268
274
|
### ✈️ publish
|
|
@@ -23,7 +23,7 @@ ws_bom_robot_app/llm/models/base.py,sha256=1TqxuTK3rjJEALn7lvgoen_1ba3R2brAgGx6E
|
|
|
23
23
|
ws_bom_robot_app/llm/models/feedback.py,sha256=zh1jLqPRLzNlxInkCMoiJbfSu0-tiOEYHM7FhC46PkM,1692
|
|
24
24
|
ws_bom_robot_app/llm/models/kb.py,sha256=oVSw6_dmNxikAHrPqcfxDXz9M0ezLIYuxpgvzfs_Now,9514
|
|
25
25
|
ws_bom_robot_app/llm/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
ws_bom_robot_app/llm/providers/llm_manager.py,sha256=
|
|
26
|
+
ws_bom_robot_app/llm/providers/llm_manager.py,sha256=kxqzYtgpdmvwA6Ku_GVIkRkLGR2DsLlSnjZGM3_V8zQ,11912
|
|
27
27
|
ws_bom_robot_app/llm/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
ws_bom_robot_app/llm/tools/tool_builder.py,sha256=QTRG1c-EnH4APP10IyfZxEkqK9KitUsutXUvDRKeAhU,3224
|
|
29
29
|
ws_bom_robot_app/llm/tools/tool_manager.py,sha256=1IgRXxdB7DU3gbIlfT_aMUWZyWuanFTAFwu3VaYKxfE,14990
|
|
@@ -56,17 +56,18 @@ ws_bom_robot_app/llm/vector_store/integration/gcs.py,sha256=fFDVDUR6eNB7FVTzDSEp
|
|
|
56
56
|
ws_bom_robot_app/llm/vector_store/integration/github.py,sha256=18PO30AZcgTn6PHhid3MwImVAdmKBNkr0kmAPgOetGw,2663
|
|
57
57
|
ws_bom_robot_app/llm/vector_store/integration/googledrive.py,sha256=R6hr8iEgrR3QMOzIj5jY6w1x8pZ1LGdh4xM_q7g_ttc,3738
|
|
58
58
|
ws_bom_robot_app/llm/vector_store/integration/jira.py,sha256=Sf6nPongo53vG4jhlzFQZj3foNzvUvAZFr7pXAJfZvM,5795
|
|
59
|
-
ws_bom_robot_app/llm/vector_store/integration/manager.py,sha256=
|
|
59
|
+
ws_bom_robot_app/llm/vector_store/integration/manager.py,sha256=2te3yYjXHW1n5OWF3-Z3uS18RVudJeLwMjQ6d0Rn_pc,1793
|
|
60
60
|
ws_bom_robot_app/llm/vector_store/integration/s3.py,sha256=3kh-VmH84IW7DdSLvOk6td1VBJ9aohlVJsk5F3cYj0U,3320
|
|
61
61
|
ws_bom_robot_app/llm/vector_store/integration/sftp.py,sha256=WNzjjS1EUykgFB-8e7QkecSa1r1jTJqKyGzR25uJCtM,2848
|
|
62
62
|
ws_bom_robot_app/llm/vector_store/integration/sharepoint.py,sha256=zqqn-6qPrK50Phch4nZHJTgaPyPkGe7W2InGL_Ru6vE,5376
|
|
63
63
|
ws_bom_robot_app/llm/vector_store/integration/sitemap.py,sha256=g0TIRZ2qIpEuVCZ9Bn0MvvxYZtU3wptnTEjoKNZyBAg,5019
|
|
64
64
|
ws_bom_robot_app/llm/vector_store/integration/slack.py,sha256=FMjESXm2QetFXI6i8epze7Kbbu22fV8CVaxb71AHnJ8,2572
|
|
65
|
+
ws_bom_robot_app/llm/vector_store/integration/thron.py,sha256=gBDGYYfe7ZkQpYS2ExF5dZP4HlHoBz03nYNYEvrbR0w,4987
|
|
65
66
|
ws_bom_robot_app/llm/vector_store/loader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
67
|
ws_bom_robot_app/llm/vector_store/loader/base.py,sha256=L_ugekNuAq0N9O-24wtlHSNHkqSeD-KsJrfGt_FX9Oc,5340
|
|
67
68
|
ws_bom_robot_app/llm/vector_store/loader/docling.py,sha256=yP0zgXLeFAlByaYuj-6cYariuknckrFds0dxdRcnVz8,3456
|
|
68
69
|
ws_bom_robot_app/llm/vector_store/loader/json_loader.py,sha256=qo9ejRZyKv_k6jnGgXnu1W5uqsMMtgqK_uvPpZQ0p74,833
|
|
69
|
-
ws_bom_robot_app-0.0.
|
|
70
|
-
ws_bom_robot_app-0.0.
|
|
71
|
-
ws_bom_robot_app-0.0.
|
|
72
|
-
ws_bom_robot_app-0.0.
|
|
70
|
+
ws_bom_robot_app-0.0.70.dist-info/METADATA,sha256=pbaTKhPvzf48buZ4eFxNd5c4F6u2YzP5QvCxQX_8QsI,8571
|
|
71
|
+
ws_bom_robot_app-0.0.70.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
72
|
+
ws_bom_robot_app-0.0.70.dist-info/top_level.txt,sha256=Yl0akyHVbynsBX_N7wx3H3ZTkcMLjYyLJs5zBMDAKcM,17
|
|
73
|
+
ws_bom_robot_app-0.0.70.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|