langchain-timbr 2.1.1__tar.gz → 2.1.3__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.
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/PKG-INFO +1 -1
- langchain_timbr-2.1.3/SECURITY.md +19 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/requirements.txt +2 -1
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/_version.py +2 -2
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/llm_wrapper/llm_wrapper.py +1 -1
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/utils/general.py +142 -9
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_jwt_token.py +2 -1
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/.github/dependabot.yml +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/.github/pull_request_template.md +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/.github/workflows/_codespell.yml +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/.github/workflows/_fossa.yml +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/.github/workflows/install-dependencies-and-run-tests.yml +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/.github/workflows/publish.yml +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/.gitignore +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/LICENSE +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/README.md +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/pyproject.toml +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/pytest.ini +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/requirements310.txt +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/requirements311.txt +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/__init__.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/config.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/__init__.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/execute_timbr_query_chain.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/generate_answer_chain.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/generate_timbr_sql_chain.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/identify_concept_chain.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/timbr_sql_agent.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/validate_timbr_sql_chain.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langgraph/__init__.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langgraph/execute_timbr_query_node.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langgraph/generate_response_node.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langgraph/generate_timbr_sql_node.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langgraph/identify_concept_node.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langgraph/validate_timbr_query_node.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/llm_wrapper/timbr_llm_wrapper.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/timbr_llm_connector.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/utils/prompt_service.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/utils/temperature_supported_models.json +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/utils/timbr_llm_utils.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/utils/timbr_utils.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/README.md +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/conftest.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_agent_integration.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_azure_databricks_provider.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_azure_openai_model.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_chain_pipeline.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_langchain_chains.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_langgraph_nodes.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_timeout_functionality.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/conftest.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_chain_documentation.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_connection_validation.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_llm_wrapper_optional_params.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_optional_llm_integration.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_standard_chain_requirements.py +0 -0
- {langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/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.
|
|
3
|
+
Version: 2.1.3
|
|
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/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Langchain-Timbr Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| < 3.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
If you discover a security vulnerability, please email `contact@timbr.ai` with the subject line `Langchain-Timbr Vulnerability Report`. In the email please, include:
|
|
12
|
+
- The `Langchain-Timbr` version used.
|
|
13
|
+
- **Steps to reproduce** the issue with sufficient detail to verify it
|
|
14
|
+
- **Impact and severity** assessment (what an attacker can do)
|
|
15
|
+
- **Suggested fix** or mitigation (when available)
|
|
16
|
+
- Any proof-of-concept code, logs, or screenshots that help **reproduce the problem**
|
|
17
|
+
|
|
18
|
+
_Please note: We do not currently operate a formal bug bounty program. For valid reports, we will review the submission promptly and consider compensation on a case-by-case basis.
|
|
19
|
+
Thank you for helping us keep Timbr secure._
|
|
@@ -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.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 1,
|
|
31
|
+
__version__ = version = '2.1.3'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 1, 3)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/llm_wrapper/llm_wrapper.py
RENAMED
|
@@ -203,7 +203,7 @@ class LlmWrapper(LLM):
|
|
|
203
203
|
if azure_tenant_id and azure_client_id:
|
|
204
204
|
from azure.identity import ClientSecretCredential, get_bearer_token_provider
|
|
205
205
|
azure_client_secret = pop_param_value(params, ['azure_client_secret', 'llm_client_secret'], default=api_key)
|
|
206
|
-
scope = pop_param_value(params, ['azure_scope', 'llm_scope'], default=config.llm_scope)
|
|
206
|
+
scope = pop_param_value(params, ['azure_scope', 'llm_scope', 'scope'], default=config.llm_scope)
|
|
207
207
|
credential = ClientSecretCredential(
|
|
208
208
|
tenant_id=azure_tenant_id,
|
|
209
209
|
client_id=azure_client_id,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Any, Optional, Union
|
|
3
3
|
import json
|
|
4
|
+
import re
|
|
4
5
|
|
|
5
6
|
### A global helper functions to use across the project
|
|
6
7
|
|
|
@@ -34,6 +35,7 @@ def to_integer(value) -> int:
|
|
|
34
35
|
def parse_additional_params(value) -> dict:
|
|
35
36
|
"""
|
|
36
37
|
Parse additional parameters from string format 'a=1,b=2' or return dict as-is.
|
|
38
|
+
Handles JSON values correctly, including nested structures with commas.
|
|
37
39
|
|
|
38
40
|
Args:
|
|
39
41
|
value: String in format 'key=value,key2=value2', JSON string, or dict
|
|
@@ -49,25 +51,156 @@ def parse_additional_params(value) -> dict:
|
|
|
49
51
|
stripped_value = value.strip()
|
|
50
52
|
if stripped_value.startswith('{') and stripped_value.endswith('}'):
|
|
51
53
|
try:
|
|
52
|
-
return
|
|
54
|
+
return _try_parse_json_value(stripped_value)
|
|
53
55
|
except json.JSONDecodeError:
|
|
54
56
|
pass
|
|
55
57
|
|
|
56
|
-
#
|
|
58
|
+
# Check if complex parsing is needed (presence of nested structures)
|
|
59
|
+
needs_complex_parsing = any(char in value for char in ['{', '}', '[', ']', '(', ')'])
|
|
60
|
+
|
|
61
|
+
if not needs_complex_parsing:
|
|
62
|
+
# Fast path: simple key=value pairs
|
|
63
|
+
params = {}
|
|
64
|
+
for pair in value.split(','):
|
|
65
|
+
if '=' in pair:
|
|
66
|
+
key, val = pair.split('=', 1)
|
|
67
|
+
params[key.strip().lower()] = _try_parse_json_value(val.strip())
|
|
68
|
+
return params
|
|
69
|
+
|
|
70
|
+
# Complex parsing that handles JSON values with commas
|
|
57
71
|
params = {}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
i = 0
|
|
73
|
+
while i < len(value):
|
|
74
|
+
# Find the next key=value pair
|
|
75
|
+
equals_pos = value.find('=', i)
|
|
76
|
+
if equals_pos == -1:
|
|
77
|
+
break
|
|
78
|
+
|
|
79
|
+
# Extract the key
|
|
80
|
+
key = value[i:equals_pos].strip()
|
|
81
|
+
|
|
82
|
+
# Find where the value starts
|
|
83
|
+
value_start = equals_pos + 1
|
|
84
|
+
|
|
85
|
+
# Determine where the value ends (considering nested structures)
|
|
86
|
+
value_end = _find_value_end(value, value_start)
|
|
87
|
+
|
|
88
|
+
# Extract and parse the value
|
|
89
|
+
val = value[value_start:value_end].strip()
|
|
90
|
+
|
|
91
|
+
# Try to parse the value as JSON if it looks like JSON
|
|
92
|
+
parsed_val = _try_parse_json_value(val)
|
|
93
|
+
|
|
94
|
+
params[key.lower()] = parsed_val
|
|
95
|
+
|
|
96
|
+
# Move to the next parameter (skip comma if present)
|
|
97
|
+
i = value_end
|
|
98
|
+
if i < len(value) and value[i] == ',':
|
|
99
|
+
i += 1
|
|
100
|
+
|
|
65
101
|
return params
|
|
66
102
|
return {}
|
|
67
103
|
except Exception as e:
|
|
68
104
|
raise ValueError(f"Failed to parse additional parameters: {e}")
|
|
69
105
|
|
|
70
106
|
|
|
107
|
+
def _find_value_end(text: str, start: int) -> int:
|
|
108
|
+
"""
|
|
109
|
+
Find the end position of a parameter value, considering nested structures.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
text: The full text being parsed
|
|
113
|
+
start: Starting position of the value
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
End position of the value
|
|
117
|
+
"""
|
|
118
|
+
depth = {'brace': 0, 'bracket': 0, 'paren': 0}
|
|
119
|
+
in_quotes = False
|
|
120
|
+
quote_char = None
|
|
121
|
+
i = start
|
|
122
|
+
|
|
123
|
+
while i < len(text):
|
|
124
|
+
char = text[i]
|
|
125
|
+
|
|
126
|
+
# Handle quotes
|
|
127
|
+
if char in ('"', "'"):
|
|
128
|
+
if not in_quotes:
|
|
129
|
+
in_quotes = True
|
|
130
|
+
quote_char = char
|
|
131
|
+
elif char == quote_char:
|
|
132
|
+
# Check if it's escaped
|
|
133
|
+
if i > 0 and text[i-1] != '\\':
|
|
134
|
+
in_quotes = False
|
|
135
|
+
quote_char = None
|
|
136
|
+
|
|
137
|
+
# Only process structural characters if not in quotes
|
|
138
|
+
elif not in_quotes:
|
|
139
|
+
if char == '{':
|
|
140
|
+
depth['brace'] += 1
|
|
141
|
+
elif char == '}':
|
|
142
|
+
depth['brace'] -= 1
|
|
143
|
+
elif char == '[':
|
|
144
|
+
depth['bracket'] += 1
|
|
145
|
+
elif char == ']':
|
|
146
|
+
depth['bracket'] -= 1
|
|
147
|
+
elif char == '(':
|
|
148
|
+
depth['paren'] += 1
|
|
149
|
+
elif char == ')':
|
|
150
|
+
depth['paren'] -= 1
|
|
151
|
+
elif char == ',' and all(d == 0 for d in depth.values()):
|
|
152
|
+
# Found a comma at the top level - this is the end of the value
|
|
153
|
+
return i
|
|
154
|
+
|
|
155
|
+
i += 1
|
|
156
|
+
|
|
157
|
+
return i
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _try_parse_json_value(val: str) -> Any:
|
|
161
|
+
"""
|
|
162
|
+
Try to parse a string value as JSON. If it fails, return the original string.
|
|
163
|
+
Supports both single and double quotes for JSON-like structures.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
val: String value to parse
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Parsed JSON value or original string
|
|
170
|
+
"""
|
|
171
|
+
val = val.strip()
|
|
172
|
+
|
|
173
|
+
# Check if it looks like JSON
|
|
174
|
+
if (val.startswith('{') and val.endswith('}')) or \
|
|
175
|
+
(val.startswith('[') and val.endswith(']')) or \
|
|
176
|
+
val in ('true', 'false', 'null') or \
|
|
177
|
+
(val.startswith('"') and val.endswith('"')):
|
|
178
|
+
try:
|
|
179
|
+
return json.loads(val)
|
|
180
|
+
except json.JSONDecodeError:
|
|
181
|
+
# Try converting single quotes to double quotes for Python-style dicts
|
|
182
|
+
if val.startswith('{') or val.startswith('['):
|
|
183
|
+
try:
|
|
184
|
+
# Replace single quotes with double quotes, handling escaped quotes
|
|
185
|
+
normalized = val.replace("\\'", "<<<ESCAPED_SINGLE>>>")
|
|
186
|
+
normalized = normalized.replace("'", '"')
|
|
187
|
+
normalized = normalized.replace("<<<ESCAPED_SINGLE>>>", "'")
|
|
188
|
+
return json.loads(normalized)
|
|
189
|
+
except json.JSONDecodeError:
|
|
190
|
+
pass
|
|
191
|
+
# Try to parse as number - validate format first
|
|
192
|
+
if re.match(r'^-?\d+(\.\d+)?$', val):
|
|
193
|
+
try:
|
|
194
|
+
if '.' in val:
|
|
195
|
+
return float(val)
|
|
196
|
+
return int(val)
|
|
197
|
+
except ValueError:
|
|
198
|
+
pass
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
return val
|
|
202
|
+
|
|
203
|
+
|
|
71
204
|
def is_llm_type(llm_type, enum_value):
|
|
72
205
|
"""Check if llm_type equals the enum value or its name, case-insensitive."""
|
|
73
206
|
if llm_type == enum_value:
|
|
@@ -5,7 +5,8 @@ from langchain_timbr import create_timbr_sql_agent
|
|
|
5
5
|
class TestTimbrSqlAgentJWTAuthentication:
|
|
6
6
|
"""Test suite for Timbr SQL Agent JWT authentication functionality."""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
# Due to policy changes and MFA requirements, this test is currently skipped (still running at timbr-chainlit repository).
|
|
9
|
+
def skip_test_timbr_sql_agent_integration(self, llm, config):
|
|
9
10
|
"""Test Timbr SQL Agent integration with JWT authentication."""
|
|
10
11
|
# Azure AD token endpoint URL
|
|
11
12
|
token_url = f'https://login.microsoftonline.com/{config["jwt_tenant_id"]}/oauth2/v2.0/token'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/langchain/timbr_sql_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/llm_wrapper/timbr_llm_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/src/langchain_timbr/utils/timbr_llm_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_azure_databricks_provider.py
RENAMED
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_azure_openai_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/integration/test_timeout_functionality.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_connection_validation.py
RENAMED
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_llm_wrapper_optional_params.py
RENAMED
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_optional_llm_integration.py
RENAMED
|
File without changes
|
{langchain_timbr-2.1.1 → langchain_timbr-2.1.3}/tests/standard/test_standard_chain_requirements.py
RENAMED
|
File without changes
|
|
File without changes
|