minchyeom commited on
Commit
64e0772
·
1 Parent(s): f782089
Files changed (1) hide show
  1. app.py +162 -0
app.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import base64
3
+ import json
4
+ import shlex
5
+
6
+ import gradio as gr
7
+ from gradio.oauth import attach_oauth, OAuthToken
8
+ from huggingface_hub import HfApi
9
+
10
+ from src.team import TeamChatSession
11
+ from src.db import list_sessions_info
12
+
13
+ # Store active chat sessions
14
+ _SESSIONS: dict[tuple[str, str], TeamChatSession] = {}
15
+ _API = HfApi()
16
+
17
+
18
+ def _username(token: OAuthToken) -> str:
19
+ """Return the username for the given token."""
20
+ info = _API.whoami(token.token)
21
+ return info.get("name") or info.get("user", "unknown")
22
+
23
+
24
+ async def _get_chat(user: str, session: str) -> TeamChatSession:
25
+ """Return an active :class:`TeamChatSession` for ``user`` and ``session``."""
26
+ key = (user, session)
27
+ chat = _SESSIONS.get(key)
28
+ if chat is None:
29
+ chat = TeamChatSession(user=user, session=session)
30
+ await chat.__aenter__()
31
+ _SESSIONS[key] = chat
32
+ return chat
33
+
34
+
35
+ async def _vm_execute(user: str, session: str, command: str) -> str:
36
+ """Execute ``command`` inside the user's VM and return output."""
37
+ chat = await _get_chat(user, session)
38
+ vm = getattr(chat.senior, "_vm", None)
39
+ if vm is None:
40
+ raise RuntimeError("VM not running")
41
+ return await vm.execute_async(command, timeout=5)
42
+
43
+
44
+ async def send_message(
45
+ message: str, history: list[dict] | None, session: str, token: OAuthToken
46
+ ):
47
+ user = _username(token)
48
+ chat = await _get_chat(user, session)
49
+ history = history or []
50
+
51
+ # user turn
52
+ history.append({"role": "user", "content": message})
53
+ yield history # show immediately
54
+
55
+ # stream assistant turns
56
+ async for part in chat.chat_stream(message):
57
+ if history[-1]["role"] == "assistant":
58
+ history[-1]["content"] += part
59
+ else:
60
+ history.append({"role": "assistant", "content": part})
61
+ yield history
62
+
63
+
64
+ def load_sessions(token: OAuthToken):
65
+ user = _username(token)
66
+ infos = list_sessions_info(user)
67
+ names = [info["name"] for info in infos]
68
+ table = [[info["name"], info["last_message"]] for info in infos]
69
+ value = names[0] if names else "default"
70
+ return gr.update(choices=names or ["default"], value=value), table
71
+
72
+
73
+ async def list_dir(path: str, session: str, token: OAuthToken):
74
+ user = _username(token)
75
+ cmd = f"ls -1ap {shlex.quote(path)}"
76
+ output = await _vm_execute(user, session, cmd)
77
+ if output.startswith("ls:"):
78
+ return []
79
+
80
+ rows = []
81
+ for line in output.splitlines():
82
+ line = line.strip()
83
+ if not line or line in (".", ".."):
84
+ continue
85
+ is_dir = line.endswith("/")
86
+ name = line[:-1] if is_dir else line
87
+ rows.append([name, is_dir])
88
+ return rows
89
+
90
+
91
+ async def read_file(path: str, session: str, token: OAuthToken):
92
+ user = _username(token)
93
+ cmd = f"cat {shlex.quote(path)}"
94
+ return await _vm_execute(user, session, cmd)
95
+
96
+
97
+ async def save_file(path: str, content: str, session: str, token: OAuthToken):
98
+ user = _username(token)
99
+ encoded = base64.b64encode(content.encode()).decode()
100
+ cmd = (
101
+ f"python -c 'import base64,os; "
102
+ f'open({json.dumps(path)}, "wb").write(base64.b64decode({json.dumps(encoded)}))\''
103
+ )
104
+ await _vm_execute(user, session, cmd)
105
+ return "Saved"
106
+
107
+
108
+ async def delete_path(path: str, session: str, token: OAuthToken):
109
+ user = _username(token)
110
+ cmd = (
111
+ f"bash -c 'if [ -d {shlex.quote(path)} ]; then rm -rf {shlex.quote(path)} && echo Deleted; "
112
+ f"elif [ -e {shlex.quote(path)} ]; then rm -f {shlex.quote(path)} && echo Deleted; "
113
+ f"else echo File not found; fi'"
114
+ )
115
+ return await _vm_execute(user, session, cmd)
116
+
117
+
118
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
119
+ attach_oauth(demo.app)
120
+
121
+ login_btn = gr.LoginButton()
122
+
123
+ with gr.Tab("Chat"):
124
+ session_dd = gr.Dropdown(["default"], label="Session", value="default")
125
+ refresh = gr.Button("Refresh Sessions")
126
+ chatbox = gr.Chatbot(type="messages")
127
+ msg = gr.Textbox(label="Message")
128
+ send = gr.Button("Send")
129
+
130
+ gr.Markdown(
131
+ """
132
+ This is a demo app of [llmOS](https://github.com/starsnatched/llmOS-Agent), an agent framework for building AI assistants that can interact with a Linux VM to accomplish tasks.
133
+ """
134
+ )
135
+
136
+ with gr.Tab("Files"):
137
+ dir_path = gr.Textbox(label="Directory", value="/")
138
+ list_btn = gr.Button("List")
139
+ table = gr.Dataframe(headers=["name", "is_dir"], datatype=["str", "bool"])
140
+ file_path = gr.Textbox(label="File Path")
141
+ load_btn = gr.Button("Load")
142
+ content = gr.Code(label="Content", language=None)
143
+ save_btn = gr.Button("Save")
144
+ del_btn = gr.Button("Delete")
145
+
146
+ refresh.click(load_sessions, outputs=[session_dd, table])
147
+ send_click = send.click(
148
+ send_message,
149
+ inputs=[msg, chatbox, session_dd],
150
+ outputs=chatbox,
151
+ )
152
+ list_btn.click(list_dir, inputs=[dir_path, session_dd], outputs=table)
153
+ load_btn.click(read_file, inputs=[file_path, session_dd], outputs=content)
154
+ save_btn.click(save_file, inputs=[file_path, content, session_dd], outputs=content)
155
+ del_btn.click(delete_path, inputs=[file_path, session_dd], outputs=content)
156
+ send_click.then(lambda: "", None, msg)
157
+
158
+
159
+ demo.queue()
160
+
161
+ if __name__ == "__main__":
162
+ demo.launch()