veadk-python 0.2.4__py3-none-any.whl → 0.2.6__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.

Potentially problematic release.


This version of veadk-python might be problematic. Click here for more details.

Files changed (64) hide show
  1. veadk/agent.py +40 -8
  2. veadk/cli/cli_deploy.py +5 -1
  3. veadk/cli/cli_init.py +25 -6
  4. veadk/cloud/cloud_app.py +21 -6
  5. veadk/consts.py +33 -1
  6. veadk/database/database_adapter.py +88 -0
  7. veadk/database/kv/redis_database.py +47 -0
  8. veadk/database/local_database.py +22 -4
  9. veadk/database/relational/mysql_database.py +58 -0
  10. veadk/database/vector/opensearch_vector_database.py +6 -3
  11. veadk/database/viking/viking_database.py +72 -3
  12. veadk/integrations/ve_cr/__init__.py +13 -0
  13. veadk/integrations/ve_cr/ve_cr.py +205 -0
  14. veadk/integrations/ve_faas/template/cookiecutter.json +2 -1
  15. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  16. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +28 -2
  17. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -1
  18. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +5 -2
  19. veadk/integrations/ve_faas/ve_faas.py +2 -0
  20. veadk/integrations/ve_faas/web_template/cookiecutter.json +17 -0
  21. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  22. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  23. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
  24. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +41 -0
  25. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
  26. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
  27. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
  28. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
  29. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
  30. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
  31. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
  32. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
  33. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
  34. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
  35. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
  36. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
  37. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
  38. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
  39. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
  40. veadk/integrations/ve_tos/ve_tos.py +238 -0
  41. veadk/knowledgebase/knowledgebase.py +8 -0
  42. veadk/runner.py +140 -34
  43. veadk/tools/builtin_tools/image_edit.py +236 -0
  44. veadk/tools/builtin_tools/image_generate.py +236 -0
  45. veadk/tools/builtin_tools/video_generate.py +326 -0
  46. veadk/tools/sandbox/browser_sandbox.py +19 -9
  47. veadk/tools/sandbox/code_sandbox.py +21 -11
  48. veadk/tools/sandbox/computer_sandbox.py +16 -9
  49. veadk/tracing/base_tracer.py +0 -19
  50. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +5 -0
  51. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +311 -128
  52. veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +20 -14
  53. veadk/tracing/telemetry/attributes/extractors/types.py +15 -4
  54. veadk/tracing/telemetry/exporters/inmemory_exporter.py +3 -0
  55. veadk/tracing/telemetry/opentelemetry_tracer.py +15 -6
  56. veadk/tracing/telemetry/telemetry.py +128 -24
  57. veadk/utils/misc.py +40 -0
  58. veadk/version.py +1 -1
  59. {veadk_python-0.2.4.dist-info → veadk_python-0.2.6.dist-info}/METADATA +1 -1
  60. {veadk_python-0.2.4.dist-info → veadk_python-0.2.6.dist-info}/RECORD +64 -37
  61. {veadk_python-0.2.4.dist-info → veadk_python-0.2.6.dist-info}/WHEEL +0 -0
  62. {veadk_python-0.2.4.dist-info → veadk_python-0.2.6.dist-info}/entry_points.txt +0 -0
  63. {veadk_python-0.2.4.dist-info → veadk_python-0.2.6.dist-info}/licenses/LICENSE +0 -0
  64. {veadk_python-0.2.4.dist-info → veadk_python-0.2.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,326 @@
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 typing import Dict
16
+ from google.adk.tools import ToolContext
17
+ from volcenginesdkarkruntime import Ark
18
+ from veadk.config import getenv
19
+ import time
20
+ import traceback
21
+ import json
22
+ from veadk.version import VERSION
23
+ from opentelemetry import trace
24
+ from opentelemetry.trace import Span
25
+
26
+ from veadk.utils.logger import get_logger
27
+
28
+ logger = get_logger(__name__)
29
+
30
+ client = Ark(
31
+ api_key=getenv("MODEL_VIDEO_API_KEY"),
32
+ base_url=getenv("MODEL_VIDEO_API_BASE"),
33
+ )
34
+
35
+
36
+ async def generate(prompt, first_frame_image=None, last_frame_image=None):
37
+ try:
38
+ if first_frame_image is None:
39
+ logger.debug("text generation")
40
+ response = client.content_generation.tasks.create(
41
+ model=getenv("MODEL_VIDEO_NAME"),
42
+ content=[
43
+ {"type": "text", "text": prompt},
44
+ ],
45
+ )
46
+ elif last_frame_image is None:
47
+ logger.debug("first frame generation")
48
+ response = client.content_generation.tasks.create(
49
+ model=getenv("MODEL_VIDEO_NAME"),
50
+ content=[
51
+ {"type": "text", "text": prompt},
52
+ {
53
+ "type": "image_url",
54
+ "image_url": {"url": first_frame_image},
55
+ },
56
+ ],
57
+ )
58
+ else:
59
+ logger.debug("last frame generation")
60
+ response = client.content_generation.tasks.create(
61
+ model=getenv("MODEL_VIDEO_NAME"),
62
+ content=[
63
+ {"type": "text", "text": prompt},
64
+ {
65
+ "type": "image_url",
66
+ "image_url": {"url": first_frame_image},
67
+ "role": "first_frame",
68
+ },
69
+ {
70
+ "type": "image_url",
71
+ "image_url": {"url": last_frame_image},
72
+ "role": "last_frame",
73
+ },
74
+ ],
75
+ )
76
+ except:
77
+ traceback.print_exc()
78
+ raise
79
+ return response
80
+
81
+
82
+ async def video_generate(params: list, tool_context: ToolContext) -> Dict:
83
+ """
84
+ Generate videos in **batch** from text prompts, optionally guided by a first/last frame,
85
+ and fine-tuned via *model text commands* (a.k.a. `parameters` appended to the prompt).
86
+
87
+ This API creates video-generation tasks. Each item in `params` describes a single video.
88
+ The function submits all items in one call and returns task metadata for tracking.
89
+
90
+ Args:
91
+ params (list[dict]):
92
+ A list of video generation requests. Each item supports the fields below.
93
+
94
+ Required per item:
95
+ - video_name (str):
96
+ Name/identifier of the output video file.
97
+
98
+ - prompt (str):
99
+ Text describing the video to generate. Supports zh/EN.
100
+ You may append **model text commands** after the prompt to control resolution,
101
+ aspect ratio, duration, fps, watermark, seed, camera lock, etc.
102
+ Format: `... --rs <resolution> --rt <ratio> --dur <seconds> --fps <fps> --wm <bool> --seed <int> --cf <bool>`
103
+ Example:
104
+ "小猫骑着滑板穿过公园。 --rs 720p --rt 16:9 --dur 5 --fps 24 --wm true --seed 11 --cf false"
105
+
106
+ Optional per item:
107
+ - first_frame (str | None):
108
+ URL or Base64 string (data URL) for the **first frame** (role = `first_frame`).
109
+ Use when you want the clip to start from a specific image.
110
+
111
+ - last_frame (str | None):
112
+ URL or Base64 string (data URL) for the **last frame** (role = `last_frame`).
113
+ Use when you want the clip to end on a specific image.
114
+
115
+ Notes on first/last frame:
116
+ * When both frames are provided, **match width/height** to avoid cropping; if they differ,
117
+ the tail frame may be auto-cropped to fit.
118
+ * If you only need one guided frame, provide either `first_frame` or `last_frame` (not both).
119
+
120
+ Image input constraints (for first/last frame):
121
+ - Formats: jpeg, png, webp, bmp, tiff, gif
122
+ - Aspect ratio (宽:高): 0.4–2.5
123
+ - Width/Height (px): 300–6000
124
+ - Size: < 30 MB
125
+ - Base64 data URL example: `data:image/png;base64,<BASE64>`
126
+
127
+ Model text commands (append after the prompt; unsupported keys are ignored by some models):
128
+ --rs / --resolution <value> Video resolution. Common values: 480p, 720p, 1080p.
129
+ Default depends on model (e.g., doubao-seedance-1-0-pro: 1080p,
130
+ some others default 720p).
131
+
132
+ --rt / --ratio <value> Aspect ratio. Typical: 16:9 (default), 9:16, 4:3, 3:4, 1:1, 2:1, 21:9.
133
+ Some models support `keep_ratio` (keep source image ratio) or `adaptive`
134
+ (auto choose suitable ratio).
135
+
136
+ --dur / --duration <seconds> Clip length in seconds. Seedance supports **3–12 s**;
137
+ Wan2.1 仅支持 5 s。Default varies by model.
138
+
139
+ --fps / --framespersecond <int> Frame rate. Common: 16 or 24 (model-dependent; e.g., seaweed=24, wan2.1=16).
140
+
141
+ --wm / --watermark <true|false> Whether to add watermark. Default: **false** (per doc).
142
+
143
+ --seed <int> Random seed in [-1, 2^32-1]. Default **-1** = auto seed.
144
+ Same seed may yield similar (not guaranteed identical) results across runs.
145
+
146
+ --cf / --camerafixed <true|false> Lock camera movement. Some models support this flag.
147
+ true: try to keep camera fixed; false: allow movement. Default: **false**.
148
+
149
+ Returns:
150
+ Dict:
151
+ API response containing task creation results for each input item. A typical shape is:
152
+ {
153
+ "status": "success",
154
+ "success_list": [{"video_name": "video_url"}],
155
+ "error_list": []
156
+ }
157
+
158
+ Constraints & Tips:
159
+ - Keep prompt concise and focused (建议 ≤ 500 字); too many details may distract the model.
160
+ - If using first/last frames, ensure their **aspect ratio matches** your chosen `--rt` to minimize cropping.
161
+ - If you must reproduce results, specify an explicit `--seed`.
162
+ - Unsupported parameters are ignored silently or may cause validation errors (model-specific).
163
+
164
+ Minimal examples:
165
+ 1) Text-only batch of two 5-second clips at 720p, 16:9, 24 fps:
166
+ params = [
167
+ {
168
+ "video_name": "cat_park.mp4",
169
+ "prompt": "小猫骑着滑板穿过公园。 --rs 720p --rt 16:9 --dur 5 --fps 24 --wm false"
170
+ },
171
+ {
172
+ "video_name": "city_night.mp4",
173
+ "prompt": "霓虹灯下的城市延时摄影风。 --rs 720p --rt 16:9 --dur 5 --fps 24 --seed 7"
174
+ },
175
+ ]
176
+
177
+ 2) With guided first/last frame (square, 6 s, camera fixed):
178
+ params = [
179
+ {
180
+ "video_name": "logo_reveal.mp4",
181
+ "first_frame": "https://cdn.example.com/brand/logo_start.png",
182
+ "last_frame": "https://cdn.example.com/brand/logo_end.png",
183
+ "prompt": "品牌 Logo 从线稿到上色的变化。 --rs 1080p --rt 1:1 --dur 6 --fps 24 --cf true"
184
+ }
185
+ ]
186
+ """
187
+ batch_size = 10
188
+ success_list = []
189
+ error_list = []
190
+ tracer = trace.get_tracer("gcp.vertex.agent")
191
+ with tracer.start_as_current_span("call_llm") as span:
192
+ input_part = {"role": "user"}
193
+ output_part = {"message.role": "model"}
194
+
195
+ for idx, item in enumerate(params):
196
+ input_part[f"parts.{idx}.type"] = "text"
197
+ input_part[f"parts.{idx}.text"] = json.dumps(item, ensure_ascii=False)
198
+
199
+ for start_idx in range(0, len(params), batch_size):
200
+ batch = params[start_idx : start_idx + batch_size]
201
+ task_dict = {}
202
+ for idx, item in enumerate(batch):
203
+ video_name = item["video_name"]
204
+ prompt = item["prompt"]
205
+ first_frame = item.get("first_frame", None)
206
+ last_frame = item.get("last_frame", None)
207
+ try:
208
+ if not first_frame:
209
+ response = await generate(prompt)
210
+ elif not last_frame:
211
+ response = await generate(prompt, first_frame)
212
+ else:
213
+ response = await generate(prompt, first_frame, last_frame)
214
+ task_dict[response.id] = video_name
215
+ except Exception as e:
216
+ logger.error(f"Error: {e}")
217
+ error_list.append(video_name)
218
+
219
+ total_tokens = 0
220
+ while True:
221
+ task_list = list(task_dict.keys())
222
+ if len(task_list) == 0:
223
+ break
224
+ for idx, task_id in enumerate(task_list):
225
+ result = client.content_generation.tasks.get(task_id=task_id)
226
+ status = result.status
227
+ if status == "succeeded":
228
+ logger.debug("----- task succeeded -----")
229
+ tool_context.state[f"{task_dict[task_id]}_video_url"] = (
230
+ result.content.video_url
231
+ )
232
+ total_tokens += result.usage.completion_tokens
233
+ output_part[f"message.parts.{idx}.type"] = "text"
234
+ output_part[f"message.parts.{idx}.text"] = (
235
+ f"{task_dict[task_id]}: {result.content.video_url}"
236
+ )
237
+ success_list.append(
238
+ {task_dict[task_id]: result.content.video_url}
239
+ )
240
+ task_dict.pop(task_id, None)
241
+ elif status == "failed":
242
+ logger.error("----- task failed -----")
243
+ logger.error(f"Error: {result.error}")
244
+ error_list.append(task_dict[task_id])
245
+ task_dict.pop(task_id, None)
246
+ else:
247
+ logger.debug(
248
+ f"Current status: {status}, Retrying after 10 seconds..."
249
+ )
250
+ time.sleep(10)
251
+
252
+ add_span_attributes(
253
+ span,
254
+ tool_context,
255
+ input_part=input_part,
256
+ output_part=output_part,
257
+ output_tokens=total_tokens,
258
+ total_tokens=total_tokens,
259
+ request_model=getenv("MODEL_VIDEO_NAME"),
260
+ response_model=getenv("MODEL_VIDEO_NAME"),
261
+ )
262
+
263
+ if len(success_list) == 0:
264
+ return {
265
+ "status": "error",
266
+ "success_list": success_list,
267
+ "error_list": error_list,
268
+ }
269
+ else:
270
+ return {
271
+ "status": "success",
272
+ "success_list": success_list,
273
+ "error_list": error_list,
274
+ }
275
+
276
+
277
+ def add_span_attributes(
278
+ span: Span,
279
+ tool_context: ToolContext,
280
+ input_part: dict = None,
281
+ output_part: dict = None,
282
+ input_tokens: int = None,
283
+ output_tokens: int = None,
284
+ total_tokens: int = None,
285
+ request_model: str = None,
286
+ response_model: str = None,
287
+ ):
288
+ try:
289
+ # common attributes
290
+ app_name = tool_context._invocation_context.app_name
291
+ user_id = tool_context._invocation_context.user_id
292
+ agent_name = tool_context.agent_name
293
+ session_id = tool_context._invocation_context.session.id
294
+ span.set_attribute("gen_ai.agent.name", agent_name)
295
+ span.set_attribute("openinference.instrumentation.veadk", VERSION)
296
+ span.set_attribute("gen_ai.app.name", app_name)
297
+ span.set_attribute("gen_ai.user.id", user_id)
298
+ span.set_attribute("gen_ai.session.id", session_id)
299
+ span.set_attribute("agent_name", agent_name)
300
+ span.set_attribute("agent.name", agent_name)
301
+ span.set_attribute("app_name", app_name)
302
+ span.set_attribute("app.name", app_name)
303
+ span.set_attribute("user.id", user_id)
304
+ span.set_attribute("session.id", session_id)
305
+ span.set_attribute("cozeloop.report.source", "veadk")
306
+
307
+ # llm attributes
308
+ span.set_attribute("gen_ai.system", "openai")
309
+ span.set_attribute("gen_ai.operation.name", "chat")
310
+ if request_model:
311
+ span.set_attribute("gen_ai.request.model", request_model)
312
+ if response_model:
313
+ span.set_attribute("gen_ai.response.model", response_model)
314
+ if total_tokens:
315
+ span.set_attribute("gen_ai.usage.total_tokens", total_tokens)
316
+ if output_tokens:
317
+ span.set_attribute("gen_ai.usage.output_tokens", output_tokens)
318
+ if input_tokens:
319
+ span.set_attribute("gen_ai.usage.input_tokens", input_tokens)
320
+ if input_part:
321
+ span.add_event("gen_ai.user.message", input_part)
322
+ if output_part:
323
+ span.add_event("gen_ai.choice", output_part)
324
+
325
+ except Exception:
326
+ traceback.print_exc()
@@ -12,16 +12,26 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- browser_sandbox = ...
15
+ from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
16
16
 
17
+ from veadk.config import getenv
18
+ from veadk.utils.mcp_utils import get_mcp_params
17
19
 
18
- def browser_use(prompt: str) -> str:
19
- """Using the remote browser sandbox to according to the prompt.
20
+ url = getenv("TOOL_BROWSER_SANDBOX_URL")
20
21
 
21
- Args:
22
- prompt (str): The prompt to be used.
23
22
 
24
- Returns:
25
- str: The response from the sandbox.
26
- """
27
- ...
23
+ browser_sandbox = MCPToolset(connection_params=get_mcp_params(url=url))
24
+
25
+ # browser_sandbox = ...
26
+
27
+
28
+ # def browser_use(prompt: str) -> str:
29
+ # """Using the remote browser sandbox to according to the prompt.
30
+
31
+ # Args:
32
+ # prompt (str): The prompt to be used.
33
+
34
+ # Returns:
35
+ # str: The response from the sandbox.
36
+ # """
37
+ # ...
@@ -12,19 +12,29 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- code_sandbox = ...
15
+ from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
16
16
 
17
+ from veadk.config import getenv
18
+ from veadk.utils.mcp_utils import get_mcp_params
17
19
 
18
- def code_execution(code: str, language: str) -> str:
19
- """Execute code in sandbox.
20
+ url = getenv("TOOL_CODE_SANDBOX_URL")
20
21
 
21
- Args:
22
- code (str): The code to be executed.
23
- language (str): The language of the code.
24
22
 
25
- Returns:
26
- str: The response from the sandbox.
27
- """
23
+ code_sandbox = MCPToolset(connection_params=get_mcp_params(url=url))
28
24
 
29
- res = code_sandbox(code, language)
30
- return res
25
+ # code_sandbox = ...
26
+
27
+
28
+ # def code_execution(code: str, language: str) -> str:
29
+ # """Execute code in sandbox.
30
+
31
+ # Args:
32
+ # code (str): The code to be executed.
33
+ # language (str): The language of the code.
34
+
35
+ # Returns:
36
+ # str: The response from the sandbox.
37
+ # """
38
+
39
+ # res = code_sandbox(code, language)
40
+ # return res
@@ -12,16 +12,23 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- computer_sandbox = ...
15
+ from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
16
16
 
17
+ from veadk.config import getenv
18
+ from veadk.utils.mcp_utils import get_mcp_params
17
19
 
18
- def computer_use(prompt: str) -> str:
19
- """Using the remote computer sandbox to according to the prompt.
20
+ url = getenv("TOOL_COMPUTER_SANDBOX_URL")
20
21
 
21
- Args:
22
- prompt (str): The prompt to be used.
23
22
 
24
- Returns:
25
- str: The response from the sandbox.
26
- """
27
- ...
23
+ computer_sandbox = MCPToolset(connection_params=get_mcp_params(url=url))
24
+
25
+ # def computer_use(prompt: str) -> str:
26
+ # """Using the remote computer sandbox to according to the prompt.
27
+
28
+ # Args:
29
+ # prompt (str): The prompt to be used.
30
+
31
+ # Returns:
32
+ # str: The response from the sandbox.
33
+ # """
34
+ # ...
@@ -19,25 +19,6 @@ from veadk.utils.logger import get_logger
19
19
  logger = get_logger(__name__)
20
20
 
21
21
 
22
- def replace_bytes_with_empty(data):
23
- """
24
- Recursively traverse the data structure and replace all bytes types with empty strings.
25
- Supports handling any nested structure of lists and dictionaries.
26
- """
27
- if isinstance(data, dict):
28
- # Handle dictionary: Recursively process each value
29
- return {k: replace_bytes_with_empty(v) for k, v in data.items()}
30
- elif isinstance(data, list):
31
- # Handle list: Recursively process each element
32
- return [replace_bytes_with_empty(item) for item in data]
33
- elif isinstance(data, bytes):
34
- # When encountering the bytes type, replace it with an empty string
35
- return "<image data>"
36
- else:
37
- # Keep other types unchanged
38
- return data
39
-
40
-
41
22
  class BaseTracer(ABC):
42
23
  def __init__(self, name: str):
43
24
  self.name = name
@@ -49,6 +49,10 @@ def common_cozeloop_report_source(**kwargs) -> str:
49
49
  return "veadk"
50
50
 
51
51
 
52
+ def common_cozeloop_call_type(**kwargs) -> str:
53
+ return kwargs.get("call_type")
54
+
55
+
52
56
  def llm_openinference_instrumentation_veadk(**kwargs) -> str:
53
57
  return VERSION
54
58
 
@@ -68,4 +72,5 @@ COMMON_ATTRIBUTES = {
68
72
  "user.id": common_gen_ai_user_id, # CozeLoop / TLS required
69
73
  "session.id": common_gen_ai_session_id, # CozeLoop / TLS required
70
74
  "cozeloop.report.source": common_cozeloop_report_source, # CozeLoop required
75
+ "cozeloop.call_type": common_cozeloop_call_type, # CozeLoop required
71
76
  }