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.
- noesium/agents/askura_agent/__init__.py +22 -0
- noesium/agents/askura_agent/askura_agent.py +480 -0
- noesium/agents/askura_agent/conversation.py +164 -0
- noesium/agents/askura_agent/extractor.py +175 -0
- noesium/agents/askura_agent/memory.py +14 -0
- noesium/agents/askura_agent/models.py +239 -0
- noesium/agents/askura_agent/prompts.py +202 -0
- noesium/agents/askura_agent/reflection.py +234 -0
- noesium/agents/askura_agent/summarizer.py +30 -0
- noesium/agents/askura_agent/utils.py +6 -0
- noesium/agents/deep_research/__init__.py +13 -0
- noesium/agents/deep_research/agent.py +398 -0
- noesium/agents/deep_research/prompts.py +84 -0
- noesium/agents/deep_research/schemas.py +42 -0
- noesium/agents/deep_research/state.py +54 -0
- noesium/agents/search/__init__.py +5 -0
- noesium/agents/search/agent.py +474 -0
- noesium/agents/search/state.py +28 -0
- noesium/core/__init__.py +1 -1
- noesium/core/agent/base.py +10 -2
- noesium/core/goalith/decomposer/llm_decomposer.py +1 -1
- noesium/core/llm/__init__.py +1 -1
- noesium/core/llm/base.py +2 -2
- noesium/core/llm/litellm.py +42 -21
- noesium/core/llm/llamacpp.py +25 -4
- noesium/core/llm/ollama.py +43 -22
- noesium/core/llm/openai.py +25 -5
- noesium/core/llm/openrouter.py +1 -1
- noesium/core/toolify/base.py +9 -2
- noesium/core/toolify/config.py +2 -2
- noesium/core/toolify/registry.py +21 -5
- noesium/core/tracing/opik_tracing.py +7 -7
- noesium/core/vector_store/__init__.py +2 -2
- noesium/core/vector_store/base.py +1 -1
- noesium/core/vector_store/pgvector.py +10 -13
- noesium/core/vector_store/weaviate.py +2 -1
- noesium/toolkits/__init__.py +1 -0
- noesium/toolkits/arxiv_toolkit.py +310 -0
- noesium/toolkits/audio_aliyun_toolkit.py +441 -0
- noesium/toolkits/audio_toolkit.py +370 -0
- noesium/toolkits/bash_toolkit.py +332 -0
- noesium/toolkits/document_toolkit.py +454 -0
- noesium/toolkits/file_edit_toolkit.py +552 -0
- noesium/toolkits/github_toolkit.py +395 -0
- noesium/toolkits/gmail_toolkit.py +575 -0
- noesium/toolkits/image_toolkit.py +425 -0
- noesium/toolkits/memory_toolkit.py +398 -0
- noesium/toolkits/python_executor_toolkit.py +334 -0
- noesium/toolkits/search_toolkit.py +451 -0
- noesium/toolkits/serper_toolkit.py +623 -0
- noesium/toolkits/tabular_data_toolkit.py +537 -0
- noesium/toolkits/user_interaction_toolkit.py +365 -0
- noesium/toolkits/video_toolkit.py +168 -0
- noesium/toolkits/wikipedia_toolkit.py +420 -0
- noesium-0.2.1.dist-info/METADATA +253 -0
- {noesium-0.1.0.dist-info → noesium-0.2.1.dist-info}/RECORD +59 -23
- {noesium-0.1.0.dist-info → noesium-0.2.1.dist-info}/licenses/LICENSE +1 -1
- noesium-0.1.0.dist-info/METADATA +0 -525
- {noesium-0.1.0.dist-info → noesium-0.2.1.dist-info}/WHEEL +0 -0
- {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
|
+
}
|