freeplay 0.2.35__tar.gz → 0.2.37__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 (26) hide show
  1. {freeplay-0.2.35 → freeplay-0.2.37}/PKG-INFO +2 -1
  2. {freeplay-0.2.35 → freeplay-0.2.37}/pyproject.toml +1 -1
  3. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/flavors.py +1 -1
  4. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/model.py +1 -1
  5. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/support.py +1 -1
  6. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/freeplay_thin.py +12 -3
  7. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/resources/prompts.py +83 -15
  8. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/resources/recordings.py +2 -2
  9. {freeplay-0.2.35 → freeplay-0.2.37}/LICENSE +0 -0
  10. {freeplay-0.2.35 → freeplay-0.2.37}/README.md +0 -0
  11. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/__init__.py +0 -0
  12. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/api_support.py +0 -0
  13. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/completions.py +0 -0
  14. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/errors.py +0 -0
  15. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/freeplay.py +0 -0
  16. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/freeplay_cli.py +0 -0
  17. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/llm_parameters.py +0 -0
  18. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/provider_config.py +0 -0
  19. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/py.typed +0 -0
  20. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/record.py +0 -0
  21. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/__init__.py +0 -0
  22. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/resources/__init__.py +0 -0
  23. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/resources/customer_feedback.py +0 -0
  24. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/resources/sessions.py +0 -0
  25. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/thin/resources/test_runs.py +0 -0
  26. {freeplay-0.2.35 → freeplay-0.2.37}/src/freeplay/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: freeplay
3
- Version: 0.2.35
3
+ Version: 0.2.37
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)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "freeplay"
3
- version = "0.2.35"
3
+ version = "0.2.37"
4
4
  description = ""
5
5
  authors = ["FreePlay Engineering <engineering@freeplay.ai>"]
6
6
  license = "MIT"
@@ -50,7 +50,7 @@ class Flavor(ABC):
50
50
  pass
51
51
 
52
52
  @abstractmethod
53
- def to_llm_syntax(self, messages: List[ChatMessage]) -> str | List[ChatMessage]:
53
+ def to_llm_syntax(self, messages: List[ChatMessage]) -> Union[str, List[ChatMessage]]:
54
54
  raise NotImplementedError()
55
55
 
56
56
  @abstractmethod
@@ -3,7 +3,7 @@ from typing import List, Union, Any, Dict, Mapping
3
3
 
4
4
  from pydantic import RootModel
5
5
 
6
- InputValue = Union[str, int, bool, dict[str, Any], list[Any]]
6
+ InputValue = Union[str, int, bool, Dict[str, Any], List[Any]]
7
7
  InputVariable = RootModel[Union[Dict[str, "InputVariable"], List["InputVariable"], str, int, bool, float]]
8
8
  InputVariable.model_rebuild()
9
9
 
@@ -28,7 +28,7 @@ class TestRunResponse:
28
28
  def __init__(
29
29
  self,
30
30
  test_run_id: str,
31
- test_cases: list[JsonDom]
31
+ test_cases: List[JsonDom]
32
32
  ):
33
33
  self.test_cases = [
34
34
  TestCaseTestRunResponse(test_case)
@@ -1,8 +1,10 @@
1
+ from typing import Optional
2
+
1
3
  from freeplay.errors import FreeplayConfigurationError
2
4
  from freeplay.record import DefaultRecordProcessor
3
5
  from freeplay.support import CallSupport
4
6
  from freeplay.thin.resources.customer_feedback import CustomerFeedback
5
- from freeplay.thin.resources.prompts import Prompts
7
+ from freeplay.thin.resources.prompts import Prompts, APITemplateResolver, TemplateResolver
6
8
  from freeplay.thin.resources.recordings import Recordings
7
9
  from freeplay.thin.resources.sessions import Sessions
8
10
  from freeplay.thin.resources.test_runs import TestRuns
@@ -12,7 +14,8 @@ class Freeplay:
12
14
  def __init__(
13
15
  self,
14
16
  freeplay_api_key: str,
15
- api_base: str
17
+ api_base: str,
18
+ template_resolver: Optional[TemplateResolver] = None
16
19
  ) -> None:
17
20
  if not freeplay_api_key or not freeplay_api_key.strip():
18
21
  raise FreeplayConfigurationError("Freeplay API key not set. It must be set to the Freeplay API.")
@@ -22,9 +25,15 @@ class Freeplay:
22
25
  self.freeplay_api_key = freeplay_api_key
23
26
  self.api_base = api_base
24
27
 
28
+ resolver: TemplateResolver
29
+ if template_resolver is None:
30
+ resolver = APITemplateResolver(self.call_support)
31
+ else:
32
+ resolver = template_resolver
33
+
25
34
  # Resources ========
26
35
  self.customer_feedback = CustomerFeedback(self.call_support)
27
- self.prompts = Prompts(self.call_support)
36
+ self.prompts = Prompts(self.call_support, resolver)
28
37
  self.recordings = Recordings(self.call_support)
29
38
  self.sessions = Sessions()
30
39
  self.test_runs = TestRuns(self.call_support)
@@ -1,8 +1,10 @@
1
1
  import json
2
+ from abc import ABC, abstractmethod
2
3
  from dataclasses import dataclass
3
- from typing import Optional, List, Union, cast
4
+ from pathlib import Path
5
+ from typing import Dict, Optional, List, Union, cast
4
6
 
5
- from freeplay.completions import PromptTemplates, ChatMessage
7
+ from freeplay.completions import PromptTemplates, ChatMessage, PromptTemplateWithMetadata
6
8
  from freeplay.errors import FreeplayConfigurationError
7
9
  from freeplay.flavors import Flavor
8
10
  from freeplay.llm_parameters import LLMParameters
@@ -27,8 +29,8 @@ class FormattedPrompt:
27
29
  def __init__(
28
30
  self,
29
31
  prompt_info: PromptInfo,
30
- messages: List[dict[str, str]],
31
- formatted_prompt: Union[str, List[dict[str, str]]]
32
+ messages: List[Dict[str, str]],
33
+ formatted_prompt: Union[str, List[Dict[str, str]]]
32
34
  ):
33
35
  self.prompt_info = prompt_info
34
36
  self.messages = messages
@@ -36,8 +38,8 @@ class FormattedPrompt:
36
38
 
37
39
  def all_messages(
38
40
  self,
39
- new_message: dict[str, str]
40
- ) -> List[dict[str, str]]:
41
+ new_message: Dict[str, str]
42
+ ) -> List[Dict[str, str]]:
41
43
  return self.messages + [new_message]
42
44
 
43
45
 
@@ -45,7 +47,7 @@ class BoundPrompt:
45
47
  def __init__(
46
48
  self,
47
49
  prompt_info: PromptInfo,
48
- messages: List[dict[str, str]]
50
+ messages: List[Dict[str, str]]
49
51
  ):
50
52
  self.prompt_info = prompt_info
51
53
  self.messages = messages
@@ -61,7 +63,7 @@ class BoundPrompt:
61
63
  return FormattedPrompt(
62
64
  self.prompt_info,
63
65
  self.messages,
64
- cast(Union[str, List[dict[str, str]]], llm_format)
66
+ cast(Union[str, List[Dict[str, str]]], llm_format)
65
67
  )
66
68
 
67
69
 
@@ -69,7 +71,7 @@ class TemplatePrompt:
69
71
  def __init__(
70
72
  self,
71
73
  prompt_info: PromptInfo,
72
- messages: List[dict[str, str]]
74
+ messages: List[Dict[str, str]]
73
75
  ):
74
76
  self.prompt_info = prompt_info
75
77
  self.messages = messages
@@ -82,19 +84,85 @@ class TemplatePrompt:
82
84
  return BoundPrompt(self.prompt_info, bound_messages)
83
85
 
84
86
 
87
+ class TemplateResolver(ABC):
88
+ @abstractmethod
89
+ def get_prompts(self, project_id: str, environment: str) -> PromptTemplates:
90
+ pass
91
+
92
+
93
+ class FilesystemTemplateResolver(TemplateResolver):
94
+
95
+ def __init__(self, freeplay_directory: Path):
96
+ FilesystemTemplateResolver.__validate_freeplay_directory(freeplay_directory)
97
+ self.prompts_directory = freeplay_directory / "freeplay" / "prompts"
98
+
99
+ def get_prompts(self, project_id: str, environment: str) -> PromptTemplates:
100
+ self.__validate_prompt_directory(project_id, environment)
101
+
102
+ directory = self.prompts_directory / project_id / environment
103
+ prompt_file_paths = directory.glob("*.json")
104
+
105
+ prompt_list = []
106
+ for prompt_file_path in prompt_file_paths:
107
+ json_dom = json.loads(prompt_file_path.read_text())
108
+
109
+ prompt_list.append(PromptTemplateWithMetadata(
110
+ prompt_template_id=json_dom.get('prompt_template_id'),
111
+ prompt_template_version_id=json_dom.get('prompt_template_version_id'),
112
+ name=json_dom.get('name'),
113
+ content=json_dom.get('content'),
114
+ flavor_name=json_dom.get('metadata').get('flavor_name'),
115
+ params=json_dom.get('metadata').get('params')
116
+ ))
117
+
118
+ return PromptTemplates(prompt_list)
119
+
120
+ @staticmethod
121
+ def __validate_freeplay_directory(freeplay_directory: Path) -> None:
122
+ if not freeplay_directory.is_dir():
123
+ raise FreeplayConfigurationError(
124
+ "Path for prompt templates is not a valid directory (%s)" % freeplay_directory
125
+ )
126
+
127
+ prompts_directory = freeplay_directory / "freeplay" / "prompts"
128
+ if not prompts_directory.is_dir():
129
+ raise FreeplayConfigurationError(
130
+ "Invalid path for prompt templates (%s). "
131
+ "Did not find a freeplay/prompts directory underneath." % freeplay_directory
132
+ )
133
+
134
+ def __validate_prompt_directory(self, project_id: str, environment: str) -> None:
135
+ maybe_prompt_dir = self.prompts_directory / project_id / environment
136
+ if not maybe_prompt_dir.is_dir():
137
+ raise FreeplayConfigurationError(
138
+ "Could not find prompt template directory for project ID %s and environment %s." %
139
+ (project_id, environment)
140
+ )
141
+
142
+
143
+ class APITemplateResolver(TemplateResolver):
144
+
145
+ def __init__(self, call_support: CallSupport):
146
+ self.call_support = call_support
147
+
148
+ def get_prompts(self, project_id: str, environment: str) -> PromptTemplates:
149
+ return self.call_support.get_prompts(
150
+ project_id=project_id,
151
+ tag=environment
152
+ )
153
+
154
+
85
155
  class Prompts:
86
- def __init__(self, call_support: CallSupport) -> None:
156
+ def __init__(self, call_support: CallSupport, template_resolver: TemplateResolver) -> None:
87
157
  self.call_support = call_support
158
+ self.template_resolver = template_resolver
88
159
 
89
160
  def get_all(self, project_id: str, environment: str) -> PromptTemplates:
90
161
  return self.call_support.get_prompts(project_id=project_id, tag=environment)
91
162
 
92
163
  def get(self, project_id: str, template_name: str, environment: str) -> TemplatePrompt:
93
- prompt_template = self.call_support.get_prompt(
94
- project_id=project_id,
95
- template_name=template_name,
96
- environment=environment
97
- )
164
+ prompt_templates = self.template_resolver.get_prompts(project_id, environment)
165
+ prompt_template = self.call_support.find_template_by_name(prompt_templates, template_name)
98
166
 
99
167
  messages = json.loads(prompt_template.content)
100
168
 
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  import logging
3
3
  from dataclasses import dataclass
4
- from typing import Optional, List
4
+ from typing import Dict, Optional, List
5
5
 
6
6
  from freeplay import api_support
7
7
  from freeplay.completions import PromptTemplateWithMetadata, OpenAIFunctionCall
@@ -50,7 +50,7 @@ class TestRunInfo:
50
50
 
51
51
  @dataclass
52
52
  class RecordPayload:
53
- all_messages: List[dict[str, str]]
53
+ all_messages: List[Dict[str, str]]
54
54
  inputs: InputVariables
55
55
 
56
56
  session_info: SessionInfo
File without changes
File without changes