veadk-python 0.2.27__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.
- veadk/__init__.py +37 -0
- veadk/a2a/__init__.py +13 -0
- veadk/a2a/agent_card.py +45 -0
- veadk/a2a/remote_ve_agent.py +390 -0
- veadk/a2a/utils/__init__.py +13 -0
- veadk/a2a/utils/agent_to_a2a.py +170 -0
- veadk/a2a/ve_a2a_server.py +93 -0
- veadk/a2a/ve_agent_executor.py +78 -0
- veadk/a2a/ve_middlewares.py +313 -0
- veadk/a2a/ve_task_store.py +37 -0
- veadk/agent.py +402 -0
- veadk/agent_builder.py +93 -0
- veadk/agents/loop_agent.py +68 -0
- veadk/agents/parallel_agent.py +72 -0
- veadk/agents/sequential_agent.py +64 -0
- veadk/auth/__init__.py +13 -0
- veadk/auth/base_auth.py +22 -0
- veadk/auth/ve_credential_service.py +203 -0
- veadk/auth/veauth/__init__.py +13 -0
- veadk/auth/veauth/apmplus_veauth.py +58 -0
- veadk/auth/veauth/ark_veauth.py +75 -0
- veadk/auth/veauth/base_veauth.py +50 -0
- veadk/auth/veauth/cozeloop_veauth.py +13 -0
- veadk/auth/veauth/opensearch_veauth.py +75 -0
- veadk/auth/veauth/postgresql_veauth.py +75 -0
- veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
- veadk/auth/veauth/speech_veauth.py +54 -0
- veadk/auth/veauth/utils.py +69 -0
- veadk/auth/veauth/vesearch_veauth.py +62 -0
- veadk/auth/veauth/viking_mem0_veauth.py +91 -0
- veadk/cli/__init__.py +13 -0
- veadk/cli/cli.py +58 -0
- veadk/cli/cli_clean.py +87 -0
- veadk/cli/cli_create.py +163 -0
- veadk/cli/cli_deploy.py +233 -0
- veadk/cli/cli_eval.py +215 -0
- veadk/cli/cli_init.py +214 -0
- veadk/cli/cli_kb.py +110 -0
- veadk/cli/cli_pipeline.py +285 -0
- veadk/cli/cli_prompt.py +86 -0
- veadk/cli/cli_update.py +106 -0
- veadk/cli/cli_uploadevalset.py +139 -0
- veadk/cli/cli_web.py +143 -0
- veadk/cloud/__init__.py +13 -0
- veadk/cloud/cloud_agent_engine.py +485 -0
- veadk/cloud/cloud_app.py +475 -0
- veadk/config.py +115 -0
- veadk/configs/__init__.py +13 -0
- veadk/configs/auth_configs.py +133 -0
- veadk/configs/database_configs.py +132 -0
- veadk/configs/model_configs.py +78 -0
- veadk/configs/tool_configs.py +54 -0
- veadk/configs/tracing_configs.py +110 -0
- veadk/consts.py +74 -0
- veadk/evaluation/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
- veadk/evaluation/base_evaluator.py +642 -0
- veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
- veadk/evaluation/eval_set_file_loader.py +48 -0
- veadk/evaluation/eval_set_recorder.py +146 -0
- veadk/evaluation/types.py +65 -0
- veadk/evaluation/utils/prometheus.py +196 -0
- veadk/integrations/__init__.py +13 -0
- veadk/integrations/ve_apig/__init__.py +13 -0
- veadk/integrations/ve_apig/ve_apig.py +349 -0
- veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
- veadk/integrations/ve_code_pipeline/__init__.py +13 -0
- veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
- veadk/integrations/ve_cozeloop/__init__.py +13 -0
- veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
- veadk/integrations/ve_cr/__init__.py +13 -0
- veadk/integrations/ve_cr/ve_cr.py +220 -0
- veadk/integrations/ve_faas/__init__.py +13 -0
- veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
- veadk/integrations/ve_faas/ve_faas.py +754 -0
- veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
- veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
- veadk/integrations/ve_identity/__init__.py +110 -0
- veadk/integrations/ve_identity/auth_config.py +261 -0
- veadk/integrations/ve_identity/auth_mixins.py +650 -0
- veadk/integrations/ve_identity/auth_processor.py +385 -0
- veadk/integrations/ve_identity/function_tool.py +158 -0
- veadk/integrations/ve_identity/identity_client.py +864 -0
- veadk/integrations/ve_identity/mcp_tool.py +181 -0
- veadk/integrations/ve_identity/mcp_toolset.py +431 -0
- veadk/integrations/ve_identity/models.py +228 -0
- veadk/integrations/ve_identity/token_manager.py +188 -0
- veadk/integrations/ve_identity/utils.py +151 -0
- veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
- veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
- veadk/integrations/ve_tls/__init__.py +13 -0
- veadk/integrations/ve_tls/utils.py +116 -0
- veadk/integrations/ve_tls/ve_tls.py +212 -0
- veadk/integrations/ve_tos/ve_tos.py +710 -0
- veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
- veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
- veadk/knowledgebase/__init__.py +17 -0
- veadk/knowledgebase/backends/__init__.py +13 -0
- veadk/knowledgebase/backends/base_backend.py +72 -0
- veadk/knowledgebase/backends/in_memory_backend.py +91 -0
- veadk/knowledgebase/backends/opensearch_backend.py +162 -0
- veadk/knowledgebase/backends/redis_backend.py +172 -0
- veadk/knowledgebase/backends/utils.py +92 -0
- veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
- veadk/knowledgebase/entry.py +25 -0
- veadk/knowledgebase/knowledgebase.py +307 -0
- veadk/memory/__init__.py +35 -0
- veadk/memory/long_term_memory.py +365 -0
- veadk/memory/long_term_memory_backends/__init__.py +13 -0
- veadk/memory/long_term_memory_backends/base_backend.py +35 -0
- veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
- veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
- veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
- veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
- veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
- veadk/memory/short_term_memory.py +252 -0
- veadk/memory/short_term_memory_backends/__init__.py +13 -0
- veadk/memory/short_term_memory_backends/base_backend.py +31 -0
- veadk/memory/short_term_memory_backends/mysql_backend.py +49 -0
- veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
- veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
- veadk/memory/short_term_memory_processor.py +100 -0
- veadk/processors/__init__.py +26 -0
- veadk/processors/base_run_processor.py +120 -0
- veadk/prompts/__init__.py +13 -0
- veadk/prompts/agent_default_prompt.py +30 -0
- veadk/prompts/prompt_evaluator.py +20 -0
- veadk/prompts/prompt_memory_processor.py +55 -0
- veadk/prompts/prompt_optimization.py +150 -0
- veadk/runner.py +732 -0
- veadk/tools/__init__.py +13 -0
- veadk/tools/builtin_tools/__init__.py +13 -0
- veadk/tools/builtin_tools/agent_authorization.py +94 -0
- veadk/tools/builtin_tools/generate_image.py +23 -0
- veadk/tools/builtin_tools/image_edit.py +300 -0
- veadk/tools/builtin_tools/image_generate.py +446 -0
- veadk/tools/builtin_tools/lark.py +67 -0
- veadk/tools/builtin_tools/las.py +24 -0
- veadk/tools/builtin_tools/link_reader.py +66 -0
- veadk/tools/builtin_tools/llm_shield.py +381 -0
- veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
- veadk/tools/builtin_tools/mcp_router.py +29 -0
- veadk/tools/builtin_tools/run_code.py +113 -0
- veadk/tools/builtin_tools/tts.py +253 -0
- veadk/tools/builtin_tools/vesearch.py +49 -0
- veadk/tools/builtin_tools/video_generate.py +363 -0
- veadk/tools/builtin_tools/web_scraper.py +76 -0
- veadk/tools/builtin_tools/web_search.py +83 -0
- veadk/tools/demo_tools.py +58 -0
- veadk/tools/load_knowledgebase_tool.py +149 -0
- veadk/tools/sandbox/__init__.py +13 -0
- veadk/tools/sandbox/browser_sandbox.py +37 -0
- veadk/tools/sandbox/code_sandbox.py +40 -0
- veadk/tools/sandbox/computer_sandbox.py +34 -0
- veadk/tracing/__init__.py +13 -0
- veadk/tracing/base_tracer.py +58 -0
- veadk/tracing/telemetry/__init__.py +13 -0
- veadk/tracing/telemetry/attributes/attributes.py +29 -0
- veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
- veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
- veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
- veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
- veadk/tracing/telemetry/exporters/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
- veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
- veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
- veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
- veadk/tracing/telemetry/telemetry.py +411 -0
- veadk/types.py +47 -0
- veadk/utils/__init__.py +13 -0
- veadk/utils/audio_manager.py +95 -0
- veadk/utils/auth.py +294 -0
- veadk/utils/logger.py +59 -0
- veadk/utils/mcp_utils.py +44 -0
- veadk/utils/misc.py +184 -0
- veadk/utils/patches.py +101 -0
- veadk/utils/volcengine_sign.py +205 -0
- veadk/version.py +15 -0
- veadk_python-0.2.27.dist-info/METADATA +373 -0
- veadk_python-0.2.27.dist-info/RECORD +218 -0
- veadk_python-0.2.27.dist-info/WHEEL +5 -0
- veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
- veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
- veadk_python-0.2.27.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import time
|
|
17
|
+
|
|
18
|
+
import requests
|
|
19
|
+
import volcenginesdkcore
|
|
20
|
+
import volcenginesdkvefaas
|
|
21
|
+
from volcenginesdkvefaas.models.env_for_create_function_input import (
|
|
22
|
+
EnvForCreateFunctionInput,
|
|
23
|
+
)
|
|
24
|
+
from volcenginesdkvefaas.models.tag_for_create_function_input import (
|
|
25
|
+
TagForCreateFunctionInput,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
import veadk.config
|
|
29
|
+
from veadk.integrations.ve_apig.ve_apig import APIGateway
|
|
30
|
+
from veadk.integrations.ve_faas.ve_faas_utils import (
|
|
31
|
+
signed_request,
|
|
32
|
+
zip_and_encode_folder,
|
|
33
|
+
)
|
|
34
|
+
from veadk.utils.logger import get_logger
|
|
35
|
+
from veadk.utils.misc import formatted_timestamp, getenv
|
|
36
|
+
from veadk.utils.volcengine_sign import ve_request
|
|
37
|
+
|
|
38
|
+
logger = get_logger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class VeFaaS:
|
|
42
|
+
def __init__(self, access_key: str, secret_key: str, region: str = "cn-beijing"):
|
|
43
|
+
self.ak = access_key
|
|
44
|
+
self.sk = secret_key
|
|
45
|
+
self.region = region
|
|
46
|
+
|
|
47
|
+
configuration = volcenginesdkcore.Configuration()
|
|
48
|
+
configuration.ak = self.ak
|
|
49
|
+
configuration.sk = self.sk
|
|
50
|
+
configuration.region = region
|
|
51
|
+
|
|
52
|
+
configuration.client_side_validation = True
|
|
53
|
+
volcenginesdkcore.Configuration.set_default(configuration)
|
|
54
|
+
|
|
55
|
+
self.client = volcenginesdkvefaas.VEFAASApi(
|
|
56
|
+
volcenginesdkcore.ApiClient(configuration)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
self.apig_client = APIGateway(self.ak, self.sk, self.region)
|
|
60
|
+
|
|
61
|
+
self.template_id = "6874f3360bdbc40008ecf8c7"
|
|
62
|
+
|
|
63
|
+
def _upload_and_mount_code(self, function_id: str, path: str):
|
|
64
|
+
"""Upload code to VeFaaS temp bucket and mount to function instance.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
function_id (str): Target function ID.
|
|
68
|
+
path (str): Local project path.
|
|
69
|
+
"""
|
|
70
|
+
# Get zipped code data
|
|
71
|
+
code_zip_data, code_zip_size, error = zip_and_encode_folder(path)
|
|
72
|
+
logger.info(
|
|
73
|
+
f"Zipped project size: {code_zip_size / 1024 / 1024:.2f} MB",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Upload code to VeFaaS temp bucket
|
|
77
|
+
req = volcenginesdkvefaas.GetCodeUploadAddressRequest(
|
|
78
|
+
function_id=function_id, content_length=code_zip_size
|
|
79
|
+
)
|
|
80
|
+
response = self.client.get_code_upload_address(req)
|
|
81
|
+
upload_url = response.upload_address
|
|
82
|
+
|
|
83
|
+
headers = {
|
|
84
|
+
"Content-Type": "application/zip",
|
|
85
|
+
}
|
|
86
|
+
response = requests.put(url=upload_url, data=code_zip_data, headers=headers)
|
|
87
|
+
if not (200 <= response.status_code < 300):
|
|
88
|
+
error_message = f"Upload failed to {upload_url} with status code {response.status_code}: {response.text}"
|
|
89
|
+
raise ValueError(error_message)
|
|
90
|
+
|
|
91
|
+
# Mount the TOS bucket to function instance
|
|
92
|
+
res = signed_request(
|
|
93
|
+
ak=self.ak,
|
|
94
|
+
sk=self.sk,
|
|
95
|
+
target="CodeUploadCallback",
|
|
96
|
+
body={"FunctionId": function_id},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return res
|
|
100
|
+
|
|
101
|
+
def _create_function(self, function_name: str, path: str):
|
|
102
|
+
# Read envs
|
|
103
|
+
envs = []
|
|
104
|
+
for key, value in veadk.config.veadk_environments.items():
|
|
105
|
+
envs.append(EnvForCreateFunctionInput(key=key, value=value))
|
|
106
|
+
logger.info(
|
|
107
|
+
f"Fetch {len(envs)} environment variables.",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Create function
|
|
111
|
+
res = self.client.create_function(
|
|
112
|
+
volcenginesdkvefaas.CreateFunctionRequest(
|
|
113
|
+
command="./run.sh",
|
|
114
|
+
name=function_name,
|
|
115
|
+
description="Created by VeADK (Volcengine Agent Development Kit)",
|
|
116
|
+
tags=[TagForCreateFunctionInput(key="provider", value="veadk")],
|
|
117
|
+
runtime="native-python3.12/v1",
|
|
118
|
+
request_timeout=1800,
|
|
119
|
+
envs=envs,
|
|
120
|
+
memory_mb=2048,
|
|
121
|
+
role=getenv("IAM_ROLE", None, allow_false_values=True),
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# avoid print secrets
|
|
126
|
+
logger.debug(
|
|
127
|
+
f"Function creation in {res.project_name} project with ID {res.id}"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
function_id = res.id
|
|
131
|
+
|
|
132
|
+
# Upload and mount code using extracted method
|
|
133
|
+
self._upload_and_mount_code(function_id, path)
|
|
134
|
+
|
|
135
|
+
return function_name, function_id
|
|
136
|
+
|
|
137
|
+
def _create_application(
|
|
138
|
+
self,
|
|
139
|
+
application_name: str,
|
|
140
|
+
function_name: str,
|
|
141
|
+
gateway_name: str,
|
|
142
|
+
upstream_name: str,
|
|
143
|
+
service_name: str,
|
|
144
|
+
enable_key_auth: bool = False,
|
|
145
|
+
):
|
|
146
|
+
response = ve_request(
|
|
147
|
+
request_body={
|
|
148
|
+
"Name": application_name,
|
|
149
|
+
"Services": [],
|
|
150
|
+
"IAM": [],
|
|
151
|
+
"Config": {
|
|
152
|
+
"Region": self.region,
|
|
153
|
+
"FunctionName": function_name,
|
|
154
|
+
"GatewayName": gateway_name,
|
|
155
|
+
"ServiceName": service_name,
|
|
156
|
+
"UpstreamName": upstream_name,
|
|
157
|
+
"EnableKeyAuth": enable_key_auth,
|
|
158
|
+
"EnableMcpSession": True,
|
|
159
|
+
},
|
|
160
|
+
"TemplateId": self.template_id,
|
|
161
|
+
},
|
|
162
|
+
action="CreateApplication",
|
|
163
|
+
ak=self.ak,
|
|
164
|
+
sk=self.sk,
|
|
165
|
+
service="vefaas",
|
|
166
|
+
version="2021-03-03",
|
|
167
|
+
region="cn-beijing",
|
|
168
|
+
host="open.volcengineapi.com",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
if response["Result"]["Status"] == "create_success":
|
|
173
|
+
return response["Result"]["Id"]
|
|
174
|
+
else:
|
|
175
|
+
raise ValueError(f"Create application failed: {response}")
|
|
176
|
+
except Exception as _:
|
|
177
|
+
raise ValueError(f"Create application failed: {response}")
|
|
178
|
+
|
|
179
|
+
def _release_application(self, app_id: str):
|
|
180
|
+
_ = ve_request(
|
|
181
|
+
request_body={"Id": app_id},
|
|
182
|
+
action="ReleaseApplication",
|
|
183
|
+
ak=self.ak,
|
|
184
|
+
sk=self.sk,
|
|
185
|
+
service="vefaas",
|
|
186
|
+
version="2021-03-03",
|
|
187
|
+
region="cn-beijing",
|
|
188
|
+
host="open.volcengineapi.com",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
status, full_response = self._get_application_status(app_id)
|
|
192
|
+
while status not in ["deploy_success", "deploy_fail"]:
|
|
193
|
+
time.sleep(10)
|
|
194
|
+
status, full_response = self._get_application_status(app_id)
|
|
195
|
+
|
|
196
|
+
if status == "deploy_success":
|
|
197
|
+
cloud_resource = full_response["Result"]["CloudResource"]
|
|
198
|
+
cloud_resource = json.loads(cloud_resource)
|
|
199
|
+
url = cloud_resource["framework"]["url"]["system_url"]
|
|
200
|
+
return url
|
|
201
|
+
else:
|
|
202
|
+
logger.error(
|
|
203
|
+
f"Release application failed. Application ID: {app_id}, Status: {status}"
|
|
204
|
+
)
|
|
205
|
+
import re
|
|
206
|
+
|
|
207
|
+
logs = "\n".join(self._get_application_logs(app_id=app_id))
|
|
208
|
+
log_text = re.sub(
|
|
209
|
+
r'([{"\']?(key|secret|token|pass|auth|credential|access|api|ak|sk|doubao|volces|coze)[^"\'\s]*["\']?\s*[:=]\s*)(["\']?)([^"\'\s]+)(["\']?)|([A-Za-z0-9+/=]{20,})',
|
|
210
|
+
lambda m: (
|
|
211
|
+
f"{m.group(1)}{m.group(3)}******{m.group(5)}"
|
|
212
|
+
if m.group(1)
|
|
213
|
+
else "******"
|
|
214
|
+
),
|
|
215
|
+
logs,
|
|
216
|
+
flags=re.IGNORECASE,
|
|
217
|
+
)
|
|
218
|
+
raise Exception(f"Release application failed. Logs:\n{log_text}")
|
|
219
|
+
|
|
220
|
+
def _get_application_status(self, app_id: str):
|
|
221
|
+
response = ve_request(
|
|
222
|
+
request_body={"Id": app_id},
|
|
223
|
+
action="GetApplication",
|
|
224
|
+
ak=self.ak,
|
|
225
|
+
sk=self.sk,
|
|
226
|
+
service="vefaas",
|
|
227
|
+
version="2021-03-03",
|
|
228
|
+
region="cn-beijing",
|
|
229
|
+
host="open.volcengineapi.com",
|
|
230
|
+
)
|
|
231
|
+
return response["Result"]["Status"], response
|
|
232
|
+
|
|
233
|
+
def _list_application(self, app_id: str = None, app_name: str = None):
|
|
234
|
+
# firt match app_id. if app_id is None,then match app_name and remove app_id
|
|
235
|
+
request_body = {
|
|
236
|
+
"OrderBy": {"Key": "CreateTime", "Ascend": False},
|
|
237
|
+
"FunctionId": app_id if app_id else None,
|
|
238
|
+
"Filters": (
|
|
239
|
+
[{"Item": {"Key": "Name", "Value": [app_name]}}]
|
|
240
|
+
if app_name and not app_id
|
|
241
|
+
else None
|
|
242
|
+
),
|
|
243
|
+
}
|
|
244
|
+
# remove None
|
|
245
|
+
request_body = {k: v for k, v in request_body.items() if v is not None}
|
|
246
|
+
|
|
247
|
+
page_size = 50
|
|
248
|
+
page_number = 1
|
|
249
|
+
all_items = []
|
|
250
|
+
total_page = None
|
|
251
|
+
while True:
|
|
252
|
+
try:
|
|
253
|
+
request_body.update({"PageNumber": page_number, "PageSize": page_size})
|
|
254
|
+
response = ve_request(
|
|
255
|
+
request_body=request_body,
|
|
256
|
+
action="ListApplications",
|
|
257
|
+
ak=self.ak,
|
|
258
|
+
sk=self.sk,
|
|
259
|
+
service="vefaas",
|
|
260
|
+
version="2021-03-03",
|
|
261
|
+
region="cn-beijing",
|
|
262
|
+
host="open.volcengineapi.com",
|
|
263
|
+
)
|
|
264
|
+
result = response.get("Result", {})
|
|
265
|
+
items = result.get("Items", [])
|
|
266
|
+
all_items.extend(items)
|
|
267
|
+
|
|
268
|
+
if total_page is None:
|
|
269
|
+
total = result.get("Total", 0)
|
|
270
|
+
total_page = (total + page_size - 1) // page_size
|
|
271
|
+
|
|
272
|
+
if page_number >= total_page or not items:
|
|
273
|
+
break
|
|
274
|
+
page_number += 1
|
|
275
|
+
except Exception as e:
|
|
276
|
+
raise ValueError(
|
|
277
|
+
f"List application failed. Error: {str(e)}. Response: {response}."
|
|
278
|
+
)
|
|
279
|
+
return all_items
|
|
280
|
+
|
|
281
|
+
def _update_function_code(
|
|
282
|
+
self,
|
|
283
|
+
application_name: str, # application name
|
|
284
|
+
path: str,
|
|
285
|
+
) -> tuple[str, str, str]:
|
|
286
|
+
"""Update existing application function code while preserving URL.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
application_name (str): Application name to update.
|
|
290
|
+
path (str): Local project path.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
tuple[str, str, str]: URL, app_id, function_id
|
|
294
|
+
"""
|
|
295
|
+
# Naming check
|
|
296
|
+
if "_" in application_name:
|
|
297
|
+
raise ValueError("Function or Application name cannot contain '_'.")
|
|
298
|
+
|
|
299
|
+
# Find existing application
|
|
300
|
+
app_id = self.find_app_id_by_name(application_name)
|
|
301
|
+
if not app_id:
|
|
302
|
+
raise ValueError(
|
|
303
|
+
f"Application '{application_name}' not found. Use deploy() for new applications."
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Get application status and extract function info
|
|
307
|
+
status, full_response = self._get_application_status(app_id)
|
|
308
|
+
|
|
309
|
+
# Extract function name from application config
|
|
310
|
+
cloud_resource = full_response["Result"]["CloudResource"]
|
|
311
|
+
cloud_resource = json.loads(cloud_resource)
|
|
312
|
+
function_name = cloud_resource["framework"]["function"]["Name"]
|
|
313
|
+
# existing_url = cloud_resource["framework"]["url"]["system_url"]
|
|
314
|
+
function_id = cloud_resource["framework"]["function"]["Id"]
|
|
315
|
+
if not function_id:
|
|
316
|
+
raise ValueError(f"Function '{function_name}' not found for update")
|
|
317
|
+
|
|
318
|
+
logger.info(
|
|
319
|
+
f"Start to update VeFaaS function {function_name} with path {path}."
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Upload and mount code using extracted method
|
|
323
|
+
self._upload_and_mount_code(function_id, path)
|
|
324
|
+
|
|
325
|
+
# Use update_function client method to apply changes
|
|
326
|
+
self.client.update_function(
|
|
327
|
+
volcenginesdkvefaas.UpdateFunctionRequest(
|
|
328
|
+
id=function_id,
|
|
329
|
+
request_timeout=1800, # Keep same timeout as deploy
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
logger.info(f"Function updated successfully: {function_id}")
|
|
334
|
+
|
|
335
|
+
logger.info(f"VeFaaS function {function_name} with ID {function_id} updated.")
|
|
336
|
+
|
|
337
|
+
# Release the application to apply changes
|
|
338
|
+
url = self._release_application(app_id)
|
|
339
|
+
|
|
340
|
+
logger.info(f"VeFaaS application {application_name} with ID {app_id} released.")
|
|
341
|
+
|
|
342
|
+
logger.info(
|
|
343
|
+
f"VeFaaS application {application_name} with ID {app_id} updated on {url}."
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
return url, app_id, function_id
|
|
347
|
+
|
|
348
|
+
def get_application_details(self, app_id: str = None, app_name: str = None):
|
|
349
|
+
if not app_id and not app_name:
|
|
350
|
+
raise ValueError("app_id and app_name cannot be both empty.")
|
|
351
|
+
apps = self._list_application(app_id=app_id, app_name=app_name)
|
|
352
|
+
if app_id:
|
|
353
|
+
for app in apps:
|
|
354
|
+
if app["Id"] == app_id:
|
|
355
|
+
return app
|
|
356
|
+
return None
|
|
357
|
+
else:
|
|
358
|
+
for app in apps:
|
|
359
|
+
if app["Name"] == app_name:
|
|
360
|
+
return app
|
|
361
|
+
|
|
362
|
+
def get_application_route(
|
|
363
|
+
self, app_id: str = None, app_name: str = None
|
|
364
|
+
) -> tuple[str, str, str] | None:
|
|
365
|
+
app = self.get_application_details(
|
|
366
|
+
app_id=app_id,
|
|
367
|
+
app_name=app_name,
|
|
368
|
+
)
|
|
369
|
+
if not app:
|
|
370
|
+
return None
|
|
371
|
+
|
|
372
|
+
cloud_resource = json.loads(app["CloudResource"])
|
|
373
|
+
gateway_id = cloud_resource["framework"]["triggers"][0]["DetailedConfig"][
|
|
374
|
+
"GatewayId"
|
|
375
|
+
]
|
|
376
|
+
service_id = cloud_resource["framework"]["triggers"][0]["Routes"][0][
|
|
377
|
+
"ServiceId"
|
|
378
|
+
]
|
|
379
|
+
route_id = cloud_resource["framework"]["triggers"][0]["Routes"][0]["Id"]
|
|
380
|
+
return gateway_id, service_id, route_id
|
|
381
|
+
|
|
382
|
+
def find_app_id_by_name(self, name: str):
|
|
383
|
+
apps = self._list_application(app_name=name)
|
|
384
|
+
for app in apps:
|
|
385
|
+
if app["Name"] == name:
|
|
386
|
+
return app["Id"]
|
|
387
|
+
logger.warning(f"Application with name {name} not found.")
|
|
388
|
+
return None
|
|
389
|
+
|
|
390
|
+
def delete(self, app_id: str):
|
|
391
|
+
try:
|
|
392
|
+
ve_request(
|
|
393
|
+
request_body={"Id": app_id},
|
|
394
|
+
action="DeleteApplication",
|
|
395
|
+
ak=self.ak,
|
|
396
|
+
sk=self.sk,
|
|
397
|
+
service="vefaas",
|
|
398
|
+
version="2021-03-03",
|
|
399
|
+
region="cn-beijing",
|
|
400
|
+
host="open.volcengineapi.com",
|
|
401
|
+
)
|
|
402
|
+
except Exception as e:
|
|
403
|
+
logger.error(f"Delete application failed. Response: {e}")
|
|
404
|
+
|
|
405
|
+
def deploy(
|
|
406
|
+
self,
|
|
407
|
+
name: str,
|
|
408
|
+
path: str,
|
|
409
|
+
gateway_name: str = "",
|
|
410
|
+
gateway_service_name: str = "",
|
|
411
|
+
gateway_upstream_name: str = "",
|
|
412
|
+
enable_key_auth: bool = False,
|
|
413
|
+
) -> tuple[str, str, str]:
|
|
414
|
+
"""Deploy an agent project to VeFaaS service.
|
|
415
|
+
|
|
416
|
+
Args:
|
|
417
|
+
name (str): Application name (warning: not function name).
|
|
418
|
+
path (str): Project path.
|
|
419
|
+
gateway_name (str, optional): Gateway name. Defaults to "".
|
|
420
|
+
gateway_service_name (str, optional): Gateway service name. Defaults to "".
|
|
421
|
+
gateway_upstream_name (str, optional): Gateway upstream name. Defaults to "".
|
|
422
|
+
enable_key_auth (bool, optional): Enable key auth. Defaults to False.
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
tuple[str, str, str]: (url, app_id, function_id)
|
|
426
|
+
"""
|
|
427
|
+
# Naming check
|
|
428
|
+
if "_" in name:
|
|
429
|
+
raise ValueError("Function or Application name cannot contain '_'.")
|
|
430
|
+
|
|
431
|
+
# Give default names
|
|
432
|
+
if not gateway_name:
|
|
433
|
+
gateway_name = f"{name}-gw-{formatted_timestamp()}"
|
|
434
|
+
|
|
435
|
+
existing_gateways = self.apig_client.list_gateways()
|
|
436
|
+
for gateway_instance in existing_gateways.items:
|
|
437
|
+
if (
|
|
438
|
+
gateway_instance.type == "serverless"
|
|
439
|
+
and gateway_instance.name != gateway_name
|
|
440
|
+
):
|
|
441
|
+
logger.warning(
|
|
442
|
+
f"You have at least one serverless gateway {gateway_instance.name}, but not {gateway_name}. Using {gateway_instance.name} instead."
|
|
443
|
+
)
|
|
444
|
+
gateway_name = gateway_instance.name
|
|
445
|
+
break
|
|
446
|
+
|
|
447
|
+
if not gateway_service_name:
|
|
448
|
+
gateway_service_name = f"{name}-gw-svr-{formatted_timestamp()}"
|
|
449
|
+
if not gateway_upstream_name:
|
|
450
|
+
gateway_upstream_name = f"{name}-gw-us-{formatted_timestamp()}"
|
|
451
|
+
|
|
452
|
+
function_name = f"{name}-fn"
|
|
453
|
+
|
|
454
|
+
logger.info(
|
|
455
|
+
f"Start to create VeFaaS function {function_name} with path {path}. Gateway: {gateway_name}, Gateway Service: {gateway_service_name}, Gateway Upstream: {gateway_upstream_name}."
|
|
456
|
+
)
|
|
457
|
+
function_name, function_id = self._create_function(function_name, path)
|
|
458
|
+
logger.info(f"VeFaaS function {function_name} with ID {function_id} created.")
|
|
459
|
+
|
|
460
|
+
logger.info(f"Start to create VeFaaS application {name}.")
|
|
461
|
+
app_id = self._create_application(
|
|
462
|
+
name,
|
|
463
|
+
function_name,
|
|
464
|
+
gateway_name,
|
|
465
|
+
gateway_upstream_name,
|
|
466
|
+
gateway_service_name,
|
|
467
|
+
enable_key_auth,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
logger.info(f"VeFaaS application {name} with ID {app_id} created.")
|
|
471
|
+
logger.info(f"Start to release VeFaaS application {app_id}.")
|
|
472
|
+
url = self._release_application(app_id)
|
|
473
|
+
logger.info(f"VeFaaS application {name} with ID {app_id} released.")
|
|
474
|
+
|
|
475
|
+
logger.info(f"VeFaaS application {name} with ID {app_id} deployed on {url}.")
|
|
476
|
+
|
|
477
|
+
return url, app_id, function_id
|
|
478
|
+
|
|
479
|
+
def _create_image_function(self, function_name: str, image: str):
|
|
480
|
+
"""Create function using container image instead of code upload."""
|
|
481
|
+
# Read environment variables from veadk configuration
|
|
482
|
+
envs = []
|
|
483
|
+
for key, value in veadk.config.veadk_environments.items():
|
|
484
|
+
envs.append(EnvForCreateFunctionInput(key=key, value=value))
|
|
485
|
+
logger.info(
|
|
486
|
+
f"Fetch {len(envs)} environment variables for image function.",
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
# Create function with container image source configuration
|
|
490
|
+
res = self.client.create_function(
|
|
491
|
+
volcenginesdkvefaas.CreateFunctionRequest(
|
|
492
|
+
command="bash ./run.sh", # Custom startup command
|
|
493
|
+
name=function_name,
|
|
494
|
+
description="Created by VeADK (Volcengine Agent Development Kit)",
|
|
495
|
+
tags=[TagForCreateFunctionInput(key="provider", value="veadk")],
|
|
496
|
+
runtime="native/v1", # Native runtime required for container images
|
|
497
|
+
source_type="image", # Set source type to container image
|
|
498
|
+
source=image, # Container image URL
|
|
499
|
+
request_timeout=1800, # Request timeout in seconds
|
|
500
|
+
envs=envs, # Environment variables from configuration
|
|
501
|
+
)
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# Log function creation success without exposing sensitive information
|
|
505
|
+
logger.debug(
|
|
506
|
+
f"Function creation in {res.project_name} project with ID {res.id}"
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
function_id = res.id
|
|
510
|
+
logger.info(
|
|
511
|
+
f"Function {function_name} created with image {image} and ID {function_id}"
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
return function_name, function_id
|
|
515
|
+
|
|
516
|
+
def query_user_cr_vpc_tunnel(
|
|
517
|
+
self, registry_name: str, max_attempts: int = 6
|
|
518
|
+
) -> bool:
|
|
519
|
+
"""Query and enable CR VPC tunnel for user registry access."""
|
|
520
|
+
logger.info(f"Setting up CR VPC tunnel for registry: {registry_name}")
|
|
521
|
+
waiting_times = 30
|
|
522
|
+
|
|
523
|
+
try:
|
|
524
|
+
for attempt in range(max_attempts):
|
|
525
|
+
# Check current status
|
|
526
|
+
logger.info(
|
|
527
|
+
f"Checking tunnel status (attempt {attempt + 1}/{max_attempts})"
|
|
528
|
+
)
|
|
529
|
+
query_resp = ve_request(
|
|
530
|
+
request_body={"Registry": registry_name},
|
|
531
|
+
action="QueryUserCrVpcTunnel",
|
|
532
|
+
ak=self.ak,
|
|
533
|
+
sk=self.sk,
|
|
534
|
+
service="vefaas",
|
|
535
|
+
version="2021-03-03",
|
|
536
|
+
region="cn-beijing",
|
|
537
|
+
host="open.volcengineapi.com",
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
current_status = query_resp.get("Result", {}).get("Ready", False)
|
|
541
|
+
logger.info(f"Current tunnel status: {current_status}")
|
|
542
|
+
|
|
543
|
+
# Always try to enable
|
|
544
|
+
logger.info("Enable VPC tunnel")
|
|
545
|
+
enable_resp = ve_request(
|
|
546
|
+
request_body={"Registry": registry_name},
|
|
547
|
+
action="EnableUserCrVpcTunnel",
|
|
548
|
+
ak=self.ak,
|
|
549
|
+
sk=self.sk,
|
|
550
|
+
service="vefaas",
|
|
551
|
+
version="2021-03-03",
|
|
552
|
+
region="cn-beijing",
|
|
553
|
+
host="open.volcengineapi.com",
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
# Handle EnableUserCrVpcTunnel response correctly
|
|
557
|
+
enable_result = enable_resp.get("Result", {})
|
|
558
|
+
enable_status = enable_result.get("Status", "")
|
|
559
|
+
enable_message = enable_result.get("Message", "")
|
|
560
|
+
|
|
561
|
+
if enable_status == "success":
|
|
562
|
+
logger.info("Enable tunnel succeeded")
|
|
563
|
+
elif enable_status == "failed":
|
|
564
|
+
logger.warning(f"Enable tunnel failed: {enable_message}")
|
|
565
|
+
else:
|
|
566
|
+
logger.warning(f"Enable tunnel unknown status: {enable_status}")
|
|
567
|
+
|
|
568
|
+
# Verify final status
|
|
569
|
+
logger.info("Verifying tunnel status")
|
|
570
|
+
verify_resp = ve_request(
|
|
571
|
+
request_body={"Registry": registry_name},
|
|
572
|
+
action="QueryUserCrVpcTunnel",
|
|
573
|
+
ak=self.ak,
|
|
574
|
+
sk=self.sk,
|
|
575
|
+
service="vefaas",
|
|
576
|
+
version="2021-03-03",
|
|
577
|
+
region="cn-beijing",
|
|
578
|
+
host="open.volcengineapi.com",
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
final_status = verify_resp.get("Result", {}).get("Ready", False)
|
|
582
|
+
logger.info(f"Final tunnel status: {final_status}")
|
|
583
|
+
|
|
584
|
+
if final_status:
|
|
585
|
+
logger.info(
|
|
586
|
+
f"CR VPC tunnel successfully enabled for {registry_name}"
|
|
587
|
+
)
|
|
588
|
+
return True
|
|
589
|
+
|
|
590
|
+
# If not ready and not last attempt, wait and retry
|
|
591
|
+
if attempt < max_attempts - 1:
|
|
592
|
+
logger.warning(
|
|
593
|
+
f"Tunnel not ready, waiting {waiting_times}s before retry"
|
|
594
|
+
)
|
|
595
|
+
time.sleep(waiting_times)
|
|
596
|
+
|
|
597
|
+
except Exception as e:
|
|
598
|
+
raise ValueError(f"Failed to setup CR VPC tunnel: {str(e)}")
|
|
599
|
+
|
|
600
|
+
return False
|
|
601
|
+
|
|
602
|
+
def _create_image_function(self, function_name: str, image: str):
|
|
603
|
+
"""Create function using container image instead of code upload."""
|
|
604
|
+
# Read environment variables from veadk configuration
|
|
605
|
+
envs = []
|
|
606
|
+
for key, value in veadk.config.veadk_environments.items():
|
|
607
|
+
envs.append(EnvForCreateFunctionInput(key=key, value=value))
|
|
608
|
+
logger.info(
|
|
609
|
+
f"Fetch {len(envs)} environment variables for image function.",
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
# Create function with container image source configuration
|
|
613
|
+
res = self.client.create_function(
|
|
614
|
+
volcenginesdkvefaas.CreateFunctionRequest(
|
|
615
|
+
command="bash ./run.sh", # Custom startup command
|
|
616
|
+
name=function_name,
|
|
617
|
+
description="Created by VeADK (Volcengine Agent Development Kit)",
|
|
618
|
+
tags=[TagForCreateFunctionInput(key="provider", value="veadk")],
|
|
619
|
+
runtime="native/v1", # Native runtime required for container images
|
|
620
|
+
source_type="image", # Set source type to container image
|
|
621
|
+
source=image, # Container image URL
|
|
622
|
+
request_timeout=1800, # Request timeout in seconds
|
|
623
|
+
envs=envs, # Environment variables from configuration
|
|
624
|
+
)
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
# Log function creation success without exposing sensitive information
|
|
628
|
+
logger.debug(
|
|
629
|
+
f"Function creation in {res.project_name} project with ID {res.id}"
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
function_id = res.id
|
|
633
|
+
logger.info(
|
|
634
|
+
f"Function {function_name} created with image {image} and ID {function_id}"
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
return function_name, function_id
|
|
638
|
+
|
|
639
|
+
def deploy_image(
|
|
640
|
+
self,
|
|
641
|
+
name: str,
|
|
642
|
+
image: str,
|
|
643
|
+
registry_name: str,
|
|
644
|
+
gateway_name: str = "",
|
|
645
|
+
gateway_service_name: str = "",
|
|
646
|
+
gateway_upstream_name: str = "",
|
|
647
|
+
) -> tuple[str, str, str]:
|
|
648
|
+
"""Deploy application using container image.
|
|
649
|
+
|
|
650
|
+
Args:
|
|
651
|
+
name (str): Application name.
|
|
652
|
+
image (str): Container image URL.
|
|
653
|
+
gateway_name (str, optional): Gateway name. Defaults to "".
|
|
654
|
+
gateway_service_name (str, optional): Gateway service name. Defaults to "".
|
|
655
|
+
gateway_upstream_name (str, optional): Gateway upstream name. Defaults to "".
|
|
656
|
+
|
|
657
|
+
Returns:
|
|
658
|
+
tuple[str, str, str]: (url, app_id, function_id)
|
|
659
|
+
"""
|
|
660
|
+
# Validate application name format
|
|
661
|
+
is_ready = self.query_user_cr_vpc_tunnel(registry_name)
|
|
662
|
+
if not is_ready:
|
|
663
|
+
raise ValueError("CR VPC tunnel is not ready")
|
|
664
|
+
|
|
665
|
+
if "_" in name:
|
|
666
|
+
raise ValueError("Function or Application name cannot contain '_'.")
|
|
667
|
+
|
|
668
|
+
# Generate default gateway names with timestamp if not provided
|
|
669
|
+
if not gateway_name:
|
|
670
|
+
gateway_name = f"{name}-gw-{formatted_timestamp()}"
|
|
671
|
+
|
|
672
|
+
# Check for existing serverless gateways to reuse
|
|
673
|
+
existing_gateways = self.apig_client.list_gateways()
|
|
674
|
+
for gateway_instance in existing_gateways.items:
|
|
675
|
+
if (
|
|
676
|
+
gateway_instance.type == "serverless"
|
|
677
|
+
and gateway_instance.name != gateway_name
|
|
678
|
+
):
|
|
679
|
+
logger.warning(
|
|
680
|
+
f"You have at least one serverless gateway {gateway_instance.name}, but not {gateway_name}. Using {gateway_instance.name} instead."
|
|
681
|
+
)
|
|
682
|
+
gateway_name = gateway_instance.name
|
|
683
|
+
break
|
|
684
|
+
|
|
685
|
+
# Set default gateway service and upstream names
|
|
686
|
+
if not gateway_service_name:
|
|
687
|
+
gateway_service_name = f"{name}-gw-svr-{formatted_timestamp()}"
|
|
688
|
+
if not gateway_upstream_name:
|
|
689
|
+
gateway_upstream_name = f"{name}-gw-us-{formatted_timestamp()}"
|
|
690
|
+
|
|
691
|
+
function_name = f"{name}-fn"
|
|
692
|
+
|
|
693
|
+
# Log deployment start with image information
|
|
694
|
+
logger.info(
|
|
695
|
+
f"Start to create VeFaaS function {function_name} with image {image}. Gateway: {gateway_name}, Gateway Service: {gateway_service_name}, Gateway Upstream: {gateway_upstream_name}."
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
# Create function using container image method
|
|
699
|
+
function_name, function_id = self._create_image_function(function_name, image)
|
|
700
|
+
logger.info(f"VeFaaS function {function_name} with ID {function_id} created.")
|
|
701
|
+
|
|
702
|
+
# Create application using existing application creation logic
|
|
703
|
+
logger.info(f"Start to create VeFaaS application {name}.")
|
|
704
|
+
app_id = self._create_application(
|
|
705
|
+
name,
|
|
706
|
+
function_name,
|
|
707
|
+
gateway_name,
|
|
708
|
+
gateway_upstream_name,
|
|
709
|
+
gateway_service_name,
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
# Release application and get deployment URL
|
|
713
|
+
logger.info(f"VeFaaS application {name} with ID {app_id} created.")
|
|
714
|
+
logger.info(f"Start to release VeFaaS application {app_id}.")
|
|
715
|
+
# Release application with retry
|
|
716
|
+
max_attempts = 5
|
|
717
|
+
attempt = 0
|
|
718
|
+
while True:
|
|
719
|
+
try:
|
|
720
|
+
url = self._release_application(app_id)
|
|
721
|
+
logger.info(f"VeFaaS application {name} with ID {app_id} released.")
|
|
722
|
+
break
|
|
723
|
+
except Exception:
|
|
724
|
+
attempt += 1
|
|
725
|
+
if attempt < max_attempts:
|
|
726
|
+
wait_time = 30 * attempt
|
|
727
|
+
logger.info(
|
|
728
|
+
f"Image sync still in progress. Waiting {wait_time} seconds before retry {attempt}/{max_attempts}."
|
|
729
|
+
)
|
|
730
|
+
time.sleep(wait_time)
|
|
731
|
+
else:
|
|
732
|
+
raise
|
|
733
|
+
|
|
734
|
+
logger.info(f"VeFaaS application {name} with ID {app_id} deployed on {url}.")
|
|
735
|
+
|
|
736
|
+
return url, app_id, function_id
|
|
737
|
+
|
|
738
|
+
def _get_application_logs(self, app_id: str) -> list[str]:
|
|
739
|
+
response = _ = ve_request(
|
|
740
|
+
request_body={"Id": app_id, "Limit": 99999, "RevisionNumber": 1},
|
|
741
|
+
action="GetApplicationRevisionLog",
|
|
742
|
+
ak=self.ak,
|
|
743
|
+
sk=self.sk,
|
|
744
|
+
service="vefaas",
|
|
745
|
+
version="2021-03-03",
|
|
746
|
+
region="cn-beijing",
|
|
747
|
+
host="open.volcengineapi.com",
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
try:
|
|
751
|
+
logs = response["Result"]["LogLines"]
|
|
752
|
+
return logs
|
|
753
|
+
except Exception as _:
|
|
754
|
+
raise ValueError(f"Get application log failed. Response: {response}")
|