freeplay 0.4.0__tar.gz → 0.5.0a1__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.
Files changed (22) hide show
  1. {freeplay-0.4.0 → freeplay-0.5.0a1}/PKG-INFO +1 -1
  2. {freeplay-0.4.0 → freeplay-0.5.0a1}/pyproject.toml +1 -1
  3. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/model.py +18 -1
  4. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/prompts.py +5 -21
  5. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/recordings.py +38 -24
  6. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/test_runs.py +46 -26
  7. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/support.py +31 -4
  8. {freeplay-0.4.0 → freeplay-0.5.0a1}/LICENSE +0 -0
  9. {freeplay-0.4.0 → freeplay-0.5.0a1}/README.md +0 -0
  10. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/__init__.py +0 -0
  11. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/api_support.py +0 -0
  12. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/errors.py +0 -0
  13. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/freeplay.py +0 -0
  14. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/freeplay_cli.py +0 -0
  15. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/llm_parameters.py +0 -0
  16. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/py.typed +0 -0
  17. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/__init__.py +0 -0
  18. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/adapters.py +0 -0
  19. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/customer_feedback.py +0 -0
  20. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/sessions.py +0 -0
  21. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/resources/test_cases.py +0 -0
  22. {freeplay-0.4.0 → freeplay-0.5.0a1}/src/freeplay/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: freeplay
3
- Version: 0.4.0
3
+ Version: 0.5.0a1
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: FreePlay Engineering
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "freeplay"
3
- version = "0.4.0"
3
+ version = "0.5.0a1"
4
4
  description = ""
5
5
  authors = ["FreePlay Engineering <engineering@freeplay.ai>"]
6
6
  license = "MIT"
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import List, Union, Any, Dict, Mapping, TypedDict, Literal
2
+ from typing import Any, Dict, List, Literal, Mapping, TypedDict, Union
3
3
 
4
4
  InputValue = Union[str, int, bool, float, Dict[str, Any], List[Any]]
5
5
  InputVariables = Mapping[str, InputValue]
@@ -7,6 +7,23 @@ TestRunInput = Mapping[str, InputValue]
7
7
  FeedbackValue = Union[bool, str, int, float]
8
8
 
9
9
 
10
+ @dataclass
11
+ class MediaInputUrl:
12
+ type: Literal["url"]
13
+ url: str
14
+
15
+
16
+ @dataclass
17
+ class MediaInputBase64:
18
+ type: Literal["base64"]
19
+ data: str
20
+ content_type: str
21
+
22
+
23
+ MediaInput = Union[MediaInputUrl, MediaInputBase64]
24
+ MediaInputMap = Dict[str, MediaInput]
25
+
26
+
10
27
  @dataclass
11
28
  class TestRun:
12
29
  id: str
@@ -24,7 +24,11 @@ from freeplay.errors import (
24
24
  log_freeplay_client_warning,
25
25
  )
26
26
  from freeplay.llm_parameters import LLMParameters
27
- from freeplay.model import InputVariables
27
+ from freeplay.model import (
28
+ InputVariables,
29
+ MediaInputMap,
30
+ MediaInputUrl,
31
+ )
28
32
  from freeplay.resources.adapters import (
29
33
  MediaContentBase64,
30
34
  MediaContentUrl,
@@ -96,8 +100,6 @@ class PromptInfo:
96
100
  provider: str
97
101
  model: str
98
102
  flavor_name: str
99
- project_id: str
100
-
101
103
 
102
104
  class FormattedPrompt:
103
105
  def __init__(
@@ -214,22 +216,6 @@ class BoundPrompt:
214
216
  )
215
217
 
216
218
 
217
- @dataclass
218
- class MediaInputUrl:
219
- type: Literal["url"]
220
- url: str
221
-
222
-
223
- @dataclass
224
- class MediaInputBase64:
225
- type: Literal["base64"]
226
- data: str
227
- content_type: str
228
-
229
-
230
- MediaInput = Union[MediaInputUrl, MediaInputBase64]
231
-
232
- MediaInputMap = Dict[str, MediaInput]
233
219
 
234
220
 
235
221
  def extract_media_content(media_inputs: MediaInputMap, media_slots: List[MediaSlot]) -> List[
@@ -552,7 +538,6 @@ class Prompts:
552
538
  model=model,
553
539
  flavor_name=prompt.metadata.flavor,
554
540
  provider_info=prompt.metadata.provider_info,
555
- project_id=prompt.project_id
556
541
  )
557
542
 
558
543
  return TemplatePrompt(prompt_info, prompt.content, prompt.tool_schema)
@@ -588,7 +573,6 @@ class Prompts:
588
573
  model=model,
589
574
  flavor_name=prompt.metadata.flavor,
590
575
  provider_info=prompt.metadata.provider_info,
591
- project_id=prompt.project_id
592
576
  )
593
577
 
594
578
  return TemplatePrompt(prompt_info, prompt.content, prompt.tool_schema)
@@ -1,20 +1,28 @@
1
1
  import json
2
2
  import logging
3
- from dataclasses import dataclass
4
- from typing import Any, Dict, List, Optional, Union, Literal
5
- from uuid import UUID
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, List, Literal, Optional, Union
5
+ from uuid import UUID, uuid4
6
6
 
7
7
  from requests import HTTPError
8
8
 
9
9
  from freeplay import api_support
10
10
  from freeplay.errors import FreeplayClientError, FreeplayError
11
11
  from freeplay.llm_parameters import LLMParameters
12
- from freeplay.model import InputVariables, OpenAIFunctionCall, TestRunInfo
13
- from freeplay.resources.prompts import PromptInfo, MediaInputMap, MediaInput, MediaInputUrl
12
+ from freeplay.model import (
13
+ InputVariables,
14
+ MediaInput,
15
+ MediaInputMap,
16
+ MediaInputUrl,
17
+ OpenAIFunctionCall,
18
+ TestRunInfo,
19
+ )
20
+ from freeplay.resources.prompts import (
21
+ PromptInfo,
22
+ )
14
23
  from freeplay.resources.sessions import SessionInfo, TraceInfo
15
24
  from freeplay.support import CallSupport
16
25
 
17
-
18
26
  logger = logging.getLogger(__name__)
19
27
 
20
28
 
@@ -29,11 +37,11 @@ ApiStyle = Union[Literal['batch'], Literal['default']]
29
37
 
30
38
  @dataclass
31
39
  class CallInfo:
32
- provider: str
33
- model: str
34
- start_time: float
35
- end_time: float
36
- model_parameters: LLMParameters
40
+ provider: Optional[str] = None
41
+ model: Optional[str] = None
42
+ start_time: Optional[float] = None
43
+ end_time: Optional[float] = None
44
+ model_parameters: Optional[LLMParameters] = None
37
45
  provider_info: Optional[Dict[str, Any]] = None
38
46
  usage: Optional[UsageTokens] = None
39
47
  api_style: Optional[ApiStyle] = None
@@ -69,12 +77,15 @@ class ResponseInfo:
69
77
 
70
78
  @dataclass
71
79
  class RecordPayload:
80
+ project_id: str
72
81
  all_messages: List[Dict[str, Any]]
73
- inputs: InputVariables
74
82
 
75
- session_info: SessionInfo
76
- prompt_info: PromptInfo
77
- call_info: CallInfo
83
+ session_info: SessionInfo = field(
84
+ default_factory=lambda: SessionInfo(session_id=str(uuid4()), custom_metadata=None)
85
+ )
86
+ inputs: Optional[InputVariables] = None
87
+ prompt_info: Optional[PromptInfo] = None
88
+ call_info: Optional[CallInfo] = None
78
89
  media_inputs: Optional[MediaInputMap] = None
79
90
  tool_schema: Optional[List[Dict[str, Any]]] = None
80
91
  response_info: Optional[ResponseInfo] = None
@@ -124,19 +135,25 @@ class Recordings:
124
135
  "inputs": record_payload.inputs,
125
136
  "tool_schema": record_payload.tool_schema,
126
137
  "session_info": {"custom_metadata": record_payload.session_info.custom_metadata},
127
- "prompt_info": {
138
+
139
+ }
140
+
141
+ if record_payload.prompt_info is not None:
142
+ record_api_payload["prompt_info"] = {
128
143
  "environment": record_payload.prompt_info.environment,
129
144
  "prompt_template_version_id": record_payload.prompt_info.prompt_template_version_id,
130
- },
131
- "call_info": {
145
+ }
146
+
147
+ if record_payload.call_info is not None:
148
+ record_api_payload["call_info"] = {
132
149
  "start_time": record_payload.call_info.start_time,
133
150
  "end_time": record_payload.call_info.end_time,
134
151
  "model": record_payload.call_info.model,
135
152
  "provider": record_payload.call_info.provider,
136
153
  "provider_info": record_payload.call_info.provider_info,
137
154
  "llm_parameters": record_payload.call_info.model_parameters,
155
+ "api_style": record_payload.call_info.api_style,
138
156
  }
139
- }
140
157
 
141
158
  if record_payload.completion_id is not None:
142
159
  record_api_payload['completion_id'] = str(record_payload.completion_id)
@@ -167,15 +184,12 @@ class Recordings:
167
184
  "trace_id": record_payload.trace_info.trace_id
168
185
  }
169
186
 
170
- if record_payload.call_info.usage is not None:
187
+ if record_payload.call_info is not None and record_payload.call_info.usage is not None:
171
188
  record_api_payload['call_info']['usage'] = {
172
189
  "prompt_tokens": record_payload.call_info.usage.prompt_tokens,
173
190
  "completion_tokens": record_payload.call_info.usage.completion_tokens,
174
191
  }
175
192
 
176
- if record_payload.call_info.api_style is not None:
177
- record_api_payload['call_info']['api_style'] = record_payload.call_info.api_style
178
-
179
193
  if record_payload.media_inputs is not None:
180
194
  record_api_payload['media_inputs'] = {
181
195
  name: media_inputs_to_json(media_input)
@@ -185,7 +199,7 @@ class Recordings:
185
199
  try:
186
200
  recorded_response = api_support.post_raw(
187
201
  api_key=self.call_support.freeplay_api_key,
188
- url=f'{self.call_support.api_base}/v2/projects/{record_payload.prompt_info.project_id}/sessions/{record_payload.session_info.session_id}/completions',
202
+ url=f'{self.call_support.api_base}/v2/projects/{record_payload.project_id}/sessions/{record_payload.session_info.session_id}/completions',
189
203
  payload=record_api_payload
190
204
  )
191
205
  recorded_response.raise_for_status()
@@ -1,25 +1,31 @@
1
- from dataclasses import dataclass
2
- from typing import List, Optional, Dict, Any
3
1
  import warnings
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, List, Optional, Union
4
4
 
5
- from freeplay.model import InputVariables, TestRunInfo
5
+ from freeplay.model import InputVariables, MediaInputBase64, MediaInputUrl, TestRunInfo
6
6
  from freeplay.support import CallSupport, SummaryStatistics
7
7
 
8
+
8
9
  @dataclass
9
10
  class CompletionTestCase:
10
11
  def __init__(
11
- self,
12
- test_case_id: str,
13
- variables: InputVariables,
14
- output: Optional[str],
15
- history: Optional[List[Dict[str, str]]],
16
- custom_metadata: Optional[Dict[str, str]]
12
+ self,
13
+ test_case_id: str,
14
+ variables: InputVariables,
15
+ output: Optional[str],
16
+ history: Optional[List[Dict[str, str]]],
17
+ custom_metadata: Optional[Dict[str, str]],
18
+ media_variables: Optional[
19
+ Dict[str, Union[MediaInputBase64, MediaInputUrl]]
20
+ ] = None,
17
21
  ):
18
22
  self.id = test_case_id
19
23
  self.variables = variables
20
24
  self.output = output
21
25
  self.history = history
22
26
  self.custom_metadata = custom_metadata
27
+ self.media_variables = media_variables
28
+
23
29
 
24
30
  class TestCase(CompletionTestCase):
25
31
  def __init__(self, *args: Any, **kwargs: Any) -> None:
@@ -30,44 +36,55 @@ class TestCase(CompletionTestCase):
30
36
  )
31
37
  super().__init__(*args, **kwargs)
32
38
 
39
+
33
40
  class TraceTestCase:
34
41
  def __init__(
35
- self,
36
- test_case_id: str,
37
- input: str,
38
- output: Optional[str],
39
- custom_metadata: Optional[Dict[str, str]]
42
+ self,
43
+ test_case_id: str,
44
+ input: str,
45
+ output: Optional[str],
46
+ custom_metadata: Optional[Dict[str, str]],
40
47
  ):
41
48
  self.id = test_case_id
42
49
  self.input = input
43
50
  self.output = output
44
51
  self.custom_metadata = custom_metadata
52
+
45
53
  @dataclass
46
54
  class TestRun:
47
55
  def __init__(
48
- self,
49
- test_run_id: str,
50
- test_cases: List[CompletionTestCase] = [],
51
- trace_test_cases: List[TraceTestCase] = []
56
+ self,
57
+ test_run_id: str,
58
+ test_cases: List[CompletionTestCase] = [],
59
+ trace_test_cases: List[TraceTestCase] = [],
52
60
  ):
53
61
  self.test_run_id = test_run_id
54
62
  self.test_cases = test_cases
55
63
  self.trace_test_cases = trace_test_cases
56
64
 
57
65
  def __must_not_be_both_trace_and_completion(self) -> None:
58
- if self.test_cases and len(self.test_cases) > 0 and self.trace_test_cases and len(self.trace_test_cases) > 0:
66
+ if (
67
+ self.test_cases
68
+ and len(self.test_cases) > 0
69
+ and self.trace_test_cases
70
+ and len(self.trace_test_cases) > 0
71
+ ):
59
72
  raise ValueError("Test case and trace test case cannot both be present")
60
73
 
61
74
  def get_test_cases(self) -> List[CompletionTestCase]:
62
75
  self.__must_not_be_both_trace_and_completion()
63
76
  if len(self.trace_test_cases) > 0:
64
- raise ValueError("Completion test cases are not present. Please use get_trace_test_cases() instead.")
77
+ raise ValueError(
78
+ "Completion test cases are not present. Please use get_trace_test_cases() instead."
79
+ )
65
80
  return self.test_cases
66
81
 
67
82
  def get_trace_test_cases(self) -> List[TraceTestCase]:
68
83
  self.__must_not_be_both_trace_and_completion()
69
84
  if len(self.test_cases) > 0:
70
- raise ValueError("Trace test cases are not present. Please use get_test_cases() instead.")
85
+ raise ValueError(
86
+ "Trace test cases are not present. Please use get_test_cases() instead."
87
+ )
71
88
  return self.trace_test_cases
72
89
 
73
90
  def get_test_run_info(self, test_case_id: str) -> TestRunInfo:
@@ -105,11 +122,14 @@ class TestRuns:
105
122
  test_run = self.call_support.create_test_run(
106
123
  project_id, testlist, include_outputs, name, description, flavor_name)
107
124
  test_cases = [
108
- CompletionTestCase(test_case_id=test_case.id,
109
- variables=test_case.variables,
110
- output=test_case.output,
111
- history=test_case.history,
112
- custom_metadata=test_case.custom_metadata)
125
+ CompletionTestCase(
126
+ test_case_id=test_case.id,
127
+ variables=test_case.variables,
128
+ output=test_case.output,
129
+ history=test_case.history,
130
+ custom_metadata=test_case.custom_metadata,
131
+ media_variables=test_case.media_variables,
132
+ )
113
133
  for test_case in test_run.test_cases
114
134
  ]
115
135
  trace_test_cases = [
@@ -1,11 +1,18 @@
1
- from dataclasses import dataclass, field, asdict
1
+ from dataclasses import asdict, dataclass, field
2
2
  from json import JSONEncoder
3
- from typing import Optional, Dict, Any, List, Union, Literal
3
+ from typing import Any, Dict, List, Literal, Optional, Union
4
4
 
5
5
  from freeplay import api_support
6
6
  from freeplay.api_support import try_decode
7
- from freeplay.errors import freeplay_response_error, FreeplayServerError
8
- from freeplay.model import InputVariables, FeedbackValue, NormalizedMessage, TestRunInfo
7
+ from freeplay.errors import FreeplayServerError, freeplay_response_error
8
+ from freeplay.model import (
9
+ FeedbackValue,
10
+ InputVariables,
11
+ MediaInputBase64,
12
+ MediaInputUrl,
13
+ NormalizedMessage,
14
+ TestRunInfo,
15
+ )
9
16
 
10
17
  CustomMetadata = Optional[Dict[str, Union[str, int, float, bool]]]
11
18
 
@@ -100,6 +107,26 @@ class TestCaseTestRunResponse:
100
107
  self.history: Optional[List[Dict[str, Any]]] = test_case.get('history')
101
108
  self.custom_metadata: Optional[Dict[str, str]] = test_case.get('custom_metadata')
102
109
 
110
+ if test_case.get("media_variables", None):
111
+ self.media_variables: Optional[
112
+ Dict[str, Union[MediaInputBase64, MediaInputUrl]]
113
+ ] = {}
114
+ for name, media_data in test_case.get("media_variables", {}).items():
115
+ media_type = media_data.get("type", "base64")
116
+ if media_type == "url":
117
+ self.media_variables[name] = MediaInputUrl(
118
+ type="url",
119
+ url=media_data["url"],
120
+ )
121
+ else:
122
+ self.media_variables[name] = MediaInputBase64(
123
+ type="base64",
124
+ data=media_data["data"],
125
+ content_type=media_data["content_type"],
126
+ )
127
+ else:
128
+ self.media_variables = None
129
+
103
130
 
104
131
  class TraceTestCaseTestRunResponse:
105
132
  def __init__(self, test_case: Dict[str, Any]):
File without changes
File without changes