camel-ai 0.2.73a4__py3-none-any.whl → 0.2.80a2__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.
- camel/__init__.py +1 -1
- camel/agents/_utils.py +38 -0
- camel/agents/chat_agent.py +2217 -519
- camel/agents/mcp_agent.py +30 -27
- camel/configs/__init__.py +15 -0
- camel/configs/aihubmix_config.py +88 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/configs/minimax_config.py +93 -0
- camel/configs/nebius_config.py +103 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/datasets/base_generator.py +39 -10
- camel/environments/single_step.py +28 -3
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +3 -12
- camel/interpreters/e2b_interpreter.py +34 -1
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/chunkr_reader.py +9 -0
- camel/memories/agent_memories.py +48 -4
- camel/memories/base.py +26 -0
- camel/memories/blocks/chat_history_block.py +122 -4
- camel/memories/context_creators/score_based.py +25 -384
- camel/memories/records.py +88 -8
- camel/messages/base.py +153 -34
- camel/models/__init__.py +10 -0
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +1 -16
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +6 -19
- camel/models/aws_bedrock_model.py +2 -33
- camel/models/azure_openai_model.py +114 -89
- camel/models/base_audio_model.py +3 -1
- camel/models/base_model.py +32 -14
- camel/models/cohere_model.py +1 -16
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +1 -16
- camel/models/deepseek_model.py +1 -16
- camel/models/fish_audio_model.py +6 -0
- camel/models/gemini_model.py +36 -18
- camel/models/groq_model.py +1 -17
- camel/models/internlm_model.py +1 -16
- camel/models/litellm_model.py +1 -16
- camel/models/lmstudio_model.py +1 -17
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +1 -16
- camel/models/model_factory.py +27 -1
- camel/models/modelscope_model.py +1 -16
- camel/models/moonshot_model.py +105 -24
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +0 -5
- camel/models/netmind_model.py +1 -16
- camel/models/novita_model.py +1 -16
- camel/models/nvidia_model.py +1 -16
- camel/models/ollama_model.py +4 -19
- camel/models/openai_compatible_model.py +62 -41
- camel/models/openai_model.py +62 -57
- camel/models/openrouter_model.py +1 -17
- camel/models/ppio_model.py +1 -16
- camel/models/qianfan_model.py +1 -16
- camel/models/qwen_model.py +1 -16
- camel/models/reka_model.py +1 -16
- camel/models/samba_model.py +34 -47
- camel/models/sglang_model.py +64 -31
- camel/models/siliconflow_model.py +1 -16
- camel/models/stub_model.py +0 -4
- camel/models/togetherai_model.py +1 -16
- camel/models/vllm_model.py +1 -16
- camel/models/volcano_model.py +0 -17
- camel/models/watsonx_model.py +1 -16
- camel/models/yi_model.py +1 -16
- camel/models/zhipuai_model.py +60 -16
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/daytona_runtime.py +11 -12
- camel/societies/__init__.py +2 -0
- camel/societies/workforce/__init__.py +2 -0
- camel/societies/workforce/events.py +122 -0
- camel/societies/workforce/prompts.py +146 -66
- camel/societies/workforce/role_playing_worker.py +15 -11
- camel/societies/workforce/single_agent_worker.py +302 -65
- camel/societies/workforce/structured_output_handler.py +30 -18
- camel/societies/workforce/task_channel.py +163 -27
- camel/societies/workforce/utils.py +107 -13
- camel/societies/workforce/workflow_memory_manager.py +772 -0
- camel/societies/workforce/workforce.py +1949 -579
- camel/societies/workforce/workforce_callback.py +74 -0
- camel/societies/workforce/workforce_logger.py +168 -145
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/key_value_storages/mem0_cloud.py +48 -47
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/oceanbase.py +13 -13
- camel/storages/vectordb_storages/qdrant.py +3 -3
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +4 -3
- camel/toolkits/__init__.py +20 -7
- camel/toolkits/aci_toolkit.py +45 -0
- camel/toolkits/base.py +6 -4
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/dappier_toolkit.py +5 -1
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
- camel/toolkits/excel_toolkit.py +1 -1
- camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +430 -36
- camel/toolkits/function_tool.py +13 -3
- camel/toolkits/github_toolkit.py +104 -17
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +38 -4
- camel/toolkits/google_drive_mcp_toolkit.py +12 -31
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +15 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +77 -8
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +884 -88
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +959 -89
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +9 -2
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +281 -213
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +23 -3
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +72 -7
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +582 -132
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +158 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +55 -8
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +43 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +321 -8
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +10 -4
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +45 -4
- camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +151 -53
- camel/toolkits/klavis_toolkit.py +5 -1
- camel/toolkits/markitdown_toolkit.py +27 -1
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +366 -71
- camel/toolkits/memory_toolkit.py +5 -1
- camel/toolkits/message_integration.py +18 -13
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +19 -10
- camel/toolkits/notion_mcp_toolkit.py +16 -26
- camel/toolkits/openbb_toolkit.py +5 -1
- camel/toolkits/origene_mcp_toolkit.py +8 -49
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/search_toolkit.py +264 -91
- camel/toolkits/slack_toolkit.py +64 -10
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +17 -11
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/zapier_toolkit.py +5 -1
- camel/types/__init__.py +2 -2
- camel/types/enums.py +274 -7
- camel/types/openai_types.py +2 -2
- camel/types/unified_model_type.py +15 -0
- camel/utils/commons.py +36 -5
- camel/utils/constants.py +3 -0
- camel/utils/context_utils.py +1003 -0
- camel/utils/mcp.py +138 -4
- camel/utils/token_counting.py +43 -20
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +223 -83
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +170 -141
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1550
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import base64
|
|
17
|
+
import os
|
|
18
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
19
|
+
|
|
20
|
+
from camel.logger import get_logger
|
|
21
|
+
from camel.toolkits import FunctionTool
|
|
22
|
+
from camel.toolkits.base import BaseToolkit
|
|
23
|
+
from camel.utils import api_keys_required, dependencies_required
|
|
24
|
+
|
|
25
|
+
logger = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@api_keys_required(
|
|
29
|
+
[("GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_APPLICATION_CREDENTIALS")]
|
|
30
|
+
)
|
|
31
|
+
class VertexAIVeoToolkit(BaseToolkit):
|
|
32
|
+
r"""A toolkit for interacting with Google Vertex AI Veo video generation.
|
|
33
|
+
|
|
34
|
+
This toolkit provides methods for generating videos using Google's Veo,
|
|
35
|
+
supporting both text-to-video and image-to-video generation with various
|
|
36
|
+
customization options.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
@dependencies_required('google.cloud.aiplatform')
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
project_id: Optional[str] = None,
|
|
43
|
+
location: str = "us-central1",
|
|
44
|
+
model_id: str = "veo-2.0-generate-001",
|
|
45
|
+
output_storage_uri: Optional[str] = None,
|
|
46
|
+
timeout: Optional[float] = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
r"""Initialize the Vertex AI Veo toolkit.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
project_id (Optional[str]): Google Cloud project ID. If not
|
|
52
|
+
provided,
|
|
53
|
+
will use the default project from environment.
|
|
54
|
+
(default: :obj:`None`)
|
|
55
|
+
location (str): Google Cloud location for the API calls.
|
|
56
|
+
(default: :obj:`"us-central1"`)
|
|
57
|
+
model_id (str): The Veo model ID to use. Options include
|
|
58
|
+
"veo-2.0-generate-001" or "veo-3.0-generate-preview".
|
|
59
|
+
(default: :obj:`"veo-2.0-generate-001"`)
|
|
60
|
+
output_storage_uri (Optional[str]): Cloud Storage URI to save
|
|
61
|
+
output videos. If not provided, returns video bytes.
|
|
62
|
+
(default: :obj:`None`)
|
|
63
|
+
timeout (Optional[float]): Request timeout in seconds.
|
|
64
|
+
(default: :obj:`None`)
|
|
65
|
+
"""
|
|
66
|
+
super().__init__(timeout)
|
|
67
|
+
|
|
68
|
+
from google.cloud import aiplatform
|
|
69
|
+
|
|
70
|
+
self.project_id = project_id or os.getenv("GOOGLE_CLOUD_PROJECT")
|
|
71
|
+
self.location = location
|
|
72
|
+
self.model_id = model_id
|
|
73
|
+
self.output_storage_uri = output_storage_uri
|
|
74
|
+
|
|
75
|
+
if not self.project_id:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
"Project ID must be provided either as parameter or "
|
|
78
|
+
"GOOGLE_CLOUD_PROJECT environment variable"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
aiplatform.init(project=self.project_id, location=self.location)
|
|
82
|
+
self.client = aiplatform.gapic.PredictionServiceClient()
|
|
83
|
+
|
|
84
|
+
def generate_video_from_text(
|
|
85
|
+
self,
|
|
86
|
+
text_prompt: str,
|
|
87
|
+
response_count: int = 1,
|
|
88
|
+
duration: int = 5,
|
|
89
|
+
aspect_ratio: str = "16:9",
|
|
90
|
+
negative_prompt: Optional[str] = None,
|
|
91
|
+
person_generation: str = "allow_adult",
|
|
92
|
+
) -> Dict[str, Any]:
|
|
93
|
+
r"""Generate video from text prompt using Vertex AI Veo.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
text_prompt (str): The text prompt to guide video generation.
|
|
97
|
+
response_count (int): Number of videos to generate (1-4).
|
|
98
|
+
(default: :obj:`1`)
|
|
99
|
+
duration (int): Video duration in seconds (5-8).
|
|
100
|
+
(default: :obj:`5`)
|
|
101
|
+
aspect_ratio (str): Video aspect ratio. Options: "16:9", "9:16".
|
|
102
|
+
(default: :obj:`"16:9"`)
|
|
103
|
+
negative_prompt (Optional[str]): What to avoid in the video.
|
|
104
|
+
(default: :obj:`None`)
|
|
105
|
+
person_generation (str): Person safety setting. Options:
|
|
106
|
+
"allow_adult", "dont_allow". (default: :obj:`"allow_adult"`)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Dict[str, Any]:
|
|
110
|
+
A dictionary containing:
|
|
111
|
+
- 'success' (bool): Whether the operation was successful
|
|
112
|
+
- 'videos' (List[Dict]): List of generated video data
|
|
113
|
+
- 'metadata' (Dict): Additional metadata from the response
|
|
114
|
+
- 'error' (str): Error message if operation failed
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
from google.protobuf import json_format
|
|
118
|
+
from google.protobuf.struct_pb2 import Value
|
|
119
|
+
|
|
120
|
+
# Construct the request
|
|
121
|
+
endpoint = (
|
|
122
|
+
f"projects/{self.project_id}/locations/{self.location}/"
|
|
123
|
+
f"publishers/google/models/{self.model_id}"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Build parameters
|
|
127
|
+
parameters = {
|
|
128
|
+
"aspectRatio": aspect_ratio,
|
|
129
|
+
"personGeneration": person_generation,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if negative_prompt:
|
|
133
|
+
parameters["negativePrompt"] = negative_prompt
|
|
134
|
+
|
|
135
|
+
# Build request body
|
|
136
|
+
request_body = {
|
|
137
|
+
"contents": [
|
|
138
|
+
{"role": "user", "parts": [{"text": text_prompt}]}
|
|
139
|
+
],
|
|
140
|
+
"generationConfig": {
|
|
141
|
+
"responseCount": response_count,
|
|
142
|
+
"duration": duration,
|
|
143
|
+
},
|
|
144
|
+
"parameters": parameters,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if self.output_storage_uri:
|
|
148
|
+
generation_config = request_body.setdefault(
|
|
149
|
+
"generationConfig", {}
|
|
150
|
+
)
|
|
151
|
+
if isinstance(generation_config, dict):
|
|
152
|
+
generation_config["outputStorageUri"] = (
|
|
153
|
+
self.output_storage_uri
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Convert to protobuf format
|
|
157
|
+
request_value = json_format.ParseDict(request_body, Value())
|
|
158
|
+
|
|
159
|
+
# Make the API call
|
|
160
|
+
response = self.client.predict(
|
|
161
|
+
endpoint=endpoint,
|
|
162
|
+
instances=[request_value],
|
|
163
|
+
timeout=self.timeout,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return self._parse_video_response(response)
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"Error generating video from text: {e}")
|
|
170
|
+
return {"error": str(e)}
|
|
171
|
+
|
|
172
|
+
def generate_video_from_image(
|
|
173
|
+
self,
|
|
174
|
+
image_path: str,
|
|
175
|
+
text_prompt: str,
|
|
176
|
+
response_count: int = 1,
|
|
177
|
+
duration: int = 5,
|
|
178
|
+
aspect_ratio: str = "16:9",
|
|
179
|
+
negative_prompt: Optional[str] = None,
|
|
180
|
+
person_generation: str = "allow_adult",
|
|
181
|
+
) -> Dict[str, Any]:
|
|
182
|
+
r"""Generate video from image and text prompt using Vertex AI Veo.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
image_path (str): Path to the input image file (local or GCS URI).
|
|
186
|
+
text_prompt (str): The text prompt to guide video generation.
|
|
187
|
+
response_count (int): Number of videos to generate (1-4).
|
|
188
|
+
(default: :obj:`1`)
|
|
189
|
+
duration (int): Video duration in seconds (5-8).
|
|
190
|
+
(default: :obj:`5`)
|
|
191
|
+
aspect_ratio (str): Video aspect ratio. Options: "16:9", "9:16".
|
|
192
|
+
(default: :obj:`"16:9"`)
|
|
193
|
+
negative_prompt (Optional[str]): What to avoid in the video.
|
|
194
|
+
(default: :obj:`None`)
|
|
195
|
+
person_generation (str): Person safety setting.
|
|
196
|
+
(default: :obj:`"allow_adult"`)
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Dict[str, Any]:
|
|
200
|
+
A dictionary containing:
|
|
201
|
+
- 'success' (bool): Whether the operation was successful
|
|
202
|
+
- 'videos' (List[Dict]): List of generated video data
|
|
203
|
+
- 'metadata' (Dict): Additional metadata from the response
|
|
204
|
+
- 'error' (str): Error message if operation failed
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
from google.protobuf import json_format
|
|
208
|
+
from google.protobuf.struct_pb2 import Value
|
|
209
|
+
|
|
210
|
+
# Read and encode image
|
|
211
|
+
image_data, mime_type = self._process_image(image_path)
|
|
212
|
+
|
|
213
|
+
# Construct the request
|
|
214
|
+
endpoint = (
|
|
215
|
+
f"projects/{self.project_id}/locations/{self.location}/"
|
|
216
|
+
f"publishers/google/models/{self.model_id}"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Build parameters
|
|
220
|
+
parameters = {
|
|
221
|
+
"aspectRatio": aspect_ratio,
|
|
222
|
+
"personGeneration": person_generation,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if negative_prompt:
|
|
226
|
+
parameters["negativePrompt"] = negative_prompt
|
|
227
|
+
|
|
228
|
+
# Build request body with image
|
|
229
|
+
request_body = {
|
|
230
|
+
"contents": [
|
|
231
|
+
{
|
|
232
|
+
"role": "user",
|
|
233
|
+
"parts": [
|
|
234
|
+
{
|
|
235
|
+
"inlineData": {
|
|
236
|
+
"mimeType": mime_type,
|
|
237
|
+
"data": image_data,
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{"text": text_prompt},
|
|
241
|
+
],
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
"generationConfig": {
|
|
245
|
+
"responseCount": response_count,
|
|
246
|
+
"duration": duration,
|
|
247
|
+
},
|
|
248
|
+
"parameters": parameters,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if self.output_storage_uri:
|
|
252
|
+
generation_config = request_body.setdefault(
|
|
253
|
+
"generationConfig", {}
|
|
254
|
+
)
|
|
255
|
+
if isinstance(generation_config, dict):
|
|
256
|
+
generation_config["outputStorageUri"] = (
|
|
257
|
+
self.output_storage_uri
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Convert to protobuf format
|
|
261
|
+
request_value = json_format.ParseDict(request_body, Value())
|
|
262
|
+
|
|
263
|
+
# Make the API call
|
|
264
|
+
response = self.client.predict(
|
|
265
|
+
endpoint=endpoint,
|
|
266
|
+
instances=[request_value],
|
|
267
|
+
timeout=self.timeout,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
return self._parse_video_response(response)
|
|
271
|
+
|
|
272
|
+
except Exception as e:
|
|
273
|
+
logger.error(f"Error generating video from image: {e}")
|
|
274
|
+
return {"error": str(e)}
|
|
275
|
+
|
|
276
|
+
def extend_video(
|
|
277
|
+
self,
|
|
278
|
+
video_uri: str,
|
|
279
|
+
text_prompt: str,
|
|
280
|
+
duration: int = 5,
|
|
281
|
+
aspect_ratio: str = "16:9",
|
|
282
|
+
negative_prompt: Optional[str] = None,
|
|
283
|
+
) -> Dict[str, Any]:
|
|
284
|
+
r"""Extend an existing video using Vertex AI Veo.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
video_uri (str): Cloud Storage URI of the video to extend.
|
|
288
|
+
text_prompt (str): The text prompt to guide video extension.
|
|
289
|
+
duration (int): Duration to extend in seconds (5-8).
|
|
290
|
+
(default: :obj:`5`)
|
|
291
|
+
aspect_ratio (str): Video aspect ratio.
|
|
292
|
+
(default: :obj:`"16:9"`)
|
|
293
|
+
negative_prompt (Optional[str]): What to avoid in the extension.
|
|
294
|
+
(default: :obj:`None`)
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Dict[str, Any]:
|
|
298
|
+
A dictionary containing:
|
|
299
|
+
- 'success' (bool): Whether the operation was successful
|
|
300
|
+
- 'videos' (List[Dict]): List of extended video data
|
|
301
|
+
- 'metadata' (Dict): Additional metadata from the response
|
|
302
|
+
- 'error' (str): Error message if operation failed
|
|
303
|
+
"""
|
|
304
|
+
try:
|
|
305
|
+
from google.protobuf import json_format
|
|
306
|
+
from google.protobuf.struct_pb2 import Value
|
|
307
|
+
|
|
308
|
+
# Construct the request
|
|
309
|
+
endpoint = (
|
|
310
|
+
f"projects/{self.project_id}/locations/{self.location}/"
|
|
311
|
+
f"publishers/google/models/{self.model_id}"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Build parameters
|
|
315
|
+
parameters = {
|
|
316
|
+
"aspectRatio": aspect_ratio,
|
|
317
|
+
"videoToExtend": video_uri,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if negative_prompt:
|
|
321
|
+
parameters["negativePrompt"] = negative_prompt
|
|
322
|
+
|
|
323
|
+
# Build request body
|
|
324
|
+
request_body = {
|
|
325
|
+
"contents": [
|
|
326
|
+
{"role": "user", "parts": [{"text": text_prompt}]}
|
|
327
|
+
],
|
|
328
|
+
"generationConfig": {
|
|
329
|
+
"duration": duration,
|
|
330
|
+
},
|
|
331
|
+
"parameters": parameters,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if self.output_storage_uri:
|
|
335
|
+
generation_config = request_body.setdefault(
|
|
336
|
+
"generationConfig", {}
|
|
337
|
+
)
|
|
338
|
+
if isinstance(generation_config, dict):
|
|
339
|
+
generation_config["outputStorageUri"] = (
|
|
340
|
+
self.output_storage_uri
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Convert to protobuf format
|
|
344
|
+
request_value = json_format.ParseDict(request_body, Value())
|
|
345
|
+
|
|
346
|
+
# Make the API call
|
|
347
|
+
response = self.client.predict(
|
|
348
|
+
endpoint=endpoint,
|
|
349
|
+
instances=[request_value],
|
|
350
|
+
timeout=self.timeout,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
return self._parse_video_response(response)
|
|
354
|
+
|
|
355
|
+
except Exception as e:
|
|
356
|
+
logger.error(f"Error extending video: {e}")
|
|
357
|
+
return {"error": str(e)}
|
|
358
|
+
|
|
359
|
+
async def agenerate_video_from_text(
|
|
360
|
+
self,
|
|
361
|
+
text_prompt: str,
|
|
362
|
+
response_count: int = 1,
|
|
363
|
+
duration: int = 5,
|
|
364
|
+
aspect_ratio: str = "16:9",
|
|
365
|
+
negative_prompt: Optional[str] = None,
|
|
366
|
+
person_generation: str = "allow_adult",
|
|
367
|
+
) -> Dict[str, Any]:
|
|
368
|
+
r"""Generate video from text prompt using Vertex AI Veo.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
text_prompt (str): The text prompt to guide video generation.
|
|
372
|
+
response_count (int): Number of videos to generate (1-4).
|
|
373
|
+
(default: :obj:`1`)
|
|
374
|
+
duration (int): Video duration in seconds (5-8).
|
|
375
|
+
(default: :obj:`5`)
|
|
376
|
+
aspect_ratio (str): Video aspect ratio. Options: "16:9", "9:16".
|
|
377
|
+
(default: :obj:`"16:9"`)
|
|
378
|
+
negative_prompt (Optional[str]): What to avoid in the video.
|
|
379
|
+
(default: :obj:`None`)
|
|
380
|
+
person_generation (str): Person safety setting. Options:
|
|
381
|
+
"allow_adult", "dont_allow". (default: :obj:`"allow_adult"`)
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Dict[str, Any]:
|
|
385
|
+
A dictionary containing:
|
|
386
|
+
- 'success' (bool): Whether the operation was successful
|
|
387
|
+
- 'videos' (List[Dict]): List of generated video data
|
|
388
|
+
- 'metadata' (Dict): Additional metadata from the response
|
|
389
|
+
- 'error' (str): Error message if operation failed
|
|
390
|
+
"""
|
|
391
|
+
loop = asyncio.get_event_loop()
|
|
392
|
+
return await loop.run_in_executor(
|
|
393
|
+
None,
|
|
394
|
+
self.generate_video_from_text,
|
|
395
|
+
text_prompt,
|
|
396
|
+
response_count,
|
|
397
|
+
duration,
|
|
398
|
+
aspect_ratio,
|
|
399
|
+
negative_prompt,
|
|
400
|
+
person_generation,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
async def agenerate_video_from_image(
|
|
404
|
+
self,
|
|
405
|
+
image_path: str,
|
|
406
|
+
text_prompt: str,
|
|
407
|
+
response_count: int = 1,
|
|
408
|
+
duration: int = 5,
|
|
409
|
+
aspect_ratio: str = "16:9",
|
|
410
|
+
negative_prompt: Optional[str] = None,
|
|
411
|
+
person_generation: str = "allow_adult",
|
|
412
|
+
) -> Dict[str, Any]:
|
|
413
|
+
r"""Generate video from image and text prompt using Vertex AI Veo.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
image_path (str): Path to the input image file (local or GCS URI).
|
|
417
|
+
text_prompt (str): The text prompt to guide video generation.
|
|
418
|
+
response_count (int): Number of videos to generate (1-4).
|
|
419
|
+
(default: :obj:`1`)
|
|
420
|
+
duration (int): Video duration in seconds (5-8).
|
|
421
|
+
(default: :obj:`5`)
|
|
422
|
+
aspect_ratio (str): Video aspect ratio. Options: "16:9", "9:16".
|
|
423
|
+
(default: :obj:`"16:9"`)
|
|
424
|
+
negative_prompt (Optional[str]): What to avoid in the video.
|
|
425
|
+
(default: :obj:`None`)
|
|
426
|
+
person_generation (str): Person safety setting.
|
|
427
|
+
(default: :obj:`"allow_adult"`)
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
Dict[str, Any]:
|
|
431
|
+
A dictionary containing:
|
|
432
|
+
- 'success' (bool): Whether the operation was successful
|
|
433
|
+
- 'videos' (List[Dict]): List of generated video data
|
|
434
|
+
- 'metadata' (Dict): Additional metadata from the response
|
|
435
|
+
- 'error' (str): Error message if operation failed
|
|
436
|
+
"""
|
|
437
|
+
loop = asyncio.get_event_loop()
|
|
438
|
+
return await loop.run_in_executor(
|
|
439
|
+
None,
|
|
440
|
+
self.generate_video_from_image,
|
|
441
|
+
image_path,
|
|
442
|
+
text_prompt,
|
|
443
|
+
response_count,
|
|
444
|
+
duration,
|
|
445
|
+
aspect_ratio,
|
|
446
|
+
negative_prompt,
|
|
447
|
+
person_generation,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
async def aextend_video(
|
|
451
|
+
self,
|
|
452
|
+
video_uri: str,
|
|
453
|
+
text_prompt: str,
|
|
454
|
+
duration: int = 5,
|
|
455
|
+
aspect_ratio: str = "16:9",
|
|
456
|
+
negative_prompt: Optional[str] = None,
|
|
457
|
+
) -> Dict[str, Any]:
|
|
458
|
+
r"""Extend an existing video using Vertex AI Veo.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
video_uri (str): Cloud Storage URI of the video to extend.
|
|
462
|
+
text_prompt (str): The text prompt to guide video extension.
|
|
463
|
+
duration (int): Duration to extend in seconds (5-8).
|
|
464
|
+
(default: :obj:`5`)
|
|
465
|
+
aspect_ratio (str): Video aspect ratio.
|
|
466
|
+
(default: :obj:`"16:9"`)
|
|
467
|
+
negative_prompt (Optional[str]): What to avoid in the extension.
|
|
468
|
+
(default: :obj:`None`)
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
Dict[str, Any]:
|
|
472
|
+
A dictionary containing:
|
|
473
|
+
- 'success' (bool): Whether the operation was successful
|
|
474
|
+
- 'videos' (List[Dict]): List of extended video data
|
|
475
|
+
- 'metadata' (Dict): Additional metadata from the response
|
|
476
|
+
- 'error' (str): Error message if operation failed
|
|
477
|
+
"""
|
|
478
|
+
loop = asyncio.get_event_loop()
|
|
479
|
+
return await loop.run_in_executor(
|
|
480
|
+
None,
|
|
481
|
+
self.extend_video,
|
|
482
|
+
video_uri,
|
|
483
|
+
text_prompt,
|
|
484
|
+
duration,
|
|
485
|
+
aspect_ratio,
|
|
486
|
+
negative_prompt,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
def _process_image(self, image_path: str) -> Tuple[str, str]:
|
|
490
|
+
r"""Process image file and return base64 encoded data and MIME type."""
|
|
491
|
+
if image_path.startswith('gs://'):
|
|
492
|
+
# Handle Google Cloud Storage URIs
|
|
493
|
+
from google.cloud import storage # type: ignore[attr-defined]
|
|
494
|
+
|
|
495
|
+
# Parse GCS URI
|
|
496
|
+
parts = image_path[5:].split('/', 1)
|
|
497
|
+
bucket_name = parts[0]
|
|
498
|
+
blob_name = parts[1]
|
|
499
|
+
|
|
500
|
+
# Download image from GCS
|
|
501
|
+
client = storage.Client()
|
|
502
|
+
bucket = client.bucket(bucket_name)
|
|
503
|
+
blob = bucket.blob(blob_name)
|
|
504
|
+
image_bytes = blob.download_as_bytes()
|
|
505
|
+
|
|
506
|
+
# Determine MIME type from file extension
|
|
507
|
+
if blob_name.lower().endswith('.png'):
|
|
508
|
+
mime_type = "image/png"
|
|
509
|
+
elif blob_name.lower().endswith(('.jpg', '.jpeg')):
|
|
510
|
+
mime_type = "image/jpeg"
|
|
511
|
+
else:
|
|
512
|
+
raise ValueError("Unsupported image format. Use PNG or JPEG.")
|
|
513
|
+
|
|
514
|
+
else:
|
|
515
|
+
# Handle local file paths
|
|
516
|
+
with open(image_path, 'rb') as f:
|
|
517
|
+
image_bytes = f.read()
|
|
518
|
+
|
|
519
|
+
# Determine MIME type from file extension
|
|
520
|
+
if image_path.lower().endswith('.png'):
|
|
521
|
+
mime_type = "image/png"
|
|
522
|
+
elif image_path.lower().endswith(('.jpg', '.jpeg')):
|
|
523
|
+
mime_type = "image/jpeg"
|
|
524
|
+
else:
|
|
525
|
+
raise ValueError("Unsupported image format. Use PNG or JPEG.")
|
|
526
|
+
|
|
527
|
+
# Encode to base64
|
|
528
|
+
image_data = base64.b64encode(image_bytes).decode('utf-8')
|
|
529
|
+
|
|
530
|
+
return image_data, mime_type
|
|
531
|
+
|
|
532
|
+
def _parse_video_response(self, response: Any) -> Dict[str, Any]:
|
|
533
|
+
r"""Parse the video generation response."""
|
|
534
|
+
try:
|
|
535
|
+
result: Dict[str, Any] = {
|
|
536
|
+
"success": True,
|
|
537
|
+
"videos": [],
|
|
538
|
+
"metadata": {},
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
# Extract prediction results
|
|
542
|
+
if hasattr(response, 'predictions'):
|
|
543
|
+
for prediction in response.predictions:
|
|
544
|
+
# Convert prediction to dict if needed
|
|
545
|
+
if hasattr(prediction, 'struct_value'):
|
|
546
|
+
pred_dict = dict(prediction.struct_value)
|
|
547
|
+
else:
|
|
548
|
+
pred_dict = prediction
|
|
549
|
+
|
|
550
|
+
videos_list = result["videos"]
|
|
551
|
+
if isinstance(videos_list, list):
|
|
552
|
+
videos_list.append(pred_dict)
|
|
553
|
+
|
|
554
|
+
# Extract metadata if available
|
|
555
|
+
if hasattr(response, 'metadata'):
|
|
556
|
+
result["metadata"] = dict(response.metadata)
|
|
557
|
+
|
|
558
|
+
return result
|
|
559
|
+
|
|
560
|
+
except Exception as e:
|
|
561
|
+
logger.error(f"Error parsing video response: {e}")
|
|
562
|
+
return {
|
|
563
|
+
"success": False,
|
|
564
|
+
"error": str(e),
|
|
565
|
+
"raw_response": str(response),
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
def get_tools(self) -> List[FunctionTool]:
|
|
569
|
+
r"""Get a list of tools for video generation with Vertex AI Veo.
|
|
570
|
+
|
|
571
|
+
Returns:
|
|
572
|
+
List[FunctionTool]: List of available function tools.
|
|
573
|
+
"""
|
|
574
|
+
return [
|
|
575
|
+
FunctionTool(self.generate_video_from_text),
|
|
576
|
+
FunctionTool(self.generate_video_from_image),
|
|
577
|
+
FunctionTool(self.extend_video),
|
|
578
|
+
]
|
|
579
|
+
|
|
580
|
+
def get_async_tools(self) -> List[FunctionTool]:
|
|
581
|
+
r"""Get a list of async tools for video generation with Vertex AI Veo.
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
List[FunctionTool]: List of available async function tools.
|
|
585
|
+
"""
|
|
586
|
+
return [
|
|
587
|
+
FunctionTool(self.agenerate_video_from_text),
|
|
588
|
+
FunctionTool(self.agenerate_video_from_image),
|
|
589
|
+
FunctionTool(self.aextend_video),
|
|
590
|
+
]
|
|
@@ -195,18 +195,24 @@ class VideoAnalysisToolkit(BaseToolkit):
|
|
|
195
195
|
destroyed.
|
|
196
196
|
"""
|
|
197
197
|
# Clean up temporary files
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
198
|
+
if hasattr(self, '_temp_files'):
|
|
199
|
+
for temp_file in self._temp_files:
|
|
200
|
+
if os.path.exists(temp_file):
|
|
201
|
+
try:
|
|
202
|
+
os.remove(temp_file)
|
|
203
|
+
logger.debug(f"Removed temporary file: {temp_file}")
|
|
204
|
+
except OSError as e:
|
|
205
|
+
logger.warning(
|
|
206
|
+
f"Failed to remove temporary file {temp_file}: {e}"
|
|
207
|
+
)
|
|
207
208
|
|
|
208
209
|
# Clean up temporary directory if needed
|
|
209
|
-
if
|
|
210
|
+
if (
|
|
211
|
+
hasattr(self, '_cleanup')
|
|
212
|
+
and self._cleanup
|
|
213
|
+
and hasattr(self, '_working_directory')
|
|
214
|
+
and os.path.exists(self._working_directory)
|
|
215
|
+
):
|
|
210
216
|
try:
|
|
211
217
|
import sys
|
|
212
218
|
|
|
@@ -594,7 +600,7 @@ class VideoAnalysisToolkit(BaseToolkit):
|
|
|
594
600
|
msg = BaseMessage.make_user_message(
|
|
595
601
|
role_name="User",
|
|
596
602
|
content=prompt,
|
|
597
|
-
image_list=video_frames,
|
|
603
|
+
image_list=video_frames, # type: ignore[arg-type]
|
|
598
604
|
)
|
|
599
605
|
# Reset the agent to clear previous state
|
|
600
606
|
self.vl_agent.reset()
|