polaris-python 0.0.1.dev3__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 (84) hide show
  1. polaris/__init__.py +74 -0
  2. polaris/_version.py +16 -0
  3. polaris/api/__init__.py +55 -0
  4. polaris/api/_log_file.py +232 -0
  5. polaris/api/config.py +145 -0
  6. polaris/api/consumer.py +240 -0
  7. polaris/api/log.py +414 -0
  8. polaris/api/lossless.py +73 -0
  9. polaris/api/provider.py +152 -0
  10. polaris/api/skill.py +162 -0
  11. polaris/pkg/__init__.py +15 -0
  12. polaris/pkg/common/__init__.py +15 -0
  13. polaris/pkg/common/plugin.py +33 -0
  14. polaris/pkg/config/__init__.py +15 -0
  15. polaris/pkg/config/api.py +1205 -0
  16. polaris/pkg/config/default.py +92 -0
  17. polaris/pkg/config/event.py +76 -0
  18. polaris/pkg/config/loader.py +392 -0
  19. polaris/pkg/config/stat_reporter.py +100 -0
  20. polaris/pkg/flow/__init__.py +15 -0
  21. polaris/pkg/flow/admin_server.py +104 -0
  22. polaris/pkg/flow/async_flow.py +55 -0
  23. polaris/pkg/flow/base_flow.py +111 -0
  24. polaris/pkg/flow/impl.py +1277 -0
  25. polaris/pkg/flow/lossless.py +605 -0
  26. polaris/pkg/flow/notify.py +46 -0
  27. polaris/pkg/flow/register.py +210 -0
  28. polaris/pkg/flow/skill.py +110 -0
  29. polaris/pkg/flow/sync_flow.py +20 -0
  30. polaris/pkg/model/__init__.py +15 -0
  31. polaris/pkg/model/context.py +53 -0
  32. polaris/pkg/model/contract.py +89 -0
  33. polaris/pkg/model/core.py +51 -0
  34. polaris/pkg/model/engine.py +83 -0
  35. polaris/pkg/model/error.py +94 -0
  36. polaris/pkg/model/event.py +159 -0
  37. polaris/pkg/model/lossless.py +102 -0
  38. polaris/pkg/model/rule.py +152 -0
  39. polaris/pkg/model/server.py +288 -0
  40. polaris/pkg/model/service.py +820 -0
  41. polaris/pkg/model/skill.py +179 -0
  42. polaris/pkg/model/stat.py +168 -0
  43. polaris/pkg/model/util.py +58 -0
  44. polaris/pkg/version/__init__.py +18 -0
  45. polaris/plugin/__init__.py +32 -0
  46. polaris/plugin/circuit_breaker/__init__.py +19 -0
  47. polaris/plugin/circuit_breaker/base.py +54 -0
  48. polaris/plugin/circuit_breaker/composite.py +265 -0
  49. polaris/plugin/circuit_breaker/config.py +36 -0
  50. polaris/plugin/event/__init__.py +21 -0
  51. polaris/plugin/event/pushgateway.py +202 -0
  52. polaris/plugin/load_balancer/__init__.py +24 -0
  53. polaris/plugin/load_balancer/base.py +30 -0
  54. polaris/plugin/load_balancer/random.py +39 -0
  55. polaris/plugin/load_balancer/weighted_random.py +50 -0
  56. polaris/plugin/local_registry/__init__.py +18 -0
  57. polaris/plugin/local_registry/base.py +47 -0
  58. polaris/plugin/local_registry/file_cache.py +335 -0
  59. polaris/plugin/local_registry/local_cache.py +433 -0
  60. polaris/plugin/server_connector/__init__.py +20 -0
  61. polaris/plugin/server_connector/base.py +104 -0
  62. polaris/plugin/server_connector/convert.py +149 -0
  63. polaris/plugin/server_connector/grpc_connector/__init__.py +21 -0
  64. polaris/plugin/server_connector/grpc_connector/connection.py +401 -0
  65. polaris/plugin/server_connector/grpc_connector/grpc_client.py +384 -0
  66. polaris/plugin/server_connector/grpc_connector/tls.py +45 -0
  67. polaris/plugin/service_router/__init__.py +28 -0
  68. polaris/plugin/service_router/base.py +105 -0
  69. polaris/plugin/service_router/isolated.py +35 -0
  70. polaris/plugin/service_router/metadata.py +84 -0
  71. polaris/plugin/service_router/recover.py +62 -0
  72. polaris/plugin/service_router/util.py +54 -0
  73. polaris/plugin/skill/__init__.py +16 -0
  74. polaris/plugin/skill/connector.py +127 -0
  75. polaris/plugin/skill/persistence.py +94 -0
  76. polaris/plugin/stat_reporter/__init__.py +16 -0
  77. polaris/plugin/stat_reporter/base.py +34 -0
  78. polaris/plugin/stat_reporter/prometheus.py +375 -0
  79. polaris/plugin_manager.py +146 -0
  80. polaris_python-0.0.1.dev3.dist-info/METADATA +213 -0
  81. polaris_python-0.0.1.dev3.dist-info/RECORD +84 -0
  82. polaris_python-0.0.1.dev3.dist-info/WHEEL +5 -0
  83. polaris_python-0.0.1.dev3.dist-info/licenses/LICENSE +18 -0
  84. polaris_python-0.0.1.dev3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,210 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
16
+ """Instance heartbeat scheduling and Java-compatible re-registration."""
17
+
18
+ from __future__ import annotations
19
+
20
+ import asyncio
21
+ import copy
22
+ import random
23
+ from collections.abc import Awaitable, Callable
24
+ from dataclasses import dataclass, field
25
+ from weakref import WeakValueDictionary
26
+
27
+ import polaris.api.log as logger
28
+ from polaris.pkg.model.error import SDKError, ServerCode
29
+ from polaris.pkg.model.server import (
30
+ InstanceDeRegisterRequest,
31
+ InstanceHeartbeatRequest,
32
+ InstanceRegisterRequest,
33
+ InstanceRegisterResponse,
34
+ )
35
+
36
+ InstanceKey = tuple[str, str, str, int]
37
+ base_logger = logger.get_logger("base")
38
+
39
+
40
+ def _instance_key(request: InstanceRegisterRequest | InstanceDeRegisterRequest) -> InstanceKey:
41
+ return request.namespace, request.service, request.host, request.port
42
+
43
+
44
+ @dataclass
45
+ class RegisterState:
46
+ """Keep a private request snapshot and the tasks for one registered instance."""
47
+
48
+ request: InstanceRegisterRequest
49
+ first_register_time: float
50
+ heartbeat_task: asyncio.Task[None] | None = None
51
+ re_register_task: asyncio.Task[None] | None = None
52
+ heartbeat_fail_count: int = 0
53
+ re_register_count: int = 0
54
+ instance_ids: set[str] = field(default_factory=set)
55
+
56
+
57
+ class RegisterStateManager:
58
+ """Own registration tasks on the SDK loop and cancel them before deregistration."""
59
+
60
+ def __init__(
61
+ self,
62
+ register: Callable[[InstanceRegisterRequest], Awaitable[InstanceRegisterResponse]],
63
+ heartbeat: Callable[[InstanceHeartbeatRequest], Awaitable[None]],
64
+ min_register_interval: int,
65
+ ) -> None:
66
+ self._register = register
67
+ self._heartbeat = heartbeat
68
+ self._min_register_interval = min_register_interval / 1000
69
+ self._states: dict[InstanceKey, RegisterState] = {}
70
+ self._locks: WeakValueDictionary[InstanceKey, asyncio.Lock] = WeakValueDictionary()
71
+ self._closed = False
72
+
73
+ def _key_for(self, request: InstanceRegisterRequest | InstanceDeRegisterRequest) -> InstanceKey:
74
+ if request.instance_id:
75
+ for key, state in self._states.items():
76
+ if request.instance_id in state.instance_ids:
77
+ return key
78
+ return _instance_key(request)
79
+
80
+ def operation_lock(self, request: InstanceRegisterRequest | InstanceDeRegisterRequest) -> asyncio.Lock:
81
+ """Serialize explicit registration and deregistration for the same instance."""
82
+ key = self._key_for(request)
83
+ lock = self._locks.get(key)
84
+ if lock is None:
85
+ lock = asyncio.Lock()
86
+ self._locks[key] = lock
87
+ return lock
88
+
89
+ async def put(self, request: InstanceRegisterRequest) -> None:
90
+ """Replace an instance's schedule without creating duplicate heartbeat tasks."""
91
+ previous = self._states.pop(_instance_key(request), None)
92
+ if previous is not None:
93
+ await self._cancel([previous])
94
+ if self._closed:
95
+ return
96
+ state = RegisterState(copy.deepcopy(request), asyncio.get_running_loop().time())
97
+ state.instance_ids.add(request.instance_id)
98
+ self._states[_instance_key(request)] = state
99
+ state.heartbeat_task = asyncio.create_task(self._run_heartbeat(state))
100
+ base_logger.info(
101
+ "instance heartbeat task started namespace=%s service=%s host=%s port=%s ttl=%ss",
102
+ request.namespace, request.service, request.host, request.port, request.ttl,
103
+ )
104
+
105
+ async def remove(
106
+ self, request: InstanceRegisterRequest | InstanceDeRegisterRequest,
107
+ ) -> InstanceRegisterRequest | None:
108
+ """Cancel heartbeats and pending re-registration by instance ID or address."""
109
+ key = self._key_for(request)
110
+ state = self._states.get(key)
111
+ if state is None:
112
+ return
113
+ # An explicit ID takes precedence over possibly stale address fields.
114
+ if request.instance_id and request.instance_id not in state.instance_ids:
115
+ return
116
+ self._states.pop(key)
117
+ await self._cancel([state])
118
+ return state.request
119
+
120
+ async def close(self) -> None:
121
+ """Stop and await all tasks before the SDK closes its connector."""
122
+ self._closed = True
123
+ states = list(self._states.values())
124
+ self._states.clear()
125
+ await self._cancel(states)
126
+
127
+ @staticmethod
128
+ async def _cancel(states: list[RegisterState]) -> None:
129
+ tasks = [task for state in states for task in (state.heartbeat_task, state.re_register_task)
130
+ if task is not None]
131
+ for state in states:
132
+ if state.heartbeat_task is None:
133
+ continue
134
+ request = state.request
135
+ base_logger.info(
136
+ "instance heartbeat task stopped namespace=%s service=%s host=%s port=%s",
137
+ request.namespace, request.service, request.host, request.port,
138
+ )
139
+ for task in tasks:
140
+ task.cancel()
141
+ if tasks:
142
+ await asyncio.gather(*tasks, return_exceptions=True)
143
+
144
+ def _active(self, state: RegisterState) -> bool:
145
+ return not self._closed and self._states.get(_instance_key(state.request)) is state
146
+
147
+ async def _run_heartbeat(self, state: RegisterState) -> None:
148
+ loop = asyncio.get_running_loop()
149
+ interval = state.request.ttl
150
+ next_tick = loop.time() + interval
151
+ while self._active(state):
152
+ await asyncio.sleep(max(0, next_tick - loop.time()))
153
+ if not self._active(state):
154
+ return
155
+ await self._heartbeat_once(state)
156
+ next_tick += interval
157
+ # Skip missed ticks; do not overlap RPCs or burst after a slow request.
158
+ if next_tick < loop.time():
159
+ next_tick = loop.time() + interval
160
+
161
+ async def _heartbeat_once(self, state: RegisterState) -> None:
162
+ request = state.request
163
+ heartbeat = InstanceHeartbeatRequest(
164
+ request.service, request.service_token, request.namespace, request.instance_id,
165
+ request.host, request.port,
166
+ )
167
+ try:
168
+ await self._heartbeat(heartbeat)
169
+ state.heartbeat_fail_count = 0
170
+ base_logger.debug(
171
+ "instance heartbeat success namespace=%s service=%s host=%s port=%s",
172
+ request.namespace, request.service, request.host, request.port,
173
+ )
174
+ except Exception as error:
175
+ if isinstance(error, SDKError) and error.server_code == ServerCode.NotFoundResource:
176
+ state.heartbeat_fail_count += 1
177
+ else:
178
+ state.heartbeat_fail_count = 0
179
+ logger.error("instance heartbeat failed for %s: %s", _instance_key(request), error)
180
+
181
+ if (not self._active(state) or state.heartbeat_fail_count < 2
182
+ or asyncio.get_running_loop().time() - state.first_register_time < self._min_register_interval):
183
+ return
184
+ if state.re_register_task is not None and not state.re_register_task.done():
185
+ return
186
+ attempt = state.re_register_count
187
+ delay = 0 if attempt == 0 else min(
188
+ request.ttl * 2 ** min(attempt - 1, 6) + random.randrange(request.ttl), 60,
189
+ )
190
+ state.re_register_count += 1
191
+ state.re_register_task = asyncio.create_task(self._re_register(state, delay))
192
+
193
+ async def _re_register(self, state: RegisterState, delay: float) -> None:
194
+ if delay:
195
+ await asyncio.sleep(delay)
196
+ if not self._active(state):
197
+ return
198
+ try:
199
+ response = await self._register(state.request)
200
+ state.request.instance_id = response.instance_id
201
+ state.instance_ids.add(response.instance_id)
202
+ state.heartbeat_fail_count = 0
203
+ state.re_register_count = 0
204
+ base_logger.info(
205
+ "instance re-registered namespace=%s service=%s host=%s port=%s instanceId=%s",
206
+ state.request.namespace, state.request.service,
207
+ state.request.host, state.request.port, response.instance_id,
208
+ )
209
+ except Exception as error:
210
+ logger.warning("instance re-registration failed for %s: %s", _instance_key(state.request), error)
@@ -0,0 +1,110 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
16
+ """Read-only Skill orchestration with network-error persistence fallback."""
17
+
18
+ from dataclasses import fields
19
+
20
+ from polaris.pkg.model.error import ErrorCode, SDKError, ServerCode
21
+ from polaris.pkg.model.skill import (
22
+ SkillDownloadResponse, SkillGetResponse, SkillListResponse, SkillResource,
23
+ SkillResourceVersion, SkillStorageInfo, SkillVersionInfo,
24
+ )
25
+ from polaris.plugin.skill.persistence import SkillPersistentHandler
26
+
27
+
28
+ def _resource(proto):
29
+ values = {item.name: getattr(proto, item.name) for item in fields(SkillResource)}
30
+ values.update(tags=list(proto.tags), examples=list(proto.examples), ext=dict(proto.ext),
31
+ version_info=SkillVersionInfo(proto.version_info.active_version,
32
+ proto.version_info.latest_version, proto.version_info.total_versions))
33
+ return SkillResource(**values)
34
+
35
+
36
+ def _version(proto):
37
+ values = {item.name: getattr(proto, item.name) for item in fields(SkillResourceVersion)}
38
+ values["storage"] = SkillStorageInfo(proto.storage.provider, list(proto.storage.files),
39
+ proto.storage.scope, proto.storage.content_digest)
40
+ return SkillResourceVersion(**values)
41
+
42
+
43
+ class SkillFlow:
44
+ """Run Skill reads on a context's owner loop and persist successful responses."""
45
+
46
+ def __init__(self, connector, config, logger):
47
+ self.connector = connector
48
+ self.config = config
49
+ self.logger = logger
50
+ self.persistence = None
51
+ if config.persist_enable:
52
+ try:
53
+ self.persistence = SkillPersistentHandler(config, logger)
54
+ except OSError as error:
55
+ self.logger.warning("Skill backup initialization failed; using remote reads only: %s", error)
56
+
57
+ async def _read(self, request, operation):
58
+ loaded = False
59
+ try:
60
+ method = self.connector.get_skill if operation == "get" else self.connector.download_skill
61
+ result = await method(request)
62
+ except SDKError as error:
63
+ if (error.err_code != ErrorCode.ErrCodeNetworkError or not self.config.fallback_to_local_cache
64
+ or self.persistence is None):
65
+ raise
66
+ result = await self.persistence.load(request, operation)
67
+ if result is None or result.code not in (0, ServerCode.ExecuteSuccess):
68
+ raise error
69
+ loaded = True
70
+ self.logger.warning("use local Skill backup for %s/%s", request.namespace, request.name)
71
+ if loaded:
72
+ version = result.version.version if operation == "get" else result.version
73
+ if (not version or (request.version and request.version != version)
74
+ or (operation == "get" and (result.resource.name != request.name
75
+ or result.resource.namespace != request.namespace))):
76
+ raise SDKError(ErrorCode.ErrCodeInvalidResponse, "Skill backup identity or version mismatch")
77
+ if operation == "download" and result.total_size:
78
+ size = len(result.zip_chunk) if request.format == "zip" else len(result.content.encode("utf-8"))
79
+ if size != result.total_size:
80
+ raise SDKError(ErrorCode.ErrCodeInvalidResponse, "Skill backup size mismatch")
81
+ if not loaded and result.code in (0, ServerCode.ExecuteSuccess) and self.persistence is not None:
82
+ try:
83
+ self.persistence.save_success(request, result, operation)
84
+ except (OSError, SDKError) as error:
85
+ self.logger.warning("failed to persist Skill %s/%s: %s", request.namespace, request.name, error)
86
+ return result, loaded
87
+
88
+ async def get_skill(self, request):
89
+ """Fetch published content with network-only local fallback."""
90
+ response, loaded = await self._read(request, "get")
91
+ return SkillGetResponse(_resource(response.resource), _version(response.version), response.content,
92
+ loaded, response.code, response.info)
93
+
94
+ async def list_skills(self, request):
95
+ """Fetch remote search results without storing or falling back to a list."""
96
+ response = await self.connector.list_skills(request)
97
+ return SkillListResponse(response.total, [_resource(item) for item in response.resources],
98
+ response.code, response.info)
99
+
100
+ async def download_skill(self, request):
101
+ """Return one complete download with network-only local fallback."""
102
+ response, loaded = await self._read(request, "download")
103
+ return SkillDownloadResponse(response.version, response.content_type, response.content,
104
+ response.zip_chunk, response.total_size, response.filename, loaded,
105
+ response.code, response.info)
106
+
107
+ def destroy(self):
108
+ """Drain pending backups when the context closes."""
109
+ if self.persistence is not None:
110
+ self.persistence.destroy()
@@ -0,0 +1,20 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
16
+ from polaris.pkg.flow.impl import Engine
17
+
18
+
19
+ class SyncEngine(Engine):
20
+ """Legacy name for the Engine's shared synchronous and asynchronous flows."""
@@ -0,0 +1,15 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
@@ -0,0 +1,53 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
16
+ from threading import RLock
17
+ from uuid import uuid4
18
+
19
+ ContextKeyEngine = "engine"
20
+ ContextKeyEventCenter = "event_center"
21
+
22
+
23
+ class ValueContext:
24
+ """Hold identity and thread-safe values belonging to one SDK context."""
25
+
26
+ def __init__(self):
27
+ self.client_id = str(uuid4())
28
+ self.logger = None
29
+ self.core_map = {}
30
+ self._lock = RLock()
31
+
32
+ def set_value(self, key: str, value):
33
+ """Store a value without touching any other SDK context."""
34
+ with self._lock:
35
+ self.core_map[key] = value
36
+
37
+ def get_value(self, key: str):
38
+ """Read a value owned by this context."""
39
+ with self._lock:
40
+ return self.core_map[key]
41
+
42
+ def get_client_id(self) -> str:
43
+ """Return the stable, unique identity used in ReportClient."""
44
+ return self.client_id
45
+
46
+ def clear(self):
47
+ """Release runtime references after context destruction."""
48
+ with self._lock:
49
+ self.core_map.clear()
50
+
51
+
52
+ def new_value_context():
53
+ return ValueContext()
@@ -0,0 +1,89 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
16
+ """Framework-independent service contract models."""
17
+
18
+ from dataclasses import dataclass, field
19
+ from enum import IntEnum
20
+
21
+ from polarismesh_specification.api.v1.service_manage import contract_pb2
22
+
23
+ from polaris.pkg.model.error import ErrorCode, SDKError
24
+
25
+
26
+ class ServiceFeatureType(IntEnum):
27
+ """Capability categories defined by the service contract protocol."""
28
+
29
+ MCP_TOOL = contract_pb2.Service_Feature_Type_MCP_Tool
30
+ MCP_RESOURCE = contract_pb2.Service_Feature_Type_MCP_Resource
31
+ MCP_PROMPT = contract_pb2.Service_Feature_Type_MCP_Prompt
32
+
33
+
34
+ class ServiceFeatureStatus(IntEnum):
35
+ """Availability of a published service capability."""
36
+
37
+ ENABLED = contract_pb2.Service_Feature_Status_Enabled
38
+ DISABLED = contract_pb2.Service_Feature_Status_Disabled
39
+
40
+
41
+ @dataclass
42
+ class ServiceFeature:
43
+ """One tool, resource or prompt in a service contract."""
44
+
45
+ type: int
46
+ name: str
47
+ description: str = ""
48
+ content: str = ""
49
+ status: int = ServiceFeatureStatus.ENABLED
50
+
51
+
52
+ @dataclass
53
+ class InterfaceDescriptor:
54
+ """One request or notification handler exposed by a service."""
55
+
56
+ name: str = ""
57
+ method: str = ""
58
+ path: str = ""
59
+ content: str = ""
60
+
61
+
62
+ @dataclass
63
+ class GetServiceContractRequest:
64
+ """Identify a service contract by namespace, service, protocol and version."""
65
+
66
+ namespace: str
67
+ service: str
68
+ protocol: str = ""
69
+ version: str = ""
70
+ timeout: int = 0
71
+
72
+ def validate(self):
73
+ """Check the service identity and timeout."""
74
+ if not self.namespace.strip() or not self.service.strip() or self.timeout < 0:
75
+ raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument, "contract namespace/service are required, timeout >=0")
76
+
77
+
78
+ @dataclass
79
+ class ServiceContract(GetServiceContractRequest):
80
+ """A service contract used for reporting and retrieval."""
81
+
82
+ name: str = ""
83
+ revision: str = ""
84
+ content: str = ""
85
+ service_features: list[ServiceFeature] = field(default_factory=list)
86
+ interfaces: list[InterfaceDescriptor] = field(default_factory=list)
87
+
88
+
89
+ ReportServiceContractRequest = ServiceContract
@@ -0,0 +1,51 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
16
+ class EventType:
17
+ Unknown = 0
18
+
19
+ Instance = 0x2001
20
+
21
+ Routing = 0x2002
22
+
23
+
24
+ # 服务的唯一标识key
25
+ class ServiceKey:
26
+ def __init__(self, namespace: str, service: str):
27
+ self.namespace: str = namespace
28
+ self.service: str = service
29
+
30
+ def __hash__(self):
31
+ return hash(self.namespace + self.service)
32
+
33
+ def get_namespace(self):
34
+ return self.namespace
35
+
36
+ def get_service(self):
37
+ return self.service
38
+
39
+
40
+ class ServiceEventKey(ServiceKey):
41
+ def __init__(self, namespace: str, service: str, event_type: int):
42
+ ServiceKey.__init__(self, namespace, service)
43
+ self.event_type = event_type
44
+
45
+ def __eq__(self, other):
46
+ return (self.service == other.service and
47
+ self.namespace == other.namespace and
48
+ self.event_type == other.event_type)
49
+
50
+ def __hash__(self):
51
+ return hash(self.namespace + self.service + str(self.event_type))
@@ -0,0 +1,83 @@
1
+ # Tencent is pleased to support the open source community by making polaris-python available.
2
+ #
3
+ # Copyright (C) 2021 Tencent. All rights reserved.
4
+ #
5
+ # Licensed under the BSD 3-Clause License (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://opensource.org/licenses/BSD-3-Clause
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software distributed
12
+ # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13
+ # CONDITIONS OF ANY KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations under the License.
15
+
16
+ from polaris.pkg.model.service import ServiceCallResult
17
+ from polaris.pkg.model.rule import GetServiceRuleRequest, ServiceRuleResponse
18
+ from polaris.pkg.model.server import (
19
+ InstanceDeRegisterRequest,
20
+ InstanceHeartbeatRequest,
21
+ InstanceRegisterRequest,
22
+ InstanceRegisterResponse,
23
+ )
24
+
25
+
26
+ class BaseEngine(object):
27
+ def destroy(self):
28
+ pass
29
+
30
+ def sync_get_one_instance(self, request):
31
+ pass
32
+
33
+ def sync_get_instances(self, request):
34
+ pass
35
+
36
+ def sync_register(self, request: InstanceRegisterRequest) -> InstanceRegisterResponse:
37
+ pass
38
+
39
+ def sync_deregister(self, request: InstanceDeRegisterRequest) -> None:
40
+ pass
41
+
42
+ async def async_register(self, request: InstanceRegisterRequest) -> InstanceRegisterResponse:
43
+ pass
44
+
45
+ async def async_deregister(self, request: InstanceDeRegisterRequest) -> None:
46
+ pass
47
+
48
+ async def async_get_one_instance(self, request):
49
+ pass
50
+
51
+ async def async_get_instances(self):
52
+ pass
53
+
54
+ def sync_get_services(self):
55
+ pass
56
+
57
+ async def async_get_services(self):
58
+ pass
59
+
60
+ def sync_heartbeat(self, request: InstanceHeartbeatRequest) -> None:
61
+ pass
62
+
63
+ async def async_heartbeat(self, request: InstanceHeartbeatRequest) -> None:
64
+ pass
65
+
66
+ def sync_update_service_call_result(self, req: ServiceCallResult):
67
+ pass
68
+
69
+ def sync_get_service_rule(self, event_type: int, req: GetServiceRuleRequest) -> ServiceRuleResponse:
70
+ pass
71
+
72
+ def sync_lossless_register(self, request) -> None:
73
+ pass
74
+
75
+ async def async_lossless_register(self, request) -> None:
76
+ pass
77
+
78
+ def sync_lossless_deregister(self, request) -> None:
79
+ pass
80
+
81
+ async def async_lossless_deregister(self, request) -> None:
82
+ pass
83
+