hmg-sdk 0.9.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.
- hmg/__init__.py +387 -0
- hmg_sdk-0.9.2.dist-info/METADATA +79 -0
- hmg_sdk-0.9.2.dist-info/RECORD +5 -0
- hmg_sdk-0.9.2.dist-info/WHEEL +5 -0
- hmg_sdk-0.9.2.dist-info/top_level.txt +1 -0
hmg/__init__.py
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""HMG Agent Memory SDK — Community Edition.
|
|
2
|
+
|
|
3
|
+
Wire-safe client types for the HMG agent memory protocol.
|
|
4
|
+
This module contains only the public DTO types needed to interact
|
|
5
|
+
with HMG Community Edition via HTTP or MCP.
|
|
6
|
+
|
|
7
|
+
For the full SDK including observation, vault, and advanced features,
|
|
8
|
+
see the Developer/Enterprise Edition.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field, asdict
|
|
14
|
+
from typing import Any, Literal, TypeAlias
|
|
15
|
+
from urllib import request
|
|
16
|
+
import json
|
|
17
|
+
|
|
18
|
+
ActorKind = Literal["User", "Agent", "Service"]
|
|
19
|
+
AccessLevel = Literal["Public", "Internal", "Restricted"]
|
|
20
|
+
CorrectAction = Literal["negate", "confirm_actual", "confirm_necessary", "demote", "replace"]
|
|
21
|
+
GovernanceAction = Literal["quarantine", "seal", "tombstone", "derive_lesson"]
|
|
22
|
+
RecallViewMode = Literal["normal", "governance", "audit"]
|
|
23
|
+
Modality = Literal["text", "code", "dialogue", "observation"]
|
|
24
|
+
OutputFormat = Literal["compact_yaml", "json", "markdown", "summary"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Context types
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class ScopeSegment:
|
|
33
|
+
"""One segment in a hierarchical scope path."""
|
|
34
|
+
kind: str
|
|
35
|
+
id: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class ScopeRef:
|
|
40
|
+
"""A hierarchical scope reference."""
|
|
41
|
+
tenant_id: str
|
|
42
|
+
path: list[ScopeSegment] = field(default_factory=list)
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def coding_agent(tenant_id: str, workspace: str, repository: str, branch: str) -> "ScopeRef":
|
|
46
|
+
"""Create a software-engineering style scope."""
|
|
47
|
+
return ScopeRef(
|
|
48
|
+
tenant_id=tenant_id,
|
|
49
|
+
path=[
|
|
50
|
+
ScopeSegment(kind="workspace", id=workspace),
|
|
51
|
+
ScopeSegment(kind="repository", id=repository),
|
|
52
|
+
ScopeSegment(kind="branch", id=branch),
|
|
53
|
+
],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class ActorPrincipal:
|
|
59
|
+
kind: ActorKind
|
|
60
|
+
id: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class AuditContext:
|
|
65
|
+
actor: ActorPrincipal
|
|
66
|
+
operation: str
|
|
67
|
+
trace_id: str | None = None
|
|
68
|
+
reason: str | None = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class EffectiveTimeWindow:
|
|
73
|
+
starts_at: str | None = None
|
|
74
|
+
ends_at: str | None = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class ObjectRef:
|
|
79
|
+
object_type: str
|
|
80
|
+
object_id: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class MemoryReferences:
|
|
85
|
+
subjects: list[ObjectRef] = field(default_factory=list)
|
|
86
|
+
artifacts: list[ObjectRef] = field(default_factory=list)
|
|
87
|
+
work_items: list[ObjectRef] = field(default_factory=list)
|
|
88
|
+
decisions: list[ObjectRef] = field(default_factory=list)
|
|
89
|
+
commitments: list[ObjectRef] = field(default_factory=list)
|
|
90
|
+
evidence: list[ObjectRef] = field(default_factory=list)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class RetentionPolicy:
|
|
95
|
+
kind: Literal["keep_indefinitely", "retain_until"] = "keep_indefinitely"
|
|
96
|
+
until: str | None = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class MemoryGovernance:
|
|
101
|
+
sensitivity: list[str] = field(default_factory=list)
|
|
102
|
+
legal_basis: str | None = None
|
|
103
|
+
retention_policy: RetentionPolicy | None = None
|
|
104
|
+
break_glass_role: str | None = None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class MemoryContext:
|
|
109
|
+
"""Unified context attached to memory operations."""
|
|
110
|
+
scope: ScopeRef | None = None
|
|
111
|
+
access_level: AccessLevel | None = None
|
|
112
|
+
policy_tags: list[str] = field(default_factory=list)
|
|
113
|
+
effective_time: EffectiveTimeWindow | None = None
|
|
114
|
+
audit: AuditContext | None = None
|
|
115
|
+
references: MemoryReferences | None = None
|
|
116
|
+
governance: MemoryGovernance | None = None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
# Request types
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
@dataclass
|
|
124
|
+
class MemorizeRequest:
|
|
125
|
+
content: str
|
|
126
|
+
source: str | None = None
|
|
127
|
+
modality: Modality | None = None
|
|
128
|
+
domain_pack_id: str | None = None
|
|
129
|
+
context: MemoryContext | None = None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class RecallRequest:
|
|
134
|
+
query: str
|
|
135
|
+
max_results: int | None = None
|
|
136
|
+
include_negated: bool | None = None
|
|
137
|
+
domain_pack_id: str | None = None
|
|
138
|
+
context: MemoryContext | None = None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class RecallViewRequest(RecallRequest):
|
|
143
|
+
mode: RecallViewMode | None = None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class CorrectRequest:
|
|
148
|
+
target_atom: str
|
|
149
|
+
action: CorrectAction
|
|
150
|
+
reason: str
|
|
151
|
+
new_content: str | None = None
|
|
152
|
+
domain_pack_id: str | None = None
|
|
153
|
+
context: MemoryContext | None = None
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass
|
|
157
|
+
class GovernanceRequest:
|
|
158
|
+
target_atom: str
|
|
159
|
+
action: GovernanceAction
|
|
160
|
+
reason: str
|
|
161
|
+
actor: str | None = None
|
|
162
|
+
lesson_content: str | None = None
|
|
163
|
+
destroy_payload: bool | None = None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class HandoffRequest:
|
|
168
|
+
summary: str
|
|
169
|
+
source: str | None = None
|
|
170
|
+
context: MemoryContext | None = None
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass
|
|
174
|
+
class AgentBriefRequest:
|
|
175
|
+
query: str | None = None
|
|
176
|
+
max_results: int | None = None
|
|
177
|
+
output_format: OutputFormat | None = None
|
|
178
|
+
context: MemoryContext | None = None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
# Response types
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
@dataclass
|
|
186
|
+
class ApiError:
|
|
187
|
+
code: str
|
|
188
|
+
message: str
|
|
189
|
+
details: dict | None = None
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@dataclass
|
|
193
|
+
class MemorizeResponse:
|
|
194
|
+
added_atoms: list[str] = field(default_factory=list)
|
|
195
|
+
snapshot_version: int | None = None
|
|
196
|
+
error: str | None = None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass
|
|
200
|
+
class RecalledAtom:
|
|
201
|
+
id: str
|
|
202
|
+
text: str | None = None
|
|
203
|
+
score: float | None = None
|
|
204
|
+
epistemic_rank: int | None = None
|
|
205
|
+
is_conditional: bool | None = None
|
|
206
|
+
exposure_state: str | None = None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@dataclass
|
|
210
|
+
class RecallResponse:
|
|
211
|
+
narrative: str | None = None
|
|
212
|
+
atoms: list[RecalledAtom] = field(default_factory=list)
|
|
213
|
+
knowledge_gaps: list[str] = field(default_factory=list)
|
|
214
|
+
candidates_considered: int | None = None
|
|
215
|
+
error: str | None = None
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass
|
|
219
|
+
class CorrectResponse:
|
|
220
|
+
success: bool
|
|
221
|
+
message: str
|
|
222
|
+
replacement_atom: str | None = None
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@dataclass
|
|
226
|
+
class GovernanceResponse:
|
|
227
|
+
success: bool
|
|
228
|
+
message: str
|
|
229
|
+
target_atom: str
|
|
230
|
+
lesson_atom: str | None = None
|
|
231
|
+
exposure: str | None = None
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@dataclass
|
|
235
|
+
class AtomHistory:
|
|
236
|
+
atom: dict
|
|
237
|
+
current: dict
|
|
238
|
+
polarity_history: list = field(default_factory=list)
|
|
239
|
+
epistemic_history: list = field(default_factory=list)
|
|
240
|
+
exposure_history: list = field(default_factory=list)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@dataclass
|
|
244
|
+
class StatsResponse:
|
|
245
|
+
atom_count: int
|
|
246
|
+
edge_count: int
|
|
247
|
+
snapshot_version: int
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@dataclass
|
|
251
|
+
class HandoffResponse:
|
|
252
|
+
summary: str
|
|
253
|
+
atoms_stored: int
|
|
254
|
+
scope: str | None = None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@dataclass
|
|
258
|
+
class AgentBriefResponse:
|
|
259
|
+
content: str
|
|
260
|
+
memory_count: int
|
|
261
|
+
decisions: list[str] = field(default_factory=list)
|
|
262
|
+
risks: list[str] = field(default_factory=list)
|
|
263
|
+
next_steps: list[str] = field(default_factory=list)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
# Client
|
|
268
|
+
# ---------------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
@dataclass
|
|
271
|
+
class ApiEnvelope:
|
|
272
|
+
ok: bool
|
|
273
|
+
data: dict | None = None
|
|
274
|
+
error: ApiError | None = None
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
ApiEnvelopeDict: TypeAlias = dict[str, Any]
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def api_envelope_data(envelope: ApiEnvelopeDict) -> Any | None:
|
|
281
|
+
return envelope.get("data")
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def api_envelope_error(envelope: ApiEnvelopeDict) -> dict[str, Any] | None:
|
|
285
|
+
error = envelope.get("error")
|
|
286
|
+
return error if isinstance(error, dict) else None
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class HMGClient:
|
|
290
|
+
"""HTTP client for the HMG memory service.
|
|
291
|
+
|
|
292
|
+
Usage:
|
|
293
|
+
client = HMGClient(base_url="http://localhost:8080")
|
|
294
|
+
client.memorize(content="We chose PostgreSQL for the main database")
|
|
295
|
+
result = client.recall(query="database choice")
|
|
296
|
+
for atom in result.atoms:
|
|
297
|
+
print(atom.content)
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
def __init__(self, base_url: str = "http://localhost:8080", api_key: str | None = None):
|
|
301
|
+
self.base_url = base_url.rstrip("/")
|
|
302
|
+
self.api_key = api_key
|
|
303
|
+
|
|
304
|
+
def _request(self, method: str, path: str, body: dict | None = None) -> dict:
|
|
305
|
+
url = f"{self.base_url}{path}"
|
|
306
|
+
headers = {"Content-Type": "application/json"}
|
|
307
|
+
if self.api_key:
|
|
308
|
+
headers["x-api-key"] = self.api_key
|
|
309
|
+
|
|
310
|
+
data = json.dumps(body).encode() if body else None
|
|
311
|
+
req = request.Request(url, data=data, headers=headers, method=method)
|
|
312
|
+
with request.urlopen(req) as resp:
|
|
313
|
+
return json.loads(resp.read())
|
|
314
|
+
|
|
315
|
+
def memorize(self, content: str, **kwargs) -> MemorizeResponse:
|
|
316
|
+
"""Store a new memory atom."""
|
|
317
|
+
req = MemorizeRequest(content=content, **kwargs)
|
|
318
|
+
envelope = self._request("POST", "/api/memorize", _to_dict(req))
|
|
319
|
+
if envelope.get("data"):
|
|
320
|
+
return MemorizeResponse(**envelope["data"])
|
|
321
|
+
raise _api_error(envelope)
|
|
322
|
+
|
|
323
|
+
def recall(self, query: str, **kwargs) -> RecallResponse:
|
|
324
|
+
"""Recall relevant memories."""
|
|
325
|
+
req = RecallRequest(query=query, **kwargs)
|
|
326
|
+
envelope = self._request("POST", "/api/recall", _to_dict(req))
|
|
327
|
+
if envelope.get("data"):
|
|
328
|
+
data = envelope["data"]
|
|
329
|
+
data["atoms"] = [RecalledAtom(**a) for a in data.get("atoms", [])]
|
|
330
|
+
return RecallResponse(**data)
|
|
331
|
+
raise _api_error(envelope)
|
|
332
|
+
|
|
333
|
+
def recall_view(self, query: str, mode: RecallViewMode = "normal", **kwargs) -> RecallResponse:
|
|
334
|
+
"""Recall memories through a governance-aware view."""
|
|
335
|
+
req = RecallViewRequest(query=query, mode=mode, **kwargs)
|
|
336
|
+
envelope = self._request("POST", "/api/recall_view", _to_dict(req))
|
|
337
|
+
if envelope.get("data"):
|
|
338
|
+
data = envelope["data"]
|
|
339
|
+
data["atoms"] = [RecalledAtom(**a) for a in data.get("atoms", [])]
|
|
340
|
+
return RecallResponse(**data)
|
|
341
|
+
raise _api_error(envelope)
|
|
342
|
+
|
|
343
|
+
def correct(self, target_atom: str, action: CorrectAction, reason: str, **kwargs) -> CorrectResponse:
|
|
344
|
+
"""Correct an existing memory atom."""
|
|
345
|
+
req = CorrectRequest(target_atom=target_atom, action=action, reason=reason, **kwargs)
|
|
346
|
+
envelope = self._request("POST", "/api/correct", _to_dict(req))
|
|
347
|
+
if envelope.get("data"):
|
|
348
|
+
return CorrectResponse(**envelope["data"])
|
|
349
|
+
raise _api_error(envelope)
|
|
350
|
+
|
|
351
|
+
def govern(self, target_atom: str, action: GovernanceAction, reason: str, **kwargs) -> GovernanceResponse:
|
|
352
|
+
"""Apply a governance action to a memory atom."""
|
|
353
|
+
req = GovernanceRequest(target_atom=target_atom, action=action, reason=reason, **kwargs)
|
|
354
|
+
envelope = self._request("POST", f"/api/governance/{action}", _to_dict(req))
|
|
355
|
+
if envelope.get("data"):
|
|
356
|
+
return GovernanceResponse(**envelope["data"])
|
|
357
|
+
raise _api_error(envelope)
|
|
358
|
+
|
|
359
|
+
def history(self, atom_id: str) -> AtomHistory:
|
|
360
|
+
"""Inspect atom correction and governance history."""
|
|
361
|
+
envelope = self._request("GET", f"/api/atom/{atom_id}/history")
|
|
362
|
+
if envelope.get("data"):
|
|
363
|
+
return AtomHistory(**envelope["data"])
|
|
364
|
+
raise _api_error(envelope)
|
|
365
|
+
|
|
366
|
+
def stats(self) -> StatsResponse:
|
|
367
|
+
"""Get memory graph statistics."""
|
|
368
|
+
envelope = self._request("GET", "/api/stats")
|
|
369
|
+
if envelope.get("data"):
|
|
370
|
+
return StatsResponse(**envelope["data"])
|
|
371
|
+
raise _api_error(envelope)
|
|
372
|
+
|
|
373
|
+
def graph_export(self) -> dict:
|
|
374
|
+
"""Export the memory graph as visualization-friendly JSON."""
|
|
375
|
+
envelope = self._request("GET", "/api/graph/export")
|
|
376
|
+
return envelope.get("data", {})
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _to_dict(obj: Any) -> dict:
|
|
380
|
+
"""Convert a dataclass to a dict, filtering None values."""
|
|
381
|
+
d = asdict(obj)
|
|
382
|
+
return {k: v for k, v in d.items() if v is not None}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _api_error(envelope: dict) -> Exception:
|
|
386
|
+
error = envelope.get("error", {})
|
|
387
|
+
return Exception(f"HMG API error: {error.get('code', 'unknown')} — {error.get('message', 'no details')}")
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hmg-sdk
|
|
3
|
+
Version: 0.9.2
|
|
4
|
+
Summary: HMG Agent Memory SDK — Community Edition
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Project-URL: Repository, https://github.com/HMG-AI/HMG-public
|
|
7
|
+
Keywords: hmg,memory,agent,protocol,ai
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# hmg
|
|
16
|
+
|
|
17
|
+
<p>
|
|
18
|
+
<img src="https://img.shields.io/badge/version-0.9.2-blue.svg" alt="Version">
|
|
19
|
+
<img src="https://img.shields.io/badge/license-Apache--2.0-green.svg" alt="License">
|
|
20
|
+
<img src="https://img.shields.io/badge/python-3.9%2B-blue.svg" alt="Python">
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
Python SDK for the HMG agent memory system.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install hmg
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from hmg import HMGClient
|
|
35
|
+
|
|
36
|
+
client = HMGClient(base_url="http://localhost:8080")
|
|
37
|
+
|
|
38
|
+
# Store a decision
|
|
39
|
+
client.memorize(
|
|
40
|
+
content="We chose PostgreSQL for the main database",
|
|
41
|
+
source="architecture-review",
|
|
42
|
+
modality="text",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Recall it later
|
|
46
|
+
result = client.recall(query="database choice")
|
|
47
|
+
for atom in result.atoms:
|
|
48
|
+
print(f"[{atom.score:.2f}] {atom.text}")
|
|
49
|
+
|
|
50
|
+
# Correct when it changes
|
|
51
|
+
client.correct(
|
|
52
|
+
atom_id=atom.id,
|
|
53
|
+
action="replace",
|
|
54
|
+
reason="Migrated to CockroachDB",
|
|
55
|
+
new_content="We migrated to CockroachDB for horizontal scale",
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## API Surface
|
|
60
|
+
|
|
61
|
+
| Method | Description |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `client.memorize(...)` | Store a memory atom |
|
|
64
|
+
| `client.recall(...)` | Recall memories by query |
|
|
65
|
+
| `client.correct(...)` | Correct a memory atom |
|
|
66
|
+
| `client.govern(...)` | Govern a memory atom's visibility |
|
|
67
|
+
| `client.handoff(...)` | Store a cross-session handoff |
|
|
68
|
+
| `client.agent_brief(...)` | Get session-start context |
|
|
69
|
+
| `client.history(...)` | Get correction/governance history |
|
|
70
|
+
| `client.stats()` | Get memory store statistics |
|
|
71
|
+
|
|
72
|
+
## Requirements
|
|
73
|
+
|
|
74
|
+
- Python 3.9+
|
|
75
|
+
- HMG daemon running (`hmg daemon start`)
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
Apache-2.0
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
hmg/__init__.py,sha256=P8hRj_FNOlvspGDHr5m6Iihh6qntKZNO_z9tJ8yq5X0,11665
|
|
2
|
+
hmg_sdk-0.9.2.dist-info/METADATA,sha256=gZyuapOyfcrg--CYjYQH3a7g_RPKiYF_HKEkaHlXZsM,2022
|
|
3
|
+
hmg_sdk-0.9.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
4
|
+
hmg_sdk-0.9.2.dist-info/top_level.txt,sha256=C51-jV1UvbUb72_uJLKqO0C9chsP0iQnP1hnopSeONA,4
|
|
5
|
+
hmg_sdk-0.9.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hmg
|