freeplay 0.3.15__py3-none-any.whl → 0.3.17__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.
- freeplay/__init__.py +2 -0
- freeplay/resources/prompts.py +70 -27
- freeplay/resources/recordings.py +12 -3
- freeplay/resources/sessions.py +40 -7
- freeplay/support.py +41 -6
- {freeplay-0.3.15.dist-info → freeplay-0.3.17.dist-info}/METADATA +1 -1
- {freeplay-0.3.15.dist-info → freeplay-0.3.17.dist-info}/RECORD +10 -10
- {freeplay-0.3.15.dist-info → freeplay-0.3.17.dist-info}/LICENSE +0 -0
- {freeplay-0.3.15.dist-info → freeplay-0.3.17.dist-info}/WHEEL +0 -0
- {freeplay-0.3.15.dist-info → freeplay-0.3.17.dist-info}/entry_points.txt +0 -0
freeplay/__init__.py
CHANGED
@@ -2,9 +2,11 @@ from .freeplay import Freeplay
|
|
2
2
|
from .resources.prompts import PromptInfo
|
3
3
|
from .resources.recordings import CallInfo, ResponseInfo, RecordPayload, TestRunInfo, UsageTokens
|
4
4
|
from .resources.sessions import SessionInfo, TraceInfo
|
5
|
+
from .support import CustomMetadata
|
5
6
|
|
6
7
|
__all__ = [
|
7
8
|
'CallInfo',
|
9
|
+
'CustomMetadata',
|
8
10
|
'Freeplay',
|
9
11
|
'PromptInfo',
|
10
12
|
'RecordPayload',
|
freeplay/resources/prompts.py
CHANGED
@@ -1,16 +1,37 @@
|
|
1
1
|
import copy
|
2
2
|
import json
|
3
3
|
import logging
|
4
|
+
import warnings
|
4
5
|
from abc import ABC, abstractmethod
|
5
6
|
from dataclasses import asdict, dataclass
|
6
7
|
from pathlib import Path
|
7
|
-
from typing import
|
8
|
-
|
9
|
-
|
8
|
+
from typing import (
|
9
|
+
Any,
|
10
|
+
Dict,
|
11
|
+
List,
|
12
|
+
Optional,
|
13
|
+
Protocol,
|
14
|
+
Sequence,
|
15
|
+
TypedDict,
|
16
|
+
Union,
|
17
|
+
cast,
|
18
|
+
runtime_checkable,
|
19
|
+
)
|
20
|
+
|
21
|
+
from freeplay.errors import (
|
22
|
+
FreeplayClientError,
|
23
|
+
FreeplayConfigurationError,
|
24
|
+
log_freeplay_client_warning,
|
25
|
+
)
|
10
26
|
from freeplay.llm_parameters import LLMParameters
|
11
27
|
from freeplay.model import InputVariables
|
12
|
-
from freeplay.support import
|
13
|
-
|
28
|
+
from freeplay.support import (
|
29
|
+
CallSupport,
|
30
|
+
PromptTemplate,
|
31
|
+
PromptTemplateMetadata,
|
32
|
+
PromptTemplates,
|
33
|
+
ToolSchema,
|
34
|
+
)
|
14
35
|
from freeplay.utils import bind_template_variables, convert_provider_message_to_dict
|
15
36
|
|
16
37
|
logger = logging.getLogger(__name__)
|
@@ -33,16 +54,30 @@ class UnsupportedToolSchemaError(FreeplayConfigurationError):
|
|
33
54
|
|
34
55
|
# Models ==
|
35
56
|
|
36
|
-
# A content block
|
57
|
+
# A content block compatible with stainless generated SDKs (such as Anthropic
|
58
|
+
# and OpenAI). This lets us generate a dictionary from the stainless classes
|
59
|
+
# correctly. Intentionally over-permissive to allow schema evolution by the
|
60
|
+
# providers.
|
37
61
|
@runtime_checkable
|
38
|
-
class
|
62
|
+
class ProviderMessageProtocol(Protocol):
|
39
63
|
def model_dump(self) -> Dict[str, Any]:
|
40
64
|
pass
|
41
65
|
|
42
66
|
|
43
|
-
|
44
|
-
|
45
|
-
|
67
|
+
class MessageDict(TypedDict):
|
68
|
+
role: str
|
69
|
+
content: Any
|
70
|
+
|
71
|
+
|
72
|
+
# This type represents a struct or dict containing a role and content. The role
|
73
|
+
# should be one of user, assistant or system. This type should be compatible
|
74
|
+
# with OpenAI and Anthropic's message format, as well as most other SDKs. If
|
75
|
+
# not using a common provider, use {'content': str, 'role': str} to record. If
|
76
|
+
# using a common provider, this is usually the `.content` field.
|
77
|
+
ProviderMessage = Union[MessageDict, Dict[str, Any], ProviderMessageProtocol]
|
78
|
+
|
79
|
+
# DEPRECATED: Use ProviderMessage instead
|
80
|
+
GenericProviderMessage = ProviderMessage
|
46
81
|
|
47
82
|
|
48
83
|
# SDK-Exposed Classes
|
@@ -69,7 +104,7 @@ class FormattedPrompt:
|
|
69
104
|
formatted_prompt_text: Optional[str] = None,
|
70
105
|
tool_schema: Optional[List[Dict[str, Any]]] = None
|
71
106
|
):
|
72
|
-
# These two definitions allow us to operate on typed fields
|
107
|
+
# These two definitions allow us to operate on typed fields until we expose them as Any for client use.
|
73
108
|
self._llm_prompt = formatted_prompt
|
74
109
|
self._tool_schema = tool_schema
|
75
110
|
|
@@ -81,11 +116,18 @@ class FormattedPrompt:
|
|
81
116
|
(message['content'] for message in messages if message['role'] == 'system'), None)
|
82
117
|
self.system_content = maybe_system_content
|
83
118
|
|
84
|
-
|
85
|
-
|
119
|
+
self._messages = messages
|
120
|
+
|
121
|
+
@property
|
122
|
+
def messages(self) -> List[Dict[str, str]]:
|
123
|
+
warnings.warn(
|
124
|
+
"The 'messages' attribute is deprecated and will be removed in a future version. It is not formatted for the provider. Use 'llm_prompt' instead.",
|
125
|
+
DeprecationWarning,
|
126
|
+
stacklevel=2,
|
127
|
+
)
|
128
|
+
return self._messages
|
86
129
|
|
87
130
|
@property
|
88
|
-
# We know this is a list of dict[str,str], but we use Any to avoid typing issues with client SDK libraries, which require strict TypedDict.
|
89
131
|
def llm_prompt(self) -> Any:
|
90
132
|
return self._llm_prompt
|
91
133
|
|
@@ -93,12 +135,9 @@ class FormattedPrompt:
|
|
93
135
|
def tool_schema(self) -> Any:
|
94
136
|
return self._tool_schema
|
95
137
|
|
96
|
-
def all_messages(
|
97
|
-
self,
|
98
|
-
new_message: GenericProviderMessage
|
99
|
-
) -> List[Dict[str, Any]]:
|
138
|
+
def all_messages(self, new_message: ProviderMessage) -> List[Dict[str, Any]]:
|
100
139
|
converted_message = convert_provider_message_to_dict(new_message)
|
101
|
-
return self.
|
140
|
+
return self._messages + [converted_message]
|
102
141
|
|
103
142
|
|
104
143
|
class BoundPrompt:
|
@@ -211,7 +250,11 @@ class TemplatePrompt:
|
|
211
250
|
self.tool_schema = tool_schema
|
212
251
|
self.messages = messages
|
213
252
|
|
214
|
-
def bind(
|
253
|
+
def bind(
|
254
|
+
self,
|
255
|
+
variables: InputVariables,
|
256
|
+
history: Optional[Sequence[ProviderMessage]] = None,
|
257
|
+
) -> BoundPrompt:
|
215
258
|
# check history for a system message
|
216
259
|
history_clean = []
|
217
260
|
if history:
|
@@ -521,13 +564,13 @@ class Prompts:
|
|
521
564
|
return TemplatePrompt(prompt_info, prompt.content, prompt.tool_schema)
|
522
565
|
|
523
566
|
def get_formatted(
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
567
|
+
self,
|
568
|
+
project_id: str,
|
569
|
+
template_name: str,
|
570
|
+
environment: str,
|
571
|
+
variables: InputVariables,
|
572
|
+
history: Optional[Sequence[ProviderMessage]] = None,
|
573
|
+
flavor_name: Optional[str] = None,
|
531
574
|
) -> FormattedPrompt:
|
532
575
|
bound_prompt = self.get(
|
533
576
|
project_id=project_id,
|
freeplay/resources/recordings.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import Any, Dict, List, Optional, Union
|
4
|
+
from typing import Any, Dict, List, Optional, Union, Literal
|
5
5
|
from uuid import UUID
|
6
6
|
|
7
7
|
from requests import HTTPError
|
@@ -23,6 +23,9 @@ class UsageTokens:
|
|
23
23
|
completion_tokens: int
|
24
24
|
|
25
25
|
|
26
|
+
ApiStyle = Union[Literal['batch'], Literal['default']]
|
27
|
+
|
28
|
+
|
26
29
|
@dataclass
|
27
30
|
class CallInfo:
|
28
31
|
provider: str
|
@@ -32,13 +35,15 @@ class CallInfo:
|
|
32
35
|
model_parameters: LLMParameters
|
33
36
|
provider_info: Optional[Dict[str, Any]] = None
|
34
37
|
usage: Optional[UsageTokens] = None
|
38
|
+
api_style: Optional[ApiStyle] = None
|
35
39
|
|
36
40
|
@staticmethod
|
37
41
|
def from_prompt_info(
|
38
42
|
prompt_info: PromptInfo,
|
39
43
|
start_time: float,
|
40
44
|
end_time: float,
|
41
|
-
usage: Optional[UsageTokens] = None
|
45
|
+
usage: Optional[UsageTokens] = None,
|
46
|
+
api_style: Optional[ApiStyle] = None
|
42
47
|
) -> 'CallInfo':
|
43
48
|
return CallInfo(
|
44
49
|
provider=prompt_info.provider,
|
@@ -47,7 +52,8 @@ class CallInfo:
|
|
47
52
|
end_time=end_time,
|
48
53
|
model_parameters=prompt_info.model_parameters,
|
49
54
|
provider_info=prompt_info.provider_info,
|
50
|
-
usage=usage
|
55
|
+
usage=usage,
|
56
|
+
api_style=api_style
|
51
57
|
)
|
52
58
|
|
53
59
|
|
@@ -157,6 +163,9 @@ class Recordings:
|
|
157
163
|
"completion_tokens": record_payload.call_info.usage.completion_tokens,
|
158
164
|
}
|
159
165
|
|
166
|
+
if record_payload.call_info.api_style is not None:
|
167
|
+
record_api_payload['call_info']['api_style'] = record_payload.call_info.api_style
|
168
|
+
|
160
169
|
try:
|
161
170
|
recorded_response = api_support.post_raw(
|
162
171
|
api_key=self.call_support.freeplay_api_key,
|
freeplay/resources/sessions.py
CHANGED
@@ -3,9 +3,7 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Optional, Dict, Union
|
4
4
|
|
5
5
|
from freeplay.errors import FreeplayClientError
|
6
|
-
from freeplay.support import CallSupport
|
7
|
-
|
8
|
-
CustomMetadata = Optional[Dict[str, Union[str, int, float, bool]]]
|
6
|
+
from freeplay.support import CallSupport, CustomMetadata
|
9
7
|
|
10
8
|
|
11
9
|
@dataclass
|
@@ -18,6 +16,8 @@ class TraceInfo:
|
|
18
16
|
session_id: str
|
19
17
|
trace_id: str
|
20
18
|
input: Optional[str] = None
|
19
|
+
agent_name: Optional[str] = None
|
20
|
+
custom_metadata: CustomMetadata = None
|
21
21
|
_call_support: CallSupport
|
22
22
|
|
23
23
|
def __init__(
|
@@ -26,16 +26,34 @@ class TraceInfo:
|
|
26
26
|
session_id: str,
|
27
27
|
_call_support: CallSupport,
|
28
28
|
input: Optional[str] = None,
|
29
|
+
agent_name: Optional[str] = None,
|
30
|
+
custom_metadata: CustomMetadata = None,
|
29
31
|
):
|
30
32
|
self.trace_id = trace_id
|
31
33
|
self.session_id = session_id
|
32
34
|
self.input = input
|
35
|
+
self.agent_name = agent_name
|
36
|
+
self.custom_metadata = custom_metadata
|
33
37
|
self._call_support = _call_support
|
34
38
|
|
35
|
-
def record_output(
|
39
|
+
def record_output(
|
40
|
+
self,
|
41
|
+
project_id: str,
|
42
|
+
output: str,
|
43
|
+
eval_results: Optional[Dict[str, Union[bool, float]]] = None
|
44
|
+
) -> None:
|
36
45
|
if self.input is None:
|
37
46
|
raise FreeplayClientError("Input must be set before recording output")
|
38
|
-
self._call_support.record_trace(
|
47
|
+
self._call_support.record_trace(
|
48
|
+
project_id,
|
49
|
+
self.session_id,
|
50
|
+
self.trace_id,
|
51
|
+
self.input,
|
52
|
+
output,
|
53
|
+
agent_name=self.agent_name,
|
54
|
+
custom_metadata=self.custom_metadata,
|
55
|
+
eval_results=eval_results
|
56
|
+
)
|
39
57
|
|
40
58
|
|
41
59
|
@dataclass
|
@@ -53,19 +71,34 @@ class Session:
|
|
53
71
|
def session_info(self) -> SessionInfo:
|
54
72
|
return self._session_info
|
55
73
|
|
56
|
-
def create_trace(
|
74
|
+
def create_trace(
|
75
|
+
self,
|
76
|
+
input: str,
|
77
|
+
agent_name: Optional[str] = None,
|
78
|
+
custom_metadata: CustomMetadata = None
|
79
|
+
) -> TraceInfo:
|
57
80
|
return TraceInfo(
|
58
81
|
trace_id=str(uuid.uuid4()),
|
59
82
|
session_id=self.session_id,
|
60
83
|
input=input,
|
84
|
+
agent_name=agent_name,
|
85
|
+
custom_metadata=custom_metadata,
|
61
86
|
_call_support=self._call_support
|
62
87
|
)
|
63
88
|
|
64
|
-
def restore_trace(
|
89
|
+
def restore_trace(
|
90
|
+
self,
|
91
|
+
trace_id: uuid.UUID,
|
92
|
+
input: Optional[str],
|
93
|
+
agent_name: Optional[str] = None,
|
94
|
+
custom_metadata: CustomMetadata = None
|
95
|
+
) -> TraceInfo:
|
65
96
|
return TraceInfo(
|
66
97
|
trace_id=str(trace_id),
|
67
98
|
session_id=self.session_id,
|
68
99
|
input=input,
|
100
|
+
agent_name=agent_name,
|
101
|
+
custom_metadata=custom_metadata,
|
69
102
|
_call_support=self._call_support
|
70
103
|
)
|
71
104
|
|
freeplay/support.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import json
|
2
1
|
from dataclasses import dataclass
|
3
2
|
from json import JSONEncoder
|
4
3
|
from typing import Optional, Dict, Any, List, Union
|
@@ -8,6 +7,8 @@ from freeplay.api_support import try_decode
|
|
8
7
|
from freeplay.errors import freeplay_response_error, FreeplayServerError
|
9
8
|
from freeplay.model import InputVariables, FeedbackValue, NormalizedMessage
|
10
9
|
|
10
|
+
CustomMetadata = Optional[Dict[str, Union[str, int, float, bool]]]
|
11
|
+
|
11
12
|
|
12
13
|
@dataclass
|
13
14
|
class PromptTemplateMetadata:
|
@@ -17,12 +18,14 @@ class PromptTemplateMetadata:
|
|
17
18
|
params: Optional[Dict[str, Any]] = None
|
18
19
|
provider_info: Optional[Dict[str, Any]] = None
|
19
20
|
|
21
|
+
|
20
22
|
@dataclass
|
21
23
|
class ToolSchema:
|
22
24
|
name: str
|
23
25
|
description: str
|
24
26
|
parameters: Dict[str, Any]
|
25
27
|
|
28
|
+
|
26
29
|
@dataclass
|
27
30
|
class PromptTemplate:
|
28
31
|
prompt_template_id: str
|
@@ -40,6 +43,7 @@ class PromptTemplate:
|
|
40
43
|
class PromptTemplates:
|
41
44
|
prompt_templates: List[PromptTemplate]
|
42
45
|
|
46
|
+
|
43
47
|
@dataclass
|
44
48
|
class SummaryStatistics:
|
45
49
|
auto_evaluation: Dict[str, Any]
|
@@ -88,8 +92,10 @@ class TestRunRetrievalResponse:
|
|
88
92
|
human_evaluation=summary_statistics['human_evaluation']
|
89
93
|
)
|
90
94
|
|
95
|
+
|
91
96
|
class DatasetTestCaseRequest:
|
92
|
-
def __init__(self, history: Optional[List[NormalizedMessage]], inputs: InputVariables,
|
97
|
+
def __init__(self, history: Optional[List[NormalizedMessage]], inputs: InputVariables,
|
98
|
+
metadata: Optional[Dict[str, str]], output: Optional[str]) -> None:
|
93
99
|
self.history: Optional[List[NormalizedMessage]] = history
|
94
100
|
self.inputs: InputVariables = inputs
|
95
101
|
self.metadata: Optional[Dict[str, str]] = metadata
|
@@ -104,6 +110,7 @@ class DatasetTestCaseResponse:
|
|
104
110
|
self.history: Optional[List[NormalizedMessage]] = test_case.get('history')
|
105
111
|
self.metadata: Optional[Dict[str, str]] = test_case.get('metadata')
|
106
112
|
|
113
|
+
|
107
114
|
class DatasetTestCasesRetrievalResponse:
|
108
115
|
def __init__(self, test_cases: List[Dict[str, Any]]) -> None:
|
109
116
|
self.test_cases = [
|
@@ -111,6 +118,7 @@ class DatasetTestCasesRetrievalResponse:
|
|
111
118
|
for test_case in test_cases
|
112
119
|
]
|
113
120
|
|
121
|
+
|
114
122
|
class CallSupport:
|
115
123
|
def __init__(
|
116
124
|
self,
|
@@ -256,13 +264,26 @@ class CallSupport:
|
|
256
264
|
summary_statistics=json_dom['summary_statistics']
|
257
265
|
)
|
258
266
|
|
259
|
-
def record_trace(
|
267
|
+
def record_trace(
|
268
|
+
self,
|
269
|
+
project_id: str,
|
270
|
+
session_id: str,
|
271
|
+
trace_id: str,
|
272
|
+
input: str,
|
273
|
+
output: str,
|
274
|
+
agent_name: Optional[str] = None,
|
275
|
+
custom_metadata: CustomMetadata = None,
|
276
|
+
eval_results: Optional[Dict[str, Union[bool, float]]] = None
|
277
|
+
) -> None:
|
260
278
|
response = api_support.post_raw(
|
261
279
|
self.freeplay_api_key,
|
262
280
|
f'{self.api_base}/v2/projects/{project_id}/sessions/{session_id}/traces/id/{trace_id}',
|
263
281
|
{
|
282
|
+
'agent_name': agent_name,
|
264
283
|
'input': input,
|
265
|
-
'output': output
|
284
|
+
'output': output,
|
285
|
+
'custom_metadata': custom_metadata,
|
286
|
+
'eval_results': eval_results,
|
266
287
|
}
|
267
288
|
)
|
268
289
|
if response.status_code != 201:
|
@@ -277,7 +298,13 @@ class CallSupport:
|
|
277
298
|
raise freeplay_response_error('Error while deleting session.', response)
|
278
299
|
|
279
300
|
def create_test_cases(self, project_id: str, dataset_id: str, test_cases: List[DatasetTestCaseRequest]) -> None:
|
280
|
-
examples = [
|
301
|
+
examples = [
|
302
|
+
{
|
303
|
+
"history": test_case.history,
|
304
|
+
"output": test_case.output,
|
305
|
+
"metadata": test_case.metadata,
|
306
|
+
"inputs": test_case.inputs
|
307
|
+
} for test_case in test_cases]
|
281
308
|
payload: Dict[str, Any] = {"examples": examples}
|
282
309
|
url = f'{self.api_base}/v2/projects/{project_id}/datasets/id/{dataset_id}/test-cases'
|
283
310
|
|
@@ -295,5 +322,13 @@ class CallSupport:
|
|
295
322
|
json_dom = response.json()
|
296
323
|
|
297
324
|
return DatasetTestCasesRetrievalResponse(
|
298
|
-
test_cases=[
|
325
|
+
test_cases=[
|
326
|
+
{
|
327
|
+
"history": jsn["history"],
|
328
|
+
"id": jsn["id"],
|
329
|
+
"output": jsn["output"],
|
330
|
+
"values": jsn["values"],
|
331
|
+
"metadata": jsn["metadata"] if 'metadata' in jsn.keys() else None
|
332
|
+
} for jsn in json_dom
|
333
|
+
]
|
299
334
|
)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
freeplay/__init__.py,sha256=
|
1
|
+
freeplay/__init__.py,sha256=tzEFgRyriPBWPo04jTgR6MxzXUqIHBlUJZvuBj5kFiE,464
|
2
2
|
freeplay/api_support.py,sha256=Kn2x3g6yloHQl3NwFRjbZE9BnIh7d1sgwGwC0mHuvw4,2483
|
3
3
|
freeplay/errors.py,sha256=vwotUBldxDzREZOmLUeoiDoZjcvDwgH1AMwKBLhLooE,807
|
4
4
|
freeplay/freeplay.py,sha256=J04-erDD6rI2SAje_Nsf3x5Qx-Z6p8gQvGrMRHFWoD4,1602
|
@@ -8,15 +8,15 @@ freeplay/model.py,sha256=o0de_RZ2WTJ4m5OJw1ZVfC2xG6zBq_XShBrRt1laEjc,1405
|
|
8
8
|
freeplay/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
freeplay/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
freeplay/resources/customer_feedback.py,sha256=bw8MfEOKbGgn4FOyvcADrcs9GhcpNXNTgxKjBjIzywE,899
|
11
|
-
freeplay/resources/prompts.py,sha256=
|
12
|
-
freeplay/resources/recordings.py,sha256=
|
13
|
-
freeplay/resources/sessions.py,sha256=
|
11
|
+
freeplay/resources/prompts.py,sha256=fkNY5mg2Iib36e0sUVr3g-btr7bYdgQL9Ug6_f-OGi0,22790
|
12
|
+
freeplay/resources/recordings.py,sha256=uslhoKWsXq51sAbF1lBs2uGL14qvBGc3dM8ZL6-H_04,8488
|
13
|
+
freeplay/resources/sessions.py,sha256=J5A3CjiV2MFqQyxN3TWTvJaa9jmMza58mRFRq2v9iAk,3746
|
14
14
|
freeplay/resources/test_cases.py,sha256=nXL_976RwSJDT6OWDM4GEzbcOzcGkJ9ulvb0XOzCRDM,2240
|
15
15
|
freeplay/resources/test_runs.py,sha256=Tp2N-odInT5XEEWrEsVhdgfnsclOE8n92_C8gTwO2MI,2623
|
16
|
-
freeplay/support.py,sha256=
|
16
|
+
freeplay/support.py,sha256=royOK31X-TtluQQn_XG2xFoINV8GZAy3FRRO_P763NU,11800
|
17
17
|
freeplay/utils.py,sha256=Xvt4mNLXLL7E6MI2hTuDLV5cl5Y83DgdjCZSyDGMjR0,3187
|
18
|
-
freeplay-0.3.
|
19
|
-
freeplay-0.3.
|
20
|
-
freeplay-0.3.
|
21
|
-
freeplay-0.3.
|
22
|
-
freeplay-0.3.
|
18
|
+
freeplay-0.3.17.dist-info/LICENSE,sha256=_jzIw45hB1XHGxiQ8leZ0GH_X7bR_a8qgxaqnHbCUOo,1064
|
19
|
+
freeplay-0.3.17.dist-info/METADATA,sha256=lIB2GCeusLys0Dza7RxasOgbpxIRJiZAPpx5DeKMXKs,1654
|
20
|
+
freeplay-0.3.17.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
21
|
+
freeplay-0.3.17.dist-info/entry_points.txt,sha256=32s3rf2UUCqiJT4jnClEXZhdXlvl30uwpcxz-Gsy4UU,54
|
22
|
+
freeplay-0.3.17.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|