Spaces:
Runtime error
Runtime error
| # coding=utf-8 | |
| # Copyright 2023 The AIWaves Inc. team. | |
| # | |
| # 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. | |
| import json | |
| from PIL import Image | |
| import requests | |
| from typing import List, Tuple | |
| class GradioConfig: | |
| # How many avatars are currently registered | |
| POINTER = 0 | |
| # Avatar image. You can add or replace. | |
| AGENT_HEAD_URL = [ | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306241687579617434043.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306241687592097408547.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306141686726561699613.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306141686726561275758.jpg", | |
| "https://img.touxiangwu.com/uploads/allimg/2021090300/ry5k31wt33c.jpg", | |
| "https://img.touxiangwu.com/uploads/allimg/2021090300/0ls2gmwhrf5.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/02/202302281677545695326193.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/03/202303271679886128550253.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306141686711344407060.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306141686711345834296.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/05/202305171684311194291520.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/05/202305171684311196958993.jpg", | |
| "https://img.touxiangwu.com/uploads/allimg/2021082612/vr0bkov0dwl.jpg", | |
| "https://img.touxiangwu.com/uploads/allimg/2021082612/auqx5zfsv5g.jpg", | |
| "https://img.touxiangwu.com/uploads/allimg/2021082612/llofpivtwls.jpg", | |
| "https://img.touxiangwu.com/uploads/allimg/2021082612/3j2sdot3ye0.jpg", | |
| "https://img.touxiangwu.com/2020/3/nQfYf2.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/08/202308131691918068774532.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/08/202308131691918068289945.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/08/202308131691918069785183.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306141686726561292003.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306141686726561578616.jpg", | |
| "https://img.touxiangwu.com/zb_users/upload/2023/06/202306141686726564597524.jpg" | |
| ] | |
| USER_HEAD_URL = "https://img.touxiangwu.com/zb_users/upload/2023/05/202305301685407468585486.jpg" | |
| # The css style of gradio.Chatbot | |
| CSS = """ | |
| #chatbot1 .user { | |
| background-color:transparent; | |
| border-color:transparent; | |
| } | |
| #chatbot1 .bot { | |
| background-color:transparent; | |
| border-color:transparent; | |
| } | |
| #btn {color: red; border-color: red;} | |
| """ | |
| ID = ["USER", "AGENT", "SYSTEM"] | |
| # Bubble template | |
| BUBBLE_CSS = { | |
| # Background-color Name-color Name-content Font-color Font-size Content Avatar-URL | |
| "USER": """ | |
| <div style="display: flex; align-items: flex-start; justify-content: flex-end;"> | |
| <div style="background-color: {}; border-radius: 20px 0px 20px 20px; padding: 15px; min-width: 100px; max-width: 300px;"> | |
| <p style="margin: 0; padding: 0; color: {}; font-weight: bold; font-size: 18px;">{}</p> | |
| <p style="margin: 0; padding: 0; color: {}; font-size: {}px;">{}</p> | |
| </div> | |
| <img src="{}" alt="USER" style="width: 50px; height: 50px; border-radius: 50%; margin-left: 10px;"> | |
| </div> | |
| """, | |
| # Avatar-URL Background-color Name-color Name-Content Font-color Font-size Content | |
| "AGENT": """ | |
| <div style="display: flex; align-items: flex-start;"> | |
| <img src="{}" alt="AGENT" style="width: 50px; height: 50px; border-radius: 50%; margin-right: 10px;"> | |
| <div style="background-color: {}; border-radius: 0px 20px 20px 20px; padding: 15px; min-width: 100px; max-width: 600px;"> | |
| <p style="margin: 0; padding: 0; color: {}; font-weight: bold; font-size: 18px;">{}</p> | |
| <p style="margin: 0; padding: 0; color: {}; font-size: {}px;">{}</p> | |
| </div> | |
| </div> | |
| """, | |
| # Backrgound-color Font-size Font-color Name Content | |
| "SYSTEM": """ | |
| <div style="display: flex; align-items: center; justify-content: center;"> | |
| <div style="background-color: {}; border-radius: 20px; padding: 1px; min-width: 200px; max-width: 1000px;"> | |
| <p style="margin: 0; padding: 0; text-align: center; font-size: {}px; font-weight: bold; font-family: '微软雅黑', sans-serif; color: {};">{}:{}</p> | |
| </div> | |
| </div> | |
| """ | |
| } | |
| ROLE_2_NAME = {} | |
| OBJECT_INFO = { | |
| "User": { | |
| # https://img-blog.csdnimg.cn/img_convert/7c20bc39ac69b6972a22e18762d02db3.jpeg | |
| "head_url": USER_HEAD_URL, | |
| "bubble_color": "#95EC69", | |
| "text_color": "#000000", | |
| "font_size": 0, | |
| "id": "USER" | |
| }, | |
| "System": { | |
| # https://img-blog.csdnimg.cn/img_convert/e7e5887cfff67df8c2205c2ef0e5e7fa.png | |
| "head_url": "https://img.touxiangwu.com/zb_users/upload/2023/03/202303141678768524747045.jpg", | |
| "bubble_color": "#7F7F7F", ##FFFFFF | |
| "text_color": "#FFFFFF", ##000000 | |
| "font_size": 0, | |
| "id": "SYSTEM" | |
| }, | |
| "wait": { | |
| "head_url": "https://img.touxiangwu.com/zb_users/upload/2022/12/202212011669881536145501.jpg", | |
| "bubble_color": "#E7CBA6", | |
| "text_color": "#000000", | |
| "font_size": 0, | |
| "id": "AGENT" | |
| }, | |
| "Recorder": { | |
| "head_url": "https://img.touxiangwu.com/zb_users/upload/2023/02/202302281677545695326193.jpg", | |
| "bubble_color": "#F7F7F7", | |
| "text_color": "#000000", | |
| "font_size": 0, | |
| "id": "AGENT" | |
| } | |
| } | |
| def color_for_img(cls, url): | |
| """ | |
| Extract the main colors from the picture and set them as the background color, | |
| then determine the corresponding text color. | |
| """ | |
| def get_main_color(image): | |
| image = image.convert("RGB") | |
| width, height = image.size | |
| pixels = image.getcolors(width * height) | |
| most_common_pixel = max(pixels, key=lambda item: item[0]) | |
| return most_common_pixel[1] | |
| def is_dark_color(rgb_color): | |
| r, g, b = rgb_color | |
| luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 | |
| return luminance < 0.5 | |
| def download_image(url): | |
| print(f"binding: {url}") | |
| response = requests.get(url) | |
| if response.status_code == 200: | |
| with open('image.jpg', 'wb') as f: | |
| f.write(response.content) | |
| def rgb_to_hex(color): | |
| return "#{:02X}{:02X}{:02X}".format(color[0], color[1], color[2]) | |
| def get_color(image_url): | |
| download_image(image_url) | |
| image = Image.open("image.jpg") | |
| main_color = get_main_color(image) | |
| is_dark = is_dark_color(main_color) | |
| if is_dark: | |
| font_color = "#FFFFFF" | |
| else: | |
| font_color = "#000000" | |
| return rgb_to_hex(main_color), font_color | |
| return get_color(url) | |
| def init(cls, JSON): | |
| # Deprecated | |
| with open(JSON) as f: | |
| sop = json.load(f) | |
| cnt = 0 | |
| FISRT_NODE = True | |
| fisrt_node_roles = [] | |
| for node_name in sop['nodes']: | |
| node_info = sop['nodes'][node_name] | |
| agent_states = node_info['agent_states'] | |
| for agent_role in agent_states: | |
| name = agent_states[agent_role]['style']['name'] | |
| cls.ROLE_2_NAME[agent_role] = name | |
| if FISRT_NODE: | |
| fisrt_node_roles.append(agent_role) | |
| bubble_color, text_color = cls.color_for_img(cls.AGENT_HEAD_URL[cnt]) | |
| cls.OBJECT_INFO[name] = { | |
| "head_url": f"{cls.AGENT_HEAD_URL[cnt]}", | |
| "bubble_color": bubble_color, | |
| "text_color": text_color, | |
| "font_size": 0, | |
| "id": "AGENT" | |
| } | |
| cnt += 1 | |
| if FISRT_NODE: | |
| FISRT_NODE = False | |
| print(cls.OBJECT_INFO) | |
| for usr_name in cls.OBJECT_INFO: | |
| if cls.OBJECT_INFO[usr_name]["id"] == "SYSTEM": | |
| cls.OBJECT_INFO[usr_name]["font_size"] = 12 | |
| elif cls.OBJECT_INFO[usr_name]["id"] in ["USER", "AGENT"]: | |
| cls.OBJECT_INFO[usr_name]["font_size"] = 16 | |
| else: | |
| assert False | |
| return fisrt_node_roles | |
| def add_agent(cls, agents_name:List): | |
| for name in agents_name: | |
| bubble_color, text_color = cls.color_for_img(cls.AGENT_HEAD_URL[cls.POINTER]) | |
| cls.OBJECT_INFO[name] = { | |
| "head_url": f"{cls.AGENT_HEAD_URL[cls.POINTER]}", | |
| "bubble_color": bubble_color, | |
| "text_color": text_color, | |
| "font_size": 0, | |
| "id": "AGENT" | |
| } | |
| cls.POINTER += 1 | |
| for usr_name in cls.OBJECT_INFO: | |
| if cls.OBJECT_INFO[usr_name]["id"] == "SYSTEM": | |
| cls.OBJECT_INFO[usr_name]["font_size"] = 12 | |
| elif cls.OBJECT_INFO[usr_name]["id"] in ["USER", "AGENT"]: | |
| cls.OBJECT_INFO[usr_name]["font_size"] = 16 | |
| else: | |
| assert False | |
| class StateConfig: | |
| """UI configuration for the step progress bar (indicating the current node)""" | |
| CSS = """ | |
| :root { | |
| --gradient-start: 100%; | |
| --gradient-end: 0%; | |
| } | |
| .container.progress-bar-container { | |
| position: relative; | |
| display: flex; | |
| align-items: flex-end; | |
| width: 100%; | |
| overflow-x: auto; | |
| padding-bottom: 30px; | |
| padding-top: 20px | |
| } | |
| .container.progress-bar-container::-webkit-scrollbar { | |
| width: 8px; | |
| background-color: transparent; | |
| } | |
| .container.progress-bar-container::-webkit-scrollbar-thumb { | |
| background-color: transparent; | |
| } | |
| .progress-bar-container .progressbar { | |
| counter-reset: step; | |
| white-space: nowrap; | |
| } | |
| .progress-bar-container .progressbar li { | |
| list-style: none; | |
| display: inline-block; | |
| width: 200px; | |
| position: relative; | |
| text-align: center; | |
| cursor: pointer; | |
| white-space: normal; | |
| } | |
| .progress-bar-container .progressbar li:before { | |
| content: counter(step); | |
| counter-increment: step; | |
| width: 30px; | |
| height: 30px; | |
| line-height: 30px; | |
| border: 1px solid #ddd; | |
| border-radius: 100%; | |
| display: block; | |
| text-align: center; | |
| margin: 0 auto 10px auto; | |
| background-color: #ffffff; | |
| } | |
| .progress-bar-container .progressbar li:after { | |
| content: attr(data-content); | |
| position: absolute; | |
| width: 87%; | |
| height: 2px; | |
| background-color: #dddddd; | |
| top: 15px; | |
| left: -45%; | |
| } | |
| .progress-bar-container .progressbar li:first-child:after { | |
| content: none; | |
| } | |
| .progress-bar-container .progressbar li.active { | |
| color: green; | |
| } | |
| .progress-bar-container .progressbar li.active:before { | |
| border-color: green; | |
| background-color: green; | |
| color: white; | |
| } | |
| .progress-bar-container .progressbar li.active + li:after { | |
| background: linear-gradient(to right, green var(--gradient-start), lightgray var(--gradient-end)); | |
| } | |
| .progress-bar-container .small-element { | |
| transform: scale(0.8); | |
| } | |
| .progress-bar-container .progressbar li span { | |
| position: absolute; | |
| top: 40px; | |
| left: 0; | |
| width: 100%; | |
| text-align: center; | |
| } | |
| .progress-bar-container .progressbar li .data-content { | |
| position: absolute; | |
| width: 100%; | |
| top: -10px; | |
| left: -100px; | |
| text-align: center; | |
| } | |
| """ | |
| FORMAT = """ | |
| <html> | |
| <head> | |
| <style> | |
| {} | |
| </style> | |
| </head> | |
| <body> | |
| <br> | |
| <center> | |
| <div class="container progress-bar-container"> | |
| <ul class="progressbar"> | |
| {} | |
| </ul> | |
| </div> | |
| </center> | |
| </body> | |
| </html> | |
| """ | |
| STATES_NAME:List[str] = None | |
| def _generate_template(cls, types:str)->str: | |
| # normal: A state with no execution. | |
| # active-show-up: Active state, and content displayed above the horizontal line. | |
| # active-show-down: Active state, and content displayed below the horizontal line. | |
| # active-show-both: Active state, and content displayed both above and below the horizontal line. | |
| # active-show-none: Active state, with no content displayed above the horizontal line. | |
| assert types.lower() in ["normal","active-show-up", "active-show-down", "active-show-both", "active", "active-show-none"] | |
| both_templates = """<li class="active" style="--gradient-start: {}%; --gradient-end: {}%;"> | |
| <div class="data-content"> | |
| <center> | |
| <p style="line-height: 1px;"></p> | |
| {} | |
| <p> | |
| {} | |
| </p> | |
| </center> | |
| </div> | |
| <span>{}</span> | |
| </li>""" | |
| if types.lower() == "normal": | |
| templates = "<li><span>{}</span></li>" | |
| elif types.lower() == "active": | |
| templates = """<li class="active"><span>{}</span></li>""" | |
| elif types.lower() == "active-show-up": | |
| templates = both_templates.format("{}","{}", "{}", "", "{}") | |
| elif types.lower() == "active-show-down": | |
| templates = both_templates.format("{}","{}", "", "{}", "{}") | |
| elif types.lower() == "active-show-both": | |
| templates = both_templates | |
| elif types.lower() == "active-show-none": | |
| templates = """<li class="active" style="--gradient-start: {}%; --gradient-end: {}%;"> | |
| <span>{}</span> | |
| </li>""" | |
| else: | |
| assert False | |
| return templates | |
| def update_states(cls, current_states:List[int], current_templates:List[str], show_content:List[Tuple[str]])->str: | |
| assert len(current_states) == len(current_templates) | |
| # You can dynamically change the number of states. | |
| # assert len(current_states) == len(cls.STATES_NAME) | |
| css_code = [] | |
| for idx in range(len(current_states)): | |
| if idx == 0: | |
| if current_states[idx] != 0: | |
| css_code = [f"{cls._generate_template('active').format(cls.STATES_NAME[idx])}"] | |
| else: | |
| css_code = [f"{cls._generate_template('normal').format(cls.STATES_NAME[idx])}"] | |
| continue | |
| if current_states[idx-1] == 0: | |
| # new_code = f"{cls._generate_template('normal').format(*(show_content[idx]))}" | |
| new_code = f"{cls._generate_template('normal').format(cls.STATES_NAME[idx])}" | |
| else: | |
| new_code = f"{cls._generate_template(current_templates[idx]).format(current_states[idx-1], 100-current_states[idx-1],*(show_content[idx-1]), cls.STATES_NAME[idx])}" | |
| if current_states[idx-1] != 100 or (current_states[idx]==0 and current_states[idx-1]==100): | |
| new_code = new_code.replace("""li class="active" ""","""li """) | |
| css_code.append(new_code) | |
| return "\n".join(css_code) | |
| def create_states(cls, states_name:List[str], manual_create_end_nodes:bool=False): | |
| # Create states | |
| if manual_create_end_nodes: | |
| states_name.append("Done") | |
| css_code = "" | |
| cls.STATES_NAME: List[str] = states_name | |
| for name in states_name: | |
| css_code = f"{css_code}\n{cls._generate_template('normal').format(name)}" | |
| return css_code | |
| if __name__ == '__main__': | |
| pass | |