rootengine-core 0.5.0__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 (36) hide show
  1. rootengine_core/__init__.py +5 -0
  2. rootengine_core/constants/__init__.py +0 -0
  3. rootengine_core/constants/conversation.py +5 -0
  4. rootengine_core/constants/framework.py +17 -0
  5. rootengine_core/constants/tool.py +0 -0
  6. rootengine_core/conversation/__init__.py +1 -0
  7. rootengine_core/conversation/no_tool_conversation.py +106 -0
  8. rootengine_core/llm/__init__.py +2 -0
  9. rootengine_core/llm/llm.py +37 -0
  10. rootengine_core/llm/openai_adapter.py +235 -0
  11. rootengine_core/schema/source/conversation/no_tool_conversation.json +40 -0
  12. rootengine_core/schema/source/llm/llm_message.json +96 -0
  13. rootengine_core/schema/source/llm/llm_output.json +50 -0
  14. rootengine_core/schema/source/llm/tool_choice.json +39 -0
  15. rootengine_core/schema/source/reif_entry.json +70 -0
  16. rootengine_core/schema/source/tool/tool_call.json +55 -0
  17. rootengine_core/schema/source/tool/tool_func_map.json +15 -0
  18. rootengine_core/schema/source/tool/tool_registry.json +37 -0
  19. rootengine_core/schema/source/tool/tool_result.json +68 -0
  20. rootengine_core/schema_runtime/reif_schema.py +338 -0
  21. rootengine_core/tool/__init__.py +1 -0
  22. rootengine_core/tool/adapter.py +19 -0
  23. rootengine_core/tool/tool.py +141 -0
  24. rootengine_core/utils/__init__.py +0 -0
  25. rootengine_core/utils/generate_json_dict.py +291 -0
  26. rootengine_core/utils/helpers.py +43 -0
  27. rootengine_core/utils/path.py +20 -0
  28. rootengine_core/utils/registrar.py +55 -0
  29. rootengine_core/utils/reif_adapter.py +9 -0
  30. rootengine_core/utils/reif_func.py +149 -0
  31. rootengine_core/utils/time.py +9 -0
  32. rootengine_core-0.5.0.dist-info/METADATA +152 -0
  33. rootengine_core-0.5.0.dist-info/RECORD +36 -0
  34. rootengine_core-0.5.0.dist-info/WHEEL +5 -0
  35. rootengine_core-0.5.0.dist-info/licenses/LICENSE +21 -0
  36. rootengine_core-0.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+
2
+ from .conversation import NoToolConversation
3
+ from .tool import Tool
4
+ from .llm import BaseLLM
5
+ from .llm.openai_adapter import OpenAIAdapter,BaseLLMAdapter
File without changes
@@ -0,0 +1,5 @@
1
+ CONVERSATION_ROLE = [
2
+ "system",
3
+ "user",
4
+ "assistant"
5
+ ]
@@ -0,0 +1,17 @@
1
+ VERSION = {
2
+ "reif_version":"1.0",
3
+ "conversation":"0.1.0",
4
+ "tool_registry":"0.1.0",
5
+ "tool_call":"0.1.0",
6
+ "tool_results":"0.1.0",
7
+ "tool_record":"0.1.0",
8
+
9
+ }
10
+
11
+ REIF_CATEGORY = [
12
+ "agent_card",
13
+ "conversation",
14
+ "tool_registry",
15
+ "tool_record"
16
+
17
+ ]
File without changes
@@ -0,0 +1 @@
1
+ from .no_tool_conversation import NoToolConversation
@@ -0,0 +1,106 @@
1
+
2
+ from ..constants.conversation import CONVERSATION_ROLE
3
+ from pathlib import Path
4
+ from ..utils.reif_func import reif_create,reif_validate
5
+ from ..utils.time import get_iso_timestamp
6
+
7
+
8
+ from jsonschema import validate
9
+
10
+
11
+ from ..schema_runtime.reif_schema import get_json
12
+ SCHEMA = get_json("conversation.no_tool_conversation")
13
+
14
+ class NoToolConversation:
15
+ def __init__(self, conversation_entry: dict = None):
16
+ # 如果未传自动创建新的
17
+ if conversation_entry is None:
18
+ self.create()
19
+ else:
20
+ self.entry = conversation_entry
21
+
22
+ self.messages = self.entry["reif_content"]
23
+
24
+
25
+ def create(self) -> NoToolConversation:
26
+ """创建新会话"""
27
+ self.entry = reif_create({"category": "conversation"})
28
+ # 确保 reif_content 存在且为空列表
29
+ self.entry["reif_content"] = []
30
+ # 链接
31
+ self.messages = self.entry["reif_content"]
32
+
33
+ return self
34
+
35
+ def add(
36
+ self ,
37
+ role: str,
38
+ content: str = None,
39
+ created_at: str = None,
40
+ extra: dict = None,
41
+ ) -> NoToolConversation :
42
+
43
+ """
44
+ 向会话添加一条消息。
45
+
46
+ :param role: 消息角色,必须为 system/user/assistant/tool 之一。
47
+ :param content: 文本内容(对 system/user/assistant 必填,对 tool 可选)。
48
+
49
+ :param extra: 可选的扩展字典,任意附加信息。
50
+ :param created_at: 可选时间戳,若不提供则自动生成。
51
+ :return: self,支持链式调用。
52
+ """
53
+ #检验角色
54
+ if role not in CONVERSATION_ROLE:
55
+ raise ValueError(f"未知角色: {role}")
56
+
57
+ # 生成时间戳
58
+ if created_at is None:
59
+ created_at = get_iso_timestamp()
60
+
61
+
62
+ # 构造消息字典
63
+ item = {
64
+ "role": role,
65
+ "content": content,
66
+ "created_at": created_at
67
+ }
68
+
69
+ if extra:
70
+ item["extra"] = extra
71
+
72
+ self.messages.append(item)
73
+ return self
74
+
75
+ def delete(self, index: int = None):
76
+ """
77
+
78
+ :param index: 索引,若不填默认删除最后一个
79
+ :return:
80
+ """
81
+
82
+ if index is None:
83
+ self.messages.pop()
84
+ else:
85
+ self.messages.pop(index)
86
+ return self
87
+
88
+
89
+
90
+ def load_entry(self,entry: dict) -> NoToolConversation :
91
+ """加载整个 conversation 条目"""
92
+ self.entry = entry
93
+ self.messages = self.entry["reif_content"]
94
+ return self
95
+ def load_messages(self,messages: list) -> NoToolConversation :
96
+ self.messages = messages
97
+ self.entry["reif_content"] = self.messages
98
+ return self
99
+
100
+ def validate_schema(self) -> bool:
101
+ if self.reif_entry is None:
102
+ raise RuntimeError("无会话可校验")
103
+ reif_validate(self.entry)
104
+ validate(instance=self.messages, schema = SCHEMA)
105
+ return True
106
+
@@ -0,0 +1,2 @@
1
+ from .llm import BaseLLM
2
+ from .openai_adapter import BaseLLMAdapter,OpenAIAdapter
@@ -0,0 +1,37 @@
1
+ from typing import Optional,Dict,List,Any
2
+
3
+ from abc import ABC,abstractmethod
4
+
5
+
6
+ class BaseLLM(ABC):
7
+
8
+
9
+ @abstractmethod
10
+
11
+ def call(self,
12
+ messages:List[dict[str,Any]],
13
+ tool_registry:Optional[List[Dict[str, Any]]],
14
+ tool_choice: Optional[str],
15
+ **kwargs)\
16
+ -> Dict[str, Any]:
17
+ """
18
+
19
+ :param messages: 消息列表 参考 ../schema/source/llm/llm_message.json
20
+ :param tool_registry: 工具注册表 参考 ../schema/source/tool/tool_registry.json
21
+ :param tool_choice: 工具选择策略 参考 ../schema/source/llm/tool_choice.json
22
+ :param kwargs: 拓展任意字段
23
+ :return: 参考 ../schema/source/llm/llm_output.json
24
+
25
+ """
26
+ pass
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
@@ -0,0 +1,235 @@
1
+ """
2
+ LLM Provider 适配器:框架格式 ↔ 各 Provider 格式
3
+
4
+ 用法:
5
+ adapter = OpenAIAdapter(model="gpt-4", api_key="...")
6
+ openai_params = adapter.to_provider(messages, tool_registry, tool_choice)
7
+ # 调用 OpenAI API...
8
+ llm_output = adapter.from_provider(openai_response)
9
+ """
10
+ from abc import ABC, abstractmethod
11
+ from typing import Any, Dict, List, Optional, Union
12
+
13
+
14
+ # ─────────────────────────────────────────────────────────────────────────────
15
+ # 基类
16
+ # ─────────────────────────────────────────────────────────────────────────────
17
+
18
+ class BaseLLMAdapter(ABC):
19
+ """LLM 适配器基类,所有 Provider 适配器需继承此类"""
20
+
21
+ @abstractmethod
22
+ def to_provider(
23
+ self,
24
+ messages: List[Dict[str, Any]],
25
+ tool_registry: Optional[Dict[str, Any]] = None,
26
+ tool_choice: Optional[Union[str, Dict]] = None,
27
+ **kwargs
28
+ ) -> Dict[str, Any]:
29
+ """
30
+ 框架格式 → Provider 格式
31
+
32
+ :param messages: 框架消息列表 (llm_message[])
33
+ :param tool_registry: 框架工具注册表
34
+ :param tool_choice: 工具选择策略
35
+ :return: Provider API 调用参数
36
+ """
37
+ pass
38
+
39
+ @abstractmethod
40
+ def from_provider(self, response: Dict[str, Any]) -> Dict[str, Any]:
41
+ """
42
+ Provider 响应格式 → 框架格式 (llm_output)
43
+
44
+ :param response: Provider API 返回的原始响应
45
+ :return: 框架 llm_output 格式
46
+ """
47
+ pass
48
+
49
+ @abstractmethod
50
+ def message_to_frame(self, message: Dict[str, Any]) -> Dict[str, Any]:
51
+ """
52
+ 单条 Provider 消息 → 框架格式 (llm_message)
53
+
54
+ 用于把 Provider 返回的消息转成框架格式后再存入 messages 列表
55
+
56
+ :param message: Provider 格式的单条消息
57
+ :return: 框架 llm_message 格式
58
+ """
59
+ pass
60
+
61
+
62
+ # ─────────────────────────────────────────────────────────────────────────────
63
+ # OpenAI 适配器
64
+ # ─────────────────────────────────────────────────────────────────────────────
65
+
66
+ class OpenAIAdapter(BaseLLMAdapter):
67
+ """OpenAI 兼容 API 适配器"""
68
+
69
+ def __init__(
70
+ self,
71
+ model: str = "gpt-4",
72
+ **config
73
+ ):
74
+ """
75
+ :param model: 模型名称
76
+ :param config: 其他 OpenAI API 配置(temperature, timeout 等)
77
+ """
78
+ self.model = model
79
+ self.config = config
80
+
81
+ def to_provider(
82
+ self,
83
+ messages: List[Dict[str, Any]],
84
+ tool_registry: Optional[Dict[str, Any]] = None,
85
+ tool_choice: Optional[Union[str, Dict]] = None,
86
+ **kwargs
87
+ ) -> Dict[str, Any]:
88
+ result = {
89
+ "model": self.model,
90
+ "messages": self._convert_messages(messages),
91
+ }
92
+
93
+ if tool_registry:
94
+ result["tools"] = self._convert_tool_registry(tool_registry)
95
+
96
+ if tool_choice is not None:
97
+ result["tool_choice"] = self._convert_tool_choice(tool_choice)
98
+
99
+ result.update(self.config)
100
+ result.update(kwargs)
101
+ return result
102
+
103
+ def from_provider(self, response: Dict[str, Any]) -> Dict[str, Any]:
104
+ content = response.get("content")
105
+ tool_calls = response.get("tool_calls")
106
+
107
+ if content is None and not tool_calls:
108
+ llm_content = None
109
+ else:
110
+ llm_content = content
111
+
112
+ llm_tool_calls = None
113
+ if tool_calls:
114
+ llm_tool_calls = self._convert_tool_calls_from_openai(tool_calls)
115
+
116
+ return {
117
+ "content": llm_content,
118
+ "tool_calls": llm_tool_calls,
119
+ "usage": self._normalize_usage(response.get("usage")),
120
+ "created_at": response.get("created") or response.get("created_at"),
121
+ }
122
+
123
+ def message_to_frame(self, message: Dict[str, Any]) -> Dict[str, Any]:
124
+ role = message["role"]
125
+ frame_msg = {
126
+ "role": role,
127
+ "created_at": message.get("created_at") or message.get("created"),
128
+ }
129
+
130
+ if role == "tool":
131
+ frame_msg["tool_call_id"] = message["tool_call_id"]
132
+ frame_msg["content"] = {
133
+ "call_id": message["tool_call_id"],
134
+ "type": "function",
135
+ "function": {
136
+ "result_content": message["content"],
137
+ "status": "success",
138
+ },
139
+ "created_at": frame_msg["created_at"],
140
+ }
141
+ else:
142
+ frame_msg["content"] = message.get("content")
143
+
144
+ if role == "assistant" and message.get("tool_calls"):
145
+ frame_msg["tool_calls"] = self._convert_tool_calls_from_openai(message["tool_calls"])
146
+
147
+ return frame_msg
148
+
149
+ # ─────────────────────────────────────────────────────────────────────────
150
+ # 内部转换方法
151
+ # ─────────────────────────────────────────────────────────────────────────
152
+
153
+ def _convert_messages(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
154
+ """框架消息列表 → OpenAI 消息列表"""
155
+ openai_messages = []
156
+ for msg in messages:
157
+ role = msg["role"]
158
+ openai_msg = {"role": role}
159
+
160
+ content = msg.get("content")
161
+ if role == "tool":
162
+ if isinstance(content, dict):
163
+ openai_msg["content"] = content.get("result_content", "")
164
+ else:
165
+ openai_msg["content"] = content or ""
166
+ else:
167
+ openai_msg["content"] = content
168
+
169
+ if role == "assistant" and msg.get("tool_calls"):
170
+ openai_msg["tool_calls"] = self._convert_tool_calls_to_openai(msg["tool_calls"])
171
+
172
+ openai_messages.append(openai_msg)
173
+ return openai_messages
174
+
175
+ def _convert_tool_calls_to_openai(self, tool_calls: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
176
+ """框架 tool_calls → OpenAI tool_calls"""
177
+ return [
178
+ {
179
+ "id": call["id"],
180
+ "type": call["type"],
181
+ "function": {
182
+ "name": call["function"]["registry_id"],
183
+ "arguments": call["function"]["arguments"],
184
+ },
185
+ }
186
+ for call in tool_calls
187
+ ]
188
+
189
+ def _convert_tool_calls_from_openai(self, tool_calls: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
190
+ """OpenAI tool_calls → 框架 tool_calls"""
191
+ from ..utils.time import get_iso_timestamp
192
+ created_at = get_iso_timestamp()
193
+ return [
194
+ {
195
+ "id": call["id"],
196
+ "type": call["type"],
197
+ "function": {
198
+ "registry_id": call["function"]["name"],
199
+ "arguments": call["function"]["arguments"],
200
+ },
201
+ "created_at": created_at,
202
+ }
203
+ for call in tool_calls
204
+ ]
205
+
206
+ def _convert_tool_registry(self, tool_registry: Dict[str, Any]) -> List[Dict[str, Any]]:
207
+ """框架工具注册表 → OpenAI tools"""
208
+ openai_tools = []
209
+ for registry_id, tool_info in tool_registry.items():
210
+ func_info = tool_info.get("function", {})
211
+ openai_tools.append({
212
+ "type": "function",
213
+ "function": {
214
+ "name": registry_id,
215
+ "description": func_info.get("description", ""),
216
+ "parameters": func_info.get("parameters", {"type": "object", "properties": {}}),
217
+ },
218
+ })
219
+ return openai_tools
220
+
221
+ def _convert_tool_choice(self, tool_choice: Union[str, Dict]) -> Union[str, Dict]:
222
+ """工具选择策略转换"""
223
+ if isinstance(tool_choice, str):
224
+ return tool_choice
225
+ return tool_choice
226
+
227
+ def _normalize_usage(self, usage: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
228
+ """标准化 usage"""
229
+ if usage is None:
230
+ return None
231
+ return {
232
+ "prompt_tokens": usage.get("prompt_tokens", 0),
233
+ "completion_tokens": usage.get("completion_tokens", 0),
234
+ "total_tokens": usage.get("total_tokens", 0),
235
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "reif_content_base_conversation",
4
+ "description": "基础对话消息列表(无工具字段)",
5
+ "type": "array",
6
+ "items": {
7
+ "type": "object",
8
+ "properties": {
9
+ "role": {
10
+ "type": "string",
11
+ "enum": [
12
+ "system",
13
+ "user",
14
+ "assistant"
15
+ ]
16
+ },
17
+ "content": {
18
+ "type": [
19
+ "string",
20
+ "null"
21
+ ]
22
+ },
23
+ "created_at": {
24
+ "type": "string",
25
+ "format": "date-time"
26
+ },
27
+ "extra": {
28
+ "type": [
29
+ "object",
30
+ "null"
31
+ ],
32
+ "additionalProperties": true
33
+ }
34
+ },
35
+ "required": [
36
+ "role",
37
+ "created_at"
38
+ ]
39
+ }
40
+ }
@@ -0,0 +1,96 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "llm_message",
4
+ "description": "LLM 对话消息单条格式,与 OpenAI 格式解耦",
5
+ "type": "object",
6
+ "properties": {
7
+ "role": {
8
+ "type": "string",
9
+ "enum": ["system", "user", "assistant", "tool"],
10
+ "description": "消息角色"
11
+ },
12
+ "content": {
13
+ "description": "消息内容",
14
+ "oneOf": [
15
+ {
16
+ "type": "null",
17
+ "description": "assistant 无文字内容时(如纯工具调用)"
18
+ },
19
+ {
20
+ "type": "string",
21
+ "description": "system/user/assistant 普通文字内容"
22
+ },
23
+ {
24
+ "type": "object",
25
+ "description": "tool 角色时为 tool_result 对象"
26
+ }
27
+ ]
28
+ },
29
+ "tool_calls": {
30
+ "type": "array",
31
+ "description": "LLM 工具调用列表,仅 assistant 消息可能包含",
32
+ "items": {
33
+ "$ref": "../tool/tool_call.json"
34
+ }
35
+ },
36
+ "tool_call_id": {
37
+ "type": "string",
38
+ "pattern": "^[0-9a-fA-F]{32}$",
39
+ "description": "tool 角色必填,关联到 assistant 消息 tool_calls 中对应 call 的 id"
40
+ },
41
+ "created_at": {
42
+ "type": "string",
43
+ "format": "date-time",
44
+ "description": "创建时间 ISO 8601"
45
+ },
46
+ "extra": {
47
+ "type": ["object", "null"],
48
+ "additionalProperties": true,
49
+ "description": "扩展数据"
50
+ }
51
+ },
52
+ "required": ["role", "created_at"],
53
+ "allOf": [
54
+ {
55
+ "if": {
56
+ "properties": {
57
+ "role": {
58
+ "const": "tool"
59
+ }
60
+ }
61
+ },
62
+ "then": {
63
+ "properties": {
64
+ "content": {
65
+ "type": "object",
66
+ "description": "tool 角色的 content 必须为 tool_result 对象"
67
+ },
68
+ "tool_call_id": {
69
+ "type": "string",
70
+ "pattern": "^[0-9a-fA-F]{32}$"
71
+ }
72
+ },
73
+ "required": ["tool_call_id"]
74
+ }
75
+ },
76
+ {
77
+ "if": {
78
+ "properties": {
79
+ "role": {
80
+ "const": "assistant"
81
+ }
82
+ }
83
+ },
84
+ "then": {
85
+ "properties": {
86
+ "tool_calls": {
87
+ "type": "array",
88
+ "items": {
89
+ "$ref": "tool/tool_call.json"
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ ]
96
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "llm_output",
4
+ "description": "LLM 单次调用的返回格式",
5
+ "type": "object",
6
+ "properties": {
7
+ "content": {
8
+ "description": "LLM 回复的文字内容,无文字时为 null(纯工具调用场景)",
9
+ "oneOf": [
10
+ { "type": "null" },
11
+ { "type": "string" }
12
+ ]
13
+ },
14
+ "tool_calls": {
15
+ "type": "array",
16
+ "description": "LLM 发起的工具调用列表,无工具调用时为 null",
17
+ "items": {
18
+ "$ref": "../tool/tool_call.json"
19
+ }
20
+ },
21
+ "usage": {
22
+ "type": "object",
23
+ "description": "Token 用量统计",
24
+ "properties": {
25
+ "prompt_tokens": {
26
+ "type": "integer",
27
+ "description": "输入 prompt 消耗的 token 数"
28
+ },
29
+ "completion_tokens": {
30
+ "type": "integer",
31
+ "description": "生成回复消耗的 token 数"
32
+ },
33
+ "total_tokens": {
34
+ "type": "integer",
35
+ "description": "总 token 数"
36
+ }
37
+ }
38
+ },
39
+ "created_at": {
40
+ "type": "string",
41
+ "format": "date-time",
42
+ "description": "LLM 返回时间,ISO 8601 格式"
43
+ },
44
+ "extra": {
45
+ "type": ["object", "null"],
46
+ "additionalProperties": true,
47
+ "description": "扩展数据"
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": " Tool Choice Schema(仿openai,其中function.name 改成了 func.registry_id)",
4
+ "description": "Controls how the model uses tools. Can be a simple string mode or an object specifying a forced function call.",
5
+ "type": ["string", "object"],
6
+ "oneOf": [
7
+ {
8
+ "title": "Simple Mode",
9
+ "type": "string",
10
+ "enum": ["auto", "none", "required"],
11
+ "description": "Auto: model decides; None: no tool call; Required: model must call at least one tool."
12
+ },
13
+ {
14
+ "title": "Specific Function Call",
15
+ "type": "object",
16
+ "properties": {
17
+ "type": {
18
+ "type": "string",
19
+ "const": "function",
20
+ "description": "Must be 'function'."
21
+ },
22
+ "function": {
23
+ "type": "object",
24
+ "properties": {
25
+ "registry_id": {
26
+ "type": "string",
27
+ "format": "uuid",
28
+ "description": "tool 在注册表的 registry_id "
29
+ }
30
+ },
31
+ "required": ["registry_id"],
32
+ "additionalProperties": false
33
+ }
34
+ },
35
+ "required": ["type", "function"],
36
+ "additionalProperties": false
37
+ }
38
+ ]
39
+ }