langchain-timbr 2.1.14__tar.gz → 3.0.0__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 (58) hide show
  1. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/PKG-INFO +1 -1
  2. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/_version.py +2 -2
  3. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langchain/execute_timbr_query_chain.py +62 -19
  4. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langchain/generate_answer_chain.py +18 -4
  5. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langchain/generate_timbr_sql_chain.py +54 -17
  6. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langchain/identify_concept_chain.py +31 -9
  7. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langchain/timbr_sql_agent.py +20 -1
  8. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langchain/validate_timbr_sql_chain.py +57 -17
  9. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langgraph/execute_timbr_query_node.py +3 -0
  10. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langgraph/generate_response_node.py +5 -2
  11. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langgraph/generate_timbr_sql_node.py +3 -0
  12. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langgraph/identify_concept_node.py +3 -0
  13. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langgraph/validate_timbr_query_node.py +3 -0
  14. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/utils/timbr_llm_utils.py +333 -166
  15. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/utils/timbr_utils.py +27 -0
  16. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/conftest.py +1 -0
  17. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_agent_integration.py +56 -0
  18. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_langchain_chains.py +7 -0
  19. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/.github/dependabot.yml +0 -0
  20. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/.github/pull_request_template.md +0 -0
  21. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/.github/workflows/_codespell.yml +0 -0
  22. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/.github/workflows/_fossa.yml +0 -0
  23. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/.github/workflows/install-dependencies-and-run-tests.yml +0 -0
  24. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/.github/workflows/publish.yml +0 -0
  25. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/.gitignore +0 -0
  26. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/LICENSE +0 -0
  27. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/README.md +0 -0
  28. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/SECURITY.md +0 -0
  29. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/pyproject.toml +0 -0
  30. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/pytest.ini +0 -0
  31. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/requirements.txt +0 -0
  32. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/requirements310.txt +0 -0
  33. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/requirements311.txt +0 -0
  34. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/__init__.py +0 -0
  35. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/config.py +0 -0
  36. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langchain/__init__.py +0 -0
  37. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/langgraph/__init__.py +0 -0
  38. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/llm_wrapper/llm_wrapper.py +0 -0
  39. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/llm_wrapper/timbr_llm_wrapper.py +0 -0
  40. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/timbr_llm_connector.py +0 -0
  41. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/utils/general.py +0 -0
  42. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/utils/prompt_service.py +0 -0
  43. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/src/langchain_timbr/utils/temperature_supported_models.json +0 -0
  44. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/README.md +0 -0
  45. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_azure_databricks_provider.py +0 -0
  46. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_azure_openai_model.py +0 -0
  47. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_chain_pipeline.py +0 -0
  48. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_chain_reasoning.py +0 -0
  49. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_jwt_token.py +0 -0
  50. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_langgraph_nodes.py +0 -0
  51. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/integration/test_timeout_functionality.py +0 -0
  52. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/standard/conftest.py +0 -0
  53. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/standard/test_chain_documentation.py +0 -0
  54. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/standard/test_connection_validation.py +0 -0
  55. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/standard/test_llm_wrapper_optional_params.py +0 -0
  56. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/standard/test_optional_llm_integration.py +0 -0
  57. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/standard/test_standard_chain_requirements.py +0 -0
  58. {langchain_timbr-2.1.14 → langchain_timbr-3.0.0}/tests/standard/test_unit_tests.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langchain-timbr
3
- Version: 2.1.14
3
+ Version: 3.0.0
4
4
  Summary: LangChain & LangGraph extensions that parse LLM prompts into Timbr semantic SQL and execute them.
5
5
  Project-URL: Homepage, https://github.com/WPSemantix/langchain-timbr
6
6
  Project-URL: Documentation, https://docs.timbr.ai/doc/docs/integration/langchain-sdk/
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '2.1.14'
32
- __version_tuple__ = version_tuple = (2, 1, 14)
31
+ __version__ = version = '3.0.0'
32
+ __version_tuple__ = version_tuple = (3, 0, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -2,6 +2,8 @@ from typing import Optional, Union, Dict, Any
2
2
  from langchain.chains.base import Chain
3
3
  from langchain.llms.base import LLM
4
4
 
5
+ from langchain_timbr.utils.timbr_utils import get_timbr_agent_options
6
+
5
7
  from ..utils.general import parse_list, to_boolean, to_integer, validate_timbr_connection_params
6
8
  from ..utils.timbr_utils import run_query, validate_sql
7
9
  from ..utils.timbr_llm_utils import generate_sql
@@ -16,6 +18,8 @@ class ExecuteTimbrQueryChain(Chain):
16
18
  returns the query results, handling retries and result validation. It uses an LLM
17
19
  for query generation and connects to Timbr via URL and token.
18
20
  """
21
+
22
+ _ontology: Optional[str] = None
19
23
 
20
24
  def __init__(
21
25
  self,
@@ -38,6 +42,7 @@ class ExecuteTimbrQueryChain(Chain):
38
42
  note: Optional[str] = '',
39
43
  db_is_case_sensitive: Optional[bool] = False,
40
44
  graph_depth: Optional[int] = 1,
45
+ agent: Optional[str] = None,
41
46
  verify_ssl: Optional[bool] = True,
42
47
  is_jwt: Optional[bool] = False,
43
48
  jwt_tenant_id: Optional[str] = None,
@@ -67,6 +72,7 @@ class ExecuteTimbrQueryChain(Chain):
67
72
  :param note: Optional additional note to extend our llm prompt
68
73
  :param db_is_case_sensitive: Whether the database is case sensitive (default is False).
69
74
  :param graph_depth: Maximum number of relationship hops to traverse from the source concept during schema exploration (default is 1).
75
+ :param agent: Optional Timbr agent name for options setup.
70
76
  :param verify_ssl: Whether to verify SSL certificates (default is True).
71
77
  :param is_jwt: Whether to use JWT authentication (default is False).
72
78
  :param jwt_tenant_id: JWT tenant ID for multi-tenant environments (required when is_jwt=True).
@@ -116,33 +122,64 @@ class ExecuteTimbrQueryChain(Chain):
116
122
 
117
123
  self._url = url if url is not None else config.url
118
124
  self._token = token if token is not None else config.token
119
- self._ontology = ontology if ontology is not None else config.ontology
120
125
 
121
126
  # Validate required parameters
122
127
  validate_timbr_connection_params(self._url, self._token)
123
128
 
124
- self._schema = schema
125
- self._concept = concept
126
- self._concepts_list = parse_list(concepts_list)
127
- self._views_list = parse_list(views_list)
128
- self._include_tags = parse_list(include_tags)
129
- self._include_logic_concepts = to_boolean(include_logic_concepts)
130
- self._exclude_properties = parse_list(exclude_properties)
131
- self._should_validate_sql = to_boolean(should_validate_sql)
132
- self._retries = to_integer(retries)
133
- self._max_limit = to_integer(max_limit)
134
- self._retry_if_no_results = to_boolean(retry_if_no_results)
135
- self._no_results_max_retries = to_integer(no_results_max_retries)
136
- self._note = note
137
- self._db_is_case_sensitive = to_boolean(db_is_case_sensitive)
138
- self._graph_depth = to_integer(graph_depth)
139
129
  self._verify_ssl = to_boolean(verify_ssl)
140
130
  self._is_jwt = to_boolean(is_jwt)
141
131
  self._jwt_tenant_id = jwt_tenant_id
142
132
  self._debug = to_boolean(debug)
143
133
  self._conn_params = conn_params or {}
144
- self._enable_reasoning = to_boolean(enable_reasoning)
145
- self._reasoning_steps = to_integer(reasoning_steps)
134
+ self._max_limit = config.llm_default_limit # use default value so the self._get_conn_params() won't fail before agent options are processed
135
+
136
+ self._agent = agent
137
+ if self._agent:
138
+ agent_options = get_timbr_agent_options(self._agent, conn_params=self._get_conn_params())
139
+
140
+ self._ontology = agent_options.get("ontology") if "ontology" in agent_options else None
141
+ self._schema = agent_options.get("schema") if "schema" in agent_options else schema
142
+ self._concept = agent_options.get("concept") if "concept" in agent_options else None
143
+ self._concepts_list = parse_list(agent_options.get("concepts_list")) if "concepts_list" in agent_options else None
144
+ self._views_list = parse_list(agent_options.get("views_list")) if "views_list" in agent_options else None
145
+ self._include_tags = parse_list(agent_options.get("include_tags")) if "include_tags" in agent_options else None
146
+ self._include_logic_concepts = to_boolean(agent_options.get("include_logic_concepts")) if "include_logic_concepts" in agent_options else False
147
+ self._exclude_properties = parse_list(agent_options.get("exclude_properties")) if "exclude_properties" in agent_options else ['entity_id', 'entity_type', 'entity_label']
148
+ self._should_validate_sql = to_boolean(agent_options.get("should_validate_sql")) if "should_validate_sql" in agent_options else config.should_validate_sql
149
+ self._retries = to_integer(agent_options.get("retries") if "retries" in agent_options else retries)
150
+ self._max_limit = to_integer(agent_options.get("max_limit")) if "max_limit" in agent_options else config.llm_default_limit
151
+ self._retry_if_no_results = to_boolean(agent_options.get("retry_if_no_results")) if "retry_if_no_results" in agent_options else config.retry_if_no_results
152
+ self._no_results_max_retries = to_integer(agent_options.get("no_results_max_retries")) if "no_results_max_retries" in agent_options else 2
153
+ self._db_is_case_sensitive = to_boolean(agent_options.get("db_is_case_sensitive")) if "db_is_case_sensitive" in agent_options else False
154
+ self._graph_depth = to_integer(agent_options.get("graph_depth")) if "graph_depth" in agent_options else 1
155
+ self._note = agent_options.get("note") if "note" in agent_options else ''
156
+ if note:
157
+ self._note = ((self._note + '\n') if self._note else '') + note
158
+ self._enable_reasoning = to_boolean(agent_options.get("enable_reasoning")) if "enable_reasoning" in agent_options else config.enable_reasoning
159
+ if enable_reasoning is not None and enable_reasoning != self._enable_reasoning:
160
+ self._enable_reasoning = to_boolean(enable_reasoning)
161
+ self._reasoning_steps = to_integer(agent_options.get("reasoning_steps")) if "reasoning_steps" in agent_options else config.reasoning_steps
162
+ if reasoning_steps is not None and reasoning_steps != self._reasoning_steps:
163
+ self._reasoning_steps = to_integer(reasoning_steps)
164
+ else:
165
+ self._ontology = ontology if ontology is not None else config.ontology
166
+ self._schema = schema
167
+ self._concept = concept
168
+ self._concepts_list = parse_list(concepts_list)
169
+ self._views_list = parse_list(views_list)
170
+ self._include_tags = parse_list(include_tags)
171
+ self._include_logic_concepts = to_boolean(include_logic_concepts)
172
+ self._exclude_properties = parse_list(exclude_properties)
173
+ self._should_validate_sql = to_boolean(should_validate_sql)
174
+ self._retries = to_integer(retries)
175
+ self._max_limit = to_integer(max_limit)
176
+ self._retry_if_no_results = to_boolean(retry_if_no_results)
177
+ self._no_results_max_retries = to_integer(no_results_max_retries)
178
+ self._db_is_case_sensitive = to_boolean(db_is_case_sensitive)
179
+ self._graph_depth = to_integer(graph_depth)
180
+ self._note = note
181
+ self._enable_reasoning = to_boolean(enable_reasoning)
182
+ self._reasoning_steps = to_integer(reasoning_steps)
146
183
 
147
184
 
148
185
  @property
@@ -171,7 +208,7 @@ class ExecuteTimbrQueryChain(Chain):
171
208
  return {
172
209
  "url": self._url,
173
210
  "token": self._token,
174
- "ontology": self._ontology,
211
+ "ontology": self._ontology if self._ontology is not None else config.ontology,
175
212
  "verify_ssl": self._verify_ssl,
176
213
  "is_jwt": self._is_jwt,
177
214
  "jwt_tenant_id": self._jwt_tenant_id,
@@ -247,6 +284,8 @@ class ExecuteTimbrQueryChain(Chain):
247
284
  concept_name = inputs.get("concept", self._concept)
248
285
  is_sql_valid = True
249
286
  error = None
287
+ identify_concept_reason = None
288
+ generate_sql_reason = None
250
289
  reasoning_status = None
251
290
  rows = []
252
291
  usage_metadata = {}
@@ -270,6 +309,8 @@ class ExecuteTimbrQueryChain(Chain):
270
309
  is_sql_valid = True
271
310
 
272
311
  error = generate_res.get("error")
312
+ identify_concept_reason = generate_res.get("identify_concept_reason")
313
+ generate_sql_reason = generate_res.get("generate_sql_reason")
273
314
  usage_metadata = self._summarize_usage_metadata(usage_metadata, generate_res.get("usage_metadata", {}))
274
315
 
275
316
  is_sql_not_tried = not any(sql.lower().strip() == gen.lower().strip() for gen in generated)
@@ -305,6 +346,8 @@ class ExecuteTimbrQueryChain(Chain):
305
346
  "concept": concept_name,
306
347
  "error": error if not is_sql_valid else None,
307
348
  "reasoning_status": reasoning_status,
349
+ "identify_concept_reason": identify_concept_reason,
350
+ "generate_sql_reason": generate_sql_reason,
308
351
  self.usage_metadata_key: usage_metadata,
309
352
  }
310
353
 
@@ -2,6 +2,8 @@ from typing import Optional, Dict, Any
2
2
  from langchain.chains.base import Chain
3
3
  from langchain.llms.base import LLM
4
4
 
5
+ from langchain_timbr.utils.timbr_utils import get_timbr_agent_options
6
+
5
7
  from ..utils.general import to_boolean, validate_timbr_connection_params
6
8
  from ..utils.timbr_llm_utils import answer_question
7
9
  from ..llm_wrapper.llm_wrapper import LlmWrapper
@@ -19,11 +21,12 @@ class GenerateAnswerChain(Chain):
19
21
  llm: Optional[LLM] = None,
20
22
  url: Optional[str] = None,
21
23
  token: Optional[str] = None,
24
+ note: Optional[str] = '',
25
+ agent: Optional[str] = None,
22
26
  verify_ssl: Optional[bool] = True,
23
27
  is_jwt: Optional[bool] = False,
24
28
  jwt_tenant_id: Optional[str] = None,
25
29
  conn_params: Optional[dict] = None,
26
- note: Optional[str] = '',
27
30
  debug: Optional[bool] = False,
28
31
  **kwargs,
29
32
  ):
@@ -31,11 +34,12 @@ class GenerateAnswerChain(Chain):
31
34
  :param llm: An LLM instance or a function that takes a prompt string and returns the LLM’s response (optional, will use LlmWrapper with env variables if not provided)
32
35
  :param url: Timbr server url (optional, defaults to TIMBR_URL environment variable)
33
36
  :param token: Timbr password or token value (optional, defaults to TIMBR_TOKEN environment variable)
37
+ :param note: Optional additional note to extend our llm prompt
38
+ :param agent: Optional Timbr agent name for options setup.
34
39
  :param verify_ssl: Whether to verify SSL certificates (default is True).
35
40
  :param is_jwt: Whether to use JWT authentication (default is False).
36
41
  :param jwt_tenant_id: JWT tenant ID for multi-tenant environments (required when is_jwt=True).
37
42
  :param conn_params: Extra Timbr connection parameters sent with every request (e.g., 'x-api-impersonate-user').
38
- :param note: Optional additional note to extend our llm prompt
39
43
 
40
44
  ## Example
41
45
  ```
@@ -79,7 +83,17 @@ class GenerateAnswerChain(Chain):
79
83
  self._jwt_tenant_id = jwt_tenant_id
80
84
  self._debug = to_boolean(debug)
81
85
  self._conn_params = conn_params or {}
82
- self._note = note
86
+
87
+ self._agent = agent
88
+ if self._agent:
89
+ agent_options = get_timbr_agent_options(self._agent, conn_params=self._get_conn_params())
90
+
91
+ self._note = agent_options.get("note") if "note" in agent_options else ''
92
+ if note:
93
+ self._note = ((self._note + '\n') if self._note else '') + note
94
+
95
+ else:
96
+ self._note = note
83
97
 
84
98
 
85
99
  @property
@@ -100,7 +114,7 @@ class GenerateAnswerChain(Chain):
100
114
  return {
101
115
  "url": self._url,
102
116
  "token": self._token,
103
- # "ontology": self._ontology,
117
+ "ontology": config.ontology,
104
118
  "verify_ssl": self._verify_ssl,
105
119
  "is_jwt": self._is_jwt,
106
120
  "jwt_tenant_id": self._jwt_tenant_id,
@@ -2,6 +2,8 @@ from typing import Optional, Union, Dict, Any
2
2
  from langchain.chains.base import Chain
3
3
  from langchain.llms.base import LLM
4
4
 
5
+ from langchain_timbr.utils.timbr_utils import get_timbr_agent_options
6
+
5
7
  from ..utils.general import parse_list, to_boolean, to_integer, validate_timbr_connection_params
6
8
  from ..utils.timbr_llm_utils import generate_sql
7
9
  from ..llm_wrapper.llm_wrapper import LlmWrapper
@@ -15,6 +17,8 @@ class GenerateTimbrSqlChain(Chain):
15
17
  against Timbr ontology/knowledge graph databases. It uses an LLM to process prompts and
16
18
  connects to Timbr via URL and token for SQL generation.
17
19
  """
20
+
21
+ _ontology: Optional[str] = None
18
22
 
19
23
  def __init__(
20
24
  self,
@@ -35,6 +39,7 @@ class GenerateTimbrSqlChain(Chain):
35
39
  note: Optional[str] = '',
36
40
  db_is_case_sensitive: Optional[bool] = False,
37
41
  graph_depth: Optional[int] = 1,
42
+ agent: Optional[str] = None,
38
43
  verify_ssl: Optional[bool] = True,
39
44
  is_jwt: Optional[bool] = False,
40
45
  jwt_tenant_id: Optional[str] = None,
@@ -62,6 +67,7 @@ class GenerateTimbrSqlChain(Chain):
62
67
  :param note: Optional additional note to extend our llm prompt
63
68
  :param db_is_case_sensitive: Whether the database is case sensitive (default is False).
64
69
  :param graph_depth: Maximum number of relationship hops to traverse from the source concept during schema exploration (default is 1).
70
+ :param agent: Optional Timbr agent name for options setup.
65
71
  :param verify_ssl: Whether to verify SSL certificates (default is True).
66
72
  :param is_jwt: Whether to use JWT authentication (default is False).
67
73
  :param jwt_tenant_id: JWT tenant ID for multi-tenant environments (required when is_jwt=True).
@@ -111,31 +117,60 @@ class GenerateTimbrSqlChain(Chain):
111
117
 
112
118
  self._url = url if url is not None else config.url
113
119
  self._token = token if token is not None else config.token
114
- self._ontology = ontology if ontology is not None else config.ontology
115
120
 
116
121
  # Validate required parameters
117
122
  validate_timbr_connection_params(self._url, self._token)
118
123
 
119
- self._schema = schema
120
- self._concept = concept
121
- self._concepts_list = parse_list(concepts_list)
122
- self._views_list = parse_list(views_list)
123
- self._include_tags = parse_list(include_tags)
124
- self._include_logic_concepts = to_boolean(include_logic_concepts)
125
- self._should_validate_sql = to_boolean(should_validate_sql)
126
- self._exclude_properties = parse_list(exclude_properties)
127
- self._retries = to_integer(retries)
128
- self._max_limit = to_integer(max_limit)
129
- self._note = note
130
- self._db_is_case_sensitive = to_boolean(db_is_case_sensitive)
131
- self._graph_depth = to_integer(graph_depth)
132
124
  self._verify_ssl = to_boolean(verify_ssl)
133
125
  self._is_jwt = to_boolean(is_jwt)
134
126
  self._jwt_tenant_id = jwt_tenant_id
135
127
  self._debug = to_boolean(debug)
136
128
  self._conn_params = conn_params or {}
137
- self._enable_reasoning = to_boolean(enable_reasoning)
138
- self._reasoning_steps = to_integer(reasoning_steps)
129
+ self._max_limit = config.llm_default_limit # use default value so the self._get_conn_params() won't fail before agent options are processed
130
+
131
+ self._agent = agent
132
+ if self._agent:
133
+ agent_options = get_timbr_agent_options(self._agent, conn_params=self._get_conn_params())
134
+
135
+ self._ontology = agent_options.get("ontology") if "ontology" in agent_options else None
136
+ self._schema = agent_options.get("schema") if "schema" in agent_options else schema
137
+ self._concept = agent_options.get("concept") if "concept" in agent_options else None
138
+ self._concepts_list = parse_list(agent_options.get("concepts_list")) if "concepts_list" in agent_options else None
139
+ self._views_list = parse_list(agent_options.get("views_list")) if "views_list" in agent_options else None
140
+ self._include_tags = parse_list(agent_options.get("include_tags")) if "include_tags" in agent_options else None
141
+ self._include_logic_concepts = to_boolean(agent_options.get("include_logic_concepts")) if "include_logic_concepts" in agent_options else False
142
+ self._exclude_properties = parse_list(agent_options.get("exclude_properties")) if "exclude_properties" in agent_options else ['entity_id', 'entity_type', 'entity_label']
143
+ self._should_validate_sql = to_boolean(agent_options.get("should_validate_sql")) if "should_validate_sql" in agent_options else config.should_validate_sql
144
+ self._retries = to_integer(agent_options.get("retries") if "retries" in agent_options else retries)
145
+ self._max_limit = to_integer(agent_options.get("max_limit")) if "max_limit" in agent_options else config.llm_default_limit
146
+ self._db_is_case_sensitive = to_boolean(agent_options.get("db_is_case_sensitive")) if "db_is_case_sensitive" in agent_options else False
147
+ self._graph_depth = to_integer(agent_options.get("graph_depth")) if "graph_depth" in agent_options else 1
148
+ self._note = agent_options.get("note") if "note" in agent_options else ''
149
+ if note:
150
+ self._note = ((self._note + '\n') if self._note else '') + note
151
+ self._enable_reasoning = to_boolean(agent_options.get("enable_reasoning")) if "enable_reasoning" in agent_options else config.enable_reasoning
152
+ if enable_reasoning is not None and enable_reasoning != self._enable_reasoning:
153
+ self._enable_reasoning = to_boolean(enable_reasoning)
154
+ self._reasoning_steps = to_integer(agent_options.get("reasoning_steps")) if "reasoning_steps" in agent_options else config.reasoning_steps
155
+ if reasoning_steps is not None and reasoning_steps != self._reasoning_steps:
156
+ self._reasoning_steps = to_integer(reasoning_steps)
157
+ else:
158
+ self._ontology = ontology if ontology is not None else config.ontology
159
+ self._schema = schema
160
+ self._concept = concept
161
+ self._concepts_list = parse_list(concepts_list)
162
+ self._views_list = parse_list(views_list)
163
+ self._include_tags = parse_list(include_tags)
164
+ self._include_logic_concepts = to_boolean(include_logic_concepts)
165
+ self._exclude_properties = parse_list(exclude_properties)
166
+ self._should_validate_sql = to_boolean(should_validate_sql)
167
+ self._retries = to_integer(retries)
168
+ self._max_limit = to_integer(max_limit)
169
+ self._db_is_case_sensitive = to_boolean(db_is_case_sensitive)
170
+ self._graph_depth = to_integer(graph_depth)
171
+ self._enable_reasoning = to_boolean(enable_reasoning)
172
+ self._reasoning_steps = to_integer(reasoning_steps)
173
+ self._note = note
139
174
 
140
175
 
141
176
  @property
@@ -163,7 +198,7 @@ class GenerateTimbrSqlChain(Chain):
163
198
  return {
164
199
  "url": self._url,
165
200
  "token": self._token,
166
- "ontology": self._ontology,
201
+ "ontology": self._ontology if self._ontology is not None else config.ontology,
167
202
  "verify_ssl": self._verify_ssl,
168
203
  "is_jwt": self._is_jwt,
169
204
  "jwt_tenant_id": self._jwt_tenant_id,
@@ -206,6 +241,8 @@ class GenerateTimbrSqlChain(Chain):
206
241
  "concept": concept,
207
242
  "is_sql_valid": generate_res.get("is_sql_valid"),
208
243
  "error": generate_res.get("error"),
244
+ "identify_concept_reason": generate_res.get("identify_concept_reason"),
245
+ "generate_sql_reason": generate_res.get("generate_sql_reason"),
209
246
  "reasoning_status": generate_res.get("reasoning_status"),
210
247
  self.usage_metadata_key: generate_res.get("usage_metadata"),
211
248
  }
@@ -2,6 +2,8 @@ from typing import Optional, Union, Dict, Any
2
2
  from langchain.chains.base import Chain
3
3
  from langchain.llms.base import LLM
4
4
 
5
+ from langchain_timbr.utils.timbr_utils import get_timbr_agent_options
6
+
5
7
  from ..utils.general import parse_list, to_boolean, to_integer, validate_timbr_connection_params
6
8
  from ..utils.timbr_llm_utils import determine_concept
7
9
  from ..llm_wrapper.llm_wrapper import LlmWrapper
@@ -16,6 +18,8 @@ class IdentifyTimbrConceptChain(Chain):
16
18
  within a Timbr ontology/knowledge graph that best matches the user's intent. It uses an LLM
17
19
  to process prompts and connects to Timbr via URL and token for concept identification.
18
20
  """
21
+
22
+ _ontology: Optional[str] = None
19
23
 
20
24
  def __init__(
21
25
  self,
@@ -30,6 +34,7 @@ class IdentifyTimbrConceptChain(Chain):
30
34
  should_validate: Optional[bool] = False,
31
35
  retries: Optional[int] = 3,
32
36
  note: Optional[str] = '',
37
+ agent: Optional[str] = None,
33
38
  verify_ssl: Optional[bool] = True,
34
39
  is_jwt: Optional[bool] = False,
35
40
  jwt_tenant_id: Optional[str] = None,
@@ -49,6 +54,7 @@ class IdentifyTimbrConceptChain(Chain):
49
54
  :param should_validate: Whether to validate the identified concept before returning it
50
55
  :param retries: Number of retry attempts if the identified concept is invalid
51
56
  :param note: Optional additional note to extend our llm prompt
57
+ :param agent: Optional Timbr agent name for options setup.
52
58
  :param verify_ssl: Whether to verify SSL certificates (default is True).
53
59
  :param is_jwt: Whether to use JWT authentication (default is False).
54
60
  :param jwt_tenant_id: JWT tenant ID for multi-tenant environments (required when is_jwt=True).
@@ -93,24 +99,40 @@ class IdentifyTimbrConceptChain(Chain):
93
99
 
94
100
  self._url = url if url is not None else config.url
95
101
  self._token = token if token is not None else config.token
96
- self._ontology = ontology if ontology is not None else config.ontology
97
102
 
98
103
  # Validate required parameters
99
104
  validate_timbr_connection_params(self._url, self._token)
100
105
 
101
- self._concepts_list = parse_list(concepts_list)
102
- self._views_list = parse_list(views_list)
103
- self._include_logic_concepts = to_boolean(include_logic_concepts)
104
- self._include_tags = parse_list(include_tags)
105
- self._should_validate = to_boolean(should_validate)
106
- self._retries = to_integer(retries)
107
- self._note = note
108
106
  self._verify_ssl = to_boolean(verify_ssl)
109
107
  self._is_jwt = to_boolean(is_jwt)
110
108
  self._jwt_tenant_id = jwt_tenant_id
111
109
  self._debug = to_boolean(debug)
112
110
  self._conn_params = conn_params or {}
113
111
 
112
+ self._agent = agent
113
+ if self._agent:
114
+ agent_options = get_timbr_agent_options(self._agent, conn_params=self._get_conn_params())
115
+
116
+ self._ontology = agent_options.get("ontology") if "ontology" in agent_options else None
117
+ self._concepts_list = parse_list(agent_options.get("concepts_list")) if "concepts_list" in agent_options else None
118
+ self._views_list = parse_list(agent_options.get("views_list")) if "views_list" in agent_options else None
119
+ self._include_logic_concepts = to_boolean(agent_options.get("include_logic_concepts")) if "include_logic_concepts" in agent_options else False
120
+ self._include_tags = parse_list(agent_options.get("include_tags")) if "include_tags" in agent_options else None
121
+ self._should_validate = to_boolean(agent_options.get("should_validate")) if "should_validate" in agent_options else False
122
+ self._retries = to_integer(agent_options.get("retries") if "retries" in agent_options else retries)
123
+ self._note = agent_options.get("note") if "note" in agent_options else ''
124
+ if note:
125
+ self._note = ((self._note + '\n') if self._note else '') + note
126
+ else:
127
+ self._ontology = ontology if ontology is not None else config.ontology
128
+ self._concepts_list = parse_list(concepts_list)
129
+ self._views_list = parse_list(views_list)
130
+ self._include_logic_concepts = to_boolean(include_logic_concepts)
131
+ self._include_tags = parse_list(include_tags)
132
+ self._should_validate = to_boolean(should_validate)
133
+ self._retries = to_integer(retries)
134
+ self._note = note
135
+
114
136
 
115
137
  @property
116
138
  def usage_metadata_key(self) -> str:
@@ -131,7 +153,7 @@ class IdentifyTimbrConceptChain(Chain):
131
153
  return {
132
154
  "url": self._url,
133
155
  "token": self._token,
134
- "ontology": self._ontology,
156
+ "ontology": self._ontology if self._ontology is not None else config.ontology,
135
157
  "verify_ssl": self._verify_ssl,
136
158
  "is_jwt": self._is_jwt,
137
159
  "jwt_tenant_id": self._jwt_tenant_id,
@@ -31,6 +31,7 @@ class TimbrSqlAgent(BaseSingleActionAgent):
31
31
  note: Optional[str] = '',
32
32
  db_is_case_sensitive: Optional[bool] = False,
33
33
  graph_depth: Optional[int] = 1,
34
+ agent: Optional[str] = None,
34
35
  verify_ssl: Optional[bool] = True,
35
36
  is_jwt: Optional[bool] = False,
36
37
  jwt_tenant_id: Optional[str] = None,
@@ -60,6 +61,7 @@ class TimbrSqlAgent(BaseSingleActionAgent):
60
61
  :param note: Optional additional note to extend our llm prompt
61
62
  :param db_is_case_sensitive: Whether the database is case sensitive (default is False).
62
63
  :param graph_depth: Maximum number of relationship hops to traverse from the source concept during schema exploration (default is 1).
64
+ :param agent: Optional Timbr agent name for options setup.
63
65
  :param verify_ssl: Whether to verify SSL certificates (default is True).
64
66
  :param is_jwt: Whether to use JWT authentication (default is False).
65
67
  :param jwt_tenant_id: JWT tenant ID for multi-tenant environments (required when is_jwt=True).
@@ -114,6 +116,7 @@ class TimbrSqlAgent(BaseSingleActionAgent):
114
116
  note=note,
115
117
  db_is_case_sensitive=to_boolean(db_is_case_sensitive),
116
118
  graph_depth=to_integer(graph_depth),
119
+ agent=agent,
117
120
  verify_ssl=to_boolean(verify_ssl),
118
121
  is_jwt=to_boolean(is_jwt),
119
122
  jwt_tenant_id=jwt_tenant_id,
@@ -129,11 +132,12 @@ class TimbrSqlAgent(BaseSingleActionAgent):
129
132
  llm=llm,
130
133
  url=url,
131
134
  token=token,
135
+ note=note,
136
+ agent=agent,
132
137
  verify_ssl=to_boolean(verify_ssl),
133
138
  is_jwt=to_boolean(is_jwt),
134
139
  jwt_tenant_id=jwt_tenant_id,
135
140
  conn_params=conn_params,
136
- note=note,
137
141
  debug=to_boolean(debug),
138
142
  ) if self._generate_answer else None
139
143
 
@@ -181,6 +185,8 @@ class TimbrSqlAgent(BaseSingleActionAgent):
181
185
  "schema": None,
182
186
  "concept": None,
183
187
  "reasoning_status": None,
188
+ "identify_concept_reason": None,
189
+ "generate_sql_reason": None,
184
190
  "usage_metadata": {},
185
191
  },
186
192
  log="Empty input received"
@@ -210,6 +216,8 @@ class TimbrSqlAgent(BaseSingleActionAgent):
210
216
  "error": result.get("error", None),
211
217
  "reasoning_status": result.get("reasoning_status", None),
212
218
  "usage_metadata": usage_metadata,
219
+ "identify_concept_reason": None,
220
+ "generate_sql_reason": None,
213
221
  },
214
222
  log=f"Successfully executed query on concept: {result.get('concept', '')}"
215
223
  )
@@ -224,6 +232,8 @@ class TimbrSqlAgent(BaseSingleActionAgent):
224
232
  "schema": None,
225
233
  "concept": None,
226
234
  "reasoning_status": None,
235
+ "identify_concept_reason": None,
236
+ "generate_sql_reason": None,
227
237
  "usage_metadata": {},
228
238
  },
229
239
  log=error_context
@@ -245,6 +255,8 @@ class TimbrSqlAgent(BaseSingleActionAgent):
245
255
  "schema": None,
246
256
  "concept": None,
247
257
  "reasoning_status": None,
258
+ "identify_concept_reason": None,
259
+ "generate_sql_reason": None,
248
260
  "usage_metadata": {},
249
261
  },
250
262
  log="Empty or whitespace-only input received"
@@ -286,6 +298,8 @@ class TimbrSqlAgent(BaseSingleActionAgent):
286
298
  "concept": result.get("concept", ""),
287
299
  "error": result.get("error", None),
288
300
  "reasoning_status": result.get("reasoning_status", None),
301
+ "identify_concept_reason": result.get("identify_concept_reason", None),
302
+ "generate_sql_reason": result.get("generate_sql_reason", None),
289
303
  "usage_metadata": usage_metadata,
290
304
  },
291
305
  log=f"Successfully executed query on concept: {result.get('concept', '')}"
@@ -301,6 +315,8 @@ class TimbrSqlAgent(BaseSingleActionAgent):
301
315
  "schema": None,
302
316
  "concept": None,
303
317
  "reasoning_status": None,
318
+ "identify_concept_reason": None,
319
+ "generate_sql_reason": None,
304
320
  "usage_metadata": {},
305
321
  },
306
322
  log=error_context
@@ -341,6 +357,7 @@ def create_timbr_sql_agent(
341
357
  note: Optional[str] = '',
342
358
  db_is_case_sensitive: Optional[bool] = False,
343
359
  graph_depth: Optional[int] = 1,
360
+ agent: Optional[str] = None,
344
361
  verify_ssl: Optional[bool] = True,
345
362
  is_jwt: Optional[bool] = False,
346
363
  jwt_tenant_id: Optional[str] = None,
@@ -372,6 +389,7 @@ def create_timbr_sql_agent(
372
389
  :param note: Optional additional note to extend our llm prompt
373
390
  :param db_is_case_sensitive: Whether the database is case sensitive (default is False).
374
391
  :param graph_depth: Maximum number of relationship hops to traverse from the source concept during schema exploration (default is 1).
392
+ :param agent: Optional Timbr agent name for options setup.
375
393
  :param verify_ssl: Whether to verify SSL certificates (default is True).
376
394
  :param is_jwt: Whether to use JWT authentication (default is False).
377
395
  :param jwt_tenant_id: JWT tenant ID for multi-tenant environments (required when is_jwt=True).
@@ -440,6 +458,7 @@ def create_timbr_sql_agent(
440
458
  note=note,
441
459
  db_is_case_sensitive=db_is_case_sensitive,
442
460
  graph_depth=graph_depth,
461
+ agent=agent,
443
462
  verify_ssl=verify_ssl,
444
463
  is_jwt=is_jwt,
445
464
  jwt_tenant_id=jwt_tenant_id,