Duibonduil commited on
Commit
11ba2d7
·
verified ·
1 Parent(s): f818a8f

Upload 3 files

Browse files
aworld/output/ui/base.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aworld.output.utils import consume_content
2
+
3
+ from aworld.output.base import MessageOutput, ToolResultOutput, StepOutput, Output
4
+
5
+
6
+ class AworldUI:
7
+ """"""
8
+
9
+ async def title(self, output):
10
+ """
11
+ Title
12
+ """
13
+ pass
14
+
15
+ async def message_output(self, __output__: MessageOutput):
16
+ """
17
+ message_output
18
+ """
19
+ pass
20
+
21
+ async def tool_result(self, output: ToolResultOutput):
22
+ """
23
+ loading
24
+ """
25
+ pass
26
+
27
+
28
+ async def step(self, output: StepOutput):
29
+ """
30
+ loading
31
+ """
32
+ pass
33
+
34
+ async def custom_output(self, output: Output) -> str:
35
+ """
36
+ custom
37
+ """
38
+ pass
39
+
40
+ @classmethod
41
+ async def parse_output(cls, output, ui: "AworldUI"):
42
+ """
43
+ parse_output
44
+ """
45
+ if isinstance(output, MessageOutput):
46
+ return await ui.message_output(output)
47
+ elif isinstance(output, ToolResultOutput):
48
+ return await ui.tool_result(output)
49
+ elif isinstance(output, StepOutput):
50
+ return await ui.step(output)
51
+ else:
52
+ return await ui.custom_output(output)
53
+
54
+ class PrinterAworldUI(AworldUI):
55
+ """"""
56
+
57
+ async def title(self, output) -> str:
58
+ """
59
+ Title
60
+ """
61
+ pass
62
+
63
+ async def message_output(self, __output__: MessageOutput) -> str:
64
+ """
65
+ message_output
66
+ """
67
+ result=[]
68
+
69
+ async def __log_item(item):
70
+ result.append(item)
71
+ print(item, end="", flush=True)
72
+
73
+ if __output__.reason_generator or __output__.response_generator:
74
+ if __output__.reason_generator:
75
+ await consume_content(__output__.reason_generator, __log_item)
76
+ if __output__.response_generator:
77
+ await consume_content(__output__.response_generator, __log_item)
78
+ else:
79
+ await consume_content(__output__.reasoning, __log_item)
80
+ await consume_content(__output__.response, __log_item)
81
+ # if __output__.tool_calls:
82
+ # await consume_content(__output__.tool_calls, __log_item)
83
+
84
+ print("")
85
+ return "".join(result)
86
+
87
+ async def tool_result(self, output: ToolResultOutput) -> str:
88
+ """
89
+ loading
90
+ """
91
+ return f"call tool {output.origin_tool_call.id}#{output.origin_tool_call.function.name} \n" \
92
+ f"with params {output.origin_tool_call.function.arguments} \n" \
93
+ f"with result {output.data}\n"
94
+
95
+
96
+ async def step(self, output: StepOutput) -> str:
97
+ """
98
+ loading
99
+ """
100
+ if output.status == "START":
101
+ return f"=============✈️START {output.name}======================"
102
+ elif output.status == "FINISHED":
103
+ return f"=============🛬FINISHED {output.name}======================"
104
+ elif output.status == "FAILED":
105
+ return f"=============🛬💥FAILED {output.name}======================"
106
+ return f"=============?UNKNOWN#{output.status} {output.name}======================"
107
+
108
+ async def custom_output(self, output: Output) -> str:
109
+ pass
110
+
111
+
aworld/output/ui/markdown_aworld_ui.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import uuid
3
+ from dataclasses import dataclass
4
+
5
+ from pydantic import Field
6
+
7
+ from aworld.output import (
8
+ MessageOutput,
9
+ AworldUI,
10
+ Output,
11
+ Artifact,
12
+ ArtifactType,
13
+ WorkSpace,
14
+ SearchOutput,
15
+ )
16
+ from aworld.output.base import StepOutput, ToolResultOutput
17
+ from aworld.output.ui.template import tool_card_template
18
+ from aworld.output.utils import consume_content
19
+
20
+
21
+ @dataclass
22
+ class MarkdownAworldUI(AworldUI):
23
+
24
+ session_id: str = Field(default="", description="session id")
25
+ workspace: WorkSpace = Field(default=None, description="workspace")
26
+ cur_agent_name: str = Field(default=None, description="cur agent name")
27
+
28
+ def __init__(self, session_id: str = None, workspace: WorkSpace = None, **kwargs):
29
+ """
30
+ Initialize MarkdownAworldUI
31
+ Args:"""
32
+ super().__init__(**kwargs)
33
+ self.session_id = session_id
34
+ self.workspace = workspace
35
+
36
+ async def message_output(self, __output__: MessageOutput):
37
+ """
38
+ Returns an async generator that yields each message item.
39
+ """
40
+ # Sentinel object for queue completion
41
+ _SENTINEL = object()
42
+
43
+ async def async_generator():
44
+ async def __log_item(item):
45
+ await queue.put(item)
46
+
47
+ from asyncio import Queue
48
+ queue = Queue()
49
+
50
+ async def consume_all():
51
+ # Consume all relevant generators
52
+ if __output__.reason_generator or __output__.response_generator:
53
+ if __output__.reason_generator:
54
+ await consume_content(__output__.reason_generator, __log_item)
55
+ if __output__.response_generator:
56
+ await consume_content(__output__.response_generator, __log_item)
57
+ else:
58
+ await consume_content(__output__.reasoning, __log_item)
59
+ await consume_content(__output__.response, __log_item)
60
+ # Only after all are done, put the sentinel
61
+ await queue.put(_SENTINEL)
62
+
63
+ # Start the consumer in the background
64
+ import asyncio
65
+
66
+ consumer_task = asyncio.create_task(consume_all())
67
+
68
+ while True:
69
+ item = await queue.get()
70
+ if item is _SENTINEL:
71
+ break
72
+ yield item
73
+ await consumer_task # Ensure background task is finished
74
+
75
+ return async_generator()
76
+
77
+ async def tool_result(self, output: ToolResultOutput):
78
+ """
79
+ tool_result
80
+ """
81
+ custom_output = await self.gen_custom_output(output)
82
+
83
+ artifacts = await self.parse_tool_artifacts(output.metadata)
84
+
85
+ tool_card_content = {
86
+ "type": "mcp",
87
+ "custom_output": custom_output,
88
+ "tool_name": output.tool_name,
89
+ "function_name": output.origin_tool_call.function.name,
90
+ "function_arguments": output.origin_tool_call.function.arguments,
91
+ "artifacts": artifacts,
92
+ }
93
+ tool_data = tool_card_template.format(
94
+ tool_card_content=json.dumps(tool_card_content, indent=2)
95
+ )
96
+
97
+ return tool_data
98
+
99
+ async def gen_custom_output(self, output):
100
+ """
101
+ hook for custom output
102
+ """
103
+ custom_output = f"{output.tool_name}#{output.origin_tool_call.function.name}"
104
+ if output.tool_name == "aworld-playwright" and output.origin_tool_call.function.name == "browser_navigate":
105
+ custom_output = f"🔍 search `{json.loads(output.origin_tool_call.function.arguments)['url']}`"
106
+ if output.tool_name == "aworldsearch-server" and output.origin_tool_call.function.name == "search":
107
+ custom_output = f"🔍 search keywords: {' '.join(json.loads(output.origin_tool_call.function.arguments)['query_list'])}"
108
+ return custom_output
109
+
110
+ async def json_parse(self, json_str):
111
+ try:
112
+ function_result = json.dumps(
113
+ json.loads(json_str), indent=2, ensure_ascii=False
114
+ )
115
+ except Exception:
116
+ function_result = json_str
117
+ return function_result
118
+
119
+ async def step(self, output: StepOutput):
120
+ emptyLine = "\n\n----\n\n"
121
+ if output.status == "START":
122
+ if self.cur_agent_name == output.name:
123
+ return f"{emptyLine}"
124
+ self.cur_agent_name = output.name
125
+ return f"\n\n🤖 {output.show_name}: \n\n"
126
+ elif output.status == "FINISHED":
127
+ return f"{emptyLine}"
128
+ elif output.status == "FAILED":
129
+ return f"\n\n{output.name} 💥FAILED: reason is {output.data} {emptyLine}"
130
+ else:
131
+ return f"\n\n{output.name} ❓❓❓UNKNOWN#{output.status} {emptyLine}"
132
+
133
+ async def custom_output(self, output: Output):
134
+ return output.data
135
+
136
+ async def parse_tool_artifacts(self, metadata):
137
+ result = []
138
+ if not metadata:
139
+ return result
140
+
141
+ # screenshots
142
+ if metadata.get('screenshots') and isinstance(metadata.get('screenshots'), list) and len(
143
+ metadata.get('screenshots')) > 0:
144
+ for index, screenshot in enumerate(metadata.get('screenshots')):
145
+ image_artifact = Artifact(artifact_id=str(uuid.uuid4()), artifact_type=ArtifactType.IMAGE,
146
+ content=screenshot.get('ossPath'))
147
+ await self.workspace.add_artifact(image_artifact)
148
+ result.append({
149
+ "artifact_type": "IMAGE",
150
+ "artifact_id": image_artifact.artifact_id
151
+ })
152
+
153
+ # web_pages
154
+ if metadata.get("artifact_type") == "WEB_PAGES":
155
+ search_output = SearchOutput.from_dict(metadata.get("artifact_data"))
156
+ artifact_id = str(uuid.uuid4())
157
+ await self.workspace.create_artifact(
158
+ artifact_type=ArtifactType.WEB_PAGES,
159
+ artifact_id=artifact_id,
160
+ content=search_output,
161
+ metadata={
162
+ "query": search_output.query,
163
+ }
164
+ )
165
+ result.append({
166
+ "artifact_type": "WEB_PAGES",
167
+ "artifact_id": artifact_id
168
+ })
169
+ return result
aworld/output/ui/template.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ tool_card_template = """
2
+ ```tool_card
3
+ {tool_card_content}
4
+ ```
5
+ """
6
+
7
+ tool_call_template = """
8
+
9
+
10
+ **call {tool_name}#{function_name}**[{tool_type}]
11
+
12
+ ```tool_call_arguments
13
+ {function_arguments}
14
+ ```
15
+
16
+ ```tool_call_result
17
+ {function_result}
18
+ ```
19
+
20
+ {images}
21
+
22
+
23
+ """
24
+
25
+ step_loading_template = """
26
+ ```loading
27
+ {data}
28
+ ```
29
+ """