Files
leon-ai--leon/bridges/python/src/sdk/widget.py
T
2026-07-13 12:44:08 +08:00

120 lines
3.7 KiB
Python

from typing import Any, Optional, Generic, TypeVar, Literal, TypedDict, Union, Dict
from dataclasses import dataclass
from abc import ABC, abstractmethod
import random
import string
from .widget_component import WidgetComponent
from ..constants import SKILL_LOCALE_CONFIG, INTENT_OBJECT
T = TypeVar('T')
UtteranceSender = Literal['leon', 'owner']
class SendUtteranceWidgetEventMethodParams(TypedDict):
from_: UtteranceSender
utterance: str
class RunSkillActionWidgetEventMethodParams(TypedDict):
action_name: str
params: Dict[str, Any]
class SendUtteranceOptions(TypedDict, total=False):
from_: Optional[UtteranceSender]
data: Optional[Dict[str, Any]]
class WidgetEventMethod(TypedDict):
methodName: Literal['send_utterance', 'run_skill_action']
methodParams: Union[
SendUtteranceWidgetEventMethodParams,
RunSkillActionWidgetEventMethodParams
]
@dataclass
class WidgetOptions(Generic[T]):
wrapper_props: dict[str, Any] = None
params: T = None
on_fetch: Optional[dict[str, Any]] = None
class Widget(ABC, Generic[T]):
def __init__(self, options: WidgetOptions[T]):
if options.wrapper_props:
self.wrapper_props = options.wrapper_props
else:
self.wrapper_props = None
self.action_name = f"{INTENT_OBJECT['skill_name']}:{INTENT_OBJECT['action_name']}"
self.params = options.params
self.widget = self.__class__.__name__
if options.on_fetch:
self.on_fetch = {
'widgetId': options.on_fetch.get('widget_id'),
'actionName': f"{INTENT_OBJECT['skill_name']}:{options.on_fetch.get('action_name')}"
}
else:
self.on_fetch = None
self.id = options.on_fetch.get('widget_id') if options.on_fetch \
else f"{self.widget.lower()}-{''.join(random.choices(string.ascii_lowercase + string.digits, k=8))}"
@abstractmethod
def render(self) -> WidgetComponent:
pass
def send_utterance(self, key: str, options: Optional[Dict[str, Any]] = None) -> WidgetEventMethod:
"""
Indicate the core to send a given utterance
:param key: The key of the content
:param options: The options of the utterance
"""
utterance_content = self.content(key, options.get('data') if options else None)
from_ = options.get('from', 'owner') if options else 'owner'
return WidgetEventMethod(
methodName='send_utterance',
methodParams={
'from': from_,
'utterance': utterance_content
}
)
def run_skill_action(self, action_name: str, params: Dict[str, Any]) -> WidgetEventMethod:
"""
Indicate the core to run a given skill action
:param action_name: The name of the action
:param params: The parameters of the action
"""
return WidgetEventMethod(
methodName='run_skill_action',
methodParams={
'actionName': action_name,
'params': params
}
)
def content(self, key: str, data: Optional[Dict[str, Any]] = None) -> str:
"""
Grab and compute the target content of the widget
:param key: The key of the content
:param data: The data to apply
"""
widget_contents = SKILL_LOCALE_CONFIG.get('widget_contents', {})
if key not in widget_contents:
return 'INVALID'
content = widget_contents[key]
if isinstance(content, list):
content = random.choice(content)
if data:
for k, v in data.items():
content = content.replace(f"{{{{ {key} }}}}", str(v))
return content