badnet 0.1.0__py3-none-any.whl
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.
- badnet-0.1.0.dist-info/METADATA +4 -0
- badnet-0.1.0.dist-info/RECORD +6 -0
- badnet-0.1.0.dist-info/WHEEL +5 -0
- badnet-0.1.0.dist-info/entry_points.txt +2 -0
- badnet-0.1.0.dist-info/top_level.txt +1 -0
- badnet.py +210 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
badnet.py,sha256=5NKG8wDY6CsyZtrJ8YtlfPN3IaXxvjs5m9KV7mrBdz8,7832
|
|
2
|
+
badnet-0.1.0.dist-info/METADATA,sha256=wKdjrqFRpagA7omPm72ExaQoAlQeJm7KEDQ-6YoRthg,72
|
|
3
|
+
badnet-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
4
|
+
badnet-0.1.0.dist-info/entry_points.txt,sha256=2ZqpIHF9esjp0QN63FU2d3edU6gV8cU1u_iWHZmrjak,39
|
|
5
|
+
badnet-0.1.0.dist-info/top_level.txt,sha256=ymTDn4a8M8Owgc-s6xxWVLg0VIyjk4d6C5mWjxxl8RU,7
|
|
6
|
+
badnet-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
badnet
|
badnet.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""badnet - консольный ассистент DeepSeek с доступом к терминалу."""
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from openai import OpenAI
|
|
9
|
+
|
|
10
|
+
CONFIG_DIR = Path.home() / ".badnet"
|
|
11
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
12
|
+
BASE_URL = "https://api.deepseek.com"
|
|
13
|
+
DEFAULT_MODEL = "deepseek-chat"
|
|
14
|
+
MODELS = ["deepseek-chat", "deepseek-reasoner"]
|
|
15
|
+
MAX_OUTPUT = 8000
|
|
16
|
+
|
|
17
|
+
SYSTEM_PROMPT = (
|
|
18
|
+
"Ты badnet, консольный ассистент с доступом к терминалу пользователя. "
|
|
19
|
+
"Когда нужно что-то проверить или сделать, вызывай инструмент run_shell. "
|
|
20
|
+
"Отвечай по-русски, кратко и по делу."
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
TOOLS = [{
|
|
24
|
+
"type": "function",
|
|
25
|
+
"function": {
|
|
26
|
+
"name": "run_shell",
|
|
27
|
+
"description": "Выполнить shell-команду в терминале и вернуть вывод",
|
|
28
|
+
"parameters": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"properties": {
|
|
31
|
+
"command": {"type": "string", "description": "Команда для выполнения"}
|
|
32
|
+
},
|
|
33
|
+
"required": ["command"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}]
|
|
37
|
+
|
|
38
|
+
# Буквы из блоков: каждая строка одной буквы имеет одинаковую ширину (5 символов)
|
|
39
|
+
LETTERS = {
|
|
40
|
+
"B": ["████ ", "█ █", "████ ", "█ █", "████ "],
|
|
41
|
+
"A": [" ███ ", "█ █", "█████", "█ █", "█ █"],
|
|
42
|
+
"D": ["████ ", "█ █", "█ █", "█ █", "████ "],
|
|
43
|
+
"N": ["█ █", "██ █", "█ █ █", "█ ██", "█ █"],
|
|
44
|
+
"E": ["█████", "█ ", "████ ", "█ ", "█████"],
|
|
45
|
+
"T": ["█████", " █ ", " █ ", " █ ", " █ "],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def print_banner():
|
|
50
|
+
word = "BADNET"
|
|
51
|
+
for row in range(5):
|
|
52
|
+
print(" " + " ".join(LETTERS[ch][row] for ch in word))
|
|
53
|
+
print(" консольный ассистент DeepSeek | /help - список команд\n")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def load_config():
|
|
57
|
+
if CONFIG_FILE.exists():
|
|
58
|
+
return json.loads(CONFIG_FILE.read_text(encoding="utf-8"))
|
|
59
|
+
return {}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def save_config(cfg):
|
|
63
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
CONFIG_FILE.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run_shell(command):
|
|
68
|
+
try:
|
|
69
|
+
p = subprocess.run(
|
|
70
|
+
command, shell=True, capture_output=True, text=True,
|
|
71
|
+
timeout=120, cwd=os.getcwd(),
|
|
72
|
+
)
|
|
73
|
+
out = (p.stdout + p.stderr).strip() or "(нет вывода)"
|
|
74
|
+
except subprocess.TimeoutExpired:
|
|
75
|
+
return "Ошибка: команда превысила таймаут 120 секунд"
|
|
76
|
+
if len(out) > MAX_OUTPUT:
|
|
77
|
+
out = out[:MAX_OUTPUT] + "\n...[вывод обрезан]"
|
|
78
|
+
print(out)
|
|
79
|
+
return f"exit={p.returncode}\n{out}"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def ask_model(client, cfg, messages, state):
|
|
83
|
+
while True:
|
|
84
|
+
resp = client.chat.completions.create(
|
|
85
|
+
model=cfg["model"], messages=messages, tools=TOOLS
|
|
86
|
+
)
|
|
87
|
+
msg = resp.choices[0].message
|
|
88
|
+
messages.append(msg.model_dump(exclude_none=True))
|
|
89
|
+
|
|
90
|
+
if not msg.tool_calls:
|
|
91
|
+
print(msg.content or "")
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
if msg.content:
|
|
95
|
+
print(msg.content)
|
|
96
|
+
|
|
97
|
+
for call in msg.tool_calls:
|
|
98
|
+
args = json.loads(call.function.arguments or "{}")
|
|
99
|
+
command = args.get("command", "")
|
|
100
|
+
print(f"\n$ {command}")
|
|
101
|
+
|
|
102
|
+
if state["auto"]:
|
|
103
|
+
result = run_shell(command)
|
|
104
|
+
else:
|
|
105
|
+
answer = input("Выполнить? [y - да / a - да и всегда / n - нет]: ").strip().lower()
|
|
106
|
+
if answer in ("a", "а"):
|
|
107
|
+
state["auto"] = True
|
|
108
|
+
print("Автовыполнение включено (/auto - выключить)")
|
|
109
|
+
answer = "y"
|
|
110
|
+
if answer in ("y", "д", "да", "yes"):
|
|
111
|
+
result = run_shell(command)
|
|
112
|
+
else:
|
|
113
|
+
result = "Пользователь отклонил выполнение команды."
|
|
114
|
+
print(result)
|
|
115
|
+
|
|
116
|
+
messages.append({"role": "tool", "tool_call_id": call.id, "content": result})
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
HELP = """Команды:
|
|
120
|
+
/put КЛЮЧ сохранить API-ключ DeepSeek
|
|
121
|
+
/mod [МОДЕЛЬ] выбрать модель (без аргумента - показать список)
|
|
122
|
+
/clear очистить контекст диалога
|
|
123
|
+
/run КОМАНДА выполнить команду в терминале без участия модели
|
|
124
|
+
/cd ПУТЬ сменить рабочую папку
|
|
125
|
+
/cwd показать текущую папку
|
|
126
|
+
/auto вкл/выкл автовыполнение команд модели
|
|
127
|
+
/help эта справка
|
|
128
|
+
/exit выход"""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main():
|
|
132
|
+
cfg = load_config()
|
|
133
|
+
cfg.setdefault("model", DEFAULT_MODEL)
|
|
134
|
+
client = OpenAI(api_key=cfg["api_key"], base_url=BASE_URL) if cfg.get("api_key") else None
|
|
135
|
+
|
|
136
|
+
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
|
137
|
+
state = {"auto": False}
|
|
138
|
+
|
|
139
|
+
print_banner()
|
|
140
|
+
|
|
141
|
+
while True:
|
|
142
|
+
try:
|
|
143
|
+
user = input(f"[{cfg['model']}] badnet> ").strip()
|
|
144
|
+
except (EOFError, KeyboardInterrupt):
|
|
145
|
+
print()
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
if not user:
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
if user.startswith("/"):
|
|
152
|
+
cmd, _, arg = user.partition(" ")
|
|
153
|
+
arg = arg.strip()
|
|
154
|
+
|
|
155
|
+
if cmd == "/exit":
|
|
156
|
+
break
|
|
157
|
+
elif cmd == "/help":
|
|
158
|
+
print(HELP)
|
|
159
|
+
elif cmd == "/put":
|
|
160
|
+
if not arg:
|
|
161
|
+
print("Использование: /put КЛЮЧ")
|
|
162
|
+
continue
|
|
163
|
+
cfg["api_key"] = arg
|
|
164
|
+
save_config(cfg)
|
|
165
|
+
client = OpenAI(api_key=arg, base_url=BASE_URL)
|
|
166
|
+
print("Ключ сохранён в", CONFIG_FILE)
|
|
167
|
+
elif cmd == "/mod":
|
|
168
|
+
if not arg:
|
|
169
|
+
print("Текущая модель:", cfg["model"])
|
|
170
|
+
print("Доступно:", ", ".join(MODELS))
|
|
171
|
+
else:
|
|
172
|
+
cfg["model"] = arg
|
|
173
|
+
save_config(cfg)
|
|
174
|
+
print("Модель:", arg)
|
|
175
|
+
elif cmd == "/clear":
|
|
176
|
+
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
|
177
|
+
print("Контекст очищен")
|
|
178
|
+
elif cmd == "/run":
|
|
179
|
+
if not arg:
|
|
180
|
+
print("Использование: /run КОМАНДА")
|
|
181
|
+
else:
|
|
182
|
+
run_shell(arg)
|
|
183
|
+
elif cmd == "/cd":
|
|
184
|
+
try:
|
|
185
|
+
os.chdir(os.path.expanduser(arg or "~"))
|
|
186
|
+
print("Папка:", os.getcwd())
|
|
187
|
+
except OSError as e:
|
|
188
|
+
print("Ошибка:", e)
|
|
189
|
+
elif cmd == "/cwd":
|
|
190
|
+
print(os.getcwd())
|
|
191
|
+
elif cmd == "/auto":
|
|
192
|
+
state["auto"] = not state["auto"]
|
|
193
|
+
print("Автовыполнение:", "вкл" if state["auto"] else "выкл")
|
|
194
|
+
else:
|
|
195
|
+
print(f"Неизвестная команда: {cmd}. Список - /help")
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
if client is None:
|
|
199
|
+
print("Сначала задай ключ: /put КЛЮЧ")
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
messages.append({"role": "user", "content": user})
|
|
203
|
+
try:
|
|
204
|
+
ask_model(client, cfg, messages, state)
|
|
205
|
+
except Exception as e:
|
|
206
|
+
print(f"Ошибка API: {e}")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if __name__ == "__main__":
|
|
210
|
+
main()
|