Skip to content

Commit

Permalink
feat: update
Browse files Browse the repository at this point in the history
  • Loading branch information
Col0ring committed Feb 27, 2025
1 parent f43b9dc commit 5e9fe35
Show file tree
Hide file tree
Showing 18 changed files with 476 additions and 317 deletions.
7 changes: 7 additions & 0 deletions .changeset/cool-clouds-spend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modelscope-studio/base': patch
'@modelscope-studio/frontend': patch
'modelscope_studio': patch
---

feat: add `Markdown` as a global component
7 changes: 7 additions & 0 deletions .changeset/short-results-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modelscope-studio/antdx': patch
'@modelscope-studio/frontend': patch
'modelscope_studio': patch
---

docs: add edit operation to the chatbot template
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(
self,
props: dict | None = None,
*,
item: dict | None = None,
item: dict | str | None = None,
as_item: str | None = None,
_internal: None = None,
# gradio properties
Expand All @@ -40,7 +40,15 @@ def __init__(
elem_style=elem_style,
**kwargs)
self.props = props
self.item = item
if isinstance(item, str):
self.item = self.serve_static_file(item)
elif isinstance(item, dict):
if not item.get("url", None) and item.get("path", None):
self.item = {
**item,
**self.serve_static_file(self.item["path"])
}
self.item = item

FRONTEND_DIR = resolve_frontend_dir("attachments",
'file-card',
Expand Down
96 changes: 85 additions & 11 deletions docs/layout_templates/chatbot/demos/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ def delete_message(state_value, e: gr.EventData):
value=state_value)

@staticmethod
def regenerating(state_value, e: gr.EventData):
def regenerate_message(state_value, e: gr.EventData):
conversation_key = e._data["component"]["conversationKey"]
history = state_value["conversations_history"][
state_value["conversation_id"]]
Expand All @@ -327,6 +327,37 @@ def regenerating(state_value, e: gr.EventData):
# custom code
return gr.update(items=history), gr.update(value=state_value)

@staticmethod
def edit_message(state_value, e: gr.EventData):
conversation_key = e._data["component"]["conversationKey"]
history = state_value["conversations_history"][
state_value["conversation_id"]]
index = -1
for i, conversation in enumerate(history):
if conversation["key"] == conversation_key:
index = i
break
if index == -1:
return gr.skip()
state_value["editing_message_index"] = index
text = ''
if isinstance(history[index]["content"], str):
text = history[index]["content"]
else:
text = history[index]["content"]["text"]
return gr.update(value=text), gr.update(value=state_value)

@staticmethod
def confirm_edit_message(edit_textarea_value, state_value):
history = state_value["conversations_history"][
state_value["conversation_id"]]
message = history[state_value["editing_message_index"]]
if isinstance(message["content"], str):
message["content"] = edit_textarea_value
else:
message["content"]["text"] = edit_textarea_value
return gr.update(items=history), gr.update(value=state_value)

@staticmethod
def select_suggestion(sender_value, e: gr.EventData):
return gr.update(value=sender_value[:-1] + e._data["payload"][0])
Expand Down Expand Up @@ -403,6 +434,14 @@ def paste_file(attachments_value, state_value, e: gr.EventData):
e._data["payload"][0]), gr.update(
open=True), gr.update(value=state_value)

@staticmethod
def close_modal():
return gr.update(open=False)

@staticmethod
def open_modal():
return gr.update(open=True)

@staticmethod
def update_browser_state(state_value):

Expand Down Expand Up @@ -488,6 +527,7 @@ def logo():
"conversations_history": {},
"conversations": [],
"conversation_id": "",
"editing_message_index": -1,
"attachments_open": False,
})

Expand Down Expand Up @@ -612,6 +652,7 @@ def logo():
copy_btn: {
copyable: { text: typeof bubble.content === 'string' ? bubble.content : bubble.content?.text, tooltips: false },
},
edit_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
delete_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
};
}"""):
Expand All @@ -631,6 +672,14 @@ def logo():
variant="text"):
with ms.Slot("icon"):
antd.Icon("CheckOutlined")
with antd.Button(value=None,
size="small",
color="default",
variant="text",
as_item="edit_btn"
) as user_edit_btn:
with ms.Slot("icon"):
antd.Icon("EditOutlined")
with antd.Popconfirm(
title="Delete the message",
description=
Expand Down Expand Up @@ -680,8 +729,9 @@ def logo():
copy_btn: {
copyable: { text: bubble.content, tooltips: false },
},
regenerating_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
regenerate_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
delete_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
edit_btn: { conversationKey: bubble.key, disabled: bubble.meta.disabled },
like_btn: {
conversationKey: bubble.key,
color: bubble.meta?.action === 'like' ? 'primary' : 'default',
Expand Down Expand Up @@ -734,23 +784,30 @@ def logo():
with ms.Slot("icon"):
antd.Icon("DislikeOutlined")
with antd.Popconfirm(
title=
"Regenerating the message",
title="Regenerate the message",
description=
"Regenerating the message will also delete all subsequent messages.",
"Regenerate the message will also delete all subsequent messages.",
ok_button_props=dict(
danger=True),
as_item="regenerating_btn"
) as chatbot_regenerating_popconfirm:
as_item="regenerate_btn"
) as chatbot_regenerate_popconfirm:
with antd.Button(
value=None,
size="small",
color="default",
variant="text",
as_item="regenerating_btn",
as_item="regenerate_btn",
):
with ms.Slot("icon"):
antd.Icon("SyncOutlined")
with antd.Button(value=None,
size="small",
color="default",
variant="text",
as_item="edit_btn"
) as chatbot_edit_btn:
with ms.Slot("icon"):
antd.Icon("EditOutlined")
with antd.Popconfirm(
title="Delete the message",
description=
Expand Down Expand Up @@ -859,7 +916,14 @@ def logo():
):
antd.Icon(
"CloudUploadOutlined")

# Modals
with antd.Modal(title="Edit Message",
open=False,
centered=True,
width="60%") as edit_modal:
edit_textarea = antd.Input.Textarea(auto_size=dict(minRows=2,
maxRows=6),
elem_style=dict(width="100%"))
# Events Handler
if save_history:
browser_state = gr.BrowserState(
Expand Down Expand Up @@ -910,15 +974,25 @@ def logo():
chatbot_dislike_btn.click(fn=Gradio_Events.dislike,
inputs=[state],
outputs=[chatbot, state])
gr.on(triggers=[user_edit_btn.click, chatbot_edit_btn.click],
fn=Gradio_Events.edit_message,
inputs=[state],
outputs=[edit_textarea, state]).then(fn=Gradio_Events.open_modal,
outputs=[edit_modal])
edit_modal.ok(fn=Gradio_Events.confirm_edit_message,
inputs=[edit_textarea, state],
outputs=[chatbot, state]).then(fn=Gradio_Events.close_modal,
outputs=[edit_modal])
edit_modal.cancel(fn=Gradio_Events.close_modal, outputs=[edit_modal])
gr.on(triggers=[
chatbot_delete_popconfirm.confirm, user_delete_popconfirm.confirm
],
fn=Gradio_Events.delete_message,
inputs=[state],
outputs=[chatbot, state])

regenerating_event = chatbot_regenerating_popconfirm.confirm(
fn=Gradio_Events.regenerating,
regenerating_event = chatbot_regenerate_popconfirm.confirm(
fn=Gradio_Events.regenerate_message,
inputs=[state],
outputs=[chatbot, state
]).then(fn=Gradio_Events.preprocess_submit(clear_input=False),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const AttachmentsFileCard = sveltify<
return {
...item,
uid: item.uid || item.path || item.url,
name: item.name || item.orig_name || item.path.split('/').pop(),
name:
item.name || item.orig_name || (item.url || item.path).split('/').pop(),
url:
item.url || get_fetchable_url_or_file(item.path, urlRoot, urlProxyUrl),
};
Expand Down
86 changes: 11 additions & 75 deletions frontend/antdx/bubble/list/bubble.list.tsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,31 @@
import { sveltify } from '@svelte-preprocess-react';
import React, { useMemo } from 'react';
import { Bubble as XBubble, type BubbleProps } from '@ant-design/x';
import type {
BubbleListProps,
RoleType,
} from '@ant-design/x/es/bubble/BubbleList';
import { useFunction } from '@utils/hooks/useFunction';
import { patchSlots } from '@utils/patchSlots';
import { Bubble as XBubble } from '@ant-design/x';
import type { BubbleListProps } from '@ant-design/x/es/bubble/BubbleList';
import { renderItems } from '@utils/renderItems';
import type { AvatarProps } from 'antd';
import { isFunction, isObject } from 'lodash-es';

import {
useItems,
useRoleItems,
withItemsContextProvider,
withRoleItemsContextProvider,
} from './context';

function patchBubbleSlots(role: RoleType, params: any[]) {
return patchSlots(params, (patchSlotRender) => {
return {
...role,
avatar: isFunction(role.avatar)
? patchSlotRender(role.avatar)
: isObject(role.avatar)
? {
...role.avatar,
icon: patchSlotRender((role.avatar as AvatarProps)?.icon),
src: patchSlotRender((role.avatar as AvatarProps)?.src),
}
: role.avatar,
footer: patchSlotRender(role.footer),
header: patchSlotRender(role.header),
loadingRender: patchSlotRender(role.loadingRender, true),
messageRender: patchSlotRender(role.messageRender, true),
};
});
}
import { useRolesRender } from './utils';

export const BubbleList = sveltify<BubbleListProps, ['items', 'roles']>(
withRoleItemsContextProvider(
['roles'],
withItemsContextProvider(
['items', 'default'],
({ items, roles: rolesProp, children, ...props }) => {
const rolesFunction = useFunction(rolesProp);
const {
items: { roles: roleItems },
} = useRoleItems<['roles']>();
({ items, roles, children, ...props }) => {
const { items: slotItems } = useItems<['items', 'default']>();
const roles = useMemo(() => {
return (
rolesProp ||
renderItems<
RoleType & {
role?: string;
}
>(roleItems, {
clone: true,
forceClone: true,
})?.reduce(
(acc, v) => {
if (v.role !== undefined) {
acc[v.role] = v;
}
return acc;
},
{} as Record<string, RoleType>
)
);
}, [roleItems, rolesProp]);

const rolesRender = useRolesRender({
roles,
});

const resolvedSlotItems =
slotItems.items.length > 0 ? slotItems.items : slotItems.default;
const rolesRender = useMemo(() => {
return (bubbleProps: BubbleProps, index: number): RoleType => {
if (bubbleProps.role && (roles || {})[bubbleProps.role]) {
return patchBubbleSlots((roles || {})[bubbleProps.role], [
bubbleProps,
index,
]) as RoleType;
}
return {
messageRender(content) {
return (
<>{isObject(content) ? JSON.stringify(content) : content}</>
);
},
};
};
}, [roles]);

return (
<>
<div style={{ display: 'none' }}>{children}</div>
Expand All @@ -104,7 +40,7 @@ export const BubbleList = sveltify<BubbleListProps, ['items', 'roles']>(

return resolvedItems;
}, [items, resolvedSlotItems])}
roles={rolesFunction || rolesRender}
roles={rolesRender}
/>
</>
);
Expand Down
Loading

0 comments on commit 5e9fe35

Please sign in to comment.