veadk-python 0.2.5__py3-none-any.whl → 0.2.7__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 (94) hide show
  1. veadk/agent.py +29 -22
  2. veadk/agent_builder.py +94 -0
  3. veadk/auth/__init__.py +13 -0
  4. veadk/auth/base_auth.py +22 -0
  5. veadk/auth/veauth/__init__.py +13 -0
  6. veadk/auth/veauth/apmplus_veauth.py +65 -0
  7. veadk/auth/veauth/ark_veauth.py +77 -0
  8. veadk/auth/veauth/base_veauth.py +50 -0
  9. veadk/auth/veauth/cozeloop_veauth.py +13 -0
  10. veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
  11. veadk/auth/veauth/vesearch_veauth.py +62 -0
  12. veadk/cli/cli.py +2 -0
  13. veadk/cli/cli_deploy.py +5 -2
  14. veadk/cli/cli_init.py +25 -6
  15. veadk/cli/cli_pipeline.py +220 -0
  16. veadk/cli/cli_prompt.py +4 -4
  17. veadk/config.py +45 -81
  18. veadk/configs/__init__.py +13 -0
  19. veadk/configs/database_configs.py +83 -0
  20. veadk/configs/model_configs.py +42 -0
  21. veadk/configs/tool_configs.py +42 -0
  22. veadk/configs/tracing_configs.py +110 -0
  23. veadk/consts.py +32 -1
  24. veadk/database/database_adapter.py +256 -3
  25. veadk/database/kv/redis_database.py +47 -0
  26. veadk/database/local_database.py +23 -4
  27. veadk/database/relational/mysql_database.py +58 -0
  28. veadk/database/vector/opensearch_vector_database.py +6 -3
  29. veadk/database/viking/viking_database.py +272 -36
  30. veadk/integrations/ve_code_pipeline/__init__.py +13 -0
  31. veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
  32. veadk/integrations/ve_cozeloop/__init__.py +13 -0
  33. veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
  34. veadk/integrations/ve_cr/__init__.py +13 -0
  35. veadk/integrations/ve_cr/ve_cr.py +220 -0
  36. veadk/integrations/ve_faas/template/cookiecutter.json +3 -2
  37. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +2 -2
  38. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +1 -1
  39. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +24 -1
  40. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -1
  41. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +1 -12
  42. veadk/integrations/ve_faas/ve_faas.py +352 -35
  43. veadk/integrations/ve_faas/web_template/cookiecutter.json +17 -0
  44. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  45. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  46. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
  47. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +41 -0
  48. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
  49. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
  50. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
  51. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
  52. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
  53. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
  54. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
  55. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
  56. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
  57. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
  58. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
  59. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
  60. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
  61. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
  62. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
  63. veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +6 -3
  64. veadk/integrations/ve_tls/__init__.py +13 -0
  65. veadk/integrations/ve_tls/utils.py +117 -0
  66. veadk/integrations/ve_tls/ve_tls.py +208 -0
  67. veadk/integrations/ve_tos/ve_tos.py +128 -73
  68. veadk/knowledgebase/knowledgebase.py +116 -20
  69. veadk/memory/long_term_memory.py +20 -21
  70. veadk/memory/short_term_memory_processor.py +9 -4
  71. veadk/runner.py +213 -223
  72. veadk/tools/builtin_tools/vesearch.py +2 -2
  73. veadk/tools/builtin_tools/video_generate.py +27 -20
  74. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +5 -0
  75. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +253 -129
  76. veadk/tracing/telemetry/attributes/extractors/types.py +15 -4
  77. veadk/tracing/telemetry/exporters/apmplus_exporter.py +158 -12
  78. veadk/tracing/telemetry/exporters/cozeloop_exporter.py +4 -9
  79. veadk/tracing/telemetry/exporters/tls_exporter.py +4 -10
  80. veadk/tracing/telemetry/opentelemetry_tracer.py +11 -5
  81. veadk/tracing/telemetry/telemetry.py +23 -5
  82. veadk/utils/logger.py +1 -1
  83. veadk/utils/misc.py +48 -0
  84. veadk/utils/volcengine_sign.py +6 -2
  85. veadk/version.py +1 -1
  86. {veadk_python-0.2.5.dist-info → veadk_python-0.2.7.dist-info}/METADATA +2 -1
  87. veadk_python-0.2.7.dist-info/RECORD +172 -0
  88. veadk_python-0.2.5.dist-info/RECORD +0 -127
  89. /veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{{ cookiecutter.app_name|replace('-', '_') }} → {{ cookiecutter.app_name }}}/__init__.py +0 -0
  90. /veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{{ cookiecutter.app_name|replace('-', '_') }} → {{ cookiecutter.app_name }}}/agent.py +0 -0
  91. {veadk_python-0.2.5.dist-info → veadk_python-0.2.7.dist-info}/WHEEL +0 -0
  92. {veadk_python-0.2.5.dist-info → veadk_python-0.2.7.dist-info}/entry_points.txt +0 -0
  93. {veadk_python-0.2.5.dist-info → veadk_python-0.2.7.dist-info}/licenses/LICENSE +0 -0
  94. {veadk_python-0.2.5.dist-info → veadk_python-0.2.7.dist-info}/top_level.txt +0 -0
@@ -12,72 +12,125 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import os
16
- from veadk.config import getenv
17
- from veadk.utils.logger import get_logger
18
- import tos
19
15
  import asyncio
20
- from typing import Union
21
- from pydantic import BaseModel, Field
22
- from typing import Any
23
- from urllib.parse import urlparse
16
+ import os
24
17
  from datetime import datetime
18
+ from typing import TYPE_CHECKING, Union
19
+ from urllib.parse import urlparse
20
+
21
+ from veadk.consts import DEFAULT_TOS_BUCKET_NAME
22
+ from veadk.utils.logger import get_logger
23
+
24
+ if TYPE_CHECKING:
25
+ pass
26
+
25
27
 
28
+ # Initialize logger before using it
26
29
  logger = get_logger(__name__)
27
30
 
28
31
 
29
- class TOSConfig(BaseModel):
30
- region: str = Field(
31
- default_factory=lambda: getenv("DATABASE_TOS_REGION"),
32
- description="TOS region",
33
- )
34
- ak: str = Field(
35
- default_factory=lambda: getenv("VOLCENGINE_ACCESS_KEY"),
36
- description="Volcengine access key",
37
- )
38
- sk: str = Field(
39
- default_factory=lambda: getenv("VOLCENGINE_SECRET_KEY"),
40
- description="Volcengine secret key",
41
- )
42
- bucket_name: str = Field(
43
- default_factory=lambda: getenv("DATABASE_TOS_BUCKET"),
44
- description="TOS bucket name",
45
- )
46
-
47
-
48
- class VeTOS(BaseModel):
49
- config: TOSConfig = Field(default_factory=TOSConfig)
50
-
51
- def model_post_init(self, __context: Any) -> None:
32
+ class VeTOS:
33
+ def __init__(
34
+ self,
35
+ ak: str = "",
36
+ sk: str = "",
37
+ region: str = "cn-beijing",
38
+ bucket_name: str = DEFAULT_TOS_BUCKET_NAME,
39
+ ) -> None:
40
+ self.ak = ak if ak else os.getenv("VOLCENGINE_ACCESS_KEY", "")
41
+ self.sk = sk if sk else os.getenv("VOLCENGINE_SECRET_KEY", "")
42
+ self.region = region
43
+ self.bucket_name = bucket_name
44
+ self._tos_module = None
45
+
52
46
  try:
53
- self._client = tos.TosClientV2(
54
- self.config.ak,
55
- self.config.sk,
56
- endpoint=f"tos-{self.config.region}.volces.com",
57
- region=self.config.region,
47
+ import tos
48
+
49
+ self._tos_module = tos
50
+ except ImportError as e:
51
+ logger.error(
52
+ "Failed to import 'tos' module. Please install it using: pip install tos\n"
58
53
  )
59
- logger.info("Connected to TOS successfully.")
54
+ raise ImportError(
55
+ "Missing 'tos' module. Please install it using: pip install tos\n"
56
+ ) from e
57
+
58
+ self._client = None
59
+ try:
60
+ self._client = self._tos_module.TosClientV2(
61
+ ak=self.ak,
62
+ sk=self.sk,
63
+ endpoint=f"tos-{self.region}.volces.com",
64
+ region=self.region,
65
+ )
66
+ logger.info("Init TOS client.")
60
67
  except Exception as e:
61
68
  logger.error(f"Client initialization failed:{e}")
62
- return None
69
+
70
+ def _refresh_client(self):
71
+ try:
72
+ if self._client:
73
+ self._client.close()
74
+ self._client = self._tos_module.TosClientV2(
75
+ self.ak,
76
+ self.sk,
77
+ endpoint=f"tos-{self.region}.volces.com",
78
+ region=self.region,
79
+ )
80
+ logger.info("refreshed client successfully.")
81
+ except Exception as e:
82
+ logger.error(f"Failed to refresh client: {str(e)}")
83
+ self._client = None
63
84
 
64
85
  def create_bucket(self) -> bool:
65
- """If the bucket does not exist, create it"""
86
+ """If the bucket does not exist, create it and set CORS rules"""
87
+ if not self._client:
88
+ logger.error("TOS client is not initialized")
89
+ return False
66
90
  try:
67
- self._client.head_bucket(self.config.bucket_name)
68
- logger.info(f"Bucket {self.config.bucket_name} already exists")
69
- return True
70
- except tos.exceptions.TosServerError as e:
91
+ self._client.head_bucket(self.bucket_name)
92
+ logger.info(f"Bucket {self.bucket_name} already exists")
93
+ except self._tos_module.exceptions.TosServerError as e:
71
94
  if e.status_code == 404:
72
- self._client.create_bucket(
73
- bucket=self.config.bucket_name,
74
- storage_class=tos.StorageClassType.Storage_Class_Standard,
75
- acl=tos.ACLType.ACL_Private,
76
- )
77
- logger.info(f"Bucket {self.config.bucket_name} created successfully")
78
- return True
95
+ try:
96
+ self._client.create_bucket(
97
+ bucket=self.bucket_name,
98
+ storage_class=self._tos_module.StorageClassType.Storage_Class_Standard,
99
+ acl=self._tos_module.ACLType.ACL_Public_Read,
100
+ )
101
+ logger.info(f"Bucket {self.bucket_name} created successfully")
102
+ self._refresh_client()
103
+ except Exception as create_error:
104
+ logger.error(f"Bucket creation failed: {str(create_error)}")
105
+ return False
106
+ else:
107
+ logger.error(f"Bucket check failed: {str(e)}")
108
+ return False
109
+ except Exception as e:
110
+ logger.error(f"Bucket check failed: {str(e)}")
111
+ return False
112
+
113
+ # ensure return bool type
114
+ return self._set_cors_rules()
115
+
116
+ def _set_cors_rules(self) -> bool:
117
+ if not self._client:
118
+ logger.error("TOS client is not initialized")
119
+ return False
120
+ try:
121
+ rule = self._tos_module.models2.CORSRule(
122
+ allowed_origins=["*"],
123
+ allowed_methods=["GET", "HEAD"],
124
+ allowed_headers=["*"],
125
+ max_age_seconds=1000,
126
+ )
127
+ self._client.put_bucket_cors(self.bucket_name, [rule])
128
+ logger.info(f"CORS rules for bucket {self.bucket_name} set successfully")
129
+ return True
79
130
  except Exception as e:
80
- logger.error(f"Bucket creation failed: {str(e)}")
131
+ logger.error(
132
+ f"Failed to set CORS rules for bucket {self.bucket_name}: {str(e)}"
133
+ )
81
134
  return False
82
135
 
83
136
  def build_tos_url(
@@ -93,7 +146,9 @@ class VeTOS(BaseModel):
93
146
 
94
147
  timestamp: str = datetime.now().strftime("%Y%m%d%H%M%S%f")[:-3]
95
148
  object_key: str = f"{app_name}-{user_id}-{session_id}/{timestamp}-{file_name}"
96
- tos_url: str = f"https://{self.config.bucket_name}.tos-{self.config.region}.volces.com/{object_key}"
149
+ tos_url: str = (
150
+ f"https://{self.bucket_name}.tos-{self.region}.volces.com/{object_key}"
151
+ )
97
152
 
98
153
  return object_key, tos_url
99
154
 
@@ -103,57 +158,57 @@ class VeTOS(BaseModel):
103
158
  data: Union[str, bytes],
104
159
  ):
105
160
  if isinstance(data, str):
106
- data_type = "file"
161
+ # data is a file path
162
+ return asyncio.to_thread(self._do_upload_file, object_key, data)
107
163
  elif isinstance(data, bytes):
108
- data_type = "bytes"
164
+ # data is bytes content
165
+ return asyncio.to_thread(self._do_upload_bytes, object_key, data)
109
166
  else:
110
167
  error_msg = f"Upload failed: data type error. Only str (file path) and bytes are supported, got {type(data)}"
111
168
  logger.error(error_msg)
112
169
  raise ValueError(error_msg)
113
- if data_type == "file":
114
- return asyncio.to_thread(self._do_upload_file, object_key, data)
115
- elif data_type == "bytes":
116
- return asyncio.to_thread(self._do_upload_bytes, object_key, data)
117
170
 
118
- def _do_upload_bytes(self, object_key: str, bytes: bytes) -> bool:
171
+ def _do_upload_bytes(self, object_key: str, data: bytes) -> None:
119
172
  try:
120
173
  if not self._client:
121
- return False
174
+ return
122
175
  if not self.create_bucket():
123
- return False
176
+ return
124
177
  self._client.put_object(
125
- bucket=self.config.bucket_name, key=object_key, content=bytes
178
+ bucket=self.bucket_name, key=object_key, content=data
126
179
  )
127
- logger.debug(f"Upload success, object_key: {object_key}")
180
+ logger.debug(f"Upload success, url: {object_key}")
128
181
  self._close()
129
- return True
182
+ return
130
183
  except Exception as e:
131
184
  logger.error(f"Upload failed: {e}")
132
185
  self._close()
133
- return False
186
+ return
134
187
 
135
- def _do_upload_file(self, object_key: str, file_path: str) -> bool:
188
+ def _do_upload_file(self, object_key: str, file_path: str) -> None:
136
189
  try:
137
190
  if not self._client:
138
- return False
191
+ return
139
192
  if not self.create_bucket():
140
- return False
141
-
193
+ return
142
194
  self._client.put_object_from_file(
143
- bucket=self.config.bucket_name, key=object_key, file_path=file_path
195
+ bucket=self.bucket_name, key=object_key, file_path=file_path
144
196
  )
145
197
  self._close()
146
198
  logger.debug(f"Upload success, object_key: {object_key}")
147
- return True
199
+ return
148
200
  except Exception as e:
149
201
  logger.error(f"Upload failed: {e}")
150
202
  self._close()
151
- return False
203
+ return
152
204
 
153
205
  def download(self, object_key: str, save_path: str) -> bool:
154
206
  """download image from TOS"""
207
+ if not self._client:
208
+ logger.error("TOS client is not initialized")
209
+ return False
155
210
  try:
156
- object_stream = self._client.get_object(self.config.bucket_name, object_key)
211
+ object_stream = self._client.get_object(self.bucket_name, object_key)
157
212
 
158
213
  save_dir = os.path.dirname(save_path)
159
214
  if save_dir and not os.path.exists(save_dir):
@@ -11,11 +11,15 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import io
15
+ import os.path
16
+ from typing import Any, BinaryIO, Literal, TextIO
14
17
 
15
- from typing import BinaryIO, Literal, TextIO
18
+ from pydantic import BaseModel
16
19
 
17
20
  from veadk.database.database_adapter import get_knowledgebase_database_adapter
18
21
  from veadk.database.database_factory import DatabaseFactory
22
+ from veadk.utils.misc import formatted_timestamp
19
23
  from veadk.utils.logger import get_logger
20
24
 
21
25
  logger = get_logger(__name__)
@@ -25,23 +29,23 @@ def build_knowledgebase_index(app_name: str):
25
29
  return f"veadk_kb_{app_name}"
26
30
 
27
31
 
28
- class KnowledgeBase:
29
- def __init__(
30
- self,
31
- backend: Literal["local", "opensearch", "viking", "redis", "mysql"] = "local",
32
- top_k: int = 10,
33
- db_config=None,
34
- ):
35
- logger.info(f"Initializing knowledgebase: backend={backend} top_k={top_k}")
32
+ class KnowledgeBase(BaseModel):
33
+ backend: Literal["local", "opensearch", "viking", "redis", "mysql"] = "local"
34
+ top_k: int = 10
35
+ db_config: Any | None = None
36
36
 
37
- self.backend = backend
38
- self.top_k = top_k
37
+ def model_post_init(self, __context: Any) -> None:
38
+ logger.info(
39
+ f"Initializing knowledgebase: backend={self.backend} top_k={self.top_k}"
40
+ )
39
41
 
40
- self.db_client = DatabaseFactory.create(backend=backend, config=db_config)
41
- self.adapter = get_knowledgebase_database_adapter(self.db_client)
42
+ self._db_client = DatabaseFactory.create(
43
+ backend=self.backend, config=self.db_config
44
+ )
45
+ self._adapter = get_knowledgebase_database_adapter(self._db_client)
42
46
 
43
47
  logger.info(
44
- f"Initialized knowledgebase: db_client={self.db_client.__class__.__name__} adapter={self.adapter}"
48
+ f"Initialized knowledgebase: db_client={self._db_client.__class__.__name__} adapter={self._adapter}"
45
49
  )
46
50
 
47
51
  def add(
@@ -52,9 +56,16 @@ class KnowledgeBase:
52
56
  ):
53
57
  """
54
58
  Add documents to the vector database.
55
- You can only upload files or file characters when the adapter type used is vikingdb.
56
- In addition, if you upload data of the bytes type,
57
- for example, if you read the file stream of a pdf, then you need to pass an additional parameter file_ext = '.pdf'.
59
+ Args:
60
+ data (str | list[str] | TextIO | BinaryIO | bytes): The data to be added.
61
+ - str: A single file path. (viking only)
62
+ - list[str]: A list of file paths.
63
+ - TextIO: A file object (TextIO). (viking only) file descriptor
64
+ - BinaryIO: A file object (BinaryIO). (viking only) file descriptor
65
+ - bytes: Binary data. (viking only) binary data (f.read())
66
+ app_name: index name
67
+ **kwargs: Additional keyword arguments.
68
+ - file_name (str | list[str]): The file name or a list of file names (including suffix). (viking only)
58
69
  """
59
70
  if self.backend != "viking" and not (
60
71
  isinstance(data, str) or isinstance(data, list)
@@ -64,10 +75,68 @@ class KnowledgeBase:
64
75
  )
65
76
 
66
77
  index = build_knowledgebase_index(app_name)
67
-
68
78
  logger.info(f"Adding documents to knowledgebase: index={index}")
69
79
 
70
- self.adapter.add(data=data, index=index)
80
+ if self.backend == "viking":
81
+ # Case 1: Handling file paths or lists of file paths (str)
82
+ if isinstance(data, str) and os.path.isfile(data):
83
+ # Get the file name (including the suffix)
84
+ if "file_name" not in kwargs or not kwargs["file_name"]:
85
+ kwargs["file_name"] = os.path.basename(data)
86
+ return self._adapter.add(data=data, index=index, **kwargs)
87
+ # Case 2: Handling when list[str] is a full path (list[str])
88
+ if isinstance(data, list):
89
+ if all(isinstance(item, str) for item in data):
90
+ all_paths = all(os.path.isfile(item) for item in data)
91
+ all_not_paths = all(not os.path.isfile(item) for item in data)
92
+ if all_paths:
93
+ if "file_name" not in kwargs or not kwargs["file_name"]:
94
+ kwargs["file_name"] = [
95
+ os.path.basename(item) for item in data
96
+ ]
97
+ return self._adapter.add(data=data, index=index, **kwargs)
98
+ elif (
99
+ not all_not_paths
100
+ ): # Prevent the occurrence of non-existent paths
101
+ # There is a mixture of paths and non-paths
102
+ raise ValueError(
103
+ "Mixed file paths and content strings in list are not allowed"
104
+ )
105
+ # Case 3: Handling strings or string arrays (content) (str or list[str])
106
+ if isinstance(data, str) or (
107
+ isinstance(data, list) and all(isinstance(item, str) for item in data)
108
+ ):
109
+ if "file_name" not in kwargs or not kwargs["file_name"]:
110
+ if isinstance(data, str):
111
+ kwargs["file_name"] = f"{formatted_timestamp()}.txt"
112
+ else: # list[str] without file_names
113
+ prefix_file_name = formatted_timestamp()
114
+ kwargs["file_name"] = [
115
+ f"{prefix_file_name}_{i}.txt" for i in range(len(data))
116
+ ]
117
+ return self._adapter.add(data=data, index=index, **kwargs)
118
+
119
+ # Case 4: Handling binary data (bytes)
120
+ if isinstance(data, bytes):
121
+ # user must give file_name
122
+ if "file_name" not in kwargs:
123
+ raise ValueError("file_name must be provided for binary data")
124
+ return self._adapter.add(data=data, index=index, **kwargs)
125
+
126
+ # Case 5: Handling file objects TextIO or BinaryIO
127
+ if isinstance(data, (io.TextIOWrapper, io.BufferedReader)):
128
+ if not kwargs.get("file_name") and hasattr(data, "name"):
129
+ kwargs["file_name"] = os.path.basename(data.name)
130
+ return self._adapter.add(data=data, index=index, **kwargs)
131
+ # Case6: Unsupported data type
132
+ raise TypeError(f"Unsupported data type: {type(data)}")
133
+
134
+ if not isinstance(data, list):
135
+ raise TypeError(
136
+ f"Unsupported data type: {type(data)}. Only viking support file_path and file bytes"
137
+ )
138
+ # not viking
139
+ return self._adapter.add(data=data, index=index, **kwargs)
71
140
 
72
141
  def search(self, query: str, app_name: str, top_k: int | None = None) -> list[str]:
73
142
  top_k = self.top_k if top_k is None else top_k
@@ -76,7 +145,34 @@ class KnowledgeBase:
76
145
  f"Searching knowledgebase: app_name={app_name} query={query} top_k={top_k}"
77
146
  )
78
147
  index = build_knowledgebase_index(app_name)
79
- result = self.adapter.query(query=query, index=index, top_k=top_k)
148
+ result = self._adapter.query(query=query, index=index, top_k=top_k)
80
149
  if len(result) == 0:
81
150
  logger.warning(f"No documents found in knowledgebase. Query: {query}")
82
151
  return result
152
+
153
+ def delete(self, app_name: str) -> bool:
154
+ index = build_knowledgebase_index(app_name)
155
+ return self._adapter.delete(index=index)
156
+
157
+ def delete_doc(self, app_name: str, id: str) -> bool:
158
+ index = build_knowledgebase_index(app_name)
159
+ return self._adapter.delete_doc(index=index, id=id)
160
+
161
+ def list_chunks(
162
+ self, app_name: str, offset: int = 0, limit: int = 100
163
+ ) -> list[dict]:
164
+ index = build_knowledgebase_index(app_name)
165
+ return self._adapter.list_chunks(index=index, offset=offset, limit=limit)
166
+
167
+ def list_docs(self, app_name: str, offset: int = 0, limit: int = 100) -> list[dict]:
168
+ if self.backend == "viking":
169
+ index = build_knowledgebase_index(app_name)
170
+ return self._adapter.list_docs(index=index, offset=offset, limit=limit)
171
+ else:
172
+ raise NotImplementedError(
173
+ f"list_docs not supported for {self.backend}, only viking support list_docs"
174
+ )
175
+
176
+ def exists(self, app_name: str) -> bool:
177
+ index = build_knowledgebase_index(app_name)
178
+ return self._adapter.index_exists(index=index)
@@ -13,8 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
  # adapted from Google ADK memory service adk-python/src/google/adk/memory/vertex_ai_memory_bank_service.py at 0a9e67dbca67789247e882d16b139dbdc76a329a · google/adk-python
16
+
16
17
  import json
17
- from typing import Literal
18
+ from typing import Any, Literal
18
19
 
19
20
  from google.adk.events.event import Event
20
21
  from google.adk.memory.base_memory_service import (
@@ -24,6 +25,7 @@ from google.adk.memory.base_memory_service import (
24
25
  from google.adk.memory.memory_entry import MemoryEntry
25
26
  from google.adk.sessions import Session
26
27
  from google.genai import types
28
+ from pydantic import BaseModel
27
29
  from typing_extensions import override
28
30
 
29
31
  from veadk.database import DatabaseFactory
@@ -37,33 +39,30 @@ def build_long_term_memory_index(app_name: str, user_id: str):
37
39
  return f"{app_name}_{user_id}"
38
40
 
39
41
 
40
- class LongTermMemory(BaseMemoryService):
41
- def __init__(
42
- self,
43
- backend: Literal[
44
- "local", "opensearch", "redis", "mysql", "viking", "viking_mem"
45
- ] = "opensearch",
46
- top_k: int = 5,
47
- ):
48
- if backend == "viking":
42
+ class LongTermMemory(BaseMemoryService, BaseModel):
43
+ backend: Literal[
44
+ "local", "opensearch", "redis", "mysql", "viking", "viking_mem"
45
+ ] = "opensearch"
46
+ top_k: int = 5
47
+
48
+ def model_post_init(self, __context: Any) -> None:
49
+ if self.backend == "viking":
49
50
  logger.warning(
50
51
  "`viking` backend is deprecated, switching to `viking_mem` backend."
51
52
  )
52
- backend = "viking_mem"
53
- self.top_k = top_k
54
- self.backend = backend
53
+ self.backend = "viking_mem"
55
54
 
56
55
  logger.info(
57
56
  f"Initializing long term memory: backend={self.backend} top_k={self.top_k}"
58
57
  )
59
58
 
60
- self.db_client = DatabaseFactory.create(
61
- backend=backend,
59
+ self._db_client = DatabaseFactory.create(
60
+ backend=self.backend,
62
61
  )
63
- self.adapter = get_long_term_memory_database_adapter(self.db_client)
62
+ self._adapter = get_long_term_memory_database_adapter(self._db_client)
64
63
 
65
64
  logger.info(
66
- f"Initialized long term memory: db_client={self.db_client.__class__.__name__} adapter={self.adapter}"
65
+ f"Initialized long term memory: db_client={self._db_client.__class__.__name__} adapter={self._adapter}"
67
66
  )
68
67
 
69
68
  def _filter_and_convert_events(self, events: list[Event]) -> list[str]:
@@ -101,9 +100,9 @@ class LongTermMemory(BaseMemoryService):
101
100
 
102
101
  # check if viking memory database, should give a user id: if/else
103
102
  if self.backend == "viking_mem":
104
- self.adapter.add(data=event_strings, index=index, user_id=session.user_id)
103
+ self._adapter.add(data=event_strings, index=index, user_id=session.user_id)
105
104
  else:
106
- self.adapter.add(data=event_strings, index=index)
105
+ self._adapter.add(data=event_strings, index=index)
107
106
 
108
107
  logger.info(
109
108
  f"Added {len(event_strings)} events to long term memory: index={index}"
@@ -119,11 +118,11 @@ class LongTermMemory(BaseMemoryService):
119
118
 
120
119
  # user id if viking memory db
121
120
  if self.backend == "viking_mem":
122
- memory_chunks = self.adapter.query(
121
+ memory_chunks = self._adapter.query(
123
122
  query=query, index=index, top_k=self.top_k, user_id=user_id
124
123
  )
125
124
  else:
126
- memory_chunks = self.adapter.query(
125
+ memory_chunks = self._adapter.query(
127
126
  query=query, index=index, top_k=self.top_k
128
127
  )
129
128
 
@@ -20,7 +20,12 @@ from google.adk.sessions import Session
20
20
  from google.genai.types import Content, Part
21
21
  from litellm import completion
22
22
 
23
- from veadk.config import getenv
23
+ from veadk.config import settings
24
+ from veadk.consts import (
25
+ DEFAULT_MODEL_AGENT_API_BASE,
26
+ DEFAULT_MODEL_AGENT_NAME,
27
+ DEFAULT_MODEL_AGENT_PROVIDER,
28
+ )
24
29
  from veadk.prompts.prompt_memory_processor import render_prompt
25
30
  from veadk.utils.logger import get_logger
26
31
 
@@ -62,9 +67,9 @@ class ShortTermMemoryProcessor:
62
67
  prompt = render_prompt(messages=messages)
63
68
 
64
69
  res = completion(
65
- model=getenv("MODEL_AGENT_PROVIDER") + "/" + getenv("MODEL_AGENT_NAME"),
66
- base_url=getenv("MODEL_AGENT_API_BASE"),
67
- api_key=getenv("MODEL_AGENT_API_KEY"),
70
+ model=DEFAULT_MODEL_AGENT_PROVIDER + "/" + DEFAULT_MODEL_AGENT_NAME,
71
+ base_url=DEFAULT_MODEL_AGENT_API_BASE,
72
+ api_key=settings.model.api_key,
68
73
  messages=[
69
74
  {
70
75
  "role": "user",