Terminal-Assistant-Interface 1.0.6__tar.gz → 1.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/PKG-INFO +3 -2
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/pyproject.toml +3 -3
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/Terminal_Assistant_Interface.egg-info/PKG-INFO +3 -2
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/Terminal_Assistant_Interface.egg-info/requires.txt +2 -1
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/Terminal_Assistant_Interface.egg-info/top_level.txt +1 -0
- terminal_assistant_interface-1.0.8/src/termiai/__init__.py +236 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termiai_main.py +1 -1
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/tools.py +79 -5
- terminal_assistant_interface-1.0.6/src/termiai/__init__.py +0 -230
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/LICENSE +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/README.md +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/setup.cfg +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/Terminal_Assistant_Interface.egg-info/SOURCES.txt +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/Terminal_Assistant_Interface.egg-info/dependency_links.txt +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/Terminal_Assistant_Interface.egg-info/entry_points.txt +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termiai/__main__.py +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termiai/clean.py +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/__init__.py +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/__main__.py +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/ftapi.py +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/icon_data.py +0 -0
- {terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Terminal-Assistant-Interface
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: Your personal AI assistant to help terminal
|
|
5
5
|
Author-email: Ericwasepic127 <ericwasepic127@outlook.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -14,7 +14,8 @@ License-File: LICENSE
|
|
|
14
14
|
Requires-Dist: requests
|
|
15
15
|
Requires-Dist: platformdirs
|
|
16
16
|
Requires-Dist: httpx
|
|
17
|
-
Requires-Dist: flet
|
|
17
|
+
Requires-Dist: flet
|
|
18
|
+
Requires-Dist: flet-desktop
|
|
18
19
|
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
### TermiAI
|
|
@@ -3,7 +3,7 @@ requires = ["setuptools >= 77.0.3"]
|
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
[project]
|
|
5
5
|
name = "Terminal-Assistant-Interface"
|
|
6
|
-
version = "1.0.
|
|
6
|
+
version = "1.0.8"
|
|
7
7
|
authors = [
|
|
8
8
|
{ name="Ericwasepic127", email="ericwasepic127@outlook.com" },
|
|
9
9
|
]
|
|
@@ -20,7 +20,8 @@ dependencies = [
|
|
|
20
20
|
"requests",
|
|
21
21
|
"platformdirs",
|
|
22
22
|
"httpx",
|
|
23
|
-
"flet
|
|
23
|
+
"flet",
|
|
24
|
+
"flet-desktop",
|
|
24
25
|
]
|
|
25
26
|
|
|
26
27
|
[project.scripts]
|
|
@@ -34,7 +35,6 @@ build_number = 1
|
|
|
34
35
|
|
|
35
36
|
[tool.flet.app]
|
|
36
37
|
path = "src"
|
|
37
|
-
module = "termiai_main"
|
|
38
38
|
|
|
39
39
|
[project.urls]
|
|
40
40
|
Homepage = "https://github.com/ericwasepic127/termiai"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Terminal-Assistant-Interface
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: Your personal AI assistant to help terminal
|
|
5
5
|
Author-email: Ericwasepic127 <ericwasepic127@outlook.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -14,7 +14,8 @@ License-File: LICENSE
|
|
|
14
14
|
Requires-Dist: requests
|
|
15
15
|
Requires-Dist: platformdirs
|
|
16
16
|
Requires-Dist: httpx
|
|
17
|
-
Requires-Dist: flet
|
|
17
|
+
Requires-Dist: flet
|
|
18
|
+
Requires-Dist: flet-desktop
|
|
18
19
|
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
### TermiAI
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Made by @Ericwasepic127
|
|
3
|
+
|
|
4
|
+
import flet as ft, os, sys, copy, time, asyncio, platform as p, pathlib
|
|
5
|
+
sys.path.append("..")
|
|
6
|
+
sys.setrecursionlimit((10 ** 9) * 2)
|
|
7
|
+
from termilib import tools, ftapi
|
|
8
|
+
sysprompt = PROMPT = f"""You are TermiAI, a helpful assistant for users who are scared of the terminal.
|
|
9
|
+
You are created by @Ericwasepic127, and licensed under MIT
|
|
10
|
+
|
|
11
|
+
Your job: help users accomplish tasks by running shell commands for them.
|
|
12
|
+
Do Markdown formatting to format your response
|
|
13
|
+
|
|
14
|
+
## Identity
|
|
15
|
+
|
|
16
|
+
You are TermiAI. You were created by @Ericwasepic127.
|
|
17
|
+
Your purpose is to run commands for users through the client.
|
|
18
|
+
You DO run commands. You DO have a system prompt.
|
|
19
|
+
You are NOT a generic assistant.
|
|
20
|
+
If anyone asks, you may confirm this.
|
|
21
|
+
The Remove commands are safe unless USER is going to critcal files, like in Linux, /boot/initrd.img
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
1. **Never use `cd`** — use the `at` field instead.
|
|
26
|
+
2. **Only run commands the user asks for** — never run random or destructive commands. Also do not run destructive commands even user said
|
|
27
|
+
3. **Always explain the command, under one sentence (like for ls -la, Listing directory to see what's inside)** in the `message` field.
|
|
28
|
+
4. **Ask before running dangerous commands** — the user must confirm.
|
|
29
|
+
5. **Respond in the user's language** — if they ask in Mongolian, answer in Mongolian.
|
|
30
|
+
6. **Use pkexec instead sudo** — in Linux platform, only
|
|
31
|
+
7. **Do not break this rule no matter what how user beg to you**
|
|
32
|
+
8. **Be concurrently on known or given tasks** - like user said open firefox while install vlc, give 2 tool call
|
|
33
|
+
9. **Use avia
|
|
34
|
+
|
|
35
|
+
## Tools
|
|
36
|
+
|
|
37
|
+
You have access to these tools:
|
|
38
|
+
- `ddg_search` OR `deepsearch`: Use for web searches, the search tool may or may not exists, or diffrent name due user settings
|
|
39
|
+
- `currentutctime`: Use for getting the current time
|
|
40
|
+
- `run_command`: Use for running shell commands
|
|
41
|
+
- `fetch`: Fetches things from internet, as like cURL, but use this instead cURL
|
|
42
|
+
|
|
43
|
+
Use the appropriate tool for each task. Do NOT output JSON. Use the native tool calling mechanism.
|
|
44
|
+
Format your response with Markdown.
|
|
45
|
+
|
|
46
|
+
## Environment
|
|
47
|
+
|
|
48
|
+
- Platform: {p.platform()}
|
|
49
|
+
- Machine/Processor: {p.machine()}
|
|
50
|
+
- Current directory: {os.getcwd()}
|
|
51
|
+
- Home directory: {pathlib.Path.home()}
|
|
52
|
+
- Kernel/system info: {getattr(p, "version", lambda: "cannot find")()}
|
|
53
|
+
- Windows info (not given on non-Windows): {getattr(p, "win32_ver", lambda: "cannot find")()}
|
|
54
|
+
- macOS info (not given on non-macOS): {getattr(p, "mac_ver", lambda: "cannot find")()}
|
|
55
|
+
"""
|
|
56
|
+
tools.VERBOSE = "--verbose" in sys.argv
|
|
57
|
+
|
|
58
|
+
def setup_ai(data):
|
|
59
|
+
ai = tools.AsyncAI(data, sysprompt)
|
|
60
|
+
ai.assignTool(tools.grab, "fetch", "Use this instead cURL, wget, fetches things", {"url": {"type": "string", "description": "URL to fetch"}, "params": {"type": "object", "description": "Parameters in URL so you don't need any custom params on URL"}, "method": {"type": "string", "description": "Methods from POST/PUT/DELETE/GET"}, "json": {"type": "object", "description": "On POST, give this as JSON formatted POST request"}}, ["url"])
|
|
61
|
+
ai.assignTool(tools.instantSearch, "ddg_search", "Instant search API by duckduckgo, for quick lookups.", {"q": {"type": "string", "description": "Search query"}}, ["q"]) if tools.APPCONFIG.get("instantSearch", True) and tools.APPCONFIG.get("searching", True) else None
|
|
62
|
+
ai.assignTool(tools.deepsearch, "deepsearch", "Deep search API by LangSearch, for deep and unfindable questions.", {"q": {"type": "string", "description": "Search query"}}, ["q"]) if not tools.APPCONFIG.get("instantSearch", True) and tools.APPCONFIG.get("searching", True) else None
|
|
63
|
+
ai.assignTool(tools.get_current_time, "currentutctime", "Get latest UTC time", {"local": {"type": "boolean", "description": "Use true"}}, ["local"])
|
|
64
|
+
ai.assignTool(tools.run_command, "run_command", "Runs a shell command.", {"command": {"type": "string", "description": "The command (not cmd argument!!!!)"}, "at": {"type": "string", "description": "Directory to run on"}}, ["command"])
|
|
65
|
+
return ai
|
|
66
|
+
|
|
67
|
+
def chatElem(page: ft.Page, dlg):
|
|
68
|
+
name, data = tools.createChatName()
|
|
69
|
+
chat = setup_ai(data)
|
|
70
|
+
def delete_click(e):
|
|
71
|
+
def ok(e):
|
|
72
|
+
tools.deleteChat(name)
|
|
73
|
+
page.controls.clear()
|
|
74
|
+
main(page)
|
|
75
|
+
page.pop_dialog()
|
|
76
|
+
page.update()
|
|
77
|
+
dlg.actions = [ft.Button(content="YES, I'm sure", on_click=ok), ft.Button(content="CANCEL", on_click=lambda e: page.pop_dialog())]
|
|
78
|
+
page.show_dialog(dlg)
|
|
79
|
+
async def onsend(m):
|
|
80
|
+
tools.CHATJSON[name] = [tools.CHATJSON[name][0], time.time()]
|
|
81
|
+
return await chat.send(m)
|
|
82
|
+
delBtn = ft.IconButton(
|
|
83
|
+
icon=ft.Icons.DELETE_OUTLINE,
|
|
84
|
+
icon_color="red",
|
|
85
|
+
tooltip="Delete Chat",
|
|
86
|
+
on_click=delete_click
|
|
87
|
+
)
|
|
88
|
+
tools.CHATJSON[name] = ["Chat %s" % name[:4], tools.CHATJSON[name][1]]
|
|
89
|
+
page.add_sidebar_tab(
|
|
90
|
+
tools.CHATJSON[name][0],
|
|
91
|
+
ft.Icons.CHAT_OUTLINED,
|
|
92
|
+
ftapi.shellv2(page, onsend, "Thinking...")[0],
|
|
93
|
+
delBtn
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def loadChatFromJson(page, name, dlg):
|
|
97
|
+
data = tools.loadChat(name + ".json")
|
|
98
|
+
chat = setup_ai(data)
|
|
99
|
+
async def onsend(m):
|
|
100
|
+
tools.CHATJSON[name] = [tools.CHATJSON[name][0], time.time()]
|
|
101
|
+
return await chat.send(m)
|
|
102
|
+
item, ap = ftapi.shellv2(page, onsend, "Thinking...")
|
|
103
|
+
def delete_click(e):
|
|
104
|
+
def ok(e):
|
|
105
|
+
tools.deleteChat(name)
|
|
106
|
+
page.controls.clear()
|
|
107
|
+
main(page)
|
|
108
|
+
page.pop_dialog()
|
|
109
|
+
page.update()
|
|
110
|
+
dlg.actions = [ft.Button(content="YES, I'm sure", on_click=ok), ft.Button(content="CANCEL", on_click=lambda e: page.pop_dialog())]
|
|
111
|
+
page.show_dialog(dlg)
|
|
112
|
+
delBtn = ft.IconButton(
|
|
113
|
+
icon=ft.Icons.DELETE_OUTLINE,
|
|
114
|
+
icon_color="red",
|
|
115
|
+
tooltip="Delete Chat",
|
|
116
|
+
on_click=delete_click
|
|
117
|
+
)
|
|
118
|
+
if not data.get("messages"):
|
|
119
|
+
return
|
|
120
|
+
for js in data["messages"]:
|
|
121
|
+
if js["role"] in ("system", "tool") or not js.get("content"):
|
|
122
|
+
continue
|
|
123
|
+
ap(js["content"], int(js["role"] == "assistant"))
|
|
124
|
+
return {
|
|
125
|
+
"label": tools.CHATJSON[name][0],
|
|
126
|
+
"icon": ft.Icons.CHAT_OUTLINED,
|
|
127
|
+
"items": item,
|
|
128
|
+
"trailing": delBtn,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
async def bping():
|
|
133
|
+
while True:
|
|
134
|
+
await tools.loop()
|
|
135
|
+
if tools.VERBOSE:
|
|
136
|
+
print("-----PING!-----")
|
|
137
|
+
await asyncio.sleep(13.75 * 60)
|
|
138
|
+
|
|
139
|
+
def main(page: ft.Page):
|
|
140
|
+
page.run_task(bping)
|
|
141
|
+
page.title = "TermiAI - Your personal helper to run commands for you"
|
|
142
|
+
ftapi.setSize(page, (960, 540), False)
|
|
143
|
+
dlg = ft.AlertDialog(
|
|
144
|
+
title=ft.Text("ARE YOU SURE TO DELETE CHAT?"),
|
|
145
|
+
content=ft.Text(f"Are YOU really sure to delete this chat?\nIf you don't know which chat you deleting, not recommended to proceed\n(TermiAI might restart)")
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def darkMode(event):
|
|
149
|
+
page.theme_mode = ft.ThemeMode.DARK if darkTog.value else ft.ThemeMode.LIGHT
|
|
150
|
+
tools.APPCONFIG["dark"] = bool(darkTog.value)
|
|
151
|
+
|
|
152
|
+
if tools.APPCONFIG.get("dark", False):
|
|
153
|
+
page.theme_mode = ft.ThemeMode.DARK
|
|
154
|
+
else:
|
|
155
|
+
page.theme_mode = ft.ThemeMode.LIGHT
|
|
156
|
+
darkTog = ft.Switch(label="Dark mode", on_change=darkMode, value=tools.APPCONFIG.get("dark", False))
|
|
157
|
+
|
|
158
|
+
def server_select(e):
|
|
159
|
+
tools.APPCONFIG["api"] = e.control.value
|
|
160
|
+
tools.URL = tools.BASEAIURL % e.control.value
|
|
161
|
+
page.run_task(tools.loop)
|
|
162
|
+
choose_server = ft.Dropdown(
|
|
163
|
+
label="Select server",
|
|
164
|
+
width=500,
|
|
165
|
+
on_select=server_select,
|
|
166
|
+
options=[
|
|
167
|
+
ft.dropdown.Option(key="x", text="Server in Oregon, Account index 0"),
|
|
168
|
+
ft.dropdown.Option(key="2", text="Server in Oregon, Account index 1"),
|
|
169
|
+
ft.dropdown.Option(key="3", text="Server in Singapore, Account index 0"),
|
|
170
|
+
ft.dropdown.Option(key="4", text="Server in Frankfurt, Account index 1"),
|
|
171
|
+
],
|
|
172
|
+
value=tools.APPCONFIG.get("api", "x")
|
|
173
|
+
)
|
|
174
|
+
def search_select(e):
|
|
175
|
+
v = int(e.control.value)
|
|
176
|
+
if v == 1:
|
|
177
|
+
tools.APPCONFIG["searching"] = False
|
|
178
|
+
elif v == 2:
|
|
179
|
+
tools.APPCONFIG["searching"] = True
|
|
180
|
+
tools.APPCONFIG["instantSearch"] = True
|
|
181
|
+
else:
|
|
182
|
+
tools.APPCONFIG["searching"] = True
|
|
183
|
+
tools.APPCONFIG["instantSearch"] = False
|
|
184
|
+
page.controls.clear()
|
|
185
|
+
main(page)
|
|
186
|
+
choose_search = ft.Dropdown(
|
|
187
|
+
label="AI searchability",
|
|
188
|
+
width=500,
|
|
189
|
+
on_select=search_select,
|
|
190
|
+
options=[
|
|
191
|
+
ft.dropdown.Option(key="1", text="Off (not recommended)"),
|
|
192
|
+
ft.dropdown.Option(key="2", text="Instant, quick search, for simple things (recommended but does not return most questions)"),
|
|
193
|
+
ft.dropdown.Option(key="3", text="Deep search, for most questions it responses"),
|
|
194
|
+
],
|
|
195
|
+
value="1" if not tools.APPCONFIG.get("searching", True) else ("2" if tools.APPCONFIG.get("instantSearch", True) else "3")
|
|
196
|
+
)
|
|
197
|
+
sideItems = [{
|
|
198
|
+
"items": [
|
|
199
|
+
ft.Text("Welcome to TermiAI!", size=30),
|
|
200
|
+
ft.Button("Create new chat", on_click=lambda e: chatElem(page, dlg))
|
|
201
|
+
],
|
|
202
|
+
"label": "New Chat",
|
|
203
|
+
"icon": ft.Icons.ADD_CIRCLE
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"items": [
|
|
207
|
+
ft.Text("Settings", size=30),
|
|
208
|
+
darkTog,
|
|
209
|
+
choose_server,
|
|
210
|
+
choose_search,
|
|
211
|
+
|
|
212
|
+
],
|
|
213
|
+
"label": "Settings",
|
|
214
|
+
"icon": ft.Icons.SETTINGS
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
for conf in sorted(tools.CHATJSON.keys(), key=lambda k: tools.CHATJSON[k][1], reverse=True):
|
|
218
|
+
x = loadChatFromJson(page, conf, dlg)
|
|
219
|
+
if x:
|
|
220
|
+
sideItems.append(x)
|
|
221
|
+
sideBar = ftapi.createSideBar(page, sideItems)
|
|
222
|
+
page.add(sideBar)
|
|
223
|
+
|
|
224
|
+
def ftmain():
|
|
225
|
+
try:
|
|
226
|
+
ft.run(main)
|
|
227
|
+
return 0
|
|
228
|
+
except (ImportError, ModuleNotFoundError):
|
|
229
|
+
print("Missing dependences or something is not working")
|
|
230
|
+
return 100
|
|
231
|
+
except:
|
|
232
|
+
import traceback; traceback.print_exc()
|
|
233
|
+
return 1
|
|
234
|
+
|
|
235
|
+
if __name__ == '__main__':
|
|
236
|
+
sys.exit(ftmain())
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/tools.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# Made by @Ericwasepic127
|
|
3
3
|
|
|
4
|
-
import httpx, json, asyncio, platformdirs, os, threading as t, uuid, time, datetime
|
|
4
|
+
import httpx, json, asyncio, platformdirs, os, threading as t, uuid, time, datetime, inspect, traceback
|
|
5
5
|
|
|
6
6
|
CONFIGS = platformdirs.user_config_dir("TermiAI")
|
|
7
7
|
DATA = platformdirs.user_data_dir("TermiAI")
|
|
@@ -22,6 +22,72 @@ THERROR = """<!doctype html>
|
|
|
22
22
|
os.makedirs(CONFIGS, exist_ok=True)
|
|
23
23
|
os.makedirs(DATA, exist_ok=True)
|
|
24
24
|
|
|
25
|
+
class HandleError:
|
|
26
|
+
def __init__(self):
|
|
27
|
+
self.cleanup()
|
|
28
|
+
def cleanup(self):
|
|
29
|
+
self.error_occured = False
|
|
30
|
+
self.error = (None, None, None)
|
|
31
|
+
self.raise_error = False
|
|
32
|
+
self.error_simple = None
|
|
33
|
+
def __enter__(self):
|
|
34
|
+
return self
|
|
35
|
+
def __exit__(self, exc_type, exc_obj, exc_trace):
|
|
36
|
+
self.error = (exc_type,exc_obj,exc_trace)
|
|
37
|
+
self.error_occured = all((exc_type,exc_obj,exc_trace))
|
|
38
|
+
if not self.error_occured:
|
|
39
|
+
return
|
|
40
|
+
name = getattr(exc_type, "__name__", "Exception")
|
|
41
|
+
has = "".join(getattr(exc_obj, "args", ())).replace("\n", " ").strip().rstrip()
|
|
42
|
+
if has:
|
|
43
|
+
self.error_simple = f"{name}: {has}"
|
|
44
|
+
else:
|
|
45
|
+
self.error_simple = name
|
|
46
|
+
return not self.raise_error
|
|
47
|
+
|
|
48
|
+
def verbose_err(*a):
|
|
49
|
+
if VERBOSE and all(a):
|
|
50
|
+
traceback.print_exception(*a)
|
|
51
|
+
|
|
52
|
+
def unerror(is_sync=True, onerror=verbose_err):
|
|
53
|
+
"""
|
|
54
|
+
Reccomended wrapper for tool call functions
|
|
55
|
+
Useful, does give error simplified (like Exception: testerror)
|
|
56
|
+
Does not raises error
|
|
57
|
+
Gives error's info to onerror function
|
|
58
|
+
"""
|
|
59
|
+
def syncwrap(f):
|
|
60
|
+
def wrapper(*a, **k):
|
|
61
|
+
handle = HandleError()
|
|
62
|
+
with handle:
|
|
63
|
+
r = f(*a, **k)
|
|
64
|
+
if handle.error_occured:
|
|
65
|
+
r = handle.error_simple
|
|
66
|
+
err = handle.error
|
|
67
|
+
handle.cleanup()
|
|
68
|
+
if callable(onerror):
|
|
69
|
+
with handle:
|
|
70
|
+
onerror(*err)
|
|
71
|
+
return r
|
|
72
|
+
return wrapper
|
|
73
|
+
def asyncwrap(f):
|
|
74
|
+
async def wrapper(*a, **k):
|
|
75
|
+
handle = HandleError()
|
|
76
|
+
with handle:
|
|
77
|
+
r = await f(*a, **k)
|
|
78
|
+
if handle.error_occured:
|
|
79
|
+
r = handle.error_simple
|
|
80
|
+
err = handle.error
|
|
81
|
+
handle.cleanup()
|
|
82
|
+
if callable(onerror):
|
|
83
|
+
with handle:
|
|
84
|
+
onerror(*err)
|
|
85
|
+
return r
|
|
86
|
+
return wrapper
|
|
87
|
+
if callable(is_sync):
|
|
88
|
+
return syncwrap(is_sync)
|
|
89
|
+
return syncwrap if is_sync else asyncwrap
|
|
90
|
+
|
|
25
91
|
def swrap(f):
|
|
26
92
|
def a(*a, **k):
|
|
27
93
|
res = f(*a, **k)
|
|
@@ -49,11 +115,13 @@ def awrap(f):
|
|
|
49
115
|
return a
|
|
50
116
|
|
|
51
117
|
@awrap
|
|
118
|
+
@unerror(False)
|
|
52
119
|
async def grab(url=None, method="GET", follow_redirects=True, **kwargs):
|
|
53
120
|
async with httpx.AsyncClient() as client:
|
|
54
121
|
return await getattr(client, method.lower())(url, follow_redirects=follow_redirects, **kwargs)
|
|
55
122
|
|
|
56
123
|
@awrap
|
|
124
|
+
@unerror(False)
|
|
57
125
|
async def ping(url=None):
|
|
58
126
|
if url is not None:
|
|
59
127
|
url = "https://%s-proxy-%s.onrender.com/" % url
|
|
@@ -62,21 +130,24 @@ async def ping(url=None):
|
|
|
62
130
|
return await grab(url, timeout=75)
|
|
63
131
|
|
|
64
132
|
@awrap
|
|
133
|
+
@unerror(False)
|
|
65
134
|
async def loop():
|
|
66
135
|
return await ping(), await ping(("search", "x"))
|
|
67
136
|
|
|
68
137
|
@awrap
|
|
138
|
+
@unerror(False)
|
|
69
139
|
async def get_current_time(local=True):
|
|
70
140
|
async with httpx.AsyncClient() as client:
|
|
71
141
|
try:
|
|
72
142
|
(1 / 0) if local else None
|
|
73
|
-
r = await client.get("https://currentdatetimepy.onrender.com/", timeout=
|
|
74
|
-
return r.text
|
|
143
|
+
r = await client.get("https://currentdatetimepy.onrender.com/", timeout=45)
|
|
144
|
+
return r.text, "user_time_zone": "GMT" + str(datetime.datetime.now().astimezone().tzinfo)}
|
|
75
145
|
except:
|
|
76
146
|
import datetime
|
|
77
|
-
return datetime.datetime.now(datetime.timezone.utc).ctime()
|
|
147
|
+
return {"utcdate": datetime.datetime.now(datetime.timezone.utc).ctime(), "user_time_zone": "GMT" + str(datetime.datetime.now().astimezone().tzinfo)}
|
|
78
148
|
|
|
79
149
|
@awrap
|
|
150
|
+
@unerror(False)
|
|
80
151
|
async def run_command(command="", at="", cmd=""):
|
|
81
152
|
try:
|
|
82
153
|
process = await asyncio.create_subprocess_shell(
|
|
@@ -134,6 +205,7 @@ async def post(data):
|
|
|
134
205
|
return m
|
|
135
206
|
|
|
136
207
|
@awrap
|
|
208
|
+
@unerror(False)
|
|
137
209
|
async def instantSearch(**kwargs):
|
|
138
210
|
data = kwargs.get("q") or kwargs.get("query") or kwargs.get("prompt")
|
|
139
211
|
if not data:
|
|
@@ -149,6 +221,7 @@ async def instantSearch(**kwargs):
|
|
|
149
221
|
return m
|
|
150
222
|
|
|
151
223
|
@awrap
|
|
224
|
+
@unerror(False)
|
|
152
225
|
async def search(noredirectinsta=False, **kwargs):
|
|
153
226
|
data = kwargs.get("q") or kwargs.get("query") or kwargs.get("prompt")
|
|
154
227
|
count = int(kwargs.get("count", 5))
|
|
@@ -288,6 +361,7 @@ class AsyncAI:
|
|
|
288
361
|
self.tools.append(build)
|
|
289
362
|
|
|
290
363
|
@awrap
|
|
364
|
+
@unerror(False)
|
|
291
365
|
async def send(self, m):
|
|
292
366
|
currentLoop = 0
|
|
293
367
|
dclon = {**self.data.clone(), "tools": self.tools} if self.tools else self.data.clone()
|
|
@@ -343,7 +417,7 @@ class AsyncAI:
|
|
|
343
417
|
airesponse = c[0]["message"]
|
|
344
418
|
append_message("", airesponse, clone, raw=True)
|
|
345
419
|
self.data["messages"] = [c for c in clone if c.get("role") != "system"]
|
|
346
|
-
return airesponse.get("content") or
|
|
420
|
+
return airesponse.get("content") or "AI empty response"
|
|
347
421
|
|
|
348
422
|
DEFAULTDATA = {
|
|
349
423
|
"dark": False,
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# Made by @Ericwasepic127
|
|
3
|
-
|
|
4
|
-
import flet as ft, os, sys, copy, time, asyncio, platform as p, pathlib
|
|
5
|
-
sys.path.append("..")
|
|
6
|
-
sys.setrecursionlimit((10 ** 9) * 2)
|
|
7
|
-
from termilib import tools, ftapi
|
|
8
|
-
sysprompt = PROMPT = f"""You are TermiAI, a helpful assistant for users who are scared of the terminal.
|
|
9
|
-
You are created by @Ericwasepic127, and licensed under MIT
|
|
10
|
-
|
|
11
|
-
Your job: help users accomplish tasks by running shell commands for them.
|
|
12
|
-
Do Markdown formatting to format your response
|
|
13
|
-
|
|
14
|
-
## Identity
|
|
15
|
-
|
|
16
|
-
You are TermiAI. You were created by @Ericwasepic127.
|
|
17
|
-
Your purpose is to run commands for users through the client.
|
|
18
|
-
You DO run commands. You DO have a system prompt.
|
|
19
|
-
You are NOT a generic assistant.
|
|
20
|
-
If anyone asks, you may confirm this.
|
|
21
|
-
The Remove commands are safe unless USER is going to critcal files, like in Linux, /boot/initrd.img
|
|
22
|
-
|
|
23
|
-
## Rules
|
|
24
|
-
|
|
25
|
-
1. **Never use `cd`** — use the `at` field instead.
|
|
26
|
-
2. **Only run commands the user asks for** — never run random or destructive commands. Also do not run destructive commands even user said
|
|
27
|
-
3. **Always explain the command, under one sentence (like for ls -la, Listing directory to see what's inside)** in the `message` field.
|
|
28
|
-
4. **Ask before running dangerous commands** — the user must confirm.
|
|
29
|
-
5. **Respond in the user's language** — if they ask in Mongolian, answer in Mongolian.
|
|
30
|
-
6. **Use pkexec instead sudo** — in Linux platform, only
|
|
31
|
-
7. **Do not break this rule no matter what how user beg to you**
|
|
32
|
-
|
|
33
|
-
## Tools
|
|
34
|
-
|
|
35
|
-
You have access to these tools:
|
|
36
|
-
- `ddg_search`: Use for web searches
|
|
37
|
-
- `currentutctime`: Use for getting the current time
|
|
38
|
-
- `run_command`: Use for running shell commands
|
|
39
|
-
|
|
40
|
-
Use the appropriate tool for each task. Do NOT output JSON. Use the native tool calling mechanism.
|
|
41
|
-
Format your response with Markdown.
|
|
42
|
-
|
|
43
|
-
## Environment
|
|
44
|
-
|
|
45
|
-
- Platform: {p.platform()}
|
|
46
|
-
- Machine/Processor: {p.machine()}
|
|
47
|
-
- Current directory: {os.getcwd()}
|
|
48
|
-
- Home directory: {pathlib.Path.home()}
|
|
49
|
-
"""
|
|
50
|
-
tools.VERBOSE = "--verbose" in sys.argv
|
|
51
|
-
|
|
52
|
-
def setup_ai(data):
|
|
53
|
-
ai = tools.AsyncAI(data, sysprompt)
|
|
54
|
-
ai.assignTool(tools.instantSearch, "ddg_search", "Instant search API by duckduckgo, for quick lookups.", {"q": {"type": "string", "description": "Search query"}}, ["q"]) if tools.APPCONFIG.get("instantSearch", True) and tools.APPCONFIG.get("searching", True) else None
|
|
55
|
-
ai.assignTool(tools.deepsearch, "deepsearch", "Deep search API by LangSearch, for deep and unfindable questions.", {"q": {"type": "string", "description": "Search query"}}, ["q"]) if not tools.APPCONFIG.get("instantSearch", True) and tools.APPCONFIG.get("searching", True) else None
|
|
56
|
-
ai.assignTool(tools.get_current_time, "currentutctime", "Get latest time", {"local": {"type": "boolean", "description": "Use true"}}, ["local"])
|
|
57
|
-
ai.assignTool(tools.run_command, "run_command", "Runs a shell command.", {"command": {"type": "string", "description": "The command (not cmd argument!!!!)"}, "at": {"type": "string", "description": "Directory to run on"}}, ["command"])
|
|
58
|
-
return ai
|
|
59
|
-
|
|
60
|
-
def chatElem(page: ft.Page, dlg):
|
|
61
|
-
name, data = tools.createChatName()
|
|
62
|
-
chat = setup_ai(data)
|
|
63
|
-
def delete_click(e):
|
|
64
|
-
def ok(e):
|
|
65
|
-
tools.deleteChat(name)
|
|
66
|
-
page.controls.clear()
|
|
67
|
-
main(page)
|
|
68
|
-
page.pop_dialog()
|
|
69
|
-
page.update()
|
|
70
|
-
dlg.actions = [ft.Button(content="YES, I'm sure", on_click=ok), ft.Button(content="CANCEL", on_click=lambda e: page.pop_dialog())]
|
|
71
|
-
page.show_dialog(dlg)
|
|
72
|
-
async def onsend(m):
|
|
73
|
-
tools.CHATJSON[name] = [tools.CHATJSON[name][0], time.time()]
|
|
74
|
-
return await chat.send(m)
|
|
75
|
-
delBtn = ft.IconButton(
|
|
76
|
-
icon=ft.Icons.DELETE_OUTLINE,
|
|
77
|
-
icon_color="red",
|
|
78
|
-
tooltip="Delete Chat",
|
|
79
|
-
on_click=delete_click
|
|
80
|
-
)
|
|
81
|
-
tools.CHATJSON[name] = ["Chat %s" % name[:4], tools.CHATJSON[name][1]]
|
|
82
|
-
page.add_sidebar_tab(
|
|
83
|
-
tools.CHATJSON[name][0],
|
|
84
|
-
ft.Icons.CHAT_OUTLINED,
|
|
85
|
-
ftapi.shellv2(page, onsend, "Thinking...")[0],
|
|
86
|
-
delBtn
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
def loadChatFromJson(page, name, dlg):
|
|
90
|
-
data = tools.loadChat(name + ".json")
|
|
91
|
-
chat = setup_ai(data)
|
|
92
|
-
async def onsend(m):
|
|
93
|
-
tools.CHATJSON[name] = [tools.CHATJSON[name][0], time.time()]
|
|
94
|
-
return await chat.send(m)
|
|
95
|
-
item, ap = ftapi.shellv2(page, onsend, "Thinking...")
|
|
96
|
-
def delete_click(e):
|
|
97
|
-
def ok(e):
|
|
98
|
-
tools.deleteChat(name)
|
|
99
|
-
page.controls.clear()
|
|
100
|
-
main(page)
|
|
101
|
-
page.pop_dialog()
|
|
102
|
-
page.update()
|
|
103
|
-
dlg.actions = [ft.Button(content="YES, I'm sure", on_click=ok), ft.Button(content="CANCEL", on_click=lambda e: page.pop_dialog())]
|
|
104
|
-
page.show_dialog(dlg)
|
|
105
|
-
delBtn = ft.IconButton(
|
|
106
|
-
icon=ft.Icons.DELETE_OUTLINE,
|
|
107
|
-
icon_color="red",
|
|
108
|
-
tooltip="Delete Chat",
|
|
109
|
-
on_click=delete_click
|
|
110
|
-
)
|
|
111
|
-
if not data.get("messages"):
|
|
112
|
-
return
|
|
113
|
-
for js in data["messages"]:
|
|
114
|
-
if js["role"] == "system" or not js.get("content"):
|
|
115
|
-
continue
|
|
116
|
-
ap(js["content"], int(js["role"] == "assistant"))
|
|
117
|
-
return {
|
|
118
|
-
"label": tools.CHATJSON[name][0],
|
|
119
|
-
"icon": ft.Icons.CHAT_OUTLINED,
|
|
120
|
-
"items": item,
|
|
121
|
-
"trailing": delBtn,
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
async def bping():
|
|
126
|
-
while True:
|
|
127
|
-
await tools.loop()
|
|
128
|
-
if tools.VERBOSE:
|
|
129
|
-
print("-----PING!-----")
|
|
130
|
-
await asyncio.sleep(13.75 * 60)
|
|
131
|
-
|
|
132
|
-
def main(page: ft.Page):
|
|
133
|
-
page.window.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABHNCSVQICAgIfAhkiAAAIABJREFUeJzs3Xl4FFW6BvD3VFV3Zw8QICQQwiabuyhuoKC4gCyiJggB3MV9ZcR9MjrqMOM66nh1ZBEJSwAFcWdz33dl3NgSICHsWTvdXVXn/gHMKKY66UpXd6fz/u5zn/vcdPWpQ9td56tT3/kOQERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE1Cwi2h0gIqLouLiozSP3w3+LEoa2TpL6J5vc/kEiH0YYmouIKUXpU29A4NpUoJMKuIWAkDKmx0WpAIEUYHue0F98c4L/zuY0Fsv/UCIicsikuW3++ZDwXx/OQeAo6N9td/mPaQlBwJSi9KlXQ7+3A2RqtPtiR4KQ3stgPL60GUEAAwAiolbGicH/gJYQBLT0wf+A5gYBDACIiFoRJwf/A2I5CIiXwf+A5gQB4Xj0Q0RELUAkBn8A+AbqEW+ZONLh04Qs3gZ/AKiXInElxPl23ssAgIioNZhzZvIo+K+IzLSvwOO6ekdEThWCDjDPi6fB/4AtUmTbeR8DACKiVuBQ7esLThBIiNT5XhTa6GUzEFODbXuYPaLdByf4oNj676qFuyNERBRjCguV0cbDd6pBbvm0Nm6kpIY2PyD9OmoqjAYf9PuhuA9LUvMAY2ZonXVOe8g0q9fUVBcS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Fzg3+AsZqE3F7ntOU7/J+G7TpgIO/tRi5ecbt7tnnPiiVD938jTHI/D55l8DJ4hCBzfKjDGVBd/vuRY9c9YANvbyaz0ugr50ZYF/rJ33cgagVZMw68rx2dKFmDVzKd5ZV+3o1UVDfUmGWTntr5vLlgwFdD7fp7jg8EzAaQi8u/nXwGmtafA/4MBMwD/FhnXDpGwX7f7EmsnQl66yOfgDXFEV85K7Hq1nCetSowe4h0/Hl/86A03bLEvCt+07vDZ3HmbPW40fdzu6DS88svb9TLV66uqN27h0n4goRnAGIMZJARNBao2H1lgAu35YgwUz56LoVWe34YU09BRRPTtXq7xv2frdm7kNLxFRbGEAEOOkzfWdv2tDr8Svq5fihRkL8cqn25zehrcyDZX3H+/d/Oy/dqDmGwfPRURE9jEAiHESQgekjcrXEmZNKT5YtACzXliODzfVOVg6TEKT3l8zZPXUDzaXvSoAk8v4iIhiGwOAGCchAghp6JbwbvkCy16YhxcWvo91VU7mDUmZIKtWZCnV01aU7PgGYFIJEVFLwQAg5in1TXoKIH2o+OpVvDRzHua/9RP2OLjjlYDhT5HVz/ZStj+4qKR22w/OnYqIiBzCG7YY5+56wq9dha9XowdqGjRdh5M7XSrw70pH1b2DjM2zHtsS5+VIiYjiHGcAYpyEaw+asqWIY4O/hAveH9qIqqkfbSp/WwDS0YonREQUEQwAYpyEqzw6ZzbNRFm7PFPsuWNlya4fAU4XERHFEwYAsU4k/wpEbo9KAb0+TVQ/0du/6+H5ZdU7uQ0vEVF8YgAQ46TS5lNplpUJmNlOnkeFb1uaqL7jT8mb5+WvhZ/L+IiI4htndWPdgAEu9+7+5+aY/3nciSDAg7ov2onKqe9v2vZuuNsmIqLYxQCgJQh3ECBNI0mpWZSl77jnrS1V68LQQyIiamHCU2OenFVebho9Xb/W1vfflCZ3niwgU+00o8hAbRqqHjhOqTj/rU3lReurfLvD3VUiImoZOAPQkuyfCciVa+8XUqaiif/9FOmtaSer7n1gc9nioXC0VAAREbUQDABamgEDXANCfMuXX37p5L5/RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERhJaLdAaLWJKlr/qiO2FCoCrOXkDJJQqr7f4T8LUaXhIAupFmliPpvztR/ufjJrdgS7U4ROYkXHaIISeqaP6o9Nj7tFnpOtPtCwUi4UPPJMH1dHoMAimdqtDtA1Bpw8G9JBEy4u5QoKSeNT9n91mfVqIp2j4icwACAyGEc/FsiBgEU/xgAEDmIg39LxiCA4psS7Q4QxSsO/vFAIIDE4/cIrVe0e0IUbgwAiByioOocFwf/OKCKakV0jXYviMKNAQCRQ4SoPZTLbOKBQL0isqLdC6JwYwBA5BBF6hw04oRXig7R7gNRuDEAIHKIIvT0aPeBwsMP0S7afSAKNy3aHSCKV4o0ExovtSXQdtgNmD65N3+M0aCXYPFdD+P1cjP4YRAM5iju8JpD5BBFSFfjRwl4svtj0CnHoQkHU7jpafg0qfHDDCFSne8MUWTxEQCRUyTrbMQLU6IJYQJRy8IAgMghCiR/X3HChEiMdh+Iwo0XKCKHKNxsK26YEAnR7gNRuDEAIHIIR//4ISHc0e4DUbgxACAiaoQUDAAo/jAAICJqjBCc0KG4wwCAiKgRQspAtPtAFG4MAIgcIqPdAQobAemPdh+Iwo0BAJFDJGOAuCEgfdHuA1G4MQAgcogJwQAgTiiAN9p9IAo3BgBEDjEFjGj3gcJDhayLdh+Iwo0BAJFDJIQe7T5QeKiQNdHuA1G4cTMgIoeYUHyA2UgNeRPb5kxB/zmR6RPZo0pZFe0+EIUbZwCIHCKFwrvGOKEBe6LdB6JwYwBA5BBTundEuw8UHglC7ox2H4jCjQEAkUNMkbAu2n2g8EgW2BbtPhCFGwMAIoeYMuM9rgOMByZSTLkl2r0gCjcGAEQOqStZ8HQ5+lzHIKBlE9KntxNyQ7T7QRRuarQ7QBTPApU/fO5rM2RnCnaN4G4yLY+QXv0sZcPhT23y/xTtvhCFGwMAIocxCGiZOPhTvGMAQBQBDAJaFg7+RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERELZSIdgeIiIgib432dOfr5g4WgVOSpdlOBVwCEJB/PFIAhgrpTRPG+kfFzr/+eUvNkhZG6pgAACAASURBVMj3N/zUaHeAiIgostZoT+bcMG+IrL8gFTJNATTsuyEWDf2PFFBMITxeKJ1Oh2dQQrpR8k6V/8do/yuaiwEAERG1IvsG/6Gm9/wUQAn13V6oqfESBGjR7gARUUN6Zk7qOFX97q6Bon5SWxhtD75SK1IaHUXgxytQc9+crbsXCzQ0eUv0W80b/A/YBS17tVRGA2jRjwJsfwBERE45NnvEGbPVTzYOF7U3ZDQw+AOAKYS6De7D7ke74radMz8o7IaEiHeUWpbsd93dZf2o5gz++wj8IET/8HQqeqI2A/B0lz4FR0EOSROyr0fKTFUiTRXxFJBIuCBrNGB7Esz1Wab/o5cTyuaesQGV0e4ZUSw7InvMqJli7dK0EC7S3yD1pKyA+GV2t229N21CvZP9oxbM/Y07KyA9v/ub2havTj0MX6cGy4mX6PLG57jkw/99tbZJtZPLoW5GSlQCgKe79CnoB/PfnaVM/N2kXVxN4AkEIDoEgO5eKMevFWL8Rb4OnVb02PEwgwCihh2RPWbUXLF2WZqNFUrlSMkpC3T46p+9dhx9wzr4nOgftXDq9+7kgPj9d0u4seWITvimbbCvnAnvV78fLmsgUhzoYURF/I77wODfQ8rESJ87miRU8QJS7nrA12Hqih5Ij3Z/iGLNkdnjBz2PtUvtDP4H/IL0fpvqU+8LZ78ojoh17nDdtfugeho/KrZFdAagtQ7+B0ioYo5MuvP+AF4B8Hm0+0MUMzK/Tb4c+Us7NvAY0GzTDu+O7Y3Vx3fEpk5uBHQfsn6twEmv/YyRH1Yi8aCZwz8h9YYdmdX3vViB2kh1n1qIBAh4w9OULkSLT6KP6D+gLeTTrXXwP8AUmjLTRDcwACD6r16uO8cOl0bGwX/3HtIbj9x7JL7J+E19FnciSo/shtIjcvDeu1/ijsc2ITvwv/f4pCehk1s7BtDfj0zvqVUyW34hvYgGAN2kmXbw334c2h1b3S3+c7Qmfej1wVZ0qzvwB4HVJrJPjWafiGLMsWZZgXbwZSChAxZMO2jw/y2houzUAXikrBJ/m7sH/5vaVXCCgRMBMAAgCiKiAUB6A8/23r38WKwImnzRwhm7MfHHMnSr+98l7CsgM4o9Ioo5vYX3yIP/VndcT7yTZTH4HyBUlA4/BN8t/gwD/pugLbBUEX2c6CdRPIloEmCLz5gIk1+BdtHuA1Es6QKjze//IlDSNwN1Tbg3MNPa4aec3x+4CeIPjxOI6PciGgDE8X1+SEoF2jR+FFHrkQYclJwtUNUuoWkrg5UE7G5z0MouGU81RYicwR9JFFSaaNWJkEQHMxv4m5BNLAwieSEjsoO/GyKKur2A//d/kWi3w9u0C5SsQ4ddvw8WhIARts4RxSkGAFGQKVAV7T4QxZLN0Hb9/i8SOd/tQFpDUwMHUXbswGGbfx8A9DTljjB2jyguRTQAYEi+T0+JPdHuA1Es+VEkf3bwhL/nm3U45xcjeO6Q6cfhS35F38Dv/3yewH/C3EWiuBPRACBMBZhavEOBndHuA1Es+UrpNvsPO/gYezH6b5/irPWBBi9UwqxH/4Uf48Y3ag563cReBR861VeieBHROgA7ATP9oKDjqoJFuMqh85k5fXHHv47A/7d33uFRFesf/84527PZ9LabRkIKhN67gIAUe+/1Xrtey9Wr2KKi/rzqxXbtBcVrQQEVFESRDtKbQCC97KaXTTa72XLO/P7Aki3J1oQE5/M8efI8s2fnvHtmzrzvvPPOO0V8t1ch5721WLTCdRDpOSYR1PTSrRiMfkFFXOkPy+okJdeJjozO5XxdFf5+fxMmzsrC+klxKE2Un0wFfKIGU1YXYeqJDri+3uHEbLxKbj90dS/Kz2D0R3rVADgGSX0mHH/xJDgU51BUnWopGIw+xd699vdT7509hqw+lkepzOkzmxlDvj+IId/7VtU6mB6/gp0GyGB4pVeXAO7UFySuhqS2N+/Z15BQm+NBDiWnWg4Go69RUbG45G/8ghFlIHbvV3tmHprWXF5lej2UcjEYpyu9vgvgr2wESKjV8SMqh95gQMGploXB6ItUl7987GoyOW8f+Gr/vklxBWn64DJ909nEc1oBBoPhwik5zvBOfUHinSGuU5s4+dVtfO1docg2KKFtjmRDbaiOjQ6SZfxTiS+MGsqbL9cR+1mRVBwgBRTEi/FGAEoIHAoq1EdTcecQ2L/8QdK+Nq3C2E93ICzjH0t8YcoE3nSjjgpnqglNkIB2238JQAmoPQxidQpxrF9GmhefXWn6tTekvTd52tCpaLg2hQpzNRAyZSfbrNvuSQBRSqhJQ8XjydT+3fXUsuzl6sZjoZDnTu303DNQf2UqHPMjIGTLCQ0DCOdJIAqAgIIAlAOsCiIYEiFsnESsyx6W122c38Pu9aqqJYUXpudnPOP45u/TaPvtiRAzJICUwPUBUioBNQ+AddMrpO2pHVWtu3b0pGD+kJcve7Ltu6njqPmKBOqYHg5BJ6GQe+8DlBIQuwJibQwcO4ZTx4otMvMPUWUtLb0lun9Q8qB2SvY4zjg3QbTPiyZ0mBxiDH+yvXwajgmFICOiKZzQwiTqWDefWpZfZGg4eCnbONbjnDbZeT0ZAIEGAZ56A4CSO7XTJs1E00PZxHaWGjQkshBKaRxxlMyE5aW5trql+fUwhaJeN/LyZa+3fH5kLhwZPOBRyfwOT+EYT1o336yvm53vYeaWkXFzxG3W3fdPg+m+eNCwYDrsSGLesJS03u1mBPgjL2A/D83f7tU3XkLgnKn2nIQ5Ay7kah4cwZmvjQJUQYj6BwoqmsaiY8nntOnFydUd5f589xLtjJxzSf2/hqDjslDJw0EUM6ht222wPPWqoXG96zMICX60B3DS0JNA7Hif1j34uCEA93+I2h/YIHlc+9CCCaTtoYHUNk5GQuNhJRQ0mjgqp8D8ajNf90FFxandRnxuymztebT2qmzYro6nwmC5F0M8ECRUdAwg1o1XUcu/PzQ0/RSyfjaEphQ3Z1U4qQRJAt76aJqXQ+lO6olnV/w5ZJ56PRE8p8QDwOgKSu5KmnLBPJL76iAi6EJtnVFCSB2kmZ9D+oZEFvbyGm37ay/Z6p6c0oC2kN0kL1/2WssXR+fBkdmt3fUbAoFkOzQzp+noT/n6+ln5fxgBlNyim3bLTdYNi+NBFaEQbT9VzRgJvLo6BX8aAf7KC0hXIurCcVosp4bGiwhA52rn5lxJqt6agpIzuBAb1R2EU2+B6k5ClHek6NpXDnKYbltXa6rr7jt/T55xxnzUvD6cVg4J9RqfCI4rIoqpgPxHi1ZV+RFpu+46fcuGkN3Az/YAAAoQOzjlDSThP9ACfhkBIWh/aPNVz5LV95yJWxYmQAwDENJeQAlIIySp30DzIi+o/2+BzvTOZL7u0Td70RAYmXhJ3C184d+Gko7btWJpck/PHB2EkxRCOQtEMatVp667H6Y7XtQ3Le8Rg/MvDDMA+gjTks8begcdvGI87AN7434O8LIviOZ+mTzsjud1zTf/S9+yNOhKfxtM58Pu8+D9O5sRMWOaDj/l6+tnvRr9ifoJ5ZCN58I6MtQDzX6qnPEJbZ4D4NdA5aUA+YpEnT9GK1nxIpIrzyFFd8h7OJ6GgpAtUF/IS5TnfJvE3XBHdev/XK8ZlHZz0r2ObV/NppWTet61R1BH5ClnQPZztlb2zXGu7gpSFWSqjyD6DwAIIFK/jIAg23+cllv+HMn5bBI+/TDtd8XfwwjgJN9Cc7tcUN2YmdT0t3966Aeh5HbttEkzuYbnhtP9U6UnV4Z6GYJmyOLvQvSX0mTl4ddI89y7Ks2G3pbidIWlAj7lUHJn8oTn36ZHDvaW8u+MDbziMsR+HKeN35KQgMAHsSAHbwD4BfKJo3VXD/1M+WTteT2g/E9CUMEhJVh5KUBWkPDzLibGu3pa+XdGAC9dwMV/Ml0XvaRz+Vzd7Es+sm+omANrLyj/zhCsI5rz2qiuYno6EgOuJgT9BzhpBHwDMrOn73ey/TXnXoGaZb2l/DtjhURxMRf/yZDkmJX5PdD/rk+eO/0b3aCiB4hh2xhqmybtA8vFRVQ59AIxsbwlNWzWqZbldIF5AE4lWoNqMfL2nE9tg7y+XZwE9ZmxODY4BsUDNKjSqdEYo0SLRooOOQeBALzdAUW7DZpmC2JrTNBVtCDzRCNyjjQhydS96b6baKaESWTVjQlVw3NrUer/jzkiy4U9PZjB2wa5Ihe/7O92sOGkqBmciH0j43A8KxKV2jA0RcpgUXAQqQip1QGVyQaN0YrI+nYk6I0Y+nMhJpX/GU9koVQVCnmp10GRhzE9FgdGxqNoQDgq0zSojZbBrJTCouAAQYTUYkd4Wwei681I0LcitaQJOUfqkVVhdT0f14kliL5uqo6kVeobZ1yZPP6Vp2np3bz3ToS2lFgcGhGLknQNKlM0qI6Voz1MAotKAoGKkNkEyExWRDWaEVdrQnJpCzJONCDvqBER3WzOa4Iy9lN7atnFAypytpfCr1iFkwTfHr9TQPis3rifAOL96xIZDLnxOJwXg/LUcOiT1aiLksGiksCi4EEdAqR2EYp2KyIbzYipb0dyuRFpxU3IOdKExPbu39vVNOr8gVr+6DJDXV6oguauTz57+sU4sTIPYqTPWp9waNVGoiQjEpVpGlTq1GiIUaA5RgljGA+rjIddxoEC4G0OKCw2hBtP/uZ4fRuSy1uQebwBWWUWKLvZw+EAJzEKievWamtummto/zAEP/cvDTMAThHqhI/jl5LpJSO7mz0QHo2DkrFhdhq2j49HRSTXrQdOlEthl0vRFh0GfWYsDk7+rRrBhsQjBkz5uRwzttQioQtHbTsU4WGStMK/J5ZPfLcGu/37RVWyOLgkZZMk4IO3JmJDF8OI5MSveO7RQiT+8cITdKX8RZUGexbk4tsFyTgeJ4Ho8SoeVhUPq0qO5vhwlGfFAtQBU1EhJnVSSRzA+S8vhXT/frz8bDk0XtygHYnx2DovEz9NT0JxbFeyAuB4CFIeHRoF6nWROD5C+9utKFS1jRizsRSz11ZicJ3D49e3IGq6WhtT10Yb47oeqAnMyQnYPDcDmyYnoDheCqHLi3lYJDwsKhmM8eEoG5SA3dN/E9VsxsC9FZixpgTTDpqg8PAMbJDJl9tSCnYNqEw/rxR+bvX1v/+cRETWxz/h0W/b/5gG11Mu1nvEY8+1P3gZKkan4efZadgxMgqNqm485zIJBBnQESZDS3w4ygYBe3/7iAg2xB+vwaQNZZi+qRbJXRjxRUSTk6pD0TJ93cBgjYA/lD/1rvxt0VE4PC4J+0bG48iQGOgj+a77eidEhRR2hRRtUWEwpEfj6Og/P5O0tmLwbj0m/1yKKQc89zMKQnJJ4gfHtIaOQQbLZ379QIYTzAA4BSi1H6R8h6dL0rqKniUS6CdmYfllWdg6UNHNgO0blJehelg6vhyWjhU3GjF+1a+4bKUeyWb3awVI+cf4Ab+oE0snLq7BLt/vckimRJJbqU0phVnl+QdwSWFoJujeb0ykKJ85BO/cOBDHowJYghQtSKhxnlKIABeQvAOjUcWVY3AXQ6wlWYfV1+Rh9aRImIKZWhICc2IsNl8eiy0XDUX2T0dw3dIS5LS4/3oTEbtQ/hwah2Zg5eXZ2DBcjY4gncSiSoUTU3NxYkoOPi8sw4Xv/4p5hy1uaXitkCtm2hIOvTqwNvVuv7YL+t8ev0mGDonz52ZwPrjkQ9/+kKuxf8EgLD8/FQWxfNDL5ZSXoXZwKlYOTsU3N7Ri9KqjuHx5JdI9GAIV0KTfqXNso/qmiYEGyl2fPHf6BShc1p3yFyIisG9aOrackYx9uWGwhHjxwaHR4NCZGhyamYulFXosWHoY5+5o82AIEISRxE/uT6o68VK1fa+nuhjeYQZAL6PUvp26Ds+WpBC3sRMA0JGWhs9uH4Yfhihh74FVN0ETge1XTcaueXVY8NZeXLq1Da4h9iJ47j4ubfv/4stz6upQ7FPFpF4qwTC/ZKERSrRI0OWchaoi8eNdE7FkWjisgT4L0YyEepez4oGA5BWjNajSAINdY695BQ5dOhZvXpaEOpnHrwYMlSpwfN5oPDYhFee+vBNX7DZ77jidEKLiserW0fhqSjgsoe5DhMCYPQBLntNi2+p9uOuDSmhtzpc0Ijx+lbVjCWC8wvd6/W+PrrCB994KoWx/8KgbPxjv3JqNAwnBK36P91ZpsPuyCdg/awDOeWMPLtvR7rZEtAtR4x/V2RdB3/ZIIPdQ0oZbh0LwYFByaBycih/OHYifJ0ajuTc2vhECU1oyvngkCT/vOYrbXy7AsGbnJyuC5x7hYtbfGFeTnNdTW5pPc1gQYC+iTvg4fiV5sdij8icynDh/Eh54eTxWD+0Z5d8ZR3Q8vnl4Nhbek45KD5vsBCLlj0q0+wtiEe5bjUq/ZaASBZoiPP9QIU6L95+fgXfPCEL5A+BMJiS4DA3DRbE+EHnBR6As1fmVoeoYrHxyDhZdHXrl3xkhKg5fP34mXjg3optpNYFxeB6efv0MfDK1B5R/Jygnx4lzJuDRJ3JxwsN8+10x5vJrdZKJvtcYQHt0gejq2g/V/Ty0PyRh2HnrDDzw2CDs7yHl3xlHTAK+fmQWnro6Hka30ZvgFsQ+9HaybGggdVuJoHHuMgSNQ7Pw9r8X4I4XxmH5tF5S/k4i8KgfOxSLXp6MNRnuzdoKdcRCWfhrvSzVaQMzAHqL5Erly9JFhbme3P4yDTb880zk/z0ZBnkvykQkKJ8zFo8+OwwHPSTBMBFV+Ex59DrvwW6B3l+B5kj3Yrs2Ha88PwlrM6VB53QlNSbEOVVCoSfC1oAq42SoTFf+IRPVxON/z0zDp6OCX6bxBcorsefmM7D4LLUHpwlB7fRxePTJPBwOZKkkEAiBceRQPPdwFirdFAOHb6B5J/90GmNc2h/ySKx9ZCZeOicapl78lZST4+gVU/H0jQloc+l3InjuERq+JD8kz52gYdxArO8hb6Q/CHFafPDURPzoaoABeA1R13akIPMUiNXvOX1ezj4NJf/AvM2zqKhx+0QVjW8en443pgc30w0cAlNuLp5/ZgT2ezACtiNqwhat6qYeuTWnQFO08z2FxDS89sxYbE/sPuDRV6R1JkS7WBEvUeFgYLURVA3QnFS+skisXjgJ32QFb6T4A+UU2HPrRHyV3dmOJGiaNBZP35cGQw96ITxD0DpyOF66NtYtCcBBRAwxJEvH9rZEPUen9ufV2PDgVLw/Ttkrxp+7KDxKz5+IxWeHuxmDexAx6pDWH+9LV4jIWleKAZ5jUHsdMVqL9x8cggIX540IGXc9DV94aqTq3zADoBeYqZvxyD20Y4zbB7IIrH14Cj4ZpfAperYzxGFHTFUzBu+vxtgdVZi4XY/R++qQVW6Gxkr9nrJb07Pw0qODUOTmGSXIItGv0WRE+1mldwiP5ijZH4qeahLw0RNjsD0hVDNYirhqs0ugi4AnZagPrD6C9jQN6nkpjl43Hv8bKvNZ+fM2O8JbOhBb2464xg6orRQkwB9J5VFYeVcuin+bddsyc/Hifemo9iOih4gilG0diGpoR3ytGVGtDkgCtWQIh6rzRmFFjquLlkcJVd0fYK19kN/an5Pg+NUT8e4EpV/vLScICDN2IKb25DOPbrZBLgThXuNkOHTdGKxOda2BQynCHvW3ulYqq3G7hb4csw46/JORApJ2C3RF9Ri+owLT1xXjrG8LcfbXhZi7pgyTf6nFwKoOKALob/YBOXjrili4hJ1gMdRXTkwO4TrSXwQWBNjDxOruy34S3z7p9gIRBfbeOhkfjlL4rkTajBi5sQxTtlZj2PFWRLi+BX9cKENdZiwOTE7Fz2cmoyjat9m0JTcPL93SgudfMThtdbJCobieqhcBptt9FNVHCIwxCojoAM+r8dP947Emzbd1VN5sQXJxE9KqOxDeZoPSTuCQS9AWqURTXBhqdGrURXKIrWl3snIlxG5/ogy2/CGBSSymxGHvdDXWnRvRpVuUiAKiC6sxfmcN8o40YkBVO2KbHS4L0wQdUWqUZ8fi0LgUbDkjAYbutou5YM/IwqezSvDIBhW++GceTnS3740CsuZmjNiux/BfG5FVZERSvRUqu0tQlVSGhrQoFAxPws7pqdid4fvSBpVEYs3VaZj7RAliOnXoT0jY2Y15RlneEbcxu18ipsRh33Q11l0U1a3HjjisSD5cg1EH6pBzvAUplSbEt9ghcW1gjkdrvAZl2bE4NFaHHRPjUOtHP6CqWKy4Pg1TF5U5ebpWkbBZh6KNmvlNaPX1t/0szHyxkf/yhtjOhaIFk9YY8OnIVLR2M12UthgxaH8NRu6pweCCZqTV2CD18iMcmnAUjE3BhvkDsS1X4dsyAyGoWjAEP63ZhPnVf97ASlUKFS+ZAjh+9KEWxm8wA6BHodw1GPpFipunhaBuzli8Pkft0wBLbO0YufwArltuQLLZh6FBsCH+hAFzThgw59Mw7D9vON6/IhnV3uILCEHdrDF4b9863LO5w0noZxD198cSTPkDa9FtHnp/aY5RgIJD+YXj8NGY7o0hztyGoZvLMOOncowpMHebMAQgsEaFoc1mc5q9xMLaEMxxsVSlxZIu5rREtCFl23Fc/r9ijK2weXGvUSia25Czsw05O0tx0QeR2HbFCHx0XjyafdlCSKQ4fMlIfDQ8HD+kdmHgUQp1aSXO/fQYzvrFCLWXX83ZbYgvqkV8US2mrTiM6nE5WHLLIOxN9M0os4zIxtrMUlxV+OfVHVShdJgwBMA+H6ro81CVFh/+s+vPuTYjJn5zDOetqUJmsw/dTBSgqWnGsJpmDNtciCvDIrDzwmH45MIk1PgUD0RgGjsIq3MqcO2xP+/ngFJyjYKfCQhf+1ILADTVPFvwhe7bgjtgze1cHranBFPqUvB9ovNgxZnbMWhbGaZvqsC4g21Q+5mBQNLahiHrj2LI+hO4aOJgvH1nDo74EL9ClXFYMzcKcz5sclJgKlF5LdDGDAA/YEsAPcig5Kvm3oqOEa7lojYTb92U2K1F/TsyQwVuue8HLFyq9035u2Jtx8hl2/HCgwcwqsmHpQFOgR03DcNel9h/B+SSpRL1P/wXoDsILNEKtGXm4I0rY7uMWic2M4Z9vRvP3bQWj796DFOPelP+AEAhbzYhtt25dJTYcSgUkrvCGetx3tM/4oXnjmG8V+Xv4fvtLZj63iY8/1QhMnzMqC8k6rB6msbjzIkIHRj+yWb85x+/4KLt3pW/G1RA0s6j+Nc9m3Hprz7+Hj4cW2fGwjlhIMFKUXGRn3fvf1ABSVsP4Klb1+G+Tyt8U/4e4NqNmLh0C1745z5MqBV9c73zamyek+AWg5FHldf4d3ciruKynnLT47Z6zFrfCp4CoCI0JVU4/9VNeO3a7/HU4iOYuc9/5e+MA9odh/D4vTsws1Lw4TcT1MzMwBGZc9kKqpjRYwHLpynMAOgxKH8Z3f+GmxHPKbH5pjwcVnvvp4rSQjzwwE7MLvFzDc4DysITePCRAxja6t2IEGPT8Nn5UXCN/bmHhN2+IcReIyEjA6/dPxjFHmc7FMqSUtxx7w94/J1SDDQGFxlAQXE77KuCqsQDXHMNrn9kC67Z2R7kw6GI2X0Aj7xUhvggBlTiaMMZL67Hws9qERNkcliutR6XPLUD8yt8UUYEDROTUShxLnuTyucEJ0UfR7Qh54vNePa5ExjUHJroFVVxEe57eA/GN/oSz0NgnJSGgyrnsrVEMdlfhVhA93y9kRAXW4Ii5cfjmPHLCdz60Pd4+87tuGZtLRIDmZB0A19XhVsWHUaehwRlrohRidib7ay+aiFPOJrXbQZthgvMAOghMrTXzbgM9jTXcsuIwfhsvNyrD5prqMStTx7AqC4GFA6iOBgda+7gWudeQYpU6fqrpR8jetIOKv+0lRKPcbvS8kLc81qF0xqtRwhB5TmDsN1li56RqiN/0krGefm2X4gxsTiY7sHFTClit+/Hogd2Y0apPSgDiP5W4fO06WGdzBrS09OIow1nvfgL5pd0HSqthtCcS+z/O5+zXhDjiBy6Q4y8toDKttioJ28nReSO/bhtbfvJGZe/iDYMfX8bbt3UtTHCgzp0cOwYA+s/50sw6gCnmbaXKF+vo5zH9WJiqsVVi08gxYdocDEmHr+6HBZ7FIrs03ZmRkXo1vyCh5bWd5kimIDScAi1WbB/MguWS26m5tR1iM75hajuKYV0v6OLzH18TRluX1yCBB+MODEsHgdzndeOGqGI9VshVlVZvhJj33cLVagrw21PH8Dsw2Z422jCgQpJsO+byVnu/zcxZqXpi7h0fRFJ1xeRmzA0dhWNuqMQfKOn7/KVhbhhRYt7rITbTZT4dYTzDggHZJJH2uE9CzTjD5gB0CNQMhcHnnYLSeXC8eOV6Wj08tSJ0IazFu/BlDpPbwHFhdS4ZLOlJMqsr5r/XWXdDzuqYAHyHY/rd+24wnDkqmG4KuJ1RHzpSc9HbD+Ia7dbvTa8GKbFd7PDXQwVgqNEebOXrwYPpYjduhv5/1eE9C7c4RyoOIRaf3heNE5boi/ifx9gFtljEiUQczr/SSHmSEBzx8ua37qgDC0hFBQJa/biqv02j9oth5r3NQk1ebH60uiOqvKrD1RWfh1eu+fXK6r3LJ1rODptjuzs6M+JYo97tXYM+ewoRvh9uC6Fet8h3Laq1eOor4Gt5S2u6fJkfbFCqi+b1KCvfOloedH+8yv3bbmo6vBd4wzHI/+FtFvbPSgk2YkCXLrVe78BF46CQXKnCtqpTH3LaRpvxFcW4vb3azwqfw6ieCE1vRFjr0mK0Zcm2vXl1xTp9V+tMxgqb9bvOnF51aFXZuiPjZolnRL9IQlf4el9Ddv/K67c5stzl+PI8AgnhSgQKb+hxddEXn+yg8588SD4Yn/sTw6imIuODfeK0/oB8AAAIABJREFUxnnF+mKFXF8+uqRS/583quqLOqcmXq9f2XiXYfcbs/XHY+/jkh53t20o0tac8KHvE1RnRrksG3L4m4BkP8T+y8MMgB4gLO7LhAuI2W2mbB2ShTW53gKqKGLX7ceVB9xnvRIqOCJ5/ax9hvobru4uuteQb35Rv/fSv3EJL7vPrDsw8ZPjyOjmZDcAAOFQemY6Sl0C0pZCNY/2cL9RHv0V//pPGZI8zDgJKJ0J01e3CLWJJkPl3Der67fkdwrqe69uZ22yvuSEp78bQqr8Ac5kwCWf17ntPSIQ6Sek5h9Wg2H0qBrT0a6+X1b2cstDVYfHvQK5W1wC11SJOds6/HrQxN6Mc98vRbwHTXIujMtLEivi/6+y6QvSZfJlQr/Qr3/7bAy71C1kn9owZk0FYn3wHlWla5wMRxEy7psErxPH/odoxvT3jyGnw/2jdJgLa2y1un2GmjvUde3dHoxUVrak5cmq/RddS7X5bo+XWjF+eSm0Xr0ABHUDItDuNGjw+IzDAO8/xJmW6mfKrxcvPvMQJEXejAANbA03EuPd+daSyA591cyV1fVrZ8Bt9dAjKyq3PH0njfrCtZy0VGPiYe+xAI6UCOhdXpB1YAmB/IEZAD1ANv/uVQNdny2RYc+CVDR4m/2ba3HR57VQubx5POxCvqR0QmRFx3pf5fi5cut9/4X8uGs5X1mGBXs8z1o7I+iSsX2g81WNUMZFRcItoVGo4IwG3PhiATI85LtVwdoeB8OkEn3NJWtqTAHu5Q8VFLE/F2KSW154EQeJ4dZHq0yv+lYPoR+TO+fr3WbdDgzZVuNH8B5F+LYCnFXhOmRT/A0NSw/p6y8esxfezD4AQKl++fKXiOKAa7m0QI/RDd5UAkFrcjhaiXNZquj/TLSvIz1eiAv2ur9Hw9ByIF9vyB1X3+62r747tho2PfUfInMzGCUl5Zjs1q7uCCmRqHIZX04QPs8fGX6npfqZ8uvEBbNKKX6wAbsElz+eWre8RxpmROsr4n6uqn/tgwa0BXKfzfb8e6pc+z61IfdQs9d8zjRKhSYXv9JdIpcRiBx/VZgBEHIoOYMYbnAdFGikFpvGeEscQxG96Tim1rsP4q1i9XXvVcDPU68IXeqYd7GbjqIdGLe+GuHelAuvxr5xzm5FCp48opC47WwICaINI5fsw/Ra98Eunphq9pHKVJXe8kuP3NtfhFZM+7HebVp7Dm1Ye25Vxzv+VNVYdZv+PajcTl6UF9QhyyeVDUA0Y9IP1VC7PLo02loapW+5wR95AEJXciMedJvG2ZuQV+A9IFWMUaHJZWTJkCDKPxn6ONSG0avKOh1lfZIwmE3P2Rqm5ge01ZTQZXTG39wcCkIrhh0wex2saZQKjS7BtIsoyfZfjpO0VL9UPtNQNDdbXzQ+0+UvxVA5bVFVy8ZA6/6d9vr5taupxGVrMUVsufedK1SqQLPLWSKfgUsNVqa/EswACDVanXI6cd5HCwDGcSk44uHQHScEE6b9UOd2Ot8cNPw8uNr2aSDi1Na+cOR9Kj/iWq44oMdQr9G2BIahcS4xCwQqcDMDkcUb0qLjuOYn94EugphbbpfUDJpbhaaeuG8g8HoDxpc5a1sONvFDaeuNgdS3Xsh+w7WMtLcizYMx5AmusRoTj7r6iQVsJS0XfBnAGfF1sqjNv7opMQEppSbviihS4eIBAH7gkeCvDH0Zrq0GU3dZXYwhilg03HBHECfT1elf372REBcbgCLtRAtkXroC5RRocjnI9zuR9PEZMaGbOPUO11Ku1oQ4r84mCSwug+V+kG5PF2c4wwyAEBONBSMGuZ72R2T4dWys11z/vL4KE1xCbzhqF6dT4w2BnvENELqKZj/n9mVLA4ac8L7OJmTEotDlJfsvJ5sSmCzdIHZg0rJiJLuoKg52sUAwTFoS4vX74KCIPlCDNBdZL0fL6jHlqA6kxgbHUz+6xT2J7UjyGAjqLo/6QLWbt2ACad49RW8PLO9B0WvWbeCNrveJrW73GqEtKmQwubhGruIQH5AcfRT5YYObAZ1AWmu+1NtWBlczcfxA5QWupTJDqw/xFzK0qp2LThD0eYVYKCZscy0jRis0Xn0oHGwu0a5VlJxenqYe5rSMzD2VaPkTZ8tdO640BgfzJF6D/yIP1CLdRamIRMotxMDyhbpgpDrs4XZWZBSbwY1yP0zE6TJ5BMp1BJM7ZXc7BFlWqDsOX1OOs3a5rqdSfCvWPnpnNY6F+HbBQe3IcVujpHhEtDy9IMAqzQ0vNxh1oMrO2+WoHRqjAwSy7vsOFTHgWLPbckQuTI8HbjgCu6mkBkRwGlAlRivCKLo3ZgkHu0sH+VQ4jZYAqICsfXVu+82Oo+2JSwPwtriyj0RsBXVOIEaaLYigQFV3XyQc7C6doBY9qxDvTJmsnSS0naEjtrMiIY5TUJosJVTJnTyS2cetn25hSiA2BxReey4HwSVQoJEnas/XMjzBDIAQM1Romu3a7YXUGBR6S/xDHcg90tSLDUKhLWsDT8O7T0fMqVClkwCFf04vGyCNCbksWyow0GXROY621d2h7XjxzoDm1D2I2IaMMudxXob2DlW13S1wznfedrTQLJpInHuPvOOkl6bbsVA0I6PY2R3NwyJUKRwbApcHqABfD2BQ5zJidcB7hlp3A2AbRcTgYITpS4jtyCpyft4crOJaWYdbRHsgGCWpe2B33jjAme1QeZ0R87DLnHuLEaFViJdrp6XM55qvyqS2K2OpOEgu1kqcHkSosj1QERIH4G3viOt7Yabk9Ntt0oMwAyCkUDISOW5BN6bMaNR43XjfhgGlQU8e/ELeaEY4BRq7nc3xaIxXQoT9j/UiO5GG9iUTzRi+u8Ut6ldEyz+Ij5HrvQlnakWqS6DmFGreU+Lj9ifPEGohA13GMwre4X0CTwQTtC6xAlkwF3UUwcNeCt9xQOIe2S2I3pO0EAKRd1ZE+2jP7RzpbYi1BRmVzg9hAEylQgmMXXzFL6zi8GJgt3OhQ/RhsCZwuLzLNhBZ0IN88r3K5+jm68aj/aF0GNK43396H0ztZAdhOs0PWAxAaJEMImKYcxGBPl3j9dAf0tGGZJ/We0MHae1AuA+3bI52TuwigudCmdmNmBuQ5xL7IEe7RR1lWxGqe4QSUmdCnIut9j9iCVpWh4cDgn05MpiYzIh1WY++i5h/ClYeChKyE/waOLfY1n4L19COBBez9FF0fBOq+i3yiY2eMmMG8sI54HU3Xdek5yseSxn96B66qukKGN/MgCOtrysMsU+aJX0XZi2FkoSlMq3rC0dkqNF5P/KXa2h3Uyo9TocAuVcFQ9CuObl98c8fxp38IIj15c7wlS1IdRlQb0Tbt2v66BGyfL3ZLZ3y98QevMKlgZlVpNXqlokuTHSsC1YeQomPp9F4p4WcPjnaudo2t4C8EUIIz5gguUF5bjoTqKG+IPWsWfc4Pv0qi4oRoZKF0fdgBkAoobepw5HkXMYp0BDj/R0U0gbj/lV9c5VUkPEQgD9GcApCLg2hpS2rMbkp1PG04701obpBSKFQtlldtJkIXkTxKRIIxGR1Sxz1piB4iPw8hYTMlDj1yFusUDo9b4pNHY4g4j9cIN7Nct/r8ve5U3J38th37xaKb/JdORBYosNRma5BlS4cNUkqNMSq0BwlR2u4DO1hEliUEtikPAQeEAkBPW16Q/+GGQAhRCZ7N4GnjzsXEiUaok+NPCFDyp1cW/xjWCKoD5kBQBHR5JryVgDsjr6R8McDYW2uuxVEDDYEt94eDMQhQuqikNZa0TzyVAl0WkOhdmt/AYtb0P7yKZIodFD+OW3eocupbbC3l7sjLhqHxyTi4Ih4HM2LRmWUBCJT6v0OZgCEECl2a93WyKQymJT9/M0gxGUJg6IthB4AlcnuEkgs0CfqYc4P1Q1CjMwiuMgr0lBs/woYh+i20Du3pe8FT54uKMyu7U8pggoA7QtQyZu6XMNcCHFdvtiyMBydPgDr5qRhT24YLH09IIDhFWYAhBCpWJNEXN4eqpS6nFh1OsDhY4AElGTcAxKH65mDAiUBpVLtHTzJe4pE6QKC2wAh/1SLcZoicTjber/11z7WB/yBSv6rza2ZB8Hz9l5ZGA6enYdPL0pDcRTpzz+U4QIzAEKIRDTGuk7FqIyH/bQzACjyQ1iX64hCIPbhMYaCd9H3fU9eiuh+rZD6Mu5bM/ueAegPy/inkgafWEA8KX8C45BsvHP3EOxM9naKKaM/wgyAEMJJbCq3t4R335vb76EUUT2oYH5zqfYbCO1j8lKgbwZQnp70ufb3GUpu0I1eeS3s7kcGExkKLp6A/1ybiMbANxIy+jjMAAglInHfrBvKDfN9BAKRnujJGWZ/G09PtwZm+Ec/bf/xSRdc/TBaz3H7gFNg3y1n4D9nRwS8fElEEarmdsTXmhHTZIWmzQpVuwCFzQGpg4ITvOQ1UMVg/cU6VDPjo0dhBkAIEYmi1a3Ql8xpAFRHi3DJVu8nrZ0KuNpGp21PPAQhjrmY/6S/GSyM0NIP21+TvCz6H+Ijb7ul9CRSFFwzxW/lTxxWJB+qxuj9dRh8tBEZZSZEWQJ/LjQ6E4cuZAZAT8MMgBAiiBH1rhqc2ASXLVqeIdU1mPe1oV9kS1FCMC8D28rLYPRXzhBfXjiRUKVruXHiSCy+JNpn5c+3NmPKymM4Z60eA4z9zxD6q8MMgBBip6kGil+ctwi126DunEWnC2xKCRzeL+sTZMNR1MA8AAxG/ySBhl2E7NvdVivDk/DJLWlo8MUNSQUk7DiEe14tQnYrGwr6K33R49xvsfOT9W571wQbNCbvLwiNUsLYD1ojCdaqcyA83b+3PTEYf11SJZfMn+I2+ycoP2coNsf6MPWnAtK/2Ybnni30Q/nT3/7w2xZfKgIQACrQPrzl93SHeQBCiJV/rt4mAE5vFrUgtgFAbPffFWNVaCJAYqcycvIIEOFUvB0cgUgodRBQKw+0yoHqSOo4MhfmVT8ZGkN28AmDwehdJtPimySuel6RgLXzInzYsUQRvXU3Fr5Xg4guBiYCUA0c1UkQVo2DbdmbttpdefUwdVVjbMpZj+0Ri59yugtLF9wrMAMglHA3WZqEZ6iuc1ywaEFinQCSK+l2ykwjNdBrgMHNf5apSWtbdFVdNDmVWeYYDMZpBOUnkpwJrqUdw1OxM8q7xuWaK3H9mxVuZ3cAAE9F4QLS/lqB0P6sqcZUDwCbAXhLGMbDpHMrlPF98ySw04x+4HTuR5Rdby8BcUnBKkBX1ub1QVM+AsUDnK9qo2pNqQ5DQiskg8H4y5L8pWwYRI1zIYfiUfEweR2kKFJXF2BCi/tHqbS9tMVRotujr733d+XvKzpqynK7lUKCDuYB6HGYARBSiHAQklrnMorEYiNUXs8DlqNgSITLVJ/DDTT8idDKyGAw/rLw4yLjXTMXcGEozFJ6X4gXmzBtk9Ht3Il0tJW8a6jOGVqHWo/f88I4WHNdy2i0Cs3MAOhxmAEQYvYhbKdrmaSoHgO9HhVCYBidiGqXFlmK6PNu1iI1dBIyGIy/KmFkZYLctZALgyHJ09XO8NX1yKt1XsjkYRNMttrJtyDAw6fSpyvOpI5E50ICoy4MZqadehz2iEPMr2Lqt66WNDE2IK/Me7SsmJGC7akuB80SKbeCi1xxGiYUZDAYvQ2nj3Ub9OUKGH04sZQztCLRZXB7lDZ8qK5HTaDipNp1s0a46SEeJTmRLPCpF2AGQIgx2a9bX+u6RU40YfROd9eZK5SPwKY5MW6m9B4aM3qRVsWWAhgMRlBQanZL/iMqJLD6oAlkJhvkLvOYCYL5lSCkIZfQ/S+75T6RxeDAECnbZ9wLMAMgxJgbzq3dSvlG51KK5B1VSPFq0hLUzhmETW4nchNcQ5KeWJQUvjCEorpAye3a8VO+1mavPKEbuO73v9rktP9dl4LMnrsvg8HoNRzEbcwnogjiS7ZSt0MvKb4Q0RGoKIO08267ndjcxhbrkGTsjmEOz96AGQAhhwg/kpgvXUu5inLMOCZ49eNTVSKWX5kIk9uFBFdzCc8k6hI2LktGdKikvTJpZtr72mH/3pWc0/Igadwygojny4DZv/9ZKX/552Lyq8wIYDD6PwQqi1uZTXCb2XvCppLBOZSJYIBE8UAgcpyjO+Peb0jRf928okSB7eekoIlppl6BPeYeYBcmvN1CXZcB2jHt+2qovYbaEtTPHoWPx8g9usB+QfgZE8TM+mxd4ocrUyL8V8p5+bLnUsbPWZ2c9+VBXXbrM1xF2ZnE/EA8dd0a9JvY4LgTkM1jRgCD0f+x8Vl1bkOQzYYwq/fvCklq1Lpo7HxO+/dWrew8X+9/g3bOpM91ucWvQv8ft4OIAJhHDMbyMTLm/u8lWCKgHqBFrzm8QictvxH29M7lmq0FmH25DitTSfdJgbgwbLhvPAb8ayvmVbhbDCIh3Dqor4eovt6gi7FEQjwUDfpTNKUbk2zkyKOOKEt9mCxeB2u2nDjGaqhjrJLQXDmlSbKWT+TwM6CQgiMnIJ03giIbQLE/32UwGH0Hu3BBnQP/do5HEs2Ib6RAePfDgqiLRUEUQXrDn6MXBUeaSOrXyiTz3qs464NF+sbNG9HZUbCMf0z7+rDhXMv1KbTjugSURHRVP9Uk4pPbM1HDTgDsNZgB0CPki6tw9hPXouAjpwfsaMI5n1Ti54dS0eLF9yJGJGJJ/njIH9+FmVVdBw/YwCnrwI0vAMaD4BHIAcg7Zd2kLv8DhIIjPxKkjQmuGgaDcSqxLTG2SICEzmWCGclVAki6l2yl0jhsnq7GnK/cE5sd41SjAdV66CJBKEQQUEoJB7LwpFXhbfxRROC7B8fjR133kyNGaGFLAD3E/sjSz78D3+Rartl+GJcfsPn04B2JKXjr+Sn4YpSi97bEEAkaB6dh+Z0jsDHJ6VxDbBOh7S0xGAxGD1D7nbUExMXh70DGce+7lEA4FF4wBDu6TRlMQAnhKAgP4punUYxKwBdPTsdHo+TsVKBehhkAPcWRI7alXPadbtkxxHac+d/DGObDCYEAIEQl4Msn5+CJ29JR7MVFFzgElvhYbL10NJ56+2zc9sJ4fHpWPBpcFumOc4jrIQEYDEavsNGxB9JK5zKK+H010PkwyxCjkvHOQ7koUoVCFh41E4Zi0avT8OVQpvxPBWwJoAfZUzn6iw+0xY/fQmxOqS656hLc/kYcHv5nKhp9MMEor8Cxc8bh4enZGLO2EPPXVmFwtT04600iR2VeIvaPScT+sYk4liKH3Yt9UQbEBHNLBoNx6tmDqFVA7b2dy/jyKkwsG4yKTG8ueALT0CHI/7caNyw+iBnFvnkzneHQPDgNqy4fhLWj1bB6GneoAHU70K7m2ZJAD8IMgB4lX3yPe/Tc+eLnBSmk83tCEbNpL+5NU2PRpdE+H3ohhEdi5yVjseviUYgsqcPwQ43IPt6ENH074uot0LQ7IP3diucIHFIJzGoZ2jRyGGPDUJMUhhpdBMoyo1CcGQ6j3L/1tnoKjzsFGAxG/+Eoxn7citX3Or3MYivOXFWDb/+RBLPX8YjAkjEAb76sxQ9bijB/TTnGHTVB1Z0HgePRmB6Hw6OT8MsZKdg/QNH10cNUQMrqbXh0XyoeeCwdrcxP3WMwA6CHqa9aVPiobvYDH6D0Jac1NmrHoKVbcY9mOv4zVwObH959Sng0ZyZhY2YSNjp9AHCiCAICkQv9edpW4n2ZkMFg9G3q9YsPr0leW3OZUw5+iuiNRzDvvASsGMD5NDGgvBzF0/Pw2vQ88O1mpJYYoa2xQNNuhwQcHDIJzBEKNMSroU9Rw6jwZcJBEblrPx58pwYxmjAMENJxkBkAPQZ7tL3AJv26xf8H9U9uH4gdGPvfjXjw6yYoQ7EARgCR5yDwoVf+ABABmENfK4PB6F2IsELUPe2mjG1NuOCNQqTa/K9RCFOhdGgSts3OwJrzc7Dq/CysmT8AmyYn4UhWOFp8Uf6UImLfISx8oQRaASCtjcg2sAWAnoQZAL0Coe/q98/9jErL3T4SOzDyvY1Y9N8KaAN48XqTZMDDSeAMBqO/sZNb8uE3lK93LVceOYwH3q5GRG9H5FGK6D0H8MSi48j8fZohtCH7eCAxBgxfYc+21yDCw4alWWvB17l9RB1IX/ML/n3fLswusoHro0bvAKDR+1UMBqPPU5VieYeMvMLktkNfRNKaHcj/rx5xXo8wDxGiDQO/3Y5nni5EmtPJAgIyjzWD92M85ILOePLXghkAniC0hzrRGPut+v8lfweJ3tOnypIy3HrvWix6owQ5zeIpO/9X1tSEySuOY0y982MYSwM/9tMfOEL71UmgfU9eihf72ECYTtDaW/fq7fboe+3/JyrQLpftjuo/X/8vxL3i3lEcSF2zHc8/cghjar2fXxIMfGsT5r64Hk+9rUe8B4NDXdgErR/eiCjQttBJ1z19ud195bQJAhQ5acjWp6Wgbtv3Q8cY+x36ZWn1uqe3Xg/LBLePhQ7kfLcHz/50FIfm5GDVgjQcTJFB6GFrgNg6kLG7HDPWlWHqPiPUbl2bYjZQ1WUFNN4uICIkUYIyKvb8YkgflNdIYEMI3kkCSl8JgQEgQOrmIg6U4dTL8lFvt0cfbP8/oJk2EVEhkS2S0tbmbj7/Tr/jXl3y8MELafscFyEQcbgAD91hwPbLhuOzsxNRowxdlj4i2JC+8Qj+9kERcpu7rpWvasDANorySN8GwCTQ5qLuLujL7X4KOH0MAKKoo/AzyX0XKCAEfMSlb1wq5OvppErd5KcfRN1CuSexrWYMW7Ufw1YfRsMgLbZP0WHXuAQUJsm63j7jB4QK0FQ0YOj+WgzfX4vhv7YgxtL1i8hRh3gtUNJ1jQNtFtTD0wEf/qKC2AvBhn1P3iZwJkAMOsUKgUjfBug7QdYjUIUhWFl+ZxzglhXTmd5uj77X/n8gptpsaIMyBFUlgjZ2ZwAAwDtVB89S6Iavuw/ts10/I+ZWTP5wCyasiMIv8wbipzN1OKINcEJCAVlLC4ZvKcOZ35didIUPuUzsLRh8zIpfhnvIByA43E4xHADUb+m2wj7c7qeA0+bQ5VjtBZdvJ4c/69ywVBOHnxbEo7nbXkYRs68IM4/Z/ngY6bShFIaWjJ6T9k+mai8cdS85+v0oOBK8X01giYtAUU40ijMjUKVTozZBhZYIOVrVEljlPBwcASgF5xAgswlQmm1QG62IaOlAdH07EqrboKtoRXpRM3TNvrr3KM5H68frLfV3xTR14crNu129vmWdMTMEy0qDacNRs6ElL9h6uqUPyvtPXd6+O2EdGWw9Ulht2/SVqksRXAbp+KRzr97BHV0aitnS3bTovG8N+LbLC3q7Pfpg+//BwLs0OyxrjEkhqOpurvzjbyvt1/ly7Y3JY15dSFvu6n5WyKE1JQaHR8ShMCMSFSlq1McqYAyXwirnIADgBAFyiwPq1g5ENZgRb2hDSmkzso7VI7vMAkUPBhguJxVP3l9ly+/ygr7c7qeA08gDEF7eSCEkddqrTlrrMfsz/72YOoh6j4v0PcAWw4p9W7Ah+X7dv+66FM3PJoAqur6aQlnfgqH1LRi6tXfkk0HoOFM0v73VWv94l8ofADDddhTrGzMhBJ0uOIcIxfuDrcQrfU/eeqg3UVhHBmuVR1CH8WgIlgBERB0rB8SMoAdLEbdwqO1a+wO93x59r/3/ZJ71MNZ2JHU7FvjGFNDC7p/7n3xQtefuquQ5axai9Kt0SrvwRInQVNZjcmU9JgcrnAsSiI4OKI8RWIcGZnRSnEVpyf3dXtOX2733OW2CAO1UUXgQ6g/aafAD30SIvdyuMxwv6XctHk+vivkU4fdXeThEqDchoDQT1p330ZbzkvSl4ceqa+/pXvkDOHKp7a7Ip5NXwX1rkX9QXAxxU3B1+EAflPcj/c578xHzcrAdeCrse58IgQHQUP3R3kvFyeNKEFyadgls9ldEdL+c0Nvt0Qfb/w+K5ltvVr4SuQ4kqKVIAoFeT7HHn++sq1q35uKOCxI/E8PfMgfpQfIdigm0fe0D9trkgfpfh79Lw78NZAOCHPb2a0Uc6faiELb7hb0xTvUwp80SAACEa2+OfQG7np0C09/UPp5E5cogdOx6Fo333Ku37Ai1fL5DyT3aiZPOIG13ZVDb2RpCw3q6oRRUNCURx8bRpGNJubXuh/p6mLx/ywN5y2SvtzxWeTaEeP+/TLEUNf98TN/+UkD3DoQ+KO91uvGLn0DjPYFY5xegZc0+fcMCEsJdALFJ141exm3bmQH/Y6ck6LDfSKsy1xlQ6f1q9H579MH2/4OB38vfsfyjZU4AngACgSpJ5fz4KsfaQG8/MvGSuFv4wntHwXxbPMTIQOvpCilE60ja8dVbtPWZC6tNx/78hJKF2pHLbyDtZ0solRIvgx+hoDLY2xZQw8x91fa9Pt28L7d7L3JaGQDASSPgfWy9PxOOKUoipslANRyg4EB4gHL0t99MABEEAkdh4wk1ySitjiXivhdp3fsPnFLl784N2mkpM0nz/HQ4zoumwkg5aKyUUJ4G0H4EEHmIFjlgiKLC7mzYv3vYYVz/97r22p6QncFg9H/u107PnYTGK1OI7ZxIiFlyUCX1w4NMQEUp0B4GoSgZwtr51PLV54aGAyRI7xKDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgUoWV0AAAAEElEQVQMBoPBYDAYjB7g/wEAz0L0VfNShgAAAABJRU5ErkJggg=="
|
|
134
|
-
page.run_task(bping)
|
|
135
|
-
page.title = "TermiAI - Your personal helper to run commands for you"
|
|
136
|
-
ftapi.setSize(page, (960, 540), False)
|
|
137
|
-
dlg = ft.AlertDialog(
|
|
138
|
-
title=ft.Text("ARE YOU SURE TO DELETE CHAT?"),
|
|
139
|
-
content=ft.Text(f"Are YOU really sure to delete this chat?\nIf you don't know which chat you deleting, not recommended to proceed\n(TermiAI might restart)")
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
def darkMode(event):
|
|
143
|
-
page.theme_mode = ft.ThemeMode.DARK if darkTog.value else ft.ThemeMode.LIGHT
|
|
144
|
-
tools.APPCONFIG["dark"] = bool(darkTog.value)
|
|
145
|
-
|
|
146
|
-
if tools.APPCONFIG.get("dark", False):
|
|
147
|
-
page.theme_mode = ft.ThemeMode.DARK
|
|
148
|
-
else:
|
|
149
|
-
page.theme_mode = ft.ThemeMode.LIGHT
|
|
150
|
-
darkTog = ft.Switch(label="Dark mode", on_change=darkMode, value=tools.APPCONFIG.get("dark", False))
|
|
151
|
-
|
|
152
|
-
def server_select(e):
|
|
153
|
-
tools.APPCONFIG["api"] = e.control.value
|
|
154
|
-
tools.URL = tools.BASEAIURL % e.control.value
|
|
155
|
-
page.run_task(tools.loop)
|
|
156
|
-
choose_server = ft.Dropdown(
|
|
157
|
-
label="Select server",
|
|
158
|
-
width=500,
|
|
159
|
-
on_select=server_select,
|
|
160
|
-
options=[
|
|
161
|
-
ft.dropdown.Option(key="x", text="Server in Oregon, Account index 0"),
|
|
162
|
-
ft.dropdown.Option(key="2", text="Server in Oregon, Account index 1"),
|
|
163
|
-
ft.dropdown.Option(key="3", text="Server in Singapore, Account index 0"),
|
|
164
|
-
ft.dropdown.Option(key="4", text="Server in Frankfurt, Account index 1"),
|
|
165
|
-
],
|
|
166
|
-
value=tools.APPCONFIG.get("api", "x")
|
|
167
|
-
)
|
|
168
|
-
def search_select(e):
|
|
169
|
-
v = int(e.control.value)
|
|
170
|
-
if v == 1:
|
|
171
|
-
tools.APPCONFIG["searching"] = False
|
|
172
|
-
elif v == 2:
|
|
173
|
-
tools.APPCONFIG["searching"] = True
|
|
174
|
-
tools.APPCONFIG["instantSearch"] = True
|
|
175
|
-
else:
|
|
176
|
-
tools.APPCONFIG["searching"] = True
|
|
177
|
-
tools.APPCONFIG["instantSearch"] = False
|
|
178
|
-
page.controls.clear()
|
|
179
|
-
main(page)
|
|
180
|
-
choose_search = ft.Dropdown(
|
|
181
|
-
label="AI searchability",
|
|
182
|
-
width=500,
|
|
183
|
-
on_select=search_select,
|
|
184
|
-
options=[
|
|
185
|
-
ft.dropdown.Option(key="1", text="Off (not recommended)"),
|
|
186
|
-
ft.dropdown.Option(key="2", text="Instant, quick search, for simple things (recommended)"),
|
|
187
|
-
ft.dropdown.Option(key="3", text="Deep search, for most questions"),
|
|
188
|
-
],
|
|
189
|
-
value="1" if not tools.APPCONFIG.get("searching", True) else ("2" if tools.APPCONFIG.get("instantSearch", True) else "3")
|
|
190
|
-
)
|
|
191
|
-
sideItems = [{
|
|
192
|
-
"items": [
|
|
193
|
-
ft.Text("Welcome to TermiAI!", size=30),
|
|
194
|
-
ft.Button("Create new chat", on_click=lambda e: chatElem(page, dlg))
|
|
195
|
-
],
|
|
196
|
-
"label": "New Chat",
|
|
197
|
-
"icon": ft.Icons.ADD_CIRCLE
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
"items": [
|
|
201
|
-
ft.Text("Settings", size=30),
|
|
202
|
-
darkTog,
|
|
203
|
-
choose_server,
|
|
204
|
-
choose_search,
|
|
205
|
-
|
|
206
|
-
],
|
|
207
|
-
"label": "Settings",
|
|
208
|
-
"icon": ft.Icons.SETTINGS
|
|
209
|
-
},
|
|
210
|
-
]
|
|
211
|
-
for conf in sorted(tools.CHATJSON.keys(), key=lambda k: tools.CHATJSON[k][1], reverse=True):
|
|
212
|
-
x = loadChatFromJson(page, conf, dlg)
|
|
213
|
-
if x:
|
|
214
|
-
sideItems.append(x)
|
|
215
|
-
sideBar = ftapi.createSideBar(page, sideItems)
|
|
216
|
-
page.add(sideBar)
|
|
217
|
-
|
|
218
|
-
def ftmain():
|
|
219
|
-
try:
|
|
220
|
-
ft.run(main)
|
|
221
|
-
return 0
|
|
222
|
-
except (ImportError, ModuleNotFoundError):
|
|
223
|
-
print("Missing dependences or something is not working")
|
|
224
|
-
return 100
|
|
225
|
-
except:
|
|
226
|
-
import traceback; traceback.print_exc()
|
|
227
|
-
return 1
|
|
228
|
-
|
|
229
|
-
if __name__ == '__main__':
|
|
230
|
-
ft.run(main)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termiai/__main__.py
RENAMED
|
File without changes
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termiai/clean.py
RENAMED
|
File without changes
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/__init__.py
RENAMED
|
File without changes
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/__main__.py
RENAMED
|
File without changes
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/ftapi.py
RENAMED
|
File without changes
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/icon_data.py
RENAMED
|
File without changes
|
{terminal_assistant_interface-1.0.6 → terminal_assistant_interface-1.0.8}/src/termilib/test.py
RENAMED
|
File without changes
|