freeplay 0.2.32__py3-none-any.whl → 0.2.34__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/freeplay_cli.py CHANGED
@@ -9,7 +9,7 @@ import click
9
9
 
10
10
  from .completions import PromptTemplates, PromptTemplateWithMetadata
11
11
  from .errors import FreeplayClientError, FreeplayServerError
12
- from .freeplay_thin import FreeplayThin
12
+ from .thin import Freeplay
13
13
 
14
14
 
15
15
  @click.group()
@@ -40,13 +40,13 @@ def download(project_id: str, environment: str, output_dir: str) -> None:
40
40
  click.echo("Downloading prompts for project %s, environment %s, to directory %s from %s" %
41
41
  (project_id, environment, output_dir, freeplay_api_url))
42
42
 
43
- fp_client = FreeplayThin(
43
+ fp_client = Freeplay(
44
44
  freeplay_api_key=FREEPLAY_API_KEY,
45
45
  api_base=freeplay_api_url
46
46
  )
47
47
 
48
48
  try:
49
- prompts: PromptTemplates = fp_client.get_prompts(project_id, tag=environment)
49
+ prompts: PromptTemplates = fp_client.prompts.get_all(project_id, environment=environment)
50
50
  click.echo("Found %s prompt templates" % len(prompts.templates))
51
51
 
52
52
  for prompt in prompts.templates:
@@ -0,0 +1,11 @@
1
+ from .freeplay_thin import Freeplay
2
+ from .model import CallInfo, ResponseInfo, RecordPayload, PromptInfo, TestRunInfo
3
+
4
+ __all__ = [
5
+ 'Freeplay',
6
+ 'CallInfo',
7
+ 'PromptInfo',
8
+ 'RecordPayload',
9
+ 'ResponseInfo',
10
+ 'TestRunInfo',
11
+ ]
@@ -0,0 +1,28 @@
1
+ from freeplay.errors import FreeplayConfigurationError
2
+ from freeplay.record import DefaultRecordProcessor
3
+ from freeplay.support import CallSupport
4
+ from freeplay.thin.resources.prompts import Prompts
5
+ from freeplay.thin.resources.recordings import Recordings
6
+ from freeplay.thin.resources.sessions import Sessions
7
+ from freeplay.thin.resources.test_runs import TestRuns
8
+
9
+
10
+ class Freeplay:
11
+ def __init__(
12
+ self,
13
+ freeplay_api_key: str,
14
+ api_base: str
15
+ ) -> None:
16
+ if not freeplay_api_key or not freeplay_api_key.strip():
17
+ raise FreeplayConfigurationError("Freeplay API key not set. It must be set to the Freeplay API.")
18
+
19
+ self.call_support = CallSupport(freeplay_api_key, api_base,
20
+ DefaultRecordProcessor(freeplay_api_key, api_base))
21
+ self.freeplay_api_key = freeplay_api_key
22
+ self.api_base = api_base
23
+
24
+ # Resources ========
25
+ self.sessions = Sessions()
26
+ self.prompts = Prompts(self.call_support)
27
+ self.test_runs = TestRuns(self.call_support)
28
+ self.recordings = Recordings(self.call_support)
freeplay/thin/model.py ADDED
@@ -0,0 +1,152 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional, List, cast, Union
3
+
4
+ from freeplay.completions import OpenAIFunctionCall, ChatMessage
5
+ from freeplay.flavors import Flavor
6
+ from freeplay.llm_parameters import LLMParameters
7
+ from freeplay.model import InputVariables
8
+ from freeplay.utils import bind_template_variables
9
+
10
+
11
+ @dataclass
12
+ class CallInfo:
13
+ provider: str
14
+ model: str
15
+ start_time: float
16
+ end_time: float
17
+ model_parameters: LLMParameters
18
+
19
+
20
+ @dataclass
21
+ class PromptInfo:
22
+ prompt_template_id: str
23
+ prompt_template_version_id: str
24
+ template_name: str
25
+ environment: str
26
+ model_parameters: LLMParameters
27
+ provider: str
28
+ model: str
29
+ flavor_name: str
30
+
31
+ def get_call_info(self, start_time: float, end_time: float) -> CallInfo:
32
+ return CallInfo(
33
+ self.provider,
34
+ self.model,
35
+ start_time,
36
+ end_time,
37
+ self.model_parameters
38
+ )
39
+
40
+
41
+ @dataclass
42
+ class ResponseInfo:
43
+ is_complete: bool
44
+ function_call_response: Optional[OpenAIFunctionCall] = None
45
+ prompt_tokens: Optional[int] = None
46
+ response_tokens: Optional[int] = None
47
+
48
+
49
+ @dataclass
50
+ class TestRunInfo:
51
+ test_run_id: str
52
+ test_case_id: str
53
+
54
+
55
+ @dataclass
56
+ class RecordPayload:
57
+ all_messages: List[dict[str, str]]
58
+ inputs: InputVariables
59
+ session_id: str
60
+
61
+ prompt_info: PromptInfo
62
+ call_info: CallInfo
63
+ response_info: ResponseInfo
64
+ test_run_info: Optional[TestRunInfo] = None
65
+
66
+
67
+ @dataclass
68
+ class Session:
69
+ session_id: str
70
+
71
+
72
+ class FormattedPrompt:
73
+ def __init__(
74
+ self,
75
+ prompt_info: PromptInfo,
76
+ messages: List[dict[str, str]],
77
+ formatted_prompt: Union[str, List[dict[str, str]]]
78
+ ):
79
+ self.prompt_info = prompt_info
80
+ self.messages = messages
81
+ self.llm_prompt = formatted_prompt
82
+
83
+ def all_messages(
84
+ self,
85
+ new_message: dict[str, str]
86
+ ) -> List[dict[str, str]]:
87
+ return self.messages + [new_message]
88
+
89
+
90
+ class BoundPrompt:
91
+ def __init__(
92
+ self,
93
+ prompt_info: PromptInfo,
94
+ messages: List[dict[str, str]]
95
+ ):
96
+ self.prompt_info = prompt_info
97
+ self.messages = messages
98
+
99
+ def format(
100
+ self,
101
+ flavor_name: Optional[str] = None
102
+ ) -> FormattedPrompt:
103
+ final_flavor = flavor_name or self.prompt_info.flavor_name
104
+ flavor = Flavor.get_by_name(final_flavor)
105
+ llm_format = flavor.to_llm_syntax(cast(List[ChatMessage], self.messages))
106
+
107
+ return FormattedPrompt(
108
+ self.prompt_info,
109
+ self.messages,
110
+ cast(Union[str, List[dict[str, str]]], llm_format)
111
+ )
112
+
113
+
114
+ class TemplatePrompt:
115
+ def __init__(
116
+ self,
117
+ prompt_info: PromptInfo,
118
+ messages: List[dict[str, str]]
119
+ ):
120
+ self.prompt_info = prompt_info
121
+ self.messages = messages
122
+
123
+ def bind(self, variables: InputVariables) -> BoundPrompt:
124
+ bound_messages = [
125
+ {'role': message['role'], 'content': bind_template_variables(message['content'], variables)}
126
+ for message in self.messages
127
+ ]
128
+ return BoundPrompt(self.prompt_info, bound_messages)
129
+
130
+
131
+ @dataclass
132
+ class TestCase:
133
+ def __init__(self, test_case_id: str, variables: InputVariables):
134
+ self.id = test_case_id
135
+ self.variables = variables
136
+
137
+
138
+ @dataclass
139
+ class TestRun:
140
+ def __init__(
141
+ self,
142
+ test_run_id: str,
143
+ test_cases: List[TestCase]
144
+ ):
145
+ self.test_run_id = test_run_id
146
+ self.test_cases = test_cases
147
+
148
+ def get_test_cases(self) -> List[TestCase]:
149
+ return self.test_cases
150
+
151
+ def get_test_run_info(self, test_case_id: str) -> TestRunInfo:
152
+ return TestRunInfo(self.test_run_id, test_case_id)
File without changes
@@ -0,0 +1,64 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+ from freeplay.completions import PromptTemplates
5
+ from freeplay.errors import FreeplayConfigurationError
6
+ from freeplay.flavors import Flavor
7
+ from freeplay.model import InputVariables
8
+ from freeplay.support import CallSupport
9
+ from freeplay.thin.model import TemplatePrompt, PromptInfo, FormattedPrompt
10
+
11
+
12
+ class Prompts:
13
+ def __init__(self, call_support: CallSupport) -> None:
14
+ self.call_support = call_support
15
+
16
+ def get_all(self, project_id: str, environment: str) -> PromptTemplates:
17
+ return self.call_support.get_prompts(project_id=project_id, tag=environment)
18
+
19
+ def get(self, project_id: str, template_name: str, environment: str) -> TemplatePrompt:
20
+ prompt_template = self.call_support.get_prompt(
21
+ project_id=project_id,
22
+ template_name=template_name,
23
+ environment=environment
24
+ )
25
+
26
+ messages = json.loads(prompt_template.content)
27
+
28
+ params = prompt_template.get_params()
29
+ model = params.pop('model')
30
+
31
+ if not prompt_template.flavor_name:
32
+ raise FreeplayConfigurationError(
33
+ "Flavor must be configured in the Freeplay UI. Unable to fulfill request.")
34
+
35
+ flavor = Flavor.get_by_name(prompt_template.flavor_name)
36
+
37
+ prompt_info = PromptInfo(
38
+ prompt_template_id=prompt_template.prompt_template_id,
39
+ prompt_template_version_id=prompt_template.prompt_template_version_id,
40
+ template_name=prompt_template.name,
41
+ environment=environment,
42
+ model_parameters=params,
43
+ provider=flavor.provider,
44
+ model=model,
45
+ flavor_name=prompt_template.flavor_name
46
+ )
47
+
48
+ return TemplatePrompt(prompt_info, messages)
49
+
50
+ def get_formatted(
51
+ self,
52
+ project_id: str,
53
+ template_name: str,
54
+ environment: str,
55
+ variables: InputVariables,
56
+ flavor_name: Optional[str] = None
57
+ ) -> FormattedPrompt:
58
+ bound_prompt = self.get(
59
+ project_id=project_id,
60
+ template_name=template_name,
61
+ environment=environment
62
+ ).bind(variables=variables)
63
+
64
+ return bound_prompt.format(flavor_name)
@@ -0,0 +1,49 @@
1
+ import json
2
+
3
+ from freeplay.completions import PromptTemplateWithMetadata
4
+ from freeplay.errors import FreeplayClientError
5
+ from freeplay.record import RecordCallFields
6
+ from freeplay.support import CallSupport
7
+ from freeplay.thin.model import RecordPayload
8
+
9
+
10
+ class Recordings:
11
+ def __init__(self, call_support: CallSupport):
12
+ self.call_support = call_support
13
+
14
+ def create(self, record_payload: RecordPayload) -> None:
15
+ if len(record_payload.all_messages) < 1:
16
+ raise FreeplayClientError("Messages list must have at least one message. "
17
+ "The last message should be the current response.")
18
+
19
+ completion = record_payload.all_messages[-1]
20
+ history_as_string = json.dumps(record_payload.all_messages[0:-1])
21
+
22
+ template = PromptTemplateWithMetadata(
23
+ prompt_template_id=record_payload.prompt_info.prompt_template_id,
24
+ prompt_template_version_id=record_payload.prompt_info.prompt_template_version_id,
25
+ name=record_payload.prompt_info.template_name,
26
+ content=history_as_string,
27
+ flavor_name=record_payload.prompt_info.flavor_name,
28
+ params=record_payload.prompt_info.model_parameters
29
+ )
30
+
31
+ self.call_support.record_processor.record_call(
32
+ RecordCallFields(
33
+ formatted_prompt=history_as_string,
34
+ completion_content=completion['content'],
35
+ completion_is_complete=record_payload.response_info.is_complete,
36
+ start=record_payload.call_info.start_time,
37
+ end=record_payload.call_info.end_time,
38
+ session_id=record_payload.session_id,
39
+ target_template=template,
40
+ variables=record_payload.inputs,
41
+ tag=record_payload.prompt_info.environment,
42
+ test_run_id=record_payload.test_run_info.test_run_id if record_payload.test_run_info else None,
43
+ test_case_id=record_payload.test_run_info.test_case_id if record_payload.test_run_info else None,
44
+ model=record_payload.call_info.model,
45
+ provider=record_payload.prompt_info.provider,
46
+ llm_parameters=record_payload.call_info.model_parameters,
47
+ record_format_type=None # This is deprecated and unused in the API
48
+ )
49
+ )
@@ -0,0 +1,9 @@
1
+ import uuid
2
+
3
+ from freeplay.thin.model import Session
4
+
5
+
6
+ class Sessions:
7
+ # noinspection PyMethodMayBeStatic
8
+ def create(self) -> Session:
9
+ return Session(session_id=str(uuid.uuid4()))
@@ -0,0 +1,16 @@
1
+ from freeplay.support import CallSupport
2
+ from freeplay.thin.model import TestRun, TestCase
3
+
4
+
5
+ class TestRuns:
6
+ def __init__(self, call_support: CallSupport) -> None:
7
+ self.call_support = call_support
8
+
9
+ def create(self, project_id: str, testlist: str) -> TestRun:
10
+ test_run = self.call_support.create_test_run(project_id, testlist)
11
+ test_cases = [
12
+ TestCase(test_case_id=test_case.id, variables=test_case.variables)
13
+ for test_case in test_run.test_cases
14
+ ]
15
+
16
+ return TestRun(test_run.test_run_id, test_cases)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: freeplay
3
- Version: 0.2.32
3
+ Version: 0.2.34
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: FreePlay Engineering
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.8
12
12
  Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
15
16
  Requires-Dist: anthropic (>=0.7.7,<0.8.0)
16
17
  Requires-Dist: click (==8.1.7)
17
18
  Requires-Dist: dacite (>=1.8.0,<2.0.0)
@@ -48,8 +49,8 @@ fp_client = Freeplay(
48
49
 
49
50
  # Completion Request
50
51
  completion = fp_client.get_completion(project_id=FREEPLAY_PROJECT_ID,
51
- template_name="template",
52
- variables={"input_variable_name": "input_variable_value"})
52
+ template_name="template",
53
+ variables={"input_variable_name": "input_variable_value"})
53
54
  ```
54
55
 
55
56
  See the [Freeplay Docs](https://docs.freeplay.ai) for more usage examples and the API reference.
@@ -0,0 +1,27 @@
1
+ freeplay/__init__.py,sha256=74A9S9hmLq9BNHsdx0-37yDxlSukudNl9bJ0TE60Z30,61
2
+ freeplay/api_support.py,sha256=E4Mxa3Lx31TEw_X6o4s9eAR1TSxs0PhFYYaWax6dH2I,2027
3
+ freeplay/completions.py,sha256=F1rMBtQaCtn0rBQqvCurkV25g8gLtwnEod5rRvf-txY,1176
4
+ freeplay/errors.py,sha256=bPqsw32YX-xSr7O-G49M0sSFF7mq-YF1WGq928UV47s,631
5
+ freeplay/flavors.py,sha256=XroBKT8Nf92VTsuC261Nceo9f9stkkpC5CXbi8y4NEs,17236
6
+ freeplay/freeplay.py,sha256=BLlr4YXh3a624xcM93KvZGsiIGSMrIMFM07lLyVCIao,17089
7
+ freeplay/freeplay_cli.py,sha256=k9QPBedcFqpy9Ec9k7DT_y-NpVxHqRF5jKPTaQBBs1Q,3863
8
+ freeplay/llm_parameters.py,sha256=bQbfuC8EICF0XMZQa5pwI3FkQqxmCUVqHO3gYHy3Tg8,898
9
+ freeplay/model.py,sha256=kVARXUEJKbhOdWz7T8eR7npTPXlpaY9wWaEzJvbIvOU,534
10
+ freeplay/provider_config.py,sha256=hruf3Khusrwb76_-hv7ouuxmvJuaRyC1UxIw7XlJx8A,1416
11
+ freeplay/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ freeplay/record.py,sha256=_0uo0rUFZz8kwgWC0JhxFg13JyXgMfAVCkzZsuRLzBA,3354
13
+ freeplay/support.py,sha256=OOnUZOMYnLSEG_3toycWfHb2XZ8meF77uQRllYexXeo,12616
14
+ freeplay/thin/__init__.py,sha256=G9ytYsefgugzUNR7B1cuhLFYumLn3Til5mFHH8lIQRE,243
15
+ freeplay/thin/freeplay_thin.py,sha256=kwCe8vh3YjAqUPbvCwMcRVQxLGDjS-K7CAOuE-yOPiE,1137
16
+ freeplay/thin/model.py,sha256=29jNYKG6ClhtOfOmc_ljUas4JecNHUC3O3SNmyytEDs,3799
17
+ freeplay/thin/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ freeplay/thin/resources/prompts.py,sha256=AUei2sZF0RgP-l6PsyHvTNOxthjOaXvCyDRiYeFFegk,2239
19
+ freeplay/thin/resources/recordings.py,sha256=DgV-l59r95JJmOuJTvhhqV11ZlSb7YNPv3BQRlGT-_o,2356
20
+ freeplay/thin/resources/sessions.py,sha256=EX4VjZBEzUm0-jgUpIuF-eV_QxE458OrwQ1iCsUG7hc,196
21
+ freeplay/thin/resources/test_runs.py,sha256=_B3KcllvHqz0pRlMkKXgXMJIg5vHh2ie1CdWR5e-YnM,569
22
+ freeplay/utils.py,sha256=cRCCIzVqWNDKlTI-DDhXGyCkplbd-X4qzDs__aUpvww,1840
23
+ freeplay-0.2.34.dist-info/LICENSE,sha256=_jzIw45hB1XHGxiQ8leZ0GH_X7bR_a8qgxaqnHbCUOo,1064
24
+ freeplay-0.2.34.dist-info/METADATA,sha256=uAaAyh8GYScvYdy0RPsKEbyfmdym9SG2_7UvpQ8yrWE,1676
25
+ freeplay-0.2.34.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
26
+ freeplay-0.2.34.dist-info/entry_points.txt,sha256=32s3rf2UUCqiJT4jnClEXZhdXlvl30uwpcxz-Gsy4UU,54
27
+ freeplay-0.2.34.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.6.1
2
+ Generator: poetry-core 1.8.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
freeplay/freeplay_thin.py DELETED
@@ -1,22 +0,0 @@
1
- from .completions import PromptTemplates
2
- from .errors import FreeplayConfigurationError
3
- from .record import DefaultRecordProcessor
4
- from .support import CallSupport
5
-
6
-
7
- class FreeplayThin:
8
- def __init__(
9
- self,
10
- freeplay_api_key: str,
11
- api_base: str
12
- ) -> None:
13
- if not freeplay_api_key or not freeplay_api_key.strip():
14
- raise FreeplayConfigurationError("Freeplay API key not set. It must be set to the Freeplay API.")
15
-
16
- self.call_support = CallSupport(freeplay_api_key, api_base,
17
- DefaultRecordProcessor(freeplay_api_key, api_base))
18
- self.freeplay_api_key = freeplay_api_key
19
- self.api_base = api_base
20
-
21
- def get_prompts(self, project_id: str, tag: str) -> PromptTemplates:
22
- return self.call_support.get_prompts(project_id=project_id, tag=tag)
@@ -1,20 +0,0 @@
1
- freeplay/__init__.py,sha256=74A9S9hmLq9BNHsdx0-37yDxlSukudNl9bJ0TE60Z30,61
2
- freeplay/api_support.py,sha256=E4Mxa3Lx31TEw_X6o4s9eAR1TSxs0PhFYYaWax6dH2I,2027
3
- freeplay/completions.py,sha256=F1rMBtQaCtn0rBQqvCurkV25g8gLtwnEod5rRvf-txY,1176
4
- freeplay/errors.py,sha256=bPqsw32YX-xSr7O-G49M0sSFF7mq-YF1WGq928UV47s,631
5
- freeplay/flavors.py,sha256=XroBKT8Nf92VTsuC261Nceo9f9stkkpC5CXbi8y4NEs,17236
6
- freeplay/freeplay.py,sha256=BLlr4YXh3a624xcM93KvZGsiIGSMrIMFM07lLyVCIao,17089
7
- freeplay/freeplay_cli.py,sha256=nasbc_ckSr5-YtUKfg_w-6X1geQZ9s5u79VzRULGsbs,3868
8
- freeplay/freeplay_thin.py,sha256=WHoVCkS30mDM7ghjDadgfj48BNL5EvaXktUsGfeXlIY,867
9
- freeplay/llm_parameters.py,sha256=bQbfuC8EICF0XMZQa5pwI3FkQqxmCUVqHO3gYHy3Tg8,898
10
- freeplay/model.py,sha256=kVARXUEJKbhOdWz7T8eR7npTPXlpaY9wWaEzJvbIvOU,534
11
- freeplay/provider_config.py,sha256=hruf3Khusrwb76_-hv7ouuxmvJuaRyC1UxIw7XlJx8A,1416
12
- freeplay/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- freeplay/record.py,sha256=_0uo0rUFZz8kwgWC0JhxFg13JyXgMfAVCkzZsuRLzBA,3354
14
- freeplay/support.py,sha256=OOnUZOMYnLSEG_3toycWfHb2XZ8meF77uQRllYexXeo,12616
15
- freeplay/utils.py,sha256=cRCCIzVqWNDKlTI-DDhXGyCkplbd-X4qzDs__aUpvww,1840
16
- freeplay-0.2.32.dist-info/LICENSE,sha256=_jzIw45hB1XHGxiQ8leZ0GH_X7bR_a8qgxaqnHbCUOo,1064
17
- freeplay-0.2.32.dist-info/METADATA,sha256=o9MiS7BrkV0M2Erp7bKomeRUBgveiqW6XQf7LLyVA4s,1633
18
- freeplay-0.2.32.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
19
- freeplay-0.2.32.dist-info/entry_points.txt,sha256=32s3rf2UUCqiJT4jnClEXZhdXlvl30uwpcxz-Gsy4UU,54
20
- freeplay-0.2.32.dist-info/RECORD,,