Repository: airtai/fastagency

Issue:
Feature: add support for Together AI
Add support for Together AI

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/airtai/fastagency/pull/314?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) Report
All modified and coverable lines are covered by tests :white_check_mark:

| [Files with missing lines](https://app.codecov.io/gh/airtai/fastagency/pull/314?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) | Coverage Δ | |
|---|---|---|
| [fastagency/ui/mesop/message.py](https://app.codecov.io/gh/airtai/fastagency/pull/314?src=pr&el=tree&filepath=fastagency%2Fui%2Fmesop%2Fmessage.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZmFzdGFnZW5jeS91aS9tZXNvcC9tZXNzYWdlLnB5) | `53.41% <100.00%> (-1.41%)` | :arrow_down: |
| [fastagency/ui/mesop/styles.py](https://app.codecov.io/gh/airtai/fastagency/pull/314?src=pr&el=tree&filepath=fastagency%2Fui%2Fmesop%2Fstyles.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZmFzdGFnZW5jeS91aS9tZXNvcC9zdHlsZXMucHk=) | `100.00% <100.00%> (ø)` | |

... and [2 files with indirect coverage changes](https://app.codecov.io/gh/airtai/fastagency/pull/314/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai)


Patch:
diff --git a/fastagency/ui/mesop/message.py b/fastagency/ui/mesop/message.py
index 2b7cd958..70014201 100644
--- a/fastagency/ui/mesop/message.py
+++ b/fastagency/ui/mesop/message.py
@@ -128,14 +128,6 @@ class MesopGUIMessageVisitor(IOMessageVisitor):
         responses = send_user_feedback_to_autogen(feedback)
         yield from consume_responses(responses)
 
-    def message_content_to_markdown(self, msg: IOMessage) -> str:
-        d = msg.model_dump()
-        d.pop("sender")
-        d.pop("recipient")
-        d.pop("type")
-
-        return "\n".join([f"**{k}**: {v} <br>" for k, v in d["content"].items()])
-
     def _render_content(self, content: str, msg_md_style: me.Style) -> None:
         content = jsonify_string(content)
         me.markdown(content, style=msg_md_style)
@@ -148,8 +140,9 @@ class MesopGUIMessageVisitor(IOMessageVisitor):
         style: Optional[MesopMessageStyles] = None,
         error: Optional[bool] = False,
         inner_callback: Optional[Callable[..., None]] = None,
+        scrollable: Optional[bool] = False,
     ) -> None:
-        logger.info(f"visit_default: {message=}")
+        # logger.info(f"visit_default: {message=}")
         style = style or self._styles.message.default
         title = message.type.replace("_", " ").capitalize()
         title = "[Error] " + title if error else title
@@ -161,12 +154,14 @@ class MesopGUIMessageVisitor(IOMessageVisitor):
                 md_style=style.header_md,
             )
 
-            content = content or self.message_content_to_markdown(message)
+            content = content or json.dumps(message.model_dump()["content"])
 
-            # me.markdown(content, style=style.md or self._styles.message.default.md)
             self._render_content(
                 content,
-                style.md or self._styles.message.default.md,
+                msg_md_style=style.scrollable_md
+                or self._styles.message.default.scrollable_md
+                if scrollable
+                else style.md or self._styles.message.default.md,
             )
 
             if inner_callback:
@@ -186,6 +181,7 @@ class MesopGUIMessageVisitor(IOMessageVisitor):
             message,
             content=f"### {message.short}\n{message.long}",
             style=self._styles.message.error,
+            scrollable=True,
         )
 
     def visit_system_message(self, message: SystemMessage) -> None:
@@ -195,32 +191,36 @@ class MesopGUIMessageVisitor(IOMessageVisitor):
 {message.message['body']}
 """
             if "heading" in message.message and "body" in message.message
-            else json.dumps(message.message, indent=2)
+            else json.dumps(message.message)
         )
 
         self.visit_default(
             message,
             content=content,
             style=self._styles.message.system,
+            scrollable=True,
         )
 
     def visit_suggested_function_call(self, message: SuggestedFunctionCall) -> None:
-        content = f"""
-**function_name**: `{message.function_name}`<br>
+        content = f"""**function_name**: `{message.function_name}`<br>
 **call_id**: `{message.call_id}`<br>
-**arguments**: {json.dumps(message.arguments)}
-"""
-        logger.warning(f"visit_suggested_function_call: {content=}")
+**arguments**: {json.dumps(message.arguments)}"""
         self.visit_default(
             message,
             content=content,
             style=self._styles.message.suggested_function_call,
+            scrollable=True,
         )
 
     def visit_function_call_execution(self, message: FunctionCallExecution) -> None:
+        content = f"""**function_name**: `{message.function_name}`<br>
+**call_id**: `{message.call_id}`<br>
+**retval**: {message.retval}"""
         return self.visit_default(
             message,
+            content=content,
             style=self._styles.message.function_call_execution,
+            scrollable=True,
         )
 
     def visit_text_input(self, message: TextInput) -> str:
diff --git a/fastagency/ui/mesop/styles.py b/fastagency/ui/mesop/styles.py
index d347bc99..05388f62 100644
--- a/fastagency/ui/mesop/styles.py
+++ b/fastagency/ui/mesop/styles.py
@@ -105,6 +105,12 @@ MSG_DEFAULT_BOX_STYLE = me.Style(
 )
 
 MSG_DEFAULT_MD_STYLE = me.Style(padding=me.Padding.all(16))
+MSG_DEFAULT_MD_SCROLABLE_STYLE = me.Style(
+    padding=me.Padding(top=16, left=16, right=16),
+    overflow_y="auto",
+    max_height="300px",
+    margin=me.Margin(bottom=16),
+)
 
 MSG_SYSTEM_HEADER_BOX_STYLE = me.Style(
     background="#b3e5fc",
@@ -174,6 +180,9 @@ MSG_DEFAULT_DISABLED_BUTTON_STYLE = me.Style(
 class MesopMessageStyles:
     box: me.Style = field(default_factory=lambda: MSG_DEFAULT_BOX_STYLE)
     md: me.Style = field(default_factory=lambda: MSG_DEFAULT_MD_STYLE)
+    scrollable_md: me.Style = field(
+        default_factory=lambda: MSG_DEFAULT_MD_SCROLABLE_STYLE
+    )
     header_box: me.Style = field(default_factory=lambda: MSG_DEFAULT_HEADER_BOX_STYLE)
     header_md: me.Style = field(default_factory=lambda: MSG_DEFAULT_HEADER_MD_STYLE)
     button: me.Style = field(default_factory=lambda: MSG_DEFAULT_BUTTON_STYLE)


Test patch:
diff --git a/tests/ui/mesop/test_message.py b/tests/ui/mesop/test_message.py
index 7623475a..ac58867a 100644
--- a/tests/ui/mesop/test_message.py
+++ b/tests/ui/mesop/test_message.py
@@ -118,8 +118,7 @@ class TestMessageBox:
         assert_called_with_one_of(
             me.markdown,
             "**Suggested function call: sender -> recipient**",
-            """
-**function_name**: `function_name`<br>
+            """**function_name**: `function_name`<br>
 **call_id**: `my_call_id`<br>
 **arguments**:
 ```
@@ -128,7 +127,6 @@ class TestMessageBox:
     "arg2": "value2"
 }
 ```
-
 """,
         )
 
@@ -154,9 +152,9 @@ class TestMessageBox:
         assert_called_with_one_of(
             me.markdown,
             "**Function call execution: sender -> recipient**",
-            """**function_name**: function_name <br>
-**call_id**: my_call_id <br>
-**retval**: return_value <br>""",
+            """**function_name**: `function_name`<br>
+**call_id**: `my_call_id`<br>
+**retval**: return_value""",
         )
 
     def test_text_input(self, monkeypatch: pytest.MonkeyPatch) -> None:
@@ -259,5 +257,11 @@ class TestMessageBox:
         assert_called_with_one_of(
             me.markdown,
             "**Workflow completed: sender -> recipient**",
-            "**result**: success <br>",
+            """
+```
+{
+    "result": "success"
+}
+```
+""",
         )