chunksilo 2.1.2__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.
Potentially problematic release.
This version of chunksilo might be problematic. Click here for more details.
- {chunksilo-2.1.2/src/chunksilo.egg-info → chunksilo-2.1.3}/PKG-INFO +1 -1
- {chunksilo-2.1.2 → chunksilo-2.1.3}/pyproject.toml +1 -1
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/__init__.py +1 -1
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/search.py +38 -8
- {chunksilo-2.1.2 → chunksilo-2.1.3/src/chunksilo.egg-info}/PKG-INFO +1 -1
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_jira_integration.py +44 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/LICENSE +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/NOTICE +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/README.md +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/requirements.txt +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/setup.cfg +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/__main__.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/cfgload.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/cli.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/confluence_html_formatter.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/index.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo/server.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo.egg-info/SOURCES.txt +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo.egg-info/dependency_links.txt +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo.egg-info/entry_points.txt +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo.egg-info/requires.txt +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/src/chunksilo.egg-info/top_level.txt +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_chunk_location.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_confluence_html_formatter.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_error_handling.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_heading_path_integration.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_incremental_ingest.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_rag_metrics.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_retrieval_only.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_system.py +0 -0
- {chunksilo-2.1.2 → chunksilo-2.1.3}/test/test_utils.py +0 -0
|
@@ -389,8 +389,8 @@ def _prepare_jira_jql_query(query: str, config: dict[str, Any]) -> str:
|
|
|
389
389
|
"""Construct a JQL query from user search terms and configuration.
|
|
390
390
|
|
|
391
391
|
Uses Jira's 'text' field which searches across Summary, Description,
|
|
392
|
-
Environment, Comments, and all text custom fields.
|
|
393
|
-
|
|
392
|
+
Environment, Comments, and all text custom fields. Additionally detects
|
|
393
|
+
Jira issue keys (e.g., "ABEI-1660") and includes exact key searches.
|
|
394
394
|
|
|
395
395
|
Note: Fuzzy search operators (~) are deprecated in Jira Cloud but work
|
|
396
396
|
in Data Center/Server. ChunkSilo's semantic search (embeddings + reranker)
|
|
@@ -414,20 +414,35 @@ def _prepare_jira_jql_query(query: str, config: dict[str, Any]) -> str:
|
|
|
414
414
|
References:
|
|
415
415
|
- Jira text field: https://support.atlassian.com/jira-software-cloud/docs/search-for-work-items-using-the-text-field/
|
|
416
416
|
- JQL operators: https://support.atlassian.com/jira-software-cloud/docs/jql-operators/
|
|
417
|
+
- JQL key field: https://support.atlassian.com/jira-software-cloud/docs/search-by-issue-key/
|
|
417
418
|
"""
|
|
419
|
+
# Detect Jira issue keys in the query (e.g., "ABEI-1660", "PROJ-123")
|
|
420
|
+
# Pattern matches: 1+ uppercase letters/digits, hyphen, 1+ digits
|
|
421
|
+
# Case-insensitive matching, but preserve original case for extraction
|
|
422
|
+
issue_key_pattern = r'\b([A-Z][A-Z0-9]+-\d+)\b'
|
|
423
|
+
detected_keys = re.findall(issue_key_pattern, query, re.IGNORECASE)
|
|
424
|
+
|
|
425
|
+
# Build key search clauses for exact issue key matches
|
|
426
|
+
key_clauses = []
|
|
427
|
+
if detected_keys:
|
|
428
|
+
# Normalize to uppercase (Jira keys are case-insensitive)
|
|
429
|
+
unique_keys = list(dict.fromkeys(k.upper() for k in detected_keys))
|
|
430
|
+
key_clauses = [f'key = "{key}"' for key in unique_keys]
|
|
431
|
+
logger.debug(f"Detected Jira issue keys in query: {unique_keys}")
|
|
432
|
+
|
|
418
433
|
# Reuse Confluence query term preparation for stopword filtering
|
|
419
434
|
# This gives us a clean list of meaningful search terms
|
|
420
435
|
query_terms = _prepare_confluence_query_terms(query)
|
|
421
436
|
|
|
422
437
|
# Build the text search clause
|
|
423
438
|
# Using JQL 'text' field which searches across all text fields for broad recall
|
|
439
|
+
text_clause = ""
|
|
424
440
|
if not query_terms:
|
|
425
441
|
# No meaningful terms after filtering, use original query
|
|
426
442
|
escaped = query.strip().replace('"', '\\"')
|
|
427
|
-
if not
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
text_clause = f'text ~ "{escaped}"'
|
|
443
|
+
if escaped and not detected_keys:
|
|
444
|
+
# Only add text clause if we don't have issue keys
|
|
445
|
+
text_clause = f'text ~ "{escaped}"'
|
|
431
446
|
elif len(query_terms) == 1:
|
|
432
447
|
# Single term - simple text search
|
|
433
448
|
text_clause = f'text ~ "{query_terms[0]}"'
|
|
@@ -437,6 +452,21 @@ def _prepare_jira_jql_query(query: str, config: dict[str, Any]) -> str:
|
|
|
437
452
|
text_conditions = ' OR '.join([f'text ~ "{term}"' for term in query_terms])
|
|
438
453
|
text_clause = f'({text_conditions})'
|
|
439
454
|
|
|
455
|
+
# Combine key and text searches
|
|
456
|
+
if key_clauses and text_clause:
|
|
457
|
+
# Search both by key and text content
|
|
458
|
+
combined_clause = f'({" OR ".join(key_clauses)} OR {text_clause})'
|
|
459
|
+
elif key_clauses:
|
|
460
|
+
# Only key searches
|
|
461
|
+
combined_clause = " OR ".join(key_clauses)
|
|
462
|
+
elif text_clause:
|
|
463
|
+
# Only text search
|
|
464
|
+
combined_clause = text_clause
|
|
465
|
+
else:
|
|
466
|
+
# No valid search terms
|
|
467
|
+
logger.warning("Jira search skipped: empty query after processing")
|
|
468
|
+
return ""
|
|
469
|
+
|
|
440
470
|
# Add project filter if configured
|
|
441
471
|
# Empty projects list means search all accessible projects
|
|
442
472
|
projects = config["jira"].get("projects", [])
|
|
@@ -444,9 +474,9 @@ def _prepare_jira_jql_query(query: str, config: dict[str, Any]) -> str:
|
|
|
444
474
|
# Restrict search to specific project keys
|
|
445
475
|
project_list = ", ".join([f'"{p}"' for p in projects])
|
|
446
476
|
project_clause = f'project IN ({project_list})'
|
|
447
|
-
jql = f'{
|
|
477
|
+
jql = f'{combined_clause} AND {project_clause}'
|
|
448
478
|
else:
|
|
449
|
-
jql =
|
|
479
|
+
jql = combined_clause
|
|
450
480
|
|
|
451
481
|
# Order by updated DESC for recency
|
|
452
482
|
# This enables ChunkSilo's recency boost feature and returns most relevant recent issues first
|
|
@@ -186,6 +186,50 @@ class TestJiraJqlQuery:
|
|
|
186
186
|
# Query with only stopwords should produce simple or empty query
|
|
187
187
|
assert jql == "" or "ORDER BY updated DESC" in jql
|
|
188
188
|
|
|
189
|
+
def test_issue_key_detection_single(self, base_config):
|
|
190
|
+
"""Single issue key should be detected and searched by key field."""
|
|
191
|
+
jql = _prepare_jira_jql_query("ABEI-1660", base_config)
|
|
192
|
+
assert 'key = "ABEI-1660"' in jql
|
|
193
|
+
assert "ORDER BY updated DESC" in jql
|
|
194
|
+
|
|
195
|
+
def test_issue_key_detection_lowercase(self, base_config):
|
|
196
|
+
"""Lowercase issue key should be normalized to uppercase."""
|
|
197
|
+
jql = _prepare_jira_jql_query("abei-1660", base_config)
|
|
198
|
+
assert 'key = "ABEI-1660"' in jql
|
|
199
|
+
assert "ORDER BY updated DESC" in jql
|
|
200
|
+
|
|
201
|
+
def test_issue_key_detection_multiple(self, base_config):
|
|
202
|
+
"""Multiple issue keys should be detected."""
|
|
203
|
+
jql = _prepare_jira_jql_query("ABEI-1660 PROJ-123", base_config)
|
|
204
|
+
assert 'key = "ABEI-1660"' in jql
|
|
205
|
+
assert 'key = "PROJ-123"' in jql
|
|
206
|
+
assert " OR " in jql
|
|
207
|
+
assert "ORDER BY updated DESC" in jql
|
|
208
|
+
|
|
209
|
+
def test_issue_key_mixed_with_text(self, base_config):
|
|
210
|
+
"""Issue key mixed with text should search both key and text."""
|
|
211
|
+
jql = _prepare_jira_jql_query("ABEI-1660 authentication", base_config)
|
|
212
|
+
assert 'key = "ABEI-1660"' in jql
|
|
213
|
+
assert 'text ~ "authentication"' in jql
|
|
214
|
+
assert " OR " in jql
|
|
215
|
+
assert "ORDER BY updated DESC" in jql
|
|
216
|
+
|
|
217
|
+
def test_no_issue_key_detection(self, base_config):
|
|
218
|
+
"""Non-issue-key queries should work as before."""
|
|
219
|
+
jql = _prepare_jira_jql_query("authentication bug", base_config)
|
|
220
|
+
assert "key =" not in jql # No key search
|
|
221
|
+
assert "text ~" in jql # Text search only
|
|
222
|
+
assert "ORDER BY updated DESC" in jql
|
|
223
|
+
|
|
224
|
+
def test_issue_key_with_project_filter(self, base_config):
|
|
225
|
+
"""Issue key search should respect project filter."""
|
|
226
|
+
base_config["jira"]["projects"] = ["ABEI"]
|
|
227
|
+
jql = _prepare_jira_jql_query("ABEI-1660", base_config)
|
|
228
|
+
assert 'key = "ABEI-1660"' in jql
|
|
229
|
+
assert "project IN" in jql
|
|
230
|
+
assert "ABEI" in jql
|
|
231
|
+
assert "ORDER BY updated DESC" in jql
|
|
232
|
+
|
|
189
233
|
|
|
190
234
|
# ============================================================================
|
|
191
235
|
# ISSUE TO TEXT CONVERSION TESTS
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|