doorcore 0.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.
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.5
2
+ Name: doorcore
3
+ Version: 0.1.0
4
+ Summary: door-Core v2: a lightweight offline Chinese AI engine with Markov generation and cosine retrieval
5
+ Project-URL: Homepage, https://github.com/door-studio/doorcore
6
+ Author-email: door 工作室 <doornb666@outlook.com>
7
+ License: MIT
8
+ Keywords: ai,chatbot,chinese,markov,offline
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+
21
+ # doorcore
22
+
23
+ door-Core v2 本地 AI 引擎,纯 Python 标准库、零第三方依赖。
24
+
25
+ 特性:
26
+ - 中文关键词知识库检索(余弦相似)
27
+ - 字符级马尔可夫生成
28
+ - 中文/口语算式安全计算:支持「二乘四」「2的10次方」「根号2」「5的平方」等
29
+ - 报时、讲笑话、简单角色扮演
30
+ - OpenAI 兼容 API(`/v1/chat/completions`)
31
+
32
+ ## 安装
33
+
34
+ ```bash
35
+ pip install doorcore
36
+ ```
37
+
38
+ ## 编程使用
39
+
40
+ ```python
41
+ from doorcore import DoorCore
42
+
43
+ ai = DoorCore()
44
+ ai.load_or_train()
45
+
46
+ print(ai.ask("1+1等于几")) # 计算
47
+ print(ai.ask("雷军是谁")) # 知识库
48
+ print(ai.ask("讲个笑话")) # 笑话
49
+ ```
50
+
51
+ ## 命令行
52
+
53
+ ```bash
54
+ doorcore # 启动交互式聊天
55
+ doorcore-api # 启动 OpenAI 兼容 API 服务
56
+ ```
57
+
58
+ API 默认监听 `http://localhost:8765/v1`,模型名 `door-core`。
59
+ 可在任何支持自定义 OpenAI 兼容模型的客户端里填入:
60
+ - Base URL: `http://localhost:8765/v1`
61
+ - Model: `door-core`
62
+ - API Key: 任意填(不校验)
63
+
64
+ ## 许可证
65
+
66
+ MIT
@@ -0,0 +1,46 @@
1
+ # doorcore
2
+
3
+ door-Core v2 本地 AI 引擎,纯 Python 标准库、零第三方依赖。
4
+
5
+ 特性:
6
+ - 中文关键词知识库检索(余弦相似)
7
+ - 字符级马尔可夫生成
8
+ - 中文/口语算式安全计算:支持「二乘四」「2的10次方」「根号2」「5的平方」等
9
+ - 报时、讲笑话、简单角色扮演
10
+ - OpenAI 兼容 API(`/v1/chat/completions`)
11
+
12
+ ## 安装
13
+
14
+ ```bash
15
+ pip install doorcore
16
+ ```
17
+
18
+ ## 编程使用
19
+
20
+ ```python
21
+ from doorcore import DoorCore
22
+
23
+ ai = DoorCore()
24
+ ai.load_or_train()
25
+
26
+ print(ai.ask("1+1等于几")) # 计算
27
+ print(ai.ask("雷军是谁")) # 知识库
28
+ print(ai.ask("讲个笑话")) # 笑话
29
+ ```
30
+
31
+ ## 命令行
32
+
33
+ ```bash
34
+ doorcore # 启动交互式聊天
35
+ doorcore-api # 启动 OpenAI 兼容 API 服务
36
+ ```
37
+
38
+ API 默认监听 `http://localhost:8765/v1`,模型名 `door-core`。
39
+ 可在任何支持自定义 OpenAI 兼容模型的客户端里填入:
40
+ - Base URL: `http://localhost:8765/v1`
41
+ - Model: `door-core`
42
+ - API Key: 任意填(不校验)
43
+
44
+ ## 许可证
45
+
46
+ MIT
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "doorcore"
7
+ version = "0.1.0"
8
+ description = "door-Core v2: a lightweight offline Chinese AI engine with Markov generation and cosine retrieval"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "door 工作室", email = "doornb666@outlook.com"},
14
+ ]
15
+ keywords = ["ai", "chatbot", "offline", "chinese", "markov"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Operating System :: OS Independent",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/door-studio/doorcore"
30
+
31
+ [project.scripts]
32
+ doorcore = "doorcore.cli:main"
33
+ doorcore-api = "doorcore.api:main"
34
+
35
+ [tool.hatch.build]
36
+ include = [
37
+ "src/doorcore/**/*.py",
38
+ "src/doorcore/data/**/*",
39
+ ]
40
+
41
+ [tool.hatch.build.targets.wheel]
42
+ packages = ["src/doorcore"]
@@ -0,0 +1,9 @@
1
+ """doorcore — door-Core v2 本地 AI 引擎(可 pip 安装的公开库)"""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from .core import DoorCore
6
+ from .api import application, get_ai
7
+ from .cli import main as cli_main
8
+
9
+ __all__ = ["DoorCore", "application", "get_ai", "cli_main", "__version__"]
@@ -0,0 +1,127 @@
1
+ # door-core v2 —— 零依赖 OpenAI 兼容 API + 可部署 WSGI application
2
+ # 本地运行: python api.py -> http://localhost:8765/v1
3
+ # 公网部署: 平台 import 本文件的 application(WSGI)即可,模型名 door-core
4
+ import os
5
+ import json
6
+ import importlib.resources as _resources
7
+
8
+ from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
9
+ from doorcore.core import DoorCore
10
+
11
+
12
+ def _pkg_path(*parts):
13
+ try:
14
+ return str(_resources.files("doorcore").joinpath(*parts))
15
+ except Exception:
16
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)), *parts)
17
+
18
+
19
+ def load_config():
20
+ p = _pkg_path("data", "config.json")
21
+ if os.path.exists(p):
22
+ try:
23
+ return json.load(open(p, encoding="utf-8"))
24
+ except Exception:
25
+ pass
26
+ return {"n": 3}
27
+
28
+
29
+ cfg = load_config()
30
+ _AI = None
31
+
32
+
33
+ def get_ai():
34
+ global _AI
35
+ if _AI is None:
36
+ n = int(cfg.get("n", 3))
37
+ _AI = DoorCore(n=n)
38
+ _AI.load_or_train()
39
+ return _AI
40
+
41
+
42
+ def _chat_reply(messages):
43
+ text = ""
44
+ for m in reversed(messages or []):
45
+ if m.get("role") == "user":
46
+ text = m.get("content", "")
47
+ break
48
+ return get_ai().ask(text)
49
+
50
+
51
+ def _completion_obj(reply):
52
+ return {
53
+ "object": "chat.completion",
54
+ "model": "door-core",
55
+ "choices": [{
56
+ "index": 0,
57
+ "message": {"role": "assistant", "content": reply},
58
+ "finish_reason": "stop",
59
+ }],
60
+ "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
61
+ }
62
+
63
+
64
+ def _body_str(obj):
65
+ return json.dumps(obj, ensure_ascii=False).encode("utf-8")
66
+
67
+
68
+ class Handler(BaseHTTPRequestHandler):
69
+ def _send(self, code, obj):
70
+ body = _body_str(obj)
71
+ self.send_response(code)
72
+ self.send_header("Content-Type", "application/json; charset=utf-8")
73
+ self.send_header("Content-Length", str(len(body)))
74
+ self.end_headers()
75
+ self.wfile.write(body)
76
+
77
+ def do_POST(self):
78
+ if self.path.rstrip("/") not in ("/v1/chat/completions", "/chat/completions"):
79
+ self._send(404, {"error": "not found"})
80
+ return
81
+ try:
82
+ ln = int(self.headers.get("Content-Length", 0) or 0)
83
+ raw = self.rfile.read(ln) if ln else b""
84
+ data = json.loads(raw.decode("utf-8"))
85
+ except Exception as e:
86
+ self._send(400, {"error": str(e)})
87
+ return
88
+ self._send(200, _completion_obj(_chat_reply(data.get("messages", []))))
89
+
90
+ def log_message(self, *a):
91
+ pass
92
+
93
+
94
+ def application(environ, start_response):
95
+ path = (environ.get("PATH_INFO", "") or "").rstrip("/")
96
+ if environ.get("REQUEST_METHOD") == "POST" and path in (
97
+ "/v1/chat/completions", "/chat/completions"):
98
+ try:
99
+ ln = int(environ.get("CONTENT_LENGTH", 0) or 0)
100
+ raw = environ["wsgi.input"].read(ln) if ln else b""
101
+ data = json.loads(raw.decode("utf-8"))
102
+ reply = _chat_reply(data.get("messages", []))
103
+ except Exception as e:
104
+ reply = "出错了:" + str(e)
105
+ body = _body_str(_completion_obj(reply))
106
+ start_response("200 OK", [
107
+ ("Content-Type", "application/json; charset=utf-8"),
108
+ ("Content-Length", str(len(body))),
109
+ ])
110
+ return [body]
111
+ body = b'{"error":"not found"}'
112
+ start_response("404 Not Found", [
113
+ ("Content-Type", "application/json"),
114
+ ("Content-Length", str(len(body))),
115
+ ])
116
+ return [body]
117
+
118
+
119
+ def main():
120
+ port = int(os.environ.get("PORT", "8765"))
121
+ print(f"[door-core v2 API] http://localhost:{port}/v1 (模型名 door-core)")
122
+ httpd = ThreadingHTTPServer(("0.0.0.0", port), Handler)
123
+ httpd.serve_forever()
124
+
125
+
126
+ if __name__ == "__main__":
127
+ main()
@@ -0,0 +1,57 @@
1
+ # door-core v2 —— 命令行聊天(训练并从 .txt 语料加载,结果存 model.pkl)
2
+ import os
3
+ import sys
4
+ import json
5
+ import importlib.resources as _resources
6
+
7
+ from doorcore.core import DoorCore
8
+
9
+
10
+ def _pkg_path(*parts):
11
+ try:
12
+ return str(_resources.files("doorcore").joinpath(*parts))
13
+ except Exception:
14
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)), *parts)
15
+
16
+
17
+ def load_config():
18
+ p = _pkg_path("data", "config.json")
19
+ if os.path.exists(p):
20
+ try:
21
+ return json.load(open(p, encoding="utf-8"))
22
+ except Exception:
23
+ pass
24
+ return {"n": 3}
25
+
26
+
27
+ def main():
28
+ cfg = load_config()
29
+ n = int(cfg.get("n", 3))
30
+ ai = DoorCore(n=n)
31
+
32
+ force = "--retrain" in sys.argv
33
+ status = ai.load_or_train(force=force)
34
+ print(f"[door-core v2] 模型: {status} | 知识条目: {len(ai.kb)} | "
35
+ f"马尔可夫节点: {len(ai.model)}")
36
+ print("聊天开始(/retrain 重新训练, /quit 退出)")
37
+
38
+ while True:
39
+ try:
40
+ u = input("你> ").strip()
41
+ except (EOFError, KeyboardInterrupt):
42
+ print()
43
+ break
44
+ if not u:
45
+ continue
46
+ if u in ("/quit", "/exit"):
47
+ break
48
+ if u == "/retrain":
49
+ ai.load_corpus()
50
+ ai.save_model()
51
+ print(f"已重新训练:知识条目 {len(ai.kb)},马尔可夫节点 {len(ai.model)}")
52
+ continue
53
+ print("AI> " + ai.ask(u))
54
+
55
+
56
+ if __name__ == "__main__":
57
+ main()