Skip to content

Commit

Permalink
Replace radio buttons with clickable buttons in Mesop UI (#302)
Browse files Browse the repository at this point in the history
* wip

* tests fixed

* import checks added

* Added error message

* Added error message
  • Loading branch information
davorrunje authored Oct 2, 2024
1 parent 61202d8 commit a60a820
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 55 deletions.
3 changes: 3 additions & 0 deletions docs/docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ search:
- base
- [ASGI](api/fastagency/base/ASGI.md)
- [AskingMessage](api/fastagency/base/AskingMessage.md)
- [Error](api/fastagency/base/Error.md)
- [FunctionCallExecution](api/fastagency/base/FunctionCallExecution.md)
- [IOMessage](api/fastagency/base/IOMessage.md)
- [IOMessageVisitor](api/fastagency/base/IOMessageVisitor.md)
Expand Down Expand Up @@ -94,6 +95,8 @@ search:
- [FastAgencyCLIPythonVersionError](api/fastagency/exceptions/FastAgencyCLIPythonVersionError.md)
- [FastAgencyError](api/fastagency/exceptions/FastAgencyError.md)
- [FastAgencyWSGINotImplementedError](api/fastagency/exceptions/FastAgencyWSGINotImplementedError.md)
- helpers
- [check_imports](api/fastagency/helpers/check_imports.md)
- logging
- [get_logger](api/fastagency/logging/get_logger.md)
- runtime
Expand Down
11 changes: 11 additions & 0 deletions docs/docs/en/api/fastagency/base/Error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
# 0.5 - API
# 2 - Release
# 3 - Contributing
# 5 - Template Page
# 10 - Default
search:
boost: 0.5
---

::: fastagency.base.Error
11 changes: 11 additions & 0 deletions docs/docs/en/api/fastagency/helpers/check_imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
# 0.5 - API
# 2 - Release
# 3 - Contributing
# 5 - Template Page
# 10 - Default
search:
boost: 0.5
---

::: fastagency.helpers.check_imports
6 changes: 5 additions & 1 deletion fastagency/api/openapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from .client import OpenAPI
from ...helpers import check_imports

check_imports(["fastapi_code_generator", "fastapi", "requests"], "openapi")

from .client import OpenAPI # noqa: E402

__all__ = ["OpenAPI"]
11 changes: 11 additions & 0 deletions fastagency/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"multiple_choice",
"system_message",
"workflow_completed",
"error",
]


Expand Down Expand Up @@ -76,6 +77,7 @@ def _get_message_class(type: Optional[MessageType]) -> "Type[IOMessage]":
"multiple_choice": MultipleChoice,
"system_message": SystemMessage,
"workflow_completed": WorkflowCompleted,
"error": Error,
}
return lookup[type]

Expand Down Expand Up @@ -161,6 +163,12 @@ class WorkflowCompleted(IOMessage):
result: Optional[str] = None


@dataclass
class Error(IOMessage):
short: Optional[str] = None
long: Optional[str] = None


class IOMessageVisitor(ABC):
def visit(self, message: IOMessage) -> Optional[str]:
method_name = f"visit_{message.type}"
Expand Down Expand Up @@ -195,6 +203,9 @@ def visit_system_message(self, message: SystemMessage) -> Optional[str]:
def visit_workflow_completed(self, message: WorkflowCompleted) -> Optional[str]:
return self.visit_default(message)

def visit_error(self, message: Error) -> Optional[str]:
return self.visit_default(message)


# @dataclass
# class IOStreamingMessage:
Expand Down
15 changes: 15 additions & 0 deletions fastagency/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import importlib

__all__ = ["check_imports"]


def check_imports(package_names: list[str], target_name: str) -> None:
not_importable = [
f"'{package_name}'"
for package_name in package_names
if importlib.util.find_spec(package_name) is None
]
if len(not_importable) > 0:
raise ImportError(
f"Package(s) {', '.join(not_importable)} not found. Please install it with:\n\npip install \"fastagency[{target_name}]\"\n"
)
7 changes: 6 additions & 1 deletion fastagency/runtime/autogen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
from .base import AutoGenWorkflows, IOStreamAdapter
from ...helpers import check_imports

check_imports(["autogen"], "autogen")


from .base import AutoGenWorkflows, IOStreamAdapter # noqa: E402

__all__ = ["IOStreamAdapter", "AutoGenWorkflows"]
5 changes: 4 additions & 1 deletion fastagency/ui/mesop/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import sys

from ...exceptions import FastAgencyCLIPythonVersionError
from ...helpers import check_imports

if sys.version_info < (3, 10):
raise FastAgencyCLIPythonVersionError("Mesop requires Python 3.10 or higher")

from .base import MesopUI
check_imports(["mesop"], "mesop")

from .base import MesopUI # noqa: E402

__all__ = ["MesopUI"]
43 changes: 21 additions & 22 deletions fastagency/ui/mesop/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,48 +265,47 @@ def conversation_worker(ui: MesopUI, subconversation: MesopUI) -> None:
initial_message=initial_message,
)

except Exception as ex:
ui.process_message(
IOMessage.create(
sender="user",
recipient="workflow",
type="system_message",
message={
"heading": "Workflow Exception",
"body": f"Ending workflow with exception: {ex}",
"heading": "Workflow END",
"body": f"Ending workflow with result: {result}",
},
)
)

ui.process_message(
IOMessage.create(
sender="user",
recipient="workflow",
type="workflow_completed",
result=None,
result=result,
)
)
return

ui.process_message(
IOMessage.create(
sender="user",
recipient="workflow",
type="system_message",
message={
"heading": "Workflow END",
"body": f"Ending workflow with result: {result}",
},
except Exception as ex:
ui.process_message(
IOMessage.create(
sender="system",
recipient="user",
type="error",
short=f"Exception raised: `{type(ex)}`",
long=str(ex.args[0]),
)
)
)

ui.process_message(
IOMessage.create(
sender="user",
recipient="workflow",
type="workflow_completed",
result=result,
ui.process_message(
IOMessage.create(
sender="user",
recipient="workflow",
type="workflow_completed",
result="Exception raised",
)
)
)
return

ui = MesopUI()
subconversation = ui.create_subconversation()
Expand Down
1 change: 1 addition & 0 deletions fastagency/ui/mesop/data_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ConversationMessage:
conversation_id: str = ""
feedback: list[str] = field(default_factory=list)
feedback_completed: bool = False
collapsed: Optional[bool] = None


@dataclass
Expand Down
67 changes: 41 additions & 26 deletions fastagency/ui/mesop/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from ...base import (
AskingMessage,
Error,
FunctionCallExecution,
IOMessage,
IOMessageVisitor,
Expand Down Expand Up @@ -118,6 +119,7 @@ def _is_completed(self) -> bool:
return self._conversation_message.feedback_completed

def _provide_feedback(self, feedback: str) -> Iterator[None]:
logger.info(f"_provide_feedback({feedback=})")
state = me.state(State)
conversation = state.conversation
conversation.feedback = ""
Expand All @@ -136,6 +138,9 @@ def message_content_to_markdown(self, msg: IOMessage) -> str:

return "\n".join([f"**{k}**: {v} <br>" for k, v in d["content"].items()])

def _render_content(self, content: str, style: me.Style) -> None:
me.markdown(content, style=style)

def visit_default(
self,
message: IOMessage,
Expand All @@ -159,7 +164,8 @@ def visit_default(

content = content or self.message_content_to_markdown(message)

me.markdown(content, style=style.md or self._styles.message.default.md)
# me.markdown(content, style=style.md or self._styles.message.default.md)
self._render_content(content, style.md or self._styles.message.default.md)

if inner_callback:
inner_callback()
Expand All @@ -173,6 +179,14 @@ def visit_text_message(self, message: TextMessage) -> None:
style=self._styles.message.text,
)

def visit_error(self, message: Error) -> None:
self.visit_default(
message,
content=f"### {message.short}\n{message.long}",
style=self._styles.message.error,
# error=True,
)

def visit_system_message(self, message: SystemMessage) -> None:
content = (
f"""#### **{message.message['heading']}**
Expand Down Expand Up @@ -245,35 +259,35 @@ def visit_multiple_choice(self, message: MultipleChoice) -> str:
return self._visit_many_choices(message)

def _visit_single_choice(self, message: MultipleChoice) -> str:
def on_change(ev: me.RadioChangeEvent) -> Iterator[None]:
feedback = ev.value
self._conversation_message.feedback = [feedback]
def on_click(ev: me.ClickEvent) -> Iterator[None]:
self._conversation_message.feedback_completed = True
yield from self._provide_feedback(feedback)
self._conversation_message.feedback = [ev.key]
yield from self._provide_feedback(ev.key)

# base_color = "#dff"
prompt = message.prompt if message.prompt else "Please enter a value"
if message.choices:
options = (
me.RadioOption(
label=(choice if choice != message.default else choice + " *"),
value=choice,
)
for choice in message.choices
)
if self._has_feedback():
pre_selected = {"value": self._conversation_message.feedback[0]}
else:
pre_selected = {}

def inner_callback() -> None:
me.radio(
on_change=on_change,
disabled=self._readonly or self._is_completed(),
options=options,
style=self._styles.message.single_choice_inner.radio,
**pre_selected,
)
with me.box(
style=self._styles.message.single_choice_inner.box,
):
for choice in message.choices:
disabled = self._readonly or self._is_completed()
selected = choice in self._conversation_message.feedback
if selected:
style = self._styles.message.single_choice_inner.selected_button
elif disabled:
style = self._styles.message.single_choice_inner.disabled_button
else:
style = self._styles.message.single_choice_inner.button
me.button(
label=choice,
on_click=on_click,
color="primary",
type="flat",
key=choice,
disabled=disabled,
style=style,
)

self.visit_default(
message,
Expand Down Expand Up @@ -363,7 +377,8 @@ def render_error_message(
)

logger.info(f"render_error_message: {content=}")
me.markdown(content, style=style.md or self._styles.message.default.md)
# me.markdown(content, style=style.md or self._styles.message.default.md)
self._render_content(content, style.md or self._styles.message.default.md)

def process_message(self, message: IOMessage) -> Optional[str]:
try:
Expand Down
Loading

0 comments on commit a60a820

Please sign in to comment.