freeplay 0.3.6__tar.gz → 0.3.8__tar.gz
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-0.3.6 → freeplay-0.3.8}/PKG-INFO +2 -1
- {freeplay-0.3.6 → freeplay-0.3.8}/pyproject.toml +2 -2
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/resources/prompts.py +59 -16
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/resources/recordings.py +3 -1
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/support.py +6 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/utils.py +14 -1
- {freeplay-0.3.6 → freeplay-0.3.8}/LICENSE +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/README.md +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/__init__.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/api_support.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/errors.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/freeplay.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/freeplay_cli.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/llm_parameters.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/model.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/py.typed +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/resources/__init__.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/resources/customer_feedback.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/resources/sessions.py +0 -0
- {freeplay-0.3.6 → freeplay-0.3.8}/src/freeplay/resources/test_runs.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: freeplay
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.8
|
4
4
|
Summary:
|
5
5
|
License: MIT
|
6
6
|
Author: FreePlay Engineering
|
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
16
17
|
Requires-Dist: click (==8.1.7)
|
17
18
|
Requires-Dist: dacite (>=1.8.0,<2.0.0)
|
18
19
|
Requires-Dist: pystache (>=0.6.5,<0.7.0)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "freeplay"
|
3
|
-
version = "0.3.
|
3
|
+
version = "0.3.8"
|
4
4
|
description = ""
|
5
5
|
authors = ["FreePlay Engineering <engineering@freeplay.ai>"]
|
6
6
|
license = "MIT"
|
@@ -16,7 +16,7 @@ pystache = "^0.6.5"
|
|
16
16
|
[tool.poetry.group.dev.dependencies]
|
17
17
|
mypy = "^1"
|
18
18
|
types-requests = "^2.31"
|
19
|
-
anthropic = {
|
19
|
+
anthropic = {extras = ["bedrock"], version = "^0.39.0"}
|
20
20
|
openai = "^1"
|
21
21
|
boto3 = "^1.34.97"
|
22
22
|
google-cloud-aiplatform = "1.51.0"
|
@@ -1,18 +1,21 @@
|
|
1
1
|
import copy
|
2
2
|
import json
|
3
3
|
from abc import ABC, abstractmethod
|
4
|
-
from dataclasses import dataclass
|
4
|
+
from dataclasses import asdict, dataclass
|
5
|
+
import logging
|
5
6
|
from pathlib import Path
|
6
|
-
from typing import Dict, Optional, List, cast, Any, Union
|
7
|
+
from typing import Dict, Optional, List, cast, Any, Union, overload
|
8
|
+
from anthropic.types.message import Message as AnthropicMessage
|
9
|
+
from openai.types.chat import ChatCompletionMessage as OpenAIMessage
|
7
10
|
|
8
11
|
from freeplay.errors import FreeplayConfigurationError, FreeplayClientError, log_freeplay_client_warning
|
9
12
|
from freeplay.llm_parameters import LLMParameters
|
10
13
|
from freeplay.model import InputVariables
|
11
|
-
from freeplay.support import CallSupport
|
14
|
+
from freeplay.support import CallSupport, ToolSchema
|
12
15
|
from freeplay.support import PromptTemplate, PromptTemplates, PromptTemplateMetadata
|
13
16
|
from freeplay.utils import bind_template_variables
|
14
17
|
|
15
|
-
|
18
|
+
logger = logging.getLogger(__name__)
|
16
19
|
class MissingFlavorError(FreeplayConfigurationError):
|
17
20
|
def __init__(self, flavor_name: str):
|
18
21
|
super().__init__(
|
@@ -20,6 +23,12 @@ class MissingFlavorError(FreeplayConfigurationError):
|
|
20
23
|
'a different model in the Freeplay UI.'
|
21
24
|
)
|
22
25
|
|
26
|
+
class UnsupportedToolSchemaError(FreeplayConfigurationError):
|
27
|
+
def __init__(self) -> None:
|
28
|
+
super().__init__(
|
29
|
+
f'Tool schema not supported for this model and provider.'
|
30
|
+
)
|
31
|
+
|
23
32
|
|
24
33
|
# SDK-Exposed Classes
|
25
34
|
@dataclass
|
@@ -42,10 +51,12 @@ class FormattedPrompt:
|
|
42
51
|
prompt_info: PromptInfo,
|
43
52
|
messages: List[Dict[str, str]],
|
44
53
|
formatted_prompt: Optional[List[Dict[str, str]]] = None,
|
45
|
-
formatted_prompt_text: Optional[str] = None
|
54
|
+
formatted_prompt_text: Optional[str] = None,
|
55
|
+
tool_schema: Optional[List[Dict[str, Any]]] = None
|
46
56
|
):
|
47
57
|
self.prompt_info = prompt_info
|
48
58
|
self.llm_prompt = formatted_prompt
|
59
|
+
self.tool_schema = tool_schema
|
49
60
|
if formatted_prompt_text:
|
50
61
|
self.llm_prompt_text = formatted_prompt_text
|
51
62
|
|
@@ -58,19 +69,23 @@ class FormattedPrompt:
|
|
58
69
|
|
59
70
|
def all_messages(
|
60
71
|
self,
|
61
|
-
new_message: Dict[str, str]
|
62
|
-
) -> List[Dict[str,
|
63
|
-
|
64
|
-
|
72
|
+
new_message: Union[Dict[str, str], AnthropicMessage, OpenAIMessage]
|
73
|
+
) -> List[Dict[str, Any]]:
|
74
|
+
if isinstance(new_message, AnthropicMessage) or isinstance(new_message, OpenAIMessage):
|
75
|
+
return self.messages + [new_message.to_dict()]
|
76
|
+
elif isinstance(new_message, dict):
|
77
|
+
return self.messages + [new_message]
|
65
78
|
|
66
79
|
class BoundPrompt:
|
67
80
|
def __init__(
|
68
81
|
self,
|
69
82
|
prompt_info: PromptInfo,
|
70
|
-
messages: List[Dict[str, str]]
|
83
|
+
messages: List[Dict[str, str]],
|
84
|
+
tool_schema: Optional[List[ToolSchema]] = None
|
71
85
|
):
|
72
86
|
self.prompt_info = prompt_info
|
73
87
|
self.messages = messages
|
88
|
+
self.tool_schema = tool_schema
|
74
89
|
|
75
90
|
@staticmethod
|
76
91
|
def __format_messages_for_flavor(
|
@@ -113,6 +128,25 @@ class BoundPrompt:
|
|
113
128
|
return formatted
|
114
129
|
|
115
130
|
raise MissingFlavorError(flavor_name)
|
131
|
+
|
132
|
+
@staticmethod
|
133
|
+
def __format_tool_schema(flavor_name: str, tool_schema: List[ToolSchema]) -> List[Dict[str, Any]]:
|
134
|
+
if flavor_name == 'anthropic_chat':
|
135
|
+
return [{
|
136
|
+
'name': tool_schema.name,
|
137
|
+
'description': tool_schema.description,
|
138
|
+
'input_schema': tool_schema.parameters
|
139
|
+
} for tool_schema in tool_schema]
|
140
|
+
elif flavor_name in ['openai_chat', 'azure_openai_chat']:
|
141
|
+
return [
|
142
|
+
{
|
143
|
+
'function': asdict(tool_schema),
|
144
|
+
'type': 'function'
|
145
|
+
} for tool_schema in tool_schema
|
146
|
+
]
|
147
|
+
|
148
|
+
raise UnsupportedToolSchemaError()
|
149
|
+
|
116
150
|
|
117
151
|
def format(
|
118
152
|
self,
|
@@ -121,17 +155,24 @@ class BoundPrompt:
|
|
121
155
|
final_flavor = flavor_name or self.prompt_info.flavor_name
|
122
156
|
formatted_prompt = BoundPrompt.__format_messages_for_flavor(final_flavor, self.messages)
|
123
157
|
|
158
|
+
formatted_tool_schema = BoundPrompt.__format_tool_schema(
|
159
|
+
final_flavor,
|
160
|
+
self.tool_schema
|
161
|
+
) if self.tool_schema else None
|
162
|
+
|
124
163
|
if isinstance(formatted_prompt, str):
|
125
164
|
return FormattedPrompt(
|
126
165
|
prompt_info=self.prompt_info,
|
127
166
|
messages=self.messages,
|
128
|
-
formatted_prompt_text=formatted_prompt
|
167
|
+
formatted_prompt_text=formatted_prompt,
|
168
|
+
tool_schema=formatted_tool_schema
|
129
169
|
)
|
130
170
|
else:
|
131
171
|
return FormattedPrompt(
|
132
172
|
prompt_info=self.prompt_info,
|
133
173
|
messages=self.messages,
|
134
|
-
formatted_prompt=formatted_prompt
|
174
|
+
formatted_prompt=formatted_prompt,
|
175
|
+
tool_schema=formatted_tool_schema
|
135
176
|
)
|
136
177
|
|
137
178
|
|
@@ -139,9 +180,11 @@ class TemplatePrompt:
|
|
139
180
|
def __init__(
|
140
181
|
self,
|
141
182
|
prompt_info: PromptInfo,
|
142
|
-
messages: List[Dict[str, str]]
|
183
|
+
messages: List[Dict[str, str]],
|
184
|
+
tool_schema: Optional[List[ToolSchema]] = None
|
143
185
|
):
|
144
186
|
self.prompt_info = prompt_info
|
187
|
+
self.tool_schema = tool_schema
|
145
188
|
self.messages = messages
|
146
189
|
|
147
190
|
def bind(self, variables: InputVariables, history: Optional[List[Dict[str, str]]] = None) -> BoundPrompt:
|
@@ -172,7 +215,7 @@ class TemplatePrompt:
|
|
172
215
|
'content': bind_template_variables(msg['content'], variables)},
|
173
216
|
)
|
174
217
|
|
175
|
-
return BoundPrompt(self.prompt_info, bound_messages)
|
218
|
+
return BoundPrompt(self.prompt_info, bound_messages, self.tool_schema)
|
176
219
|
|
177
220
|
|
178
221
|
class TemplateResolver(ABC):
|
@@ -410,7 +453,7 @@ class Prompts:
|
|
410
453
|
project_id=prompt.project_id
|
411
454
|
)
|
412
455
|
|
413
|
-
return TemplatePrompt(prompt_info, prompt.content)
|
456
|
+
return TemplatePrompt(prompt_info, prompt.content, prompt.tool_schema)
|
414
457
|
|
415
458
|
def get_by_version_id(self, project_id: str, template_id: str, version_id: str) -> TemplatePrompt:
|
416
459
|
prompt = self.template_resolver.get_prompt_version_id(project_id, template_id, version_id)
|
@@ -443,7 +486,7 @@ class Prompts:
|
|
443
486
|
project_id=prompt.project_id
|
444
487
|
)
|
445
488
|
|
446
|
-
return TemplatePrompt(prompt_info, prompt.content)
|
489
|
+
return TemplatePrompt(prompt_info, prompt.content, prompt.tool_schema)
|
447
490
|
|
448
491
|
def get_formatted(
|
449
492
|
self,
|
@@ -9,7 +9,7 @@ from freeplay import api_support
|
|
9
9
|
from freeplay.errors import FreeplayClientError, FreeplayError
|
10
10
|
from freeplay.llm_parameters import LLMParameters
|
11
11
|
from freeplay.model import InputVariables, OpenAIFunctionCall
|
12
|
-
from freeplay.resources.prompts import PromptInfo
|
12
|
+
from freeplay.resources.prompts import FormattedPrompt, PromptInfo
|
13
13
|
from freeplay.resources.sessions import SessionInfo, TraceInfo
|
14
14
|
from freeplay.support import CallSupport
|
15
15
|
|
@@ -59,6 +59,7 @@ class RecordPayload:
|
|
59
59
|
session_info: SessionInfo
|
60
60
|
prompt_info: PromptInfo
|
61
61
|
call_info: CallInfo
|
62
|
+
tool_schema: Optional[List[Dict[str, Any]]] = None
|
62
63
|
response_info: Optional[ResponseInfo] = None
|
63
64
|
test_run_info: Optional[TestRunInfo] = None
|
64
65
|
eval_results: Optional[Dict[str, Union[bool, float]]] = None
|
@@ -82,6 +83,7 @@ class Recordings:
|
|
82
83
|
record_api_payload = {
|
83
84
|
"messages": record_payload.all_messages,
|
84
85
|
"inputs": record_payload.inputs,
|
86
|
+
"tool_schema": record_payload.tool_schema,
|
85
87
|
"session_info": {"custom_metadata": record_payload.session_info.custom_metadata},
|
86
88
|
"prompt_info": {
|
87
89
|
"environment": record_payload.prompt_info.environment,
|
@@ -16,6 +16,11 @@ class PromptTemplateMetadata:
|
|
16
16
|
params: Optional[Dict[str, Any]] = None
|
17
17
|
provider_info: Optional[Dict[str, Any]] = None
|
18
18
|
|
19
|
+
@dataclass
|
20
|
+
class ToolSchema:
|
21
|
+
name: str
|
22
|
+
description: str
|
23
|
+
parameters: Dict[str, Any]
|
19
24
|
|
20
25
|
@dataclass
|
21
26
|
class PromptTemplate:
|
@@ -27,6 +32,7 @@ class PromptTemplate:
|
|
27
32
|
project_id: str
|
28
33
|
format_version: int
|
29
34
|
environment: Optional[str] = None
|
35
|
+
tool_schema: Optional[List[ToolSchema]] = None
|
30
36
|
|
31
37
|
|
32
38
|
@dataclass
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import json
|
1
2
|
from typing import Dict, Union, Optional, Any
|
2
3
|
import importlib.metadata
|
3
4
|
import platform
|
@@ -20,6 +21,18 @@ def all_valid(obj: Any) -> bool:
|
|
20
21
|
return False
|
21
22
|
|
22
23
|
|
24
|
+
class StandardPystache(pystache.Renderer): # type: ignore
|
25
|
+
|
26
|
+
def __init__(self) -> None:
|
27
|
+
super().__init__(escape=lambda s: s)
|
28
|
+
|
29
|
+
def str_coerce(self, val: Any) -> str:
|
30
|
+
if isinstance(val, dict) or isinstance(val, list):
|
31
|
+
# We hide spacing after punctuation so that the templating is the same across all SDKs.
|
32
|
+
return json.dumps(val, separators=(',', ':'))
|
33
|
+
return str(val)
|
34
|
+
|
35
|
+
|
23
36
|
def bind_template_variables(template: str, variables: InputVariables) -> str:
|
24
37
|
if not all_valid(variables):
|
25
38
|
raise FreeplayError(
|
@@ -28,7 +41,7 @@ def bind_template_variables(template: str, variables: InputVariables) -> str:
|
|
28
41
|
)
|
29
42
|
|
30
43
|
# When rendering mustache, do not escape HTML special characters.
|
31
|
-
rendered: str =
|
44
|
+
rendered: str = StandardPystache().render(template, variables)
|
32
45
|
return rendered
|
33
46
|
|
34
47
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|