freeplay 0.2.42__py3-none-any.whl → 0.3.0a2__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/utils.py CHANGED
@@ -1,19 +1,27 @@
1
- from typing import Dict, Union, Optional
1
+ from typing import Dict, Union, Optional, Any
2
2
  import importlib.metadata
3
3
  import platform
4
4
 
5
5
  import pystache # type: ignore
6
- from pydantic import ValidationError
7
6
 
8
7
  from .errors import FreeplayError, FreeplayConfigurationError
9
- from .model import PydanticInputVariables, InputVariables
8
+ from .model import InputVariables
9
+
10
+
11
+ # Validate that the variables are of the correct type, and do not include functions, dates, classes or None values.
12
+ def all_valid(obj: Any) -> bool:
13
+ if isinstance(obj, (int, str, bool)):
14
+ return True
15
+ elif isinstance(obj, list):
16
+ return all(all_valid(item) for item in obj)
17
+ elif isinstance(obj, dict):
18
+ return all(isinstance(key, str) and all_valid(value) for key, value in obj.items())
19
+ else:
20
+ return False
10
21
 
11
22
 
12
23
  def bind_template_variables(template: str, variables: InputVariables) -> str:
13
- # Validate that the variables are of the correct type, and do not include functions or None values.
14
- try:
15
- PydanticInputVariables.model_validate(variables)
16
- except ValidationError as err:
24
+ if not all_valid(variables):
17
25
  raise FreeplayError(
18
26
  'Variables must be a string, number, bool, or a possibly nested'
19
27
  ' list or dict of strings, numbers and booleans.'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: freeplay
3
- Version: 0.2.42
3
+ Version: 0.3.0a2
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: FreePlay Engineering
@@ -13,10 +13,8 @@ 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
- Requires-Dist: anthropic (>=0.20.0,<0.21.0)
17
16
  Requires-Dist: click (==8.1.7)
18
17
  Requires-Dist: dacite (>=1.8.0,<2.0.0)
19
- Requires-Dist: openai (>=1,<2)
20
18
  Requires-Dist: pystache (>=0.6.5,<0.7.0)
21
19
  Requires-Dist: requests (>=2.20.0,<3.0.0dev)
22
20
  Description-Content-Type: text/markdown
@@ -0,0 +1,20 @@
1
+ freeplay/__init__.py,sha256=_Uk0dUfn59IuToFeFXlhmA1-ULoA3uBWkg8DGbVomhw,346
2
+ freeplay/api_support.py,sha256=Tmc6VRcmPcIgXrDNJXz0VbBD969t5QHAyV3yZlMsYRI,2331
3
+ freeplay/errors.py,sha256=bPqsw32YX-xSr7O-G49M0sSFF7mq-YF1WGq928UV47s,631
4
+ freeplay/freeplay.py,sha256=vwtgLNcHnXTAjyuXU1cOjvYuLBqcZ-RQDAjdFAKh7q0,1479
5
+ freeplay/freeplay_cli.py,sha256=lmdsYwzdpWmUKHz_ieCzB-e6j1EnDHlVw3XIEyP_NEk,3460
6
+ freeplay/llm_parameters.py,sha256=bQbfuC8EICF0XMZQa5pwI3FkQqxmCUVqHO3gYHy3Tg8,898
7
+ freeplay/model.py,sha256=pC24wUsedD4RTI4k1BcYuDjizroeEHINH6FtEa_RLCk,384
8
+ freeplay/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ freeplay/resources/customer_feedback.py,sha256=aTM7Eez7iYmjXSpRqkHxf4pi6xBrzVnMiQCEJVfPGvg,527
10
+ freeplay/resources/prompts.py,sha256=MvrB7rAuHMzRyz6kH9NqfD5Yt33g-pmK7x0tVlyM2Cs,12476
11
+ freeplay/resources/recordings.py,sha256=6hrunYZtWFgGBZx4HzZRKcEYCzHbmaBVH48lBcXrL3w,5181
12
+ freeplay/resources/sessions.py,sha256=ioWdeTM9BSrEWKrFH66ysQIw5kCTlCVopJDmWtFgHz0,868
13
+ freeplay/resources/test_runs.py,sha256=2NAwoW_Yx7K8YM7QyQ3sv9dN6p0gjNgUAR7wSzLYP6w,1452
14
+ freeplay/support.py,sha256=7BizelIEIaSRFj2IL53-RQkjncUNlUS-DPUln2VxoJg,4532
15
+ freeplay/utils.py,sha256=8ZncuwCnzsAhRsaoxOMGa0Py8kXqGHlB9Avr3n79fk0,2064
16
+ freeplay-0.3.0a2.dist-info/LICENSE,sha256=_jzIw45hB1XHGxiQ8leZ0GH_X7bR_a8qgxaqnHbCUOo,1064
17
+ freeplay-0.3.0a2.dist-info/METADATA,sha256=btKL7QhbCkbZKOo_EplzS33-fsoSH8q66iAnaYI_1iY,1604
18
+ freeplay-0.3.0a2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
19
+ freeplay-0.3.0a2.dist-info/entry_points.txt,sha256=32s3rf2UUCqiJT4jnClEXZhdXlvl30uwpcxz-Gsy4UU,54
20
+ freeplay-0.3.0a2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.8.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
freeplay/completions.py DELETED
@@ -1,56 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Any, Dict, List, Optional, TypedDict
3
-
4
- from openai.types.chat.chat_completion_chunk import ChoiceDeltaFunctionCall
5
-
6
- from .llm_parameters import LLMParameters
7
-
8
-
9
- class ChatMessage(TypedDict):
10
- role: str
11
- content: str
12
-
13
-
14
- class OpenAIFunctionCall(TypedDict):
15
- name: str
16
- arguments: str
17
-
18
-
19
- @dataclass
20
- class CompletionResponse:
21
- content: str
22
- is_complete: bool
23
- openai_function_call: Optional[OpenAIFunctionCall] = None
24
-
25
-
26
- @dataclass
27
- class ChatCompletionResponse:
28
- content: str
29
- is_complete: bool
30
- message_history: List[ChatMessage]
31
-
32
-
33
- @dataclass
34
- class PromptTemplateWithMetadata:
35
- prompt_template_id: str
36
- prompt_template_version_id: str
37
-
38
- name: str
39
- content: str
40
- flavor_name: Optional[str]
41
- params: Optional[Dict[str, Any]]
42
-
43
- def get_params(self) -> LLMParameters:
44
- return LLMParameters.empty() if self.params is None else LLMParameters(self.params)
45
-
46
-
47
- @dataclass
48
- class PromptTemplates:
49
- templates: List[PromptTemplateWithMetadata]
50
-
51
-
52
- @dataclass
53
- class CompletionChunk:
54
- text: str
55
- is_complete: bool
56
- openai_function_call: Optional[ChoiceDeltaFunctionCall] = None
freeplay/flavors.py DELETED
@@ -1,459 +0,0 @@
1
- import json
2
- from abc import abstractmethod, ABC
3
- from copy import copy
4
- from typing import cast, Any, Dict, Generator, List, Optional, Union
5
-
6
- import anthropic
7
- import openai
8
- from openai import AuthenticationError, BadRequestError, Stream
9
- from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageParam
10
-
11
- from .completions import CompletionChunk, PromptTemplateWithMetadata, CompletionResponse, ChatCompletionResponse, \
12
- ChatMessage, OpenAIFunctionCall
13
- from .errors import FreeplayConfigurationError, LLMClientError, LLMServerError, FreeplayError
14
- from .llm_parameters import LLMParameters
15
- from .model import InputVariables
16
- from .provider_config import AnthropicConfig, AzureConfig, OpenAIConfig, ProviderConfig
17
- from .utils import bind_template_variables
18
-
19
-
20
- class Flavor(ABC):
21
- @classmethod
22
- def get_by_name(cls, flavor_name: str) -> 'Flavor':
23
- if flavor_name == OpenAIChat.record_format_type:
24
- return OpenAIChat()
25
- elif flavor_name == AzureOpenAIChat.record_format_type:
26
- return AzureOpenAIChat()
27
- elif flavor_name == AnthropicClaudeChat.record_format_type:
28
- return AnthropicClaudeChat()
29
- else:
30
- raise FreeplayConfigurationError(
31
- f'Configured flavor ({flavor_name}) not found in SDK. Please update your SDK version or configure '
32
- 'a different model in the Freeplay UI.')
33
-
34
- @property
35
- @abstractmethod
36
- def provider(self) -> str:
37
- raise NotImplementedError()
38
-
39
- @property
40
- @abstractmethod
41
- def record_format_type(self) -> str:
42
- raise NotImplementedError()
43
-
44
- @property
45
- def _model_params_with_defaults(self) -> LLMParameters:
46
- return LLMParameters.empty()
47
-
48
- @abstractmethod
49
- def format(self, prompt_template: PromptTemplateWithMetadata, variables: InputVariables) -> str:
50
- pass
51
-
52
- @abstractmethod
53
- def to_llm_syntax(self, messages: List[ChatMessage]) -> Union[str, List[ChatMessage]]:
54
- raise NotImplementedError()
55
-
56
- @abstractmethod
57
- def call_service(
58
- self,
59
- formatted_prompt: str,
60
- provider_config: ProviderConfig,
61
- llm_parameters: LLMParameters
62
- ) -> CompletionResponse:
63
- pass
64
-
65
- @abstractmethod
66
- def call_service_stream(
67
- self,
68
- formatted_prompt: str,
69
- provider_config: ProviderConfig,
70
- llm_parameters: LLMParameters
71
- ) -> Generator[CompletionChunk, None, None]:
72
- pass
73
-
74
- def get_model_params(self, llm_parameters: LLMParameters) -> LLMParameters:
75
- return self._model_params_with_defaults.merge_and_override(llm_parameters)
76
-
77
-
78
- class ChatFlavor(Flavor, ABC):
79
- @abstractmethod
80
- def continue_chat(
81
- self,
82
- messages: List[ChatMessage],
83
- provider_config: ProviderConfig,
84
- llm_parameters: LLMParameters
85
- ) -> ChatCompletionResponse:
86
- pass
87
-
88
- @abstractmethod
89
- def continue_chat_stream(
90
- self,
91
- messages: List[ChatMessage],
92
- provider_config: ProviderConfig,
93
- llm_parameters: LLMParameters
94
- ) -> Generator[CompletionChunk, None, None]:
95
- pass
96
-
97
-
98
- class OpenAIChatFlavor(ChatFlavor, ABC):
99
-
100
- @abstractmethod
101
- def _call_openai(
102
- self,
103
- messages: List[ChatMessage],
104
- provider_config: ProviderConfig,
105
- llm_parameters: LLMParameters,
106
- stream: bool
107
- ) -> Union[ChatCompletion, openai.Stream[ChatCompletionChunk]]:
108
- pass
109
-
110
- def format(self, prompt_template: PromptTemplateWithMetadata, variables: InputVariables) -> str:
111
- # Extract messages JSON to enable formatting of individual content fields of each message. If we do not
112
- # extract the JSON, current variable interpolation will fail on JSON curly braces.
113
- messages_as_json: List[Dict[str, str]] = json.loads(prompt_template.content)
114
- formatted_messages = [
115
- {
116
- "content": bind_template_variables(message['content'], variables), "role": message['role']
117
- } for message in messages_as_json]
118
- return json.dumps(formatted_messages)
119
-
120
- def to_llm_syntax(self, messages: List[ChatMessage]) -> List[ChatMessage]:
121
- return messages
122
-
123
- def call_service(
124
- self,
125
- formatted_prompt: str,
126
- provider_config: ProviderConfig,
127
- llm_parameters: LLMParameters
128
- ) -> CompletionResponse:
129
- messages = json.loads(formatted_prompt)
130
- completion = cast(ChatCompletion, self._call_openai(messages, provider_config, llm_parameters, stream=False))
131
-
132
- return CompletionResponse(
133
- content=completion.choices[0].message.content or '',
134
- is_complete=completion.choices[0].finish_reason == 'stop',
135
- openai_function_call=self.__maybe_function_call(completion),
136
- )
137
-
138
- # noinspection PyMethodMayBeStatic
139
- def __maybe_function_call(self, completion: ChatCompletion) -> Optional[OpenAIFunctionCall]:
140
- maybe_function_call = completion.choices[0].message.function_call
141
- if maybe_function_call:
142
- return OpenAIFunctionCall(
143
- name=maybe_function_call.name,
144
- arguments=maybe_function_call.arguments
145
- )
146
- return None
147
-
148
- def call_service_stream(
149
- self,
150
- formatted_prompt: str,
151
- provider_config: ProviderConfig,
152
- llm_parameters: LLMParameters
153
- ) -> Generator[CompletionChunk, None, None]:
154
- messages = json.loads(formatted_prompt)
155
- completion_stream = cast(Stream[ChatCompletionChunk],
156
- self._call_openai(messages, provider_config, llm_parameters, stream=True))
157
- for chunk in completion_stream:
158
- yield CompletionChunk(
159
- text=chunk.choices[0].delta.content or '',
160
- is_complete=chunk.choices[0].finish_reason == 'stop',
161
- openai_function_call=chunk.choices[0].delta.function_call
162
- )
163
-
164
- def continue_chat(
165
- self,
166
- messages: List[ChatMessage],
167
- provider_config: ProviderConfig,
168
- llm_parameters: LLMParameters
169
- ) -> ChatCompletionResponse:
170
- completion = cast(ChatCompletion, self._call_openai(messages, provider_config, llm_parameters, stream=False))
171
-
172
- message_history = copy(messages)
173
- message = completion.choices[0].message
174
- message_history.append(ChatMessage(
175
- role=message.role or '',
176
- content=message.content or ''
177
- ))
178
- return ChatCompletionResponse(
179
- content=message.content or '',
180
- message_history=message_history,
181
- is_complete=completion.choices[0].finish_reason == "stop"
182
- )
183
-
184
- def continue_chat_stream(
185
- self,
186
- messages: List[ChatMessage],
187
- provider_config: ProviderConfig,
188
- llm_parameters: LLMParameters
189
- ) -> Generator[CompletionChunk, None, None]:
190
- completion_stream = cast(Stream[ChatCompletionChunk],
191
- self._call_openai(messages, provider_config, llm_parameters, stream=True))
192
- for chunk in completion_stream:
193
- yield CompletionChunk(
194
- text=chunk.choices[0].delta.content or '',
195
- is_complete=chunk.choices[0].finish_reason == "stop"
196
- )
197
-
198
-
199
- class OpenAIChat(OpenAIChatFlavor):
200
- record_format_type = "openai_chat"
201
- _model_params_with_defaults = LLMParameters({
202
- "model": "gpt-3.5-turbo"
203
- })
204
-
205
- def __init__(self) -> None:
206
- self.client: Optional[openai.OpenAI] = None
207
-
208
- @property
209
- def provider(self) -> str:
210
- return "openai"
211
-
212
- def get_openai_client(self, openai_config: Optional[OpenAIConfig]) -> openai.OpenAI:
213
- if self.client:
214
- return self.client
215
-
216
- if not openai_config:
217
- raise FreeplayConfigurationError(
218
- "Missing OpenAI key. Use a ProviderConfig to specify keys prior to getting completion.")
219
-
220
- self.client = openai.OpenAI(api_key=openai_config.api_key, base_url=openai_config.base_url)
221
- return self.client
222
-
223
- def _call_openai(
224
- self,
225
- messages: List[ChatMessage],
226
- provider_config: ProviderConfig,
227
- llm_parameters: LLMParameters,
228
- stream: bool
229
- ) -> Union[ChatCompletion, openai.Stream[ChatCompletionChunk]]:
230
- client = self.get_openai_client(provider_config.openai)
231
- try:
232
- return client.chat.completions.create(
233
- messages=cast(List[ChatCompletionMessageParam], messages),
234
- **self.get_model_params(llm_parameters),
235
- stream=stream,
236
- )
237
- except (BadRequestError, AuthenticationError) as e:
238
- raise LLMClientError("Unable to call OpenAI") from e
239
- except Exception as e:
240
- raise LLMServerError("Unable to call OpenAI") from e
241
-
242
-
243
- class AzureOpenAIChat(OpenAIChatFlavor):
244
- record_format_type = "azure_openai_chat"
245
-
246
- def __init__(self) -> None:
247
- self.client: Optional[openai.AzureOpenAI] = None
248
-
249
- @property
250
- def provider(self) -> str:
251
- return "azure"
252
-
253
- def get_azure_client(
254
- self,
255
- azure_config: Optional[AzureConfig],
256
- api_version: Optional[str] = None,
257
- endpoint: Optional[str] = None,
258
- deployment: Optional[str] = None,
259
- ) -> openai.AzureOpenAI:
260
- if self.client:
261
- return self.client
262
-
263
- if not azure_config:
264
- raise FreeplayConfigurationError(
265
- "Missing Azure key. Use a ProviderConfig to specify keys prior to getting completion.")
266
-
267
- self.client = openai.AzureOpenAI(
268
- api_key=azure_config.api_key,
269
- api_version=api_version,
270
- azure_endpoint=endpoint or '',
271
- azure_deployment=deployment,
272
- )
273
- return self.client
274
-
275
- def _call_openai(
276
- self,
277
- messages: List[ChatMessage],
278
- provider_config: ProviderConfig,
279
- llm_parameters: LLMParameters,
280
- stream: bool
281
- ) -> Any:
282
- api_version = llm_parameters.get('api_version')
283
- deployment_id = llm_parameters.get('deployment_id')
284
- resource_name = llm_parameters.get('resource_name')
285
- endpoint = f'https://{resource_name}.openai.azure.com'
286
- llm_parameters.pop('resource_name')
287
-
288
- client = self.get_azure_client(
289
- azure_config=provider_config.azure,
290
- api_version=api_version,
291
- endpoint=endpoint,
292
- deployment=deployment_id,
293
- )
294
-
295
- try:
296
- return client.chat.completions.create(
297
- messages=cast(List[ChatCompletionMessageParam], messages),
298
- **self.get_model_params(llm_parameters),
299
- stream=stream,
300
- )
301
- except (BadRequestError, AuthenticationError) as e:
302
- raise LLMClientError("Unable to call Azure") from e
303
- except Exception as e:
304
- raise LLMServerError("Unable to call Azure") from e
305
-
306
-
307
- class AnthropicClaudeChat(ChatFlavor):
308
- record_format_type = "anthropic_chat"
309
- _model_params_with_defaults = LLMParameters({
310
- "model": "claude-2",
311
- "max_tokens_to_sample": 100
312
- })
313
-
314
- def __init__(self) -> None:
315
- self.client: Optional[anthropic.Anthropic] = None
316
-
317
- @property
318
- def provider(self) -> str:
319
- return "anthropic"
320
-
321
- def get_anthropic_client(self, anthropic_config: Optional[AnthropicConfig]) -> anthropic.Client:
322
- if self.client:
323
- return self.client
324
-
325
- if not anthropic_config:
326
- raise FreeplayConfigurationError(
327
- "Missing Anthropic key. Use a ProviderConfig to specify keys prior to getting completion.")
328
-
329
- self.client = anthropic.Client(api_key=anthropic_config.api_key)
330
- return self.client
331
-
332
- # This just formats the prompt for uploading to the record endpoint.
333
- # TODO: Move this to a base class.
334
- def format(self, prompt_template: PromptTemplateWithMetadata, variables: InputVariables) -> str:
335
- # Extract messages JSON to enable formatting of individual content fields of each message. If we do not
336
- # extract the JSON, current variable interpolation will fail on JSON curly braces.
337
- messages_as_json: List[Dict[str, str]] = json.loads(prompt_template.content)
338
- formatted_messages = [
339
- {
340
- "content": bind_template_variables(message['content'], variables),
341
- "role": self.__to_anthropic_role(message['role'])
342
- } for message in messages_as_json]
343
- return json.dumps(formatted_messages)
344
-
345
- def to_llm_syntax(self, messages: List[ChatMessage]) -> str:
346
- formatted_messages = [
347
- ChatMessage(
348
- content=message['content'],
349
- role=self.__to_anthropic_role(message['role'])
350
- ) for message in messages
351
- ]
352
- return self.__to_anthropic_chat_format(formatted_messages)
353
-
354
- @staticmethod
355
- def __to_anthropic_role(role: str) -> str:
356
- if role == 'Human':
357
- return 'Human'
358
- elif role == 'assistant' or role == 'Assistant':
359
- return 'Assistant'
360
- else:
361
- # Anthropic does not support system role for now.
362
- return 'Human'
363
-
364
- @staticmethod
365
- def __to_anthropic_chat_format(messages: List[ChatMessage]) -> str:
366
- formatted_messages = []
367
- for message in messages:
368
- formatted_messages.append(f"{message['role']}: {message['content']}")
369
- formatted_messages.append('Assistant:')
370
-
371
- return "\n\n" + "\n\n".join(formatted_messages)
372
-
373
- def continue_chat(
374
- self,
375
- messages: List[ChatMessage],
376
- provider_config: ProviderConfig,
377
- llm_parameters: LLMParameters
378
- ) -> ChatCompletionResponse:
379
- formatted_prompt = self.__to_anthropic_chat_format(messages)
380
- try:
381
- client = self.get_anthropic_client(provider_config.anthropic)
382
- completion = client.completions.create(
383
- prompt=formatted_prompt,
384
- **self.get_model_params(llm_parameters)
385
- )
386
- content = completion.completion
387
- message_history = messages + [{"role": "assistant", "content": content}]
388
- return ChatCompletionResponse(
389
- content=content,
390
- is_complete=completion.stop_reason == 'stop_sequence',
391
- message_history=message_history,
392
- )
393
- except anthropic.APIError as e:
394
- raise FreeplayError("Error calling Anthropic") from e
395
-
396
- def continue_chat_stream(
397
- self,
398
- messages: List[ChatMessage],
399
- provider_config: ProviderConfig,
400
- llm_parameters: LLMParameters
401
- ) -> Generator[CompletionChunk, None, None]:
402
- formatted_prompt = self.__to_anthropic_chat_format(messages)
403
- try:
404
- client = self.get_anthropic_client(provider_config.anthropic)
405
- anthropic_response = client.completions.create(
406
- prompt=formatted_prompt,
407
- stream=True,
408
- **self.get_model_params(llm_parameters)
409
- )
410
-
411
- for chunk in anthropic_response:
412
- yield CompletionChunk(
413
- text=chunk.completion,
414
- is_complete=chunk.stop_reason == 'stop_sequence'
415
- )
416
- except anthropic.APIError as e:
417
- raise FreeplayError("Error calling Anthropic") from e
418
-
419
- def call_service(self, formatted_prompt: str, provider_config: ProviderConfig,
420
- llm_parameters: LLMParameters) -> CompletionResponse:
421
- messages = json.loads(formatted_prompt)
422
- completion = self.continue_chat(messages, provider_config, llm_parameters)
423
- return CompletionResponse(
424
- content=completion.content,
425
- is_complete=completion.is_complete,
426
- )
427
-
428
- def call_service_stream(
429
- self,
430
- formatted_prompt: str,
431
- provider_config: ProviderConfig,
432
- llm_parameters: LLMParameters
433
- ) -> Generator[CompletionChunk, None, None]:
434
- messages = json.loads(formatted_prompt)
435
- return self.continue_chat_stream(messages, provider_config, llm_parameters)
436
-
437
-
438
- def pick_flavor_from_config(completion_flavor: Optional[Flavor], ui_flavor_name: Optional[str]) -> Flavor:
439
- ui_flavor = Flavor.get_by_name(ui_flavor_name) if ui_flavor_name else None
440
- flavor = completion_flavor or ui_flavor
441
-
442
- if flavor is None:
443
- raise FreeplayConfigurationError(
444
- "Flavor must be configured on either the Freeplay client, completion call, "
445
- "or in the Freeplay UI. Unable to fulfill request.")
446
-
447
- return flavor
448
-
449
-
450
- def get_chat_flavor_from_config(completion_flavor: Optional[Flavor], ui_flavor_name: Optional[str]) -> ChatFlavor:
451
- flavor = pick_flavor_from_config(completion_flavor, ui_flavor_name)
452
- return require_chat_flavor(flavor)
453
-
454
-
455
- def require_chat_flavor(flavor: Flavor) -> ChatFlavor:
456
- if not isinstance(flavor, ChatFlavor):
457
- raise FreeplayConfigurationError('A Chat flavor is required to start a chat session.')
458
-
459
- return flavor
@@ -1,49 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Optional
3
-
4
- from .errors import FreeplayConfigurationError
5
-
6
-
7
- @dataclass
8
- class OpenAIConfig:
9
- api_key: str
10
- base_url: Optional[str] = None
11
-
12
- def validate(self) -> None:
13
- if not self.api_key or not self.api_key.strip():
14
- raise FreeplayConfigurationError("OpenAI API key not set. It must be set to make calls to the service.")
15
-
16
-
17
- @dataclass
18
- class AzureConfig(OpenAIConfig):
19
- engine: Optional[str] = None
20
- api_version: Optional[str] = None
21
-
22
- def validate(self) -> None:
23
- super().validate()
24
-
25
- if self.api_version is None:
26
- raise FreeplayConfigurationError(
27
- "OpenAI API version not set. It must be set to make calls to the service.")
28
-
29
- if self.engine is None:
30
- raise FreeplayConfigurationError("Azure engine is not set. It must be set to make calls to the service.")
31
-
32
-
33
- @dataclass
34
- class AnthropicConfig:
35
- api_key: str
36
-
37
-
38
- @dataclass
39
- class ProviderConfig:
40
- anthropic: Optional[AnthropicConfig] = None
41
- openai: Optional[OpenAIConfig] = None
42
- azure: Optional[AzureConfig] = None
43
-
44
- def validate(self) -> None:
45
- if all(config is None for config in [self.anthropic, self.openai, self.azure]):
46
- FreeplayConfigurationError("At least one provider key must be set in ProviderConfig.")
47
-
48
- if self.openai is not None:
49
- self.openai.validate()
freeplay/py.typed DELETED
File without changes