docsif 0.1.0__py3-none-any.whl
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.
- docsif-0.1.0.dist-info/METADATA +393 -0
- docsif-0.1.0.dist-info/RECORD +86 -0
- docsif-0.1.0.dist-info/WHEEL +4 -0
- docsif-0.1.0.dist-info/entry_points.txt +3 -0
- docsif-0.1.0.dist-info/licenses/LICENSE +21 -0
- sif/__init__.py +24 -0
- sif/_version.py +3 -0
- sif/cli/__init__.py +11 -0
- sif/cli/commands/__init__.py +18 -0
- sif/cli/commands/bench.py +148 -0
- sif/cli/commands/collection.py +360 -0
- sif/cli/commands/context.py +154 -0
- sif/cli/commands/get.py +200 -0
- sif/cli/commands/index.py +294 -0
- sif/cli/commands/ls.py +67 -0
- sif/cli/commands/mcp.py +93 -0
- sif/cli/commands/pull.py +111 -0
- sif/cli/commands/search.py +574 -0
- sif/cli/config.py +206 -0
- sif/cli/formatters.py +269 -0
- sif/cli/main.py +205 -0
- sif/config/__init__.py +30 -0
- sif/config/constants.py +52 -0
- sif/config/settings.py +217 -0
- sif/core/__init__.py +21 -0
- sif/core/collection.py +161 -0
- sif/core/context.py +167 -0
- sif/core/document.py +125 -0
- sif/core/models.py +298 -0
- sif/database/__init__.py +23 -0
- sif/database/connection.py +139 -0
- sif/database/database.py +136 -0
- sif/database/migrations.py +187 -0
- sif/database/repositories.py +500 -0
- sif/database/repository.py +171 -0
- sif/database/schema.py +362 -0
- sif/embedding/__init__.py +22 -0
- sif/embedding/cache.py +196 -0
- sif/embedding/embedder.py +328 -0
- sif/embedding/factory.py +92 -0
- sif/embedding/manager.py +200 -0
- sif/embedding/model.py +126 -0
- sif/indexing/__init__.py +21 -0
- sif/indexing/chunker.py +226 -0
- sif/indexing/indexer.py +226 -0
- sif/indexing/parser.py +192 -0
- sif/indexing/scanner.py +148 -0
- sif/indexing/watcher.py +156 -0
- sif/mcp/README.md +258 -0
- sif/mcp/__init__.py +9 -0
- sif/mcp/claude_desktop_config.json +16 -0
- sif/mcp/cli.py +92 -0
- sif/mcp/protocol.py +336 -0
- sif/mcp/server.py +433 -0
- sif/mcp/server_http.py +54 -0
- sif/mcp/test_mcp.py +108 -0
- sif/mcp/tools.py +497 -0
- sif/mcp/transport_http.py +424 -0
- sif/mcp/transport_stdio.py +292 -0
- sif/mcp_server/__init__.py +26 -0
- sif/mcp_server/handlers.py +68 -0
- sif/mcp_server/server.py +58 -0
- sif/mcp_server/tools.py +216 -0
- sif/mcp_server/transport.py +221 -0
- sif/models/__init__.py +70 -0
- sif/models/collection.py +51 -0
- sif/models/context.py +45 -0
- sif/models/document.py +76 -0
- sif/models/download.py +208 -0
- sif/models/embedding.py +71 -0
- sif/models/search.py +92 -0
- sif/search/__init__.py +22 -0
- sif/search/benchmark.py +110 -0
- sif/search/bm25.py +216 -0
- sif/search/expansion.py +157 -0
- sif/search/hybrid.py +323 -0
- sif/search/rerank.py +390 -0
- sif/search/rrf.py +186 -0
- sif/search/snippets.py +130 -0
- sif/search/strategy.py +97 -0
- sif/search/vector.py +177 -0
- sif/utils/__init__.py +26 -0
- sif/utils/logging.py +143 -0
- sif/utils/paths.py +82 -0
- sif/utils/progress.py +83 -0
- sif/utils/text.py +128 -0
sif/utils/text.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Text processing utilities for SIF."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from re import Pattern
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Compile regex patterns once
|
|
9
|
+
WHITESPACE_PATTERN: Pattern[str] = re.compile(r"\s+")
|
|
10
|
+
NON_ALPHANUMERIC_PATTERN: Pattern[str] = re.compile(r"[^\w\s-]")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def normalize_text(text: str) -> str:
|
|
14
|
+
"""Normalize text by cleaning whitespace and special characters.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
text: Input text
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Normalized text
|
|
21
|
+
"""
|
|
22
|
+
# Replace multiple whitespace with single space
|
|
23
|
+
text = WHITESPACE_PATTERN.sub(" ", text)
|
|
24
|
+
# Strip leading/trailing whitespace
|
|
25
|
+
text = text.strip()
|
|
26
|
+
return text
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def truncate_text(text: str, max_length: int, suffix: str = "...") -> str:
|
|
30
|
+
"""Truncate text to maximum length.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
text: Input text
|
|
34
|
+
max_length: Maximum length
|
|
35
|
+
suffix: Suffix to add if truncated
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Truncated text
|
|
39
|
+
"""
|
|
40
|
+
if len(text) <= max_length:
|
|
41
|
+
return text
|
|
42
|
+
|
|
43
|
+
truncated = text[: max_length - len(suffix)]
|
|
44
|
+
return truncated + suffix
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def count_tokens(text: str, tokenizer: Any | None = None) -> int:
|
|
48
|
+
"""Count tokens in text.
|
|
49
|
+
|
|
50
|
+
Uses a rough estimate if no tokenizer is provided.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
text: Input text
|
|
54
|
+
tokenizer: Optional tokenizer to use
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Estimated token count
|
|
58
|
+
"""
|
|
59
|
+
if tokenizer is not None:
|
|
60
|
+
try:
|
|
61
|
+
return len(tokenizer.encode(text))
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
# Rough estimate: 1 token ~= 4 characters for English text
|
|
66
|
+
return len(text) // 4
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def split_into_sentences(text: str) -> list[str]:
|
|
70
|
+
"""Split text into sentences.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
text: Input text
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
List of sentences
|
|
77
|
+
"""
|
|
78
|
+
# Simple sentence splitting on punctuation
|
|
79
|
+
sentences = re.split(r"(?<=[.!?])\s+", text)
|
|
80
|
+
return [s.strip() for s in sentences if s.strip()]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def extract_code_blocks(text: str) -> list[tuple[str, str]]:
|
|
84
|
+
"""Extract code blocks from markdown text.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
text: Markdown text
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
List of (language, code) tuples
|
|
91
|
+
"""
|
|
92
|
+
pattern = r"```(\w+)?\n(.*?)```"
|
|
93
|
+
matches = re.findall(pattern, text, re.DOTALL)
|
|
94
|
+
return [(lang or "text", code.strip()) for lang, code in matches]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def remove_code_blocks(text: str) -> str:
|
|
98
|
+
"""Remove code blocks from markdown text.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
text: Markdown text
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Text without code blocks
|
|
105
|
+
"""
|
|
106
|
+
pattern = r"```.*?```"
|
|
107
|
+
return re.sub(pattern, "", text, flags=re.DOTALL).strip()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def slugify(text: str) -> str:
|
|
111
|
+
"""Convert text to URL-friendly slug.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
text: Input text
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Slugified text
|
|
118
|
+
"""
|
|
119
|
+
# Convert to lowercase
|
|
120
|
+
text = text.lower()
|
|
121
|
+
# Remove non-alphanumeric characters except hyphens
|
|
122
|
+
text = NON_ALPHANUMERIC_PATTERN.sub("", text)
|
|
123
|
+
# Replace spaces with hyphens
|
|
124
|
+
text = text.replace(" ", "-")
|
|
125
|
+
# Remove multiple hyphens
|
|
126
|
+
text = re.sub(r"-+", "-", text)
|
|
127
|
+
# Strip leading/trailing hyphens
|
|
128
|
+
return text.strip("-")
|