agentscope-runtime 0.1.1__py3-none-any.whl → 0.1.2__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 (21) hide show
  1. agentscope_runtime/engine/services/context_manager.py +28 -1
  2. agentscope_runtime/engine/services/rag_service.py +101 -0
  3. agentscope_runtime/sandbox/box/training_box/env_service.py +1 -1
  4. agentscope_runtime/sandbox/box/training_box/environments/bfcl/bfcl_dataprocess.py +216 -0
  5. agentscope_runtime/sandbox/box/training_box/environments/bfcl/bfcl_env.py +380 -0
  6. agentscope_runtime/sandbox/box/training_box/environments/bfcl/env_handler.py +934 -0
  7. agentscope_runtime/sandbox/box/training_box/training_box.py +139 -9
  8. agentscope_runtime/sandbox/enums.py +2 -0
  9. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +19 -9
  10. agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +61 -6
  11. agentscope_runtime/sandbox/manager/sandbox_manager.py +95 -35
  12. agentscope_runtime/sandbox/manager/server/app.py +41 -4
  13. agentscope_runtime/sandbox/model/__init__.py +1 -5
  14. agentscope_runtime/sandbox/model/manager_config.py +2 -13
  15. agentscope_runtime/version.py +1 -1
  16. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.2.dist-info}/METADATA +6 -1
  17. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.2.dist-info}/RECORD +21 -17
  18. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.2.dist-info}/WHEEL +0 -0
  19. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.2.dist-info}/entry_points.txt +0 -0
  20. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.2.dist-info}/licenses/LICENSE +0 -0
  21. {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.2.dist-info}/top_level.txt +0 -0
@@ -4,12 +4,19 @@ from typing import List
4
4
 
5
5
  from .manager import ServiceManager
6
6
  from .memory_service import MemoryService, InMemoryMemoryService
7
+ from .rag_service import RAGService
7
8
  from .session_history_service import (
8
9
  SessionHistoryService,
9
10
  Session,
10
11
  InMemorySessionHistoryService,
11
12
  )
12
- from ..schemas.agent_schemas import Message
13
+ from ..schemas.agent_schemas import (
14
+ Message,
15
+ MessageType,
16
+ Role,
17
+ TextContent,
18
+ ContentType,
19
+ )
13
20
 
14
21
 
15
22
  class ContextComposer:
@@ -19,6 +26,7 @@ class ContextComposer:
19
26
  session: Session, # session
20
27
  memory_service: MemoryService = None,
21
28
  session_history_service: SessionHistoryService = None,
29
+ rag_service: RAGService = None,
22
30
  ):
23
31
  # session
24
32
  if session_history_service:
@@ -42,6 +50,18 @@ class ContextComposer:
42
50
  )
43
51
  session.messages = memories + session.messages
44
52
 
53
+ # rag
54
+ if rag_service:
55
+ query = await rag_service.get_query_text(request_input[-1])
56
+ docs = await rag_service.retrieve(query=query, k=5)
57
+ cooked_doc = "\n".join(docs)
58
+ message = Message(
59
+ type=MessageType.MESSAGE,
60
+ role=Role.SYSTEM,
61
+ content=[TextContent(type=ContentType.TEXT, text=cooked_doc)],
62
+ )
63
+ session.messages.append(message)
64
+
45
65
 
46
66
  class ContextManager(ServiceManager):
47
67
  """
@@ -53,10 +73,12 @@ class ContextManager(ServiceManager):
53
73
  context_composer_cls=ContextComposer,
54
74
  session_history_service: SessionHistoryService = None,
55
75
  memory_service: MemoryService = None,
76
+ rag_service: RAGService = None,
56
77
  ):
57
78
  self._context_composer_cls = context_composer_cls
58
79
  self._session_history_service = session_history_service
59
80
  self._memory_service = memory_service
81
+ self._rag_service = rag_service
60
82
  super().__init__()
61
83
 
62
84
  def _register_default_services(self):
@@ -68,6 +90,8 @@ class ContextManager(ServiceManager):
68
90
 
69
91
  self.register_service("session", self._session_history_service)
70
92
  self.register_service("memory", self._memory_service)
93
+ if self._rag_service:
94
+ self.register_service("rag", self._rag_service)
71
95
 
72
96
  async def compose_context(
73
97
  self,
@@ -77,6 +101,7 @@ class ContextManager(ServiceManager):
77
101
  await self._context_composer_cls.compose(
78
102
  memory_service=self._memory_service,
79
103
  session_history_service=self._session_history_service,
104
+ rag_service=self._rag_service,
80
105
  session=session,
81
106
  request_input=request_input,
82
107
  )
@@ -119,10 +144,12 @@ class ContextManager(ServiceManager):
119
144
  async def create_context_manager(
120
145
  memory_service: MemoryService = None,
121
146
  session_history_service: SessionHistoryService = None,
147
+ rag_service: RAGService = None,
122
148
  ):
123
149
  manager = ContextManager(
124
150
  memory_service=memory_service,
125
151
  session_history_service=session_history_service,
152
+ rag_service=rag_service,
126
153
  )
127
154
 
128
155
  async with manager:
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Optional
3
+
4
+ from .base import ServiceWithLifecycleManager
5
+ from ..schemas.agent_schemas import Message, MessageType
6
+
7
+
8
+ class RAGService(ServiceWithLifecycleManager):
9
+ """
10
+ RAG Service
11
+ """
12
+
13
+ async def get_query_text(self, message: Message) -> str:
14
+ """
15
+ Gets the query text from the messages.
16
+
17
+ Args:
18
+ message: A list of messages.
19
+
20
+ Returns:
21
+ The query text.
22
+ """
23
+ if message:
24
+ if message.type == MessageType.MESSAGE:
25
+ for content in message.content:
26
+ if content.type == "text":
27
+ return content.text
28
+ return ""
29
+
30
+ async def retrieve(self, query: str, k: int = 1) -> list[str]:
31
+ raise NotImplementedError
32
+
33
+
34
+ DEFAULT_URI = "milvus_demo.db"
35
+
36
+
37
+ class LangChainRAGService(RAGService):
38
+ """
39
+ RAG Service using LangChain
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ uri: Optional[str] = None,
45
+ docs: Optional[list[str]] = None,
46
+ ):
47
+ from langchain_community.embeddings import DashScopeEmbeddings
48
+ from langchain_milvus import Milvus
49
+
50
+ self.Milvus = Milvus
51
+ self.embeddings = DashScopeEmbeddings()
52
+ self.vectorstore = None
53
+
54
+ if uri:
55
+ self.uri = uri
56
+ self.from_db()
57
+ elif docs:
58
+ self.uri = DEFAULT_URI
59
+ self.from_docs(docs)
60
+ else:
61
+ docs = []
62
+ self.uri = DEFAULT_URI
63
+ self.from_docs(docs)
64
+
65
+ def from_docs(self, docs=None):
66
+ if docs is None:
67
+ docs = []
68
+
69
+ self.vectorstore = self.Milvus.from_documents(
70
+ documents=docs,
71
+ embedding=self.embeddings,
72
+ connection_args={
73
+ "uri": self.uri,
74
+ },
75
+ drop_old=False,
76
+ )
77
+
78
+ def from_db(self):
79
+ self.vectorstore = self.Milvus(
80
+ embedding_function=self.embeddings,
81
+ connection_args={"uri": self.uri},
82
+ index_params={"index_type": "FLAT", "metric_type": "L2"},
83
+ )
84
+
85
+ async def retrieve(self, query: str, k: int = 1) -> list[str]:
86
+ if self.vectorstore is None:
87
+ raise ValueError(
88
+ "Vector store not initialized. Call build_index first.",
89
+ )
90
+ docs = self.vectorstore.similarity_search(query, k=k)
91
+ return [doc.page_content for doc in docs]
92
+
93
+ async def start(self) -> None:
94
+ """Starts the service."""
95
+
96
+ async def stop(self) -> None:
97
+ """Stops the service."""
98
+
99
+ async def health(self) -> bool:
100
+ """Checks the health of the service."""
101
+ return True
@@ -749,4 +749,4 @@ if __name__ == "__main__":
749
749
  sys.exit(1)
750
750
 
751
751
  print(f"Starting server on {args.portal}:{args.port}")
752
- uvicorn.run(app, host=args.portal, port=args.port)
752
+ uvicorn.run(app, host=args.portal, port=args.port, log_level="error")
@@ -0,0 +1,216 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ BFCL数据预处理脚本 - 数据处理与分割工具
4
+
5
+ 脚本用途:
6
+ 1. 加载指定测试类别的用例
7
+ 2. 对测试用例进行预处理,加载工具集合schema
8
+ 3. 将数据集按指定比例分割为训练集和测试集
9
+ 4. 分别保存数据文件和ID文件
10
+
11
+ 使用示例:
12
+ result = bfcl_task_preprocess(
13
+ test_categories=["multi_turn_base"], # 指定测试类别
14
+ train_ratio=0.5, # 训练集占50%
15
+ output_dir="/path/to/output" # 输出目录
16
+ )
17
+
18
+ 生成两个文件:
19
+ {类别}_processed.jsonl:处理后的数据集
20
+ {类别}_split_ids.json:训练/测试集ID
21
+
22
+ """
23
+
24
+ from typing import List, Dict, Any, Optional
25
+ import json
26
+ import random
27
+ from pathlib import Path
28
+
29
+ from bfcl_eval.constants.eval_config import (
30
+ PROMPT_PATH,
31
+ )
32
+ from bfcl_eval.eval_checker.eval_runner_helper import load_file
33
+ from bfcl_eval.utils import (
34
+ parse_test_category_argument,
35
+ populate_test_cases_with_predefined_functions,
36
+ )
37
+
38
+
39
+ TEST_FILE_MAPPING = {
40
+ "simple": "BFCL_v4_simple.json",
41
+ "irrelevance": "BFCL_v4_irrelevance.json",
42
+ "parallel": "BFCL_v4_parallel.json",
43
+ "multiple": "BFCL_v4_multiple.json",
44
+ "parallel_multiple": "BFCL_v4_parallel_multiple.json",
45
+ "java": "BFCL_v4_java.json",
46
+ "javascript": "BFCL_v4_javascript.json",
47
+ "live_simple": "BFCL_v4_live_simple.json",
48
+ "live_multiple": "BFCL_v4_live_multiple.json",
49
+ "live_parallel": "BFCL_v4_live_parallel.json",
50
+ "live_parallel_multiple": "BFCL_v4_live_parallel_multiple.json",
51
+ "live_irrelevance": "BFCL_v4_live_irrelevance.json",
52
+ "live_relevance": "BFCL_v4_live_relevance.json",
53
+ "multi_turn_base": "BFCL_v4_multi_turn_base.json",
54
+ "multi_turn_miss_func": "BFCL_v4_multi_turn_miss_func.json",
55
+ "multi_turn_miss_param": "BFCL_v4_multi_turn_miss_param.json",
56
+ "multi_turn_long_context": "BFCL_v4_multi_turn_long_context.json",
57
+ }
58
+
59
+
60
+ def bfcl_task_preprocess(
61
+ test_categories: Optional[List[str]] = None,
62
+ train_ratio: float = 0.5,
63
+ random_seed: int = 42,
64
+ output_dir: str = "",
65
+ enable_shuffle: bool = False,
66
+ ) -> Dict[str, List[Dict[str, Any]]]:
67
+ """
68
+ Preprocess training dataset by loading test cases, processing them and
69
+ splitting into train/test sets.
70
+
71
+ Args:
72
+ test_categories: List of test categories to process. Can be specific
73
+ category names or collection names
74
+ (e.g. 'all', 'multi_turn'). If None, process all categories.
75
+ train_ratio: Ratio for training set split, range [0, 1]. If 1.0, no
76
+ split is performed.
77
+ random_seed: Random seed for reproducible data splitting.
78
+ output_dir: Output directory path.
79
+ output_prefix: Prefix for output files.
80
+ save_by_category: Whether to save files separately by category.
81
+ save_parquet: Whether to save parquet files.
82
+ enable_export_verl_data_schema: Whether to export data in verl format
83
+ schema.
84
+ Returns:
85
+ Dict containing train and test sets: {'train': [...], 'test': [...]}
86
+ """
87
+
88
+ def load_selected_test_cases(categories: List[str]):
89
+ all_test_entries_by_category = {}
90
+
91
+ try:
92
+ test_categories_resolved = parse_test_category_argument(categories)
93
+ except Exception as e:
94
+ print(f"Error: Invalid test categories - {str(e)}")
95
+ return {}
96
+
97
+ print(f"Selected test categories: {test_categories_resolved}")
98
+
99
+ for category in test_categories_resolved:
100
+ if category in TEST_FILE_MAPPING:
101
+ test_file_path = TEST_FILE_MAPPING[category]
102
+ test_entries = load_file(PROMPT_PATH / test_file_path)
103
+ print(f"Loaded {len(test_entries)} test cases from {category}")
104
+ if category not in all_test_entries_by_category:
105
+ all_test_entries_by_category[category] = []
106
+ all_test_entries_by_category[category].extend(test_entries)
107
+
108
+ return all_test_entries_by_category
109
+
110
+ random.seed(random_seed)
111
+
112
+ if test_categories is None:
113
+ test_categories = ["all"]
114
+
115
+ all_test_cases_by_category = load_selected_test_cases(test_categories)
116
+
117
+ if not all_test_cases_by_category:
118
+ print("Warning: No test cases found")
119
+ return {"train": [], "test": []}
120
+
121
+ total_cases = sum(
122
+ len(cases) for cases in all_test_cases_by_category.values()
123
+ )
124
+ print(
125
+ f"Loaded {total_cases} test cases in total across \
126
+ {len(all_test_cases_by_category)} categories",
127
+ )
128
+
129
+ all_processed_cases = []
130
+ processed_cases_by_category = {}
131
+
132
+ for category, test_cases in all_test_cases_by_category.items():
133
+ print(f"Processing category: {category}")
134
+
135
+ category_processed_cases = (
136
+ populate_test_cases_with_predefined_functions(test_cases)
137
+ )
138
+ processed_cases_by_category[category] = category_processed_cases
139
+ all_processed_cases.extend(category_processed_cases)
140
+ print(
141
+ f"Successfully processed {len(category_processed_cases)} test \
142
+ cases for {category}",
143
+ )
144
+
145
+ print(
146
+ f"Successfully processed {len(all_processed_cases)} test \
147
+ cases in total",
148
+ )
149
+
150
+ if enable_shuffle:
151
+ random.shuffle(all_processed_cases)
152
+ train_size = int(len(all_processed_cases) * train_ratio)
153
+ train_cases = all_processed_cases[:train_size]
154
+ test_cases = all_processed_cases[train_size:]
155
+ print(
156
+ f"Data split complete: {len(train_cases)} training, \
157
+ {len(test_cases)} test cases",
158
+ )
159
+
160
+ case_result = {"train": train_cases, "test": test_cases}
161
+
162
+ if output_dir:
163
+ output_path = Path(output_dir)
164
+ output_path.mkdir(parents=True, exist_ok=True)
165
+ test_categories_str = "_".join(test_categories)
166
+
167
+ full_jsonl_path = (
168
+ output_path / f"{test_categories_str}_processed.jsonl"
169
+ )
170
+ with open(full_jsonl_path, "w", encoding="utf-8") as f:
171
+ for case in all_processed_cases:
172
+ f.write(json.dumps(case, ensure_ascii=False) + "\n")
173
+ print(f"Full dataset saved to: {full_jsonl_path}")
174
+
175
+ split_ids = {
176
+ "train": [
177
+ case.get("id", idx) for idx, case in enumerate(train_cases)
178
+ ],
179
+ "val": [
180
+ case.get("id", idx) for idx, case in enumerate(test_cases)
181
+ ],
182
+ }
183
+
184
+ split_ids_path = output_path / f"{test_categories_str}_split_ids.json"
185
+ with open(split_ids_path, "w", encoding="utf-8") as f:
186
+ json.dump(split_ids, f, ensure_ascii=False, indent=2)
187
+ print(f"Split IDs saved to: {split_ids_path}")
188
+
189
+ return case_result
190
+
191
+
192
+ if __name__ == "__main__":
193
+ category_list = [
194
+ "all",
195
+ "all_scoring",
196
+ "multi_turn",
197
+ "single_turn",
198
+ "live",
199
+ "non_live",
200
+ "non_python",
201
+ "python",
202
+ ]
203
+
204
+ for bfcl_category in category_list:
205
+ result = bfcl_task_preprocess(
206
+ test_categories=[bfcl_category],
207
+ train_ratio=0.5,
208
+ output_dir="./bfcl/multi_turn",
209
+ )
210
+
211
+ print("-" * 50)
212
+ print("Processing complete!")
213
+ if result["train"]:
214
+ print(f"Training samples: {len(result['train'])}")
215
+ if result["test"]:
216
+ print(f"Test samples: {len(result['test'])}")