freeplay 0.3.22__tar.gz → 0.3.24a0__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.3.22 → freeplay-0.3.24a0}/PKG-INFO +1 -1
  2. {freeplay-0.3.22 → freeplay-0.3.24a0}/pyproject.toml +1 -1
  3. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/__init__.py +2 -1
  4. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/model.py +6 -0
  5. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/adapters.py +4 -1
  6. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/recordings.py +2 -6
  7. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/sessions.py +5 -2
  8. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/test_runs.py +34 -4
  9. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/support.py +33 -14
  10. {freeplay-0.3.22 → freeplay-0.3.24a0}/LICENSE +0 -0
  11. {freeplay-0.3.22 → freeplay-0.3.24a0}/README.md +0 -0
  12. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/api_support.py +0 -0
  13. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/errors.py +0 -0
  14. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/freeplay.py +0 -0
  15. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/freeplay_cli.py +0 -0
  16. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/llm_parameters.py +0 -0
  17. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/py.typed +0 -0
  18. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/__init__.py +0 -0
  19. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/customer_feedback.py +0 -0
  20. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/prompts.py +0 -0
  21. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/resources/test_cases.py +0 -0
  22. {freeplay-0.3.22 → freeplay-0.3.24a0}/src/freeplay/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: freeplay
3
- Version: 0.3.22
3
+ Version: 0.3.24a0
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.3.22"
3
+ version = "0.3.24-alpha"
4
4
  description = ""
5
5
  authors = ["FreePlay Engineering <engineering@freeplay.ai>"]
6
6
  license = "MIT"
@@ -1,6 +1,7 @@
1
1
  from .freeplay import Freeplay
2
2
  from .resources.prompts import PromptInfo
3
- from .resources.recordings import CallInfo, ResponseInfo, RecordPayload, TestRunInfo, UsageTokens
3
+ from .model import TestRunInfo
4
+ from .resources.recordings import CallInfo, ResponseInfo, RecordPayload, UsageTokens
4
5
  from .resources.sessions import SessionInfo, TraceInfo
5
6
  from .support import CustomMetadata
6
7
 
@@ -13,6 +13,12 @@ class TestRun:
13
13
  inputs: List[TestRunInput]
14
14
 
15
15
 
16
+ @dataclass
17
+ class TestRunInfo:
18
+ test_run_id: str
19
+ test_case_id: str
20
+
21
+
16
22
  class OpenAIFunctionCall(TypedDict):
17
23
  name: str
18
24
  arguments: str
@@ -35,6 +35,7 @@ class MissingFlavorError(FreeplayConfigurationError):
35
35
 
36
36
 
37
37
  class LLMAdapter(Protocol):
38
+ # This method must handle BOTH prompt template messages and provider specific messages.
38
39
  def to_llm_syntax(self, messages: List[Dict[str, Any]]) -> Union[str, List[Dict[str, Any]]]:
39
40
  pass
40
41
 
@@ -187,11 +188,13 @@ class GeminiAdapter(LLMAdapter):
187
188
  "role": self.__translate_role(message["role"]),
188
189
  "parts": [self.__map_content(content) for content in message['content']]
189
190
  })
190
- else:
191
+ elif "content" in message:
191
192
  gemini_messages.append({
192
193
  "role": self.__translate_role(message["role"]),
193
194
  "parts": [{"text": message["content"]}]
194
195
  })
196
+ else:
197
+ gemini_messages.append(copy.deepcopy(message))
195
198
 
196
199
  return gemini_messages
197
200
 
@@ -9,11 +9,12 @@ from requests import HTTPError
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
12
+ from freeplay.model import InputVariables, OpenAIFunctionCall, TestRunInfo
13
13
  from freeplay.resources.prompts import PromptInfo, MediaInputMap, MediaInput, MediaInputUrl
14
14
  from freeplay.resources.sessions import SessionInfo, TraceInfo
15
15
  from freeplay.support import CallSupport
16
16
 
17
+
17
18
  logger = logging.getLogger(__name__)
18
19
 
19
20
 
@@ -65,11 +66,6 @@ class ResponseInfo:
65
66
  response_tokens: Optional[int] = None
66
67
 
67
68
 
68
- @dataclass
69
- class TestRunInfo:
70
- test_run_id: str
71
- test_case_id: str
72
-
73
69
 
74
70
  @dataclass
75
71
  class RecordPayload:
@@ -3,6 +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.model import TestRunInfo
6
7
  from freeplay.support import CallSupport, CustomMetadata
7
8
 
8
9
 
@@ -40,7 +41,8 @@ class TraceInfo:
40
41
  self,
41
42
  project_id: str,
42
43
  output: str,
43
- eval_results: Optional[Dict[str, Union[bool, float]]] = None
44
+ eval_results: Optional[Dict[str, Union[bool, float]]] = None,
45
+ test_run_info: Optional[TestRunInfo] = None
44
46
  ) -> None:
45
47
  if self.input is None:
46
48
  raise FreeplayClientError("Input must be set before recording output")
@@ -52,7 +54,8 @@ class TraceInfo:
52
54
  output,
53
55
  agent_name=self.agent_name,
54
56
  custom_metadata=self.custom_metadata,
55
- eval_results=eval_results
57
+ eval_results=eval_results,
58
+ test_run_info=test_run_info
56
59
  )
57
60
 
58
61
 
@@ -1,8 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import List, Optional, Dict
3
3
 
4
- from freeplay.model import InputVariables
5
- from freeplay.resources.recordings import TestRunInfo
4
+ from freeplay.model import InputVariables, TestRunInfo
6
5
  from freeplay.support import CallSupport, SummaryStatistics
7
6
 
8
7
 
@@ -20,20 +19,45 @@ class TestCase:
20
19
  self.output = output
21
20
  self.history = history
22
21
 
22
+ class TraceTestCase:
23
+ def __init__(
24
+ self,
25
+ test_case_id: str,
26
+ input: str,
27
+ output: Optional[str],
28
+ ):
29
+ self.id = test_case_id
30
+ self.input = input
31
+ self.output = output
23
32
 
24
33
  @dataclass
25
34
  class TestRun:
26
35
  def __init__(
27
36
  self,
28
37
  test_run_id: str,
29
- test_cases: List[TestCase]
38
+ test_cases: List[TestCase] = [],
39
+ trace_test_cases: List[TraceTestCase] = []
30
40
  ):
31
41
  self.test_run_id = test_run_id
32
42
  self.test_cases = test_cases
43
+ self.trace_test_cases = trace_test_cases
44
+
45
+ def __must_not_be_both_trace_and_completion(self) -> None:
46
+ if self.test_cases and len(self.test_cases) > 0 and self.trace_test_cases and len(self.trace_test_cases) > 0:
47
+ raise ValueError("Test case and trace test case cannot both be present")
33
48
 
34
49
  def get_test_cases(self) -> List[TestCase]:
50
+ self.__must_not_be_both_trace_and_completion()
51
+ if len(self.trace_test_cases) > 0:
52
+ raise ValueError("Completion test cases are not present. Please use get_trace_test_cases() instead.")
35
53
  return self.test_cases
36
54
 
55
+ def get_trace_test_cases(self) -> List[TraceTestCase]:
56
+ self.__must_not_be_both_trace_and_completion()
57
+ if len(self.test_cases) > 0:
58
+ raise ValueError("Trace test cases are not present. Please use get_test_cases() instead.")
59
+ return self.trace_test_cases
60
+
37
61
  def get_test_run_info(self, test_case_id: str) -> TestRunInfo:
38
62
  return TestRunInfo(self.test_run_id, test_case_id)
39
63
 
@@ -75,8 +99,14 @@ class TestRuns:
75
99
  history=test_case.history)
76
100
  for test_case in test_run.test_cases
77
101
  ]
102
+ trace_test_cases = [
103
+ TraceTestCase(test_case_id=test_case.id,
104
+ input=test_case.input,
105
+ output=test_case.output)
106
+ for test_case in test_run.trace_test_cases
107
+ ]
78
108
 
79
- return TestRun(test_run.test_run_id, test_cases)
109
+ return TestRun(test_run.test_run_id, test_cases, trace_test_cases)
80
110
 
81
111
  def get(self, project_id: str, test_run_id: str) -> TestRunResults:
82
112
  test_run_results = self.call_support.get_test_run_results(project_id, test_run_id)
@@ -1,11 +1,11 @@
1
- from dataclasses import dataclass, field
1
+ from dataclasses import dataclass, field, asdict
2
2
  from json import JSONEncoder
3
3
  from typing import Optional, Dict, Any, List, Union, Literal
4
4
 
5
5
  from freeplay import api_support
6
6
  from freeplay.api_support import try_decode
7
7
  from freeplay.errors import freeplay_response_error, FreeplayServerError
8
- from freeplay.model import InputVariables, FeedbackValue, NormalizedMessage
8
+ from freeplay.model import InputVariables, FeedbackValue, NormalizedMessage, TestRunInfo
9
9
 
10
10
  CustomMetadata = Optional[Dict[str, Union[str, int, float, bool]]]
11
11
 
@@ -83,22 +83,38 @@ class PromptTemplateEncoder(JSONEncoder):
83
83
 
84
84
  class TestCaseTestRunResponse:
85
85
  def __init__(self, test_case: Dict[str, Any]):
86
- self.variables: InputVariables = test_case['variables']
87
86
  self.id: str = test_case['test_case_id']
87
+ self.variables: InputVariables = test_case['variables']
88
88
  self.output: Optional[str] = test_case.get('output')
89
89
  self.history: Optional[List[Dict[str, Any]]] = test_case.get('history')
90
+ self.custom_metadata: Optional[Dict[str, str]] = test_case.get('custom_metadata')
90
91
 
92
+ class TraceTestCaseTestRunResponse:
93
+ def __init__(self, test_case: Dict[str, Any]):
94
+ self.id: str = test_case['test_case_id']
95
+ self.input: str = test_case['input']
96
+ self.output: Optional[str] = test_case.get('output')
97
+ self.custom_metadata: Optional[Dict[str, str]] = test_case.get('custom_metadata')
91
98
 
92
99
  class TestRunResponse:
93
100
  def __init__(
94
101
  self,
95
102
  test_run_id: str,
96
- test_cases: List[Dict[str, Any]]
103
+ test_cases: Optional[List[Dict[str, Any]]],
104
+ trace_test_cases: Optional[List[Dict[str, Any]]]
97
105
  ):
106
+ if test_cases and trace_test_cases:
107
+ raise ValueError("Test cases and trace test cases cannot both be present.")
108
+
98
109
  self.test_cases = [
99
110
  TestCaseTestRunResponse(test_case)
100
- for test_case in test_cases
111
+ for test_case in (test_cases or []) if test_case is not None
112
+ ]
113
+ self.trace_test_cases = [
114
+ TraceTestCaseTestRunResponse(test_case)
115
+ for test_case in (trace_test_cases or []) if test_case is not None
101
116
  ]
117
+
102
118
  self.test_run_id = test_run_id
103
119
 
104
120
 
@@ -267,7 +283,7 @@ class CallSupport:
267
283
 
268
284
  json_dom = response.json()
269
285
 
270
- return TestRunResponse(json_dom['test_run_id'], json_dom['test_cases'])
286
+ return TestRunResponse(json_dom['test_run_id'], json_dom['test_cases'], json_dom['trace_test_cases'])
271
287
 
272
288
  def get_test_run_results(
273
289
  self,
@@ -299,18 +315,21 @@ class CallSupport:
299
315
  output: str,
300
316
  agent_name: Optional[str] = None,
301
317
  custom_metadata: CustomMetadata = None,
302
- eval_results: Optional[Dict[str, Union[bool, float]]] = None
318
+ eval_results: Optional[Dict[str, Union[bool, float]]] = None,
319
+ test_run_info: Optional[TestRunInfo] = None
303
320
  ) -> None:
321
+ payload = {
322
+ 'agent_name': agent_name,
323
+ 'input': input,
324
+ 'output': output,
325
+ 'custom_metadata': custom_metadata,
326
+ 'eval_results': eval_results,
327
+ 'test_run_info': asdict(test_run_info) if test_run_info else None
328
+ }
304
329
  response = api_support.post_raw(
305
330
  self.freeplay_api_key,
306
331
  f'{self.api_base}/v2/projects/{project_id}/sessions/{session_id}/traces/id/{trace_id}',
307
- {
308
- 'agent_name': agent_name,
309
- 'input': input,
310
- 'output': output,
311
- 'custom_metadata': custom_metadata,
312
- 'eval_results': eval_results,
313
- }
332
+ payload
314
333
  )
315
334
  if response.status_code != 201:
316
335
  raise freeplay_response_error('Error while recording trace.', response)
File without changes
File without changes