freeplay 0.2.37__py3-none-any.whl → 0.2.38__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/flavors.py +1 -1
- freeplay/support.py +45 -5
- freeplay/thin/freeplay_thin.py +5 -2
- freeplay/thin/resources/prompts.py +86 -16
- {freeplay-0.2.37.dist-info → freeplay-0.2.38.dist-info}/METADATA +1 -1
- {freeplay-0.2.37.dist-info → freeplay-0.2.38.dist-info}/RECORD +9 -9
- {freeplay-0.2.37.dist-info → freeplay-0.2.38.dist-info}/LICENSE +0 -0
- {freeplay-0.2.37.dist-info → freeplay-0.2.38.dist-info}/WHEEL +0 -0
- {freeplay-0.2.37.dist-info → freeplay-0.2.38.dist-info}/entry_points.txt +0 -0
freeplay/flavors.py
CHANGED
@@ -28,7 +28,7 @@ class Flavor(ABC):
|
|
28
28
|
return AnthropicClaudeChat()
|
29
29
|
else:
|
30
30
|
raise FreeplayConfigurationError(
|
31
|
-
'Configured flavor not found in SDK. Please update your SDK version or configure '
|
31
|
+
f'Configured flavor ({flavor_name}) not found in SDK. Please update your SDK version or configure '
|
32
32
|
'a different model in the Freeplay UI.')
|
33
33
|
|
34
34
|
@property
|
freeplay/support.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import json
|
2
2
|
import time
|
3
3
|
from copy import copy
|
4
|
+
from dataclasses import dataclass
|
4
5
|
from typing import Dict, Any, Optional, Union, List, Generator
|
5
6
|
from uuid import uuid4
|
6
7
|
|
@@ -37,6 +38,25 @@ class TestRunResponse:
|
|
37
38
|
self.test_run_id = test_run_id
|
38
39
|
|
39
40
|
|
41
|
+
@dataclass
|
42
|
+
class PromptTemplateMetadata:
|
43
|
+
provider: Optional[str]
|
44
|
+
flavor: Optional[str]
|
45
|
+
model: Optional[str]
|
46
|
+
params: Optional[Dict[str, Any]] = None
|
47
|
+
provider_info: Optional[Dict[str, Any]] = None
|
48
|
+
|
49
|
+
|
50
|
+
@dataclass
|
51
|
+
class PromptTemplate:
|
52
|
+
prompt_template_id: str
|
53
|
+
prompt_template_version_id: str
|
54
|
+
prompt_template_name: str
|
55
|
+
content: List[Dict[str, str]]
|
56
|
+
metadata: PromptTemplateMetadata
|
57
|
+
format_version: int
|
58
|
+
|
59
|
+
|
40
60
|
class CallSupport:
|
41
61
|
def __init__(
|
42
62
|
self,
|
@@ -57,10 +77,12 @@ class CallSupport:
|
|
57
77
|
raise FreeplayConfigurationError(f'Could not find template with name "{template_name}"')
|
58
78
|
return templates[0]
|
59
79
|
|
60
|
-
|
80
|
+
@staticmethod
|
81
|
+
def create_session_id() -> str:
|
61
82
|
return str(uuid4())
|
62
83
|
|
63
|
-
|
84
|
+
@staticmethod
|
85
|
+
def check_all_values_string_or_number(metadata: Optional[Dict[str, Union[str, int, float]]]) -> None:
|
64
86
|
if metadata:
|
65
87
|
for key, value in metadata.items():
|
66
88
|
if not isinstance(value, (str, int, float)):
|
@@ -79,9 +101,27 @@ class CallSupport:
|
|
79
101
|
if response.status_code != 201:
|
80
102
|
raise freeplay_response_error("Error updating customer feedback", response)
|
81
103
|
|
82
|
-
def get_prompt(self, project_id: str, template_name: str, environment: str) ->
|
83
|
-
|
84
|
-
|
104
|
+
def get_prompt(self, project_id: str, template_name: str, environment: str) -> PromptTemplate:
|
105
|
+
response = api_support.get_raw(
|
106
|
+
api_key=self.freeplay_api_key,
|
107
|
+
url=f'{self.api_base}/v2/projects/{project_id}/prompt-templates/name/{template_name}'
|
108
|
+
)
|
109
|
+
|
110
|
+
if response.status_code != 200:
|
111
|
+
raise freeplay_response_error(
|
112
|
+
f"Error getting prompt template {template_name} in project {project_id} "
|
113
|
+
f"and environment {environment}",
|
114
|
+
response
|
115
|
+
)
|
116
|
+
|
117
|
+
maybe_prompt = try_decode(PromptTemplate, response.content)
|
118
|
+
if maybe_prompt is None:
|
119
|
+
raise FreeplayServerError(
|
120
|
+
f"Error handling prompt {template_name} in project {project_id} "
|
121
|
+
f"and environment {environment}"
|
122
|
+
)
|
123
|
+
|
124
|
+
return maybe_prompt
|
85
125
|
|
86
126
|
def get_prompts(self, project_id: str, tag: str) -> PromptTemplates:
|
87
127
|
response = api_support.get_raw(
|
freeplay/thin/freeplay_thin.py
CHANGED
@@ -20,8 +20,11 @@ class Freeplay:
|
|
20
20
|
if not freeplay_api_key or not freeplay_api_key.strip():
|
21
21
|
raise FreeplayConfigurationError("Freeplay API key not set. It must be set to the Freeplay API.")
|
22
22
|
|
23
|
-
self.call_support = CallSupport(
|
24
|
-
|
23
|
+
self.call_support = CallSupport(
|
24
|
+
freeplay_api_key,
|
25
|
+
api_base,
|
26
|
+
DefaultRecordProcessor(freeplay_api_key, api_base)
|
27
|
+
)
|
25
28
|
self.freeplay_api_key = freeplay_api_key
|
26
29
|
self.api_base = api_base
|
27
30
|
|
@@ -2,17 +2,18 @@ import json
|
|
2
2
|
from abc import ABC, abstractmethod
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import Dict, Optional, List, Union, cast
|
5
|
+
from typing import Dict, Optional, List, Union, cast, Any
|
6
6
|
|
7
7
|
from freeplay.completions import PromptTemplates, ChatMessage, PromptTemplateWithMetadata
|
8
|
-
from freeplay.errors import FreeplayConfigurationError
|
8
|
+
from freeplay.errors import FreeplayConfigurationError, FreeplayClientError
|
9
9
|
from freeplay.flavors import Flavor
|
10
10
|
from freeplay.llm_parameters import LLMParameters
|
11
11
|
from freeplay.model import InputVariables
|
12
|
-
from freeplay.support import CallSupport
|
12
|
+
from freeplay.support import CallSupport, PromptTemplate, PromptTemplateMetadata
|
13
13
|
from freeplay.utils import bind_template_variables
|
14
14
|
|
15
15
|
|
16
|
+
# SDK-Exposed Classes
|
16
17
|
@dataclass
|
17
18
|
class PromptInfo:
|
18
19
|
prompt_template_id: str
|
@@ -20,6 +21,7 @@ class PromptInfo:
|
|
20
21
|
template_name: str
|
21
22
|
environment: str
|
22
23
|
model_parameters: LLMParameters
|
24
|
+
provider_info: Optional[Dict[str, Any]]
|
23
25
|
provider: str
|
24
26
|
model: str
|
25
27
|
flavor_name: str
|
@@ -89,8 +91,21 @@ class TemplateResolver(ABC):
|
|
89
91
|
def get_prompts(self, project_id: str, environment: str) -> PromptTemplates:
|
90
92
|
pass
|
91
93
|
|
94
|
+
@abstractmethod
|
95
|
+
def get_prompt(self, project_id: str, template_name: str, environment: str) -> PromptTemplate:
|
96
|
+
pass
|
97
|
+
|
92
98
|
|
93
99
|
class FilesystemTemplateResolver(TemplateResolver):
|
100
|
+
# If you think you need a change here, be sure to check the server as the translations must match. Once we have
|
101
|
+
# all the SDKs and all customers on the new common format, this translation can go away.
|
102
|
+
__role_translations = {
|
103
|
+
'system': 'system',
|
104
|
+
'user': 'user',
|
105
|
+
'assistant': 'assistant',
|
106
|
+
'Assistant': 'assistant',
|
107
|
+
'Human': 'user' # Don't think we ever store this, but in case...
|
108
|
+
}
|
94
109
|
|
95
110
|
def __init__(self, freeplay_directory: Path):
|
96
111
|
FilesystemTemplateResolver.__validate_freeplay_directory(freeplay_directory)
|
@@ -117,6 +132,52 @@ class FilesystemTemplateResolver(TemplateResolver):
|
|
117
132
|
|
118
133
|
return PromptTemplates(prompt_list)
|
119
134
|
|
135
|
+
def get_prompt(self, project_id: str, template_name: str, environment: str) -> PromptTemplate:
|
136
|
+
self.__validate_prompt_directory(project_id, environment)
|
137
|
+
|
138
|
+
expected_file: Path = self.prompts_directory / project_id / environment / f"{template_name}.json"
|
139
|
+
|
140
|
+
if not expected_file.exists():
|
141
|
+
raise FreeplayClientError(
|
142
|
+
f"Could not find prompt with name {template_name} for project "
|
143
|
+
f"{project_id} in environment {environment}"
|
144
|
+
)
|
145
|
+
|
146
|
+
json_dom = json.loads(expected_file.read_text())
|
147
|
+
|
148
|
+
format_version = json_dom.get('format_version')
|
149
|
+
|
150
|
+
if format_version == 2:
|
151
|
+
raise NotImplementedError("Cannot yet handle new format bundled prompts")
|
152
|
+
|
153
|
+
flavor_name = json_dom.get('metadata').get('flavor_name')
|
154
|
+
flavor = Flavor.get_by_name(flavor_name)
|
155
|
+
|
156
|
+
params = json_dom.get('metadata').get('params')
|
157
|
+
model = params.pop('model') if 'model' in params else None
|
158
|
+
|
159
|
+
return PromptTemplate(
|
160
|
+
format_version=2,
|
161
|
+
prompt_template_id=json_dom.get('prompt_template_id'),
|
162
|
+
prompt_template_version_id=json_dom.get('prompt_template_version_id'),
|
163
|
+
prompt_template_name=json_dom.get('name'),
|
164
|
+
content=FilesystemTemplateResolver.__normalize_roles(json.loads(json_dom.get('content'))), # type: ignore
|
165
|
+
metadata=PromptTemplateMetadata(
|
166
|
+
provider=flavor.provider,
|
167
|
+
flavor=flavor_name,
|
168
|
+
model=model,
|
169
|
+
params=params
|
170
|
+
)
|
171
|
+
)
|
172
|
+
|
173
|
+
@staticmethod
|
174
|
+
def __normalize_roles(messages: List[ChatMessage]) -> List[ChatMessage]:
|
175
|
+
normalized = []
|
176
|
+
for message in messages:
|
177
|
+
role = FilesystemTemplateResolver.__role_translations.get(message['role']) or message['role']
|
178
|
+
normalized.append(ChatMessage(role=role, content=message['content']))
|
179
|
+
return normalized
|
180
|
+
|
120
181
|
@staticmethod
|
121
182
|
def __validate_freeplay_directory(freeplay_directory: Path) -> None:
|
122
183
|
if not freeplay_directory.is_dir():
|
@@ -151,6 +212,13 @@ class APITemplateResolver(TemplateResolver):
|
|
151
212
|
tag=environment
|
152
213
|
)
|
153
214
|
|
215
|
+
def get_prompt(self, project_id: str, template_name: str, environment: str) -> PromptTemplate:
|
216
|
+
return self.call_support.get_prompt(
|
217
|
+
project_id=project_id,
|
218
|
+
template_name=template_name,
|
219
|
+
environment=environment
|
220
|
+
)
|
221
|
+
|
154
222
|
|
155
223
|
class Prompts:
|
156
224
|
def __init__(self, call_support: CallSupport, template_resolver: TemplateResolver) -> None:
|
@@ -161,32 +229,34 @@ class Prompts:
|
|
161
229
|
return self.call_support.get_prompts(project_id=project_id, tag=environment)
|
162
230
|
|
163
231
|
def get(self, project_id: str, template_name: str, environment: str) -> TemplatePrompt:
|
164
|
-
|
165
|
-
prompt_template = self.call_support.find_template_by_name(prompt_templates, template_name)
|
232
|
+
prompt = self.template_resolver.get_prompt(project_id, template_name, environment)
|
166
233
|
|
167
|
-
|
234
|
+
params = prompt.metadata.params
|
235
|
+
model = prompt.metadata.model
|
168
236
|
|
169
|
-
|
170
|
-
|
237
|
+
if not model:
|
238
|
+
raise FreeplayConfigurationError(
|
239
|
+
"Model must be configured in the Freeplay UI. Unable to fulfill request.")
|
171
240
|
|
172
|
-
if not
|
241
|
+
if not prompt.metadata.flavor:
|
173
242
|
raise FreeplayConfigurationError(
|
174
243
|
"Flavor must be configured in the Freeplay UI. Unable to fulfill request.")
|
175
244
|
|
176
|
-
flavor = Flavor.get_by_name(
|
245
|
+
flavor = Flavor.get_by_name(prompt.metadata.flavor)
|
177
246
|
|
178
247
|
prompt_info = PromptInfo(
|
179
|
-
prompt_template_id=
|
180
|
-
prompt_template_version_id=
|
181
|
-
template_name=
|
248
|
+
prompt_template_id=prompt.prompt_template_id,
|
249
|
+
prompt_template_version_id=prompt.prompt_template_version_id,
|
250
|
+
template_name=prompt.prompt_template_name,
|
182
251
|
environment=environment,
|
183
|
-
model_parameters=params,
|
252
|
+
model_parameters=cast(LLMParameters, params) or LLMParameters({}),
|
184
253
|
provider=flavor.provider,
|
185
254
|
model=model,
|
186
|
-
flavor_name=
|
255
|
+
flavor_name=prompt.metadata.flavor,
|
256
|
+
provider_info=prompt.metadata.provider_info
|
187
257
|
)
|
188
258
|
|
189
|
-
return TemplatePrompt(prompt_info,
|
259
|
+
return TemplatePrompt(prompt_info, prompt.content)
|
190
260
|
|
191
261
|
def get_formatted(
|
192
262
|
self,
|
@@ -2,7 +2,7 @@ freeplay/__init__.py,sha256=74A9S9hmLq9BNHsdx0-37yDxlSukudNl9bJ0TE60Z30,61
|
|
2
2
|
freeplay/api_support.py,sha256=u8b1e9prlR4_qOcqmFduU7U35ubQt1tWJ6bXhUdsv0c,2240
|
3
3
|
freeplay/completions.py,sha256=F1rMBtQaCtn0rBQqvCurkV25g8gLtwnEod5rRvf-txY,1176
|
4
4
|
freeplay/errors.py,sha256=bPqsw32YX-xSr7O-G49M0sSFF7mq-YF1WGq928UV47s,631
|
5
|
-
freeplay/flavors.py,sha256=
|
5
|
+
freeplay/flavors.py,sha256=9jVvhym9L5zyWM3RTBViibIgG6f6Fh1_63uMEVZAFT4,17259
|
6
6
|
freeplay/freeplay.py,sha256=BLlr4YXh3a624xcM93KvZGsiIGSMrIMFM07lLyVCIao,17089
|
7
7
|
freeplay/freeplay_cli.py,sha256=k9QPBedcFqpy9Ec9k7DT_y-NpVxHqRF5jKPTaQBBs1Q,3863
|
8
8
|
freeplay/llm_parameters.py,sha256=bQbfuC8EICF0XMZQa5pwI3FkQqxmCUVqHO3gYHy3Tg8,898
|
@@ -10,18 +10,18 @@ freeplay/model.py,sha256=7BRAuyxsLl8X-ue8wXPmAiKYp4OaYI-HkaGc3t5qtMU,534
|
|
10
10
|
freeplay/provider_config.py,sha256=hruf3Khusrwb76_-hv7ouuxmvJuaRyC1UxIw7XlJx8A,1416
|
11
11
|
freeplay/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
freeplay/record.py,sha256=69vO9vYGFL8gLrvRqLzlvodVNo05GOzKDJnCyhUha08,3615
|
13
|
-
freeplay/support.py,sha256=
|
13
|
+
freeplay/support.py,sha256=tipac902gW1TRICUgxGhO4X645XRglxHHmaqYvqfifo,14197
|
14
14
|
freeplay/thin/__init__.py,sha256=XgU_eMmTpOPCa9w5mVOyxPgwAgRFfVdRWDazYGYZtQ0,351
|
15
|
-
freeplay/thin/freeplay_thin.py,sha256=
|
15
|
+
freeplay/thin/freeplay_thin.py,sha256=0w_ECjassczsHJUcpzPxEHUDbiGo7Ey7Ba7vIaWc5ds,1619
|
16
16
|
freeplay/thin/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
freeplay/thin/resources/customer_feedback.py,sha256=aTM7Eez7iYmjXSpRqkHxf4pi6xBrzVnMiQCEJVfPGvg,527
|
18
|
-
freeplay/thin/resources/prompts.py,sha256=
|
18
|
+
freeplay/thin/resources/prompts.py,sha256=3sfH1k1AAXRfgHxfV6Acws0fAiMswgE93auRKWLGVIQ,10135
|
19
19
|
freeplay/thin/resources/recordings.py,sha256=utrGTjPPiNt1btI0Hiti9s7VvCYFWqitAx35zrXrOfg,5047
|
20
20
|
freeplay/thin/resources/sessions.py,sha256=ioWdeTM9BSrEWKrFH66ysQIw5kCTlCVopJDmWtFgHz0,868
|
21
21
|
freeplay/thin/resources/test_runs.py,sha256=L8A1tQHzYg9UmLgbNFtFa8pGXqyrcoyTtSE0sSnd9UE,1267
|
22
22
|
freeplay/utils.py,sha256=cRCCIzVqWNDKlTI-DDhXGyCkplbd-X4qzDs__aUpvww,1840
|
23
|
-
freeplay-0.2.
|
24
|
-
freeplay-0.2.
|
25
|
-
freeplay-0.2.
|
26
|
-
freeplay-0.2.
|
27
|
-
freeplay-0.2.
|
23
|
+
freeplay-0.2.38.dist-info/LICENSE,sha256=_jzIw45hB1XHGxiQ8leZ0GH_X7bR_a8qgxaqnHbCUOo,1064
|
24
|
+
freeplay-0.2.38.dist-info/METADATA,sha256=N9DOEBhiTSyDMCBm9MYBK7XserJj0JxlPhYN96NhrnI,1676
|
25
|
+
freeplay-0.2.38.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
26
|
+
freeplay-0.2.38.dist-info/entry_points.txt,sha256=32s3rf2UUCqiJT4jnClEXZhdXlvl30uwpcxz-Gsy4UU,54
|
27
|
+
freeplay-0.2.38.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|