ws-bom-robot-app 0.0.33__tar.gz → 0.0.35__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.
Files changed (72) hide show
  1. {ws_bom_robot_app-0.0.33/ws_bom_robot_app.egg-info → ws_bom_robot_app-0.0.35}/PKG-INFO +25 -12
  2. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/README.md +8 -3
  3. ws_bom_robot_app-0.0.35/requirements.txt +48 -0
  4. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/setup.py +1 -1
  5. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/config.py +10 -1
  6. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/agent_description.py +123 -124
  7. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/agent_handler.py +180 -167
  8. ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/agent_lcel.py +54 -0
  9. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/api.py +33 -21
  10. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/defaut_prompt.py +15 -9
  11. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/main.py +109 -102
  12. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/models/api.py +55 -7
  13. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/models/kb.py +11 -2
  14. ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/providers/llm_manager.py +174 -0
  15. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/settings.py +4 -4
  16. ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/tools/models/main.py +9 -0
  17. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/tools/tool_builder.py +23 -19
  18. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/tools/tool_manager.py +133 -101
  19. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/tools/utils.py +25 -25
  20. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/utils/agent_utils.py +17 -16
  21. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/utils/download.py +79 -79
  22. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/utils/print.py +29 -29
  23. ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/utils/secrets.py +26 -0
  24. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/generator.py +137 -137
  25. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/base.py +12 -1
  26. ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/vector_store/loader/__init__.py +0 -0
  27. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/loader/base.py +6 -5
  28. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/loader/docling.py +27 -6
  29. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/loader/json_loader.py +25 -25
  30. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/main.py +7 -2
  31. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35/ws_bom_robot_app.egg-info}/PKG-INFO +25 -12
  32. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app.egg-info/SOURCES.txt +3 -1
  33. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app.egg-info/requires.txt +16 -8
  34. ws_bom_robot_app-0.0.33/requirements.txt +0 -45
  35. ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/agent_lcel.py +0 -64
  36. ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/tools/models/main.py +0 -7
  37. ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/utils/faiss_helper.py +0 -127
  38. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/MANIFEST.in +0 -0
  39. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/pyproject.toml +0 -0
  40. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/setup.cfg +0 -0
  41. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/__init__.py +0 -0
  42. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/auth.py +0 -0
  43. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/cron_manager.py +0 -0
  44. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/__init__.py +0 -0
  45. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/models/__init__.py +0 -0
  46. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/models/base.py +0 -0
  47. {ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/tools → ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/providers}/__init__.py +0 -0
  48. {ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/tools/models → ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/tools}/__init__.py +0 -0
  49. {ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/utils → ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/tools/models}/__init__.py +0 -0
  50. {ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/vector_store → ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/utils}/__init__.py +0 -0
  51. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/utils/chunker.py +0 -0
  52. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/utils/kb.py +0 -0
  53. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/utils/webhooks.py +0 -0
  54. {ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/vector_store/integration → ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/vector_store}/__init__.py +0 -0
  55. {ws_bom_robot_app-0.0.33/ws_bom_robot_app/llm/vector_store/loader → ws_bom_robot_app-0.0.35/ws_bom_robot_app/llm/vector_store/integration}/__init__.py +0 -0
  56. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/azure.py +0 -0
  57. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/confluence.py +0 -0
  58. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/dropbox.py +0 -0
  59. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/gcs.py +0 -0
  60. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/github.py +0 -0
  61. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/googledrive.py +0 -0
  62. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/jira.py +0 -0
  63. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/manager.py +0 -0
  64. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/s3.py +0 -0
  65. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/sftp.py +0 -0
  66. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/sharepoint.py +0 -0
  67. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/sitemap.py +0 -0
  68. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/llm/vector_store/integration/slack.py +0 -0
  69. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/task_manager.py +0 -0
  70. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app/util.py +0 -0
  71. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app.egg-info/dependency_links.txt +0 -0
  72. {ws_bom_robot_app-0.0.33 → ws_bom_robot_app-0.0.35}/ws_bom_robot_app.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ws_bom_robot_app
3
- Version: 0.0.33
3
+ Version: 0.0.35
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
@@ -13,17 +13,25 @@ Description-Content-Type: text/markdown
13
13
  Requires-Dist: standardwebhooks==1.0.0
14
14
  Requires-Dist: apscheduler==3.11.0
15
15
  Requires-Dist: aiofiles==24.1.0
16
- Requires-Dist: pydantic==2.10.5
16
+ Requires-Dist: pydantic==2.10.6
17
17
  Requires-Dist: pydantic-settings==2.7.1
18
- Requires-Dist: fastapi[standard]==0.115.6
19
- Requires-Dist: langchain==0.3.14
20
- Requires-Dist: langchain-openai==0.3.0
21
- Requires-Dist: langchain-community==0.3.14
22
- Requires-Dist: langchain-core==0.3.29
18
+ Requires-Dist: fastapi[standard]==0.115.8
19
+ Requires-Dist: langchain==0.3.18
20
+ Requires-Dist: langchain-community==0.3.17
21
+ Requires-Dist: langchain-core==0.3.34
22
+ Requires-Dist: langchain-openai==0.3.5
23
+ Requires-Dist: langchain-anthropic==0.3.6
24
+ Requires-Dist: langchain-google-genai==2.0.7
25
+ Requires-Dist: langchain-google-vertexai==2.0.13
23
26
  Requires-Dist: faiss-cpu==1.9.0
24
- Requires-Dist: unstructured==0.16.13
27
+ Requires-Dist: chromadb==0.6.3
28
+ Requires-Dist: langchain_chroma==0.2.1
29
+ Requires-Dist: fastembed==0.5.1
30
+ Requires-Dist: langchain-qdrant==0.2.0
31
+ Requires-Dist: lark==1.2.2
32
+ Requires-Dist: unstructured==0.16.17
25
33
  Requires-Dist: unstructured[image]
26
- Requires-Dist: unstructured-ingest==0.3.14
34
+ Requires-Dist: unstructured-ingest==0.4.6
27
35
  Requires-Dist: unstructured-ingest[azure]
28
36
  Requires-Dist: unstructured-ingest[confluence]
29
37
  Requires-Dist: unstructured-ingest[dropbox]
@@ -185,6 +193,7 @@ py -m pip install --upgrade setuptools build twine streamlit
185
193
  ### 🪛 build
186
194
 
187
195
  ```pwsh
196
+ if (Test-Path ./dist) {rm ./dist -r -force}; `
188
197
  py -m build && twine check dist/*
189
198
  ```
190
199
 
@@ -217,14 +226,18 @@ prospector ./ws_bom_robot_app -t pyroma
217
226
  lauch pytest
218
227
 
219
228
  ```pwsh
220
- !py -m pip install -U pytest pytest-asyncio pytest-mock pytest-cov
229
+ !py -m pip install -U pytest pytest-asyncio pytest-mock pytest-cov pyclean
230
+ # clean cache if needed
231
+ # pyclean --verbose .
221
232
  pytest --cov=ws_bom_robot_app --log-cli-level=info
233
+ # directory
234
+ # pytest --cov=ws_bom_robot_app --log-cli-level=info ./tests/app/llm/vector_store/db
222
235
  ```
223
236
 
224
237
  launch debugger
225
238
 
226
239
  ```pwsh
227
- streamlit run debugger.py --server.port 6002
240
+ streamlit run debugger.py --server.port 6011
228
241
  ```
229
242
 
230
243
  dockerize base image
@@ -242,7 +255,7 @@ dockerize app from src
242
255
 
243
256
  ```pwsh
244
257
  docker build -f Dockerfile-src -t ws-bom-robot-app:src .
245
- docker run --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)/tests:/app/tests" -v "$(pwd)/tmp:/tmp" -p 6001:6001 ws-bom-robot-app:src
258
+ docker run --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)/tests:/app/tests" -v "$(pwd)/tmp:/tmp" -p 6002:6001 ws-bom-robot-app:src
246
259
  ```
247
260
 
248
261
  ### ✈️ publish
@@ -135,6 +135,7 @@ py -m pip install --upgrade setuptools build twine streamlit
135
135
  ### 🪛 build
136
136
 
137
137
  ```pwsh
138
+ if (Test-Path ./dist) {rm ./dist -r -force}; `
138
139
  py -m build && twine check dist/*
139
140
  ```
140
141
 
@@ -167,14 +168,18 @@ prospector ./ws_bom_robot_app -t pyroma
167
168
  lauch pytest
168
169
 
169
170
  ```pwsh
170
- !py -m pip install -U pytest pytest-asyncio pytest-mock pytest-cov
171
+ !py -m pip install -U pytest pytest-asyncio pytest-mock pytest-cov pyclean
172
+ # clean cache if needed
173
+ # pyclean --verbose .
171
174
  pytest --cov=ws_bom_robot_app --log-cli-level=info
175
+ # directory
176
+ # pytest --cov=ws_bom_robot_app --log-cli-level=info ./tests/app/llm/vector_store/db
172
177
  ```
173
178
 
174
179
  launch debugger
175
180
 
176
181
  ```pwsh
177
- streamlit run debugger.py --server.port 6002
182
+ streamlit run debugger.py --server.port 6011
178
183
  ```
179
184
 
180
185
  dockerize base image
@@ -192,7 +197,7 @@ dockerize app from src
192
197
 
193
198
  ```pwsh
194
199
  docker build -f Dockerfile-src -t ws-bom-robot-app:src .
195
- docker run --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)/tests:/app/tests" -v "$(pwd)/tmp:/tmp" -p 6001:6001 ws-bom-robot-app:src
200
+ docker run --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)/tests:/app/tests" -v "$(pwd)/tmp:/tmp" -p 6002:6001 ws-bom-robot-app:src
196
201
  ```
197
202
 
198
203
  ### ✈️ publish
@@ -0,0 +1,48 @@
1
+ #app
2
+ standardwebhooks==1.0.0
3
+ apscheduler==3.11.0
4
+ aiofiles==24.1.0
5
+ pydantic==2.10.6
6
+ pydantic-settings==2.7.1
7
+ fastapi[standard]==0.115.8
8
+
9
+ #framework
10
+ langchain==0.3.18
11
+ langchain-community==0.3.17
12
+ langchain-core==0.3.34
13
+ langchain-openai==0.3.5
14
+ langchain-anthropic==0.3.6 #issue get_models() from 0.3.7
15
+ langchain-google-genai==2.0.7 #waiting for new release: https://github.com/langchain-ai/langchain-google/issues/711
16
+ langchain-google-vertexai==2.0.13
17
+
18
+ #vector DB
19
+ faiss-cpu==1.9.0
20
+ chromadb==0.6.3
21
+ langchain_chroma==0.2.1
22
+ fastembed==0.5.1 #qdrant sparse embedding
23
+ langchain-qdrant==0.2.0
24
+ lark==1.2.2 #self-query retriever
25
+
26
+
27
+ #loaders
28
+ unstructured==0.16.17
29
+ unstructured[image]
30
+ unstructured-ingest==0.4.6
31
+ unstructured-ingest[azure]
32
+ unstructured-ingest[confluence]
33
+ unstructured-ingest[dropbox]
34
+ unstructured-ingest[gcs]
35
+ unstructured-ingest[github]
36
+ unstructured-ingest[google_drive]
37
+ unstructured-ingest[jira]
38
+ unstructured-ingest[s3]
39
+ unstructured-ingest[sftp]
40
+ unstructured-ingest[sharepoint]
41
+ unstructured-ingest[slack]
42
+ html5lib==1.1 #beautifulsoup4 parser
43
+
44
+ #integrations
45
+ markdownify==0.14.1 #sitemap
46
+
47
+ #telemetry
48
+ nebuly==0.3.36
@@ -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.33",
7
+ version="0.0.35",
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",
@@ -20,12 +20,21 @@ class Settings(BaseSettings):
20
20
  robot_cms_auth: str = ''
21
21
  robot_cms_db_folder: str = 'llmVectorDb'
22
22
  robot_cms_kb_folder: str ='llmKbFile'
23
- robot_debugger_openai_key: str = ''
23
+ robot_debugger_llm_provider: str = 'openai'
24
+ robot_debugger_llm_model: str = 'gpt-4o'
25
+ robot_debugger_llm_key: str = ''
26
+ robot_debugger_embedding_key: str = ''
27
+ OPENAI_API_KEY: str = '' # used for dall-e api
28
+ GOOGLE_APPLICATION_CREDENTIALS: str = './.secrets/google.json' # path to google credentials iam file
24
29
  model_config = ConfigDict(
25
30
  env_file='./.env',
26
31
  extra='ignore',
27
32
  case_sensitive=False
28
33
  )
34
+ def __init__(self, **kwargs):
35
+ super().__init__(**kwargs)
36
+ os.environ["OPENAI_API_KEY"] = self.OPENAI_API_KEY
37
+ os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = self.GOOGLE_APPLICATION_CREDENTIALS
29
38
 
30
39
  class RuntimeOptions(BaseModel):
31
40
  @staticmethod
@@ -1,124 +1,123 @@
1
- import json, requests, re
2
- from typing import Any
3
- from abc import ABC, abstractmethod
4
- from langchain_openai import ChatOpenAI
5
- from langchain_core.prompts import ChatPromptTemplate
6
- from langchain_core.messages import AIMessage
7
- from langchain_core.runnables import RunnableSerializable
8
- from langchain_core.runnables import RunnableLambda
9
- from bs4 import BeautifulSoup
10
- from ws_bom_robot_app.llm.models.api import LlmRules
11
- from ws_bom_robot_app.llm.utils.agent_utils import get_rules
12
-
13
- # SafeDict helper class
14
- class SafeDict(dict):
15
- def __missing__(self, key):
16
- return ''
17
-
18
- # Strategy Interface
19
- class AgentDescriptorStrategy(ABC):
20
- @abstractmethod
21
- def enrich_prompt(self, prompt: str, input: dict) -> str:
22
- pass
23
-
24
- @abstractmethod
25
- def rule_input(self, input: dict) -> str:
26
- pass
27
-
28
- # Concrete Strategy for Default Agent
29
- class DefaultAgentDescriptor(AgentDescriptorStrategy):
30
- def enrich_prompt(self, prompt: str, input: dict) -> str:
31
- # Default enrichment logic (could be minimal or no-op)
32
- return prompt.format_map(SafeDict(input))
33
-
34
- def rule_input(self, input: dict) -> str:
35
- return input.get('content', "")
36
-
37
- # Concrete Strategy for URL2Text Agent
38
- class URL2TextAgentDescriptor(AgentDescriptorStrategy):
39
- def enrich_prompt(self, prompt: str, input: dict) -> str:
40
- input["context"] = self._get_page_text(input)
41
- return prompt.format_map(SafeDict(input))
42
-
43
- def rule_input(self, input: dict) -> str:
44
- return input.get('context', "")
45
-
46
- def _get_page_text(self, input: dict) -> str:
47
- url = input.get("content", "")
48
- exclusions = input.get("exclude", {})
49
- response = requests.get(url)
50
- response.raise_for_status()
51
- soup = BeautifulSoup(response.content, 'html5lib')
52
- classes_to_exclude = exclusions.get("classes", [])
53
- ids_to_exclude = exclusions.get("ids", [])
54
- for class_name in classes_to_exclude:
55
- for element in soup.find_all(class_=class_name):
56
- element.extract()
57
- for id_name in ids_to_exclude:
58
- for element in soup.find_all(id=id_name):
59
- element.extract()
60
- for script in soup(["script", "noscript", "style", "head", "footer", "iframe"]):
61
- script.extract()
62
- return re.sub(' +', ' ', soup.get_text())
63
-
64
-
65
- class AgentDescriptor:
66
- # Dictionary to hold all agent strategies
67
- _list: dict[str,AgentDescriptorStrategy] = {
68
- "default": DefaultAgentDescriptor(),
69
- "url2text": URL2TextAgentDescriptor(),
70
- }
71
-
72
- # Functions to manage strategies
73
- @staticmethod
74
- def add_strategy(name: str, strategy: AgentDescriptorStrategy):
75
- """_summary_
76
- add a new strategy to the dictionary
77
- Args:
78
- name (str): name of the strategy, in lowercase
79
- strategy (AgentDescriptorStrategy): class implementing the strategy
80
- Examples:
81
- AgentDescriptor.add_strategy("custom_agent_descriptor", CustomAgentDescriptor())
82
- """
83
- AgentDescriptor._list[name.lower()] = strategy
84
-
85
- @staticmethod
86
- def get_strategy(name: str) -> AgentDescriptorStrategy:
87
- return AgentDescriptor._list.get(name.lower(), DefaultAgentDescriptor())
88
-
89
- def __init__(self, api_key: str, prompt: str, mode: str, rules: LlmRules = None):
90
- self.__prompt = prompt
91
- self.__llm = ChatOpenAI(model="gpt-4o", temperature=0, api_key=api_key) # type: ignore
92
- self.api_key = api_key
93
- self.rules= rules
94
- self.strategy = self.get_strategy(mode) # Selects the strategy from the dictionary
95
-
96
- async def __create_prompt(self, input_dict: dict):
97
- input_data = json.loads(input_dict.get("input", {}))
98
- system = self.strategy.enrich_prompt(self.__prompt, input_data)
99
- if self.rules:
100
- rule_input = self.strategy.rule_input(input_data)
101
- rules_prompt = await get_rules(self.rules,self.api_key, rule_input)
102
- system += rules_prompt
103
- return ChatPromptTemplate.from_messages(
104
- [
105
- ("system", system),
106
- ("user", input_data.get("content", ""))
107
- ]
108
- )
109
-
110
- def __create_agent_descriptor(self, content) -> RunnableSerializable[Any, Any]:
111
- content = json.loads(content)
112
- agent = (
113
- {
114
- "input": lambda x: x["input"],
115
- }
116
- | RunnableLambda(self.__create_prompt)
117
- | self.__llm
118
- )
119
- return agent
120
-
121
- async def run_agent(self, content) -> Any:
122
- agent_descriptor = self.__create_agent_descriptor(content)
123
- response: AIMessage = await agent_descriptor.ainvoke({"input": content})
124
- return response
1
+ import json, requests, re
2
+ from typing import Any
3
+ from abc import ABC, abstractmethod
4
+ from langchain_core.prompts import ChatPromptTemplate
5
+ from langchain_core.messages import AIMessage
6
+ from langchain_core.runnables import RunnableSerializable
7
+ from langchain_core.runnables import RunnableLambda
8
+ from bs4 import BeautifulSoup
9
+ from ws_bom_robot_app.llm.models.api import LlmRules
10
+ from ws_bom_robot_app.llm.providers.llm_manager import LlmInterface
11
+ from ws_bom_robot_app.llm.utils.agent_utils import get_rules
12
+
13
+ # SafeDict helper class
14
+ class SafeDict(dict):
15
+ def __missing__(self, key):
16
+ return ''
17
+
18
+ # Strategy Interface
19
+ class AgentDescriptorStrategy(ABC):
20
+ @abstractmethod
21
+ def enrich_prompt(self, prompt: str, input: dict) -> str:
22
+ pass
23
+
24
+ @abstractmethod
25
+ def rule_input(self, input: dict) -> str:
26
+ pass
27
+
28
+ # Concrete Strategy for Default Agent
29
+ class DefaultAgentDescriptor(AgentDescriptorStrategy):
30
+ def enrich_prompt(self, prompt: str, input: dict) -> str:
31
+ # Default enrichment logic (could be minimal or no-op)
32
+ return prompt.format_map(SafeDict(input))
33
+
34
+ def rule_input(self, input: dict) -> str:
35
+ return input.get('content', "")
36
+
37
+ # Concrete Strategy for URL2Text Agent
38
+ class URL2TextAgentDescriptor(AgentDescriptorStrategy):
39
+ def enrich_prompt(self, prompt: str, input: dict) -> str:
40
+ input["context"] = self._get_page_text(input)
41
+ return prompt.format_map(SafeDict(input))
42
+
43
+ def rule_input(self, input: dict) -> str:
44
+ return input.get('context', "")
45
+
46
+ def _get_page_text(self, input: dict) -> str:
47
+ url = input.get("content", "")
48
+ exclusions = input.get("exclude", {})
49
+ response = requests.get(url)
50
+ response.raise_for_status()
51
+ soup = BeautifulSoup(response.content, 'html5lib')
52
+ classes_to_exclude = exclusions.get("classes", [])
53
+ ids_to_exclude = exclusions.get("ids", [])
54
+ for class_name in classes_to_exclude:
55
+ for element in soup.find_all(class_=class_name):
56
+ element.extract()
57
+ for id_name in ids_to_exclude:
58
+ for element in soup.find_all(id=id_name):
59
+ element.extract()
60
+ for script in soup(["script", "noscript", "style", "head", "footer", "iframe"]):
61
+ script.extract()
62
+ return re.sub(' +', ' ', soup.get_text())
63
+
64
+
65
+ class AgentDescriptor:
66
+ # Dictionary to hold all agent strategies
67
+ _list: dict[str,AgentDescriptorStrategy] = {
68
+ "default": DefaultAgentDescriptor(),
69
+ "url2text": URL2TextAgentDescriptor(),
70
+ }
71
+
72
+ # Functions to manage strategies
73
+ @staticmethod
74
+ def add_strategy(name: str, strategy: AgentDescriptorStrategy):
75
+ """_summary_
76
+ add a new strategy to the dictionary
77
+ Args:
78
+ name (str): name of the strategy, in lowercase
79
+ strategy (AgentDescriptorStrategy): class implementing the strategy
80
+ Examples:
81
+ AgentDescriptor.add_strategy("custom_agent_descriptor", CustomAgentDescriptor())
82
+ """
83
+ AgentDescriptor._list[name.lower()] = strategy
84
+
85
+ @staticmethod
86
+ def get_strategy(name: str) -> AgentDescriptorStrategy:
87
+ return AgentDescriptor._list.get(name.lower(), DefaultAgentDescriptor())
88
+
89
+ def __init__(self, llm: LlmInterface, prompt: str, mode: str, rules: LlmRules = None):
90
+ self.__prompt = prompt
91
+ self.__llm = llm,
92
+ self.rules= rules
93
+ self.strategy = self.get_strategy(mode) # Selects the strategy from the dictionary
94
+
95
+ async def __create_prompt(self, input_dict: dict):
96
+ input_data = json.loads(input_dict.get("input", {}))
97
+ system = self.strategy.enrich_prompt(self.__prompt, input_data)
98
+ if self.rules:
99
+ rule_input = self.strategy.rule_input(input_data)
100
+ rules_prompt = await get_rules(self.__llm.get_embeddings(), self.rules, rule_input)
101
+ system += rules_prompt
102
+ return ChatPromptTemplate.from_messages(
103
+ [
104
+ ("system", system),
105
+ ("user", input_data.get("content", ""))
106
+ ]
107
+ )
108
+
109
+ def __create_agent_descriptor(self, content) -> RunnableSerializable[Any, Any]:
110
+ content = json.loads(content)
111
+ agent = (
112
+ {
113
+ "input": lambda x: x["input"],
114
+ }
115
+ | RunnableLambda(self.__create_prompt)
116
+ | self.__llm.get_llm()
117
+ )
118
+ return agent
119
+
120
+ async def run_agent(self, content) -> Any:
121
+ agent_descriptor = self.__create_agent_descriptor(content)
122
+ response: AIMessage = await agent_descriptor.ainvoke({"input": content})
123
+ return response