ambivo-agents 1.0.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.
- ambivo_agents/__init__.py +91 -0
- ambivo_agents/agents/__init__.py +21 -0
- ambivo_agents/agents/assistant.py +203 -0
- ambivo_agents/agents/code_executor.py +133 -0
- ambivo_agents/agents/code_executor2.py +222 -0
- ambivo_agents/agents/knowledge_base.py +935 -0
- ambivo_agents/agents/media_editor.py +992 -0
- ambivo_agents/agents/moderator.py +617 -0
- ambivo_agents/agents/simple_web_search.py +404 -0
- ambivo_agents/agents/web_scraper.py +1027 -0
- ambivo_agents/agents/web_search.py +933 -0
- ambivo_agents/agents/youtube_download.py +784 -0
- ambivo_agents/cli.py +699 -0
- ambivo_agents/config/__init__.py +4 -0
- ambivo_agents/config/loader.py +301 -0
- ambivo_agents/core/__init__.py +33 -0
- ambivo_agents/core/base.py +1024 -0
- ambivo_agents/core/history.py +606 -0
- ambivo_agents/core/llm.py +333 -0
- ambivo_agents/core/memory.py +640 -0
- ambivo_agents/executors/__init__.py +8 -0
- ambivo_agents/executors/docker_executor.py +108 -0
- ambivo_agents/executors/media_executor.py +237 -0
- ambivo_agents/executors/youtube_executor.py +404 -0
- ambivo_agents/services/__init__.py +6 -0
- ambivo_agents/services/agent_service.py +605 -0
- ambivo_agents/services/factory.py +370 -0
- ambivo_agents-1.0.1.dist-info/METADATA +1090 -0
- ambivo_agents-1.0.1.dist-info/RECORD +33 -0
- ambivo_agents-1.0.1.dist-info/WHEEL +5 -0
- ambivo_agents-1.0.1.dist-info/entry_points.txt +3 -0
- ambivo_agents-1.0.1.dist-info/licenses/LICENSE +21 -0
- ambivo_agents-1.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
# ambivo_agents/executors/docker_executor.py
|
2
|
+
"""
|
3
|
+
Docker executor for secure code execution.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import time
|
7
|
+
import tempfile
|
8
|
+
from pathlib import Path
|
9
|
+
from typing import Dict, Any
|
10
|
+
|
11
|
+
from ..config.loader import load_config, get_config_section
|
12
|
+
|
13
|
+
try:
|
14
|
+
import docker
|
15
|
+
DOCKER_AVAILABLE = True
|
16
|
+
except ImportError:
|
17
|
+
DOCKER_AVAILABLE = False
|
18
|
+
|
19
|
+
|
20
|
+
class DockerCodeExecutor:
|
21
|
+
"""Secure code execution using Docker containers"""
|
22
|
+
|
23
|
+
def __init__(self, config: Dict[str, Any] = None):
|
24
|
+
# Load from YAML if config not provided
|
25
|
+
if config is None:
|
26
|
+
try:
|
27
|
+
full_config = load_config()
|
28
|
+
config = get_config_section('docker', full_config)
|
29
|
+
except Exception:
|
30
|
+
config = {}
|
31
|
+
|
32
|
+
self.config = config
|
33
|
+
self.work_dir = config.get("work_dir", '/opt/ambivo/work_dir')
|
34
|
+
self.docker_images = config.get("images", ["sgosain/amb-ubuntu-python-public-pod"])
|
35
|
+
self.timeout = config.get("timeout", 60)
|
36
|
+
self.memory_limit = config.get("memory_limit", "512m")
|
37
|
+
self.default_image = self.docker_images[0] if self.docker_images else "sgosain/amb-ubuntu-python-public-pod"
|
38
|
+
|
39
|
+
if not DOCKER_AVAILABLE:
|
40
|
+
raise ImportError("Docker package is required but not installed")
|
41
|
+
|
42
|
+
try:
|
43
|
+
self.docker_client = docker.from_env()
|
44
|
+
self.docker_client.ping()
|
45
|
+
self.available = True
|
46
|
+
except Exception as e:
|
47
|
+
raise ConnectionError(f"Failed to connect to Docker: {e}")
|
48
|
+
|
49
|
+
def execute_code(self, code: str, language: str = "python", files: Dict[str, str] = None) -> Dict[str, Any]:
|
50
|
+
"""Execute code in Docker container"""
|
51
|
+
try:
|
52
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
53
|
+
temp_path = Path(temp_dir)
|
54
|
+
|
55
|
+
if language == "python":
|
56
|
+
code_file = temp_path / "code.py"
|
57
|
+
code_file.write_text(code)
|
58
|
+
cmd = ["python", "/workspace/code.py"]
|
59
|
+
elif language == "bash":
|
60
|
+
code_file = temp_path / "script.sh"
|
61
|
+
code_file.write_text(code)
|
62
|
+
cmd = ["bash", "/workspace/script.sh"]
|
63
|
+
else:
|
64
|
+
raise ValueError(f"Unsupported language: {language}")
|
65
|
+
|
66
|
+
if files:
|
67
|
+
for filename, content in files.items():
|
68
|
+
file_path = temp_path / filename
|
69
|
+
file_path.write_text(content)
|
70
|
+
|
71
|
+
container_config = {
|
72
|
+
'image': self.default_image,
|
73
|
+
'command': cmd,
|
74
|
+
'volumes': {str(temp_path): {'bind': '/workspace', 'mode': 'rw'}},
|
75
|
+
'working_dir': '/workspace',
|
76
|
+
'mem_limit': self.memory_limit,
|
77
|
+
'network_disabled': True,
|
78
|
+
'remove': True,
|
79
|
+
'stdout': True,
|
80
|
+
'stderr': True
|
81
|
+
}
|
82
|
+
|
83
|
+
start_time = time.time()
|
84
|
+
container = self.docker_client.containers.run(**container_config)
|
85
|
+
execution_time = time.time() - start_time
|
86
|
+
|
87
|
+
output = container.decode('utf-8') if isinstance(container, bytes) else str(container)
|
88
|
+
|
89
|
+
return {
|
90
|
+
'success': True,
|
91
|
+
'output': output,
|
92
|
+
'execution_time': execution_time,
|
93
|
+
'language': language
|
94
|
+
}
|
95
|
+
|
96
|
+
except docker.errors.ContainerError as e:
|
97
|
+
return {
|
98
|
+
'success': False,
|
99
|
+
'error': f"Container error: {e.stderr.decode('utf-8') if e.stderr else 'Unknown error'}",
|
100
|
+
'exit_code': e.exit_status,
|
101
|
+
'language': language
|
102
|
+
}
|
103
|
+
except Exception as e:
|
104
|
+
return {
|
105
|
+
'success': False,
|
106
|
+
'error': str(e),
|
107
|
+
'language': language
|
108
|
+
}
|
@@ -0,0 +1,237 @@
|
|
1
|
+
# ambivo_agents/executors/media_executor.py
|
2
|
+
"""
|
3
|
+
Media Docker executor for FFmpeg operations.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import asyncio
|
7
|
+
import json
|
8
|
+
import time
|
9
|
+
import tempfile
|
10
|
+
import shutil
|
11
|
+
from pathlib import Path
|
12
|
+
from typing import Dict, Any
|
13
|
+
|
14
|
+
from ..config.loader import load_config, get_config_section
|
15
|
+
|
16
|
+
try:
|
17
|
+
import docker
|
18
|
+
DOCKER_AVAILABLE = True
|
19
|
+
except ImportError:
|
20
|
+
DOCKER_AVAILABLE = False
|
21
|
+
|
22
|
+
|
23
|
+
class MediaDockerExecutor:
|
24
|
+
"""Specialized Docker executor for media processing with ffmpeg"""
|
25
|
+
|
26
|
+
def __init__(self, config: Dict[str, Any] = None):
|
27
|
+
# Load from YAML if config not provided
|
28
|
+
if config is None:
|
29
|
+
try:
|
30
|
+
full_config = load_config()
|
31
|
+
config = get_config_section('media_editor', full_config)
|
32
|
+
except Exception:
|
33
|
+
config = {}
|
34
|
+
|
35
|
+
self.config = config
|
36
|
+
self.work_dir = config.get("work_dir", '/opt/ambivo/work_dir')
|
37
|
+
self.docker_image = config.get("docker_image", "sgosain/amb-ubuntu-python-public-pod")
|
38
|
+
self.timeout = config.get("timeout", 300) # 5 minutes for media processing
|
39
|
+
self.memory_limit = config.get("memory_limit", "2g")
|
40
|
+
|
41
|
+
# Media specific directories
|
42
|
+
self.input_dir = Path(config.get("input_dir", "./media_input"))
|
43
|
+
self.output_dir = Path(config.get("output_dir", "./media_output"))
|
44
|
+
|
45
|
+
# Ensure directories exist
|
46
|
+
self.input_dir.mkdir(exist_ok=True)
|
47
|
+
self.output_dir.mkdir(exist_ok=True)
|
48
|
+
|
49
|
+
if not DOCKER_AVAILABLE:
|
50
|
+
raise ImportError("Docker package is required for media processing")
|
51
|
+
|
52
|
+
try:
|
53
|
+
self.docker_client = docker.from_env()
|
54
|
+
self.docker_client.ping()
|
55
|
+
self.available = True
|
56
|
+
except Exception as e:
|
57
|
+
raise ConnectionError(f"Failed to connect to Docker for media processing: {e}")
|
58
|
+
|
59
|
+
def execute_ffmpeg_command(self,
|
60
|
+
ffmpeg_command: str,
|
61
|
+
input_files: Dict[str, str] = None,
|
62
|
+
output_filename: str = None,
|
63
|
+
work_files: Dict[str, str] = None) -> Dict[str, Any]:
|
64
|
+
"""
|
65
|
+
Execute ffmpeg command in Docker container
|
66
|
+
|
67
|
+
Args:
|
68
|
+
ffmpeg_command: FFmpeg command to execute
|
69
|
+
input_files: Dict of {container_path: host_path} for input files
|
70
|
+
output_filename: Expected output filename
|
71
|
+
work_files: Additional working files needed
|
72
|
+
"""
|
73
|
+
try:
|
74
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
75
|
+
temp_path = Path(temp_dir)
|
76
|
+
|
77
|
+
# Create input and output directories in temp
|
78
|
+
container_input = temp_path / "input"
|
79
|
+
container_output = temp_path / "output"
|
80
|
+
container_input.mkdir()
|
81
|
+
container_output.mkdir()
|
82
|
+
|
83
|
+
# Copy input files to temp directory
|
84
|
+
file_mapping = {}
|
85
|
+
if input_files:
|
86
|
+
for container_name, host_path in input_files.items():
|
87
|
+
if Path(host_path).exists():
|
88
|
+
dest_path = container_input / container_name
|
89
|
+
shutil.copy2(host_path, dest_path)
|
90
|
+
file_mapping[container_name] = f"/workspace/input/{container_name}"
|
91
|
+
else:
|
92
|
+
return {
|
93
|
+
'success': False,
|
94
|
+
'error': f'Input file not found: {host_path}',
|
95
|
+
'command': ffmpeg_command
|
96
|
+
}
|
97
|
+
|
98
|
+
# Copy additional work files
|
99
|
+
if work_files:
|
100
|
+
for container_name, content in work_files.items():
|
101
|
+
work_file = temp_path / container_name
|
102
|
+
work_file.write_text(content)
|
103
|
+
|
104
|
+
# Prepare the ffmpeg command with proper paths
|
105
|
+
final_command = ffmpeg_command
|
106
|
+
for container_name, container_path in file_mapping.items():
|
107
|
+
final_command = final_command.replace(f"${{{container_name}}}", container_path)
|
108
|
+
|
109
|
+
# Add output path
|
110
|
+
if output_filename:
|
111
|
+
final_command = final_command.replace("${OUTPUT}", f"/workspace/output/{output_filename}")
|
112
|
+
|
113
|
+
# Create execution script
|
114
|
+
script_content = f"""#!/bin/bash
|
115
|
+
set -e
|
116
|
+
cd /workspace
|
117
|
+
|
118
|
+
echo "FFmpeg version:"
|
119
|
+
ffmpeg -version | head -1
|
120
|
+
|
121
|
+
echo "Starting media processing..."
|
122
|
+
echo "Command: {final_command}"
|
123
|
+
|
124
|
+
# Execute the command
|
125
|
+
{final_command}
|
126
|
+
|
127
|
+
echo "Media processing completed successfully"
|
128
|
+
ls -la /workspace/output/
|
129
|
+
"""
|
130
|
+
|
131
|
+
script_file = temp_path / "process_media.sh"
|
132
|
+
script_file.write_text(script_content)
|
133
|
+
script_file.chmod(0o755)
|
134
|
+
|
135
|
+
# Container configuration for media processing
|
136
|
+
container_config = {
|
137
|
+
'image': self.docker_image,
|
138
|
+
'command': ["bash", "/workspace/process_media.sh"],
|
139
|
+
'volumes': {str(temp_path): {'bind': '/workspace', 'mode': 'rw'}},
|
140
|
+
'working_dir': '/workspace',
|
141
|
+
'mem_limit': self.memory_limit,
|
142
|
+
'network_disabled': True,
|
143
|
+
'remove': True,
|
144
|
+
'stdout': True,
|
145
|
+
'stderr': True,
|
146
|
+
'environment': {
|
147
|
+
'FFMPEG_PATH': '/usr/bin/ffmpeg',
|
148
|
+
'FFPROBE_PATH': '/usr/bin/ffprobe'
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
start_time = time.time()
|
153
|
+
|
154
|
+
try:
|
155
|
+
result = self.docker_client.containers.run(**container_config)
|
156
|
+
execution_time = time.time() - start_time
|
157
|
+
|
158
|
+
output = result.decode('utf-8') if isinstance(result, bytes) else str(result)
|
159
|
+
|
160
|
+
# Check if output file was created
|
161
|
+
output_files = list(container_output.glob("*"))
|
162
|
+
output_info = {}
|
163
|
+
|
164
|
+
if output_files:
|
165
|
+
output_file = output_files[0] # Take first output file
|
166
|
+
output_info = {
|
167
|
+
'filename': output_file.name,
|
168
|
+
'size_bytes': output_file.stat().st_size,
|
169
|
+
'path': str(output_file)
|
170
|
+
}
|
171
|
+
|
172
|
+
# Move output file to permanent location
|
173
|
+
permanent_output = self.output_dir / output_file.name
|
174
|
+
shutil.move(str(output_file), str(permanent_output))
|
175
|
+
output_info['final_path'] = str(permanent_output)
|
176
|
+
|
177
|
+
return {
|
178
|
+
'success': True,
|
179
|
+
'output': output,
|
180
|
+
'execution_time': execution_time,
|
181
|
+
'command': final_command,
|
182
|
+
'output_file': output_info,
|
183
|
+
'temp_dir': str(temp_path)
|
184
|
+
}
|
185
|
+
|
186
|
+
except Exception as container_error:
|
187
|
+
return {
|
188
|
+
'success': False,
|
189
|
+
'error': f"Container execution failed: {str(container_error)}",
|
190
|
+
'command': final_command,
|
191
|
+
'execution_time': time.time() - start_time
|
192
|
+
}
|
193
|
+
|
194
|
+
except Exception as e:
|
195
|
+
return {
|
196
|
+
'success': False,
|
197
|
+
'error': f"Media processing setup failed: {str(e)}",
|
198
|
+
'command': ffmpeg_command
|
199
|
+
}
|
200
|
+
|
201
|
+
def get_media_info(self, file_path: str) -> Dict[str, Any]:
|
202
|
+
"""Get media file information using ffprobe"""
|
203
|
+
|
204
|
+
if not Path(file_path).exists():
|
205
|
+
return {
|
206
|
+
'success': False,
|
207
|
+
'error': f'File not found: {file_path}'
|
208
|
+
}
|
209
|
+
|
210
|
+
# Use ffprobe to get media information
|
211
|
+
ffprobe_command = (
|
212
|
+
f"ffprobe -v quiet -print_format json -show_format -show_streams "
|
213
|
+
f"${{input_file}}"
|
214
|
+
)
|
215
|
+
|
216
|
+
result = self.execute_ffmpeg_command(
|
217
|
+
ffmpeg_command=ffprobe_command,
|
218
|
+
input_files={'input_file': file_path}
|
219
|
+
)
|
220
|
+
|
221
|
+
if result['success']:
|
222
|
+
try:
|
223
|
+
# Parse JSON output from ffprobe
|
224
|
+
media_info = json.loads(result['output'].split('\n')[-2]) # Get JSON from output
|
225
|
+
return {
|
226
|
+
'success': True,
|
227
|
+
'media_info': media_info,
|
228
|
+
'file_path': file_path
|
229
|
+
}
|
230
|
+
except:
|
231
|
+
return {
|
232
|
+
'success': True,
|
233
|
+
'raw_output': result['output'],
|
234
|
+
'file_path': file_path
|
235
|
+
}
|
236
|
+
|
237
|
+
return result
|