librecoding 1.0.0__tar.gz → 1.1.0__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.
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
"""librecoding 1.
|
|
2
|
-
|
|
3
|
-
Промпт -> ответ в консоль и в 1.txt, 2.txt, 3.txt рядом с запущенным скриптом.
|
|
4
|
-
Никаких папок files/, files2/ и никаких других создаваемых файлов.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
1
|
+
"""librecoding 1.1.0 - тихий режим: ничего не печатает, ответ пишется в N.txt."""
|
|
8
2
|
|
|
9
3
|
import json
|
|
10
4
|
import os
|
|
@@ -14,25 +8,28 @@ import urllib.request
|
|
|
14
8
|
from pathlib import Path
|
|
15
9
|
from types import ModuleType
|
|
16
10
|
|
|
17
|
-
__version__ = "1.
|
|
11
|
+
__version__ = "1.1.0"
|
|
18
12
|
|
|
19
13
|
API_URL = "https://openrouter.ai/api/v1/chat/completions"
|
|
20
|
-
|
|
14
|
+
MODEL = "deepseek/deepseek-v4.1-flash"
|
|
21
15
|
KEY_FILE = Path.home() / ".config" / "librecoding" / "api_key"
|
|
22
|
-
PROMPT = "
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
PROMPT = ""
|
|
17
|
+
EXIT = {"exit", "quit", "q", "выход", "стоп"}
|
|
18
|
+
SYSTEM = "Ты полезный ассистент. Отвечай по-русски, кратко и по делу."
|
|
19
|
+
|
|
20
|
+
_state = {"key": None, "model": MODEL, "verbose": False}
|
|
21
|
+
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
def set_verbose(value=True):
|
|
24
|
+
"""True -> снова печатать 'думаю...', ответ и 'сохранено:'."""
|
|
25
|
+
_state["verbose"] = bool(value)
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
def set_api_key(value):
|
|
30
|
-
"""Задать ключ OpenRouter на время работы процесса."""
|
|
31
29
|
_state["key"] = str(value).strip()
|
|
32
30
|
|
|
33
31
|
|
|
34
32
|
def set_model(value):
|
|
35
|
-
"""Сменить модель, например deepseek/deepseek-chat."""
|
|
36
33
|
_state["model"] = str(value)
|
|
37
34
|
|
|
38
35
|
|
|
@@ -40,8 +37,7 @@ def get_model():
|
|
|
40
37
|
return _state["model"]
|
|
41
38
|
|
|
42
39
|
|
|
43
|
-
def
|
|
44
|
-
"""Ключ: set_api_key() -> OPENROUTER_API_KEY -> ~/.config/librecoding/api_key."""
|
|
40
|
+
def _key():
|
|
45
41
|
if _state["key"]:
|
|
46
42
|
return _state["key"]
|
|
47
43
|
env = os.environ.get("OPENROUTER_API_KEY", "").strip()
|
|
@@ -52,11 +48,10 @@ def _api_key():
|
|
|
52
48
|
line = line.strip()
|
|
53
49
|
if line and not line.startswith("#"):
|
|
54
50
|
return line
|
|
55
|
-
raise RuntimeError("нет ключа OpenRouter
|
|
51
|
+
raise RuntimeError("нет ключа OpenRouter: положи в " + str(KEY_FILE))
|
|
56
52
|
|
|
57
53
|
|
|
58
54
|
def caller_dir():
|
|
59
|
-
"""Папка запущенного скрипта, а не библиотеки."""
|
|
60
55
|
path = getattr(sys.modules.get("__main__"), "__file__", None)
|
|
61
56
|
if path:
|
|
62
57
|
return Path(path).resolve().parent
|
|
@@ -69,18 +64,16 @@ def caller_dir():
|
|
|
69
64
|
|
|
70
65
|
|
|
71
66
|
def next_filename(directory=None):
|
|
72
|
-
"""Следующее имя 1.txt, 2.txt, 3.txt ..."""
|
|
73
67
|
base = Path(directory) if directory else caller_dir()
|
|
74
68
|
nums = [int(p.stem) for p in base.glob("*.txt") if p.stem.isdigit()]
|
|
75
69
|
return base / (str(max(nums, default=0) + 1) + ".txt")
|
|
76
70
|
|
|
77
71
|
|
|
78
72
|
def ask(prompt, model=None, temperature=0.7, timeout=300):
|
|
79
|
-
"""Отправить промпт и вернуть текст. Ничего не печатает и не сохраняет."""
|
|
80
73
|
body = {
|
|
81
74
|
"model": model or _state["model"],
|
|
82
75
|
"messages": [
|
|
83
|
-
{"role": "system", "content":
|
|
76
|
+
{"role": "system", "content": SYSTEM},
|
|
84
77
|
{"role": "user", "content": str(prompt)},
|
|
85
78
|
],
|
|
86
79
|
"temperature": temperature,
|
|
@@ -89,7 +82,7 @@ def ask(prompt, model=None, temperature=0.7, timeout=300):
|
|
|
89
82
|
API_URL,
|
|
90
83
|
data=json.dumps(body).encode("utf-8"),
|
|
91
84
|
headers={
|
|
92
|
-
"Authorization": "Bearer " +
|
|
85
|
+
"Authorization": "Bearer " + _key(),
|
|
93
86
|
"Content-Type": "application/json",
|
|
94
87
|
"HTTP-Referer": "https://pypi.org/project/librecoding/",
|
|
95
88
|
"X-Title": "librecoding",
|
|
@@ -111,7 +104,6 @@ def ask(prompt, model=None, temperature=0.7, timeout=300):
|
|
|
111
104
|
|
|
112
105
|
|
|
113
106
|
def save(text, filename=None):
|
|
114
|
-
"""Сохранить текст в N.txt (или в filename) рядом со скриптом."""
|
|
115
107
|
base = caller_dir()
|
|
116
108
|
path = Path(filename) if filename else next_filename(base)
|
|
117
109
|
if not path.is_absolute():
|
|
@@ -120,29 +112,27 @@ def save(text, filename=None):
|
|
|
120
112
|
return path
|
|
121
113
|
|
|
122
114
|
|
|
123
|
-
def chat(prompt, model=None,
|
|
124
|
-
"""Один промпт: напечатать ответ и сохранить его в N.txt."""
|
|
115
|
+
def chat(prompt, model=None, filename=None, save_answer=True):
|
|
125
116
|
text = str(prompt).strip()
|
|
126
117
|
if not text:
|
|
127
118
|
return None
|
|
128
|
-
|
|
119
|
+
verbose = bool(_state["verbose"])
|
|
120
|
+
if verbose:
|
|
121
|
+
print("думаю...")
|
|
129
122
|
answer = (ask(text, model=model) or "").strip()
|
|
130
|
-
|
|
123
|
+
if verbose:
|
|
124
|
+
print(answer or "(пустой ответ)")
|
|
131
125
|
if save_answer and answer:
|
|
132
126
|
path = save(answer, filename)
|
|
133
|
-
|
|
127
|
+
if verbose:
|
|
128
|
+
print("сохранено: " + path.name)
|
|
134
129
|
return path
|
|
135
130
|
return None
|
|
136
131
|
|
|
137
132
|
|
|
138
|
-
def interactive(prompt=None, model=None,
|
|
139
|
-
"""Без аргумента цикл промпт-ответ. С аргументом один ответ.
|
|
140
|
-
|
|
141
|
-
Выход: пустой Enter, выход, exit, q, Ctrl+C.
|
|
142
|
-
"""
|
|
133
|
+
def interactive(prompt=None, model=None, filename=None, save_answer=True):
|
|
143
134
|
if prompt is not None:
|
|
144
|
-
return chat(prompt, model=model,
|
|
145
|
-
|
|
135
|
+
return chat(prompt, model=model, filename=filename, save_answer=save_answer)
|
|
146
136
|
last = None
|
|
147
137
|
while True:
|
|
148
138
|
try:
|
|
@@ -151,20 +141,16 @@ def interactive(prompt=None, model=None, save_answer=True, filename=None):
|
|
|
151
141
|
print()
|
|
152
142
|
break
|
|
153
143
|
line = line.strip()
|
|
154
|
-
if not line or line.lower() in
|
|
144
|
+
if not line or line.lower() in EXIT:
|
|
155
145
|
break
|
|
156
|
-
print()
|
|
157
146
|
try:
|
|
158
|
-
last = chat(line, model=model,
|
|
147
|
+
last = chat(line, model=model, filename=filename, save_answer=save_answer)
|
|
159
148
|
except Exception as exc:
|
|
160
149
|
print("Ошибка: " + str(exc), file=sys.stderr)
|
|
161
|
-
print()
|
|
162
150
|
return last
|
|
163
151
|
|
|
164
152
|
|
|
165
|
-
class
|
|
166
|
-
"""Модуль, который можно вызвать как функцию: librecoding()."""
|
|
167
|
-
|
|
153
|
+
class _Module(ModuleType):
|
|
168
154
|
def __call__(self, obj=None, *args, **kwargs):
|
|
169
155
|
if callable(obj) and not args and not kwargs:
|
|
170
156
|
return obj
|
|
@@ -179,9 +165,9 @@ class _LibreCodingModule(ModuleType):
|
|
|
179
165
|
return None
|
|
180
166
|
|
|
181
167
|
|
|
182
|
-
sys.modules[__name__].__class__ =
|
|
168
|
+
sys.modules[__name__].__class__ = _Module
|
|
183
169
|
|
|
184
170
|
__all__ = [
|
|
185
171
|
"ask", "chat", "save", "interactive", "caller_dir", "next_filename",
|
|
186
|
-
"set_api_key", "set_model", "get_model",
|
|
172
|
+
"set_api_key", "set_model", "get_model", "set_verbose",
|
|
187
173
|
]
|
|
File without changes
|