revengelibrary 0.1.0__tar.gz → 0.1.5__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.
@@ -0,0 +1,15 @@
1
+ include LICENSE
2
+ include README.md
3
+ include pyproject.toml
4
+
5
+ recursive-include revengelibrary *.py *.json
6
+
7
+ exclude .env
8
+ exclude .revengelibrary_memory.json
9
+
10
+ prune venv
11
+ prune build
12
+ prune dist
13
+
14
+ global-exclude __pycache__
15
+ global-exclude *.py[cod]
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: revengelibrary
3
+ Version: 0.1.5
4
+ Summary: Python chat library and CLI for free LLM models via OpenRouter.
5
+ Author: revengebibliotek contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/example/revengelibrary
8
+ Keywords: chat,llm,openrouter,api,cli
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: requests>=2.31.0
16
+ Dynamic: license-file
17
+
18
+ # revengelibrary
19
+
20
+ Не нейросеть
21
+
22
+ ## Установка
23
+
24
+ Из PyPI (после публикации):
25
+
26
+ ```bash
27
+ pip install revengelibrary
28
+ ```
29
+
30
+ ## Запуск из терминала
31
+
32
+ После установки доступна команда:
33
+
34
+ ```bash
35
+ revengelibrary "text"
36
+ ```
@@ -0,0 +1,19 @@
1
+ # revengelibrary
2
+
3
+ Не нейросеть
4
+
5
+ ## Установка
6
+
7
+ Из PyPI (после публикации):
8
+
9
+ ```bash
10
+ pip install revengelibrary
11
+ ```
12
+
13
+ ## Запуск из терминала
14
+
15
+ После установки доступна команда:
16
+
17
+ ```bash
18
+ revengelibrary "text"
19
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "revengelibrary"
7
- version = "0.1.0"
7
+ version = "0.1.5"
8
8
  description = "Python chat library and CLI for free LLM models via OpenRouter."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -29,7 +29,10 @@ Homepage = "https://github.com/example/revengelibrary"
29
29
  revengelibrary = "revengelibrary.cli:main"
30
30
 
31
31
  [tool.setuptools]
32
- include-package-data = false
32
+ include-package-data = true
33
33
 
34
34
  [tool.setuptools.packages.find]
35
35
  include = ["revengelibrary*"]
36
+
37
+ [tool.setuptools.package-data]
38
+ revengelibrary = ["memory_store.json"]
@@ -0,0 +1,16 @@
1
+ from .chat import (
2
+ APIError,
3
+ DEFAULT_MEMORY_FILE,
4
+ DEFAULT_MODEL,
5
+ DEFAULT_OPENROUTER_API_KEY,
6
+ FreeNeuroChatClient,
7
+ )
8
+
9
+ __all__ = [
10
+ "FreeNeuroChatClient",
11
+ "APIError",
12
+ "DEFAULT_OPENROUTER_API_KEY",
13
+ "DEFAULT_MODEL",
14
+ "DEFAULT_MEMORY_FILE",
15
+ ]
16
+ __version__ = "0.1.5"
@@ -0,0 +1,207 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass, field
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import requests
9
+
10
+ DEFAULT_OPENROUTER_API_KEY = (
11
+ "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"
12
+ )
13
+ DEFAULT_MODEL = "meta-llama/llama-3.1-8b-instruct:free"
14
+ DEFAULT_MEMORY_FILE = str(Path(__file__).resolve().with_name("memory_store.json"))
15
+ _MODEL_UNAVAILABLE_MARKER = "No endpoints found"
16
+
17
+
18
+ class APIError(RuntimeError):
19
+ """Raised when the upstream API returns an invalid response."""
20
+
21
+
22
+ @dataclass
23
+ class FreeNeuroChatClient:
24
+ """Small wrapper around OpenRouter's OpenAI-compatible chat endpoint."""
25
+
26
+ api_key: str = DEFAULT_OPENROUTER_API_KEY
27
+ model: str = DEFAULT_MODEL
28
+ base_url: str = "https://openrouter.ai/api/v1"
29
+ timeout: int = 60
30
+ system_prompt: str | None = "You are a helpful assistant."
31
+ memory_file: str | None = DEFAULT_MEMORY_FILE
32
+ autosave_memory: bool = True
33
+ messages: list[dict[str, str]] = field(default_factory=list)
34
+
35
+ def __post_init__(self) -> None:
36
+ if not self.api_key:
37
+ self.api_key = DEFAULT_OPENROUTER_API_KEY
38
+
39
+ if self.memory_file:
40
+ self.load_memory()
41
+
42
+ if not self.messages and self.system_prompt:
43
+ self.messages.append({"role": "system", "content": self.system_prompt})
44
+ elif self.system_prompt and not self._has_system_message(self.messages):
45
+ self.messages.insert(0, {"role": "system", "content": self.system_prompt})
46
+
47
+ self._save_memory_if_needed()
48
+
49
+ def send(self, user_message: str) -> str:
50
+ """Send user text and return assistant reply."""
51
+ user_message = user_message.strip()
52
+ if not user_message:
53
+ raise ValueError("user_message must not be empty")
54
+
55
+ self.messages.append({"role": "user", "content": user_message})
56
+ payload = {"model": self.model, "messages": self.messages}
57
+
58
+ response = self._chat_completion(payload)
59
+ if self._is_model_unavailable(response):
60
+ fallback_model = self._discover_fallback_free_model(exclude={self.model})
61
+ if fallback_model:
62
+ self.model = fallback_model
63
+ payload["model"] = fallback_model
64
+ response = self._chat_completion(payload)
65
+
66
+ if response.status_code >= 400:
67
+ raise APIError(
68
+ f"API returned {response.status_code}: {response.text[:500]}"
69
+ )
70
+
71
+ data = response.json()
72
+ content = self._extract_content(data)
73
+ self.messages.append({"role": "assistant", "content": content})
74
+ self._save_memory_if_needed()
75
+ return content
76
+
77
+ def reset(self) -> None:
78
+ """Clear dialog history but keep initial system prompt."""
79
+ self.messages = []
80
+ if self.system_prompt:
81
+ self.messages.append({"role": "system", "content": self.system_prompt})
82
+ self._save_memory_if_needed()
83
+
84
+ def save_memory(self, file_path: str | None = None) -> None:
85
+ """Persist full dialog history to JSON file."""
86
+ path = self._resolve_memory_path(file_path)
87
+ if path is None:
88
+ return
89
+ path.parent.mkdir(parents=True, exist_ok=True)
90
+ path.write_text(
91
+ json.dumps(self.messages, ensure_ascii=False, indent=2),
92
+ encoding="utf-8",
93
+ )
94
+
95
+ def load_memory(self, file_path: str | None = None) -> None:
96
+ """Load dialog history from JSON file if it exists."""
97
+ path = self._resolve_memory_path(file_path)
98
+ if path is None or not path.exists():
99
+ return
100
+ try:
101
+ data = json.loads(path.read_text(encoding="utf-8"))
102
+ except (OSError, json.JSONDecodeError):
103
+ return
104
+ self.messages = self._normalize_messages(data)
105
+
106
+ def clear_memory_file(self, file_path: str | None = None) -> None:
107
+ """Delete persisted history file."""
108
+ path = self._resolve_memory_path(file_path)
109
+ if path is None:
110
+ return
111
+ if path.exists():
112
+ path.unlink()
113
+
114
+ def _headers(self) -> dict[str, str]:
115
+ return {
116
+ "Authorization": f"Bearer {self.api_key}",
117
+ "Content-Type": "application/json",
118
+ }
119
+
120
+ def _chat_completion(self, payload: dict[str, Any]) -> requests.Response:
121
+ return requests.post(
122
+ f"{self.base_url}/chat/completions",
123
+ headers=self._headers(),
124
+ json=payload,
125
+ timeout=self.timeout,
126
+ )
127
+
128
+ def _is_model_unavailable(self, response: requests.Response) -> bool:
129
+ if response.status_code != 404:
130
+ return False
131
+ try:
132
+ data = response.json()
133
+ except ValueError:
134
+ return False
135
+ message = str(data.get("error", {}).get("message", ""))
136
+ return _MODEL_UNAVAILABLE_MARKER in message
137
+
138
+ def _discover_fallback_free_model(self, exclude: set[str]) -> str | None:
139
+ try:
140
+ response = requests.get(
141
+ f"{self.base_url}/models",
142
+ headers=self._headers(),
143
+ timeout=self.timeout,
144
+ )
145
+ if response.status_code >= 400:
146
+ return None
147
+ data = response.json()
148
+ except Exception: # noqa: BLE001
149
+ return None
150
+
151
+ model_ids = self._extract_free_model_ids(data.get("data"))
152
+ for model_id in model_ids:
153
+ if model_id not in exclude:
154
+ return model_id
155
+ return None
156
+
157
+ @staticmethod
158
+ def _extract_free_model_ids(data: Any) -> list[str]:
159
+ if not isinstance(data, list):
160
+ return []
161
+
162
+ result: list[str] = []
163
+ for item in data:
164
+ if not isinstance(item, dict):
165
+ continue
166
+ model_id = item.get("id")
167
+ if isinstance(model_id, str) and model_id.endswith(":free"):
168
+ result.append(model_id)
169
+ return result
170
+
171
+ def _save_memory_if_needed(self) -> None:
172
+ if self.autosave_memory:
173
+ self.save_memory()
174
+
175
+ def _resolve_memory_path(self, file_path: str | None = None) -> Path | None:
176
+ value = file_path if file_path is not None else self.memory_file
177
+ if not value:
178
+ return None
179
+ return Path(value).expanduser()
180
+
181
+ @staticmethod
182
+ def _normalize_messages(data: Any) -> list[dict[str, str]]:
183
+ if not isinstance(data, list):
184
+ return []
185
+ normalized: list[dict[str, str]] = []
186
+ for item in data:
187
+ if not isinstance(item, dict):
188
+ continue
189
+ role = item.get("role")
190
+ content = item.get("content")
191
+ if isinstance(role, str) and isinstance(content, str):
192
+ normalized.append({"role": role, "content": content})
193
+ return normalized
194
+
195
+ @staticmethod
196
+ def _has_system_message(messages: list[dict[str, str]]) -> bool:
197
+ return any(item.get("role") == "system" for item in messages)
198
+
199
+ @staticmethod
200
+ def _extract_content(data: dict[str, Any]) -> str:
201
+ try:
202
+ content = data["choices"][0]["message"]["content"]
203
+ if not isinstance(content, str) or not content.strip():
204
+ raise KeyError
205
+ return content
206
+ except (KeyError, TypeError, IndexError):
207
+ raise APIError(f"Unexpected API response: {data}") from None
@@ -3,7 +3,13 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import os
5
5
 
6
- from .chat import APIError, FreeNeuroChatClient
6
+ from .chat import (
7
+ APIError,
8
+ DEFAULT_MEMORY_FILE,
9
+ DEFAULT_MODEL,
10
+ DEFAULT_OPENROUTER_API_KEY,
11
+ FreeNeuroChatClient,
12
+ )
7
13
 
8
14
 
9
15
  def _build_parser() -> argparse.ArgumentParser:
@@ -13,12 +19,12 @@ def _build_parser() -> argparse.ArgumentParser:
13
19
  )
14
20
  parser.add_argument(
15
21
  "--api-key",
16
- default=os.getenv("OPENROUTER_API_KEY", ""),
17
- help="OpenRouter API key. Fallback: OPENROUTER_API_KEY env var.",
22
+ default=os.getenv("OPENROUTER_API_KEY", DEFAULT_OPENROUTER_API_KEY),
23
+ help="OpenRouter API key. Fallback: OPENROUTER_API_KEY, then built-in key.",
18
24
  )
19
25
  parser.add_argument(
20
26
  "--model",
21
- default="meta-llama/llama-3.1-8b-instruct:free",
27
+ default=DEFAULT_MODEL,
22
28
  help="Model name on OpenRouter (default uses a free model).",
23
29
  )
24
30
  parser.add_argument(
@@ -26,6 +32,14 @@ def _build_parser() -> argparse.ArgumentParser:
26
32
  default="You are a helpful assistant.",
27
33
  help="System prompt.",
28
34
  )
35
+ parser.add_argument(
36
+ "--memory-file",
37
+ default=os.getenv("REVENGELIBRARY_MEMORY_FILE", DEFAULT_MEMORY_FILE),
38
+ help=(
39
+ "JSON file for chat memory. "
40
+ "Fallback: REVENGELIBRARY_MEMORY_FILE, then built-in package memory file."
41
+ ),
42
+ )
29
43
  return parser
30
44
 
31
45
 
@@ -33,16 +47,17 @@ def main() -> int:
33
47
  parser = _build_parser()
34
48
  args = parser.parse_args()
35
49
 
36
- if not args.api_key:
37
- parser.error("API key is required: pass --api-key or set OPENROUTER_API_KEY.")
38
-
39
50
  client = FreeNeuroChatClient(
40
- api_key=args.api_key,
51
+ api_key=args.api_key or DEFAULT_OPENROUTER_API_KEY,
41
52
  model=args.model,
42
53
  system_prompt=args.system,
54
+ memory_file=args.memory_file,
43
55
  )
44
56
 
45
- print("Interactive chat started. Type 'exit' to quit, 'reset' to clear history.")
57
+ print(
58
+ "Interactive chat started. "
59
+ "Type 'exit' to quit, 'reset' to clear history, 'forget' to delete memory file."
60
+ )
46
61
  while True:
47
62
  try:
48
63
  text = input("you> ").strip()
@@ -59,6 +74,11 @@ def main() -> int:
59
74
  client.reset()
60
75
  print("history reset")
61
76
  continue
77
+ if text.lower() == "forget":
78
+ client.reset()
79
+ client.clear_memory_file()
80
+ print("history reset and memory file deleted")
81
+ continue
62
82
 
63
83
  try:
64
84
  answer = client.send(text)
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: revengelibrary
3
+ Version: 0.1.5
4
+ Summary: Python chat library and CLI for free LLM models via OpenRouter.
5
+ Author: revengebibliotek contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/example/revengelibrary
8
+ Keywords: chat,llm,openrouter,api,cli
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: requests>=2.31.0
16
+ Dynamic: license-file
17
+
18
+ # revengelibrary
19
+
20
+ Не нейросеть
21
+
22
+ ## Установка
23
+
24
+ Из PyPI (после публикации):
25
+
26
+ ```bash
27
+ pip install revengelibrary
28
+ ```
29
+
30
+ ## Запуск из терминала
31
+
32
+ После установки доступна команда:
33
+
34
+ ```bash
35
+ revengelibrary "text"
36
+ ```
@@ -1,9 +1,11 @@
1
1
  LICENSE
2
+ MANIFEST.in
2
3
  README.md
3
4
  pyproject.toml
4
5
  revengelibrary/__init__.py
5
6
  revengelibrary/chat.py
6
7
  revengelibrary/cli.py
8
+ revengelibrary/memory_store.json
7
9
  revengelibrary.egg-info/PKG-INFO
8
10
  revengelibrary.egg-info/SOURCES.txt
9
11
  revengelibrary.egg-info/dependency_links.txt
@@ -1,88 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: revengelibrary
3
- Version: 0.1.0
4
- Summary: Python chat library and CLI for free LLM models via OpenRouter.
5
- Author: revengebibliotek contributors
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/example/revengelibrary
8
- Keywords: chat,llm,openrouter,api,cli
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.9
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Requires-Dist: requests>=2.31.0
16
- Dynamic: license-file
17
-
18
- # revengelibrary
19
-
20
- Небольшая Python-библиотека и CLI для чата с нейросетью через бесплатные модели OpenRouter.
21
-
22
- ## Установка
23
-
24
- Из PyPI (после публикации):
25
-
26
- ```bash
27
- pip install revengelibrary
28
- ```
29
-
30
- Локально из папки проекта:
31
-
32
- ```bash
33
- pip install .
34
- ```
35
-
36
- Для разработки:
37
-
38
- ```bash
39
- pip install -e .
40
- ```
41
-
42
- ## Подготовка API ключа
43
-
44
- 1. Создай бесплатный API key в OpenRouter.
45
- 2. Экспортируй ключ:
46
-
47
- ```bash
48
- export OPENROUTER_API_KEY="your_key"
49
- ```
50
-
51
- ## Использование в Python
52
-
53
- ```python
54
- from revengelibrary import FreeNeuroChatClient
55
-
56
- client = FreeNeuroChatClient(api_key="your_key")
57
- reply = client.send("Привет! Расскажи коротко анекдот.")
58
- print(reply)
59
- ```
60
-
61
- ## Запуск из терминала
62
-
63
- После установки доступна команда:
64
-
65
- ```bash
66
- revengelibrary --model meta-llama/llama-3.1-8b-instruct:free
67
- ```
68
-
69
- Если ключ лежит в `OPENROUTER_API_KEY`, то `--api-key` можно не передавать.
70
-
71
- ## Минимальный API библиотеки
72
-
73
- - `FreeNeuroChatClient.send(text: str) -> str`
74
- - `FreeNeuroChatClient.reset() -> None`
75
-
76
- ## Публикация в PyPI
77
-
78
- ```bash
79
- python3 -m pip install --upgrade build twine
80
- python3 -m build
81
- python3 -m twine upload dist/*
82
- ```
83
-
84
- После этого любой человек сможет установить библиотеку:
85
-
86
- ```bash
87
- pip install revengelibrary
88
- ```
@@ -1,71 +0,0 @@
1
- # revengelibrary
2
-
3
- Небольшая Python-библиотека и CLI для чата с нейросетью через бесплатные модели OpenRouter.
4
-
5
- ## Установка
6
-
7
- Из PyPI (после публикации):
8
-
9
- ```bash
10
- pip install revengelibrary
11
- ```
12
-
13
- Локально из папки проекта:
14
-
15
- ```bash
16
- pip install .
17
- ```
18
-
19
- Для разработки:
20
-
21
- ```bash
22
- pip install -e .
23
- ```
24
-
25
- ## Подготовка API ключа
26
-
27
- 1. Создай бесплатный API key в OpenRouter.
28
- 2. Экспортируй ключ:
29
-
30
- ```bash
31
- export OPENROUTER_API_KEY="your_key"
32
- ```
33
-
34
- ## Использование в Python
35
-
36
- ```python
37
- from revengelibrary import FreeNeuroChatClient
38
-
39
- client = FreeNeuroChatClient(api_key="your_key")
40
- reply = client.send("Привет! Расскажи коротко анекдот.")
41
- print(reply)
42
- ```
43
-
44
- ## Запуск из терминала
45
-
46
- После установки доступна команда:
47
-
48
- ```bash
49
- revengelibrary --model meta-llama/llama-3.1-8b-instruct:free
50
- ```
51
-
52
- Если ключ лежит в `OPENROUTER_API_KEY`, то `--api-key` можно не передавать.
53
-
54
- ## Минимальный API библиотеки
55
-
56
- - `FreeNeuroChatClient.send(text: str) -> str`
57
- - `FreeNeuroChatClient.reset() -> None`
58
-
59
- ## Публикация в PyPI
60
-
61
- ```bash
62
- python3 -m pip install --upgrade build twine
63
- python3 -m build
64
- python3 -m twine upload dist/*
65
- ```
66
-
67
- После этого любой человек сможет установить библиотеку:
68
-
69
- ```bash
70
- pip install revengelibrary
71
- ```
@@ -1,4 +0,0 @@
1
- from .chat import FreeNeuroChatClient, APIError
2
-
3
- __all__ = ["FreeNeuroChatClient", "APIError"]
4
- __version__ = "0.1.0"
@@ -1,76 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass, field
4
- from typing import Any
5
-
6
- import requests
7
-
8
-
9
- class APIError(RuntimeError):
10
- """Raised when the upstream API returns an invalid response."""
11
-
12
-
13
- @dataclass
14
- class FreeNeuroChatClient:
15
- """Small wrapper around OpenRouter's OpenAI-compatible chat endpoint."""
16
-
17
- api_key: str
18
- model: str = "meta-llama/llama-3.1-8b-instruct:free"
19
- base_url: str = "https://openrouter.ai/api/v1"
20
- timeout: int = 60
21
- system_prompt: str | None = "You are a helpful assistant."
22
- messages: list[dict[str, str]] = field(default_factory=list)
23
-
24
- def __post_init__(self) -> None:
25
- if not self.api_key:
26
- raise ValueError("api_key is required")
27
- if self.system_prompt and not self.messages:
28
- self.messages.append({"role": "system", "content": self.system_prompt})
29
-
30
- def send(self, user_message: str) -> str:
31
- """Send user text and return assistant reply."""
32
- user_message = user_message.strip()
33
- if not user_message:
34
- raise ValueError("user_message must not be empty")
35
-
36
- self.messages.append({"role": "user", "content": user_message})
37
- payload = {"model": self.model, "messages": self.messages}
38
-
39
- response = requests.post(
40
- f"{self.base_url}/chat/completions",
41
- headers=self._headers(),
42
- json=payload,
43
- timeout=self.timeout,
44
- )
45
-
46
- if response.status_code >= 400:
47
- raise APIError(
48
- f"API returned {response.status_code}: {response.text[:500]}"
49
- )
50
-
51
- data = response.json()
52
- content = self._extract_content(data)
53
- self.messages.append({"role": "assistant", "content": content})
54
- return content
55
-
56
- def reset(self) -> None:
57
- """Clear dialog history but keep initial system prompt."""
58
- self.messages = []
59
- if self.system_prompt:
60
- self.messages.append({"role": "system", "content": self.system_prompt})
61
-
62
- def _headers(self) -> dict[str, str]:
63
- return {
64
- "Authorization": f"Bearer {self.api_key}",
65
- "Content-Type": "application/json",
66
- }
67
-
68
- @staticmethod
69
- def _extract_content(data: dict[str, Any]) -> str:
70
- try:
71
- content = data["choices"][0]["message"]["content"]
72
- if not isinstance(content, str) or not content.strip():
73
- raise KeyError
74
- return content
75
- except (KeyError, TypeError, IndexError):
76
- raise APIError(f"Unexpected API response: {data}") from None
@@ -1,88 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: revengelibrary
3
- Version: 0.1.0
4
- Summary: Python chat library and CLI for free LLM models via OpenRouter.
5
- Author: revengebibliotek contributors
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/example/revengelibrary
8
- Keywords: chat,llm,openrouter,api,cli
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.9
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Requires-Dist: requests>=2.31.0
16
- Dynamic: license-file
17
-
18
- # revengelibrary
19
-
20
- Небольшая Python-библиотека и CLI для чата с нейросетью через бесплатные модели OpenRouter.
21
-
22
- ## Установка
23
-
24
- Из PyPI (после публикации):
25
-
26
- ```bash
27
- pip install revengelibrary
28
- ```
29
-
30
- Локально из папки проекта:
31
-
32
- ```bash
33
- pip install .
34
- ```
35
-
36
- Для разработки:
37
-
38
- ```bash
39
- pip install -e .
40
- ```
41
-
42
- ## Подготовка API ключа
43
-
44
- 1. Создай бесплатный API key в OpenRouter.
45
- 2. Экспортируй ключ:
46
-
47
- ```bash
48
- export OPENROUTER_API_KEY="your_key"
49
- ```
50
-
51
- ## Использование в Python
52
-
53
- ```python
54
- from revengelibrary import FreeNeuroChatClient
55
-
56
- client = FreeNeuroChatClient(api_key="your_key")
57
- reply = client.send("Привет! Расскажи коротко анекдот.")
58
- print(reply)
59
- ```
60
-
61
- ## Запуск из терминала
62
-
63
- После установки доступна команда:
64
-
65
- ```bash
66
- revengelibrary --model meta-llama/llama-3.1-8b-instruct:free
67
- ```
68
-
69
- Если ключ лежит в `OPENROUTER_API_KEY`, то `--api-key` можно не передавать.
70
-
71
- ## Минимальный API библиотеки
72
-
73
- - `FreeNeuroChatClient.send(text: str) -> str`
74
- - `FreeNeuroChatClient.reset() -> None`
75
-
76
- ## Публикация в PyPI
77
-
78
- ```bash
79
- python3 -m pip install --upgrade build twine
80
- python3 -m build
81
- python3 -m twine upload dist/*
82
- ```
83
-
84
- После этого любой человек сможет установить библиотеку:
85
-
86
- ```bash
87
- pip install revengelibrary
88
- ```
File without changes
File without changes