ws-bom-robot-app 0.0.61__tar.gz → 0.0.63__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 (81) hide show
  1. {ws_bom_robot_app-0.0.61/ws_bom_robot_app.egg-info → ws_bom_robot_app-0.0.63}/PKG-INFO +17 -17
  2. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/requirements.txt +18 -17
  3. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/setup.py +1 -1
  4. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/config.py +2 -3
  5. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/cron_manager.py +2 -2
  6. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/agent_handler.py +1 -1
  7. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/agent_lcel.py +1 -2
  8. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/api.py +12 -0
  9. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/feedbacks/feedback_manager.py +0 -8
  10. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/main.py +2 -0
  11. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/models/api.py +6 -0
  12. ws_bom_robot_app-0.0.63/ws_bom_robot_app/llm/tools/tool_builder.py +65 -0
  13. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/tools/tool_manager.py +73 -44
  14. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/agent.py +5 -5
  15. ws_bom_robot_app-0.0.63/ws_bom_robot_app/llm/utils/cms.py +77 -0
  16. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/task_manager.py +3 -1
  17. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/util.py +59 -20
  18. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63/ws_bom_robot_app.egg-info}/PKG-INFO +17 -17
  19. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app.egg-info/SOURCES.txt +1 -0
  20. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app.egg-info/requires.txt +16 -16
  21. ws_bom_robot_app-0.0.61/ws_bom_robot_app/llm/tools/tool_builder.py +0 -23
  22. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/MANIFEST.in +0 -0
  23. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/README.md +0 -0
  24. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/pyproject.toml +0 -0
  25. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/setup.cfg +0 -0
  26. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/__init__.py +0 -0
  27. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/auth.py +0 -0
  28. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/__init__.py +0 -0
  29. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/agent_context.py +0 -0
  30. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/agent_description.py +0 -0
  31. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/defaut_prompt.py +0 -0
  32. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/feedbacks/__init__.py +0 -0
  33. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/models/__init__.py +0 -0
  34. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/models/base.py +0 -0
  35. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/models/feedback.py +0 -0
  36. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/models/kb.py +0 -0
  37. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/nebuly_handler.py +0 -0
  38. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/providers/__init__.py +0 -0
  39. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/providers/llm_manager.py +0 -0
  40. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/settings.py +0 -0
  41. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/tools/__init__.py +0 -0
  42. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/tools/models/__init__.py +0 -0
  43. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/tools/models/main.py +0 -0
  44. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/tools/utils.py +0 -0
  45. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/__init__.py +0 -0
  46. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/chunker.py +0 -0
  47. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/download.py +0 -0
  48. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/kb.py +0 -0
  49. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/print.py +0 -0
  50. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/secrets.py +0 -0
  51. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/utils/webhooks.py +0 -0
  52. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/__init__.py +0 -0
  53. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/db/__init__.py +0 -0
  54. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/db/base.py +0 -0
  55. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/db/chroma.py +0 -0
  56. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/db/faiss.py +0 -0
  57. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/db/manager.py +0 -0
  58. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/db/qdrant.py +0 -0
  59. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/generator.py +0 -0
  60. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/__init__.py +0 -0
  61. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/azure.py +0 -0
  62. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/base.py +0 -0
  63. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/confluence.py +0 -0
  64. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/dropbox.py +0 -0
  65. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/gcs.py +0 -0
  66. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/github.py +0 -0
  67. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/googledrive.py +0 -0
  68. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/jira.py +0 -0
  69. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/manager.py +0 -0
  70. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/s3.py +0 -0
  71. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/sftp.py +0 -0
  72. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/sharepoint.py +0 -0
  73. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/sitemap.py +0 -0
  74. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/integration/slack.py +0 -0
  75. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/loader/__init__.py +0 -0
  76. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/loader/base.py +0 -0
  77. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/loader/docling.py +0 -0
  78. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/llm/vector_store/loader/json_loader.py +0 -0
  79. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app/main.py +0 -0
  80. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app.egg-info/dependency_links.txt +0 -0
  81. {ws_bom_robot_app-0.0.61 → ws_bom_robot_app-0.0.63}/ws_bom_robot_app.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ws_bom_robot_app
3
- Version: 0.0.61
3
+ Version: 0.0.63
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,24 +13,23 @@ 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.6
17
- Requires-Dist: pydantic-settings==2.7.1
18
- Requires-Dist: fastapi[standard]==0.115.8
16
+ Requires-Dist: pydantic==2.11.7
17
+ Requires-Dist: pydantic-settings==2.10.1
18
+ Requires-Dist: fastapi[standard]==0.115.14
19
19
  Requires-Dist: chevron==0.14.0
20
- Requires-Dist: trafilatura==2.0.0
21
- Requires-Dist: langchain==0.3.25
22
- Requires-Dist: langchain-community==0.3.24
23
- Requires-Dist: langchain-core==0.3.59
24
- Requires-Dist: langchain-openai==0.3.16
20
+ Requires-Dist: langchain==0.3.26
21
+ Requires-Dist: langchain-community==0.3.26
22
+ Requires-Dist: langchain-core==0.3.67
23
+ Requires-Dist: langchain-openai==0.3.27
25
24
  Requires-Dist: langchain-anthropic==0.3.6
26
25
  Requires-Dist: langchain-google-genai==2.0.7
27
- Requires-Dist: langchain-google-vertexai==2.0.13
28
- Requires-Dist: langchain-groq==0.3.2
29
- Requires-Dist: langchain-ollama==0.3.2
30
- Requires-Dist: faiss-cpu==1.9.0
31
- Requires-Dist: chromadb==0.6.3
32
- Requires-Dist: langchain_chroma==0.2.1
33
- Requires-Dist: fastembed==0.5.1
26
+ Requires-Dist: langchain-google-vertexai==2.0.27
27
+ Requires-Dist: langchain-groq==0.3.5
28
+ Requires-Dist: langchain-ollama==0.3.3
29
+ Requires-Dist: faiss-cpu==1.11.0
30
+ Requires-Dist: chromadb==1.0.13
31
+ Requires-Dist: langchain_chroma==0.2.4
32
+ Requires-Dist: fastembed==0.7.1
34
33
  Requires-Dist: langchain-qdrant==0.2.0
35
34
  Requires-Dist: lark==1.2.2
36
35
  Requires-Dist: unstructured==0.16.21
@@ -48,9 +47,10 @@ Requires-Dist: unstructured-ingest[sftp]
48
47
  Requires-Dist: unstructured-ingest[sharepoint]
49
48
  Requires-Dist: unstructured-ingest[slack]
50
49
  Requires-Dist: html5lib==1.1
51
- Requires-Dist: markdownify==0.14.1
50
+ Requires-Dist: markdownify==1.1.0
52
51
  Requires-Dist: duckduckgo-search==8.0.4
53
52
  Requires-Dist: langchain_google_community==2.0.7
53
+ Requires-Dist: trafilatura==2.0.0
54
54
  Dynamic: author
55
55
  Dynamic: author-email
56
56
  Dynamic: classifier
@@ -2,32 +2,30 @@
2
2
  standardwebhooks==1.0.0
3
3
  apscheduler==3.11.0
4
4
  aiofiles==24.1.0
5
- pydantic==2.10.6
6
- pydantic-settings==2.7.1
7
- fastapi[standard]==0.115.8
5
+ pydantic==2.11.7
6
+ pydantic-settings==2.10.1
7
+ fastapi[standard]==0.115.14
8
8
  chevron==0.14.0
9
- trafilatura==2.0.0
10
9
 
11
10
  #framework
12
- langchain==0.3.25
13
- langchain-community==0.3.24
14
- langchain-core==0.3.59
15
- langchain-openai==0.3.16
11
+ langchain==0.3.26
12
+ langchain-community==0.3.26
13
+ langchain-core==0.3.67
14
+ langchain-openai==0.3.27
16
15
  langchain-anthropic==0.3.6 #issue get_models() from 0.3.7
17
16
  langchain-google-genai==2.0.7 #waiting for new release: https://github.com/langchain-ai/langchain-google/issues/711
18
- langchain-google-vertexai==2.0.13
19
- langchain-groq==0.3.2
20
- langchain-ollama==0.3.2
17
+ langchain-google-vertexai==2.0.27
18
+ langchain-groq==0.3.5
19
+ langchain-ollama==0.3.3
21
20
 
22
21
  #vector DB
23
- faiss-cpu==1.9.0
24
- chromadb==0.6.3
25
- langchain_chroma==0.2.1
26
- fastembed==0.5.1 #qdrant sparse embedding
22
+ faiss-cpu==1.11.0
23
+ chromadb==1.0.13
24
+ langchain_chroma==0.2.4
25
+ fastembed==0.7.1 #qdrant sparse embedding
27
26
  langchain-qdrant==0.2.0
28
27
  lark==1.2.2 #self-query retriever
29
28
 
30
-
31
29
  #loaders
32
30
  unstructured==0.16.21
33
31
  unstructured[image]
@@ -46,6 +44,9 @@ unstructured-ingest[slack]
46
44
  html5lib==1.1 #beautifulsoup4 parser
47
45
 
48
46
  #integrations
49
- markdownify==0.14.1 #sitemap
47
+ markdownify==1.1.0 #sitemap
48
+
49
+ ##tools
50
50
  duckduckgo-search==8.0.4
51
51
  langchain_google_community==2.0.7
52
+ trafilatura==2.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.61",
7
+ version="0.0.63",
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",
@@ -4,6 +4,7 @@ from pydantic_settings import BaseSettings
4
4
  import os
5
5
 
6
6
  class Settings(BaseSettings):
7
+ USER_AGENT: str = 'ws-bom-robot'
7
8
  robot_env: str = 'local'
8
9
  robot_user: str = 'user'
9
10
  robot_password: str = 'password'
@@ -28,7 +29,6 @@ class Settings(BaseSettings):
28
29
  GOOGLE_API_KEY: str = ''
29
30
  NEBULY_API_URL: str =''
30
31
  GOOGLE_APPLICATION_CREDENTIALS: str = '' # path to google credentials iam file, e.d. ./.secrets/google-credentials.json
31
- TAVILY_API_KEY: str = '' #TODO DELETE
32
32
  model_config = ConfigDict(
33
33
  env_file='./.env',
34
34
  extra='ignore',
@@ -36,6 +36,7 @@ class Settings(BaseSettings):
36
36
  )
37
37
  def __init__(self, **kwargs):
38
38
  super().__init__(**kwargs)
39
+ os.environ["USER_AGENT"] = self.USER_AGENT
39
40
  os.environ["OPENAI_API_KEY"] = self.OPENAI_API_KEY
40
41
  os.environ["OLLAMA_API_URL"] = self.OLLAMA_API_URL
41
42
  os.environ["ANTHROPIC_API_KEY"] = self.ANTHROPIC_API_KEY
@@ -44,8 +45,6 @@ class Settings(BaseSettings):
44
45
  os.environ["GOOGLE_API_KEY"] = self.GOOGLE_API_KEY
45
46
  os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = self.GOOGLE_APPLICATION_CREDENTIALS
46
47
  os.environ["NEBULY_API_URL"] = self.NEBULY_API_URL
47
- # TODO DELETE
48
- os.environ["TAVILY_API_KEY"] = self.TAVILY_API_KEY
49
48
 
50
49
  class RuntimeOptions(BaseModel):
51
50
  @staticmethod
@@ -59,14 +59,14 @@ class CronManager:
59
59
  Job('cleanup-task',task_manager.cleanup_task, interval=5 * 60),
60
60
  Job('cleanup-data',kb_cleanup_data_file, interval=180 * 60),
61
61
  ]
62
- def __get_jobstore_strategy() -> JobstoreStrategy:
62
+ def __get_jobstore_strategy(self) -> JobstoreStrategy:
63
63
  if True or config.runtime_options().is_multi_process:
64
64
  return MemoryJobstoreStrategy()
65
65
  return PersistentJobstoreStrategy()
66
66
  def __init__(self, strategy: JobstoreStrategy = None, enable_defaults: bool = True):
67
67
  self.enable_defaults = enable_defaults
68
68
  if strategy is None:
69
- strategy = CronManager.__get_jobstore_strategy()
69
+ strategy = self.__get_jobstore_strategy()
70
70
  jobstores = strategy.get_jobstore()
71
71
  self.scheduler: BackgroundScheduler = BackgroundScheduler(jobstores=jobstores)
72
72
  self.__scheduler_is_running = False
@@ -60,7 +60,7 @@ class AgentHandler(AsyncCallbackHandler):
60
60
  tags: Optional[List[str]] = None,
61
61
  **kwargs: Any,
62
62
  ) -> None:
63
- if token and "llm_chain" not in tags:
63
+ if token and "llm_chain" not in (tags or []):
64
64
  token = _parse_token(self.llm,token)
65
65
  if token:
66
66
  self.stream_buffer += token # append new data to pending buffer
@@ -24,8 +24,7 @@ class AgentLcel:
24
24
 
25
25
  async def __create_prompt(self, input: dict) -> ChatPromptTemplate:
26
26
  message : LlmMessage = input[self.memory_key][-1]
27
- input = message.content
28
- rules_prompt = await get_rules(self.embeddings, self.rules, input) if self.rules else ""
27
+ rules_prompt = await get_rules(self.embeddings, self.rules, message.content) if self.rules else ""
29
28
  system = default_prompt + (tool_prompt(render_text_description(self.__tools)) if len(self.__tools)>0 else "") + self.sys_message + rules_prompt
30
29
  return ChatPromptTemplate([
31
30
  ("system", system),
@@ -26,6 +26,18 @@ def _stream_headers(rq: StreamRequest) -> Mapping[str, str]:
26
26
  "X-thread-id": rq.msg_id or str(uuid4()),
27
27
  "X-msg-id": rq.msg_id or str(uuid4()),
28
28
  }
29
+
30
+ @router.get("/cms/app", tags=["cms"])
31
+ async def cms_apps():
32
+ from ws_bom_robot_app.llm.utils.cms import get_apps
33
+ return await get_apps()
34
+
35
+ @router.get("/cms/app/{id}", tags=["cms"])
36
+ async def cms_app_by_id(id: str):
37
+ from ws_bom_robot_app.llm.utils.cms import get_app_by_id
38
+ return await get_app_by_id(id)
39
+
40
+
29
41
  @router.post("/stream")
30
42
  async def _stream(rq: StreamRequest, ctx: Request) -> StreamingResponse:
31
43
  return StreamingResponse(stream(rq, ctx), media_type="application/json", headers=_stream_headers(rq))
@@ -59,14 +59,6 @@ class NebulyFeedback(FeedbackInterface):
59
59
  raise Exception(f"Error sending feedback: {response.status_code} - {response.text}")
60
60
  return response.text
61
61
 
62
- def __buildAiMessage(self) -> str:
63
- message = eval(self.config.message_output)
64
- message_output = ""
65
- for chunk in message:
66
- if isinstance(chunk, dict) and "text" in chunk:
67
- message_output += chunk["text"]
68
- return message_output
69
-
70
62
  class FeedbackManager:
71
63
  #class variables (static)
72
64
  _list: dict[str,FeedbackInterface] = {
@@ -89,6 +89,8 @@ async def __stream(rq: StreamRequest, ctx: Request, queue: Queue,formatted: bool
89
89
  callbacks.append(trace)
90
90
 
91
91
  __llm: LlmInterface =rq.get_llm()
92
+ for tool in rq.app_tools:
93
+ tool.thread_id = rq.thread_id
92
94
  processor = AgentLcel(
93
95
  llm=__llm,
94
96
  sys_message=rq.system_message,
@@ -36,6 +36,7 @@ class LlmAppToolDbSettings(BaseModel):
36
36
 
37
37
  class LlmAppTool(BaseModel):
38
38
  id: Optional[str] = None
39
+ thread_id: Optional[str] = Field(None, validation_alias=AliasChoices("threadId","thread_id"))
39
40
  name: str
40
41
  description: Optional[str] = None
41
42
  type: str
@@ -53,6 +54,11 @@ class LlmAppTool(BaseModel):
53
54
  vector_type: Optional[str] = Field('faiss', validation_alias=AliasChoices("vectorDbType","vector_type"))
54
55
  vector_db: Optional[str] = Field(None, validation_alias=AliasChoices("vectorDbFile","vector_db"))
55
56
  is_active: Optional[bool] = Field(True, validation_alias=AliasChoices("isActive","is_active"))
57
+ def secrets_to_dict(self) -> Dict[str, str]:
58
+ _secrets = {}
59
+ for d in self.secrets or []:
60
+ _secrets[d.get("secretId")] = d.get("secretValue")
61
+ return _secrets
56
62
  def get_vector_filtering(self) -> Optional[Tuple[str, List[AttributeInfo]]]:
57
63
  _description = None
58
64
  _metadata = None
@@ -0,0 +1,65 @@
1
+ import asyncio
2
+ from asyncio import Queue
3
+ from langchain.tools import StructuredTool
4
+ from ws_bom_robot_app.llm.models.api import LlmAppTool
5
+ from ws_bom_robot_app.llm.tools.tool_manager import ToolManager
6
+ from ws_bom_robot_app.llm.providers.llm_manager import LlmInterface
7
+
8
+ async def __process_proxy_tool(proxy_tool: LlmAppTool) -> LlmAppTool | None:
9
+ import os
10
+ from ws_bom_robot_app.llm.utils.cms import CmsApp, get_app_by_id
11
+ from ws_bom_robot_app.config import config
12
+ try:
13
+ secrets = proxy_tool.secrets_to_dict()
14
+ app_id = secrets.get("appId")
15
+ if not app_id:
16
+ raise ValueError("Tool configuration is invalid. 'appId' is required.")
17
+ app: CmsApp = await get_app_by_id(app_id)
18
+ if not app:
19
+ raise ValueError(f"App with id {app_id} not found.")
20
+ tool_id = secrets.get("toolId")
21
+ tool = next((t for t in app.app_tools if t.id == tool_id), None)
22
+ if not tool:
23
+ raise ValueError(f"Tool with function_id {tool_id} not found in app {app.name}.")
24
+ #override derived tool with proxy tool props
25
+ tool.name = proxy_tool.name if proxy_tool.name else tool.name
26
+ tool.description = proxy_tool.description if proxy_tool.description else tool.description
27
+ tool.function_id = proxy_tool.function_id if proxy_tool.function_id else tool.function_id
28
+ tool.function_description = proxy_tool.function_description if proxy_tool.function_description else tool.function_description
29
+ #normalize vector_db
30
+ if tool.vector_db:
31
+ tool.vector_db = os.path.join(
32
+ os.path.join(config.robot_data_folder,config.robot_data_db_folder,config.robot_data_db_folder_store),
33
+ os.path.splitext(os.path.basename(tool.vector_db))[0]) if tool.vector_db else None
34
+ return tool
35
+ except Exception as e:
36
+ print(f"[!] Error in proxy_app_tool: {e}")
37
+ return None
38
+
39
+ def get_structured_tools(llm: LlmInterface, tools: list[LlmAppTool], callbacks:list, queue: Queue) -> list[StructuredTool]:
40
+ _structured_tools :list[StructuredTool] = []
41
+ for tool in [tool for tool in tools if tool.is_active]:
42
+ if tool.function_name == "proxy_app_tool":
43
+ # override the tool
44
+ loop = asyncio.get_event_loop()
45
+ if loop.is_running():
46
+ import nest_asyncio
47
+ nest_asyncio.apply()
48
+ processed_tool = loop.run_until_complete(__process_proxy_tool(tool))
49
+ if processed_tool is None:
50
+ continue
51
+ tool = processed_tool
52
+ if _tool_config := ToolManager._list.get(tool.function_name):
53
+ _tool_instance = ToolManager(llm, tool, callbacks, queue)
54
+ _structured_tool = StructuredTool.from_function(
55
+ coroutine=_tool_instance.get_coroutine(),
56
+ name=tool.function_id if tool.function_id else tool.function_name,
57
+ description=tool.function_description,
58
+ args_schema=_tool_config.model
59
+ #infer_schema=True,
60
+ #parse_docstring=True,
61
+ #error_on_invalid_docstring=True
62
+ )
63
+ _structured_tool.tags = [tool.function_id if tool.function_id else tool.function_name]
64
+ _structured_tools.append(_structured_tool)
65
+ return _structured_tools
@@ -1,14 +1,14 @@
1
1
  from asyncio import Queue
2
+ import aiohttp
2
3
  from typing import Optional, Type, Callable
3
- from ws_bom_robot_app.llm.models.api import LlmAppTool
4
+ from ws_bom_robot_app.config import config
5
+ from ws_bom_robot_app.llm.models.api import LlmApp,LlmAppTool
4
6
  from ws_bom_robot_app.llm.providers.llm_manager import LlmInterface
7
+ from ws_bom_robot_app.llm.utils.cms import CmsApp, get_app_by_id
5
8
  from ws_bom_robot_app.llm.vector_store.db.manager import VectorDbManager
6
9
  from ws_bom_robot_app.llm.tools.utils import getRandomWaitingMessage, translate_text
7
10
  from ws_bom_robot_app.llm.tools.models.main import NoopInput,DocumentRetrieverInput,ImageGeneratorInput,LlmChainInput,SearchOnlineInput,EmailSenderInput
8
11
  from pydantic import BaseModel, ConfigDict
9
- import smtplib
10
- from email.mime.multipart import MIMEMultipart
11
- from email.mime.text import MIMEText
12
12
 
13
13
  class ToolConfig(BaseModel):
14
14
  function: Callable
@@ -165,23 +165,50 @@ class ToolManager:
165
165
  result = await chain.ainvoke({"input": input})
166
166
  return result
167
167
 
168
+ async def proxy_app_chat(self, query: str) -> str | None:
169
+ secrets = self.app_tool.secrets_to_dict()
170
+ app_id = secrets.get("appId")
171
+ if not app_id:
172
+ raise ValueError("Tool configuration is invalid. 'appId' is required.")
173
+ app: CmsApp = await get_app_by_id(app_id)
174
+ if not app:
175
+ raise ValueError(f"App with id {app_id} not found.")
176
+ url = f"{config.robot_cms_host}/api/llm/message?locale=en&raw=true"
177
+ auth = config.robot_cms_auth
178
+ headers = {"Authorization": auth} if auth else {}
179
+ async with aiohttp.ClientSession() as session:
180
+ data = {
181
+ "appKey": app.credentials.app_key,
182
+ "apiKey": app.credentials.api_key,
183
+ "messages": [
184
+ {
185
+ "role": "user",
186
+ "content": query
187
+ }
188
+ ]
189
+ }
190
+ async with session.post(url, json=data, headers=headers) as response:
191
+ if response.status == 200:
192
+ return await response.text()
193
+ else:
194
+ raise ValueError(f"Error fetching chat response: {response.status}")
195
+ return None
168
196
 
169
- async def search_online(self, query: str):
197
+ async def proxy_app_tool(self) -> None:
198
+ return None
199
+
200
+ async def _fetch_urls(self, urls: list[str]) -> list[dict]:
201
+ import aiohttp, asyncio
170
202
  from ws_bom_robot_app.llm.tools.utils import fetch_page, extract_content_with_trafilatura
171
- from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
172
- import aiohttp, asyncio, ast
173
- # Wrapper DuckDuckGo
174
- search = DuckDuckGoSearchAPIWrapper(max_results=10)
175
- try:
176
- raw_results = search.results(query, max_results=10)
177
- except Exception as e:
178
- print(f"[!] Errore ricerca: {e}")
179
- urls = [r["link"] for r in raw_results]
203
+ if not urls:
204
+ return []
180
205
  async with aiohttp.ClientSession() as session:
181
206
  tasks = [fetch_page(session, url) for url in urls]
182
- responses = await asyncio.gather(*tasks)
207
+ responses = await asyncio.gather(*tasks, return_exceptions=True)
183
208
  final_results = []
184
209
  for item in responses:
210
+ if isinstance(item, Exception):
211
+ continue
185
212
  url = item["url"]
186
213
  html = item["html"]
187
214
  if html:
@@ -194,13 +221,20 @@ class ToolManager:
194
221
  final_results.append({"url": url, "content": "Page not found"})
195
222
  return final_results
196
223
 
197
- async def search_online_google(self, query: str):
224
+ async def search_online(self, query: str) -> list[dict]:
225
+ from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
226
+ # Wrapper DuckDuckGo
227
+ search = DuckDuckGoSearchAPIWrapper(max_results=10)
228
+ try:
229
+ raw_results = search.results(query, max_results=10)
230
+ except Exception as e:
231
+ return f"[!] Errore ricerca: {e}"
232
+ urls = [r["link"] for r in raw_results]
233
+ return await self._fetch_urls(urls)
234
+
235
+ async def search_online_google(self, query: str) -> list[dict]:
198
236
  from langchain_google_community import GoogleSearchAPIWrapper
199
- from ws_bom_robot_app.llm.tools.utils import fetch_page, extract_content_with_trafilatura
200
- import aiohttp, asyncio
201
- secrets = {}
202
- for d in self.app_tool.secrets:
203
- secrets[d.get("secretId")] = d.get("secretValue")
237
+ secrets = self.app_tool.secrets_to_dict()
204
238
  search_type = secrets.get("searchType")
205
239
  if search_type:
206
240
  search_kwargs = {"searchType" : search_type}
@@ -218,27 +252,19 @@ class ToolManager:
218
252
  num_results=secrets.get("num_results", 5)
219
253
  )
220
254
  urls = [r["link"] for r in raw_results]
221
- async with aiohttp.ClientSession() as session:
222
- tasks = [fetch_page(session, url) for url in urls]
223
- responses = await asyncio.gather(*tasks)
224
- final_results = []
225
- for item in responses:
226
- url = item["url"]
227
- html = item["html"]
228
- if html:
229
- content = await extract_content_with_trafilatura(html)
230
- if content:
231
- final_results.append({"url": url, "content": content, "type": "web"})
232
- else:
233
- final_results.append({"url": url, "content": "No content found", "type": "web"})
234
- else:
235
- final_results.append({"url": url, "content": "Page not found", "type": "web"})
236
- return final_results
237
-
255
+ return await self._fetch_urls(urls)
238
256
 
239
257
  async def send_email(self, email_subject: str, body: str, to_email:str):
258
+ import smtplib
259
+ from email.mime.multipart import MIMEMultipart
260
+ from email.mime.text import MIMEText
240
261
  secrets = self.app_tool.secrets
241
262
  secrets = {item["secretId"]: item["secretValue"] for item in secrets}
263
+ import urllib.parse as urlparse
264
+ url_preview = secrets.get("url_preview", "")
265
+ if url_preview and url_preview != "":
266
+ message_tread = "Puoi visualizzare la chat su questo indirizzo: " + urlparse.urljoin(url_preview, f"?llmThreadId={self.app_tool.thread_id}")
267
+ body = body.replace("##url_preview##", message_tread)
242
268
  # Email configuration
243
269
  smtp_server = secrets.get("smtp_server")
244
270
  smtp_port = secrets.get("smtp_port")
@@ -274,12 +300,15 @@ class ToolManager:
274
300
 
275
301
  #class variables (static)
276
302
  _list: dict[str,ToolConfig] = {
277
- "document_retriever": ToolConfig(function=document_retriever, model=DocumentRetrieverInput),
278
- "image_generator": ToolConfig(function=image_generator, model=ImageGeneratorInput),
279
- "llm_chain": ToolConfig(function=llm_chain, model=LlmChainInput),
280
- "search_online": ToolConfig(function=search_online, model=SearchOnlineInput),
281
- "search_online_google": ToolConfig(function=search_online_google, model=SearchOnlineInput),
282
- "send_email": ToolConfig(function=send_email, model=EmailSenderInput),
303
+ f"{document_retriever.__name__}": ToolConfig(function=document_retriever, model=DocumentRetrieverInput),
304
+ f"{image_generator.__name__}": ToolConfig(function=image_generator, model=ImageGeneratorInput),
305
+ f"{llm_chain.__name__}": ToolConfig(function=llm_chain, model=LlmChainInput),
306
+ f"{search_online.__name__}": ToolConfig(function=search_online, model=SearchOnlineInput),
307
+ f"{search_online_google.__name__}": ToolConfig(function=search_online_google, model=SearchOnlineInput),
308
+ f"{send_email.__name__}": ToolConfig(function=send_email, model=EmailSenderInput),
309
+ f"{proxy_app_chat.__name__}": ToolConfig(function=proxy_app_chat, model=DocumentRetrieverInput),
310
+ f"{proxy_app_tool.__name__}": ToolConfig(function=proxy_app_tool, model=NoopInput),
311
+
283
312
  }
284
313
 
285
314
  #instance methods
@@ -5,21 +5,21 @@ from ws_bom_robot_app.llm.utils.print import HiddenPrints
5
5
  from ws_bom_robot_app.llm.vector_store.db.manager import VectorDbManager
6
6
  import warnings
7
7
 
8
- async def get_rules(embeddings: Embeddings, rules: LlmRules, input: str | list) -> str:
8
+ async def get_rules(embeddings: Embeddings, rules: LlmRules, query: str | list) -> str:
9
9
  with warnings.catch_warnings():
10
10
  warnings.simplefilter("ignore", category=Warning)
11
11
  # check if the input is multimodal and convert it to text
12
- if isinstance(input, list):
13
- input = " ".join(obj.get("text", "") for obj in input)
12
+ if isinstance(query, list):
13
+ query = " ".join(obj.get("text", "") for obj in query)
14
14
  # check if the input is empty or the rules are not provided
15
- if any([input=="",rules is None,rules and rules.vector_db == "",rules and not os.path.exists(rules.vector_db)]):
15
+ if any([query=="",rules is None,rules and rules.vector_db == "",rules and not os.path.exists(rules.vector_db)]):
16
16
  return ""
17
17
  # get the rules from the vector db and return prompt with rules
18
18
  rules_prompt = ""
19
19
  rules_doc = await VectorDbManager.get_strategy(rules.vector_type).invoke(
20
20
  embeddings,
21
21
  rules.vector_db,
22
- input,
22
+ query,
23
23
  search_type="similarity_score_threshold",
24
24
  search_kwargs={
25
25
  "score_threshold": rules.threshold,
@@ -0,0 +1,77 @@
1
+ import logging, aiohttp
2
+ from typing import List, Optional
3
+
4
+ from pydantic import AliasChoices, BaseModel, ConfigDict, Field
5
+ from ws_bom_robot_app.llm.models.api import LlmAppTool
6
+ from ws_bom_robot_app.util import cache_with_ttl
7
+
8
+ class CmsAppCredential(BaseModel):
9
+ app_key: str = Field(..., description="The app key for the credential", validation_alias=AliasChoices("appKey","app_key"))
10
+ api_key: str = Field(..., description="The api key for the credential", validation_alias=AliasChoices("apiKey","api_key"))
11
+ model_config = ConfigDict(extra='ignore')
12
+ class CmsApp(BaseModel):
13
+ id: str = Field(..., description="Unique identifier for the app")
14
+ name: str = Field(..., description="Name of the app")
15
+ credentials: CmsAppCredential = None
16
+ app_tools: Optional[List[LlmAppTool]] = Field([], validation_alias=AliasChoices("appTools","app_tools"))
17
+ model_config = ConfigDict(extra='ignore')
18
+
19
+ @cache_with_ttl(600) # Cache for 10 minutes
20
+ async def get_apps() -> list[CmsApp]:
21
+ import json, os
22
+ from ws_bom_robot_app.config import config
23
+ class DictObject(object):
24
+ def __init__(self, dict_):
25
+ self.__dict__.update(dict_)
26
+ def __repr__(self):
27
+ return json.dumps(self.__dict__)
28
+ @classmethod
29
+ def from_dict(cls, d):
30
+ return json.loads(json.dumps(d), object_hook=DictObject)
31
+ def __attr(obj, *attrs, default=None):
32
+ for attr in attrs:
33
+ obj = getattr(obj, attr, default)
34
+ if obj is None:
35
+ break
36
+ return obj
37
+ host = config.robot_cms_host
38
+ if host:
39
+ url = f"{host}/api/llmApp?depth=1&pagination=false"
40
+ auth = config.robot_cms_auth
41
+ headers = {"Authorization": auth} if auth else {}
42
+ async with aiohttp.ClientSession() as session:
43
+ async with session.get(url, headers=headers) as response:
44
+ if response.status == 200:
45
+ _apps=[]
46
+ cms_apps = await response.json()
47
+ for cms_app in cms_apps:
48
+ if __attr(cms_app,"isActive",default=True) == True:
49
+ _cms_app_dict = DictObject.from_dict(cms_app)
50
+ _app: CmsApp = CmsApp(
51
+ id=_cms_app_dict.id,
52
+ name=_cms_app_dict.name,
53
+ credentials=CmsAppCredential(app_key=_cms_app_dict.settings.credentials.appKey,api_key=_cms_app_dict.settings.credentials.apiKey),
54
+ app_tools=[LlmAppTool(**tool) for tool in cms_app.get('settings').get('appTools',[])]
55
+ )
56
+ if _app.app_tools:
57
+ for tool in _app.app_tools:
58
+ _knowledgeBase = tool.knowledgeBase
59
+ tool.vector_db = _knowledgeBase.get('vectorDbFile').get('filename') if _knowledgeBase.get('vectorDbFile') else None
60
+ tool.vector_type = _knowledgeBase.get('vectorDbType') if _knowledgeBase.get('vectorDbType') else 'faiss'
61
+ del tool.knowledgeBase
62
+ _apps.append(_app)
63
+ return _apps
64
+ else:
65
+ logging.error(f"Error fetching cms apps: {response.status}")
66
+ else:
67
+ logging.error("robot_cms_host environment variable is not set.")
68
+ return []
69
+
70
+ async def get_app_by_id(app_id: str) -> CmsApp | None:
71
+ apps = await get_apps()
72
+ app = next((a for a in apps if a.id == app_id), None)
73
+ if app:
74
+ return app
75
+ else:
76
+ logging.error(f"App with id {app_id} not found.")
77
+ return None
@@ -350,13 +350,15 @@ class DatabaseTaskManagerStrategy(TaskManagerStrategy):
350
350
  session.commit()
351
351
  #endregion
352
352
 
353
- # global instance
353
+ #region global
354
354
  def __get_taskmanager_strategy() -> TaskManagerStrategy:
355
+ """ Factory function to get the appropriate task manager strategy based on the runtime configuration."""
355
356
  if config.runtime_options().is_multi_process:
356
357
  return DatabaseTaskManagerStrategy()
357
358
  return MemoryTaskManagerStrategy()
358
359
  task_manager = __get_taskmanager_strategy()
359
360
  _log.info(f"Task manager strategy: {task_manager.__class__.__name__}")
361
+ #endregion
360
362
 
361
363
  #region api
362
364
  router = APIRouter(prefix="/api/task", tags=["task"])
@@ -1,6 +1,6 @@
1
1
  import logging.handlers
2
2
  import os, logging, json
3
- from typing import TypeVar, Generic
3
+ from typing import Callable, TypeVar, Generic
4
4
  from functools import wraps
5
5
  from .config import config
6
6
 
@@ -23,25 +23,64 @@ _log: logging.Logger = locals().get("_loc", logger_instance(__name__))
23
23
  #endregion
24
24
 
25
25
  #region cache
26
- class cache(Generic[T]):
27
- def _filepath() -> str:
28
- return os.path.join('.data',f'{T.__module__}.{T.__name__}.json')
29
- @staticmethod
30
- def get() -> list[T]:
31
- filepath: str = cache._filepath()
32
- if os.path.exists(filepath):
33
- with open(filepath, 'r') as file:
34
- content = file.read()
35
- items: list[T] = json.loads(content)
36
- return items
37
- return None
38
- @staticmethod
39
- def set(items: list[T]):
40
- with open(cache._filepath(), 'w') as file:
41
- file.write(json.dumps(items))
42
- @staticmethod
43
- def clear():
44
- os.remove(cache._filepath())
26
+ _cache = {}
27
+ _cache_timestamps = {}
28
+
29
+ def cache_with_ttl(ttl_seconds: int):
30
+ """
31
+ Decorator for caching async function results with TTL (Time To Live)
32
+
33
+ Args:
34
+ ttl_seconds: Cache expiration time in seconds
35
+ """
36
+ import time
37
+ def decorator(func: Callable) -> Callable:
38
+ @wraps(func)
39
+ async def wrapper(*args, **kwargs):
40
+ # Create cache key from function name and arguments
41
+ cache_key = f"{func.__name__}:{hash(str(args) + str(sorted(kwargs.items())))}"
42
+
43
+ current_time = time.time()
44
+
45
+ # Check if cached result exists and is still valid
46
+ if (cache_key in _cache and
47
+ cache_key in _cache_timestamps and
48
+ current_time - _cache_timestamps[cache_key] < ttl_seconds):
49
+ return _cache[cache_key]
50
+
51
+ # Call the original function and cache the result
52
+ result = await func(*args, **kwargs)
53
+ _cache[cache_key] = result
54
+ _cache_timestamps[cache_key] = current_time
55
+
56
+ return result
57
+ return wrapper
58
+ return decorator
59
+
60
+ def clear_cache(id: str = None):
61
+ """Clear the cache by id function"""
62
+ cache_key_prefix = f"{id}:"
63
+ keys_to_remove = [key for key in _cache.keys() if key.startswith(cache_key_prefix)]
64
+ for key in keys_to_remove:
65
+ _cache.pop(key, None)
66
+ _cache_timestamps.pop(key, None)
67
+
68
+ def get_cache_info(id: str) -> dict:
69
+ """Get information about current cache status"""
70
+ import time
71
+ current_time = time.time()
72
+ cache_info = {}
73
+
74
+ for key, timestamp in _cache_timestamps.items():
75
+ if key.startswith(f"{id}:"):
76
+ remaining_ttl = 600 - (current_time - timestamp)
77
+ cache_info[key] = {
78
+ "cached_at": timestamp,
79
+ "remaining_ttl": max(0, remaining_ttl),
80
+ "is_expired": remaining_ttl <= 0
81
+ }
82
+
83
+ return cache_info
45
84
  #endregion
46
85
 
47
86
  def _get_timer_wrapper(is_async=False):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ws_bom_robot_app
3
- Version: 0.0.61
3
+ Version: 0.0.63
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,24 +13,23 @@ 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.6
17
- Requires-Dist: pydantic-settings==2.7.1
18
- Requires-Dist: fastapi[standard]==0.115.8
16
+ Requires-Dist: pydantic==2.11.7
17
+ Requires-Dist: pydantic-settings==2.10.1
18
+ Requires-Dist: fastapi[standard]==0.115.14
19
19
  Requires-Dist: chevron==0.14.0
20
- Requires-Dist: trafilatura==2.0.0
21
- Requires-Dist: langchain==0.3.25
22
- Requires-Dist: langchain-community==0.3.24
23
- Requires-Dist: langchain-core==0.3.59
24
- Requires-Dist: langchain-openai==0.3.16
20
+ Requires-Dist: langchain==0.3.26
21
+ Requires-Dist: langchain-community==0.3.26
22
+ Requires-Dist: langchain-core==0.3.67
23
+ Requires-Dist: langchain-openai==0.3.27
25
24
  Requires-Dist: langchain-anthropic==0.3.6
26
25
  Requires-Dist: langchain-google-genai==2.0.7
27
- Requires-Dist: langchain-google-vertexai==2.0.13
28
- Requires-Dist: langchain-groq==0.3.2
29
- Requires-Dist: langchain-ollama==0.3.2
30
- Requires-Dist: faiss-cpu==1.9.0
31
- Requires-Dist: chromadb==0.6.3
32
- Requires-Dist: langchain_chroma==0.2.1
33
- Requires-Dist: fastembed==0.5.1
26
+ Requires-Dist: langchain-google-vertexai==2.0.27
27
+ Requires-Dist: langchain-groq==0.3.5
28
+ Requires-Dist: langchain-ollama==0.3.3
29
+ Requires-Dist: faiss-cpu==1.11.0
30
+ Requires-Dist: chromadb==1.0.13
31
+ Requires-Dist: langchain_chroma==0.2.4
32
+ Requires-Dist: fastembed==0.7.1
34
33
  Requires-Dist: langchain-qdrant==0.2.0
35
34
  Requires-Dist: lark==1.2.2
36
35
  Requires-Dist: unstructured==0.16.21
@@ -48,9 +47,10 @@ Requires-Dist: unstructured-ingest[sftp]
48
47
  Requires-Dist: unstructured-ingest[sharepoint]
49
48
  Requires-Dist: unstructured-ingest[slack]
50
49
  Requires-Dist: html5lib==1.1
51
- Requires-Dist: markdownify==0.14.1
50
+ Requires-Dist: markdownify==1.1.0
52
51
  Requires-Dist: duckduckgo-search==8.0.4
53
52
  Requires-Dist: langchain_google_community==2.0.7
53
+ Requires-Dist: trafilatura==2.0.0
54
54
  Dynamic: author
55
55
  Dynamic: author-email
56
56
  Dynamic: classifier
@@ -43,6 +43,7 @@ ws_bom_robot_app/llm/tools/models/main.py
43
43
  ws_bom_robot_app/llm/utils/__init__.py
44
44
  ws_bom_robot_app/llm/utils/agent.py
45
45
  ws_bom_robot_app/llm/utils/chunker.py
46
+ ws_bom_robot_app/llm/utils/cms.py
46
47
  ws_bom_robot_app/llm/utils/download.py
47
48
  ws_bom_robot_app/llm/utils/kb.py
48
49
  ws_bom_robot_app/llm/utils/print.py
@@ -1,24 +1,23 @@
1
1
  standardwebhooks==1.0.0
2
2
  apscheduler==3.11.0
3
3
  aiofiles==24.1.0
4
- pydantic==2.10.6
5
- pydantic-settings==2.7.1
6
- fastapi[standard]==0.115.8
4
+ pydantic==2.11.7
5
+ pydantic-settings==2.10.1
6
+ fastapi[standard]==0.115.14
7
7
  chevron==0.14.0
8
- trafilatura==2.0.0
9
- langchain==0.3.25
10
- langchain-community==0.3.24
11
- langchain-core==0.3.59
12
- langchain-openai==0.3.16
8
+ langchain==0.3.26
9
+ langchain-community==0.3.26
10
+ langchain-core==0.3.67
11
+ langchain-openai==0.3.27
13
12
  langchain-anthropic==0.3.6
14
13
  langchain-google-genai==2.0.7
15
- langchain-google-vertexai==2.0.13
16
- langchain-groq==0.3.2
17
- langchain-ollama==0.3.2
18
- faiss-cpu==1.9.0
19
- chromadb==0.6.3
20
- langchain_chroma==0.2.1
21
- fastembed==0.5.1
14
+ langchain-google-vertexai==2.0.27
15
+ langchain-groq==0.3.5
16
+ langchain-ollama==0.3.3
17
+ faiss-cpu==1.11.0
18
+ chromadb==1.0.13
19
+ langchain_chroma==0.2.4
20
+ fastembed==0.7.1
22
21
  langchain-qdrant==0.2.0
23
22
  lark==1.2.2
24
23
  unstructured==0.16.21
@@ -36,6 +35,7 @@ unstructured-ingest[sftp]
36
35
  unstructured-ingest[sharepoint]
37
36
  unstructured-ingest[slack]
38
37
  html5lib==1.1
39
- markdownify==0.14.1
38
+ markdownify==1.1.0
40
39
  duckduckgo-search==8.0.4
41
40
  langchain_google_community==2.0.7
41
+ trafilatura==2.0.0
@@ -1,23 +0,0 @@
1
- from asyncio import Queue
2
- from langchain.tools import Tool, StructuredTool
3
- from ws_bom_robot_app.llm.models.api import LlmAppTool
4
- from ws_bom_robot_app.llm.tools.tool_manager import ToolManager
5
- from ws_bom_robot_app.llm.providers.llm_manager import LlmInterface
6
-
7
- def get_structured_tools(llm: LlmInterface, tools: list[LlmAppTool], callbacks:list, queue: Queue) -> list[StructuredTool]:
8
- _structured_tools :list[StructuredTool] = []
9
- for tool in [tool for tool in tools if tool.is_active]:
10
- if _tool_config := ToolManager._list.get(tool.function_name):
11
- _tool_instance = ToolManager(llm, tool, callbacks, queue)
12
- _structured_tool = StructuredTool.from_function(
13
- coroutine=_tool_instance.get_coroutine(),
14
- name=tool.function_id if tool.function_id else tool.function_name,
15
- description=tool.function_description,
16
- args_schema=_tool_config.model
17
- #infer_schema=True,
18
- #parse_docstring=True,
19
- #error_on_invalid_docstring=True
20
- )
21
- _structured_tool.tags = [tool.function_id if tool.function_id else tool.function_name]
22
- _structured_tools.append(_structured_tool)
23
- return _structured_tools