basic-memory 0.14.4__py3-none-any.whl → 0.15.1__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.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- basic_memory/__init__.py +1 -1
- basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +5 -9
- basic_memory/api/app.py +10 -4
- basic_memory/api/routers/directory_router.py +23 -2
- basic_memory/api/routers/knowledge_router.py +25 -8
- basic_memory/api/routers/project_router.py +100 -4
- basic_memory/cli/app.py +9 -28
- basic_memory/cli/auth.py +277 -0
- basic_memory/cli/commands/cloud/__init__.py +5 -0
- basic_memory/cli/commands/cloud/api_client.py +112 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +818 -0
- basic_memory/cli/commands/cloud/core_commands.py +288 -0
- basic_memory/cli/commands/cloud/mount_commands.py +295 -0
- basic_memory/cli/commands/cloud/rclone_config.py +288 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +198 -0
- basic_memory/cli/commands/command_utils.py +43 -0
- basic_memory/cli/commands/import_memory_json.py +0 -4
- basic_memory/cli/commands/mcp.py +77 -60
- basic_memory/cli/commands/project.py +154 -152
- basic_memory/cli/commands/status.py +25 -22
- basic_memory/cli/commands/sync.py +45 -228
- basic_memory/cli/commands/tool.py +87 -16
- basic_memory/cli/main.py +1 -0
- basic_memory/config.py +131 -21
- basic_memory/db.py +104 -3
- basic_memory/deps.py +27 -8
- basic_memory/file_utils.py +37 -13
- basic_memory/ignore_utils.py +295 -0
- basic_memory/markdown/plugins.py +9 -7
- basic_memory/mcp/async_client.py +124 -14
- basic_memory/mcp/project_context.py +141 -0
- basic_memory/mcp/prompts/ai_assistant_guide.py +49 -4
- basic_memory/mcp/prompts/continue_conversation.py +17 -16
- basic_memory/mcp/prompts/recent_activity.py +116 -32
- basic_memory/mcp/prompts/search.py +13 -12
- basic_memory/mcp/prompts/utils.py +11 -4
- basic_memory/mcp/resources/ai_assistant_guide.md +211 -341
- basic_memory/mcp/resources/project_info.py +27 -11
- basic_memory/mcp/server.py +0 -37
- basic_memory/mcp/tools/__init__.py +5 -6
- basic_memory/mcp/tools/build_context.py +67 -56
- basic_memory/mcp/tools/canvas.py +38 -26
- basic_memory/mcp/tools/chatgpt_tools.py +187 -0
- basic_memory/mcp/tools/delete_note.py +81 -47
- basic_memory/mcp/tools/edit_note.py +155 -138
- basic_memory/mcp/tools/list_directory.py +112 -99
- basic_memory/mcp/tools/move_note.py +181 -101
- basic_memory/mcp/tools/project_management.py +113 -277
- basic_memory/mcp/tools/read_content.py +91 -74
- basic_memory/mcp/tools/read_note.py +152 -115
- basic_memory/mcp/tools/recent_activity.py +471 -68
- basic_memory/mcp/tools/search.py +105 -92
- basic_memory/mcp/tools/sync_status.py +136 -130
- basic_memory/mcp/tools/utils.py +4 -0
- basic_memory/mcp/tools/view_note.py +44 -33
- basic_memory/mcp/tools/write_note.py +151 -90
- basic_memory/models/knowledge.py +12 -6
- basic_memory/models/project.py +6 -2
- basic_memory/repository/entity_repository.py +89 -82
- basic_memory/repository/relation_repository.py +13 -0
- basic_memory/repository/repository.py +18 -5
- basic_memory/repository/search_repository.py +46 -2
- basic_memory/schemas/__init__.py +6 -0
- basic_memory/schemas/base.py +39 -11
- basic_memory/schemas/cloud.py +46 -0
- basic_memory/schemas/memory.py +90 -21
- basic_memory/schemas/project_info.py +9 -10
- basic_memory/schemas/sync_report.py +48 -0
- basic_memory/services/context_service.py +25 -11
- basic_memory/services/directory_service.py +124 -3
- basic_memory/services/entity_service.py +100 -48
- basic_memory/services/initialization.py +30 -11
- basic_memory/services/project_service.py +101 -24
- basic_memory/services/search_service.py +16 -8
- basic_memory/sync/sync_service.py +173 -34
- basic_memory/sync/watch_service.py +101 -40
- basic_memory/utils.py +14 -4
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.1.dist-info}/METADATA +57 -9
- basic_memory-0.15.1.dist-info/RECORD +146 -0
- basic_memory/mcp/project_session.py +0 -120
- basic_memory-0.14.4.dist-info/RECORD +0 -133
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.1.dist-info}/WHEEL +0 -0
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.1.dist-info}/entry_points.txt +0 -0
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.1.dist-info}/licenses/LICENSE +0 -0
basic_memory/mcp/tools/search.py
CHANGED
|
@@ -4,15 +4,18 @@ from textwrap import dedent
|
|
|
4
4
|
from typing import List, Optional
|
|
5
5
|
|
|
6
6
|
from loguru import logger
|
|
7
|
+
from fastmcp import Context
|
|
7
8
|
|
|
8
|
-
from basic_memory.mcp.async_client import
|
|
9
|
+
from basic_memory.mcp.async_client import get_client
|
|
10
|
+
from basic_memory.mcp.project_context import get_active_project
|
|
9
11
|
from basic_memory.mcp.server import mcp
|
|
10
12
|
from basic_memory.mcp.tools.utils import call_post
|
|
11
|
-
from basic_memory.mcp.project_session import get_active_project
|
|
12
13
|
from basic_memory.schemas.search import SearchItemType, SearchQuery, SearchResponse
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
def _format_search_error_response(
|
|
16
|
+
def _format_search_error_response(
|
|
17
|
+
project: str, error_message: str, query: str, search_type: str = "text"
|
|
18
|
+
) -> str:
|
|
16
19
|
"""Format helpful error responses for search failures that guide users to successful searches."""
|
|
17
20
|
|
|
18
21
|
# FTS5 syntax errors
|
|
@@ -50,13 +53,13 @@ def _format_search_error_response(error_message: str, query: str, search_type: s
|
|
|
50
53
|
|
|
51
54
|
## Try again with:
|
|
52
55
|
```
|
|
53
|
-
search_notes("{clean_query}")
|
|
56
|
+
search_notes("{project}","{clean_query}")
|
|
54
57
|
```
|
|
55
58
|
|
|
56
59
|
## Alternative search strategies:
|
|
57
|
-
- Break into simpler terms: `search_notes("{" ".join(clean_query.split()[:2])}")`
|
|
58
|
-
- Try different search types: `search_notes("{clean_query}", search_type="title")`
|
|
59
|
-
- Use filtering: `search_notes("{clean_query}", types=["entity"])`
|
|
60
|
+
- Break into simpler terms: `search_notes("{project}", "{" ".join(clean_query.split()[:2])}")`
|
|
61
|
+
- Try different search types: `search_notes("{project}","{clean_query}", search_type="title")`
|
|
62
|
+
- Use filtering: `search_notes("{project}","{clean_query}", types=["entity"])`
|
|
60
63
|
""").strip()
|
|
61
64
|
|
|
62
65
|
# Project not found errors (check before general "not found")
|
|
@@ -68,11 +71,9 @@ def _format_search_error_response(error_message: str, query: str, search_type: s
|
|
|
68
71
|
|
|
69
72
|
## How to resolve:
|
|
70
73
|
1. **Check available projects**: `list_projects()`
|
|
71
|
-
2. **Switch to valid project**: `switch_project("valid-project-name")`
|
|
72
74
|
3. **Verify project setup**: Ensure your project is properly configured
|
|
73
75
|
|
|
74
76
|
## Current session info:
|
|
75
|
-
- Check current project: `get_current_project()`
|
|
76
77
|
- See available projects: `list_projects()`
|
|
77
78
|
""").strip()
|
|
78
79
|
|
|
@@ -100,29 +101,28 @@ def _format_search_error_response(error_message: str, query: str, search_type: s
|
|
|
100
101
|
- Try synonyms or related terms
|
|
101
102
|
|
|
102
103
|
3. **Use different search approaches**:
|
|
103
|
-
- **Text search**: `search_notes("{query}", search_type="text")` (searches full content)
|
|
104
|
-
- **Title search**: `search_notes("{query}", search_type="title")` (searches only titles)
|
|
105
|
-
- **Permalink search**: `search_notes("{query}", search_type="permalink")` (searches file paths)
|
|
104
|
+
- **Text search**: `search_notes("{project}","{query}", search_type="text")` (searches full content)
|
|
105
|
+
- **Title search**: `search_notes("{project}","{query}", search_type="title")` (searches only titles)
|
|
106
|
+
- **Permalink search**: `search_notes("{project}","{query}", search_type="permalink")` (searches file paths)
|
|
106
107
|
|
|
107
108
|
4. **Try boolean operators for broader results**:
|
|
108
|
-
- OR search: `search_notes("{" OR ".join(query.split()[:3])}")`
|
|
109
|
+
- OR search: `search_notes("{project}","{" OR ".join(query.split()[:3])}")`
|
|
109
110
|
- Remove restrictive terms: Focus on the most important keywords
|
|
110
111
|
|
|
111
112
|
5. **Use filtering to narrow scope**:
|
|
112
|
-
- By content type: `search_notes("{query}", types=["entity"])`
|
|
113
|
-
- By recent content: `search_notes("{query}", after_date="1 week")`
|
|
114
|
-
- By entity type: `search_notes("{query}", entity_types=["observation"])`
|
|
113
|
+
- By content type: `search_notes("{project}","{query}", types=["entity"])`
|
|
114
|
+
- By recent content: `search_notes("{project}","{query}", after_date="1 week")`
|
|
115
|
+
- By entity type: `search_notes("{project}","{query}", entity_types=["observation"])`
|
|
115
116
|
|
|
116
117
|
6. **Try advanced search patterns**:
|
|
117
|
-
- Tag search: `search_notes("tag:your-tag")`
|
|
118
|
-
- Category search: `search_notes("category:observation")`
|
|
119
|
-
- Pattern matching: `search_notes("*{query}*", search_type="permalink")`
|
|
118
|
+
- Tag search: `search_notes("{project}","tag:your-tag")`
|
|
119
|
+
- Category search: `search_notes("{project}","category:observation")`
|
|
120
|
+
- Pattern matching: `search_notes("{project}","*{query}*", search_type="permalink")`
|
|
120
121
|
|
|
121
122
|
## Explore what content exists:
|
|
122
123
|
- **Recent activity**: `recent_activity(timeframe="7d")` - See what's been updated recently
|
|
123
|
-
- **List directories**: `list_directory("/")` - Browse all content
|
|
124
|
-
- **Browse by folder**: `list_directory("/notes")` or `list_directory("/docs")`
|
|
125
|
-
- **Check project**: `get_current_project()` - Verify you're in the right project
|
|
124
|
+
- **List directories**: `list_directory("{project}","/")` - Browse all content
|
|
125
|
+
- **Browse by folder**: `list_directory("{project}","/notes")` or `list_directory("/docs")`
|
|
126
126
|
""").strip()
|
|
127
127
|
|
|
128
128
|
# Server/API errors
|
|
@@ -138,9 +138,9 @@ def _format_search_error_response(error_message: str, query: str, search_type: s
|
|
|
138
138
|
3. **Check project status**: Ensure your project is properly synced
|
|
139
139
|
|
|
140
140
|
## Alternative approaches:
|
|
141
|
-
- Browse files directly: `list_directory("/")`
|
|
141
|
+
- Browse files directly: `list_directory("{project}","/")`
|
|
142
142
|
- Check recent activity: `recent_activity(timeframe="7d")`
|
|
143
|
-
- Try a different search type: `search_notes("{query}", search_type="title")`
|
|
143
|
+
- Try a different search type: `search_notes("{project}","{query}", search_type="title")`
|
|
144
144
|
|
|
145
145
|
## If the problem persists:
|
|
146
146
|
The search index might need to be rebuilt. Send a message to support@basicmachines.co or check the project sync status.
|
|
@@ -162,9 +162,7 @@ You don't have permission to search in the current project: {error_message}
|
|
|
162
162
|
3. **Check authentication**: You might need to re-authenticate
|
|
163
163
|
|
|
164
164
|
## Alternative actions:
|
|
165
|
-
- List available projects: `list_projects()`
|
|
166
|
-
- Switch to accessible project: `switch_project("project-name")`
|
|
167
|
-
- Check current project: `get_current_project()`"""
|
|
165
|
+
- List available projects: `list_projects()`"""
|
|
168
166
|
|
|
169
167
|
# Generic fallback
|
|
170
168
|
return f"""# Search Failed
|
|
@@ -179,17 +177,16 @@ Error searching for '{query}': {error_message}
|
|
|
179
177
|
|
|
180
178
|
## Alternative search approaches:
|
|
181
179
|
- **Different search types**:
|
|
182
|
-
- Title only: `search_notes("{query}", search_type="title")`
|
|
183
|
-
- Permalink patterns: `search_notes("{query}*", search_type="permalink")`
|
|
184
|
-
- **With filters**: `search_notes("{query}", types=["entity"])`
|
|
185
|
-
- **Recent content**: `search_notes("{query}", after_date="1 week")`
|
|
186
|
-
- **Boolean variations**: `search_notes("{" OR ".join(query.split()[:2])}")`
|
|
180
|
+
- Title only: `search_notes("{project}","{query}", search_type="title")`
|
|
181
|
+
- Permalink patterns: `search_notes("{project}","{query}*", search_type="permalink")`
|
|
182
|
+
- **With filters**: `search_notes("{project}","{query}", types=["entity"])`
|
|
183
|
+
- **Recent content**: `search_notes("{project}","{query}", after_date="1 week")`
|
|
184
|
+
- **Boolean variations**: `search_notes("{project}","{" OR ".join(query.split()[:2])}")`
|
|
187
185
|
|
|
188
186
|
## Explore your content:
|
|
189
|
-
- **Browse files**: `list_directory("/")` - See all available content
|
|
187
|
+
- **Browse files**: `list_directory("{project}","/")` - See all available content
|
|
190
188
|
- **Recent activity**: `recent_activity(timeframe="7d")` - Check what's been updated
|
|
191
|
-
- **
|
|
192
|
-
- **All projects**: `list_projects()` - Switch to different project if needed
|
|
189
|
+
- **All projects**: `list_projects()`
|
|
193
190
|
|
|
194
191
|
## Search syntax reference:
|
|
195
192
|
- **Basic**: `keyword` or `multiple words`
|
|
@@ -204,13 +201,14 @@ Error searching for '{query}': {error_message}
|
|
|
204
201
|
)
|
|
205
202
|
async def search_notes(
|
|
206
203
|
query: str,
|
|
204
|
+
project: Optional[str] = None,
|
|
207
205
|
page: int = 1,
|
|
208
206
|
page_size: int = 10,
|
|
209
207
|
search_type: str = "text",
|
|
210
208
|
types: Optional[List[str]] = None,
|
|
211
209
|
entity_types: Optional[List[str]] = None,
|
|
212
210
|
after_date: Optional[str] = None,
|
|
213
|
-
|
|
211
|
+
context: Context | None = None,
|
|
214
212
|
) -> SearchResponse | str:
|
|
215
213
|
"""Search across all content in the knowledge base with comprehensive syntax support.
|
|
216
214
|
|
|
@@ -218,51 +216,57 @@ async def search_notes(
|
|
|
218
216
|
or exact permalink lookup. It supports filtering by content type, entity type,
|
|
219
217
|
and date, with advanced boolean and phrase search capabilities.
|
|
220
218
|
|
|
219
|
+
Project Resolution:
|
|
220
|
+
Server resolves projects in this order: Single Project Mode → project parameter → default project.
|
|
221
|
+
If project unknown, use list_memory_projects() or recent_activity() first.
|
|
222
|
+
|
|
221
223
|
## Search Syntax Examples
|
|
222
224
|
|
|
223
225
|
### Basic Searches
|
|
224
|
-
- `search_notes("keyword")` - Find any content containing "keyword"
|
|
225
|
-
- `search_notes("exact phrase")` - Search for exact phrase match
|
|
226
|
+
- `search_notes("my-project", "keyword")` - Find any content containing "keyword"
|
|
227
|
+
- `search_notes("work-docs", "'exact phrase'")` - Search for exact phrase match
|
|
226
228
|
|
|
227
229
|
### Advanced Boolean Searches
|
|
228
|
-
- `search_notes("term1 term2")` - Find content with both terms (implicit AND)
|
|
229
|
-
- `search_notes("term1 AND term2")` - Explicit AND search (both terms required)
|
|
230
|
-
- `search_notes("term1 OR term2")` - Either term can be present
|
|
231
|
-
- `search_notes("term1 NOT term2")` - Include term1 but exclude term2
|
|
232
|
-
- `search_notes("(project OR planning) AND notes")` - Grouped boolean logic
|
|
230
|
+
- `search_notes("my-project", "term1 term2")` - Find content with both terms (implicit AND)
|
|
231
|
+
- `search_notes("my-project", "term1 AND term2")` - Explicit AND search (both terms required)
|
|
232
|
+
- `search_notes("my-project", "term1 OR term2")` - Either term can be present
|
|
233
|
+
- `search_notes("my-project", "term1 NOT term2")` - Include term1 but exclude term2
|
|
234
|
+
- `search_notes("my-project", "(project OR planning) AND notes")` - Grouped boolean logic
|
|
233
235
|
|
|
234
236
|
### Content-Specific Searches
|
|
235
|
-
- `search_notes("tag:example")` - Search within specific tags (if supported by content)
|
|
236
|
-
- `search_notes("category:observation")` - Filter by observation categories
|
|
237
|
-
- `search_notes("author:username")` - Find content by author (if metadata available)
|
|
237
|
+
- `search_notes("research", "tag:example")` - Search within specific tags (if supported by content)
|
|
238
|
+
- `search_notes("work-project", "category:observation")` - Filter by observation categories
|
|
239
|
+
- `search_notes("team-docs", "author:username")` - Find content by author (if metadata available)
|
|
238
240
|
|
|
239
241
|
### Search Type Examples
|
|
240
|
-
- `search_notes("Meeting", search_type="title")` - Search only in titles
|
|
241
|
-
- `search_notes("docs/meeting-*", search_type="permalink")` - Pattern match permalinks
|
|
242
|
-
- `search_notes("keyword", search_type="text")` - Full-text search (default)
|
|
242
|
+
- `search_notes("my-project", "Meeting", search_type="title")` - Search only in titles
|
|
243
|
+
- `search_notes("work-docs", "docs/meeting-*", search_type="permalink")` - Pattern match permalinks
|
|
244
|
+
- `search_notes("research", "keyword", search_type="text")` - Full-text search (default)
|
|
243
245
|
|
|
244
246
|
### Filtering Options
|
|
245
|
-
- `search_notes("query", types=["entity"])` - Search only entities
|
|
246
|
-
- `search_notes("query", types=["note", "person"])` - Multiple content types
|
|
247
|
-
- `search_notes("query", entity_types=["observation"])` - Filter by entity type
|
|
248
|
-
- `search_notes("query", after_date="2024-01-01")` - Recent content only
|
|
249
|
-
- `search_notes("query", after_date="1 week")` - Relative date filtering
|
|
247
|
+
- `search_notes("my-project", "query", types=["entity"])` - Search only entities
|
|
248
|
+
- `search_notes("work-docs", "query", types=["note", "person"])` - Multiple content types
|
|
249
|
+
- `search_notes("research", "query", entity_types=["observation"])` - Filter by entity type
|
|
250
|
+
- `search_notes("team-docs", "query", after_date="2024-01-01")` - Recent content only
|
|
251
|
+
- `search_notes("my-project", "query", after_date="1 week")` - Relative date filtering
|
|
250
252
|
|
|
251
253
|
### Advanced Pattern Examples
|
|
252
|
-
- `search_notes("project AND (meeting OR discussion)")` - Complex boolean logic
|
|
253
|
-
- `search_notes("\"exact phrase\" AND keyword")` - Combine phrase and keyword search
|
|
254
|
-
- `search_notes("bug NOT fixed")` - Exclude resolved issues
|
|
255
|
-
- `search_notes("docs/2024-*", search_type="permalink")` - Year-based permalink search
|
|
254
|
+
- `search_notes("work-project", "project AND (meeting OR discussion)")` - Complex boolean logic
|
|
255
|
+
- `search_notes("research", "\"exact phrase\" AND keyword")` - Combine phrase and keyword search
|
|
256
|
+
- `search_notes("dev-notes", "bug NOT fixed")` - Exclude resolved issues
|
|
257
|
+
- `search_notes("archive", "docs/2024-*", search_type="permalink")` - Year-based permalink search
|
|
256
258
|
|
|
257
259
|
Args:
|
|
258
260
|
query: The search query string (supports boolean operators, phrases, patterns)
|
|
261
|
+
project: Project name to search in. Optional - server will resolve using hierarchy.
|
|
262
|
+
If unknown, use list_memory_projects() to discover available projects.
|
|
259
263
|
page: The page number of results to return (default 1)
|
|
260
264
|
page_size: The number of results to return per page (default 10)
|
|
261
265
|
search_type: Type of search to perform, one of: "text", "title", "permalink" (default: "text")
|
|
262
266
|
types: Optional list of note types to search (e.g., ["note", "person"])
|
|
263
267
|
entity_types: Optional list of entity types to filter by (e.g., ["entity", "observation"])
|
|
264
268
|
after_date: Optional date filter for recent content (e.g., "1 week", "2d", "2024-01-01")
|
|
265
|
-
|
|
269
|
+
context: Optional FastMCP context for performance caching.
|
|
266
270
|
|
|
267
271
|
Returns:
|
|
268
272
|
SearchResponse with results and pagination info, or helpful error guidance if search fails
|
|
@@ -288,37 +292,43 @@ async def search_notes(
|
|
|
288
292
|
|
|
289
293
|
# Search with type filter
|
|
290
294
|
results = await search_notes(
|
|
291
|
-
|
|
295
|
+
"meeting notes",
|
|
292
296
|
types=["entity"],
|
|
293
297
|
)
|
|
294
298
|
|
|
295
299
|
# Search with entity type filter
|
|
296
300
|
results = await search_notes(
|
|
297
|
-
|
|
301
|
+
"meeting notes",
|
|
298
302
|
entity_types=["observation"],
|
|
299
303
|
)
|
|
300
304
|
|
|
301
305
|
# Search for recent content
|
|
302
306
|
results = await search_notes(
|
|
303
|
-
|
|
307
|
+
"bug report",
|
|
304
308
|
after_date="1 week"
|
|
305
309
|
)
|
|
306
310
|
|
|
307
311
|
# Pattern matching on permalinks
|
|
308
312
|
results = await search_notes(
|
|
309
|
-
|
|
313
|
+
"docs/meeting-*",
|
|
310
314
|
search_type="permalink"
|
|
311
315
|
)
|
|
312
316
|
|
|
313
|
-
#
|
|
314
|
-
results = await search_notes(
|
|
317
|
+
# Title-only search
|
|
318
|
+
results = await search_notes(
|
|
319
|
+
"Machine Learning",
|
|
320
|
+
search_type="title"
|
|
321
|
+
)
|
|
315
322
|
|
|
316
323
|
# Complex search with multiple filters
|
|
317
324
|
results = await search_notes(
|
|
318
|
-
|
|
325
|
+
"(bug OR issue) AND NOT resolved",
|
|
319
326
|
types=["entity"],
|
|
320
327
|
after_date="2024-01-01"
|
|
321
328
|
)
|
|
329
|
+
|
|
330
|
+
# Explicit project specification
|
|
331
|
+
results = await search_notes("project planning", project="my-project")
|
|
322
332
|
"""
|
|
323
333
|
# Create a SearchQuery object based on the parameters
|
|
324
334
|
search_query = SearchQuery()
|
|
@@ -343,29 +353,32 @@ async def search_notes(
|
|
|
343
353
|
if after_date:
|
|
344
354
|
search_query.after_date = after_date
|
|
345
355
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
356
|
+
async with get_client() as client:
|
|
357
|
+
active_project = await get_active_project(client, project, context)
|
|
358
|
+
project_url = active_project.project_url
|
|
359
|
+
|
|
360
|
+
logger.info(f"Searching for {search_query} in project {active_project.name}")
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
response = await call_post(
|
|
364
|
+
client,
|
|
365
|
+
f"{project_url}/search/",
|
|
366
|
+
json=search_query.model_dump(),
|
|
367
|
+
params={"page": page, "page_size": page_size},
|
|
368
|
+
)
|
|
369
|
+
result = SearchResponse.model_validate(response.json())
|
|
370
|
+
|
|
371
|
+
# Check if we got no results and provide helpful guidance
|
|
372
|
+
if not result.results:
|
|
373
|
+
logger.info(
|
|
374
|
+
f"Search returned no results for query: {query} in project {active_project.name}"
|
|
375
|
+
)
|
|
376
|
+
# Don't treat this as an error, but the user might want guidance
|
|
377
|
+
# We return the empty result as normal - the user can decide if they need help
|
|
378
|
+
|
|
379
|
+
return result
|
|
380
|
+
|
|
381
|
+
except Exception as e:
|
|
382
|
+
logger.error(f"Search failed for query '{query}': {e}, project: {active_project.name}")
|
|
383
|
+
# Return formatted error message as string for better user experience
|
|
384
|
+
return _format_search_error_response(active_project.name, str(e), query, search_type)
|