nonebot-plugin-githubmodels 0.2.3__tar.gz → 0.2.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.
@@ -16,4 +16,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
16
  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
17
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
18
  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-githubmodels
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: API 调用 GitHub Models 的大语言模型
5
5
  Home-page: https://github.com/lyqgzbl/nonebot-plugin-githubmodels
6
6
  License: MIT
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
- Requires-Dist: nonebot-plugin-alconna (>=0.52.0,<0.53.0)
18
+ Requires-Dist: nonebot-plugin-alconna (>=0.53.0,<0.54.0)
19
19
  Requires-Dist: nonebot-plugin-htmlrender (>=0.3.5,<0.4.0)
20
20
  Requires-Dist: nonebot2 (>=2.2.1,<3.0.0)
21
21
  Requires-Dist: openai (>=1.44.1,<2.0.0)
@@ -38,33 +38,29 @@ Description-Content-Type: text/markdown
38
38
 
39
39
  _✨ API调用GitHub models大语言模型 ✨_
40
40
 
41
- </div>
41
+ <a href ="https://raw.githubusercontent.com/lyqgzbl/nonebot-plugin-githubmodels/refs/heads/main/LICENSE"><img src="https://img.shields.io/pypi/l/nonebot-plugin-githubmodels"> </a>
42
+ <a href="https://pypi.python.org/pypi/nonebot-plugin-githubmodels"> <img src="https://img.shields.io/pypi/v/nonebot-plugin-githubmodels.svg" alt="pypi"></a>
43
+ <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
42
44
 
43
- <p align="center">
44
- <a href="https://raw.githubusercontent.com/kexue-z/nonebot-plugin-heweather/master/LICENSE">
45
- <img src="https://img.shields.io/github/license/kexue-z/nonebot-plugin-heweather.svg" alt="license">
46
- </a>
47
- <a href="https://pypi.org/project/nonebot-plugin-heweather/">
48
- <img src="https://img.shields.io/pypi/v/nonebot-plugin-heweather" alt="pypi">
49
- </a>
50
- <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
51
- </p>
45
+ </div>
52
46
 
53
47
  # 安装
54
-
55
- 使用nb-cli
48
+ 使用nb-cli [推荐]
56
49
  ```shell
57
50
  nb plugin install nonebot-plugin-githubmodels
58
51
  ```
52
+ 使用pip
53
+ ```shell
54
+ pip install nonebot-plugin-githubmodels
55
+ ```
59
56
 
60
- # 指令
61
-
62
- AI xxx” 询问AI
63
- 例如“AI 嗨”
64
-
65
- ”AI 重置“ 重置上下文记忆
57
+ # 使用
58
+ 命令需要加 [NoneBot 命令前缀](https://nonebot.dev/docs/appendices/config#command-start-和-command-separator) (默认为`/`)
59
+ 使用命令`AI`/`ai`触发插件
60
+ 命令选项`-r` 重置上下文记忆
61
+ 命令选项`-i` 临时启用图片回复
66
62
 
67
63
  # 配置
68
64
 
69
- ## [.env.example](https://github.com/lyqgzbl/nonebot-plugin-githubmodels/blob/main/.env.example)
65
+ 配置参见 [.env.example](https://github.com/lyqgzbl/nonebot-plugin-githubmodels/blob/main/.env.example)
70
66
 
@@ -0,0 +1,41 @@
1
+ <!-- markdownlint-disable MD033 MD036 MD041 -->
2
+
3
+ <div align="center">
4
+
5
+ <a href="https://v2.nonebot.dev/store">
6
+ <img src="https://raw.githubusercontent.com/A-kirami/nonebot-plugin-template/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo">
7
+ </a>
8
+
9
+ <p>
10
+ <img src="https://raw.githubusercontent.com/lgc-NB2Dev/readme/main/template/plugin.svg" alt="NoneBotPluginText">
11
+ </p>
12
+
13
+ # nonebot-plugin-githubmodels
14
+
15
+ _✨ API调用GitHub models大语言模型 ✨_
16
+
17
+ <a href ="https://raw.githubusercontent.com/lyqgzbl/nonebot-plugin-githubmodels/refs/heads/main/LICENSE"><img src="https://img.shields.io/pypi/l/nonebot-plugin-githubmodels"> </a>
18
+ <a href="https://pypi.python.org/pypi/nonebot-plugin-githubmodels"> <img src="https://img.shields.io/pypi/v/nonebot-plugin-githubmodels.svg" alt="pypi"></a>
19
+ <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
20
+
21
+ </div>
22
+
23
+ # 安装
24
+ 使用nb-cli [推荐]
25
+ ```shell
26
+ nb plugin install nonebot-plugin-githubmodels
27
+ ```
28
+ 使用pip
29
+ ```shell
30
+ pip install nonebot-plugin-githubmodels
31
+ ```
32
+
33
+ # 使用
34
+ 命令需要加 [NoneBot 命令前缀](https://nonebot.dev/docs/appendices/config#command-start-和-command-separator) (默认为`/`)
35
+ 使用命令`AI`/`ai`触发插件
36
+ 命令选项`-r` 重置上下文记忆
37
+ 命令选项`-i` 临时启用图片回复
38
+
39
+ # 配置
40
+
41
+ 配置参见 [.env.example](https://github.com/lyqgzbl/nonebot-plugin-githubmodels/blob/main/.env.example)
@@ -0,0 +1,29 @@
1
+ from openai import AsyncOpenAI
2
+
3
+ class GPTHandler:
4
+ def __init__(
5
+ self,
6
+ api_key: str,
7
+ endpoint: str,
8
+ model_name: str,
9
+ max_context_length: int,
10
+ temperature: float,
11
+ max_tokens: int,
12
+ top_p: float,
13
+ ):
14
+ self.client = AsyncOpenAI(base_url=endpoint, api_key=api_key)
15
+ self.model_name = model_name
16
+ self.max_context_length = max_context_length
17
+ self.temperature = temperature
18
+ self.max_tokens = max_tokens
19
+ self.top_p = top_p
20
+
21
+ async def get_response(self, messages: list) -> str:
22
+ response = await self.client.chat.completions.create(
23
+ messages=messages,
24
+ model=self.model_name,
25
+ temperature=self.temperature,
26
+ max_tokens=self.max_tokens,
27
+ top_p=self.top_p,
28
+ )
29
+ return response.choices[0].message.content
@@ -1,26 +1,48 @@
1
1
  import nonebot
2
2
  from nonebot import require, get_plugin_config
3
+ from nonebot.rule import Rule
4
+ from nonebot.log import logger
5
+ from nonebot.plugin import PluginMetadata
6
+
3
7
  require("nonebot_plugin_alconna")
4
8
  require("nonebot_plugin_htmlrender")
5
- from openai import AsyncOpenAI
9
+ from openai import BadRequestError
6
10
  from arclet.alconna import Args, Option, Alconna, MultiVar
7
- from nonebot.plugin import PluginMetadata
8
- from nonebot_plugin_htmlrender import md_to_pic
9
11
  from nonebot_plugin_alconna import UniMessage, on_alconna, Match
12
+ from nonebot_plugin_htmlrender import md_to_pic
13
+
10
14
  from .config import Config
15
+ from .GPT_handler import GPTHandler
16
+ from .context_manager import ContextManager
17
+
11
18
 
12
19
  plugin_config = get_plugin_config(Config)
13
- TOKEN = plugin_config.github_token
14
- MODEL_NAME = plugin_config.ai_model_name
15
20
  REPLY_IMAGE = plugin_config.ai_reply_image
16
- MAX_CONTEXT_LENGTH = plugin_config.max_context_length
17
21
 
18
- endpoint = "https://models.inference.ai.azure.com"
19
- client = AsyncOpenAI(
20
- base_url=endpoint,
21
- api_key=TOKEN,
22
- )
23
- shared_context = []
22
+
23
+ if not plugin_config.github_token:
24
+ logger.opt(colors=True).warning("<yellow>缺失必要配置项 'github_token',已禁用该插件</yellow>")
25
+ GPT_handler = None
26
+ else:
27
+ GPT_handler = GPTHandler(
28
+ api_key=plugin_config.github_token,
29
+ endpoint="https://models.inference.ai.azure.com",
30
+ model_name=plugin_config.ai_model_name,
31
+ max_context_length=plugin_config.max_context_length,
32
+ temperature=plugin_config.ai_temperature,
33
+ max_tokens=plugin_config.ai_max_tokens,
34
+ top_p=plugin_config.ai_top_p,
35
+ )
36
+
37
+
38
+ def is_enable() -> Rule:
39
+ def _rule() -> bool:
40
+ return bool(plugin_config.github_token)
41
+ return Rule(_rule)
42
+
43
+
44
+ context_manager = ContextManager(max_context_length=plugin_config.max_context_length)
45
+
24
46
 
25
47
  ai = on_alconna(
26
48
  Alconna(
@@ -29,17 +51,19 @@ ai = on_alconna(
29
51
  Option("-r|--reset"),
30
52
  Option("-i|--image"),
31
53
  ),
54
+ rule=is_enable(),
32
55
  use_cmd_start=True,
33
56
  block=True,
34
57
  aliases={"ai"},
35
58
  )
36
59
 
60
+
37
61
  @ai.assign("reset")
38
62
  async def ai_reset():
39
- global shared_context
40
- shared_context = []
63
+ context_manager.reset_context()
41
64
  await ai.finish("上下文已重置")
42
65
 
66
+
43
67
  @ai.assign("image")
44
68
  async def ai_image(user_input: Match[tuple[str]]):
45
69
  if user_input.available:
@@ -47,48 +71,34 @@ async def ai_image(user_input: Match[tuple[str]]):
47
71
  REPLY_IMAGE = True
48
72
  ai.set_path_arg("user_input", " ".join(user_input.result))
49
73
 
74
+
50
75
  @ai.handle()
51
76
  async def handle_function(user_input: Match[tuple[str]]):
52
77
  if user_input.available:
53
78
  ai.set_path_arg("user_input", " ".join(user_input.result))
54
79
 
80
+
55
81
  @ai.got_path("user_input", prompt="请输入有效问题")
56
82
  async def got_location(user_input: str):
57
- global shared_context, REPLY_IMAGE
58
- if MAX_CONTEXT_LENGTH > 0:
59
- shared_context.append({"role": "user", "content": user_input})
60
- if len(shared_context) > MAX_CONTEXT_LENGTH:
61
- shared_context = shared_context[-MAX_CONTEXT_LENGTH:]
62
-
63
- messages = [
64
- {"role": "system", "content": "回答尽量简练,请始终用中文回答"}
65
- ]
66
- if MAX_CONTEXT_LENGTH > 0:
67
- messages += shared_context
68
- else:
69
- messages.append({"role": "user", "content": user_input})
70
-
83
+ global REPLY_IMAGE
71
84
  try:
72
- response = await client.chat.completions.create(
73
- messages=messages,
74
- model=MODEL_NAME,
75
- temperature=1,
76
- max_tokens=500,
77
- top_p=1,
78
- )
79
-
80
- reply = response.choices[0].message.content
81
- if MAX_CONTEXT_LENGTH > 0:
82
- shared_context.append({"role": "assistant", "content": reply})
83
-
85
+ messages = [{"role": "system", "content": "回答尽量简练,请始终用中文回答"}]
86
+ context_manager.add_message("user", user_input)
87
+ messages += context_manager.get_context()
88
+ reply = await GPT_handler.get_response(messages)
89
+ context_manager.add_message("assistant", reply)
84
90
  if REPLY_IMAGE:
85
91
  pic = await md_to_pic(md=reply)
86
92
  await UniMessage.image(raw=pic).send(reply_to=True)
87
93
  else:
88
94
  await UniMessage.text(reply).send(reply_to=True)
95
+ except BadRequestError as e:
96
+ logger.opt(colors=True).error(f"<red>API 请求失败: {e}</red>")
97
+ await ai.send("问题触发了内容过滤策略,请修改问题后重试")
89
98
  finally:
90
99
  REPLY_IMAGE = plugin_config.ai_reply_image
91
100
 
101
+
92
102
  __plugin_meta__ = PluginMetadata(
93
103
  name="githubmodels",
94
104
  description="API 调用 GitHub Models 的大语言模型",
@@ -96,4 +106,4 @@ __plugin_meta__ = PluginMetadata(
96
106
  type="application",
97
107
  homepage="https://github.com/lyqgzbl/nonebot-plugin-githubmodels",
98
108
  supported_adapters=None,
99
- )
109
+ )
@@ -3,6 +3,9 @@ from pydantic import BaseModel, Field
3
3
 
4
4
  class Config(BaseModel):
5
5
  github_token: Optional[str] = None
6
- ai_model_name: str = "gpt-4o-mini"
7
6
  max_context_length: int = Field(20)
8
7
  ai_reply_image: bool = False
8
+ ai_model_name: str = "gpt-4o-mini"
9
+ ai_temperature: float = Field(1.0)
10
+ ai_max_tokens: int = Field(1024)
11
+ ai_top_p: float = Field(1.0)
@@ -0,0 +1,16 @@
1
+ class ContextManager:
2
+ def __init__(self, max_context_length: int):
3
+ self.max_context_length = max_context_length
4
+ self.shared_context = []
5
+
6
+ def add_message(self, role: str, content: str):
7
+ if self.max_context_length > 0:
8
+ self.shared_context.append({"role": role, "content": content})
9
+ if len(self.shared_context) > self.max_context_length:
10
+ self.shared_context = self.shared_context[-self.max_context_length:]
11
+
12
+ def get_context(self):
13
+ return self.shared_context.copy() if self.max_context_length > 0 else []
14
+
15
+ def reset_context(self):
16
+ self.shared_context = []
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "nonebot-plugin-githubmodels"
3
- version = "0.2.3"
3
+ version = "0.2.5"
4
4
  description = "API 调用 GitHub Models 的大语言模型"
5
5
  readme = "README.md"
6
6
  authors = ["lyqgzbl <admin@lyqgzbl.com>"]
@@ -13,7 +13,7 @@ python = "^3.8"
13
13
  nonebot2 = "^2.2.1"
14
14
  openai = "^1.44.1"
15
15
  nonebot-plugin-htmlrender = "^0.3.5"
16
- nonebot-plugin-alconna = "^0.52.0"
16
+ nonebot-plugin-alconna = "^0.53.0"
17
17
 
18
18
  [build-system]
19
19
  requires = ["poetry-core>=1.0.0"]
@@ -1,45 +0,0 @@
1
- <!-- markdownlint-disable MD033 MD036 MD041 -->
2
-
3
- <div align="center">
4
-
5
- <a href="https://v2.nonebot.dev/store">
6
- <img src="https://raw.githubusercontent.com/A-kirami/nonebot-plugin-template/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo">
7
- </a>
8
-
9
- <p>
10
- <img src="https://raw.githubusercontent.com/lgc-NB2Dev/readme/main/template/plugin.svg" alt="NoneBotPluginText">
11
- </p>
12
-
13
- # nonebot-plugin-githubmodels
14
-
15
- _✨ API调用GitHub models大语言模型 ✨_
16
-
17
- </div>
18
-
19
- <p align="center">
20
- <a href="https://raw.githubusercontent.com/kexue-z/nonebot-plugin-heweather/master/LICENSE">
21
- <img src="https://img.shields.io/github/license/kexue-z/nonebot-plugin-heweather.svg" alt="license">
22
- </a>
23
- <a href="https://pypi.org/project/nonebot-plugin-heweather/">
24
- <img src="https://img.shields.io/pypi/v/nonebot-plugin-heweather" alt="pypi">
25
- </a>
26
- <img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
27
- </p>
28
-
29
- # 安装
30
-
31
- 使用nb-cli
32
- ```shell
33
- nb plugin install nonebot-plugin-githubmodels
34
- ```
35
-
36
- # 指令
37
-
38
- “AI xxx” 询问AI
39
- 例如“AI 嗨”
40
-
41
- ”AI 重置“ 重置上下文记忆
42
-
43
- # 配置
44
-
45
- ## 见 [.env.example](https://github.com/lyqgzbl/nonebot-plugin-githubmodels/blob/main/.env.example)