sirchmunk 0.0.1.post1__py3-none-any.whl → 0.0.2__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.
Files changed (43) hide show
  1. sirchmunk/api/__init__.py +1 -0
  2. sirchmunk/api/chat.py +1123 -0
  3. sirchmunk/api/components/__init__.py +0 -0
  4. sirchmunk/api/components/history_storage.py +402 -0
  5. sirchmunk/api/components/monitor_tracker.py +518 -0
  6. sirchmunk/api/components/settings_storage.py +353 -0
  7. sirchmunk/api/history.py +254 -0
  8. sirchmunk/api/knowledge.py +411 -0
  9. sirchmunk/api/main.py +120 -0
  10. sirchmunk/api/monitor.py +219 -0
  11. sirchmunk/api/run_server.py +54 -0
  12. sirchmunk/api/search.py +230 -0
  13. sirchmunk/api/settings.py +309 -0
  14. sirchmunk/api/tools.py +315 -0
  15. sirchmunk/cli/__init__.py +11 -0
  16. sirchmunk/cli/cli.py +789 -0
  17. sirchmunk/learnings/knowledge_base.py +5 -2
  18. sirchmunk/llm/prompts.py +12 -1
  19. sirchmunk/retrieve/text_retriever.py +186 -2
  20. sirchmunk/scan/file_scanner.py +2 -2
  21. sirchmunk/schema/knowledge.py +119 -35
  22. sirchmunk/search.py +384 -26
  23. sirchmunk/storage/__init__.py +2 -2
  24. sirchmunk/storage/{knowledge_manager.py → knowledge_storage.py} +265 -60
  25. sirchmunk/utils/constants.py +7 -5
  26. sirchmunk/utils/embedding_util.py +217 -0
  27. sirchmunk/utils/tokenizer_util.py +36 -1
  28. sirchmunk/version.py +1 -1
  29. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/METADATA +124 -9
  30. sirchmunk-0.0.2.dist-info/RECORD +69 -0
  31. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/WHEEL +1 -1
  32. sirchmunk-0.0.2.dist-info/top_level.txt +2 -0
  33. sirchmunk_mcp/__init__.py +25 -0
  34. sirchmunk_mcp/cli.py +478 -0
  35. sirchmunk_mcp/config.py +276 -0
  36. sirchmunk_mcp/server.py +355 -0
  37. sirchmunk_mcp/service.py +327 -0
  38. sirchmunk_mcp/setup.py +15 -0
  39. sirchmunk_mcp/tools.py +410 -0
  40. sirchmunk-0.0.1.post1.dist-info/RECORD +0 -45
  41. sirchmunk-0.0.1.post1.dist-info/top_level.txt +0 -1
  42. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/entry_points.txt +0 -0
  43. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,309 @@
1
+ # Copyright (c) ModelScope Contributors. All rights reserved.
2
+ """
3
+ Settings API endpoints with persistent storage
4
+ Provides UI settings and environment variable management
5
+ """
6
+
7
+ from fastapi import APIRouter, HTTPException
8
+ from typing import Dict, Any, Optional
9
+ from pydantic import BaseModel
10
+
11
+ from sirchmunk.api.components.settings_storage import SettingsStorage
12
+ from sirchmunk.utils.constants import (
13
+ GREP_CONCURRENT_LIMIT,
14
+ LLM_BASE_URL,
15
+ LLM_API_KEY,
16
+ LLM_MODEL_NAME,
17
+ SIRCHMUNK_WORK_PATH
18
+ )
19
+
20
+ router = APIRouter(prefix="/api/v1/settings", tags=["settings"])
21
+
22
+ # Initialize settings storage (with error handling)
23
+ try:
24
+ settings_storage = SettingsStorage()
25
+ except Exception as e:
26
+ print(f"[ERROR] Failed to initialize SettingsStorage: {e}")
27
+ settings_storage = None
28
+
29
+ # === Request/Response Models ===
30
+
31
+ class UISettings(BaseModel):
32
+ theme: str = "light"
33
+ language: str = "en"
34
+
35
+ class EnvironmentVariables(BaseModel):
36
+ SIRCHMUNK_WORK_PATH: Optional[str] = None
37
+ LLM_BASE_URL: Optional[str] = None
38
+ LLM_API_KEY: Optional[str] = None
39
+ LLM_MODEL_NAME: Optional[str] = None
40
+ GREP_CONCURRENT_LIMIT: Optional[int] = None
41
+
42
+ class SaveSettingsRequest(BaseModel):
43
+ ui: Optional[UISettings] = None
44
+ environment: Optional[Dict[str, str]] = None
45
+
46
+ # === Helper Functions ===
47
+
48
+ def get_default_ui_settings() -> Dict[str, Any]:
49
+ """Get default UI settings"""
50
+ return {
51
+ "theme": settings_storage.get_setting("ui.theme", "light"),
52
+ "language": settings_storage.get_setting("ui.language", "en"),
53
+ }
54
+
55
+ def get_current_env_variables() -> Dict[str, Any]:
56
+ """Get current environment variables with saved overrides"""
57
+ # Get saved values from storage
58
+ saved_work_path = settings_storage.get_env_variable("SIRCHMUNK_WORK_PATH")
59
+ saved_llm_base_url = settings_storage.get_env_variable("LLM_BASE_URL")
60
+ saved_llm_api_key = settings_storage.get_env_variable("LLM_API_KEY")
61
+ saved_llm_model = settings_storage.get_env_variable("LLM_MODEL_NAME")
62
+ saved_grep_limit = settings_storage.get_env_variable("GREP_CONCURRENT_LIMIT")
63
+
64
+ return {
65
+ "SIRCHMUNK_WORK_PATH": {
66
+ "value": saved_work_path or str(SIRCHMUNK_WORK_PATH),
67
+ "default": str(SIRCHMUNK_WORK_PATH),
68
+ "description": "Working directory for Sirchmunk data",
69
+ "category": "system"
70
+ },
71
+ "LLM_BASE_URL": {
72
+ "value": saved_llm_base_url or LLM_BASE_URL,
73
+ "default": LLM_BASE_URL,
74
+ "description": "Base URL for LLM API (OpenAI-compatible endpoint)",
75
+ "category": "llm"
76
+ },
77
+ "LLM_API_KEY": {
78
+ "value": "***" if (saved_llm_api_key or LLM_API_KEY) else "",
79
+ "default": "***" if LLM_API_KEY else "",
80
+ "description": "API key for LLM service",
81
+ "category": "llm",
82
+ "sensitive": True
83
+ },
84
+ "LLM_MODEL_NAME": {
85
+ "value": saved_llm_model or LLM_MODEL_NAME,
86
+ "default": LLM_MODEL_NAME,
87
+ "description": "Model name for LLM",
88
+ "category": "llm"
89
+ },
90
+ "GREP_CONCURRENT_LIMIT": {
91
+ "value": saved_grep_limit or str(GREP_CONCURRENT_LIMIT),
92
+ "default": str(GREP_CONCURRENT_LIMIT),
93
+ "description": "Maximum concurrent grep requests",
94
+ "category": "system"
95
+ }
96
+ }
97
+
98
+ # === API Endpoints ===
99
+
100
+ @router.get("")
101
+ async def get_all_settings():
102
+ """Get all settings including UI and environment variables"""
103
+ if settings_storage is None:
104
+ raise HTTPException(status_code=503, detail="Settings storage not available")
105
+
106
+ try:
107
+ ui_settings = get_default_ui_settings()
108
+ env_variables = get_current_env_variables()
109
+
110
+ return {
111
+ "success": True,
112
+ "data": {
113
+ "ui": ui_settings,
114
+ "environment": env_variables
115
+ }
116
+ }
117
+ except Exception as e:
118
+ raise HTTPException(status_code=500, detail=str(e))
119
+
120
+ @router.get("/ui")
121
+ async def get_ui_settings():
122
+ """Get UI settings"""
123
+ if settings_storage is None:
124
+ # Return default settings if storage not available
125
+ return {
126
+ "success": True,
127
+ "data": {
128
+ "theme": "light",
129
+ "language": "en"
130
+ }
131
+ }
132
+
133
+ try:
134
+ ui_settings = get_default_ui_settings()
135
+ return {
136
+ "success": True,
137
+ "data": ui_settings
138
+ }
139
+ except Exception as e:
140
+ raise HTTPException(status_code=500, detail=str(e))
141
+
142
+ @router.get("/environment")
143
+ async def get_environment_variables():
144
+ """Get environment variables"""
145
+ if settings_storage is None:
146
+ raise HTTPException(status_code=503, detail="Settings storage not available")
147
+
148
+ try:
149
+ env_variables = get_current_env_variables()
150
+ return {
151
+ "success": True,
152
+ "data": env_variables
153
+ }
154
+ except Exception as e:
155
+ raise HTTPException(status_code=500, detail=str(e))
156
+
157
+ @router.post("")
158
+ async def save_settings(request: SaveSettingsRequest):
159
+ """Save settings (UI and/or environment variables)"""
160
+ if settings_storage is None:
161
+ raise HTTPException(status_code=503, detail="Settings storage not available")
162
+
163
+ try:
164
+ saved_items = []
165
+
166
+ # Save UI settings
167
+ if request.ui:
168
+ if request.ui.theme:
169
+ settings_storage.save_setting("ui.theme", request.ui.theme, "ui")
170
+ saved_items.append("theme")
171
+
172
+ if request.ui.language:
173
+ settings_storage.save_setting("ui.language", request.ui.language, "ui")
174
+ saved_items.append("language")
175
+
176
+ # Save environment variables
177
+ if request.environment:
178
+ for key, value in request.environment.items():
179
+ if value: # Only save non-empty values
180
+ # For sensitive fields, store the actual value (not masked)
181
+ settings_storage.save_env_variable(
182
+ key,
183
+ value,
184
+ description=f"User-configured {key}",
185
+ category="llm" if "LLM" in key else "system"
186
+ )
187
+ saved_items.append(key)
188
+
189
+ return {
190
+ "success": True,
191
+ "message": f"Settings saved successfully: {', '.join(saved_items)}",
192
+ "saved_items": saved_items
193
+ }
194
+ except Exception as e:
195
+ raise HTTPException(status_code=500, detail=f"Failed to save settings: {str(e)}")
196
+
197
+ @router.post("/ui")
198
+ async def update_ui_settings(ui: UISettings):
199
+ """Update UI settings"""
200
+ if settings_storage is None:
201
+ raise HTTPException(status_code=503, detail="Settings storage not available")
202
+
203
+ try:
204
+ settings_storage.save_setting("ui.theme", ui.theme, "ui")
205
+ settings_storage.save_setting("ui.language", ui.language, "ui")
206
+
207
+ return {
208
+ "success": True,
209
+ "message": "UI settings updated successfully",
210
+ "data": {
211
+ "theme": ui.theme,
212
+ "language": ui.language
213
+ }
214
+ }
215
+ except Exception as e:
216
+ raise HTTPException(status_code=500, detail=str(e))
217
+
218
+ @router.get("/test/llm")
219
+ async def test_llm_connection():
220
+ """Test LLM connection"""
221
+ from sirchmunk.llm import OpenAIChat
222
+
223
+ try:
224
+ # Get current LLM settings
225
+ base_url = settings_storage.get_env_variable("LLM_BASE_URL") or LLM_BASE_URL
226
+ api_key = settings_storage.get_env_variable("LLM_API_KEY") or LLM_API_KEY
227
+ model = settings_storage.get_env_variable("LLM_MODEL_NAME") or LLM_MODEL_NAME
228
+
229
+ print(f"[DEBUG] Testing LLM connection with base_url={base_url}, model={model}, api_key={'***' if api_key else '(not set)'}")
230
+
231
+ # Simple validation
232
+ if not api_key:
233
+ return {
234
+ "success": False,
235
+ "status": "error",
236
+ "message": "LLM API key is not configured",
237
+ "model": None
238
+ }
239
+
240
+ if not base_url:
241
+ return {
242
+ "success": False,
243
+ "status": "error",
244
+ "message": "LLM base URL is not configured",
245
+ "model": None
246
+ }
247
+
248
+
249
+ llm = OpenAIChat(
250
+ base_url=base_url,
251
+ api_key=api_key,
252
+ model=model
253
+ )
254
+
255
+ messages = [
256
+ {"role": "system",
257
+ "content": "You are a helpful AI assistant."},
258
+ {"role": "user", "content": "Output the word: 'test'."}
259
+ ]
260
+ resp = await llm.achat(
261
+ messages=messages,
262
+ stream=False
263
+ )
264
+ print(f"[DEBUG] LLM response: {resp.content}")
265
+
266
+ return {
267
+ "success": True,
268
+ "status": "configured",
269
+ "message": "LLM connection successful",
270
+ "model": model,
271
+ "base_url": base_url
272
+ }
273
+ except Exception as e:
274
+ return {
275
+ "success": False,
276
+ "status": "error",
277
+ "message": str(e),
278
+ "model": None
279
+ }
280
+
281
+ @router.get("/status")
282
+ async def get_settings_status():
283
+ """Get settings status for quick overview"""
284
+ try:
285
+ ui_settings = get_default_ui_settings()
286
+
287
+ # Check LLM configuration
288
+ llm_api_key = settings_storage.get_env_variable("LLM_API_KEY") or LLM_API_KEY
289
+ llm_base_url = settings_storage.get_env_variable("LLM_BASE_URL") or LLM_BASE_URL
290
+ llm_model = settings_storage.get_env_variable("LLM_MODEL_NAME") or LLM_MODEL_NAME
291
+
292
+ llm_configured = bool(llm_api_key and llm_base_url and llm_model)
293
+
294
+ return {
295
+ "success": True,
296
+ "data": {
297
+ "ui": {
298
+ "theme": ui_settings.get("theme", "light"),
299
+ "language": ui_settings.get("language", "en")
300
+ },
301
+ "llm": {
302
+ "configured": llm_configured,
303
+ "model": llm_model if llm_configured else None,
304
+ "status": "ready" if llm_configured else "not_configured"
305
+ }
306
+ }
307
+ }
308
+ except Exception as e:
309
+ raise HTTPException(status_code=500, detail=str(e))
sirchmunk/api/tools.py ADDED
@@ -0,0 +1,315 @@
1
+ # Copyright (c) ModelScope Contributors. All rights reserved.
2
+ """
3
+ Tools API endpoints for Sirchmunk
4
+ Handles quick actions like file export, PPT generation, document conversion, etc.
5
+ """
6
+
7
+ from fastapi import APIRouter, HTTPException
8
+ from typing import Dict, List, Any, Optional
9
+ import uuid
10
+ from datetime import datetime, timedelta
11
+ import random
12
+
13
+ router = APIRouter(prefix="/api/v1/tools", tags=["tools"])
14
+
15
+ # Mock tool configurations
16
+ TOOL_CONFIGS = {
17
+ "export-pdf": {
18
+ "name": "PDF Export",
19
+ "description": "Export conversation or content to PDF format",
20
+ "processing_time": (2, 5), # seconds range
21
+ "output_format": "pdf"
22
+ },
23
+ "generate-ppt": {
24
+ "name": "PPT Generation",
25
+ "description": "Generate PowerPoint presentation from content",
26
+ "processing_time": (5, 10),
27
+ "output_format": "pptx"
28
+ },
29
+ "convert-doc": {
30
+ "name": "Document Conversion",
31
+ "description": "Convert documents between different formats",
32
+ "processing_time": (3, 7),
33
+ "output_format": "various"
34
+ },
35
+ "generate-video": {
36
+ "name": "Video Generation",
37
+ "description": "Create video content from text or images",
38
+ "processing_time": (15, 30),
39
+ "output_format": "mp4"
40
+ },
41
+ "create-image": {
42
+ "name": "Image Creation",
43
+ "description": "Generate images using AI",
44
+ "processing_time": (3, 8),
45
+ "output_format": "png"
46
+ },
47
+ "export-excel": {
48
+ "name": "Excel Export",
49
+ "description": "Export data to Excel spreadsheet",
50
+ "processing_time": (2, 4),
51
+ "output_format": "xlsx"
52
+ }
53
+ }
54
+
55
+ @router.get("/")
56
+ async def list_available_tools():
57
+ """List all available tools and their configurations"""
58
+ try:
59
+ tools = []
60
+ for tool_id, config in TOOL_CONFIGS.items():
61
+ tools.append({
62
+ "id": tool_id,
63
+ "name": config["name"],
64
+ "description": config["description"],
65
+ "output_format": config["output_format"],
66
+ "estimated_time": f"{config['processing_time'][0]}-{config['processing_time'][1]}s"
67
+ })
68
+
69
+ return {
70
+ "success": True,
71
+ "data": tools,
72
+ "timestamp": datetime.now().isoformat()
73
+ }
74
+
75
+ except Exception as e:
76
+ raise HTTPException(status_code=500, detail=f"Failed to list tools: {str(e)}")
77
+
78
+ @router.post("/{tool_id}")
79
+ async def execute_tool(tool_id: str, request: Dict[str, Any]):
80
+ """
81
+ Execute a specific tool
82
+
83
+ Args:
84
+ tool_id: ID of the tool to execute
85
+ request: Tool execution parameters
86
+ """
87
+ try:
88
+ if tool_id not in TOOL_CONFIGS:
89
+ raise HTTPException(status_code=404, detail=f"Tool '{tool_id}' not found")
90
+
91
+ config = TOOL_CONFIGS[tool_id]
92
+
93
+ # Simulate processing time
94
+ processing_time = random.randint(*config["processing_time"])
95
+
96
+ # Generate mock result based on tool type
97
+ result = await _generate_tool_result(tool_id, config, request)
98
+
99
+ return {
100
+ "success": True,
101
+ "data": result,
102
+ "message": f"{config['name']} completed successfully",
103
+ "timestamp": datetime.now().isoformat()
104
+ }
105
+
106
+ except HTTPException:
107
+ raise
108
+ except Exception as e:
109
+ raise HTTPException(status_code=500, detail=f"Failed to execute tool: {str(e)}")
110
+
111
+ async def _generate_tool_result(tool_id: str, config: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
112
+ """Generate mock result for tool execution"""
113
+
114
+ base_result = {
115
+ "task_id": str(uuid.uuid4()),
116
+ "tool_id": tool_id,
117
+ "tool_name": config["name"],
118
+ "started_at": datetime.now().isoformat(),
119
+ "completed_at": (datetime.now() + timedelta(seconds=random.randint(*config["processing_time"]))).isoformat(),
120
+ "status": "completed"
121
+ }
122
+
123
+ if tool_id == "export-pdf":
124
+ return {
125
+ **base_result,
126
+ "output": {
127
+ "file_name": f"conversation_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf",
128
+ "file_size": f"{random.randint(500, 2000)} KB",
129
+ "pages": random.randint(3, 15),
130
+ "download_url": f"https://example.com/downloads/pdf_{base_result['task_id']}.pdf"
131
+ },
132
+ "metadata": {
133
+ "format": "PDF",
134
+ "quality": "high",
135
+ "includes_images": True,
136
+ "includes_formatting": True
137
+ }
138
+ }
139
+
140
+ elif tool_id == "generate-ppt":
141
+ return {
142
+ **base_result,
143
+ "output": {
144
+ "file_name": f"presentation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pptx",
145
+ "file_size": f"{random.randint(2, 8)} MB",
146
+ "slides": random.randint(8, 20),
147
+ "download_url": f"https://example.com/downloads/ppt_{base_result['task_id']}.pptx"
148
+ },
149
+ "metadata": {
150
+ "template": "professional",
151
+ "theme": "modern",
152
+ "includes_charts": True,
153
+ "includes_animations": False
154
+ }
155
+ }
156
+
157
+ elif tool_id == "convert-doc":
158
+ source_format = request.get("source_format", "docx")
159
+ target_format = request.get("target_format", "pdf")
160
+
161
+ return {
162
+ **base_result,
163
+ "output": {
164
+ "file_name": f"converted_document_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{target_format}",
165
+ "file_size": f"{random.randint(800, 3000)} KB",
166
+ "download_url": f"https://example.com/downloads/converted_{base_result['task_id']}.{target_format}"
167
+ },
168
+ "metadata": {
169
+ "source_format": source_format,
170
+ "target_format": target_format,
171
+ "conversion_quality": "high",
172
+ "preserved_formatting": True
173
+ }
174
+ }
175
+
176
+ elif tool_id == "generate-video":
177
+ return {
178
+ **base_result,
179
+ "output": {
180
+ "file_name": f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4",
181
+ "file_size": f"{random.randint(10, 50)} MB",
182
+ "duration": f"{random.randint(30, 180)} seconds",
183
+ "download_url": f"https://example.com/downloads/video_{base_result['task_id']}.mp4"
184
+ },
185
+ "metadata": {
186
+ "resolution": "1920x1080",
187
+ "fps": 30,
188
+ "codec": "H.264",
189
+ "audio": True
190
+ }
191
+ }
192
+
193
+ elif tool_id == "create-image":
194
+ return {
195
+ **base_result,
196
+ "output": {
197
+ "file_name": f"generated_image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png",
198
+ "file_size": f"{random.randint(500, 2000)} KB",
199
+ "dimensions": "1024x1024",
200
+ "download_url": f"https://example.com/downloads/image_{base_result['task_id']}.png"
201
+ },
202
+ "metadata": {
203
+ "format": "PNG",
204
+ "quality": "high",
205
+ "style": "photorealistic",
206
+ "ai_model": "DALL-E 3"
207
+ }
208
+ }
209
+
210
+ elif tool_id == "export-excel":
211
+ return {
212
+ **base_result,
213
+ "output": {
214
+ "file_name": f"data_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx",
215
+ "file_size": f"{random.randint(200, 1500)} KB",
216
+ "sheets": random.randint(1, 5),
217
+ "rows": random.randint(100, 5000),
218
+ "download_url": f"https://example.com/downloads/excel_{base_result['task_id']}.xlsx"
219
+ },
220
+ "metadata": {
221
+ "format": "Excel 2019",
222
+ "includes_charts": True,
223
+ "includes_formulas": True,
224
+ "data_types": ["text", "numbers", "dates"]
225
+ }
226
+ }
227
+
228
+ else:
229
+ return {
230
+ **base_result,
231
+ "output": {
232
+ "message": "Tool executed successfully",
233
+ "result": "Generic tool result"
234
+ }
235
+ }
236
+
237
+ @router.get("/{tool_id}/status")
238
+ async def get_tool_status(tool_id: str):
239
+ """Get the current status of a tool"""
240
+ try:
241
+ if tool_id not in TOOL_CONFIGS:
242
+ raise HTTPException(status_code=404, detail=f"Tool '{tool_id}' not found")
243
+
244
+ config = TOOL_CONFIGS[tool_id]
245
+
246
+ status_data = {
247
+ "tool_id": tool_id,
248
+ "name": config["name"],
249
+ "status": "available",
250
+ "description": config["description"],
251
+ "output_format": config["output_format"],
252
+ "estimated_processing_time": f"{config['processing_time'][0]}-{config['processing_time'][1]} seconds",
253
+ "usage_stats": {
254
+ "total_executions": random.randint(50, 500),
255
+ "success_rate": round(random.uniform(0.85, 0.99), 2),
256
+ "average_processing_time": f"{random.randint(config['processing_time'][0], config['processing_time'][1])} seconds"
257
+ },
258
+ "last_used": (datetime.now() - timedelta(hours=random.randint(1, 48))).isoformat()
259
+ }
260
+
261
+ return {
262
+ "success": True,
263
+ "data": status_data,
264
+ "timestamp": datetime.now().isoformat()
265
+ }
266
+
267
+ except HTTPException:
268
+ raise
269
+ except Exception as e:
270
+ raise HTTPException(status_code=500, detail=f"Failed to get tool status: {str(e)}")
271
+
272
+ @router.get("/history")
273
+ async def get_tool_execution_history(
274
+ tool_id: Optional[str] = None,
275
+ limit: int = 20,
276
+ offset: int = 0
277
+ ):
278
+ """Get tool execution history"""
279
+ try:
280
+ # Generate mock history
281
+ history = []
282
+
283
+ for i in range(limit):
284
+ if tool_id and tool_id not in TOOL_CONFIGS:
285
+ continue
286
+
287
+ selected_tool_id = tool_id or random.choice(list(TOOL_CONFIGS.keys()))
288
+ config = TOOL_CONFIGS[selected_tool_id]
289
+
290
+ execution = {
291
+ "id": str(uuid.uuid4()),
292
+ "tool_id": selected_tool_id,
293
+ "tool_name": config["name"],
294
+ "status": random.choice(["completed", "completed", "completed", "failed"]),
295
+ "started_at": (datetime.now() - timedelta(hours=random.randint(1, 168))).isoformat(),
296
+ "completed_at": (datetime.now() - timedelta(hours=random.randint(1, 168))).isoformat(),
297
+ "processing_time": f"{random.randint(*config['processing_time'])} seconds",
298
+ "output_size": f"{random.randint(100, 5000)} KB"
299
+ }
300
+
301
+ history.append(execution)
302
+
303
+ return {
304
+ "success": True,
305
+ "data": history,
306
+ "pagination": {
307
+ "limit": limit,
308
+ "offset": offset,
309
+ "total": random.randint(100, 1000)
310
+ },
311
+ "timestamp": datetime.now().isoformat()
312
+ }
313
+
314
+ except Exception as e:
315
+ raise HTTPException(status_code=500, detail=f"Failed to get tool history: {str(e)}")
@@ -0,0 +1,11 @@
1
+ # Copyright (c) ModelScope Contributors. All rights reserved.
2
+ """
3
+ Sirchmunk Command Line Interface.
4
+
5
+ Provides CLI commands for initialization, configuration, server management,
6
+ and search operations.
7
+ """
8
+
9
+ from .cli import run_cmd
10
+
11
+ __all__ = ["run_cmd"]