para-sdk 0.21.0rc9__cp38-abi3-macosx_10_12_x86_64.whl → 0.23.1rc3__cp38-abi3-macosx_10_12_x86_64.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.

Potentially problematic release.


This version of para-sdk might be problematic. Click here for more details.

para/__init__.py CHANGED
@@ -1 +1 @@
1
- # from para import para
1
+ from .client import UserClient, new_client, new_connection
para/client.py ADDED
@@ -0,0 +1,326 @@
1
+ import os
2
+ import json
3
+ import base64
4
+ import builtins
5
+ from dataclasses import dataclass
6
+ from queue import Empty, Queue
7
+
8
+ from para import para
9
+
10
+ from .poller import Poller
11
+ from .messages import *
12
+ if hasattr(builtins, "__IPYTHON__"):
13
+ from .conversation_panel import ConversationPanel
14
+
15
+ class PncpConversation:
16
+ """Represents and active or completed conversation.
17
+ """
18
+
19
+ id: str
20
+ """The unique conversation ID"""
21
+
22
+ messages: list[PncpMessage]
23
+ """List of messages in the conversation"""
24
+
25
+ _user_client: any
26
+ _complete: bool
27
+ _message_ids: set[str]
28
+
29
+ def __init__(self, id: str, client, actor=None):
30
+ self.id = id
31
+ self._user_client = client
32
+ self._complete = False
33
+ self._message_ids = set()
34
+ self.provider = actor
35
+ self.messages = []
36
+
37
+ def _repr_json_(self):
38
+ meta = {'root': 'conversation', 'expanded': True}
39
+ data = {'id': self.id, 'messages': [m._repr_json_()[0] for m in self.messages]}
40
+ return (data, meta)
41
+
42
+ def _push_pncp(self, msg):
43
+ body = msg['body']
44
+ id = body['messageId']
45
+ if id not in self._message_ids:
46
+ obj = pncp_to_py(msg)
47
+ if obj is not None:
48
+ if isinstance(obj, PncpResponse) or isinstance(obj, PncpError):
49
+ self._complete = True
50
+ self.messages.append(obj)
51
+ self._message_ids.add(obj.id)
52
+ return obj
53
+
54
+ def _push_gql(self, msg):
55
+ id = msg['id']
56
+ if id not in self._message_ids:
57
+ obj = graphql_to_py(msg)
58
+ if obj is not None:
59
+ if isinstance(obj, PncpResponse) or isinstance(obj, PncpError):
60
+ self._complete = True
61
+ self.messages.append(obj)
62
+ self._message_ids.add(obj.id)
63
+ return obj
64
+
65
+ def _sort(self):
66
+ self.messages.sort(key=lambda x: x.created)
67
+
68
+ def dup(self):
69
+ return PncpConversation(self.id, self._user_client, actor=self.provider)
70
+
71
+ def get_last_message(self) -> PncpMessage:
72
+ if len(self.messages) > 0:
73
+ return self.messages[-1]
74
+
75
+ def get_request(self) -> PncpRequest:
76
+ for m in self.messages:
77
+ if isinstance(m, PncpRequest):
78
+ return m
79
+ return None
80
+
81
+ def get_response(self) -> PncpMessage:
82
+ for m in self.messages:
83
+ if isinstance(m, PncpResponse) or isinstance(m, PncpError):
84
+ return m
85
+ return None
86
+
87
+ def update(self):
88
+ """Update `messages` attribute with any new messages.
89
+ """
90
+
91
+ if self._complete:
92
+ return
93
+ list_resp = self._user_client._client.list_messages(self.id, 9999)
94
+ for m in list_resp['messages']:
95
+ self._push_gql(m)
96
+ self._sort()
97
+
98
+ def wait_response(self, show_status=False) -> PncpMessage:
99
+ """Wait for the conversation to complete.
100
+
101
+ Returns the response (`PncpResponse`) and an error (`PncpError`).
102
+ """
103
+
104
+ if not self._complete:
105
+ # start poller before update to ensure no messages are missed
106
+ q = self._user_client._poller.subscribe(id(self), self.id)
107
+ self.update()
108
+ try:
109
+ while not self._complete:
110
+ try:
111
+ item = q.get(timeout=0.5)
112
+ if item['type'] == 'message':
113
+ obj = self._push_pncp(item)
114
+ if show_status and obj is not None:
115
+ if isinstance(obj, PncpStatus):
116
+ print(obj.display_text())
117
+ self._sort()
118
+ except Empty:
119
+ pass
120
+ finally:
121
+ self._user_client._poller.unsubscribe(id(self), self.id)
122
+ return self.get_response()
123
+
124
+ def panel(self, out=None):
125
+ """Create a live notebook panel that displays conversation status and messages."""
126
+
127
+ if hasattr(builtins, "__IPYTHON__"):
128
+ return make_panel(self, out)
129
+ else:
130
+ raise Exception('Not an ipython environment')
131
+
132
+
133
+ class ConversationStream:
134
+ def __init__(self, conv: PncpConversation):
135
+ self._conversation = conv
136
+ # start poller before update to ensure no messages are missed
137
+ self._queue = conv._user_client._poller.subscribe(id(self), conv.id)
138
+ self._position = 0
139
+ conv.update()
140
+ if conv._complete:
141
+ conv._user_client._poller.unsubscribe(id(self), conv.id)
142
+
143
+ def _poll(self):
144
+ try:
145
+ item = self._queue.get(block=False)
146
+ if item['type'] == 'message':
147
+ self._conversation._push_pncp(item)
148
+ if self._conversation._complete:
149
+ self._conversation._user_client._poller.unsubscribe(id(self), self._conversation.id)
150
+ self._conversation._sort()
151
+ except Empty:
152
+ pass
153
+
154
+ def next(self):
155
+ cur = self._position
156
+ if cur == len(self._conversation.messages):
157
+ if not self._conversation._complete:
158
+ self._poll()
159
+
160
+ if cur < len(self._conversation.messages):
161
+ self._position += 1
162
+ return self._conversation.messages[cur]
163
+ return None
164
+
165
+ def make_panel(conv: PncpConversation, out) -> ConversationStream:
166
+ # create new instance of PncpConversation panel uses on background thread
167
+ copy = conv.dup()
168
+ stream = ConversationStream(copy)
169
+ p = ConversationPanel(out)
170
+ return p.create(stream)
171
+
172
+
173
+ @dataclass
174
+ class ActorList:
175
+ actors: list[dict]
176
+
177
+ def _repr_markdown_(self):
178
+ lines = ['| id | kind |', '| --- | --- |']
179
+ for a in self.actors:
180
+ lines.append(f"| {a['id']} | {a['kind']} |")
181
+ return '\n'.join(lines)
182
+
183
+ @dataclass
184
+ class SkillList:
185
+ skills: list[dict]
186
+
187
+ def _repr_markdown_(self):
188
+ lines = ['| skillset | subject | action | inputs |', '| --- | --- | --- | --- |']
189
+ for s in self.skills:
190
+ schema = s.get('input_schema')
191
+ inputs = []
192
+ if schema is not None:
193
+ if schema['type'] == 'object':
194
+ for prop in schema['properties']:
195
+ inp = prop
196
+ typ = schema['properties'][prop].get('type')
197
+ if typ is not None:
198
+ inp = inp + ': ' + typ
199
+ inputs.append(inp)
200
+
201
+ lines.append(f"| {s['skillset']} | {s['subject']} | {s['action']} | {', '.join(inputs)} |")
202
+ return '\n'.join(lines)
203
+
204
+ class UserClient:
205
+ """A build-in instance of this class is created and accessible as `client` within the notebook environment. The client is automatically logged in with your user ID."""
206
+
207
+ _client: any
208
+ _poller: Poller
209
+
210
+ def __init__(self, paranet_client):
211
+ self._client = paranet_client
212
+ self._poller = Poller(paranet_client)
213
+ self._poller.start()
214
+
215
+ def new_request(self, subject: str, action: str, target_actor_id=None, **kwargs) -> PncpConversation:
216
+ """Send a new skill request.
217
+
218
+ - `subject` The skill request subject.
219
+ - `action` The skill request action.
220
+ - `kwargs` Any number of keyword arguments that are passed as the inputs for the skill request.
221
+
222
+ Returns the active conversation (`PncpConversation`).
223
+
224
+ """
225
+
226
+ resp = self._client.skill_request(subject, action, target_actor_id=target_actor_id, **kwargs)
227
+ cid = resp['id'].split('@')[0]
228
+ conv = PncpConversation(cid, self, actor=resp.get('matched_id'))
229
+ conv.update()
230
+ return conv
231
+
232
+ def skill_request(self, subject: str, action: str, target_actor_id=None, **kwargs) -> PncpMessage:
233
+ """Send a new skill request and wait for the response.
234
+
235
+ - `subject` The skill request subject.
236
+ - `action` The skill request action.
237
+ - `kwargs` Any number of keyword arguments that are passed as the inputs for the skill request.
238
+
239
+ Returns the response (`PncpResponse`) and an error (`PncpError`).
240
+
241
+ """
242
+
243
+ resp = self._client.skill_request(subject, action, target_actor_id=target_actor_id, **kwargs)
244
+ cid = resp['id'].split('@')[0]
245
+ conv = PncpConversation(cid, self, actor=resp.get('matched_id'))
246
+ return conv.wait_response(show_status=True)
247
+
248
+ def list_actors(self):
249
+ """Request a list of registered actors.
250
+
251
+ Returns `ActorList`
252
+ """
253
+
254
+ resp = self.skill_request('paranode', 'list_actors')
255
+ actors = resp.response['actors']
256
+ return ActorList(actors=actors)
257
+
258
+ def list_skills(self):
259
+ """Request a full list of registered skills.
260
+
261
+ Returns `SkillList`
262
+ """
263
+
264
+ resp = self.skill_request('paranode', 'list_skills')
265
+ skills = resp.response['skills']
266
+ return SkillList(skills=skills)
267
+
268
+ def list_actor_skills(self, actor: str):
269
+ """Request a list of the given actor's registered skills.
270
+
271
+ `actor` Name of the actor to request skills for.
272
+
273
+ Returns `SkillList`
274
+ """
275
+
276
+ if '@' not in actor:
277
+ actor = actor + '@1.0.0'
278
+ resp = self.skill_request('paranode', 'list_actor_skills', actor=actor)
279
+ skills = resp.response['skills']
280
+ return SkillList(skills=skills)
281
+
282
+ def new_connection(endpoint=None, actor=None, version=None, password=None, jwt=None, token=None, tls_id=None):
283
+ endpoint = endpoint or os.environ['PARANET_ENDPOINT']
284
+
285
+ broker_url = endpoint if endpoint else 'https://paranode:3131'
286
+ service_url = endpoint+'/api/paranet-service' if endpoint else 'https://paranode:3132'
287
+
288
+ actor = actor or os.environ.get('PARANET_ACTOR') or 'root'
289
+ version = version or os.environ.get('PARANET_ACTOR_VERSION') or '1.0.0'
290
+ actor_entity = f'{actor}@{version}'
291
+ password = password or os.environ.get('PARANET_PASSWORD')
292
+ jwt = jwt or os.environ.get('PARANET_JWT')
293
+ token = token or os.environ.get('PARANET_TOKEN')
294
+
295
+ tls_id = tls_id or os.environ.get('PARANET_TLS_ID')
296
+
297
+ connection = para.connect(broker_url, service_url, actor_entity, password, jwt, token, tls_id)
298
+
299
+ print(f'Connected to {broker_url} with actor {actor_entity}')
300
+
301
+ return UserClient(connection)
302
+
303
+ def new_client(proxy=None):
304
+ if 'PARANET_PROXY' in os.environ:
305
+ endpoint = os.environ['PARANET_PROXY']
306
+ broker_url = endpoint
307
+ service_url = endpoint+'/api/paranet-service'
308
+ if endpoint.startswith('http:'):
309
+ client = para.connect(broker_url, service_url, 'root', None)
310
+ return UserClient(client)
311
+ else:
312
+ broker_url = 'https://paranode:3131'
313
+ service_url = 'https://paranode:3132'
314
+
315
+ encoded_token = os.environ.get("PN_PY_USER")
316
+ del os.environ["PN_PY_USER"]
317
+ credentials = json.loads(base64.b64decode(encoded_token))
318
+ actor_id = credentials['id']
319
+ actor = actor_id.split('@')[0]
320
+ access_token = {'access_token': credentials['token'], 'refresh_token': credentials['refresh']}
321
+ client = para.connect(broker_url, service_url, actor, json.dumps(access_token))
322
+ return UserClient(client)
323
+
324
+ if hasattr(builtins, "__IPYTHON__"):
325
+ import panel as pn
326
+ pn.extension()
@@ -0,0 +1,67 @@
1
+ import threading
2
+ import time
3
+ from typing import cast
4
+ import panel as pn
5
+
6
+ from .messages import *
7
+
8
+ class ConversationPanel:
9
+ def __init__(self, out):
10
+ self._out = out
11
+
12
+ def create(self, stream):
13
+ self._stream = stream
14
+ self._conversation = stream._conversation
15
+ self._recipient = 'actor' if self._conversation.provider is None else self._conversation.provider.split('@')[0]
16
+ self._request = cast(PncpRequest, self._conversation.get_request())
17
+
18
+ self._status = 'Live'
19
+
20
+ # build panel
21
+ self.spinner = pn.indicators.LoadingSpinner(value=True, width=20, height=20, color="primary")
22
+ self.title = pn.pane.Markdown(self._title_display())
23
+ self.status_text = pn.pane.HTML(self._status_display(), width=400)
24
+ self.column = pn.Column(
25
+ self.title,
26
+ self.spinner
27
+ )
28
+ self._insert = 1
29
+ self._poller = pn.state.add_periodic_callback(self._update_status, period=1000)
30
+ return self.column
31
+
32
+ def _title_display(self):
33
+ req = self._request.request
34
+ title = f"### {self._status}: {req['subject']}/{req['action']}"
35
+ if self._conversation.provider is not None:
36
+ title += ' -> ' + self._conversation.provider.split('@')[0]
37
+ return title
38
+
39
+ def _status_display(self):
40
+ return f"<B>{self._status}</B>"
41
+
42
+ def _update_status(self):
43
+ while True:
44
+ obj = self._stream.next()
45
+ if obj is None:
46
+ return
47
+ if self._out is not None:
48
+ self._out.append_stdout(f"got: {obj}\n")
49
+ self._append_message(obj)
50
+ if isinstance(obj, PncpResponse) or isinstance(obj, PncpError):
51
+ self._status = 'Complete' if isinstance(obj, PncpResponse) else 'Failed'
52
+ self.status_text.object = self._status_display()
53
+ self.title.object = self._title_display()
54
+ self.spinner.value = False
55
+ self.column.pop()
56
+ self._poller.stop()
57
+ self._out.append_stdout(f"{self._status}\n")
58
+
59
+ def _append_message(self, msg):
60
+ match msg:
61
+ case PncpRequest():
62
+ party = 'you'
63
+ case _:
64
+ party = self._recipient
65
+ index = self._insert
66
+ self._insert += 1
67
+ self.column.insert(index, pn.pane.HTML(f"({party}) {msg.display_text()}", width=400))
para/messages.py ADDED
@@ -0,0 +1,181 @@
1
+ import sys
2
+ import json
3
+ import html
4
+ import builtins
5
+ from datetime import datetime
6
+ from dataclasses import dataclass
7
+ from types import MethodType
8
+ from IPython.core import display_functions
9
+ import mimeparse
10
+
11
+ def value_summary(v):
12
+ if type(v) == list:
13
+ elms = [value_summary(e) for e in v]
14
+ return '[' + ', '.join(elms) + ']'
15
+ elif type(v) == dict:
16
+ elms = []
17
+ for k in v:
18
+ elms.append(k+': ' + value_summary(v[k]))
19
+ return '{' + ', '.join(elms) + '}'
20
+ elif type(v) == str:
21
+ return json.dumps(v)
22
+ return str(v)
23
+
24
+ def data_summary(data):
25
+ if type(data) == dict:
26
+ elms = []
27
+ for k in data:
28
+ elms.append(k+': ' + value_summary(data[k]))
29
+ return ', '.join(elms)
30
+ else:
31
+ return value_summary(data)
32
+
33
+ @dataclass
34
+ class PncpMessage:
35
+ id: str
36
+ created: datetime
37
+
38
+ def display_text(self):
39
+ return f"Unknown message type"
40
+
41
+ @dataclass
42
+ class PncpStatus(PncpMessage):
43
+ status: dict
44
+
45
+ def _repr_json_(self):
46
+ meta = {'root': 'message', 'expanded': True}
47
+ data = {'id': self.id, 'created': self.created, 'status': self.status}
48
+ return (data, meta)
49
+
50
+ def display_text(self):
51
+ keys = list(self.status.keys())
52
+ if len(keys) == 1:
53
+ k = keys[0].lower()
54
+ if k == 'message' or k == 'status':
55
+ return 'Status: ' + data_summary(self.status[keys[0]])
56
+ return 'Status: ' + data_summary(self.status)
57
+
58
+ @dataclass
59
+ class PncpResponse(PncpMessage):
60
+ response: dict
61
+
62
+ def _repr_json_(self):
63
+ meta = {'root': 'message', 'expanded': True}
64
+ data = {'id': self.id, 'created': self.created, 'response': self.response}
65
+ return (data, meta)
66
+
67
+ def display_text(self):
68
+ keys = list(self.response.keys())
69
+ if len(keys) == 1:
70
+ k = keys[0].lower()
71
+ if k == 'message' or k == 'response' or k == 'result':
72
+ return 'Response: ' + data_summary(self.response[keys[0]])
73
+ return 'Response: ' + data_summary(self.response)
74
+
75
+ @dataclass
76
+ class PncpError(PncpMessage):
77
+ error: dict
78
+
79
+ def _repr_json_(self):
80
+ meta = {'root': 'message', 'expanded': True}
81
+ data = {'id': self.id, 'created': self.created, 'error': self.error}
82
+ return (data, meta)
83
+
84
+ def display_text(self):
85
+ keys = list(self.error.keys())
86
+ if len(keys) == 1:
87
+ k = keys[0].lower()
88
+ if k == 'message' or k == 'error' or k == 'result':
89
+ return 'Error: ' + data_summary(self.error[keys[0]])
90
+ return 'Error: ' + data_summary(self.error)
91
+
92
+ @dataclass
93
+ class PncpRequest(PncpMessage):
94
+ request: dict
95
+
96
+ def _repr_json_(self):
97
+ meta = {'root': 'message', 'expanded': True}
98
+ request = {'subject': self.request['subject'], 'action': self.request['action'], 'data': self.request['data']}
99
+ data = {'id': self.id, 'created': self.created, 'request': request}
100
+ return (data, meta)
101
+
102
+ def display_text(self):
103
+ args = data_summary(self.request['data'])
104
+ return f"Request: {self.request['subject']}/{self.request['action']}({args})"
105
+
106
+ def get_mime_field(body: dict[str,any]):
107
+ if type(body) == dict:
108
+ for mk in body:
109
+ if mk.startswith('Mime'):
110
+ k = mk[4:]
111
+ k = k[0:1].lower() + k[1:]
112
+ if k in body:
113
+ try:
114
+ mime_type = mimeparse.parse_mime_type(body[mk])
115
+ return (mime_type, body[k])
116
+ except:
117
+ pass
118
+ elif mk.startswith('_mime_'):
119
+ k = mk[6:]
120
+ if k in body:
121
+ try:
122
+ mime_type = mimeparse.parse_mime_type(body[mk])
123
+ return (mime_type, body[k])
124
+ except:
125
+ pass
126
+
127
+ def apply_rendering(msg: PncpMessage, body: dict[str,any]):
128
+ if not hasattr(builtins, "__IPYTHON__"):
129
+ return msg
130
+
131
+ render = get_mime_field(body)
132
+ if render is not None:
133
+ (mime, data) = render
134
+ (base, subtype, params) = mime
135
+ if base == 'application' and subtype == 'vnd.paranet.embed+html':
136
+ width = params.get('width', '100%')
137
+ height = params.get('height', '400')
138
+ iframe = f"<iframe width=\"{width}\" height=\"{height}\" srcdoc=\"{html.escape(data)}\" frameborder=\"0\"></iframe>"
139
+ # Notebook prioritizes json over html, so this method circumvents that by only providing html
140
+ def display(self):
141
+ display_functions.display({'text/html': iframe}, raw=True)
142
+ setattr(msg, '_ipython_display_', display.__get__(msg, msg.__class__))
143
+
144
+ return msg
145
+
146
+ def graphql_to_py(msg):
147
+ id = msg['id']
148
+ created = datetime.fromisoformat(msg['createdAt'].replace("Z", "+00:00"))
149
+ if msg['contents']['packet']['type'] == 'request':
150
+ request = msg['contents']['packet']['body']['body']
151
+ request['data'] = request['body']
152
+ del request['body']
153
+ return PncpRequest(id=id, created=created, request=request)
154
+ elif msg['contents']['packet']['type'] == 'message':
155
+ body = msg['contents']['packet']['body']
156
+ if body['message']['type'] == 'response':
157
+ response = body['message']['body']['data']
158
+ return apply_rendering(PncpResponse(id=id, created=created, response=response), response)
159
+ elif body['message']['type'] == 'status':
160
+ status = body['message']['body']['data']
161
+ return PncpStatus(id=id, created=created, status=status)
162
+ elif body['message']['type'] == 'error':
163
+ error = body['message']['body']['data']
164
+ return PncpError(id=id, created=created, error=error)
165
+ print('Unexpected GQL message', msg, file=sys.stderr)
166
+
167
+ def pncp_to_py(msg):
168
+ if msg['type'] == 'message':
169
+ body = msg['body']
170
+ id = body['messageId']
171
+ created = datetime.fromisoformat(body['timeCreated'].replace("Z", "+00:00"))
172
+ if body['message']['message']['type'] == 'response':
173
+ response = body['message']['message']['body']['data']
174
+ return apply_rendering(PncpResponse(id=id, created=created, response=response), response)
175
+ elif body['message']['message']['type'] == 'status':
176
+ status = body['message']['message']['body']['data']
177
+ return PncpStatus(id=id, created=created, status=status)
178
+ elif body['message']['message']['type'] == 'error':
179
+ error = body['message']['message']['body']['data']
180
+ return PncpError(id=id, created=created, error=error)
181
+ print('Unexpected PNCP message', msg, file=sys.stderr)
para/para.abi3.so CHANGED
Binary file
para/poller.py ADDED
@@ -0,0 +1,60 @@
1
+ import time
2
+ from dataclasses import dataclass
3
+ from typing import Tuple
4
+ from queue import Queue
5
+ from threading import Lock, Thread
6
+
7
+ class Poller:
8
+ _client: any
9
+ _subscribers: dict[str,list[Tuple[int,Queue]]]
10
+ _lock: Lock
11
+
12
+ def __init__(self, client):
13
+ self._client = client
14
+ self._subscribers = {}
15
+ self._lock = Lock()
16
+
17
+ def start(self):
18
+ self._thread = Thread(target=self._mainloop, daemon=True)
19
+ self._thread.start()
20
+
21
+ def _mainloop(self):
22
+ while True:
23
+ resp = self._client.poll_next()
24
+ if resp is not None:
25
+ if resp['type'] == 'message':
26
+ body = resp['body']
27
+ cid = body['id'].split('@')[0]
28
+ if self._lock.acquire():
29
+ try:
30
+ if cid in self._subscribers:
31
+ for (_, q) in self._subscribers[cid]:
32
+ q.put(resp)
33
+ except Exception as ex:
34
+ print(ex)
35
+ self._lock.release()
36
+ else:
37
+ time.sleep(0.1)
38
+
39
+ def subscribe(self, uid: int, cid: str) -> Queue:
40
+ if self._lock.acquire():
41
+ try:
42
+ if cid not in self._subscribers:
43
+ self._subscribers[cid] = []
44
+ q = Queue()
45
+ self._subscribers[cid].append((uid, q))
46
+ except Exception as ex:
47
+ print(ex)
48
+ self._lock.release()
49
+ return q
50
+
51
+ def unsubscribe(self, uid: int, cid: str):
52
+ if self._lock.acquire():
53
+ try:
54
+ if cid in self._subscribers:
55
+ self._subscribers[cid] = [sub for sub in self._subscribers[cid] if sub[0] != uid]
56
+ if len(self._subscribers[cid]) == 0:
57
+ del self._subscribers[cid]
58
+ except Exception as ex:
59
+ print(ex)
60
+ self._lock.release()
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: para-sdk
3
+ Version: 0.23.1rc3
4
+ Requires-Dist: python-mimeparse
5
+ Requires-Dist: panel
6
+ Requires-Dist: pydeck
7
+ Requires-Dist: ipython
@@ -0,0 +1,10 @@
1
+ para/__init__.py,sha256=j91SUu6B2VlgXvHvNkH5NK3WDkBdrNBytoPhO5E_LWU,58
2
+ para/__main__.py,sha256=GUDEQZo7PdWXRCYbNZm65kcl0lENtu3AikKs0bflDrM,64
3
+ para/client.py,sha256=-L3BQ54fumiD06vW97etNcPbFgUA6qIjJUu0YF6Ng8A,10141
4
+ para/conversation_panel.py,sha256=cGRdl2ay1BJJ1yOsJTunDgJAY7CD4BczTiWE0VzN3xc,2168
5
+ para/messages.py,sha256=U41mSpPXp9ub5JpUU53hn-yha9dCA9qOxrj9vsGQUHY,6102
6
+ para/para.abi3.so,sha256=g4-Oacrr7vzdrxeZCkJXw5hWwSy_uaRkq0b99pkhT24,59584336
7
+ para/poller.py,sha256=vmUeA_Gj9xD7O15n9tY8w37QMMHZwfJpbShB9lNHoJ4,1666
8
+ para_sdk-0.23.1rc3.dist-info/METADATA,sha256=XRSG1TduRLl1Y8m3z2CgKdbQBGnDNV4NDu3sF8XofkA,154
9
+ para_sdk-0.23.1rc3.dist-info/WHEEL,sha256=GPFYR4mIPdHeeQ1ax6E8LJaGIrcByvaZAJbs3IVoO7M,104
10
+ para_sdk-0.23.1rc3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.9.4)
2
+ Generator: maturin (1.9.6)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp38-abi3-macosx_10_12_x86_64
@@ -1,3 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: para-sdk
3
- Version: 0.21.0rc9
@@ -1,7 +0,0 @@
1
- para/__init__.py,sha256=8nY2C-MRakbnN1mtZlVfrTj96QQAJ0FqPtuPL6pGF_U,23
2
- para/__main__.py,sha256=GUDEQZo7PdWXRCYbNZm65kcl0lENtu3AikKs0bflDrM,64
3
- para/para.abi3.so,sha256=nB25aNUayLlseCWOQODjfRJ-hSlVbfPty5TDUhO_Eko,51610044
4
- para_sdk-0.21.0rc9.dist-info/METADATA,sha256=i2mQfYYAjx_zPEZ4YiJPf-Q9PD987ZmXGVzXvixt0y4,56
5
- para_sdk-0.21.0rc9.dist-info/WHEEL,sha256=qq8T5RNN4nLzVDHWhr9aubnbXTyL36gsBivIyKvr8Qc,104
6
- para_sdk-0.21.0rc9.dist-info/entry_points.txt,sha256=px5FZ1yAAKZ9kGJTb7564AHax4LzD90r73E5_shEwkU,33
7
- para_sdk-0.21.0rc9.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- para=para::cli