Merging PR_218 openai_rev package with new streamlit chat app
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022)
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Functions and data structures shared by session_state.py and widgets.py"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Callable, Dict, Generic, Optional, Tuple, TypeVar, Union
|
||||
|
||||
from typing_extensions import Final, TypeAlias
|
||||
|
||||
from streamlit.errors import StreamlitAPIException
|
||||
from streamlit.proto.Arrow_pb2 import Arrow
|
||||
from streamlit.proto.Button_pb2 import Button
|
||||
from streamlit.proto.CameraInput_pb2 import CameraInput
|
||||
from streamlit.proto.Checkbox_pb2 import Checkbox
|
||||
from streamlit.proto.ColorPicker_pb2 import ColorPicker
|
||||
from streamlit.proto.Components_pb2 import ComponentInstance
|
||||
from streamlit.proto.DateInput_pb2 import DateInput
|
||||
from streamlit.proto.DownloadButton_pb2 import DownloadButton
|
||||
from streamlit.proto.FileUploader_pb2 import FileUploader
|
||||
from streamlit.proto.MultiSelect_pb2 import MultiSelect
|
||||
from streamlit.proto.NumberInput_pb2 import NumberInput
|
||||
from streamlit.proto.Radio_pb2 import Radio
|
||||
from streamlit.proto.Selectbox_pb2 import Selectbox
|
||||
from streamlit.proto.Slider_pb2 import Slider
|
||||
from streamlit.proto.TextArea_pb2 import TextArea
|
||||
from streamlit.proto.TextInput_pb2 import TextInput
|
||||
from streamlit.proto.TimeInput_pb2 import TimeInput
|
||||
from streamlit.type_util import ValueFieldName
|
||||
|
||||
# Protobuf types for all widgets.
|
||||
WidgetProto: TypeAlias = Union[
|
||||
Arrow,
|
||||
Button,
|
||||
CameraInput,
|
||||
Checkbox,
|
||||
ColorPicker,
|
||||
ComponentInstance,
|
||||
DateInput,
|
||||
DownloadButton,
|
||||
FileUploader,
|
||||
MultiSelect,
|
||||
NumberInput,
|
||||
Radio,
|
||||
Selectbox,
|
||||
Slider,
|
||||
TextArea,
|
||||
TextInput,
|
||||
TimeInput,
|
||||
]
|
||||
|
||||
GENERATED_WIDGET_ID_PREFIX: Final = "$$GENERATED_WIDGET_ID"
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
|
||||
|
||||
WidgetArgs: TypeAlias = Tuple[Any, ...]
|
||||
WidgetKwargs: TypeAlias = Dict[str, Any]
|
||||
WidgetCallback: TypeAlias = Callable[..., None]
|
||||
|
||||
# A deserializer receives the value from whatever field is set on the
|
||||
# WidgetState proto, and returns a regular python value. A serializer
|
||||
# receives a regular python value, and returns something suitable for
|
||||
# a value field on WidgetState proto. They should be inverses.
|
||||
WidgetDeserializer: TypeAlias = Callable[[Any, str], T]
|
||||
WidgetSerializer: TypeAlias = Callable[[T], Any]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WidgetMetadata(Generic[T]):
|
||||
"""Metadata associated with a single widget. Immutable."""
|
||||
|
||||
id: str
|
||||
deserializer: WidgetDeserializer[T] = field(repr=False)
|
||||
serializer: WidgetSerializer[T] = field(repr=False)
|
||||
value_type: ValueFieldName
|
||||
|
||||
# An optional user-code callback invoked when the widget's value changes.
|
||||
# Widget callbacks are called at the start of a script run, before the
|
||||
# body of the script is executed.
|
||||
callback: WidgetCallback | None = None
|
||||
callback_args: WidgetArgs | None = None
|
||||
callback_kwargs: WidgetKwargs | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RegisterWidgetResult(Generic[T_co]):
|
||||
"""Result returned by the `register_widget` family of functions/methods.
|
||||
|
||||
Should be usable by widget code to determine what value to return, and
|
||||
whether to update the UI.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : T_co
|
||||
The widget's current value, or, in cases where the true widget value
|
||||
could not be determined, an appropriate fallback value.
|
||||
|
||||
This value should be returned by the widget call.
|
||||
value_changed : bool
|
||||
True if the widget's value is different from the value most recently
|
||||
returned from the frontend.
|
||||
|
||||
Implies an update to the frontend is needed.
|
||||
"""
|
||||
|
||||
value: T_co
|
||||
value_changed: bool
|
||||
|
||||
@classmethod
|
||||
def failure(
|
||||
cls, deserializer: WidgetDeserializer[T_co]
|
||||
) -> "RegisterWidgetResult[T_co]":
|
||||
"""The canonical way to construct a RegisterWidgetResult in cases
|
||||
where the true widget value could not be determined.
|
||||
"""
|
||||
return cls(value=deserializer(None, ""), value_changed=False)
|
||||
|
||||
|
||||
def compute_widget_id(
|
||||
element_type: str, element_proto: WidgetProto, user_key: Optional[str] = None
|
||||
) -> str:
|
||||
"""Compute the widget id for the given widget. This id is stable: a given
|
||||
set of inputs to this function will always produce the same widget id output.
|
||||
|
||||
The widget id includes the user_key so widgets with identical arguments can
|
||||
use it to be distinct.
|
||||
|
||||
The widget id includes an easily identified prefix, and the user_key as a
|
||||
suffix, to make it easy to identify it and know if a key maps to it.
|
||||
|
||||
Does not mutate the element_proto object.
|
||||
"""
|
||||
h = hashlib.new("md5")
|
||||
h.update(element_type.encode("utf-8"))
|
||||
h.update(element_proto.SerializeToString())
|
||||
return f"{GENERATED_WIDGET_ID_PREFIX}-{h.hexdigest()}-{user_key}"
|
||||
|
||||
|
||||
def user_key_from_widget_id(widget_id: str) -> Optional[str]:
|
||||
"""Return the user key portion of a widget id, or None if the id does not
|
||||
have a user key.
|
||||
|
||||
TODO This will incorrectly indicate no user key if the user actually provides
|
||||
"None" as a key, but we can't avoid this kind of problem while storing the
|
||||
string representation of the no-user-key sentinel as part of the widget id.
|
||||
"""
|
||||
user_key = widget_id.split("-", maxsplit=2)[-1]
|
||||
user_key = None if user_key == "None" else user_key
|
||||
return user_key
|
||||
|
||||
|
||||
def is_widget_id(key: str) -> bool:
|
||||
"""True if the given session_state key has the structure of a widget ID."""
|
||||
return key.startswith(GENERATED_WIDGET_ID_PREFIX)
|
||||
|
||||
|
||||
def is_keyed_widget_id(key: str) -> bool:
|
||||
"""True if the given session_state key has the structure of a widget ID with a user_key."""
|
||||
return is_widget_id(key) and not key.endswith("-None")
|
||||
|
||||
|
||||
def require_valid_user_key(key: str) -> None:
|
||||
"""Raise an Exception if the given user_key is invalid."""
|
||||
if is_widget_id(key):
|
||||
raise StreamlitAPIException(
|
||||
f"Keys beginning with {GENERATED_WIDGET_ID_PREFIX} are reserved."
|
||||
)
|
||||
Reference in New Issue
Block a user