Skip to content

Commit f88d5f1

Browse files
Merge pull request #115 from MarcSkovMadsen/enhancement/truncation
Add truncate of large search results
2 parents 021f7e8 + 84cffc6 commit f88d5f1

7 files changed

Lines changed: 967 additions & 54 deletions

File tree

.mcp.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"mcpServers": {
3+
"holoviz": {
4+
"type": "stdio",
5+
"command": ".pixi/envs/default/bin/holoviz-mcp",
6+
"args": [],
7+
"env": {}
8+
}
9+
}
10+
}

src/holoviz_mcp/apps/holoviz_search.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@
4141
- **`project`**: Filter results by specific project (e.g., "panel", "hvplot", "datashader") or search across all projects
4242
- **`max_results`**: Control the number of results returned (1-50 documents)
4343
- **`content`**: Choose whether to include full document content or just metadata for faster responses
44+
- **`max_content_chars`**: Maximum characters of content per result (100-50,000). Smaller values provide faster responses. Content is truncated at word boundaries with query-relevant excerpts.
45+
46+
### Query Optimization Tips
47+
48+
For best results with content truncation, use **specific terms** rather than generic phrases:
49+
50+
- ✅ **Good**: "Button onClick on_click callback event" - specific class and method names
51+
- ✅ **Good**: "Tabulator pagination page_size local remote" - feature-specific terms
52+
- ❌ **Avoid**: "how to add pagination to a table" - too generic, common words
53+
54+
**Why This Matters**: The search uses context-aware truncation that centers content on your query keywords. Specific terms help the system extract the most relevant sections of documentation.
55+
56+
**Best Practices**:
57+
- Use class/function names: "RangeSlider add_filter" > "filtering with widgets"
58+
- Use unique identifiers: "background_gradient text_gradient" > "styling colors"
59+
- Avoid common terms: "pandas", "import", "data" appear everywhere
60+
- Use multiple specific terms to help locate the right section
4461
4562
### What's Indexed
4663
@@ -84,12 +101,18 @@ class SearchConfiguration(param.Parameterized):
84101
doc="Filter results to a specific project. Select 'all' for all projects.",
85102
)
86103

87-
max_results = param.Integer(default=5, bounds=(1, 50), doc="Maximum number of search results to return")
104+
max_results = param.Integer(default=2, bounds=(1, 50), doc="Maximum number of search results to return")
88105

89106
content = param.Boolean(
90107
default=True, label="Include Full Content", doc="Include full document content in results. Disable for faster and simpler responses with metadata only."
91108
)
92109

110+
max_content_chars = param.Integer(
111+
default=10000,
112+
bounds=(100, 50000),
113+
doc="Maximum characters of content per result. Use smaller values for faster responses. Set to None for full content (may cause token limit errors).",
114+
)
115+
93116
search = param.Event(doc="Event to trigger search when parameters change")
94117

95118
results = param.List(item_type=Document, doc="Search results as a list of Documents", precedence=-1)
@@ -99,7 +122,7 @@ def __init__(self, **params):
99122
super().__init__(**params)
100123

101124
if pn.state.location:
102-
pn.state.location.sync(self, parameters=["query", "project", "content", "max_results"])
125+
pn.state.location.sync(self, parameters=["query", "project", "content", "max_results", "max_content_chars"])
103126

104127
if self.query:
105128
self.param.trigger("search")
@@ -108,7 +131,9 @@ def __init__(self, **params):
108131
async def _update_results(self):
109132
indexer = _get_indexer()
110133
project = self.project if self.project != ALL else None
111-
self.results = await indexer.search(self.query, project=project, content=self.content, max_results=self.max_results)
134+
self.results = await indexer.search(
135+
self.query, project=project, content=self.content, max_results=self.max_results, max_content_chars=self.max_content_chars
136+
)
112137

113138

114139
async def _update_projects(self):
@@ -158,13 +183,13 @@ def _to_secondary(document: Document):
158183
"""Convert a Document to a secondary text for the menu item."""
159184
return f"""{document.description}
160185
161-
Relevance Score: {document.relevance_score or 'N/A':0.2f}
186+
Relevance Score: {document.relevance_score or "N/A":0.2f}
162187
"""
163188

164189
@param.depends("documents")
165190
def _items(self):
166191
return [
167-
{"label": f"{index+1}. {document.project}: {document.title}", "icon": None, "secondary": self._to_secondary(document)}
192+
{"label": f"{index + 1}. {document.project}: {document.title}", "icon": None, "secondary": self._to_secondary(document)}
168193
for index, document in enumerate(self.documents)
169194
]
170195

@@ -279,6 +304,7 @@ async def _config(self):
279304
widgets={
280305
"query": {"type": pmui.TextAreaInput, "rows": 3, "placeholder": "Enter search query ..."},
281306
"search": {"type": pmui.Button, "label": "Search", "button_type": "primary"},
307+
"max_content_chars": {"type": pn.widgets.IntInput, "step": 1000, "start": 100, "end": 50000, "placeholder": "Characters per result"},
282308
},
283309
)
284310

0 commit comments

Comments
 (0)