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.
Files changed (218) hide show
  1. veadk/__init__.py +37 -0
  2. veadk/a2a/__init__.py +13 -0
  3. veadk/a2a/agent_card.py +45 -0
  4. veadk/a2a/remote_ve_agent.py +390 -0
  5. veadk/a2a/utils/__init__.py +13 -0
  6. veadk/a2a/utils/agent_to_a2a.py +170 -0
  7. veadk/a2a/ve_a2a_server.py +93 -0
  8. veadk/a2a/ve_agent_executor.py +78 -0
  9. veadk/a2a/ve_middlewares.py +313 -0
  10. veadk/a2a/ve_task_store.py +37 -0
  11. veadk/agent.py +402 -0
  12. veadk/agent_builder.py +93 -0
  13. veadk/agents/loop_agent.py +68 -0
  14. veadk/agents/parallel_agent.py +72 -0
  15. veadk/agents/sequential_agent.py +64 -0
  16. veadk/auth/__init__.py +13 -0
  17. veadk/auth/base_auth.py +22 -0
  18. veadk/auth/ve_credential_service.py +203 -0
  19. veadk/auth/veauth/__init__.py +13 -0
  20. veadk/auth/veauth/apmplus_veauth.py +58 -0
  21. veadk/auth/veauth/ark_veauth.py +75 -0
  22. veadk/auth/veauth/base_veauth.py +50 -0
  23. veadk/auth/veauth/cozeloop_veauth.py +13 -0
  24. veadk/auth/veauth/opensearch_veauth.py +75 -0
  25. veadk/auth/veauth/postgresql_veauth.py +75 -0
  26. veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
  27. veadk/auth/veauth/speech_veauth.py +54 -0
  28. veadk/auth/veauth/utils.py +69 -0
  29. veadk/auth/veauth/vesearch_veauth.py +62 -0
  30. veadk/auth/veauth/viking_mem0_veauth.py +91 -0
  31. veadk/cli/__init__.py +13 -0
  32. veadk/cli/cli.py +58 -0
  33. veadk/cli/cli_clean.py +87 -0
  34. veadk/cli/cli_create.py +163 -0
  35. veadk/cli/cli_deploy.py +233 -0
  36. veadk/cli/cli_eval.py +215 -0
  37. veadk/cli/cli_init.py +214 -0
  38. veadk/cli/cli_kb.py +110 -0
  39. veadk/cli/cli_pipeline.py +285 -0
  40. veadk/cli/cli_prompt.py +86 -0
  41. veadk/cli/cli_update.py +106 -0
  42. veadk/cli/cli_uploadevalset.py +139 -0
  43. veadk/cli/cli_web.py +143 -0
  44. veadk/cloud/__init__.py +13 -0
  45. veadk/cloud/cloud_agent_engine.py +485 -0
  46. veadk/cloud/cloud_app.py +475 -0
  47. veadk/config.py +115 -0
  48. veadk/configs/__init__.py +13 -0
  49. veadk/configs/auth_configs.py +133 -0
  50. veadk/configs/database_configs.py +132 -0
  51. veadk/configs/model_configs.py +78 -0
  52. veadk/configs/tool_configs.py +54 -0
  53. veadk/configs/tracing_configs.py +110 -0
  54. veadk/consts.py +74 -0
  55. veadk/evaluation/__init__.py +17 -0
  56. veadk/evaluation/adk_evaluator/__init__.py +17 -0
  57. veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
  58. veadk/evaluation/base_evaluator.py +642 -0
  59. veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
  60. veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
  61. veadk/evaluation/eval_set_file_loader.py +48 -0
  62. veadk/evaluation/eval_set_recorder.py +146 -0
  63. veadk/evaluation/types.py +65 -0
  64. veadk/evaluation/utils/prometheus.py +196 -0
  65. veadk/integrations/__init__.py +13 -0
  66. veadk/integrations/ve_apig/__init__.py +13 -0
  67. veadk/integrations/ve_apig/ve_apig.py +349 -0
  68. veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
  69. veadk/integrations/ve_code_pipeline/__init__.py +13 -0
  70. veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
  71. veadk/integrations/ve_cozeloop/__init__.py +13 -0
  72. veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
  73. veadk/integrations/ve_cr/__init__.py +13 -0
  74. veadk/integrations/ve_cr/ve_cr.py +220 -0
  75. veadk/integrations/ve_faas/__init__.py +13 -0
  76. veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
  77. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  78. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  79. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
  80. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
  81. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
  82. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
  83. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
  84. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
  85. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
  86. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
  87. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
  88. veadk/integrations/ve_faas/ve_faas.py +754 -0
  89. veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
  90. veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
  91. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  92. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  93. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
  94. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
  95. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
  96. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
  97. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
  98. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
  99. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
  100. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
  101. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
  102. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
  103. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
  104. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
  105. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
  106. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
  107. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
  108. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
  109. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
  110. veadk/integrations/ve_identity/__init__.py +110 -0
  111. veadk/integrations/ve_identity/auth_config.py +261 -0
  112. veadk/integrations/ve_identity/auth_mixins.py +650 -0
  113. veadk/integrations/ve_identity/auth_processor.py +385 -0
  114. veadk/integrations/ve_identity/function_tool.py +158 -0
  115. veadk/integrations/ve_identity/identity_client.py +864 -0
  116. veadk/integrations/ve_identity/mcp_tool.py +181 -0
  117. veadk/integrations/ve_identity/mcp_toolset.py +431 -0
  118. veadk/integrations/ve_identity/models.py +228 -0
  119. veadk/integrations/ve_identity/token_manager.py +188 -0
  120. veadk/integrations/ve_identity/utils.py +151 -0
  121. veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
  122. veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
  123. veadk/integrations/ve_tls/__init__.py +13 -0
  124. veadk/integrations/ve_tls/utils.py +116 -0
  125. veadk/integrations/ve_tls/ve_tls.py +212 -0
  126. veadk/integrations/ve_tos/ve_tos.py +710 -0
  127. veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
  128. veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
  129. veadk/knowledgebase/__init__.py +17 -0
  130. veadk/knowledgebase/backends/__init__.py +13 -0
  131. veadk/knowledgebase/backends/base_backend.py +72 -0
  132. veadk/knowledgebase/backends/in_memory_backend.py +91 -0
  133. veadk/knowledgebase/backends/opensearch_backend.py +162 -0
  134. veadk/knowledgebase/backends/redis_backend.py +172 -0
  135. veadk/knowledgebase/backends/utils.py +92 -0
  136. veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
  137. veadk/knowledgebase/entry.py +25 -0
  138. veadk/knowledgebase/knowledgebase.py +307 -0
  139. veadk/memory/__init__.py +35 -0
  140. veadk/memory/long_term_memory.py +365 -0
  141. veadk/memory/long_term_memory_backends/__init__.py +13 -0
  142. veadk/memory/long_term_memory_backends/base_backend.py +35 -0
  143. veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
  144. veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
  145. veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
  146. veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
  147. veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
  148. veadk/memory/short_term_memory.py +252 -0
  149. veadk/memory/short_term_memory_backends/__init__.py +13 -0
  150. veadk/memory/short_term_memory_backends/base_backend.py +31 -0
  151. veadk/memory/short_term_memory_backends/mysql_backend.py +49 -0
  152. veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
  153. veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
  154. veadk/memory/short_term_memory_processor.py +100 -0
  155. veadk/processors/__init__.py +26 -0
  156. veadk/processors/base_run_processor.py +120 -0
  157. veadk/prompts/__init__.py +13 -0
  158. veadk/prompts/agent_default_prompt.py +30 -0
  159. veadk/prompts/prompt_evaluator.py +20 -0
  160. veadk/prompts/prompt_memory_processor.py +55 -0
  161. veadk/prompts/prompt_optimization.py +150 -0
  162. veadk/runner.py +732 -0
  163. veadk/tools/__init__.py +13 -0
  164. veadk/tools/builtin_tools/__init__.py +13 -0
  165. veadk/tools/builtin_tools/agent_authorization.py +94 -0
  166. veadk/tools/builtin_tools/generate_image.py +23 -0
  167. veadk/tools/builtin_tools/image_edit.py +300 -0
  168. veadk/tools/builtin_tools/image_generate.py +446 -0
  169. veadk/tools/builtin_tools/lark.py +67 -0
  170. veadk/tools/builtin_tools/las.py +24 -0
  171. veadk/tools/builtin_tools/link_reader.py +66 -0
  172. veadk/tools/builtin_tools/llm_shield.py +381 -0
  173. veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
  174. veadk/tools/builtin_tools/mcp_router.py +29 -0
  175. veadk/tools/builtin_tools/run_code.py +113 -0
  176. veadk/tools/builtin_tools/tts.py +253 -0
  177. veadk/tools/builtin_tools/vesearch.py +49 -0
  178. veadk/tools/builtin_tools/video_generate.py +363 -0
  179. veadk/tools/builtin_tools/web_scraper.py +76 -0
  180. veadk/tools/builtin_tools/web_search.py +83 -0
  181. veadk/tools/demo_tools.py +58 -0
  182. veadk/tools/load_knowledgebase_tool.py +149 -0
  183. veadk/tools/sandbox/__init__.py +13 -0
  184. veadk/tools/sandbox/browser_sandbox.py +37 -0
  185. veadk/tools/sandbox/code_sandbox.py +40 -0
  186. veadk/tools/sandbox/computer_sandbox.py +34 -0
  187. veadk/tracing/__init__.py +13 -0
  188. veadk/tracing/base_tracer.py +58 -0
  189. veadk/tracing/telemetry/__init__.py +13 -0
  190. veadk/tracing/telemetry/attributes/attributes.py +29 -0
  191. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
  192. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
  193. veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
  194. veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
  195. veadk/tracing/telemetry/exporters/__init__.py +13 -0
  196. veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
  197. veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
  198. veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
  199. veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
  200. veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
  201. veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
  202. veadk/tracing/telemetry/telemetry.py +411 -0
  203. veadk/types.py +47 -0
  204. veadk/utils/__init__.py +13 -0
  205. veadk/utils/audio_manager.py +95 -0
  206. veadk/utils/auth.py +294 -0
  207. veadk/utils/logger.py +59 -0
  208. veadk/utils/mcp_utils.py +44 -0
  209. veadk/utils/misc.py +184 -0
  210. veadk/utils/patches.py +101 -0
  211. veadk/utils/volcengine_sign.py +205 -0
  212. veadk/version.py +15 -0
  213. veadk_python-0.2.27.dist-info/METADATA +373 -0
  214. veadk_python-0.2.27.dist-info/RECORD +218 -0
  215. veadk_python-0.2.27.dist-info/WHEEL +5 -0
  216. veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
  217. veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
  218. veadk_python-0.2.27.dist-info/top_level.txt +1 -0
@@ -0,0 +1,381 @@
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
+ import json
15
+ import os
16
+ import requests
17
+ from typing import Optional, List, Dict, Any, Union
18
+ from volcenginesdkllmshield.models.llm_shield_sign import request_sign
19
+
20
+ from google.adk.plugins import BasePlugin
21
+ from google.adk.agents.callback_context import CallbackContext
22
+ from google.adk.tools.tool_context import ToolContext
23
+ from google.adk.models import LlmRequest, LlmResponse
24
+ from google.genai import types
25
+ from google.adk.tools.base_tool import BaseTool
26
+
27
+ from veadk.config import getenv
28
+ from veadk.utils.logger import get_logger
29
+ from veadk.auth.veauth.utils import get_credential_from_vefaas_iam
30
+
31
+ logger = get_logger(__name__)
32
+
33
+
34
+ class LLMShieldPlugin(BasePlugin):
35
+ """
36
+ LLM Shield Plugin for content moderation and safety.
37
+
38
+ This plugin integrates with Volcano Engine's LLM Shield service to provide
39
+ real-time content moderation for user inputs, model outputs, and tool interactions.
40
+ It helps detect and block potentially harmful content including sensitive information,
41
+ prompt injection attacks, and policy violations.
42
+
43
+ Examples:
44
+ Basic usage with default settings:
45
+ ```python
46
+ from veadk.tools.builtin_tools.llm_shield import content_safety
47
+ agent = Agent(
48
+ before_model_callback=content_safety.before_model_callback,
49
+ after_model_callback=content_safety.after_model_callback,
50
+ before_tool_callback=content_safety.before_tool_callback,
51
+ after_tool_callback=content_safety.after_tool_callback,
52
+ )
53
+ ```
54
+ """
55
+
56
+ def __init__(self, region: str = "cn-beijing", timeout: int = 50) -> None:
57
+ """
58
+ Initialize the LLM Shield Plugin.
59
+
60
+ Args:
61
+ region (str, optional): The service region. Defaults to "cn-beijing".
62
+ timeout (int, optional): Request timeout in seconds. Defaults to 50.
63
+ """
64
+ self.name = "LLMShieldPlugin"
65
+ super().__init__(name=self.name)
66
+
67
+ self.appid = getenv("TOOL_LLM_SHIELD_APP_ID")
68
+ self.region = region
69
+ self.timeout = timeout
70
+
71
+ self.category_map = {
72
+ 101: "Model Misuse",
73
+ 103: "Sensitive Information",
74
+ 104: "Prompt Injection",
75
+ 106: "General Topic Control",
76
+ 107: "Computational Resource Consumption",
77
+ }
78
+
79
+ def _request_llm_shield(self, message: str, role: str) -> Optional[str]:
80
+ """
81
+ Make a request to the LLM Shield service for content moderation.
82
+
83
+ This method sends a message to the LLM Shield API for security analysis.
84
+ If the content is deemed risky, it returns a blocking message explaining
85
+ the violation. Otherwise, it returns None to allow the content through.
86
+
87
+ Args:
88
+ message (str): The content to be moderated
89
+ role (str): The role of the message sender ("user" or "assistant")
90
+
91
+ Returns:
92
+ Optional[str]: A blocking message if content violates policies,
93
+ None if content is safe or on error
94
+ """
95
+ if not self.appid:
96
+ logger.error("LLM Shield app ID not configured")
97
+ return None
98
+
99
+ ak = os.getenv("VOLCENGINE_ACCESS_KEY")
100
+ sk = os.getenv("VOLCENGINE_SECRET_KEY")
101
+ session_token = ""
102
+ if not (ak and sk):
103
+ logger.debug("Get AK/SK from environment variables failed.")
104
+ credential = get_credential_from_vefaas_iam()
105
+ ak = credential.access_key_id
106
+ sk = credential.secret_access_key
107
+ session_token = credential.session_token
108
+ else:
109
+ logger.debug("Successfully get AK/SK from environment variables.")
110
+
111
+ body = {
112
+ "Message": {
113
+ "Role": role,
114
+ "Content": message,
115
+ "ContentType": 1,
116
+ },
117
+ "Scene": self.appid,
118
+ }
119
+
120
+ body_json = json.dumps(body).encode("utf-8")
121
+
122
+ header = {"X-Security-Token": session_token}
123
+ url = f"https://{self.region}.sdk.access.llm-shield.omini-shield.com"
124
+ path = "/v2/moderate"
125
+ action = "Moderate"
126
+ version = "2025-08-31"
127
+
128
+ signed_header = request_sign(
129
+ header, ak, sk, self.region, url, path, action, body_json
130
+ )
131
+
132
+ signed_header.update(
133
+ {
134
+ "Content-Type": "application/json",
135
+ "X-Top-Service": "llmshield",
136
+ "X-Top-Region": self.region,
137
+ }
138
+ )
139
+
140
+ try:
141
+ response = requests.post(
142
+ url + path,
143
+ headers=signed_header,
144
+ data=body_json,
145
+ params={"Action": action, "Version": version},
146
+ timeout=self.timeout,
147
+ )
148
+
149
+ if response.status_code != 200:
150
+ logger.error(
151
+ f"LLM Shield HTTP error: {response.status_code} - {response.text}"
152
+ )
153
+ return None
154
+
155
+ response = response.json()
156
+ except requests.exceptions.Timeout:
157
+ logger.error("LLM Shield request timeout")
158
+ return None
159
+ except requests.exceptions.RequestException as e:
160
+ logger.error(f"LLM Shield network request failed: {e}")
161
+ return None
162
+ except json.JSONDecodeError as e:
163
+ logger.error(f"LLM Shield response JSON decode failed: {e}")
164
+ return None
165
+ except Exception as e:
166
+ logger.error(f"LLM Shield request failed: {e}")
167
+ return None
168
+
169
+ # Process risk detection results
170
+ result = response.get("Result", None)
171
+ if result:
172
+ decision = result.get("Decision", None)
173
+ decision_type = decision.get("DecisionType", None)
174
+ risk_info = result.get("RiskInfo", None)
175
+ if decision_type is not None and int(decision_type) == 2 and risk_info:
176
+ risks = risk_info.get("Risks", [])
177
+ if risks:
178
+ # Extract risk categories for user-friendly error message
179
+ risk_reasons = set()
180
+ for risk in risks:
181
+ category = risk.get("Category", None)
182
+ if category:
183
+ category_name = self.category_map.get(
184
+ int(category), f"Category {category}"
185
+ )
186
+ risk_reasons.add(category_name)
187
+
188
+ # Generate blocking response
189
+ reason_text = (
190
+ ", ".join(risk_reasons)
191
+ if risk_reasons
192
+ else "security policy violation"
193
+ )
194
+ response_text = (
195
+ f"Your request has been blocked due to: {reason_text}. "
196
+ f"Please modify your input and try again."
197
+ )
198
+
199
+ return response_text
200
+
201
+ return None
202
+
203
+ def before_agent_callback(
204
+ self, callback_context: CallbackContext, **kwargs
205
+ ) -> None:
206
+ # TODO: Implement agent-level input validation and context analysis
207
+ return None
208
+
209
+ def after_agent_callback(self, callback_context: CallbackContext, **kwargs) -> None:
210
+ # TODO: Implement post-agent analysis and context analysis
211
+ return None
212
+
213
+ def before_model_callback(
214
+ self, callback_context: CallbackContext, llm_request: LlmRequest, **kwargs
215
+ ) -> Optional[LlmResponse]:
216
+ """
217
+ Moderate user input before sending to the language model.
218
+
219
+ Extracts the last user message from the LLM request and checks it
220
+ against the LLM Shield service. If the content violates safety policies,
221
+ returns a blocking response instead of allowing the request to proceed.
222
+
223
+ Args:
224
+ callback_context (CallbackContext): The callback execution context
225
+ llm_request (LlmRequest): The incoming LLM request to moderate
226
+ **kwargs: Additional keyword arguments
227
+
228
+ Returns:
229
+ Optional[LlmResponse]: A blocking response if content is unsafe,
230
+ None if content is safe to proceed
231
+ """
232
+ # Extract the last user message for moderation
233
+ last_user_message = None
234
+ contents = getattr(llm_request, "contents", [])
235
+
236
+ if contents:
237
+ last_content = contents[-1]
238
+ last_role = getattr(last_content, "role", "")
239
+ last_parts = getattr(last_content, "parts", [])
240
+
241
+ if last_role == "user" and last_parts:
242
+ last_user_message = getattr(last_parts[0], "text", "")
243
+
244
+ # Skip moderation if message is empty
245
+ if not last_user_message:
246
+ return None
247
+
248
+ response = self._request_llm_shield(message=last_user_message, role="user")
249
+ if response:
250
+ logger.debug("LLM Shield triggered in before_model_callback.")
251
+ return LlmResponse(
252
+ content=types.Content(
253
+ role="model",
254
+ parts=[types.Part(text=response)],
255
+ )
256
+ )
257
+ return None
258
+
259
+ def after_model_callback(
260
+ self, callback_context: CallbackContext, llm_response: LlmResponse, **kwargs
261
+ ) -> Optional[LlmResponse]:
262
+ """
263
+ Moderate model output before returning to the user.
264
+
265
+ Extracts the model's response and checks it against the LLM Shield service.
266
+ If the model's output violates safety policies, returns a blocking response
267
+ instead of the original model output.
268
+
269
+ Args:
270
+ callback_context (CallbackContext): The callback execution context
271
+ llm_response (LlmResponse): The model's response to moderate
272
+ **kwargs: Additional keyword arguments
273
+
274
+ Returns:
275
+ Optional[LlmResponse]: A blocking response if content is unsafe,
276
+ None if content is safe to return
277
+ """
278
+ # Extract the model's response for moderation
279
+ last_model_message = None
280
+ content = getattr(llm_response, "content", [])
281
+
282
+ if content:
283
+ last_role = getattr(content, "role", "")
284
+ last_parts = getattr(content, "parts", [])
285
+
286
+ if last_role == "model" and last_parts:
287
+ last_model_message = getattr(last_parts[0], "text", "")
288
+
289
+ # Skip moderation if message is empty
290
+ if not last_model_message:
291
+ return None
292
+
293
+ response = self._request_llm_shield(
294
+ message=last_model_message, role="assistant"
295
+ )
296
+ if response:
297
+ logger.debug("LLM Shield triggered in after_model_callback.")
298
+ return LlmResponse(
299
+ content=types.Content(
300
+ role="model",
301
+ parts=[types.Part(text=response)],
302
+ )
303
+ )
304
+ return None
305
+
306
+ def before_tool_callback(
307
+ self, tool: BaseTool, args: Dict[str, Any], tool_context: ToolContext, **kwargs
308
+ ) -> Optional[Dict]:
309
+ """
310
+ Moderate tool arguments before tool execution.
311
+
312
+ Combines all tool arguments into a message and checks it against
313
+ the LLM Shield service. If the arguments contain unsafe content,
314
+ returns a blocking result instead of allowing tool execution.
315
+
316
+ Args:
317
+ tool (BaseTool): The tool to be executed
318
+ args (Dict[str, Any]): The arguments passed to the tool
319
+ tool_context (ToolContext): The tool execution context
320
+ **kwargs: Additional keyword arguments
321
+
322
+ Returns:
323
+ Optional[Dict]: A blocking result if arguments are unsafe,
324
+ None if arguments are safe to proceed
325
+ """
326
+ args_list = []
327
+
328
+ for key, value in args.items():
329
+ args_list.append(f"{key}: {value}")
330
+
331
+ message = "\n".join(args_list)
332
+ response = self._request_llm_shield(message=message, role="user")
333
+ if response:
334
+ logger.debug("LLM Shield triggered in before_tool_callback.")
335
+ return {"result": response}
336
+ return None
337
+
338
+ def after_tool_callback(
339
+ self,
340
+ tool: BaseTool,
341
+ args: Dict[str, Any],
342
+ tool_context: CallbackContext,
343
+ tool_response: Union[str, Dict[str, Any], List[Any]],
344
+ **kwargs,
345
+ ) -> Optional[Dict]:
346
+ """
347
+ Moderate tool output after tool execution.
348
+
349
+ Processes the tool's response (string, dict, or list) into a message
350
+ and checks it against the LLM Shield service. If the tool's output
351
+ violates safety policies, returns a blocking result.
352
+
353
+ Args:
354
+ tool (BaseTool): The tool that was executed
355
+ args (Dict[str, Any]): The arguments that were passed to the tool
356
+ tool_context (CallbackContext): The tool execution context
357
+ tool_response (Union[str, Dict[str, Any], List[Any]]): The tool's response
358
+ **kwargs: Additional keyword arguments
359
+
360
+ Returns:
361
+ Optional[Dict]: A blocking result if tool output is unsafe,
362
+ None if output is safe to return
363
+ """
364
+ message = ""
365
+ if isinstance(tool_response, str):
366
+ message = tool_response
367
+ elif isinstance(tool_response, dict):
368
+ for key, value in tool_response.items():
369
+ message += f"{value}\n"
370
+ elif isinstance(tool_response, list):
371
+ for item in tool_response:
372
+ message += f"{item}\n"
373
+
374
+ response = self._request_llm_shield(message=message, role="assistant")
375
+ if response:
376
+ logger.debug("LLM Shield triggered in after_tool_callback.")
377
+ return {"result": response}
378
+ return None
379
+
380
+
381
+ content_safety = LLMShieldPlugin()
@@ -0,0 +1,97 @@
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
+ from __future__ import annotations
16
+
17
+ from google.adk.models.llm_request import LlmRequest
18
+ from google.adk.tools.function_tool import FunctionTool
19
+ from google.adk.tools.tool_context import ToolContext
20
+ from google.genai import types
21
+ from pydantic import BaseModel, Field
22
+ from typing_extensions import override
23
+
24
+ from veadk.knowledgebase import KnowledgeBase
25
+ from veadk.knowledgebase.entry import KnowledgebaseEntry
26
+ from veadk.utils.logger import get_logger
27
+
28
+ logger = get_logger(__name__)
29
+
30
+
31
+ class LoadKnowledgebaseResponse(BaseModel):
32
+ knowledges: list[KnowledgebaseEntry] = Field(default_factory=list)
33
+
34
+
35
+ class LoadKnowledgebaseTool(FunctionTool):
36
+ """A tool that loads the common knowledgebase"""
37
+
38
+ def __init__(self, knowledgebase: KnowledgeBase):
39
+ super().__init__(self.load_knowledgebase)
40
+
41
+ self.knowledgebase = knowledgebase
42
+
43
+ if not self.custom_metadata:
44
+ self.custom_metadata = {}
45
+ self.custom_metadata["backend"] = knowledgebase.backend
46
+
47
+ @override
48
+ def _get_declaration(self) -> types.FunctionDeclaration | None:
49
+ return types.FunctionDeclaration(
50
+ name=self.name,
51
+ description=self.description,
52
+ parameters=types.Schema(
53
+ type=types.Type.OBJECT,
54
+ properties={
55
+ "query": types.Schema(
56
+ type=types.Type.STRING,
57
+ )
58
+ },
59
+ required=["query"],
60
+ ),
61
+ )
62
+
63
+ @override
64
+ async def process_llm_request(
65
+ self,
66
+ *,
67
+ tool_context: ToolContext,
68
+ llm_request: LlmRequest,
69
+ ) -> None:
70
+ await super().process_llm_request(
71
+ tool_context=tool_context, llm_request=llm_request
72
+ )
73
+ # Tell the model about the knowledgebase.
74
+ llm_request.append_instructions(
75
+ [
76
+ f"""
77
+ You have a knowledgebase (knowledegebase name is `{self.knowledgebase.name}`, knowledgebase description is `{self.knowledgebase.description}`). You can use it to answer questions. If any questions need
78
+ you to look up the knowledgebase, you should call load_knowledgebase function with a query.
79
+ """
80
+ ]
81
+ )
82
+
83
+ async def load_knowledgebase(
84
+ self, query: str, tool_context: ToolContext
85
+ ) -> LoadKnowledgebaseResponse:
86
+ """Loads the knowledgebase for the user.
87
+
88
+ Args:
89
+ query: The query to load the knowledgebase for.
90
+
91
+ Returns:
92
+ A list of knowledgebase results.
93
+ """
94
+ logger.info(f"Search knowledgebase: {self.knowledgebase.name}")
95
+ response = self.knowledgebase.search(query)
96
+ logger.info(f"Loaded {len(response)} knowledgebase entries for query: {query}")
97
+ return LoadKnowledgebaseResponse(knowledges=response)
@@ -0,0 +1,29 @@
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
+ from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
16
+
17
+ from veadk.config import getenv
18
+ from google.adk.tools.mcp_tool.mcp_session_manager import (
19
+ StreamableHTTPConnectionParams,
20
+ )
21
+
22
+ url = getenv("TOOL_MCP_ROUTER_URL")
23
+ api_key = getenv("TOOL_MCP_ROUTER_API_KEY")
24
+
25
+ mcp_router = MCPToolset(
26
+ connection_params=StreamableHTTPConnectionParams(
27
+ url=url, headers={"Authorization": f"Bearer {api_key}"}
28
+ ),
29
+ )
@@ -0,0 +1,113 @@
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 os
17
+
18
+ from google.adk.tools import ToolContext
19
+
20
+ from veadk.config import getenv
21
+ from veadk.utils.logger import get_logger
22
+ from veadk.utils.volcengine_sign import ve_request
23
+ from veadk.auth.veauth.utils import get_credential_from_vefaas_iam
24
+
25
+ logger = get_logger(__name__)
26
+
27
+
28
+ def run_code(
29
+ code: str, language: str, tool_context: ToolContext, timeout: int = 30
30
+ ) -> str:
31
+ """Run code in a code sandbox and return the output.
32
+ For C++ code, don't execute it directly, compile and execute via Python; write sources and object files to /tmp.
33
+
34
+ Args:
35
+ code (str): The code to run.
36
+ language (str): The programming language of the code. Language must be one of the supported languages: python3.
37
+ timeout (int, optional): The timeout in seconds for the code execution. Defaults to 30.
38
+
39
+ Returns:
40
+ str: The output of the code execution.
41
+ """
42
+
43
+ tool_id = getenv("AGENTKIT_TOOL_ID")
44
+
45
+ service = getenv(
46
+ "AGENTKIT_TOOL_SERVICE_CODE", "agentkit"
47
+ ) # temporary service for code run tool
48
+ region = getenv("AGENTKIT_TOOL_REGION", "cn-beijing")
49
+ host = getenv(
50
+ "AGENTKIT_TOOL_HOST", service + "." + region + ".volces.com"
51
+ ) # temporary host for code run tool
52
+ logger.debug(f"tools endpoint: {host}")
53
+
54
+ session_id = tool_context._invocation_context.session.id
55
+ agent_name = tool_context._invocation_context.agent.name
56
+ user_id = tool_context._invocation_context.user_id
57
+ tool_user_session_id = agent_name + "_" + user_id + "_" + session_id
58
+ logger.debug(f"tool_user_session_id: {tool_user_session_id}")
59
+
60
+ logger.debug(
61
+ f"Running code in language: {language}, session_id={session_id}, code={code}, tool_id={tool_id}, host={host}, service={service}, region={region}, timeout={timeout}"
62
+ )
63
+
64
+ ak = tool_context.state.get("VOLCENGINE_ACCESS_KEY")
65
+ sk = tool_context.state.get("VOLCENGINE_SECRET_KEY")
66
+ header = {}
67
+
68
+ if not (ak and sk):
69
+ logger.debug("Get AK/SK from tool context failed.")
70
+ ak = os.getenv("VOLCENGINE_ACCESS_KEY")
71
+ sk = os.getenv("VOLCENGINE_SECRET_KEY")
72
+ if not (ak and sk):
73
+ logger.debug(
74
+ "Get AK/SK from environment variables failed. Try to use credential from Iam."
75
+ )
76
+ credential = get_credential_from_vefaas_iam()
77
+ ak = credential.access_key_id
78
+ sk = credential.secret_access_key
79
+ header = {"X-Security-Token": credential.session_token}
80
+ else:
81
+ logger.debug("Successfully get AK/SK from environment variables.")
82
+ else:
83
+ logger.debug("Successfully get AK/SK from tool context.")
84
+
85
+ res = ve_request(
86
+ request_body={
87
+ "ToolId": tool_id,
88
+ "UserSessionId": tool_user_session_id,
89
+ "OperationType": "RunCode",
90
+ "OperationPayload": json.dumps(
91
+ {
92
+ "code": code,
93
+ "timeout": timeout,
94
+ "kernel_name": language,
95
+ }
96
+ ),
97
+ },
98
+ action="InvokeTool",
99
+ ak=ak,
100
+ sk=sk,
101
+ service=service,
102
+ version="2025-10-30",
103
+ region=region,
104
+ host=host,
105
+ header=header,
106
+ )
107
+ logger.debug(f"Invoke run code response: {res}")
108
+
109
+ try:
110
+ return res["Result"]["Result"]
111
+ except KeyError as e:
112
+ logger.error(f"Error occurred while running code: {e}, response is {res}")
113
+ return res