noesium 0.1.0__py3-none-any.whl → 0.2.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.
Files changed (60) hide show
  1. noesium/agents/askura_agent/__init__.py +22 -0
  2. noesium/agents/askura_agent/askura_agent.py +480 -0
  3. noesium/agents/askura_agent/conversation.py +164 -0
  4. noesium/agents/askura_agent/extractor.py +175 -0
  5. noesium/agents/askura_agent/memory.py +14 -0
  6. noesium/agents/askura_agent/models.py +239 -0
  7. noesium/agents/askura_agent/prompts.py +202 -0
  8. noesium/agents/askura_agent/reflection.py +234 -0
  9. noesium/agents/askura_agent/summarizer.py +30 -0
  10. noesium/agents/askura_agent/utils.py +6 -0
  11. noesium/agents/deep_research/__init__.py +13 -0
  12. noesium/agents/deep_research/agent.py +398 -0
  13. noesium/agents/deep_research/prompts.py +84 -0
  14. noesium/agents/deep_research/schemas.py +42 -0
  15. noesium/agents/deep_research/state.py +54 -0
  16. noesium/agents/search/__init__.py +5 -0
  17. noesium/agents/search/agent.py +474 -0
  18. noesium/agents/search/state.py +28 -0
  19. noesium/core/__init__.py +1 -1
  20. noesium/core/agent/base.py +10 -2
  21. noesium/core/goalith/decomposer/llm_decomposer.py +1 -1
  22. noesium/core/llm/__init__.py +1 -1
  23. noesium/core/llm/base.py +2 -2
  24. noesium/core/llm/litellm.py +42 -21
  25. noesium/core/llm/llamacpp.py +25 -4
  26. noesium/core/llm/ollama.py +43 -22
  27. noesium/core/llm/openai.py +25 -5
  28. noesium/core/llm/openrouter.py +1 -1
  29. noesium/core/toolify/base.py +9 -2
  30. noesium/core/toolify/config.py +2 -2
  31. noesium/core/toolify/registry.py +21 -5
  32. noesium/core/tracing/opik_tracing.py +7 -7
  33. noesium/core/vector_store/__init__.py +2 -2
  34. noesium/core/vector_store/base.py +1 -1
  35. noesium/core/vector_store/pgvector.py +10 -13
  36. noesium/core/vector_store/weaviate.py +2 -1
  37. noesium/toolkits/__init__.py +1 -0
  38. noesium/toolkits/arxiv_toolkit.py +310 -0
  39. noesium/toolkits/audio_aliyun_toolkit.py +441 -0
  40. noesium/toolkits/audio_toolkit.py +370 -0
  41. noesium/toolkits/bash_toolkit.py +332 -0
  42. noesium/toolkits/document_toolkit.py +454 -0
  43. noesium/toolkits/file_edit_toolkit.py +552 -0
  44. noesium/toolkits/github_toolkit.py +395 -0
  45. noesium/toolkits/gmail_toolkit.py +575 -0
  46. noesium/toolkits/image_toolkit.py +425 -0
  47. noesium/toolkits/memory_toolkit.py +398 -0
  48. noesium/toolkits/python_executor_toolkit.py +334 -0
  49. noesium/toolkits/search_toolkit.py +451 -0
  50. noesium/toolkits/serper_toolkit.py +623 -0
  51. noesium/toolkits/tabular_data_toolkit.py +537 -0
  52. noesium/toolkits/user_interaction_toolkit.py +365 -0
  53. noesium/toolkits/video_toolkit.py +168 -0
  54. noesium/toolkits/wikipedia_toolkit.py +420 -0
  55. noesium-0.2.1.dist-info/METADATA +253 -0
  56. {noesium-0.1.0.dist-info → noesium-0.2.1.dist-info}/RECORD +59 -23
  57. {noesium-0.1.0.dist-info → noesium-0.2.1.dist-info}/licenses/LICENSE +1 -1
  58. noesium-0.1.0.dist-info/METADATA +0 -525
  59. {noesium-0.1.0.dist-info → noesium-0.2.1.dist-info}/WHEEL +0 -0
  60. {noesium-0.1.0.dist-info → noesium-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,365 @@
1
+ """
2
+ User interaction toolkit for communication and input handling.
3
+
4
+ Provides tools for interacting with users, collecting input, and managing
5
+ the flow of information between the AI system and human users.
6
+ """
7
+
8
+ import asyncio
9
+ from typing import Any, Callable, Dict, Optional
10
+
11
+ from noesium.core.toolify.base import AsyncBaseToolkit
12
+ from noesium.core.toolify.config import ToolkitConfig
13
+ from noesium.core.toolify.registry import register_toolkit
14
+ from noesium.core.utils.logging import get_logger
15
+
16
+ logger = get_logger(__name__)
17
+
18
+
19
+ @register_toolkit("user_interaction")
20
+ class UserInteractionToolkit(AsyncBaseToolkit):
21
+ """
22
+ Toolkit for user interaction and communication.
23
+
24
+ This toolkit provides capabilities for:
25
+ - Asking questions and collecting user input
26
+ - Providing final answers and results
27
+ - Managing interactive workflows
28
+ - Handling user confirmations and choices
29
+ - Formatting and presenting information to users
30
+
31
+ Features:
32
+ - Customizable input prompts
33
+ - Support for different input types
34
+ - Validation and error handling
35
+ - Configurable interaction modes
36
+ - Integration with various UI frameworks
37
+ - Async-compatible user interaction
38
+
39
+ Use cases:
40
+ - Interactive problem-solving sessions
41
+ - User preference collection
42
+ - Confirmation dialogs
43
+ - Multi-step workflows requiring user input
44
+ - Educational and tutorial applications
45
+ - Debugging and troubleshooting assistance
46
+ """
47
+
48
+ def __init__(self, config: ToolkitConfig = None):
49
+ """
50
+ Initialize the user interaction toolkit.
51
+
52
+ Args:
53
+ config: Toolkit configuration
54
+ """
55
+ super().__init__(config)
56
+
57
+ # Configuration
58
+ self.interaction_mode = self.config.config.get("interaction_mode", "console") # console, web, api
59
+ self.timeout_seconds = self.config.config.get("timeout_seconds", 300) # 5 minutes default
60
+ self.enable_validation = self.config.config.get("enable_validation", True)
61
+ self.prompt_prefix = self.config.config.get("prompt_prefix", "🤖 ")
62
+
63
+ # Custom interaction functions
64
+ self.custom_ask_function: Optional[Callable] = None
65
+ self.custom_display_function: Optional[Callable] = None
66
+
67
+ # Interaction history
68
+ self.interaction_history = []
69
+
70
+ self.logger.info(f"User interaction toolkit initialized in {self.interaction_mode} mode")
71
+
72
+ def set_custom_ask_function(self, ask_function: Callable[[str], str]):
73
+ """
74
+ Set a custom function for asking user questions.
75
+
76
+ Args:
77
+ ask_function: Function that takes a question string and returns user response
78
+ """
79
+ self.custom_ask_function = ask_function
80
+ self.logger.info("Custom ask function registered")
81
+
82
+ def set_custom_display_function(self, display_function: Callable[[str], None]):
83
+ """
84
+ Set a custom function for displaying information to users.
85
+
86
+ Args:
87
+ display_function: Function that takes a message string and displays it
88
+ """
89
+ self.custom_display_function = display_function
90
+ self.logger.info("Custom display function registered")
91
+
92
+ def _record_interaction(self, interaction_type: str, question: str, response: Any = None):
93
+ """Record an interaction in the history."""
94
+ self.interaction_history.append(
95
+ {
96
+ "type": interaction_type,
97
+ "question": question,
98
+ "response": response,
99
+ "timestamp": asyncio.get_event_loop().time(),
100
+ }
101
+ )
102
+
103
+ def _console_input(self, prompt: str) -> str:
104
+ """Get input from console with proper formatting."""
105
+ try:
106
+ return input(f"{self.prompt_prefix}{prompt}\n> ").strip()
107
+ except (EOFError, KeyboardInterrupt):
108
+ return ""
109
+
110
+ async def ask_user(
111
+ self, question: str, expected_type: str = "text", validation_pattern: Optional[str] = None
112
+ ) -> str:
113
+ """
114
+ Ask the user a question and wait for their response.
115
+
116
+ This tool allows you to interact with the user by asking questions and
117
+ collecting their input. It's essential for gathering information that
118
+ only the user can provide, such as preferences, confirmations, or
119
+ specific requirements.
120
+
121
+ Use this tool when you need to:
122
+ - Get user preferences or choices
123
+ - Ask for clarification on ambiguous requests
124
+ - Collect specific information not available elsewhere
125
+ - Confirm actions before proceeding
126
+ - Get user feedback on results or proposals
127
+
128
+ Args:
129
+ question: The question to ask the user
130
+ expected_type: Type of expected response (text, number, yes_no, choice)
131
+ validation_pattern: Optional regex pattern for input validation
132
+
133
+ Returns:
134
+ User's response as a string
135
+
136
+ Examples:
137
+ - ask_user("What is your preferred programming language?")
138
+ - ask_user("How many items would you like?", "number")
139
+ - ask_user("Do you want to proceed?", "yes_no")
140
+ - ask_user("Choose an option (A, B, or C):", "choice")
141
+ """
142
+ self.logger.info(f"Asking user: {question}")
143
+
144
+ # Format the question
145
+ formatted_question = question
146
+ if expected_type == "yes_no":
147
+ formatted_question += " (yes/no)"
148
+ elif expected_type == "number":
149
+ formatted_question += " (enter a number)"
150
+ elif expected_type == "choice":
151
+ formatted_question += " (enter your choice)"
152
+
153
+ # Use custom function if available
154
+ if self.custom_ask_function:
155
+ try:
156
+ response = self.custom_ask_function(formatted_question)
157
+ except Exception as e:
158
+ self.logger.error(f"Custom ask function failed: {e}")
159
+ response = self._console_input(formatted_question)
160
+ else:
161
+ response = self._console_input(formatted_question)
162
+
163
+ # Validate response if enabled
164
+ if self.enable_validation and response:
165
+ if expected_type == "yes_no":
166
+ response = response.lower()
167
+ if response not in ["yes", "no", "y", "n", "true", "false", "1", "0"]:
168
+ return await self.ask_user("Please answer with yes/no (or y/n):", expected_type, validation_pattern)
169
+ # Normalize response
170
+ response = "yes" if response in ["yes", "y", "true", "1"] else "no"
171
+
172
+ elif expected_type == "number":
173
+ try:
174
+ float(response) # Validate it's a number
175
+ except ValueError:
176
+ return await self.ask_user("Please enter a valid number:", expected_type, validation_pattern)
177
+
178
+ elif validation_pattern:
179
+ import re
180
+
181
+ if not re.match(validation_pattern, response):
182
+ return await self.ask_user(
183
+ f"Invalid format. Please try again: {question}", expected_type, validation_pattern
184
+ )
185
+
186
+ # Record the interaction
187
+ self._record_interaction("question", question, response)
188
+
189
+ self.logger.info(f"User responded: {response}")
190
+ return response
191
+
192
+ async def confirm_action(self, action_description: str) -> bool:
193
+ """
194
+ Ask the user to confirm an action before proceeding.
195
+
196
+ Args:
197
+ action_description: Description of the action to confirm
198
+
199
+ Returns:
200
+ True if user confirms, False otherwise
201
+ """
202
+ question = f"Are you sure you want to {action_description}?"
203
+ response = await self.ask_user(question, "yes_no")
204
+ return response.lower() in ["yes", "y"]
205
+
206
+ async def get_user_choice(self, prompt: str, choices: list) -> str:
207
+ """
208
+ Present multiple choices to the user and get their selection.
209
+
210
+ Args:
211
+ prompt: Question or instruction for the user
212
+ choices: List of available choices
213
+
214
+ Returns:
215
+ Selected choice
216
+ """
217
+ # Format choices
218
+ choices_text = "\n".join(f"{i+1}. {choice}" for i, choice in enumerate(choices))
219
+ full_prompt = f"{prompt}\n\n{choices_text}\n\nEnter your choice (1-{len(choices)}):"
220
+
221
+ while True:
222
+ response = await self.ask_user(full_prompt, "number")
223
+
224
+ try:
225
+ choice_index = int(response) - 1
226
+ if 0 <= choice_index < len(choices):
227
+ selected = choices[choice_index]
228
+ self.logger.info(f"User selected: {selected}")
229
+ return selected
230
+ else:
231
+ await self.display_message(f"Please enter a number between 1 and {len(choices)}")
232
+ except ValueError:
233
+ await self.display_message("Please enter a valid number")
234
+
235
+ async def display_message(self, message: str, message_type: str = "info") -> str:
236
+ """
237
+ Display a message to the user.
238
+
239
+ Args:
240
+ message: Message to display
241
+ message_type: Type of message (info, warning, error, success)
242
+
243
+ Returns:
244
+ Confirmation that message was displayed
245
+ """
246
+ # Format message based on type
247
+ prefixes = {"info": "ℹ️ ", "warning": "⚠️ ", "error": "❌ ", "success": "✅ "}
248
+
249
+ formatted_message = f"{prefixes.get(message_type, '')}{message}"
250
+
251
+ # Use custom display function if available
252
+ if self.custom_display_function:
253
+ try:
254
+ self.custom_display_function(formatted_message)
255
+ except Exception as e:
256
+ self.logger.error(f"Custom display function failed: {e}")
257
+ print(formatted_message)
258
+ else:
259
+ print(formatted_message)
260
+
261
+ # Record the interaction
262
+ self._record_interaction("display", formatted_message)
263
+
264
+ return f"Message displayed to user: {message_type}"
265
+
266
+ async def final_answer(self, answer: Any, format_type: str = "text") -> str:
267
+ """
268
+ Provide a final answer to the user's original question or request.
269
+
270
+ This tool should be used when you have completed the user's request
271
+ and want to present the final result or conclusion. It formats and
272
+ presents the answer in a clear, user-friendly manner.
273
+
274
+ Args:
275
+ answer: The final answer or result to present
276
+ format_type: How to format the answer (text, json, markdown, list)
277
+
278
+ Returns:
279
+ Confirmation that the final answer was provided
280
+
281
+ Examples:
282
+ - final_answer("The calculation result is 42")
283
+ - final_answer({"result": 42, "method": "calculation"}, "json")
284
+ - final_answer("# Results\n\nThe analysis shows...", "markdown")
285
+ """
286
+ self.logger.info("Providing final answer to user")
287
+
288
+ # Format the answer based on type
289
+ if format_type == "json":
290
+ import json
291
+
292
+ if isinstance(answer, (dict, list)):
293
+ formatted_answer = json.dumps(answer, indent=2)
294
+ else:
295
+ formatted_answer = json.dumps({"result": answer}, indent=2)
296
+
297
+ elif format_type == "markdown":
298
+ formatted_answer = str(answer)
299
+
300
+ elif format_type == "list" and isinstance(answer, (list, tuple)):
301
+ formatted_answer = "\n".join(f"• {item}" for item in answer)
302
+
303
+ else:
304
+ formatted_answer = str(answer)
305
+
306
+ # Display the final answer
307
+ await self.display_message(f"Final Answer:\n\n{formatted_answer}", "success")
308
+
309
+ # Record the interaction
310
+ self._record_interaction("final_answer", formatted_answer)
311
+
312
+ return "Final answer provided to user"
313
+
314
+ async def get_interaction_history(self) -> str:
315
+ """
316
+ Get a summary of all user interactions in this session.
317
+
318
+ Returns:
319
+ Formatted interaction history
320
+ """
321
+ if not self.interaction_history:
322
+ return "No user interactions recorded in this session."
323
+
324
+ history_lines = ["User Interaction History:", "=" * 30, ""]
325
+
326
+ for i, interaction in enumerate(self.interaction_history, 1):
327
+ interaction["timestamp"]
328
+ interaction_type = interaction["type"]
329
+ question = interaction["question"]
330
+ response = interaction.get("response", "N/A")
331
+
332
+ history_lines.append(f"{i}. [{interaction_type.upper()}] {question}")
333
+ if response and interaction_type != "display":
334
+ history_lines.append(f" Response: {response}")
335
+ history_lines.append("")
336
+
337
+ return "\n".join(history_lines)
338
+
339
+ async def clear_interaction_history(self) -> str:
340
+ """
341
+ Clear the interaction history.
342
+
343
+ Returns:
344
+ Confirmation message
345
+ """
346
+ count = len(self.interaction_history)
347
+ self.interaction_history.clear()
348
+ return f"Cleared {count} interactions from history."
349
+
350
+ async def get_tools_map(self) -> Dict[str, Callable]:
351
+ """
352
+ Get the mapping of tool names to their implementation functions.
353
+
354
+ Returns:
355
+ Dictionary mapping tool names to callable functions
356
+ """
357
+ return {
358
+ "ask_user": self.ask_user,
359
+ "confirm_action": self.confirm_action,
360
+ "get_user_choice": self.get_user_choice,
361
+ "display_message": self.display_message,
362
+ "final_answer": self.final_answer,
363
+ "get_interaction_history": self.get_interaction_history,
364
+ "clear_interaction_history": self.clear_interaction_history,
365
+ }
@@ -0,0 +1,168 @@
1
+ """
2
+ Video analysis toolkit for video understanding and processing.
3
+
4
+ Provides tools for video analysis, question answering, and content extraction
5
+ using Google's Gemini API for video understanding.
6
+ """
7
+
8
+ from typing import Callable, Dict
9
+
10
+ from noesium.core.toolify.base import AsyncBaseToolkit
11
+ from noesium.core.toolify.config import ToolkitConfig
12
+ from noesium.core.toolify.registry import register_toolkit
13
+ from noesium.core.utils.logging import get_logger
14
+
15
+ logger = get_logger(__name__)
16
+
17
+ try:
18
+ from google import genai
19
+ from google.genai.types import HttpOptions, Part
20
+
21
+ GOOGLE_GENAI_AVAILABLE = True
22
+ except ImportError:
23
+ genai = None
24
+ HttpOptions = None
25
+ Part = None
26
+ GOOGLE_GENAI_AVAILABLE = False
27
+
28
+
29
+ @register_toolkit("video")
30
+ class VideoToolkit(AsyncBaseToolkit):
31
+ """
32
+ Toolkit for video analysis and understanding.
33
+
34
+ This toolkit provides capabilities for:
35
+ - Video content analysis using Google's Gemini API
36
+ - Video question answering
37
+ - Scene description and object detection
38
+ - Temporal analysis of video content
39
+
40
+ Features:
41
+ - Support for various video formats
42
+ - URL and local file processing
43
+ - Comprehensive video understanding
44
+ - Integration with Google's advanced video models
45
+
46
+ Required dependencies:
47
+ - google-genai package
48
+ - Google API key with Gemini access
49
+
50
+ Note: This is a simplified implementation. Full video processing
51
+ capabilities require additional development and API integration.
52
+ """
53
+
54
+ def __init__(self, config: ToolkitConfig = None):
55
+ """
56
+ Initialize the video toolkit.
57
+
58
+ Args:
59
+ config: Toolkit configuration containing API keys and settings
60
+ """
61
+ super().__init__(config)
62
+
63
+ if not GOOGLE_GENAI_AVAILABLE:
64
+ self.logger.warning(
65
+ "google-genai package not available. Video analysis will be limited. "
66
+ "Install with: pip install google-genai"
67
+ )
68
+
69
+ # Configuration
70
+ self.google_api_key = self.config.config.get("GOOGLE_API_KEY")
71
+ self.model_name = self.config.config.get("google_model", "gemini-1.5-pro")
72
+
73
+ # Initialize client if available
74
+ self.client = None
75
+ if GOOGLE_GENAI_AVAILABLE and self.google_api_key:
76
+ try:
77
+ self.client = genai.Client(api_key=self.google_api_key, http_options=HttpOptions(api_version="v1alpha"))
78
+ self.logger.info("Google Gemini client initialized for video analysis")
79
+ except Exception as e:
80
+ self.logger.error(f"Failed to initialize Google client: {e}")
81
+
82
+ async def analyze_video(self, video_path: str, question: str = "Describe this video") -> str:
83
+ """
84
+ Analyze a video and answer questions about its content.
85
+
86
+ This tool uses Google's Gemini API to analyze video content and answer
87
+ questions about what's happening in the video, including objects, actions,
88
+ scenes, and temporal sequences.
89
+
90
+ Args:
91
+ video_path: Path or URL to the video file
92
+ question: Question to ask about the video content
93
+
94
+ Returns:
95
+ Analysis or answer based on the video content
96
+
97
+ Note: This is a placeholder implementation. Full functionality requires
98
+ proper Google Gemini API integration and video processing capabilities.
99
+ """
100
+ self.logger.info(f"Analyzing video: {video_path}")
101
+
102
+ if not self.client:
103
+ return (
104
+ "Video analysis not available. Please ensure:\n"
105
+ "1. google-genai package is installed\n"
106
+ "2. GOOGLE_API_KEY is configured\n"
107
+ "3. API key has access to Gemini video capabilities"
108
+ )
109
+
110
+ try:
111
+ # This is a simplified placeholder implementation
112
+ # Full implementation would require proper video upload and processing
113
+ return (
114
+ f"Video analysis for: {video_path}\n"
115
+ f"Question: {question}\n\n"
116
+ "Note: Full video analysis capabilities are under development. "
117
+ "This toolkit provides the framework for video understanding "
118
+ "but requires additional implementation for complete functionality."
119
+ )
120
+
121
+ except Exception as e:
122
+ error_msg = f"Video analysis failed: {str(e)}"
123
+ self.logger.error(error_msg)
124
+ return error_msg
125
+
126
+ async def get_video_info(self, video_path: str) -> Dict:
127
+ """
128
+ Get basic information about a video file.
129
+
130
+ Args:
131
+ video_path: Path to the video file
132
+
133
+ Returns:
134
+ Dictionary with video metadata
135
+ """
136
+ try:
137
+ import os
138
+ from pathlib import Path
139
+
140
+ if os.path.exists(video_path):
141
+ file_path = Path(video_path)
142
+ file_size = file_path.stat().st_size
143
+
144
+ return {
145
+ "path": video_path,
146
+ "exists": True,
147
+ "size_bytes": file_size,
148
+ "size_mb": round(file_size / (1024 * 1024), 2),
149
+ "extension": file_path.suffix,
150
+ "note": "Detailed video metadata requires additional video processing libraries",
151
+ }
152
+ else:
153
+ return {"path": video_path, "exists": False, "error": "File not found"}
154
+
155
+ except Exception as e:
156
+ return {"error": f"Failed to get video info: {str(e)}"}
157
+
158
+ async def get_tools_map(self) -> Dict[str, Callable]:
159
+ """
160
+ Get the mapping of tool names to their implementation functions.
161
+
162
+ Returns:
163
+ Dictionary mapping tool names to callable functions
164
+ """
165
+ return {
166
+ "analyze_video": self.analyze_video,
167
+ "get_video_info": self.get_video_info,
168
+ }