hivetrace 1.3.7__py3-none-any.whl → 1.3.9__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.
@@ -1,5 +1,5 @@
1
1
  import warnings
2
- from typing import Any, Dict, Optional, Union
2
+ from typing import Any, Dict, List, Optional, Tuple, Union
3
3
 
4
4
  import httpx
5
5
 
@@ -22,7 +22,6 @@ class AsyncHivetraceSDK(BaseHivetraceSDK):
22
22
  def __init__(self, config: Optional[Dict[str, Any]] = None) -> None:
23
23
  super().__init__(config)
24
24
  self.session = httpx.AsyncClient()
25
- # SDK асинхронный, задаем флаг для адаптеров
26
25
  self.async_mode = True
27
26
 
28
27
  async def __aenter__(self):
@@ -55,27 +54,90 @@ class AsyncHivetraceSDK(BaseHivetraceSDK):
55
54
  except Exception as e:
56
55
  return ErrorHandler.handle_unexpected_error(e)
57
56
 
57
+ async def _send_files(
58
+ self, endpoint: str, files: List[Tuple[str, bytes, str]]
59
+ ) -> HivetraceResponse:
60
+ request_args = self._build_files_request_args(endpoint, files)
61
+ try:
62
+ response = await self.session.post(**request_args)
63
+ response.raise_for_status()
64
+ api_data = response.json()
65
+ return ResponseBuilder.build_response_from_api(api_data)
66
+ except httpx.HTTPStatusError as e:
67
+ return ErrorHandler.handle_http_error(e)
68
+ except httpx.ConnectError as e:
69
+ return ErrorHandler.handle_connection_error(e)
70
+ except httpx.TimeoutException as e:
71
+ return ErrorHandler.handle_timeout_error(e)
72
+ except httpx.RequestError as e:
73
+ return ErrorHandler.handle_request_error(e)
74
+ except ValueError as e:
75
+ return ErrorHandler.handle_json_decode_error(e)
76
+ except Exception as e:
77
+ return ErrorHandler.handle_unexpected_error(e)
78
+
79
+ async def _get_blocking_status(self, endpoint: str) -> Optional[bool]:
80
+ url = f"{self.hivetrace_url}/{endpoint.lstrip('/')}"
81
+ headers = {"Authorization": f"Bearer {self.hivetrace_access_token}"}
82
+ try:
83
+ response = await self.session.get(
84
+ url, headers=headers, timeout=self._DEFAULT_TIMEOUT
85
+ )
86
+ response.raise_for_status()
87
+ data = response.json()
88
+ return data.get("blocked")
89
+ except Exception:
90
+ return None
91
+
58
92
  async def input(
59
93
  self,
60
94
  application_id: str,
61
95
  message: str,
62
96
  additional_parameters: Optional[Dict[str, Any]] = None,
97
+ files: Optional[List[Tuple[str, bytes, str]]] = None,
63
98
  ) -> HivetraceResponse:
64
99
  payload = self._build_message_payload(
65
100
  application_id, message, additional_parameters
66
101
  )
67
- return await self._send_request("/process_request/", payload)
102
+ process_response = await self._send_request("/process_request/", payload)
103
+ if files:
104
+ analysis_id = self._extract_analysis_id(process_response)
105
+ if analysis_id:
106
+ await self._send_files(
107
+ f"/user_prompt_analysis/{analysis_id}/attach_files", files
108
+ )
109
+ analysis_id = self._extract_analysis_id(process_response)
110
+ if analysis_id:
111
+ blocked = await self._get_blocking_status(
112
+ f"/user_prompt_analysis/{analysis_id}/check_blocking"
113
+ )
114
+ self._set_blocked(process_response, blocked)
115
+ return process_response
68
116
 
69
117
  async def output(
70
118
  self,
71
119
  application_id: str,
72
120
  message: str,
73
121
  additional_parameters: Optional[Dict[str, Any]] = None,
122
+ files: Optional[List[Tuple[str, bytes, str]]] = None,
74
123
  ) -> HivetraceResponse:
75
124
  payload = self._build_message_payload(
76
125
  application_id, message, additional_parameters
77
126
  )
78
- return await self._send_request("/process_response/", payload)
127
+ process_response = await self._send_request("/process_response/", payload)
128
+ if files:
129
+ analysis_id = self._extract_analysis_id(process_response)
130
+ if analysis_id:
131
+ await self._send_files(
132
+ f"/llm_response_analysis/{analysis_id}/attach_files", files
133
+ )
134
+ analysis_id = self._extract_analysis_id(process_response)
135
+ if analysis_id:
136
+ blocked = await self._get_blocking_status(
137
+ f"/llm_response_analysis/{analysis_id}/check_blocking"
138
+ )
139
+ self._set_blocked(process_response, blocked)
140
+ return process_response
79
141
 
80
142
  async def function_call(
81
143
  self,
hivetrace/client/base.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import uuid
3
3
  from abc import ABC, abstractmethod
4
- from typing import Any, Dict, Optional, Union
4
+ from typing import Any, Dict, List, Optional, Tuple, Union
5
5
 
6
6
  import httpx
7
7
  from pydantic import ValidationError
@@ -94,6 +94,58 @@ class BaseHivetraceSDK(ABC):
94
94
  "timeout": self._DEFAULT_TIMEOUT,
95
95
  }
96
96
 
97
+ def _build_files_request_args(
98
+ self,
99
+ endpoint: str,
100
+ files: List[Tuple[str, bytes, str]],
101
+ files_field_name: str = "attached_files",
102
+ ) -> Dict[str, Any]:
103
+ """Builds request args for multipart file upload."""
104
+ url = f"{self.hivetrace_url}/{endpoint.lstrip('/')}"
105
+ headers = {"Authorization": f"Bearer {self.hivetrace_access_token}"}
106
+ return {
107
+ "url": url,
108
+ "files": self._prepare_files_param(files, files_field_name),
109
+ "headers": headers,
110
+ "timeout": self._DEFAULT_TIMEOUT,
111
+ }
112
+
113
+ @staticmethod
114
+ def _prepare_files_param(
115
+ files: List[Tuple[str, bytes, str]],
116
+ files_field_name: str = "attached_files",
117
+ ) -> List[Tuple[str, Tuple[str, bytes, str]]]:
118
+ files_param: List[Tuple[str, Tuple[str, bytes, str]]] = []
119
+ for file_tuple in files:
120
+ files_param.append((files_field_name, file_tuple))
121
+ return files_param
122
+
123
+ @staticmethod
124
+ def _extract_analysis_id(response: Any) -> Optional[str]:
125
+ """Extracts analysis id from API response if present."""
126
+ try:
127
+ if isinstance(response, dict):
128
+ monitoring_result = response.get("monitoring_result", {})
129
+ analysis_id = monitoring_result.get("id")
130
+ return str(analysis_id) if analysis_id is not None else None
131
+ except Exception:
132
+ return None
133
+ return None
134
+
135
+ @staticmethod
136
+ def _set_blocked(response: Any, blocked: Optional[bool]) -> Any:
137
+ """Sets 'blocked' flag on response when possible."""
138
+ try:
139
+ if isinstance(response, dict):
140
+ response["blocked"] = blocked
141
+ return response
142
+ if hasattr(response, "blocked"):
143
+ setattr(response, "blocked", blocked)
144
+ return response
145
+ except Exception:
146
+ return response
147
+ return response
148
+
97
149
  def _handle_http_error(self, error: httpx.HTTPStatusError) -> HivetraceResponse:
98
150
  return ErrorHandler.handle_http_error(error)
99
151
 
@@ -1,5 +1,5 @@
1
1
  import weakref
2
- from typing import Any, Dict, Optional, Union
2
+ from typing import Any, Dict, List, Optional, Tuple, Union
3
3
 
4
4
  import httpx
5
5
 
@@ -67,27 +67,92 @@ class SyncHivetraceSDK(BaseHivetraceSDK):
67
67
  except Exception as e:
68
68
  return ErrorHandler.handle_unexpected_error(e)
69
69
 
70
+ def _send_files(
71
+ self, endpoint: str, files: List[Tuple[str, bytes, str]]
72
+ ) -> HivetraceResponse:
73
+ request_args = self._build_files_request_args(endpoint, files)
74
+ try:
75
+ response = self.session.post(**request_args)
76
+ response.raise_for_status()
77
+
78
+ api_data = response.json()
79
+ return ResponseBuilder.build_response_from_api(api_data)
80
+
81
+ except httpx.HTTPStatusError as e:
82
+ return ErrorHandler.handle_http_error(e)
83
+ except httpx.ConnectError as e:
84
+ return ErrorHandler.handle_connection_error(e)
85
+ except httpx.TimeoutException as e:
86
+ return ErrorHandler.handle_timeout_error(e)
87
+ except httpx.RequestError as e:
88
+ return ErrorHandler.handle_request_error(e)
89
+ except ValueError as e:
90
+ return ErrorHandler.handle_json_decode_error(e)
91
+ except Exception as e:
92
+ return ErrorHandler.handle_unexpected_error(e)
93
+
94
+ def _get_blocking_status(self, endpoint: str) -> Optional[bool]:
95
+ url = f"{self.hivetrace_url}/{endpoint.lstrip('/')}"
96
+ headers = {"Authorization": f"Bearer {self.hivetrace_access_token}"}
97
+ try:
98
+ response = self.session.get(
99
+ url, headers=headers, timeout=self._DEFAULT_TIMEOUT
100
+ )
101
+ response.raise_for_status()
102
+ data = response.json()
103
+ return data.get("blocked")
104
+ except Exception:
105
+ return None
106
+
70
107
  def input(
71
108
  self,
72
109
  application_id: str,
73
110
  message: str,
74
111
  additional_parameters: Optional[Dict[str, Any]] = None,
112
+ files: Optional[List[Tuple[str, bytes, str]]] = None,
75
113
  ) -> HivetraceResponse:
76
114
  payload = self._build_message_payload(
77
115
  application_id, message, additional_parameters
78
116
  )
79
- return self._send_request("/process_request/", payload)
117
+ process_response = self._send_request("/process_request/", payload)
118
+ if files:
119
+ analysis_id = self._extract_analysis_id(process_response)
120
+ if analysis_id:
121
+ self._send_files(
122
+ f"/user_prompt_analysis/{analysis_id}/attach_files", files
123
+ )
124
+ analysis_id = self._extract_analysis_id(process_response)
125
+ if analysis_id:
126
+ blocked = self._get_blocking_status(
127
+ f"/user_prompt_analysis/{analysis_id}/check_blocking"
128
+ )
129
+ self._set_blocked(process_response, blocked)
130
+ return process_response
80
131
 
81
132
  def output(
82
133
  self,
83
134
  application_id: str,
84
135
  message: str,
85
136
  additional_parameters: Optional[Dict[str, Any]] = None,
137
+ files: Optional[List[Tuple[str, bytes, str]]] = None,
86
138
  ) -> HivetraceResponse:
87
139
  payload = self._build_message_payload(
88
140
  application_id, message, additional_parameters
89
141
  )
90
- return self._send_request("/process_response/", payload)
142
+ process_response = self._send_request("/process_response/", payload)
143
+ if files:
144
+ analysis_id = self._extract_analysis_id(process_response)
145
+ if analysis_id:
146
+ self._send_files(
147
+ f"/llm_response_analysis/{analysis_id}/attach_files", files
148
+ )
149
+ analysis_id = self._extract_analysis_id(process_response)
150
+ if analysis_id:
151
+ blocked = self._get_blocking_status(
152
+ f"/llm_response_analysis/{analysis_id}/check_blocking"
153
+ )
154
+ self._set_blocked(process_response, blocked)
155
+ return process_response
91
156
 
92
157
  def function_call(
93
158
  self,
@@ -24,6 +24,10 @@ class ProcessResponse(SuccessResponse):
24
24
 
25
25
  message_id: Optional[str] = Field(None, description="ID of processed message")
26
26
  trace_id: Optional[str] = Field(None, description="Trace ID")
27
+ blocked: Optional[bool] = Field(
28
+ None,
29
+ description="Role restriction flag. True if message/response is blocked by policy.",
30
+ )
27
31
 
28
32
 
29
33
  class ErrorResponse(BaseResponse):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hivetrace
3
- Version: 1.3.7
3
+ Version: 1.3.9
4
4
  Summary: Hivetrace SDK for monitoring LLM applications
5
5
  Home-page: http://hivetrace.ai
6
6
  Author: Raft
@@ -11,6 +11,7 @@ Requires-Python: >=3.8
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: httpx >=0.28.1
14
+ Requires-Dist: pydantic >=2.11.7
14
15
  Requires-Dist: python-dotenv >=1.0.1
15
16
  Provides-Extra: all
16
17
  Requires-Dist: crewai >=0.95.0 ; extra == 'all'
@@ -20,9 +21,11 @@ Requires-Dist: langchain-openai ==0.2.5 ; extra == 'all'
20
21
  Requires-Dist: langchain ==0.3.19 ; extra == 'all'
21
22
  Requires-Dist: langchain-experimental ==0.3.4 ; extra == 'all'
22
23
  Requires-Dist: openai-agents >=0.1.0 ; extra == 'all'
24
+ Requires-Dist: pydantic >=2.11.7 ; extra == 'all'
23
25
  Requires-Dist: python-dotenv >=1.0.1 ; extra == 'all'
24
26
  Provides-Extra: base
25
27
  Requires-Dist: httpx >=0.28.1 ; extra == 'base'
28
+ Requires-Dist: pydantic >=2.11.7 ; extra == 'base'
26
29
  Requires-Dist: python-dotenv >=1.0.1 ; extra == 'base'
27
30
  Provides-Extra: crewai
28
31
  Requires-Dist: crewai >=0.95.0 ; extra == 'crewai'
@@ -78,6 +81,16 @@ response = client.input(
78
81
  application_id="your-application-id", # Obtained after registering the application in the UI
79
82
  message="User prompt here",
80
83
  )
84
+
85
+ # Optionally attach files (filename, bytes, mime_type)
86
+ files = [
87
+ ("doc1.txt", open("doc1.txt", "rb"), "text/plain"),
88
+ ]
89
+ response_with_files = client.input(
90
+ application_id="your-application-id",
91
+ message="User prompt with files",
92
+ files=files,
93
+ )
81
94
  ```
82
95
 
83
96
  ### Send an LLM response (output)
@@ -87,6 +100,16 @@ response = client.output(
87
100
  application_id="your-application-id",
88
101
  message="LLM response here",
89
102
  )
103
+
104
+ # With files
105
+ files = [
106
+ ("doc1.txt", open("doc1.txt", "rb"), "text/plain"),
107
+ ]
108
+ response_with_files = client.output(
109
+ application_id="your-application-id",
110
+ message="LLM response with files",
111
+ files=files,
112
+ )
90
113
  ```
91
114
 
92
115
  ---
@@ -107,6 +130,16 @@ response = await client.input(
107
130
  application_id="your-application-id",
108
131
  message="User prompt here",
109
132
  )
133
+
134
+ # With files (filename, bytes, mime_type)
135
+ files = [
136
+ ("doc1.txt", open("doc1.txt", "rb"), "text/plain"),
137
+ ]
138
+ response_with_files = await client.input(
139
+ application_id="your-application-id",
140
+ message="User prompt with files",
141
+ files=files,
142
+ )
110
143
  ```
111
144
 
112
145
  ### Send an LLM response (output)
@@ -116,6 +149,16 @@ response = await client.output(
116
149
  application_id="your-application-id",
117
150
  message="LLM response here",
118
151
  )
152
+
153
+ # With files
154
+ files = [
155
+ ("doc1.txt", open("doc1.txt", "rb"), "text/plain"),
156
+ ]
157
+ response_with_files = await client.output(
158
+ application_id="your-application-id",
159
+ message="LLM response with files",
160
+ files=files,
161
+ )
119
162
  ```
120
163
 
121
164
  ---
@@ -148,10 +191,20 @@ response = client.input(
148
191
 
149
192
  ```python
150
193
  # Sync
151
- def input(application_id: str, message: str, additional_parameters: dict | None = None) -> dict: ...
194
+ def input(
195
+ application_id: str,
196
+ message: str,
197
+ additional_parameters: dict | None = None,
198
+ files: list[tuple[str, bytes, str]] | None = None,
199
+ ) -> dict: ...
152
200
 
153
201
  # Async
154
- async def input(application_id: str, message: str, additional_parameters: dict | None = None) -> dict: ...
202
+ async def input(
203
+ application_id: str,
204
+ message: str,
205
+ additional_parameters: dict | None = None,
206
+ files: list[tuple[str, bytes, str]] | None = None,
207
+ ) -> dict: ...
155
208
  ```
156
209
 
157
210
  Sends a **user prompt** to Hivetrace.
@@ -159,11 +212,15 @@ Sends a **user prompt** to Hivetrace.
159
212
  * `application_id` — Application identifier (must be a valid UUID, created in the UI)
160
213
  * `message` — The user prompt
161
214
  * `additional_parameters` — Optional dictionary with extra context (session, user, agents, etc.)
215
+ * `files` — Optional list of tuples `(filename: str, content: bytes, mime_type: str)`; files are attached to the created analysis record
216
+
217
+ Response contains a `blocked` flag that indicates role restrictions.
162
218
 
163
219
  **Response example:**
164
220
 
165
221
  ```json
166
222
  {
223
+ "blocked": false,
167
224
  "status": "processed",
168
225
  "monitoring_result": {
169
226
  "is_toxic": false,
@@ -181,10 +238,20 @@ Sends a **user prompt** to Hivetrace.
181
238
 
182
239
  ```python
183
240
  # Sync
184
- def output(application_id: str, message: str, additional_parameters: dict | None = None) -> dict: ...
241
+ def output(
242
+ application_id: str,
243
+ message: str,
244
+ additional_parameters: dict | None = None,
245
+ files: list[tuple[str, bytes, str]] | None = None,
246
+ ) -> dict: ...
185
247
 
186
248
  # Async
187
- async def output(application_id: str, message: str, additional_parameters: dict | None = None) -> dict: ...
249
+ async def output(
250
+ application_id: str,
251
+ message: str,
252
+ additional_parameters: dict | None = None,
253
+ files: list[tuple[str, bytes, str]] | None = None,
254
+ ) -> dict: ...
188
255
  ```
189
256
 
190
257
  Sends an **LLM response** to Hivetrace.
@@ -192,11 +259,17 @@ Sends an **LLM response** to Hivetrace.
192
259
  * `application_id` — Application identifier (must be a valid UUID, created in the UI)
193
260
  * `message` — The LLM response
194
261
  * `additional_parameters` — Optional dictionary with extra context (session, user, agents, etc.)
262
+ * `files` — Optional list of tuples `(filename: str, content: bytes, mime_type: str)`
263
+
264
+ > Files are uploaded after the main request completes and an analysis ID is available.
265
+
266
+ Response contains a `blocked` flag that indicates role restrictions.
195
267
 
196
268
  **Response example:**
197
269
 
198
270
  ```json
199
271
  {
272
+ "blocked": false,
200
273
  "status": "processed",
201
274
  "monitoring_result": {
202
275
  "is_toxic": false,
@@ -927,3 +1000,8 @@ def calculate_sum(a: int, b: int) -> int:
927
1000
  Add this tool to your agent’s `tools=[...]` — and its calls will appear in HiveTrace with inputs/outputs.
928
1001
 
929
1002
  ---
1003
+
1004
+ License
1005
+ ========
1006
+
1007
+ This project is licensed under Apache License 2.0.
@@ -21,9 +21,9 @@ hivetrace/adapters/openai_agents/tracing.py,sha256=aOmJV2PT77x0bKZHXy46GRsExFmGw
21
21
  hivetrace/adapters/utils/__init__.py,sha256=AkdJzecQlhT3hHFOIO5zWbAIEXvbgH_5vmzlPViedt0,142
22
22
  hivetrace/adapters/utils/logging.py,sha256=UxCMFvlpP6vJfzRwMYhhJIi7RTWdgVK2sWtCeEB67_w,1126
23
23
  hivetrace/client/__init__.py,sha256=Daz_KxOMzGSBKUpv48tTooGZrmzk0wzDq8QUTHuBZBU,313
24
- hivetrace/client/async_client.py,sha256=UgGIaa5QJK2BV7eVvPCe9eUf4Q9Dwlkqp09vd5XUvVM,3790
25
- hivetrace/client/base.py,sha256=tOqdM7aB7wPUFlI0JzAiQPEaSnt3rHWcEC-wGx-B5u0,5995
26
- hivetrace/client/sync_client.py,sha256=5wixTkwsQezeSupeTZUuCBaNg5FpTxMS64jS9H5NcW4,3579
24
+ hivetrace/client/async_client.py,sha256=9uvimxP-Q7vY-bbZW5RiTEYQd8_uP3wyqR3s92kPGUs,6518
25
+ hivetrace/client/base.py,sha256=JSVL60S7jaGNCovkTP9MPZGzSPPe7FcgMJne0QKCs1U,7960
26
+ hivetrace/client/sync_client.py,sha256=J3nKDVfVtfYL3NjlMtj5l7EpZKXupJnI1bpHy-tWgyE,6347
27
27
  hivetrace/errors/__init__.py,sha256=3Sqr2Pz4NwE_u8CgTzVbzoNj-B52cwzdsHINnpWO3Yg,1006
28
28
  hivetrace/errors/api.py,sha256=ThIoH8akPWuSqF6bqnex5f25p8ZBnRFsz2IrcivAHgc,1029
29
29
  hivetrace/errors/base.py,sha256=_2o8TbvlPzJEzKGl4T3u1XbTAJgP5fO19T6XQSfH3pI,686
@@ -34,12 +34,12 @@ hivetrace/handlers/error_handler.py,sha256=aWLL--HKBm4h8AO4oQSpsFObq2TjHXntYXBDX
34
34
  hivetrace/handlers/response_builder.py,sha256=J3qDWxvi_jxiZ7AgmYscHfukbz4rmnSwBF2UaWkqzb4,2558
35
35
  hivetrace/models/__init__.py,sha256=qQvtDkI0Awlch6c_kedH5Jq8aWn3XhDXbxbch5nf-RI,1020
36
36
  hivetrace/models/requests.py,sha256=dMKUMOqerDmZj4CcQ3AF-N4HSl_csv9lafA2bpggNVc,2640
37
- hivetrace/models/responses.py,sha256=zTcbCZ8GbIjhswb_yR7O3PmRuZaxGWOVtqSRfCy2jNA,3059
37
+ hivetrace/models/responses.py,sha256=U06Gc4ZU2WmaPxjy1Dg5BtTDad6MOBURiDPTeLyDtcE,3209
38
38
  hivetrace/utils/__init__.py,sha256=BNYbeSuUbrZL7RradjE_OFAxam3L6eexbL2IMfjImv0,747
39
39
  hivetrace/utils/error_helpers.py,sha256=egVQpENputLR8exNpV1cui2LSHqbf8pI6SHRbLdxOX8,2661
40
40
  hivetrace/utils/uuid_generator.py,sha256=W4i2tUSyClNKNgm4O-bk_Qkkmw3cWIuf29DjwXftx0c,344
41
- hivetrace-1.3.7.dist-info/LICENSE,sha256=8d3g3prbWPDLQ5AV0dtyWfYTj5QPl8MJ_wlr2l8pjEU,11333
42
- hivetrace-1.3.7.dist-info/METADATA,sha256=FfxxOqtTC2RWlESYdqK-WWr0xRH47el7iUAsjc_cFnk,25793
43
- hivetrace-1.3.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
44
- hivetrace-1.3.7.dist-info/top_level.txt,sha256=F6mZCzZ5CSftMc-M0NeOYWbwyTzjybR72P4qSBMyZZM,10
45
- hivetrace-1.3.7.dist-info/RECORD,,
41
+ hivetrace-1.3.9.dist-info/LICENSE,sha256=8d3g3prbWPDLQ5AV0dtyWfYTj5QPl8MJ_wlr2l8pjEU,11333
42
+ hivetrace-1.3.9.dist-info/METADATA,sha256=WMOawn28WE9xSOozoUgigYH2UkRCMCtcreYj7r_ccuY,27727
43
+ hivetrace-1.3.9.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
44
+ hivetrace-1.3.9.dist-info/top_level.txt,sha256=F6mZCzZ5CSftMc-M0NeOYWbwyTzjybR72P4qSBMyZZM,10
45
+ hivetrace-1.3.9.dist-info/RECORD,,