perf: Exclude vector-sync status polling from distributed tracing
Skip tracing for /app/vector-sync/status to reduce noise from HTMX polling. Metrics collection continues for this endpoint. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -34,7 +34,7 @@ services:
|
|||||||
- ./app-hooks:/docker-entrypoint-hooks.d:ro
|
- ./app-hooks:/docker-entrypoint-hooks.d:ro
|
||||||
# Mount OIDC development directory outside /var/www/html to avoid rsync conflicts
|
# Mount OIDC development directory outside /var/www/html to avoid rsync conflicts
|
||||||
# The post-installation hook will register /opt/apps as an additional app directory
|
# The post-installation hook will register /opt/apps as an additional app directory
|
||||||
- ./third_party:/opt/apps:ro
|
#- ./third_party:/opt/apps:ro
|
||||||
environment:
|
environment:
|
||||||
- NEXTCLOUD_TRUSTED_DOMAINS=app
|
- NEXTCLOUD_TRUSTED_DOMAINS=app
|
||||||
- NEXTCLOUD_ADMIN_USER=admin
|
- NEXTCLOUD_ADMIN_USER=admin
|
||||||
@@ -82,6 +82,7 @@ services:
|
|||||||
- NEXTCLOUD_HOST=http://app:80
|
- NEXTCLOUD_HOST=http://app:80
|
||||||
- NEXTCLOUD_USERNAME=admin
|
- NEXTCLOUD_USERNAME=admin
|
||||||
- NEXTCLOUD_PASSWORD=admin
|
- NEXTCLOUD_PASSWORD=admin
|
||||||
|
- NEXTCLOUD_PUBLIC_ISSUER_URL=http://localhost:8080
|
||||||
|
|
||||||
# Vector sync configuration (ADR-007)
|
# Vector sync configuration (ADR-007)
|
||||||
- VECTOR_SYNC_ENABLED=true
|
- VECTOR_SYNC_ENABLED=true
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
else "unknown"
|
else "unknown"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get Nextcloud host for generating links to apps
|
||||||
|
# Use public issuer URL if available (for browser-accessible links),
|
||||||
|
# otherwise fall back to NEXTCLOUD_HOST
|
||||||
|
import os
|
||||||
|
|
||||||
|
nextcloud_host = os.getenv("NEXTCLOUD_PUBLIC_ISSUER_URL") or settings.nextcloud_host
|
||||||
|
|
||||||
html_content = f"""
|
html_content = f"""
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -93,11 +100,15 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
}}
|
}}
|
||||||
.controls {{
|
.controls {{
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}}
|
}}
|
||||||
|
.control-row {{
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2fr 1fr auto;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
align-items: end;
|
||||||
|
}}
|
||||||
.control-group {{
|
.control-group {{
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}}
|
}}
|
||||||
@@ -107,7 +118,7 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
}}
|
}}
|
||||||
input[type="text"], select {{
|
input[type="text"], input[type="number"], select {{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
@@ -117,6 +128,9 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
input[type="range"] {{
|
input[type="range"] {{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}}
|
}}
|
||||||
|
select[multiple] {{
|
||||||
|
min-height: 100px;
|
||||||
|
}}
|
||||||
.weight-display {{
|
.weight-display {{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
@@ -136,6 +150,19 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
.btn:hover {{
|
.btn:hover {{
|
||||||
background: #0052a3;
|
background: #0052a3;
|
||||||
}}
|
}}
|
||||||
|
.btn-secondary {{
|
||||||
|
background: #6c757d;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}}
|
||||||
|
.btn-secondary:hover {{
|
||||||
|
background: #5a6268;
|
||||||
|
}}
|
||||||
#plot {{
|
#plot {{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 600px;
|
height: 600px;
|
||||||
@@ -145,11 +172,17 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
padding: 40px;
|
padding: 40px;
|
||||||
color: #666;
|
color: #666;
|
||||||
}}
|
}}
|
||||||
.weight-controls {{
|
.advanced-section {{
|
||||||
display: none;
|
margin-top: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
}}
|
}}
|
||||||
.weight-controls.active {{
|
.advanced-grid {{
|
||||||
display: block;
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
}}
|
}}
|
||||||
.info-box {{
|
.info-box {{
|
||||||
background: #e3f2fd;
|
background: #e3f2fd;
|
||||||
@@ -170,15 +203,16 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
|
|
||||||
<form @submit.prevent="executeSearch">
|
<form @submit.prevent="executeSearch">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div>
|
<!-- Main Controls -->
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>Search Query</label>
|
<label>Search Query</label>
|
||||||
<input type="text" x-model="query" placeholder="Enter search query..." />
|
<input type="text" x-model="query" placeholder="Enter search query..." required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-row">
|
||||||
<label>Search Algorithm</label>
|
<div class="control-group" style="margin-bottom: 0;">
|
||||||
<select x-model="algorithm" @change="updateWeightControls">
|
<label>Algorithm</label>
|
||||||
|
<select x-model="algorithm">
|
||||||
<option value="semantic">Semantic (Vector Similarity)</option>
|
<option value="semantic">Semantic (Vector Similarity)</option>
|
||||||
<option value="keyword">Keyword (Token Matching)</option>
|
<option value="keyword">Keyword (Token Matching)</option>
|
||||||
<option value="fuzzy">Fuzzy (Character Overlap)</option>
|
<option value="fuzzy">Fuzzy (Character Overlap)</option>
|
||||||
@@ -186,56 +220,71 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div style="display: flex; align-items: flex-end;">
|
||||||
<label>Document Types (multi-select)</label>
|
<button type="submit" class="btn" style="width: 100%;">Search & Visualize</button>
|
||||||
<select x-model="docTypes" multiple size="4" style="height: auto;">
|
|
||||||
<option value="">All Types (cross-app search)</option>
|
|
||||||
<option value="note">Notes</option>
|
|
||||||
<option value="file">Files</option>
|
|
||||||
<option value="calendar">Calendar Events</option>
|
|
||||||
<option value="contact">Contacts</option>
|
|
||||||
<option value="deck">Deck Cards</option>
|
|
||||||
</select>
|
|
||||||
<small style="color: #666; display: block; margin-top: 4px;">
|
|
||||||
Hold Ctrl/Cmd to select multiple. Select "All Types" for cross-app search.
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group weight-controls" :class="{{ active: algorithm === 'hybrid' }}">
|
<div style="display: flex; align-items: flex-end;">
|
||||||
<label>Hybrid Weights</label>
|
<button type="button" class="btn-secondary" @click="showAdvanced = !showAdvanced" style="white-space: nowrap;">
|
||||||
|
<span x-text="showAdvanced ? 'Hide Advanced' : 'Advanced'"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Advanced Options (Collapsible) -->
|
||||||
|
<div class="advanced-section" x-show="showAdvanced" x-transition.opacity.duration.200ms>
|
||||||
|
<h3 style="margin-top: 0; margin-bottom: 16px; font-size: 16px;">Advanced Options</h3>
|
||||||
|
|
||||||
|
<div class="advanced-grid">
|
||||||
|
<div class="control-group">
|
||||||
|
<label>Document Types</label>
|
||||||
|
<select x-model="docTypes" multiple>
|
||||||
|
<option value="">All Types (cross-app search)</option>
|
||||||
|
<option value="note">Notes</option>
|
||||||
|
<option value="file">Files</option>
|
||||||
|
<option value="calendar">Calendar Events</option>
|
||||||
|
<option value="contact">Contacts</option>
|
||||||
|
<option value="deck">Deck Cards</option>
|
||||||
|
</select>
|
||||||
|
<small style="color: #666; display: block; margin-top: 4px;">
|
||||||
|
Hold Ctrl/Cmd to select multiple
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label>Score Threshold (Semantic/Hybrid)</label>
|
||||||
|
<input type="number" x-model.number="scoreThreshold" min="0" max="1" step="0.1" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label>Result Limit</label>
|
||||||
|
<input type="number" x-model.number="limit" min="1" max="100" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Hybrid Weights (only when hybrid selected) -->
|
||||||
|
<div x-show="algorithm === 'hybrid'" style="margin-top: 16px; padding: 12px; background: #e9ecef; border-radius: 4px;">
|
||||||
|
<label style="margin-bottom: 12px; display: block;">Hybrid Algorithm Weights</label>
|
||||||
|
|
||||||
<div style="margin-bottom: 8px;">
|
<div style="margin-bottom: 8px;">
|
||||||
<label style="display: inline-block; width: 100px;">Semantic:</label>
|
<label style="display: inline-block; width: 100px; font-weight: normal;">Semantic:</label>
|
||||||
<input type="range" x-model.number="semanticWeight" min="0" max="1" step="0.1" style="width: 200px; display: inline-block;">
|
<input type="range" x-model.number="semanticWeight" min="0" max="1" step="0.1" style="width: 200px; display: inline-block;">
|
||||||
<span class="weight-display" x-text="semanticWeight.toFixed(1)"></span>
|
<span class="weight-display" x-text="semanticWeight.toFixed(1)"></span>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-bottom: 8px;">
|
<div style="margin-bottom: 8px;">
|
||||||
<label style="display: inline-block; width: 100px;">Keyword:</label>
|
<label style="display: inline-block; width: 100px; font-weight: normal;">Keyword:</label>
|
||||||
<input type="range" x-model.number="keywordWeight" min="0" max="1" step="0.1" style="width: 200px; display: inline-block;">
|
<input type="range" x-model.number="keywordWeight" min="0" max="1" step="0.1" style="width: 200px; display: inline-block;">
|
||||||
<span class="weight-display" x-text="keywordWeight.toFixed(1)"></span>
|
<span class="weight-display" x-text="keywordWeight.toFixed(1)"></span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label style="display: inline-block; width: 100px;">Fuzzy:</label>
|
<label style="display: inline-block; width: 100px; font-weight: normal;">Fuzzy:</label>
|
||||||
<input type="range" x-model.number="fuzzyWeight" min="0" max="1" step="0.1" style="width: 200px; display: inline-block;">
|
<input type="range" x-model.number="fuzzyWeight" min="0" max="1" step="0.1" style="width: 200px; display: inline-block;">
|
||||||
<span class="weight-display" x-text="fuzzyWeight.toFixed(1)"></span>
|
<span class="weight-display" x-text="fuzzyWeight.toFixed(1)"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="control-group">
|
|
||||||
<label>Result Limit</label>
|
|
||||||
<input type="number" x-model.number="limit" min="1" max="100" value="50" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label>Score Threshold (Semantic/Hybrid)</label>
|
|
||||||
<input type="number" x-model.number="scoreThreshold" min="0" max="1" step="0.1" value="0.7" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<button type="submit" class="btn">Search & Visualize</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -251,7 +300,9 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
<h2>Search Results (<span x-text="results.length"></span>)</h2>
|
<h2>Search Results (<span x-text="results.length"></span>)</h2>
|
||||||
<template x-for="result in results" :key="result.id">
|
<template x-for="result in results" :key="result.id">
|
||||||
<div style="padding: 12px; border-bottom: 1px solid #eee;">
|
<div style="padding: 12px; border-bottom: 1px solid #eee;">
|
||||||
<div style="font-weight: 500; color: #0066cc;" x-text="result.title"></div>
|
<a :href="getNextcloudUrl(result)" target="_blank" style="font-weight: 500; color: #0066cc; text-decoration: none;">
|
||||||
|
<span x-text="result.title"></span>
|
||||||
|
</a>
|
||||||
<div style="font-size: 14px; color: #666; margin-top: 4px;" x-text="result.excerpt"></div>
|
<div style="font-size: 14px; color: #666; margin-top: 4px;" x-text="result.excerpt"></div>
|
||||||
<div style="font-size: 12px; color: #999; margin-top: 4px;">
|
<div style="font-size: 12px; color: #999; margin-top: 4px;">
|
||||||
Score: <span x-text="result.score.toFixed(3)"></span> |
|
Score: <span x-text="result.score.toFixed(3)"></span> |
|
||||||
@@ -267,6 +318,7 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
return {{
|
return {{
|
||||||
query: '',
|
query: '',
|
||||||
algorithm: 'hybrid',
|
algorithm: 'hybrid',
|
||||||
|
showAdvanced: false,
|
||||||
docTypes: [''], // Default to "All Types"
|
docTypes: [''], // Default to "All Types"
|
||||||
limit: 50,
|
limit: 50,
|
||||||
scoreThreshold: 0.7,
|
scoreThreshold: 0.7,
|
||||||
@@ -276,10 +328,6 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
loading: false,
|
loading: false,
|
||||||
results: [],
|
results: [],
|
||||||
|
|
||||||
updateWeightControls() {{
|
|
||||||
// Update weight controls visibility based on algorithm
|
|
||||||
}},
|
|
||||||
|
|
||||||
async executeSearch() {{
|
async executeSearch() {{
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.results = [];
|
this.results = [];
|
||||||
@@ -342,6 +390,27 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
Plotly.newPlot('plot', [trace], layout);
|
Plotly.newPlot('plot', [trace], layout);
|
||||||
|
}},
|
||||||
|
|
||||||
|
getNextcloudUrl(result) {{
|
||||||
|
// Generate Nextcloud URL based on document type
|
||||||
|
// Use the actual Nextcloud host (port 8080), not the MCP server
|
||||||
|
const baseUrl = '{nextcloud_host}';
|
||||||
|
|
||||||
|
switch (result.doc_type) {{
|
||||||
|
case 'note':
|
||||||
|
return `${{baseUrl}}/apps/notes/note/${{result.id}}`;
|
||||||
|
case 'file':
|
||||||
|
return `${{baseUrl}}/apps/files/?fileId=${{result.id}}`;
|
||||||
|
case 'calendar':
|
||||||
|
return `${{baseUrl}}/apps/calendar`;
|
||||||
|
case 'contact':
|
||||||
|
return `${{baseUrl}}/apps/contacts`;
|
||||||
|
case 'deck':
|
||||||
|
return `${{baseUrl}}/apps/deck`;
|
||||||
|
default:
|
||||||
|
return `${{baseUrl}}`;
|
||||||
|
}}
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
@@ -547,7 +616,7 @@ async def vector_visualization_search(request: Request) -> JSONResponse:
|
|||||||
),
|
),
|
||||||
limit=len(doc_ids) * 2, # Account for multiple chunks per doc
|
limit=len(doc_ids) * 2, # Account for multiple chunks per doc
|
||||||
with_vectors=True,
|
with_vectors=True,
|
||||||
with_payload=False,
|
with_payload=["doc_id"], # Need doc_id to map vectors to results
|
||||||
)
|
)
|
||||||
|
|
||||||
points = points_response[0]
|
points = points_response[0]
|
||||||
|
|||||||
@@ -66,8 +66,12 @@ class ObservabilityMiddleware(BaseHTTPMiddleware):
|
|||||||
# Record start time
|
# Record start time
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
# Skip tracing for health/metrics endpoints to reduce noise
|
# Skip tracing for health/metrics/polling endpoints to reduce noise
|
||||||
should_trace = not (path.startswith("/health/") or path == "/metrics")
|
should_trace = not (
|
||||||
|
path.startswith("/health/")
|
||||||
|
or path == "/metrics"
|
||||||
|
or path == "/app/vector-sync/status"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if should_trace:
|
if should_trace:
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ async def get_indexed_doc_types(user_id: str) -> set[str]:
|
|||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
|
|
||||||
qdrant_client = await get_qdrant_client()
|
qdrant_client = await get_qdrant_client()
|
||||||
collection = settings.qdrant_collection
|
collection = settings.get_collection_name()
|
||||||
|
|
||||||
# Use scroll to sample documents and extract doc_types
|
# Use scroll to sample documents and extract doc_types
|
||||||
# Note: This could be optimized with a facet/aggregation query if Qdrant adds support
|
# Note: This could be optimized with a facet/aggregation query if Qdrant adds support
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class FuzzySearchAlgorithm(SearchAlgorithm):
|
|||||||
|
|
||||||
# Scroll through Qdrant to get all matching documents
|
# Scroll through Qdrant to get all matching documents
|
||||||
qdrant_client = await get_qdrant_client()
|
qdrant_client = await get_qdrant_client()
|
||||||
collection = settings.qdrant_collection
|
collection = settings.get_collection_name()
|
||||||
|
|
||||||
all_points = []
|
all_points = []
|
||||||
offset = None
|
offset = None
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class KeywordSearchAlgorithm(SearchAlgorithm):
|
|||||||
# Scroll through Qdrant to get all matching documents
|
# Scroll through Qdrant to get all matching documents
|
||||||
# We need title and excerpt from payload for token matching
|
# We need title and excerpt from payload for token matching
|
||||||
qdrant_client = await get_qdrant_client()
|
qdrant_client = await get_qdrant_client()
|
||||||
collection = settings.qdrant_collection
|
collection = settings.get_collection_name()
|
||||||
|
|
||||||
all_points = []
|
all_points = []
|
||||||
offset = None
|
offset = None
|
||||||
|
|||||||
Reference in New Issue
Block a user