dhti-elixir-base 1.5.0__tar.gz → 1.5.2__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 (100) hide show
  1. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/CHANGELOG.md +16 -5
  2. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/PKG-INFO +2 -2
  3. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/pyproject.toml +1 -1
  4. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/crewai/agent_wrapper.py +12 -11
  5. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/crewai/chain_tool_wrapper.py +32 -23
  6. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/crewai/langchain_tool_wrapper.py +13 -10
  7. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/crewai/llm_wrapper.py +57 -40
  8. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/uv.lock +2790 -2567
  9. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.devcontainer/Dockerfile +0 -0
  10. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.devcontainer/devcontainer.json +0 -0
  11. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.devcontainer/postCreateCommand.sh +0 -0
  12. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/actions/setup-python-env/action.yml +0 -0
  13. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/config.yml +0 -0
  14. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/dependabot.yml +0 -0
  15. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/bump.yml +0 -0
  16. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/docs.yml +0 -0
  17. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/github-dependents-info.yml +0 -0
  18. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/pr.yml +0 -0
  19. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/publish.yml +0 -0
  20. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/pytest.yml +0 -0
  21. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/tox.yml +0 -0
  22. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.github/workflows/upgrade.yml +0 -0
  23. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/.gitignore +0 -0
  24. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/AUTHORS.md +0 -0
  25. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/CONTRIBUTING.md +0 -0
  26. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/Dockerfile +0 -0
  27. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/LICENSE +0 -0
  28. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/Makefile +0 -0
  29. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/README.md +0 -0
  30. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/codecov.yaml +0 -0
  31. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/docs/contributing.md +0 -0
  32. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/docs/crewai.md +0 -0
  33. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/docs/github-dependents-info.md +0 -0
  34. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/docs/index.md +0 -0
  35. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/docs/modules.md +0 -0
  36. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/examples/chain.md +0 -0
  37. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/examples/crewai_test.py +0 -0
  38. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/examples/graph.md +0 -0
  39. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/examples/graph_limitations.md +0 -0
  40. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/mkdocs.yml +0 -0
  41. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/notes/cds-hook.md +0 -0
  42. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/notes/crewai_future_improvements.md +0 -0
  43. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/notes/parlant.md +0 -0
  44. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/notes/vllm-images.md +0 -0
  45. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/__init__.py +0 -0
  46. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/agent.py +0 -0
  47. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/cds_hook/__init__.py +0 -0
  48. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/cds_hook/card.py +0 -0
  49. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/cds_hook/generate_cards.py +0 -0
  50. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/cds_hook/request.py +0 -0
  51. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/cds_hook/request_parser.py +0 -0
  52. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/cds_hook/routes.py +0 -0
  53. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/cds_hook/service.py +0 -0
  54. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/chain.py +0 -0
  55. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/chatllm.py +0 -0
  56. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/crewai/__init__.py +0 -0
  57. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/embedding.py +0 -0
  58. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/fhir/__init__.py +0 -0
  59. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/fhir/fhir_search.py +0 -0
  60. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/fhir/smart_on_fhir.py +0 -0
  61. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/graph.py +0 -0
  62. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/llm.py +0 -0
  63. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/mcp.py +0 -0
  64. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/model.py +0 -0
  65. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/mydi.py +0 -0
  66. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/parlant_agent.py +0 -0
  67. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/rag/__init__.py +0 -0
  68. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/rag/process.py +0 -0
  69. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/rag/redis_schema.yaml +0 -0
  70. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/src/dhti_elixir_base/server.py +0 -0
  71. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/t_install.py +0 -0
  72. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/__init__.py +0 -0
  73. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/bootstrap.py +0 -0
  74. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/cds_hook/__init__.py +0 -0
  75. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/cds_hook/test_card.py +0 -0
  76. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/cds_hook/test_generate_cards.py +0 -0
  77. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/cds_hook/test_request.py +0 -0
  78. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/cds_hook/test_request_parser.py +0 -0
  79. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/cds_hook/test_service.py +0 -0
  80. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/conftest.py +0 -0
  81. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/crewai/__init__.py +0 -0
  82. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/crewai/test_agent_wrapper.py +0 -0
  83. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/crewai/test_chain_tool_wrapper.py +0 -0
  84. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/crewai/test_langchain_tool_wrapper.py +0 -0
  85. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/crewai/test_llm_wrapper.py +0 -0
  86. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/fhir/__init__.py +0 -0
  87. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/fhir/test_fhir_search.py +0 -0
  88. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/fhir/test_smart_on_fhir_search.py +0 -0
  89. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/rag/__init__.py +0 -0
  90. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/rag/test_process.py +0 -0
  91. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_agent.py +0 -0
  92. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_chain.py +0 -0
  93. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_chatllm.py +0 -0
  94. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_embedding.py +0 -0
  95. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_graph.py +0 -0
  96. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_llm.py +0 -0
  97. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_model.py +0 -0
  98. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_parlant_agent.py +0 -0
  99. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tests/test_server.py +0 -0
  100. {dhti_elixir_base-1.5.0 → dhti_elixir_base-1.5.2}/tox.ini +0 -0
@@ -2,14 +2,13 @@
2
2
 
3
3
  ## [Unreleased](https://github.com/dermatologist/dhti-elixir-base/tree/HEAD)
4
4
 
5
- [Full Changelog](https://github.com/dermatologist/dhti-elixir-base/compare/v1.4.1...HEAD)
6
-
7
- **Implemented enhancements:**
8
-
9
- - Add CrewAI wrappers for LLMs, Tools and Agents [\#77](https://github.com/dermatologist/dhti-elixir-base/issues/77)
5
+ [Full Changelog](https://github.com/dermatologist/dhti-elixir-base/compare/v1.5.1...HEAD)
10
6
 
11
7
  **Merged pull requests:**
12
8
 
9
+ - Refactor code structure for improved readability and maintainability [\#106](https://github.com/dermatologist/dhti-elixir-base/pull/106) ([dermatologist](https://github.com/dermatologist))
10
+ - \[Automated\] Dependencies upgrade [\#84](https://github.com/dermatologist/dhti-elixir-base/pull/84) ([github-actions[bot]](https://github.com/apps/github-actions))
11
+ - Feature/fix return type 1 [\#79](https://github.com/dermatologist/dhti-elixir-base/pull/79) ([dermatologist](https://github.com/dermatologist))
13
12
  - Add CrewAI wrappers for LLMs, Tools, and Agents [\#78](https://github.com/dermatologist/dhti-elixir-base/pull/78) ([Copilot](https://github.com/apps/copilot-swe-agent))
14
13
  - \[Automated\] Dependencies upgrade [\#76](https://github.com/dermatologist/dhti-elixir-base/pull/76) ([github-actions[bot]](https://github.com/apps/github-actions))
15
14
  - fix\(routes\): Improve response handling in invoke\_chain function [\#73](https://github.com/dermatologist/dhti-elixir-base/pull/73) ([dermatologist](https://github.com/dermatologist))
@@ -40,6 +39,18 @@
40
39
  - Feature/uv build 1 [\#40](https://github.com/dermatologist/dhti-elixir-base/pull/40) ([dermatologist](https://github.com/dermatologist))
41
40
  - Feature/mcp 1 [\#39](https://github.com/dermatologist/dhti-elixir-base/pull/39) ([dermatologist](https://github.com/dermatologist))
42
41
 
42
+ ## [v1.5.1](https://github.com/dermatologist/dhti-elixir-base/tree/v1.5.1) (2026-02-10)
43
+
44
+ [Full Changelog](https://github.com/dermatologist/dhti-elixir-base/compare/v1.5.0...v1.5.1)
45
+
46
+ ## [v1.5.0](https://github.com/dermatologist/dhti-elixir-base/tree/v1.5.0) (2026-02-09)
47
+
48
+ [Full Changelog](https://github.com/dermatologist/dhti-elixir-base/compare/v1.4.1...v1.5.0)
49
+
50
+ **Implemented enhancements:**
51
+
52
+ - Add CrewAI wrappers for LLMs, Tools and Agents [\#77](https://github.com/dermatologist/dhti-elixir-base/issues/77)
53
+
43
54
  ## [v1.4.1](https://github.com/dermatologist/dhti-elixir-base/tree/v1.4.1) (2026-01-18)
44
55
 
45
56
  [Full Changelog](https://github.com/dermatologist/dhti-elixir-base/compare/v1.4.0...v1.4.1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhti-elixir-base
3
- Version: 1.5.0
3
+ Version: 1.5.2
4
4
  Summary: DHTI Elixir Base
5
5
  Project-URL: Documentation, https://github.com/dermatologist/dhti
6
6
  Project-URL: Homepage, https://nuchange.ca
@@ -32,7 +32,7 @@ Requires-Dist: kink
32
32
  Requires-Dist: langchain-community
33
33
  Requires-Dist: langchain-core
34
34
  Requires-Dist: langchain-mcp-adapters
35
- Requires-Dist: langchain<1.1.0,>=1.0.0
35
+ Requires-Dist: langchain>=1.0.0
36
36
  Requires-Dist: langgraph
37
37
  Requires-Dist: langserve
38
38
  Requires-Dist: mcp
@@ -18,7 +18,7 @@ classifiers = [
18
18
  ]
19
19
  dependencies = [
20
20
  "langchain-core",
21
- "langchain>=1.0.0,<1.1.0",
21
+ "langchain>=1.0.0",
22
22
  "kink",
23
23
  "python-dotenv",
24
24
  "httpx",
@@ -100,23 +100,24 @@ class CrewAIAgentWrapper(CrewAIAgent):
100
100
  try:
101
101
  from crewai.tools import BaseTool as CrewAIBaseTool
102
102
  except ImportError:
103
- from crewai_tools import BaseTool as CrewAIBaseTool
103
+ from crewai.tools.base_tool import BaseTool as CrewAIBaseTool
104
104
 
105
105
  from .langchain_tool_wrapper import CrewAILangChainToolWrapper
106
106
 
107
107
  validated_tools = []
108
- for tool in tools if isinstance(tools, list) else [tools]:
109
- # If it's already a BaseTool, use it as is
110
- if isinstance(tool, CrewAIBaseTool):
111
- validated_tools.append(tool)
112
- # Try to wrap as a LangChain tool
113
- else:
114
- try:
108
+ tool_list = tools if isinstance(tools, list) else [tools]
109
+ for tool in tool_list:
110
+ try:
111
+ # If it's already a BaseTool, use it as is
112
+ if isinstance(tool, CrewAIBaseTool):
113
+ validated_tools.append(tool)
114
+ # Try to wrap as a LangChain tool
115
+ else:
115
116
  wrapped = CrewAILangChainToolWrapper(langchain_tool=tool)
116
117
  validated_tools.append(wrapped)
117
- except Exception:
118
- # Skip tools that can't be wrapped
119
- pass
118
+ except Exception as e:
119
+ # Log but skip tools that can't be wrapped
120
+ pass
120
121
  tools = validated_tools if validated_tools else None
121
122
 
122
123
  # Initialize CrewAI Agent
@@ -21,7 +21,7 @@ from pydantic import PrivateAttr
21
21
  try:
22
22
  from crewai.tools import BaseTool as CrewAIBaseTool
23
23
  except ImportError:
24
- from crewai_tools import BaseTool as CrewAIBaseTool
24
+ from crewai.tools.base_tool import BaseTool as CrewAIBaseTool
25
25
 
26
26
  from ..chain import BaseChain
27
27
 
@@ -114,30 +114,39 @@ class CrewAIChainToolWrapper(CrewAIBaseTool):
114
114
 
115
115
  Returns:
116
116
  str: The result of the chain execution
117
+
118
+ Raises:
119
+ ValueError: If neither positional nor keyword arguments are provided
120
+ RuntimeError: If chain execution fails
117
121
  """
118
- # If kwargs are provided, use them directly
119
- if kwargs:
120
- result = self._dhti_chain.invoke(**kwargs)
121
- # If a single positional arg is provided, treat it as the input
122
- elif args:
123
- result = self._dhti_chain.invoke(input=args[0])
124
- else:
125
- raise ValueError(
126
- "Either provide input as a keyword argument or as a positional argument"
127
- )
128
-
129
- # Convert result to string
130
- if isinstance(result, dict):
131
- # If result is a dict, try to extract the most relevant value
132
- if "cards" in result:
133
- # Handle CDS Hook response format
134
- return str(result.get("cards", []))
135
- elif "output" in result:
136
- return str(result["output"])
122
+ try:
123
+ # If kwargs are provided, use them directly
124
+ if kwargs:
125
+ result = self._dhti_chain.invoke(**kwargs)
126
+ # If a single positional arg is provided, treat it as the input
127
+ elif args:
128
+ result = self._dhti_chain.invoke(input=args[0])
137
129
  else:
138
- return str(result)
139
-
140
- return str(result)
130
+ raise ValueError(
131
+ "Either provide input as a keyword argument or as a positional argument"
132
+ )
133
+
134
+ # Convert result to string
135
+ if isinstance(result, dict):
136
+ # If result is a dict, try to extract the most relevant value
137
+ if "cards" in result:
138
+ # Handle CDS Hook response format
139
+ return str(result.get("cards", []))
140
+ elif "output" in result:
141
+ return str(result["output"])
142
+ else:
143
+ return str(result)
144
+
145
+ return str(result)
146
+ except ValueError:
147
+ raise
148
+ except Exception as e:
149
+ raise RuntimeError(f"Chain execution failed: {e}") from e
141
150
 
142
151
  def __str__(self) -> str:
143
152
  """Return string representation of the wrapper."""
@@ -21,7 +21,7 @@ from pydantic import PrivateAttr
21
21
  try:
22
22
  from crewai.tools import BaseTool as CrewAIBaseTool
23
23
  except ImportError:
24
- from crewai_tools import BaseTool as CrewAIBaseTool
24
+ from crewai.tools.base_tool import BaseTool as CrewAIBaseTool
25
25
 
26
26
  from langchain_core.tools import BaseTool as LangChainBaseTool
27
27
 
@@ -104,11 +104,11 @@ class CrewAILangChainToolWrapper(CrewAIBaseTool):
104
104
 
105
105
  Returns:
106
106
  str: The result of the tool execution
107
+
108
+ Raises:
109
+ AttributeError: If no valid invocation method is found on the tool
107
110
  """
108
- # Try different invocation methods based on the tool type
109
111
  try:
110
- result = None
111
-
112
112
  # Try using the run method (common in LangChain tools)
113
113
  if hasattr(self._langchain_tool, "run"):
114
114
  try:
@@ -117,18 +117,18 @@ class CrewAILangChainToolWrapper(CrewAIBaseTool):
117
117
  elif kwargs:
118
118
  result = self._langchain_tool.run(**kwargs)
119
119
  else:
120
- result = self._langchain_tool.run()
120
+ result = self._langchain_tool.run(tool_input={})
121
121
  return str(result)
122
122
  except (AttributeError, TypeError):
123
123
  # run method doesn't exist or failed, try next option
124
124
  pass
125
125
 
126
- # If not run, try using the invoke method (newer LangChain tools)
126
+ # Try using the invoke method (newer LangChain tools)
127
127
  if hasattr(self._langchain_tool, "invoke"):
128
128
  try:
129
129
  if args and not kwargs:
130
130
  result = self._langchain_tool.invoke(
131
- args[0] if len(args) == 1 else args
131
+ args[0] if len(args) == 1 else args # type: ignore
132
132
  )
133
133
  elif kwargs:
134
134
  result = self._langchain_tool.invoke(kwargs)
@@ -139,7 +139,7 @@ class CrewAILangChainToolWrapper(CrewAIBaseTool):
139
139
  # invoke method doesn't exist or failed, try next option
140
140
  pass
141
141
 
142
- # Check if __call__ is explicitly set onthe object (for test mocks)
142
+ # Check if __call__ is explicitly set on the object (for test mocks)
143
143
  if "__call__" in self._langchain_tool.__dict__:
144
144
  __call_method = self._langchain_tool.__dict__["__call__"]
145
145
  if args and not kwargs:
@@ -165,11 +165,14 @@ class CrewAILangChainToolWrapper(CrewAIBaseTool):
165
165
  pass
166
166
 
167
167
  # If we got here, no valid method was found
168
+ tool_type = type(self._langchain_tool).__name__
168
169
  raise AttributeError(
169
- f"LangChain tool {type(self._langchain_tool)} does not have "
170
- "run, invoke, or __call__ methods"
170
+ f"LangChain tool '{tool_type}' does not have any of the following: "
171
+ "run(), invoke(), or __call__() methods"
171
172
  )
172
173
 
174
+ except AttributeError:
175
+ raise
173
176
  except Exception as e:
174
177
  return f"Error executing LangChain tool: {e!s}"
175
178
 
@@ -17,6 +17,7 @@ limitations under the License.
17
17
  from typing import Any
18
18
 
19
19
  from crewai.llms.base_llm import BaseLLM as CrewAIBaseLLM
20
+ from crewai.types.usage_metrics import UsageMetrics
20
21
 
21
22
  from ..chatllm import BaseChatLLM
22
23
  from ..llm import BaseLLM
@@ -72,40 +73,51 @@ class CrewAILLMWrapper(CrewAIBaseLLM):
72
73
 
73
74
  Returns:
74
75
  str: The generated response from the LLM
75
- """
76
- # Convert messages to the format expected by DHTI LLMs
77
- if isinstance(self._dhti_llm, BaseChatLLM):
78
- # For BaseChatLLM, convert to LangChain messages
79
- from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
80
-
81
- lc_messages = []
82
- for msg in messages:
83
- role = msg.get("role", "user")
84
- content = msg.get("content", "")
85
-
86
- if role == "system":
87
- lc_messages.append(SystemMessage(content=content))
88
- elif role == "assistant":
89
- lc_messages.append(AIMessage(content=content))
90
- else: # user or any other role
91
- lc_messages.append(HumanMessage(content=content))
92
-
93
- result = self._dhti_llm.invoke(lc_messages)
94
- return result.content if hasattr(result, "content") else str(result)
95
- else:
96
- # For BaseLLM, combine messages into a single prompt
97
- prompt = "\n".join(
98
- [
99
- f"{msg.get('role', 'user')}: {msg.get('content', '')}"
100
- for msg in messages
101
- ]
102
- )
103
- return self._dhti_llm.invoke(prompt)
104
76
 
105
- @property
106
- def is_litellm(self) -> bool:
107
- """Return whether this LLM is a LiteLLM provider."""
108
- return False
77
+ Raises:
78
+ ValueError: If messages list is empty or malformed
79
+ RuntimeError: If LLM invocation fails
80
+ """
81
+ if not messages:
82
+ raise ValueError("Messages list cannot be empty")
83
+
84
+ try:
85
+ # Convert messages to the format expected by DHTI LLMs
86
+ if isinstance(self._dhti_llm, BaseChatLLM):
87
+ # For BaseChatLLM, convert to LangChain messages
88
+ from langchain_core.messages import (
89
+ AIMessage,
90
+ HumanMessage,
91
+ SystemMessage,
92
+ )
93
+
94
+ lc_messages = []
95
+ for msg in messages:
96
+ role = msg.get("role", "user")
97
+ content = msg.get("content", "")
98
+
99
+ if role == "system":
100
+ lc_messages.append(SystemMessage(content=content))
101
+ elif role == "assistant":
102
+ lc_messages.append(AIMessage(content=content))
103
+ else: # user or any other role
104
+ lc_messages.append(HumanMessage(content=content))
105
+
106
+ result = self._dhti_llm.invoke(lc_messages)
107
+ return result.content if hasattr(result, "content") else str(result) # type: ignore
108
+ else:
109
+ # For BaseLLM, combine messages into a single prompt
110
+ prompt = "\n".join(
111
+ [
112
+ f"{msg.get('role', 'user')}: {msg.get('content', '')}"
113
+ for msg in messages
114
+ ]
115
+ )
116
+ return self._dhti_llm.invoke(prompt)
117
+ except Exception as e:
118
+ raise RuntimeError(f"Failed to invoke LLM: {e}") from e
119
+
120
+ is_litellm: bool = False
109
121
 
110
122
  @property
111
123
  def model(self) -> str:
@@ -117,19 +129,24 @@ class CrewAILLMWrapper(CrewAIBaseLLM):
117
129
  """Return the provider name."""
118
130
  return "dhti-elixir"
119
131
 
132
+ @provider.setter
133
+ def provider(self, value: str) -> None:
134
+ # Read-only property; setter is a no-op to satisfy base class signature
135
+ pass
136
+
120
137
  def get_context_window_size(self) -> int:
121
138
  """Get the context window size for the model."""
122
139
  # Default context window size
123
140
  # This can be overridden in subclasses for specific models
124
- return 4096
141
+ return 4096 * 2
125
142
 
126
- def get_token_usage_summary(self) -> dict[str, Any]:
143
+ def get_token_usage_summary(self) -> UsageMetrics:
127
144
  """Get token usage summary."""
128
- return {
129
- "prompt_tokens": 0,
130
- "completion_tokens": 0,
131
- "total_tokens": 0,
132
- }
145
+ return UsageMetrics(
146
+ prompt_tokens=0,
147
+ completion_tokens=0,
148
+ total_tokens=0,
149
+ )
133
150
 
134
151
  def supports_stop_words(self) -> bool:
135
152
  """Check if the model supports stop words."""