agentscope-runtime 0.1.3__py3-none-any.whl → 0.1.5__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 (49) hide show
  1. agentscope_runtime/engine/agents/agentscope_agent/agent.py +56 -12
  2. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +2 -1
  3. agentscope_runtime/engine/agents/agno_agent.py +11 -5
  4. agentscope_runtime/engine/agents/autogen_agent.py +10 -4
  5. agentscope_runtime/engine/agents/utils.py +53 -0
  6. agentscope_runtime/engine/services/context_manager.py +2 -0
  7. agentscope_runtime/engine/services/mem0_memory_service.py +124 -0
  8. agentscope_runtime/engine/services/memory_service.py +2 -1
  9. agentscope_runtime/engine/services/redis_session_history_service.py +4 -3
  10. agentscope_runtime/engine/services/sandbox_service.py +6 -16
  11. agentscope_runtime/engine/services/session_history_service.py +4 -3
  12. agentscope_runtime/engine/services/tablestore_memory_service.py +304 -0
  13. agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
  14. agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
  15. agentscope_runtime/engine/services/utils/__init__.py +0 -0
  16. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
  17. agentscope_runtime/sandbox/box/base/base_sandbox.py +2 -2
  18. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +2 -2
  19. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +2 -2
  20. agentscope_runtime/sandbox/box/training_box/training_box.py +4 -12
  21. agentscope_runtime/sandbox/build.py +37 -17
  22. agentscope_runtime/sandbox/client/http_client.py +42 -10
  23. agentscope_runtime/sandbox/client/training_client.py +0 -1
  24. agentscope_runtime/sandbox/constant.py +26 -0
  25. agentscope_runtime/sandbox/custom/custom_sandbox.py +5 -5
  26. agentscope_runtime/sandbox/custom/example.py +2 -2
  27. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +4 -2
  28. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +25 -9
  29. agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
  30. agentscope_runtime/sandbox/manager/container_clients/agentrun_client.py +1096 -0
  31. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +25 -201
  32. agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +1 -3
  33. agentscope_runtime/sandbox/manager/sandbox_manager.py +40 -13
  34. agentscope_runtime/sandbox/manager/server/app.py +27 -0
  35. agentscope_runtime/sandbox/manager/server/config.py +30 -2
  36. agentscope_runtime/sandbox/model/container.py +1 -1
  37. agentscope_runtime/sandbox/model/manager_config.py +93 -5
  38. agentscope_runtime/sandbox/utils.py +97 -0
  39. agentscope_runtime/version.py +1 -1
  40. {agentscope_runtime-0.1.3.dist-info → agentscope_runtime-0.1.5.dist-info}/METADATA +56 -57
  41. {agentscope_runtime-0.1.3.dist-info → agentscope_runtime-0.1.5.dist-info}/RECORD +45 -40
  42. agentscope_runtime/engine/agents/llm_agent.py +0 -51
  43. agentscope_runtime/engine/llms/__init__.py +0 -3
  44. agentscope_runtime/engine/llms/base_llm.py +0 -60
  45. agentscope_runtime/engine/llms/qwen_llm.py +0 -47
  46. {agentscope_runtime-0.1.3.dist-info → agentscope_runtime-0.1.5.dist-info}/WHEEL +0 -0
  47. {agentscope_runtime-0.1.3.dist-info → agentscope_runtime-0.1.5.dist-info}/entry_points.txt +0 -0
  48. {agentscope_runtime-0.1.3.dist-info → agentscope_runtime-0.1.5.dist-info}/licenses/LICENSE +0 -0
  49. {agentscope_runtime-0.1.3.dist-info → agentscope_runtime-0.1.5.dist-info}/top_level.txt +0 -0
@@ -5,21 +5,13 @@ Module for the Training Sandbox implementation.
5
5
  This module provides a sandbox environment for training tasks
6
6
  with specific configuration and tool calling methods.
7
7
  """
8
- import platform
9
8
  from typing import Dict, Optional
10
9
  import os
11
10
 
11
+ from ...utils import build_image_uri
12
12
  from ...registry import SandboxRegistry
13
13
  from ...enums import SandboxType
14
14
  from ...box.sandbox import Sandbox
15
- from ...constant import IMAGE_TAG
16
-
17
-
18
- def get_image_tag() -> str:
19
- machine = platform.machine().lower()
20
- if machine in ("arm64", "aarch64", "armv7l", "armv8"):
21
- return f"{IMAGE_TAG}-arm64"
22
- return IMAGE_TAG
23
15
 
24
16
 
25
17
  class TrainingSandbox(Sandbox):
@@ -214,7 +206,7 @@ class TrainingSandbox(Sandbox):
214
206
 
215
207
 
216
208
  @SandboxRegistry.register(
217
- f"agentscope/runtime-sandbox-appworld:{get_image_tag()}",
209
+ build_image_uri("runtime-sandbox-appworld", arm64_compatible=False),
218
210
  sandbox_type=SandboxType.APPWORLD,
219
211
  runtime_config={"shm_size": "5.06gb"},
220
212
  security_level="medium",
@@ -258,7 +250,7 @@ DATASET_SUB_TYPE = os.environ.get("DATASET_SUB_TYPE", "multi_turn")
258
250
 
259
251
 
260
252
  @SandboxRegistry.register(
261
- f"agentscope/runtime-sandbox-bfcl:{get_image_tag()}",
253
+ build_image_uri("runtime-sandbox-bfcl", arm64_compatible=False),
262
254
  sandbox_type=SandboxType.BFCL,
263
255
  runtime_config={"shm_size": "8.06gb"},
264
256
  security_level="medium",
@@ -309,7 +301,7 @@ class BFCLSandbox(TrainingSandbox):
309
301
 
310
302
 
311
303
  @SandboxRegistry.register(
312
- f"agentscope/runtime-sandbox-webshop:{get_image_tag()}",
304
+ build_image_uri("runtime-sandbox-webshop", arm64_compatible=False),
313
305
  sandbox_type=SandboxType.WEBSHOP,
314
306
  runtime_config={"shm_size": "5.06gb"},
315
307
  security_level="medium",
@@ -11,6 +11,10 @@ import requests
11
11
 
12
12
  from .enums import SandboxType
13
13
  from .registry import SandboxRegistry
14
+ from .utils import dynamic_import
15
+
16
+
17
+ logger = logging.getLogger(__name__)
14
18
 
15
19
 
16
20
  def find_free_port(start_port, end_port):
@@ -18,7 +22,7 @@ def find_free_port(start_port, end_port):
18
22
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
19
23
  if sock.connect_ex(("localhost", port)) != 0:
20
24
  return port
21
- logging.error(
25
+ logger.error(
22
26
  f"No free ports available in the range {start_port}-{end_port}",
23
27
  )
24
28
  raise RuntimeError(
@@ -30,7 +34,7 @@ def check_health(url, secret_token, timeout=120, interval=5):
30
34
  headers = {"Authorization": f"Bearer {secret_token}"}
31
35
  spent_time = 0
32
36
  while spent_time < timeout:
33
- logging.info(
37
+ logger.info(
34
38
  f"Attempting to connect to {url} (Elapsed time: {spent_time} "
35
39
  f"seconds)...",
36
40
  )
@@ -41,13 +45,13 @@ def check_health(url, secret_token, timeout=120, interval=5):
41
45
  return True
42
46
  except requests.exceptions.RequestException:
43
47
  pass
44
- logging.info(
48
+ logger.info(
45
49
  f"Health check failed for {url}. Retrying in {interval} "
46
50
  f"seconds...",
47
51
  )
48
52
  time.sleep(interval)
49
53
  spent_time += interval
50
- logging.error(f"Health check failed for {url} after {timeout} seconds.")
54
+ logger.error(f"Health check failed for {url} after {timeout} seconds.")
51
55
  return False
52
56
 
53
57
 
@@ -57,10 +61,10 @@ def build_image(build_type, dockerfile_path=None):
57
61
  f"src/agentscope_runtime/sandbox/box/{build_type}/Dockerfile"
58
62
  )
59
63
 
60
- logging.info(f"Building {build_type} with `{dockerfile_path}`...")
64
+ logger.info(f"Building {build_type} with `{dockerfile_path}`...")
61
65
 
62
66
  # Initialize and update Git submodule
63
- logging.info("Initializing and updating Git submodule...")
67
+ logger.info("Initializing and updating Git submodule...")
64
68
  subprocess.run(
65
69
  ["git", "submodule", "update", "--init", "--recursive"],
66
70
  check=True,
@@ -69,7 +73,7 @@ def build_image(build_type, dockerfile_path=None):
69
73
  secret_token = "secret_token123"
70
74
  image_name = SandboxRegistry.get_image_by_type(build_type)
71
75
 
72
- logging.info(f"Building Docker image {image_name}...")
76
+ logger.info(f"Building Docker image {image_name}...")
73
77
 
74
78
  # Check if image exists
75
79
  result = subprocess.run(
@@ -87,7 +91,7 @@ def build_image(build_type, dockerfile_path=None):
87
91
  f"you want to overwrite it? (y/N): ",
88
92
  )
89
93
  if choice.lower() != "y":
90
- logging.info("Exiting without overwriting the existing image.")
94
+ logger.info("Exiting without overwriting the existing image.")
91
95
  return
92
96
 
93
97
  if not os.path.exists(dockerfile_path):
@@ -109,9 +113,9 @@ def build_image(build_type, dockerfile_path=None):
109
113
  ],
110
114
  check=False,
111
115
  )
112
- logging.info(f"Docker image {image_name}dev built successfully.")
116
+ logger.info(f"Docker image {image_name}dev built successfully.")
113
117
 
114
- logging.info(f"Start to build image {image_name}.")
118
+ logger.info(f"Start to build image {image_name}.")
115
119
 
116
120
  # Run the container with port mapping and environment variable
117
121
  free_port = find_free_port(8080, 8090)
@@ -131,7 +135,7 @@ def build_image(build_type, dockerfile_path=None):
131
135
  check=False,
132
136
  )
133
137
  container_id = result.stdout.strip()
134
- logging.info(f"Running container {container_id} on port {free_port}")
138
+ logger.info(f"Running container {container_id} on port {free_port}")
135
139
 
136
140
  # Check health endpoints
137
141
  fastapi_health_url = f"http://localhost:{free_port}/fastapi/healthz"
@@ -149,18 +153,18 @@ def build_image(build_type, dockerfile_path=None):
149
153
  browser_healthy = True
150
154
 
151
155
  if browser_healthy and fastapi_healthy:
152
- logging.info("Health checks passed.")
156
+ logger.info("Health checks passed.")
153
157
  subprocess.run(
154
158
  ["docker", "commit", container_id, f"{image_name}"],
155
159
  check=True,
156
160
  )
157
- logging.info(
161
+ logger.info(
158
162
  f"Docker image {image_name} committed successfully.",
159
163
  )
160
164
  subprocess.run(["docker", "stop", container_id], check=True)
161
165
  subprocess.run(["docker", "rm", container_id], check=True)
162
166
  else:
163
- logging.error("Health checks failed.")
167
+ logger.error("Health checks failed.")
164
168
  subprocess.run(["docker", "stop", container_id], check=True)
165
169
 
166
170
  choice = input(
@@ -171,9 +175,9 @@ def build_image(build_type, dockerfile_path=None):
171
175
  ["docker", "rmi", "-f", f"{image_name}dev"],
172
176
  check=True,
173
177
  )
174
- logging.info(f"Dev image {image_name}dev deleted.")
178
+ logger.info(f"Dev image {image_name}dev deleted.")
175
179
  else:
176
- logging.info(f"Dev image {image_name}dev retained.")
180
+ logger.info(f"Dev image {image_name}dev retained.")
177
181
 
178
182
 
179
183
  def main():
@@ -182,8 +186,8 @@ def main():
182
186
  )
183
187
  parser.add_argument(
184
188
  "build_type",
189
+ nargs="?",
185
190
  default="base",
186
- choices=[x.value for x in SandboxType] + ["all"],
187
191
  help="Specify the build type to execute.",
188
192
  )
189
193
 
@@ -193,13 +197,29 @@ def main():
193
197
  help="Specify the path for the Dockerfile.",
194
198
  )
195
199
 
200
+ parser.add_argument(
201
+ "--extension",
202
+ action="append",
203
+ help="Path to a Python file or module name to load as an extension",
204
+ )
205
+
196
206
  args = parser.parse_args()
197
207
 
208
+ if args.extension:
209
+ for ext in args.extension:
210
+ logger.info(f"Loading extension: {ext}")
211
+ mod = dynamic_import(ext)
212
+ logger.info(f"Extension loaded: {mod.__name__}")
213
+
198
214
  if args.build_type == "all":
199
215
  # Only build the built-in images
200
216
  for build_type in [x.value for x in SandboxType.get_builtin_members()]:
201
217
  build_image(build_type)
202
218
  else:
219
+ assert args.build_type in [
220
+ x.value for x in SandboxType
221
+ ], f"Invalid build type: {args.build_type}"
222
+
203
223
  if args.build_type not in [
204
224
  x.value for x in SandboxType.get_builtin_members()
205
225
  ]:
@@ -69,7 +69,7 @@ class SandboxHttpClient:
69
69
  def __init__(
70
70
  self,
71
71
  model: Optional[ContainerModel] = None,
72
- timeout: int = 30,
72
+ timeout: int = 60,
73
73
  enable_browser: bool = True,
74
74
  domain: str = "localhost",
75
75
  ) -> None:
@@ -95,7 +95,10 @@ class SandboxHttpClient:
95
95
  self.secret = model.runtime_token
96
96
 
97
97
  # Update headers with secret if provided
98
- headers = {"Content-Type": "application/json"}
98
+ headers = {
99
+ "Content-Type": "application/json",
100
+ "x-agentrun-session-id": "s" + self.session_id,
101
+ }
99
102
  if self.secret:
100
103
  headers["Authorization"] = f"Bearer {self.secret}"
101
104
  self.session.headers.update(headers)
@@ -112,16 +115,20 @@ class SandboxHttpClient:
112
115
  base_url=self.browser_url,
113
116
  )
114
117
 
118
+ headers = {"x-agentrun-session-id": "s" + self.session_id}
119
+
115
120
  # Create a new browser session if it doesn't exist
116
121
  try:
117
122
  # Try to connet to existing session
118
123
  self.steel_client.sessions.retrieve(
119
124
  BROWSER_SESSION_ID,
125
+ extra_headers=headers,
120
126
  )
121
127
  except Exception:
122
128
  # Session not found, create a new one
123
129
  self.steel_client.sessions.create(
124
130
  session_id=BROWSER_SESSION_ID,
131
+ extra_headers=headers,
125
132
  )
126
133
 
127
134
  return self
@@ -246,7 +253,10 @@ class SandboxHttpClient:
246
253
  """Run an IPython cell."""
247
254
  try:
248
255
  endpoint = f"{self.base_url}/tools/run_ipython_cell"
249
- response = self.session.post(endpoint, json={"code": code})
256
+ response = self.session.post(
257
+ endpoint,
258
+ json={"code": code},
259
+ )
250
260
  response.raise_for_status()
251
261
  return response.json()
252
262
  except requests.exceptions.RequestException as e:
@@ -379,7 +389,11 @@ class SandboxHttpClient:
379
389
  endpoint = f"{self.base_url}/workspace/files"
380
390
  params = {"file_path": file_path}
381
391
  data = {"content": content}
382
- response = self.session.post(endpoint, params=params, json=data)
392
+ response = self.session.post(
393
+ endpoint,
394
+ params=params,
395
+ json=data,
396
+ )
383
397
  response.raise_for_status()
384
398
  return response.json()
385
399
  except requests.exceptions.RequestException as e:
@@ -402,7 +416,10 @@ class SandboxHttpClient:
402
416
  try:
403
417
  endpoint = f"{self.base_url}/workspace/list-directories"
404
418
  params = {"directory": directory}
405
- response = self.session.get(endpoint, params=params)
419
+ response = self.session.get(
420
+ endpoint,
421
+ params=params,
422
+ )
406
423
  response.raise_for_status()
407
424
  return response.json()
408
425
  except requests.exceptions.RequestException as e:
@@ -419,7 +436,10 @@ class SandboxHttpClient:
419
436
  try:
420
437
  endpoint = f"{self.base_url}/workspace/directories"
421
438
  params = {"directory_path": directory_path}
422
- response = self.session.post(endpoint, params=params)
439
+ response = self.session.post(
440
+ endpoint,
441
+ params=params,
442
+ )
423
443
  response.raise_for_status()
424
444
  return response.json()
425
445
  except requests.exceptions.RequestException as e:
@@ -438,7 +458,10 @@ class SandboxHttpClient:
438
458
  try:
439
459
  endpoint = f"{self.base_url}/workspace/files"
440
460
  params = {"file_path": file_path}
441
- response = self.session.delete(endpoint, params=params)
461
+ response = self.session.delete(
462
+ endpoint,
463
+ params=params,
464
+ )
442
465
  response.raise_for_status()
443
466
  return response.json()
444
467
  except requests.exceptions.RequestException as e:
@@ -461,7 +484,10 @@ class SandboxHttpClient:
461
484
  try:
462
485
  endpoint = f"{self.base_url}/workspace/directories"
463
486
  params = {"directory_path": directory_path, "recursive": recursive}
464
- response = self.session.delete(endpoint, params=params)
487
+ response = self.session.delete(
488
+ endpoint,
489
+ params=params,
490
+ )
465
491
  response.raise_for_status()
466
492
  return response.json()
467
493
  except requests.exceptions.RequestException as e:
@@ -487,7 +513,10 @@ class SandboxHttpClient:
487
513
  "source_path": source_path,
488
514
  "destination_path": destination_path,
489
515
  }
490
- response = self.session.put(endpoint, params=params)
516
+ response = self.session.put(
517
+ endpoint,
518
+ params=params,
519
+ )
491
520
  response.raise_for_status()
492
521
  return response.json()
493
522
  except requests.exceptions.RequestException as e:
@@ -514,7 +543,10 @@ class SandboxHttpClient:
514
543
  "source_path": source_path,
515
544
  "destination_path": destination_path,
516
545
  }
517
- response = self.session.post(endpoint, params=params)
546
+ response = self.session.post(
547
+ endpoint,
548
+ params=params,
549
+ )
518
550
  response.raise_for_status()
519
551
  return response.json()
520
552
  except requests.exceptions.RequestException as e:
@@ -212,7 +212,6 @@ class TrainingSandboxClient:
212
212
  def list_tools(self, **kwargs):
213
213
  """list tools"""
214
214
  if "instance_id" in kwargs:
215
- # 只传get_tools_info的方法参数
216
215
  return self.get_tools_info(
217
216
  instance_id=kwargs.get("instance_id"),
218
217
  messages=kwargs.get("messages", {}),
@@ -1,5 +1,31 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import os
3
+ import sys
4
+ import logging
3
5
 
6
+ logger = logging.getLogger(__name__)
7
+
8
+ # Image Registry: default "", means using Docker Hub.
9
+ # For AgentScope official registry, use
10
+ # "agentscope-registry.ap-southeast-1.cr.aliyuncs.com"
11
+ REGISTRY = os.getenv("RUNTIME_SANDBOX_REGISTRY", "")
12
+ if REGISTRY == "":
13
+ agentscope_acr = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com"
14
+ if sys.platform.startswith("win"):
15
+ cmd = f"set RUNTIME_SANDBOX_REGISTRY={agentscope_acr}"
16
+ else:
17
+ cmd = f"export RUNTIME_SANDBOX_REGISTRY={agentscope_acr}"
18
+ logger.warning(
19
+ "Using Docker Hub as image registry. If pulling is slow or fails, "
20
+ f"you can switch to the AgentScope official registry by running:\n "
21
+ f"{cmd}\n",
22
+ )
23
+
24
+ # Image Namespace
25
+ IMAGE_NAMESPACE = os.getenv("RUNTIME_SANDBOX_IMAGE_NAMESPACE", "agentscope")
26
+
27
+ # Image Tag
4
28
  IMAGE_TAG = os.getenv("RUNTIME_SANDBOX_IMAGE_TAG", "latest")
29
+
30
+ # Browser Session ID
5
31
  BROWSER_SESSION_ID = "123e4567-e89b-12d3-a456-426614174000"
@@ -3,17 +3,17 @@ import os
3
3
 
4
4
  from typing import Optional
5
5
 
6
- from ..constant import IMAGE_TAG
6
+ from ..utils import build_image_uri
7
7
  from ..registry import SandboxRegistry
8
8
  from ..enums import SandboxType
9
9
  from ..box.sandbox import Sandbox
10
10
 
11
- SANDBOXTYPE = "custom_sandbox"
11
+ SANDBOX_TYPE = "custom_sandbox"
12
12
 
13
13
 
14
14
  @SandboxRegistry.register(
15
- f"agentscope/runtime-sandbox-{SANDBOXTYPE}:{IMAGE_TAG}",
16
- sandbox_type=SANDBOXTYPE,
15
+ build_image_uri(f"runtime-sandbox-{SANDBOX_TYPE}"),
16
+ sandbox_type=SANDBOX_TYPE,
17
17
  security_level="medium",
18
18
  timeout=60,
19
19
  description="my sandbox",
@@ -35,5 +35,5 @@ class CustomSandbox(Sandbox):
35
35
  timeout,
36
36
  base_url,
37
37
  bearer_token,
38
- SandboxType(SANDBOXTYPE),
38
+ SandboxType(SANDBOX_TYPE),
39
39
  )
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  from typing import Optional
3
3
 
4
- from ..constant import IMAGE_TAG
4
+ from ..utils import build_image_uri
5
5
  from ..registry import SandboxRegistry
6
6
  from ..enums import SandboxType
7
7
  from ..box.sandbox import Sandbox
@@ -10,7 +10,7 @@ SANDBOX_TYPE = "example"
10
10
 
11
11
 
12
12
  @SandboxRegistry.register(
13
- f"agentscope/runtime-sandbox-{SANDBOX_TYPE}:{IMAGE_TAG}",
13
+ build_image_uri(f"runtime-sandbox-{SANDBOX_TYPE}"),
14
14
  sandbox_type=SANDBOX_TYPE,
15
15
  security_level="medium",
16
16
  timeout=60,
@@ -1,4 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
+ from typing import Any
3
+
2
4
  from .base_mapping import Mapping
3
5
 
4
6
 
@@ -6,10 +8,10 @@ class InMemoryMapping(Mapping):
6
8
  def __init__(self):
7
9
  self.store = {}
8
10
 
9
- def set(self, key: str, value: dict):
11
+ def set(self, key: str, value: Any):
10
12
  self.store[key] = value
11
13
 
12
- def get(self, key: str) -> dict:
14
+ def get(self, key: str) -> Any:
13
15
  return self.store.get(key)
14
16
 
15
17
  def delete(self, key: str):
@@ -1,26 +1,42 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import json
3
3
 
4
+ from typing import Any
5
+
4
6
  from .base_mapping import Mapping
5
7
 
6
8
 
7
9
  class RedisMapping(Mapping):
8
- def __init__(self, redis_client):
10
+ def __init__(self, redis_client, prefix: str = ""):
9
11
  self.client = redis_client
12
+ self.prefix = prefix.rstrip(":") + ":" if prefix else ""
13
+
14
+ def _get_full_key(self, key: str) -> str:
15
+ return f"{self.prefix}{key}"
16
+
17
+ def _strip_prefix(self, full_key: str) -> str:
18
+ if self.prefix and full_key.startswith(self.prefix):
19
+ return full_key[len(self.prefix) :]
20
+ return full_key
10
21
 
11
- def set(self, key: str, value: dict):
12
- self.client.set(key, json.dumps(value))
22
+ def set(self, key: str, value: Any):
23
+ self.client.set(self._get_full_key(key), json.dumps(value))
13
24
 
14
- def get(self, key: str) -> dict:
15
- value = self.client.get(key)
25
+ def get(self, key: str) -> Any:
26
+ value = self.client.get(self._get_full_key(key))
16
27
  return json.loads(value) if value else None
17
28
 
18
29
  def delete(self, key: str):
19
- self.client.delete(key)
30
+ self.client.delete(self._get_full_key(key))
20
31
 
21
- def scan(self, prefix: str):
32
+ def scan(self, prefix: str = ""):
33
+ search_pattern = f"{self._get_full_key(prefix)}*"
22
34
  cursor = 0
23
35
  while cursor != 0:
24
- cursor, keys = self.client.scan(cursor=cursor, match=f"{prefix}*")
36
+ cursor, keys = self.client.scan(
37
+ cursor=cursor,
38
+ match=search_pattern,
39
+ )
25
40
  for key in keys:
26
- yield key.decode("utf-8")
41
+ decoded_key = key.decode("utf-8")
42
+ yield self._strip_prefix(decoded_key)
@@ -1,10 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from .base_client import BaseClient
3
- from .docker_client import DockerClient
4
- from .kubernetes_client import KubernetesClient
5
-
6
- __all__ = [
7
- "BaseClient",
8
- "DockerClient",
9
- "KubernetesClient",
10
- ]