feat: add OpenAI-compatible AI provider plugin with SSE streaming support

- Implemented the OpenAI-compatible AI provider plugin, including configuration, chat, and chat_stream functionalities.
- Added support for SSE streaming and tool calls.
- Integrated Boost.JSON for JSON handling.
- Created CMake configuration for the plugin.
- Added error handling and logging throughout the plugin.
This commit is contained in:
2026-05-31 05:37:04 +08:00
parent f6cb51b40a
commit ba7382db2a
61 changed files with 163 additions and 147 deletions

View File

@@ -11,16 +11,19 @@ option(DSTALK_BUILD_GUI "Build the SDL3 GUI frontend" OFF)
option(DSTALK_BUILD_WEB "Build the web UI frontend" OFF) option(DSTALK_BUILD_WEB "Build the web UI frontend" OFF)
option(DSTALK_BUILD_TESTS "Build dstalk tests" ON) option(DSTALK_BUILD_TESTS "Build dstalk tests" ON)
add_subdirectory(dstalk-core) add_subdirectory(dstalk_core)
add_subdirectory(dstalk-cli) add_subdirectory(dstalk_cli)
add_subdirectory(plugins) # 插件按依赖层级分三个目录 / Plugins split into three directories by dependency tier
add_subdirectory(plugins_base)
add_subdirectory(plugins_middle)
add_subdirectory(plugins_upper)
if(DSTALK_BUILD_GUI) if(DSTALK_BUILD_GUI)
add_subdirectory(dstalk-gui) add_subdirectory(dstalk_gui)
endif() endif()
if(DSTALK_BUILD_WEB) if(DSTALK_BUILD_WEB)
add_subdirectory(dstalk-web) add_subdirectory(dstalk_web)
endif() endif()
if(DSTALK_BUILD_TESTS) if(DSTALK_BUILD_TESTS)

View File

@@ -16,7 +16,7 @@ dstalk 是一款 AI 编程助手命令行工具, 通过调用大模型在终端
┌───────────────────────────────────────────────────────────┐ ┌───────────────────────────────────────────────────────────┐
│ 前端层 (Frontends) │ │ 前端层 (Frontends) │
│ ┌──────────────────┐ ┌──────────────────────────┐ │ │ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ dstalk-cli │ │ dstalk-gui │ │ │ │ dstalk_cli │ │ dstalk_gui │ │
│ │ ANSI 终端 UI │ │ SDL3 图形化 UI │ │ │ │ ANSI 终端 UI │ │ SDL3 图形化 UI │ │
│ └────────┬─────────┘ └─────────────┬─────────────┘ │ │ └────────┬─────────┘ └─────────────┬─────────────┘ │
│ └──────────────┬───────────────┘ │ │ └──────────────┬───────────────┘ │
@@ -24,7 +24,7 @@ dstalk 是一款 AI 编程助手命令行工具, 通过调用大模型在终端
└──────────────────────────┼─────────────────────────────────┘ └──────────────────────────┼─────────────────────────────────┘
┌──────────────────────────▼─────────────────────────────────┐ ┌──────────────────────────▼─────────────────────────────────┐
│ 核心层 (dstalk-core.dll) — 插件宿主 │ │ 核心层 (dstalk_core.dll) — 插件宿主 │
│ ┌──────────────────────────────────────────────────────┐ │ │ ┌──────────────────────────────────────────────────────┐ │
│ │ Host: 插件加载 · 服务注册 · 事件总线 · 配置管理 │ │ │ │ Host: 插件加载 · 服务注册 · 事件总线 · 配置管理 │ │
│ └──────────────────────────────────────────────────────┘ │ │ └──────────────────────────────────────────────────────┘ │
@@ -33,15 +33,15 @@ dstalk 是一款 AI 编程助手命令行工具, 通过调用大模型在终端
│ │ (ai) │ │ (ai) │ │ (http) │ │ 客户端 │ │ │ │ (ai) │ │ (ai) │ │ (http) │ │ 客户端 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ session │ │ context │ │ file-io │ │ tools │ │ │ │ session │ │ context │ │ file_io │ │ tools │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
``` ```
- **dstalk-core** —— C11/C++20 插件化宿主 DLL, 负责插件加载、服务注册、事件总线、配置管理 - **dstalk_core** —— C11/C++20 插件化宿主 DLL, 负责插件加载、服务注册、事件总线、配置管理
- **dstalk-cli** —— 命令行前端, ANSI 终端 UI - **dstalk_cli** —— 命令行前端, ANSI 终端 UI
- **dstalk-gui** —— 图形化前端, SDL3 跨平台窗口 - **dstalk_gui** —— 图形化前端, SDL3 跨平台窗口
- **plugins/** —— 9 个功能插件, 编译为独立 DLL, 通过 C ABI 动态注册服务 - **plugins_base/、plugins_middle/、plugins_upper/** —— 9 个功能插件按三层架构分布, 编译为独立 DLL, 通过 C ABI 动态注册服务
核心与界面完全解耦, 可编写自己的前端或把 AI 能力嵌入到现有工具中。 核心与界面完全解耦, 可编写自己的前端或把 AI 能力嵌入到现有工具中。
@@ -65,7 +65,7 @@ dstalk 是一款 AI 编程助手命令行工具, 通过调用大模型在终端
cd tools && setup.bat # 1. 安装工具链 (CMake / Ninja / LLVM / Conan2) cd tools && setup.bat # 1. 安装工具链 (CMake / Ninja / LLVM / Conan2)
build.bat # 2. 编译 build.bat # 2. 编译
# 3. 创建 config.toml # 见教程: docs/tutorial/quick-start.md # 3. 创建 config.toml # 见教程: docs/tutorial/quick-start.md
build/dstalk-cli/dstalk-cli.exe # 4. 运行 build/dstalk_cli/dstalk_cli.exe # 4. 运行
# 5. 输入自然语言 # "帮我写一个 C 程序" # 5. 输入自然语言 # "帮我写一个 C 程序"
``` ```

View File

@@ -140,7 +140,7 @@ if !errorlevel! neq 0 (
echo. echo.
echo ============================================ echo ============================================
echo 编译成功! echo 编译成功!
echo build\dstalk-core\dstalk.dll echo build\bin\dstalk.dll
echo build\dstalk-cli\dstalk-cli.exe echo build\bin\dstalk_cli.exe
echo ============================================ echo ============================================
pause pause

View File

@@ -13,7 +13,7 @@ dstalk 选择的不是单体架构,而是**以 C ABI 为边界的插件架构*
2. **能力会增长**。LSP 集成、文件管理、会话持久化、工具调用——这些能力不是 CLI 启动时必须加载的。使用者可能只需要聊天,不需要 LSP。插件架构让能力按需加载启动更快内存更省。 2. **能力会增长**。LSP 集成、文件管理、会话持久化、工具调用——这些能力不是 CLI 启动时必须加载的。使用者可能只需要聊天,不需要 LSP。插件架构让能力按需加载启动更快内存更省。
3. **插件作者不是核心团队**。第三方应该能用自己的编译器、自己的 C++ 标准库版本编写插件,而不必须链接 dstalk-core 的静态库。这要求 ABI 稳定。C ABI 是唯一具有跨编译器二进制兼容性的选择。 3. **插件作者不是核心团队**。第三方应该能用自己的编译器、自己的 C++ 标准库版本编写插件,而不必须链接 dstalk_core 的静态库。这要求 ABI 稳定。C ABI 是唯一具有跨编译器二进制兼容性的选择。
**一句话心智模型**: 不要想象一个胖二进制把所有功能静态链接在一起;想象一个内核 (host) + 一圈可插拔的服务单元 (plugin),内核只负责编排,不负责实现。 **一句话心智模型**: 不要想象一个胖二进制把所有功能静态链接在一起;想象一个内核 (host) + 一圈可插拔的服务单元 (plugin),内核只负责编排,不负责实现。
@@ -29,7 +29,7 @@ dstalk 的架构由 3 层组成。从上到下看,每一层依赖下一层提
│ 实现具体能力AI 聊天、文件读写、LSP... │ │ 实现具体能力AI 聊天、文件读写、LSP... │
│ 通过服务注册表向其他插件暴露自己的功能 │ │ 通过服务注册表向其他插件暴露自己的功能 │
├────────────────────────────────────────────┤ ├────────────────────────────────────────────┤
│ Host (dstalk-core) │ │ Host (dstalk_core) │
│ 拥有事件总线、服务注册表、插件加载器、配置 │ │ 拥有事件总线、服务注册表、插件加载器、配置 │
│ 提供一个 dstalk_host_api_t 接口给所有插件 │ │ 提供一个 dstalk_host_api_t 接口给所有插件 │
├────────────────────────────────────────────┤ ├────────────────────────────────────────────┤
@@ -48,7 +48,7 @@ Host 拥有四样东西:
- **配置存储 (ConfigStore)**: 管理 `config.toml` 的加载和键值查询。 - **配置存储 (ConfigStore)**: 管理 `config.toml` 的加载和键值查询。
- **事件总线 (EventBus)**: 插件间松耦合通信的唯一通道。 - **事件总线 (EventBus)**: 插件间松耦合通信的唯一通道。
- **服务注册表 (ServiceRegistry)**: 按名称 + 版本号存储和查找服务 vtable。 - **服务注册表 (ServiceRegistry)**: 按名称 + 版本号存储和查找服务 vtable。
- **插件加载器 (PluginLoader)**: 扫描 `plugins/` 目录、加载 DLL、按依赖拓扑排序后调用初始化。 - **插件加载器 (PluginLoader)**: 扫描 `plugins_base/``plugins_middle/``plugins_upper/` 三层目录、加载 DLL、按依赖拓扑排序后调用初始化。
Host 启动时,按严格顺序执行: Host 启动时,按严格顺序执行:
1. 分配上述四个组件。 1. 分配上述四个组件。

View File

@@ -52,7 +52,7 @@ Host 调用 `load_plugin(path)`,由 `PluginLoader` 执行:
{ "http", "config", NULL } { "http", "config", NULL }
``` ```
依赖名称与目标插件的 `info->name` 字段匹配(如 `"file-io"` 不是 DLL 的文件名),因此依赖声明和插件命名必须一致。 依赖名称与目标插件的 `info->name` 字段匹配(如 `"file_io"` 不是 DLL 的文件名),因此依赖声明和插件命名必须一致。
--- ---

View File

@@ -18,7 +18,7 @@
## 文件清单 ## 文件清单
### 1. `plugins/openai/src/openai_plugin.cpp` -- 安全 ### 1. `plugins_upper/openai/src/openai_plugin.cpp` -- 安全
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|
@@ -31,7 +31,7 @@
**parse_response() 错误路径 (行 135-151)**: HTTP 错误响应体仅用于提取 JSON `error.message` 字段放入 `r.error`,不会输出到日志。原始 response_body 在解析后被 `g_host->free()` 释放。 **parse_response() 错误路径 (行 135-151)**: HTTP 错误响应体仅用于提取 JSON `error.message` 字段放入 `r.error`,不会输出到日志。原始 response_body 在解析后被 `g_host->free()` 释放。
### 2. `plugins/anthropic/src/anthropic_plugin.cpp` -- 安全 ### 2. `plugins_upper/anthropic/src/anthropic_plugin.cpp` -- 安全
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|
@@ -42,7 +42,7 @@
**build_headers_json() (行 59-65)**: 构建 `{"x-api-key":"<key>","anthropic-version":"2023-06-01"}` 仅用于 HTTP 请求,不经过日志路径。 **build_headers_json() (行 59-65)**: 构建 `{"x-api-key":"<key>","anthropic-version":"2023-06-01"}` 仅用于 HTTP 请求,不经过日志路径。
### 3. `plugins/network/src/network_plugin.cpp` -- 安全 ### 3. `plugins_middle/network/src/network_plugin.cpp` -- 安全
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|
@@ -52,7 +52,7 @@
**do_post_stream() 异常路径 (行 280-282)**: `catch (std::exception& e)``e.what()` 赋值给 `result_body`。Beast/ASIO 异常消息为 OS 级别错误描述(如 "Connection refused"),不含 HTTP header/body 内容。 **do_post_stream() 异常路径 (行 280-282)**: `catch (std::exception& e)``e.what()` 赋值给 `result_body`。Beast/ASIO 异常消息为 OS 级别错误描述(如 "Connection refused"),不含 HTTP header/body 内容。
### 4. `plugins/config/src/config_plugin.cpp` -- 安全 ### 4. `plugins_base/config/src/config_plugin.cpp` -- 安全
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|
@@ -60,7 +60,7 @@
ConfigStore 仅提供 get/set/load_file无日志输出。 ConfigStore 仅提供 get/set/load_file无日志输出。
### 5. `dstalk-core/src/host.cpp` -- 基础设施(不动) ### 5. `dstalk_core/src/host.cpp` -- 基础设施(不动)
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|
@@ -68,13 +68,13 @@ ConfigStore 仅提供 get/set/load_file无日志输出。
该文件是日志基础设施 (`host_log_impl`),仅负责格式化输出。安全性依赖于调用方不传敏感数据(本审计已确认所有调用方均安全)。按 W9.3 禁忌不修改此文件。 该文件是日志基础设施 (`host_log_impl`),仅负责格式化输出。安全性依赖于调用方不传敏感数据(本审计已确认所有调用方均安全)。按 W9.3 禁忌不修改此文件。
### 6. `dstalk-core/src/plugin_loader.cpp` -- 安全 ### 6. `dstalk_core/src/plugin_loader.cpp` -- 安全
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|
| -- | 无任何日志调用 | -- | 无 | | -- | 无任何日志调用 | -- | 无 |
### 7. `plugins/session/src/session_plugin.cpp` -- 安全 ### 7. `plugins_middle/session/src/session_plugin.cpp` -- 安全
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|
@@ -82,7 +82,7 @@ ConfigStore 仅提供 get/set/load_file无日志输出。
该插件处理消息内容role/content但不记录任何消息数据到日志。 该插件处理消息内容role/content但不记录任何消息数据到日志。
### 8. `plugins/lsp/src/lsp_plugin.cpp` -- 低风险 ### 8. `plugins_base/lsp/src/lsp_plugin.cpp` -- 低风险
| 行号 | 调用 | 内容 | 风险 | | 行号 | 调用 | 内容 | 风险 |
|------|------|------|------| |------|------|------|------|

View File

@@ -32,8 +32,8 @@ build.bat
``` ```
编译产物输出到 `build/` 目录。核心产物: 编译产物输出到 `build/` 目录。核心产物:
- `build/dstalk-core/dstalk.dll` —— 核心 DLL - `build/dstalk_core/dstalk.dll` —— 核心 DLL
- `build/dstalk-cli/dstalk-cli.exe` —— 命令行前端 - `build/dstalk_cli/dstalk_cli.exe` —— 命令行前端
- `build/plugins/*.dll` —— 功能插件 - `build/plugins/*.dll` —— 功能插件
--- ---
@@ -69,10 +69,10 @@ api.model = "gpt-4o"
--- ---
## 4. 运行 dstalk-cli ## 4. 运行 dstalk_cli
```bash ```bash
build/dstalk-cli/dstalk-cli.exe build/dstalk_cli/dstalk_cli.exe
``` ```
启动后显示欢迎横幅: 启动后显示欢迎横幅:

View File

@@ -1,17 +1,17 @@
# ============================================================ # ============================================================
# dstalk-cli 命令行前端 (ANSI 转义码) # dstalk_cli 命令行前端 (ANSI 转义码)
# ============================================================ # ============================================================
add_executable(dstalk-cli add_executable(dstalk_cli
src/main.cpp src/main.cpp
) )
set_target_properties(dstalk-cli PROPERTIES set_target_properties(dstalk_cli PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
) )
find_package(Boost REQUIRED CONFIG) find_package(Boost REQUIRED CONFIG)
target_link_libraries(dstalk-cli target_link_libraries(dstalk_cli
PRIVATE dstalk boost::boost dstalk_boost_config PRIVATE dstalk boost::boost dstalk_boost_config
) )

View File

@@ -480,7 +480,7 @@ int main(int argc, char* argv[])
} }
} }
// 初始化主机(加载配置 + 自动扫描 plugins/ 目录加载插件) / Init host: load config + auto-scan plugins/ directory // 初始化主机(加载配置 + 自动扫描 plugins_base/middle/upper 目录加载插件) / Init host: load config + auto-scan plugins_base/middle/upper directories
if (dstalk_init(config_path) != 0) { if (dstalk_init(config_path) != 0) {
std::fprintf(stderr, CLR_RED "[dstalk] 初始化失败\n" CLR_RESET); std::fprintf(stderr, CLR_RED "[dstalk] 初始化失败\n" CLR_RESET);
return EXIT_CONFIG; return EXIT_CONFIG;

View File

@@ -1,5 +1,5 @@
# ============================================================ # ============================================================
# dstalk-core DLL (插件宿主) # dstalk_core DLL (插件宿主)
# : / / 线 / # : / / 线 /
# ============================================================ # ============================================================

View File

@@ -5,7 +5,7 @@
*/ */
#include "config_store.hpp" #include "config_store.hpp"
#include "../../plugins/config/include/toml_parse.h" #include "../../plugins_base/config/include/toml_parse.h"
#include <cstdio> #include <cstdio>
#include <fstream> #include <fstream>

View File

@@ -225,14 +225,22 @@ DSTALK_API int dstalk_init(const char* config_path)
} }
} }
// 扫描插件目录 / Scan plugin directory // 扫描插件目录 / Scan plugin directories
const char* plugin_dir = g_config->get("plugin_dir"); // 优先扫描三级插件目录,回退到单一 plugins/ 目录(构建输出)
if (!plugin_dir) plugin_dir = "plugins"; // Prefer three-tier plugin dirs, fall back to single plugins/ dir (build output)
int loaded = load_plugins_from_directory(plugin_dir); const char* dirs[] = {
"plugins_base", "plugins_middle", "plugins_upper",
"../plugins_base", "../plugins_middle", "../plugins_upper",
"plugins", "../plugins",
nullptr
};
int loaded = 0;
for (int i = 0; dirs[i]; ++i) {
int n = load_plugins_from_directory(dirs[i]);
if (n > 0) loaded += n;
}
if (loaded <= 0) { if (loaded <= 0) {
host_log(DSTALK_LOG_WARN, host_log(DSTALK_LOG_WARN, "No plugins found in any plugin directory");
"No plugins found in '%s', trying '../plugins'", plugin_dir);
loaded = load_plugins_from_directory("../plugins");
} }
// 初始化所有插件 / Initialize all plugins // 初始化所有插件 / Initialize all plugins

View File

@@ -78,7 +78,9 @@ int PluginLoader::load_plugin(const char* path)
has_dotdot = true; has_dotdot = true;
break; break;
} }
if (comp == "plugins") { std::string comp_str = comp.string();
if (comp_str == "plugins" ||
comp_str.substr(0, 8) == "plugins_") {
in_plugins_dir = true; in_plugins_dir = true;
} }
} }
@@ -91,8 +93,8 @@ int PluginLoader::load_plugin(const char* path)
return -1; return -1;
} }
// 目录约束:必须位于 'plugins' 目录下或为纯文件名 // 目录约束:必须位于 'plugins' 或 'plugins_*' 目录下或为纯文件名
// Directory constraint: must be under a 'plugins' directory or be a plain filename // Directory constraint: must be under a 'plugins' or 'plugins_*' directory, or be a plain filename
if (!in_plugins_dir && p.has_parent_path()) { if (!in_plugins_dir && p.has_parent_path()) {
if (host_api_) { if (host_api_) {
host_api_->log(DSTALK_LOG_ERROR, host_api_->log(DSTALK_LOG_ERROR,

View File

@@ -1,15 +1,15 @@
# ============================================================ # ============================================================
# dstalk-gui 图形化前端 (SDL3) # dstalk_gui 图形化前端 (SDL3)
# ============================================================ # ============================================================
# DSTALK_BUILD_GUI=ON deps/conanfile.txt sdl # DSTALK_BUILD_GUI=ON deps/conanfile.txt sdl
find_package(SDL3 REQUIRED CONFIG) find_package(SDL3 REQUIRED CONFIG)
add_executable(dstalk-gui add_executable(dstalk_gui
src/main.cpp src/main.cpp
) )
target_link_libraries(dstalk-gui target_link_libraries(dstalk_gui
PRIVATE PRIVATE
dstalk dstalk
SDL3::SDL3 SDL3::SDL3

View File

@@ -1,20 +1,20 @@
# ============================================================ # ============================================================
# dstalk-web Web / Web frontend (Boost.Beast HTTP + SSE) # dstalk_web Web / Web frontend (Boost.Beast HTTP + SSE)
# ============================================================ # ============================================================
find_package(Boost REQUIRED CONFIG) find_package(Boost REQUIRED CONFIG)
add_executable(dstalk-web add_executable(dstalk_web
src/main.cpp src/main.cpp
) )
set_target_properties(dstalk-web PROPERTIES set_target_properties(dstalk_web PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
) )
target_compile_features(dstalk-web PRIVATE cxx_std_20) target_compile_features(dstalk_web PRIVATE cxx_std_20)
target_link_libraries(dstalk-web target_link_libraries(dstalk_web
PRIVATE PRIVATE
dstalk dstalk
boost::boost boost::boost
@@ -23,5 +23,5 @@ target_link_libraries(dstalk-web
# Windows: Boost.Asio Winsock / Boost.Asio requires Winsock # Windows: Boost.Asio Winsock / Boost.Asio requires Winsock
if(WIN32) if(WIN32)
target_link_libraries(dstalk-web PRIVATE ws2_32) target_link_libraries(dstalk_web PRIVATE ws2_32)
endif() endif()

View File

@@ -1,7 +1,7 @@
/* /*
* @file main.cpp * @file main.cpp
* @brief Boost.Beast HTTP server frontend for dstalk-web: SSE streaming chat, embedded web UI, CORS support. * @brief Boost.Beast HTTP server frontend for dstalk_web: SSE streaming chat, embedded web UI, CORS support.
* dstalk-web Boost.Beast HTTP SSE CORS * dstalk_web Boost.Beast HTTP SSE CORS
* Copyright (c) 2026 dstalk contributors. GPLv3. * Copyright (c) 2026 dstalk contributors. GPLv3.
*/ */
@@ -423,18 +423,18 @@ public:
beast::error_code ec; beast::error_code ec;
acceptor_.open(ep.protocol(), ec); acceptor_.open(ep.protocol(), ec);
if (ec) { if (ec) {
std::fprintf(stderr, "[dstalk-web] acceptor.open: %s\n", ec.message().c_str()); std::fprintf(stderr, "[dstalk_web] acceptor.open: %s\n", ec.message().c_str());
return; return;
} }
acceptor_.set_option(asio::socket_base::reuse_address(true), ec); acceptor_.set_option(asio::socket_base::reuse_address(true), ec);
acceptor_.bind(ep, ec); acceptor_.bind(ep, ec);
if (ec) { if (ec) {
std::fprintf(stderr, "[dstalk-web] acceptor.bind: %s\n", ec.message().c_str()); std::fprintf(stderr, "[dstalk_web] acceptor.bind: %s\n", ec.message().c_str());
return; return;
} }
acceptor_.listen(asio::socket_base::max_listen_connections, ec); acceptor_.listen(asio::socket_base::max_listen_connections, ec);
if (ec) { if (ec) {
std::fprintf(stderr, "[dstalk-web] acceptor.listen: %s\n", ec.message().c_str()); std::fprintf(stderr, "[dstalk_web] acceptor.listen: %s\n", ec.message().c_str());
return; return;
} }
} }
@@ -499,9 +499,9 @@ int main(int argc, char* argv[])
} }
} }
// 初始化 dstalk 主机(加载配置 + 自动扫描 plugins/ 目录) / Init dstalk host (load config + auto-scan plugins/) // 初始化 dstalk 主机(加载配置 + 自动扫描 plugins_base/middle/upper 目录) / Init dstalk host (load config + auto-scan plugins_base/middle/upper)
if (dstalk_init(config_path) != 0) { if (dstalk_init(config_path) != 0) {
std::fprintf(stderr, "[dstalk-web] dstalk_init failed\n"); std::fprintf(stderr, "[dstalk_web] dstalk_init failed\n");
return 3; return 3;
} }
@@ -512,10 +512,10 @@ int main(int argc, char* argv[])
g_session = static_cast<const dstalk_session_service_t*>(dstalk_service_query("session", 1)); g_session = static_cast<const dstalk_session_service_t*>(dstalk_service_query("session", 1));
if (!g_ai) { if (!g_ai) {
std::fprintf(stderr, "[dstalk-web] AI service not found (check plugins directory)\n"); std::fprintf(stderr, "[dstalk_web] AI service not found (check plugins directory)\n");
} }
if (!g_session) { if (!g_session) {
std::fprintf(stderr, "[dstalk-web] Session service not found\n"); std::fprintf(stderr, "[dstalk_web] Session service not found\n");
} }
// 从配置自动加载 AI 设置 / Auto-load AI settings from config // 从配置自动加载 AI 设置 / Auto-load AI settings from config
@@ -546,8 +546,8 @@ int main(int argc, char* argv[])
listener.run(); listener.run();
// 打印启动信息 / Print startup message // 打印启动信息 / Print startup message
std::printf("[dstalk-web] running at http://%s:%u\n", web_host, web_port); std::printf("[dstalk_web] running at http://%s:%u\n", web_host, web_port);
std::printf("[dstalk-web] Press Ctrl+C to stop\n"); std::printf("[dstalk_web] Press Ctrl+C to stop\n");
// 运行事件循环(阻塞直到 g_ioc->stop() 被信号处理函数调用) / Run event loop (blocks until g_ioc->stop() called by signal handler) // 运行事件循环(阻塞直到 g_ioc->stop() 被信号处理函数调用) / Run event loop (blocks until g_ioc->stop() called by signal handler)
ioc.run(); ioc.run();
@@ -556,6 +556,6 @@ int main(int argc, char* argv[])
g_ioc = nullptr; g_ioc = nullptr;
dstalk_shutdown(); dstalk_shutdown();
std::printf("[dstalk-web] stopped\n"); std::printf("[dstalk_web] stopped\n");
return 0; return 0;
} }

View File

@@ -1,7 +1,7 @@
/* /*
* @file web_ui.hpp * @file web_ui.hpp
* @brief Embedded HTML/JS chat UI served by dstalk-web. * @brief Embedded HTML/JS chat UI served by dstalk_web.
* HTML/JS dstalk-web * HTML/JS dstalk_web
* Copyright (c) 2026 dstalk contributors. GPLv3. * Copyright (c) 2026 dstalk contributors. GPLv3.
*/ */

View File

@@ -1,18 +0,0 @@
# ============================================================
# 插件目录 — 所有功能插件 / Plugin directory — all functional plugins
# ============================================================
# 基础插件(无依赖) / Base plugins (no dependencies)
add_subdirectory(config)
add_subdirectory(file-io)
add_subdirectory(lsp)
# 依赖基础插件的插件 / Plugins depending on base plugins only
add_subdirectory(network) # 依赖 config / depends on config
add_subdirectory(session) # 依赖 file_io / depends on file_io
add_subdirectory(tools) # 依赖 file_io / depends on file_io
# 依赖其他插件的插件 / Plugins depending on non-base plugins
add_subdirectory(context) # 依赖 session / depends on session
add_subdirectory(openai) # 依赖 http, config / depends on http, config
add_subdirectory(anthropic) # 依赖 http, config / depends on http, config

View File

@@ -1,9 +0,0 @@
add_library(plugin-file-io SHARED src/file_io_plugin.cpp)
target_link_libraries(plugin-file-io PRIVATE dstalk)
set_target_properties(plugin-file-io PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
)

View File

@@ -0,0 +1,7 @@
# ============================================================
# 基础插件(无依赖) / Base plugins (no dependencies)
# ============================================================
add_subdirectory(config)
add_subdirectory(file_io)
add_subdirectory(lsp)

View File

@@ -0,0 +1,9 @@
add_library(plugin-file_io SHARED src/file_io_plugin.cpp)
target_link_libraries(plugin-file_io PRIVATE dstalk)
set_target_properties(plugin-file_io PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
)

View File

@@ -91,7 +91,7 @@ static void on_shutdown() {
} }
static dstalk_plugin_info_t g_info = { static dstalk_plugin_info_t g_info = {
"file-io", // name 名称 "file_io", // name 名称
"1.0.0", // version 版本 "1.0.0", // version 版本
"Basic file I/O service", // description 描述 "Basic file I/O service", // description 描述
DSTALK_API_VERSION, // api_version DSTALK_API_VERSION, // api_version

View File

@@ -0,0 +1,7 @@
# ============================================================
# 依赖基础插件的插件 / Plugins depending on base plugins only
# ============================================================
add_subdirectory(network) # 依赖 config / depends on config
add_subdirectory(session) # 依赖 file_io / depends on file_io
add_subdirectory(tools) # 依赖 file_io / depends on file_io

View File

@@ -89,7 +89,7 @@ static std::unordered_map<std::string, std::string> parse_headers_json(const cha
} }
// ============================================================ // ============================================================
// HTTP 客户端实现(改编自 dstalk-core HttpClient / HTTP Client implementation (adapted from dstalk-core HttpClient) // HTTP 客户端实现(改编自 dstalk_core HttpClient / HTTP Client implementation (adapted from dstalk_core HttpClient)
// ============================================================ // ============================================================
struct HttpClientCtx { struct HttpClientCtx {
asio::io_context ioc; asio::io_context ioc;

View File

@@ -0,0 +1,7 @@
# ============================================================
# 依赖其他插件的插件 / Plugins depending on non-base plugins
# ============================================================
add_subdirectory(context) # 依赖 session / depends on session
add_subdirectory(openai) # 依赖 http, config / depends on http, config
add_subdirectory(anthropic) # 依赖 http, config / depends on http, config

View File

@@ -11,10 +11,10 @@ add_library(plugin-openai SHARED
target_link_libraries(plugin-openai PRIVATE dstalk) target_link_libraries(plugin-openai PRIVATE dstalk)
# Boost.JSON (header-only) # Boost.JSON (header-only)
find_package(Boost REQUIRED CONFIG)
target_link_libraries(plugin-openai PRIVATE boost::boost dstalk_boost_config) target_link_libraries(plugin-openai PRIVATE boost::boost dstalk_boost_config)
set_target_properties(plugin-openai PROPERTIES set_target_properties(plugin-openai PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
) )

View File

@@ -18,11 +18,11 @@ add_test(NAME dstalk-smoke-test COMMAND dstalk-smoke-test)
add_executable(dstalk-host-api-test add_executable(dstalk-host-api-test
host_api_test.cpp host_api_test.cpp
${CMAKE_SOURCE_DIR}/dstalk-core/src/service_registry.cpp ${CMAKE_SOURCE_DIR}/dstalk_core/src/service_registry.cpp
) )
target_include_directories(dstalk-host-api-test target_include_directories(dstalk-host-api-test
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/src PRIVATE ${CMAKE_SOURCE_DIR}/dstalk_core/src
) )
target_compile_features(dstalk-host-api-test target_compile_features(dstalk-host-api-test
@@ -41,11 +41,11 @@ add_test(NAME dstalk-host-api-test COMMAND dstalk-host-api-test)
add_executable(dstalk-event-bus-test add_executable(dstalk-event-bus-test
event_bus_test.cpp event_bus_test.cpp
${CMAKE_SOURCE_DIR}/dstalk-core/src/event_bus.cpp ${CMAKE_SOURCE_DIR}/dstalk_core/src/event_bus.cpp
) )
target_include_directories(dstalk-event-bus-test target_include_directories(dstalk-event-bus-test
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/src PRIVATE ${CMAKE_SOURCE_DIR}/dstalk_core/src
) )
target_compile_features(dstalk-event-bus-test target_compile_features(dstalk-event-bus-test
@@ -60,11 +60,11 @@ add_test(NAME dstalk-event-bus-test COMMAND dstalk-event-bus-test)
add_executable(dstalk-service-registry-test add_executable(dstalk-service-registry-test
service_registry_test.cpp service_registry_test.cpp
${CMAKE_SOURCE_DIR}/dstalk-core/src/service_registry.cpp ${CMAKE_SOURCE_DIR}/dstalk_core/src/service_registry.cpp
) )
target_include_directories(dstalk-service-registry-test target_include_directories(dstalk-service-registry-test
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/src PRIVATE ${CMAKE_SOURCE_DIR}/dstalk_core/src
) )
target_compile_features(dstalk-service-registry-test target_compile_features(dstalk-service-registry-test
@@ -95,12 +95,12 @@ add_test(NAME dstalk-context-plugin-test COMMAND dstalk-context-plugin-test)
add_executable(dstalk-plugin-loader-test add_executable(dstalk-plugin-loader-test
plugin_loader_test.cpp plugin_loader_test.cpp
${CMAKE_SOURCE_DIR}/dstalk-core/src/plugin_loader.cpp ${CMAKE_SOURCE_DIR}/dstalk_core/src/plugin_loader.cpp
${CMAKE_SOURCE_DIR}/dstalk-core/src/boost_json.cpp ${CMAKE_SOURCE_DIR}/dstalk_core/src/boost_json.cpp
) )
target_include_directories(dstalk-plugin-loader-test target_include_directories(dstalk-plugin-loader-test
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/src PRIVATE ${CMAKE_SOURCE_DIR}/dstalk_core/src
) )
target_compile_features(dstalk-plugin-loader-test target_compile_features(dstalk-plugin-loader-test
@@ -134,7 +134,7 @@ add_executable(dstalk-anthropic-plugin-test
) )
target_include_directories(dstalk-anthropic-plugin-test target_include_directories(dstalk-anthropic-plugin-test
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/include PRIVATE ${CMAKE_SOURCE_DIR}/dstalk_core/include
) )
target_compile_definitions(dstalk-anthropic-plugin-test target_compile_definitions(dstalk-anthropic-plugin-test
@@ -161,7 +161,7 @@ add_executable(dstalk-openai-plugin-test
) )
target_include_directories(dstalk-openai-plugin-test target_include_directories(dstalk-openai-plugin-test
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/include PRIVATE ${CMAKE_SOURCE_DIR}/dstalk_core/include
) )
target_compile_definitions(dstalk-openai-plugin-test target_compile_definitions(dstalk-openai-plugin-test
@@ -190,7 +190,7 @@ add_executable(dstalk-network-plugin-test
) )
target_include_directories(dstalk-network-plugin-test target_include_directories(dstalk-network-plugin-test
PRIVATE ${CMAKE_SOURCE_DIR}/dstalk-core/include PRIVATE ${CMAKE_SOURCE_DIR}/dstalk_core/include
) )
target_compile_definitions(dstalk-network-plugin-test target_compile_definitions(dstalk-network-plugin-test

View File

@@ -9,7 +9,7 @@
*/ */
#define BOOST_JSON_HEADER_ONLY #define BOOST_JSON_HEADER_ONLY
#define BOOST_ALL_NO_LIB #define BOOST_ALL_NO_LIB
#include "../plugins/anthropic/src/anthropic_plugin.cpp" #include "../plugins_upper/anthropic/src/anthropic_plugin.cpp"
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>

View File

@@ -9,7 +9,7 @@
*/ */
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#define BOOST_ASIO_DISABLE_STD_TO_ADDRESS #define BOOST_ASIO_DISABLE_STD_TO_ADDRESS
#include "../plugins/network/src/network_plugin.cpp" #include "../plugins_middle/network/src/network_plugin.cpp"
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>

View File

@@ -9,7 +9,7 @@
*/ */
#define BOOST_JSON_HEADER_ONLY #define BOOST_JSON_HEADER_ONLY
#define BOOST_ALL_NO_LIB #define BOOST_ALL_NO_LIB
#include "../plugins/openai/src/openai_plugin.cpp" #include "../plugins_upper/openai/src/openai_plugin.cpp"
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>

View File

@@ -82,8 +82,8 @@ static void reset_log_state() {
g_last_log_msg[0] = '\0'; g_last_log_msg[0] = '\0';
} }
// Get the absolute path to the build output plugins/ directory // Get the absolute path to the plugin tier directory (plugins_base / middle / upper)
// 获取构建输出 plugins/ 目录的绝对路径 // 获取插件层级目录 (plugins_base / middle / upper) 的绝对路径
static fs::path get_plugins_dir() { static fs::path get_plugins_dir() {
#ifdef DSTALK_TEST_PLUGINS_DIR #ifdef DSTALK_TEST_PLUGINS_DIR
return fs::path(DSTALK_TEST_PLUGINS_DIR); return fs::path(DSTALK_TEST_PLUGINS_DIR);
@@ -121,10 +121,10 @@ int main()
CHECK(loader.load_plugin("../plugins/test.dll") == -1, CHECK(loader.load_plugin("../plugins/test.dll") == -1,
"T1.3: ../ traversal rejected"); "T1.3: ../ traversal rejected");
// T1.4: 不在 plugins/ 目录下 / not under plugins/ dir // T1.4: 不在插件 tier 目录下 / not under plugin tier dir
auto tmp = fs::temp_directory_path() / "dstalk_test_no_plugins" / "test.dll"; auto tmp = fs::temp_directory_path() / "dstalk_test_no_plugins" / "test.dll";
CHECK(loader.load_plugin(tmp.string().c_str()) == -1, CHECK(loader.load_plugin(tmp.string().c_str()) == -1,
"T1.4: path not under plugins/ dir rejected"); "T1.4: path not under plugin tier dir rejected");
// T1.5: 路径中间的 .. 段 / .. segment in middle of path // T1.5: 路径中间的 .. 段 / .. segment in middle of path
CHECK(loader.load_plugin("plugins/../secret/test.dll") == -1, CHECK(loader.load_plugin("plugins/../secret/test.dll") == -1,
@@ -134,9 +134,9 @@ int main()
CHECK(loader.load_plugin("plugins/test") == -1, CHECK(loader.load_plugin("plugins/test") == -1,
"T1.6: no extension rejected"); "T1.6: no extension rejected");
// T1.7: 合法扩展名但不在 plugins/ 下 / valid extension but not under plugins/ // T1.7: 合法扩展名但不在插件 tier 目录下 / valid extension but not under plugin tier dir
CHECK(loader.load_plugin("/etc/someconfig.so") == -1, CHECK(loader.load_plugin("/etc/someconfig.so") == -1,
"T1.7: .so extension but not under plugins/ rejected"); "T1.7: .so extension but not under plugin tier dir rejected");
} }
// ======================================================================== // ========================================================================
@@ -149,7 +149,7 @@ int main()
fs::path plugins_dir = get_plugins_dir(); fs::path plugins_dir = get_plugins_dir();
fs::path dll_config = plugins_dir / "plugin-config.dll"; fs::path dll_config = plugins_dir / "plugin-config.dll";
fs::path dll_fileio = plugins_dir / "plugin-file-io.dll"; fs::path dll_fileio = plugins_dir / "plugin-file_io.dll";
bool have_plugins = fs::exists(dll_config) && fs::exists(dll_fileio); bool have_plugins = fs::exists(dll_config) && fs::exists(dll_fileio);
@@ -206,7 +206,7 @@ int main()
fs::path plugins_dir = get_plugins_dir(); fs::path plugins_dir = get_plugins_dir();
std::vector<fs::path> dlls; std::vector<fs::path> dlls;
for (auto name : {"plugin-config.dll", "plugin-file-io.dll", for (auto name : {"plugin-config.dll", "plugin-file_io.dll",
"plugin-context.dll", "plugin-session.dll"}) { "plugin-context.dll", "plugin-session.dll"}) {
fs::path p = plugins_dir / name; fs::path p = plugins_dir / name;
if (fs::exists(p)) dlls.push_back(p); if (fs::exists(p)) dlls.push_back(p);

View File

@@ -58,7 +58,7 @@ int main()
<< "model = \"gpt-4o\"\n"; << "model = \"gpt-4o\"\n";
} }
// 初始化主机(会自动扫描 plugins/ 加载插件)/ Init host (auto-scans plugins/ to load plugins) // 初始化主机(会自动扫描 plugins_base/middle/upper 加载插件)/ Init host (auto-scans plugins_base/middle/upper to load plugins)
if (dstalk_init(config_path.string().c_str()) != 0) { if (dstalk_init(config_path.string().c_str()) != 0) {
std::cerr << "dstalk_init failed\n"; std::cerr << "dstalk_init failed\n";
return 1; return 1;
@@ -112,7 +112,7 @@ int main()
return 1; return 1;
} }
} else { } else {
std::cerr << "[WARN] file_io service not found (plugin may not be in plugins/ dir)\n"; std::cerr << "[WARN] file_io service not found (plugin may not be in plugins_base/ dir)\n";
} }
// 测试服务查询: session / Test service query: session // 测试服务查询: session / Test service query: session

View File

@@ -4,19 +4,19 @@
| 目录 | 功能说明 | | 目录 | 功能说明 |
|------|---------| |------|---------|
| `dstalk-core/` | 核心网关库dstalk.dll / libdstalk.so提供插件加载、服务注册、事件总线、配置存储、日志、内存管理 | | `dstalk_core/` | 核心网关库dstalk.dll / libdstalk.so提供插件加载、服务注册、事件总线、配置存储、日志、内存管理 |
## 二、前端模块 ## 二、前端模块
| 目录 | 功能说明 | | 目录 | 功能说明 |
|------|---------| |------|---------|
| `dstalk-cli/` | 命令行前端ANSI 终端交互、命令解析、流式对话、工具调用、批处理/管道模式 | | `dstalk_cli/` | 命令行前端ANSI 终端交互、命令解析、流式对话、工具调用、批处理/管道模式 |
| `dstalk-gui/` | 图形界面前端SDL3 窗口化界面、暗色主题、流式对话(默认关闭) | | `dstalk_gui/` | 图形界面前端SDL3 窗口化界面、暗色主题、流式对话(默认关闭) |
| `dstalk-web/` | Web 前端Boost.Beast HTTP + SSE 流式推送、内嵌 HTML/CSS/JS 聊天界面(默认关闭) | | `dstalk_web/` | Web 前端Boost.Beast HTTP + SSE 流式推送、内嵌 HTML/CSS/JS 聊天界面(默认关闭) |
## 三、插件模块 ## 三、插件模块
所有插件位于 `plugins/` 目录下,通过 C ABI 与核心网关通信。 插件按依赖层级分布在三个目录下,通过 C ABI 与核心网关通信。
### 3.1 基础插件(无依赖) ### 3.1 基础插件(无依赖)
@@ -24,9 +24,9 @@
| 目录 | 服务名 | 功能说明 | | 目录 | 服务名 | 功能说明 |
|------|--------|---------| |------|--------|---------|
| `plugins/config/` | `"config"` | TOML 配置文件解析、键值读写 | | `plugins_base/config/` | `"config"` | TOML 配置文件解析、键值读写 |
| `plugins/file-io/` | `"file_io"` | 文件读写服务 | | `plugins_base/file_io/` | `"file_io"` | 文件读写服务 |
| `plugins/lsp/` | `"lsp"` | 语言服务器协议 JSON-RPC 客户端(诊断、悬停、补全),自行管理子进程 | | `plugins_base/lsp/` | `"lsp"` | 语言服务器协议 JSON-RPC 客户端(诊断、悬停、补全),自行管理子进程 |
### 3.2 只依赖基础插件的插件 ### 3.2 只依赖基础插件的插件
@@ -34,9 +34,9 @@
| 目录 | 服务名 | 功能说明 | 依赖 | | 目录 | 服务名 | 功能说明 | 依赖 |
|------|--------|---------|------| |------|--------|---------|------|
| `plugins/network/` | `"http"` | HTTP/HTTPS POST 和流式请求Boost.Beast + OpenSSL | `config` | | `plugins_middle/network/` | `"http"` | HTTP/HTTPS POST 和流式请求Boost.Beast + OpenSSL | `config` |
| `plugins/session/` | `"session"` | 会话消息历史管理、保存/加载、Token 计数 | `file_io` | | `plugins_middle/session/` | `"session"` | 会话消息历史管理、保存/加载、Token 计数 | `file_io` |
| `plugins/tools/` | `"tools"` | 工具注册、Schema 管理、执行分发(内置 file_read/file_write | `file_io` | | `plugins_middle/tools/` | `"tools"` | 工具注册、Schema 管理、执行分发(内置 file_read/file_write | `file_io` |
### 3.3 依赖其他插件的插件 ### 3.3 依赖其他插件的插件
@@ -44,9 +44,9 @@
| 目录 | 服务名 | 功能说明 | 依赖 | | 目录 | 服务名 | 功能说明 | 依赖 |
|------|--------|---------|------| |------|--------|---------|------|
| `plugins/context/` | `"context"` | Token 计数UTF-8 字节估算)、上下文窗口裁剪 | `session` | | `plugins_upper/context/` | `"context"` | Token 计数UTF-8 字节估算)、上下文窗口裁剪 | `session` |
| `plugins/openai/` | `"ai.openai"` | OpenAI 兼容格式 AI 接入chat/stream/tools、SSE 解析) | `http``config` | | `plugins_upper/openai/` | `"ai.openai"` | OpenAI 兼容格式 AI 接入chat/stream/tools、SSE 解析) | `http``config` |
| `plugins/anthropic/` | `"ai.anthropic"` | Anthropic Claude Messages API 接入chat/stream、SSE 解析) | `http``config` | | `plugins_upper/anthropic/` | `"ai.anthropic"` | Anthropic Claude Messages API 接入chat/stream、SSE 解析) | `http``config` |
### 3.4 规划中的插件(尚未实现) ### 3.4 规划中的插件(尚未实现)

View File

@@ -69,7 +69,7 @@ int ConfigStore::load_file(const char* path)
| 类型 | 规则 | 示例 | | 类型 | 规则 | 示例 |
|------|------|------| |------|------|------|
| 目录名 | 小写英文 + 数字 + 下划线/连字符,字母或下划线开头 | `dstalk-core``file-io``plugins` | | 目录名 | 小写英文 + 数字 + 下划线/连字符,字母或下划线开头 | `dstalk_core``file_io``plugins` |
| 源文件 | 小写英文 + 下划线,字母开头 | `config_store.cpp``openai_plugin.cpp` | | 源文件 | 小写英文 + 下划线,字母开头 | `config_store.cpp``openai_plugin.cpp` |
| 头文件 | 同源文件规则 | `dstalk_host.h``event_bus.hpp` | | 头文件 | 同源文件规则 | `dstalk_host.h``event_bus.hpp` |
| 测试文件 | `<模块名>_test.cpp` | `event_bus_test.cpp``openai_plugin_test.cpp` | | 测试文件 | `<模块名>_test.cpp` | `event_bus_test.cpp``openai_plugin_test.cpp` |
@@ -99,7 +99,7 @@ int ConfigStore::load_file(const char* path)
| 类型 | 规则 | 示例 | | 类型 | 规则 | 示例 |
|------|------|------| |------|------|------|
| 插件目录 | 功能名,小写 + 连字符 | `plugins/openai/``plugins/file-io/` | | 插件目录 | 功能名,小写 + 下划线 | `plugins_upper/openai/``plugins_base/file_io/` |
| 插件源文件 | `<功能名>_plugin.cpp` | `openai_plugin.cpp``session_plugin.cpp` | | 插件源文件 | `<功能名>_plugin.cpp` | `openai_plugin.cpp``session_plugin.cpp` |
| CMake 目标 | `plugin-<功能名>` | `plugin-openai``plugin-network` | | CMake 目标 | `plugin-<功能名>` | `plugin-openai``plugin-network` |
| 服务注册名 | 小写 + 点号分级 | `"ai.openai"``"http"``"session"` | | 服务注册名 | 小写 + 点号分级 | `"ai.openai"``"http"``"session"` |
@@ -116,13 +116,13 @@ int ConfigStore::load_file(const char* path)
### 4.1 目录 README ### 4.1 目录 README
- `dstalk/`(根目录)和所有二级目录(`dstalk-core/``dstalk-cli/``plugins/` 等)必须有 `README.md` - `dstalk/`(根目录)和所有二级目录(`dstalk_core/``dstalk_cli/``plugins_base/``plugins_middle/``plugins_upper/` 等)必须有 `README.md`
- 三级目录(如 `plugins/openai/`)如内容简单可暂不添加 - 三级目录(如 `plugins_upper/openai/`)如内容简单可暂不添加
### 4.2 include 路径 ### 4.2 include 路径
- 公共头文件放在 `dstalk-core/include/dstalk/` - 公共头文件放在 `dstalk_core/include/dstalk/`
- 私有实现头文件放在 `dstalk-core/src/` - 私有实现头文件放在 `dstalk_core/src/`
- 插件不得被 core 反向依赖core 禁止 `#include` 插件目录下的文件) - 插件不得被 core 反向依赖core 禁止 `#include` 插件目录下的文件)
### 4.3 内存管理 ### 4.3 内存管理
@@ -135,4 +135,4 @@ int ConfigStore::load_file(const char* path)
- 所有目标输出到 `${CMAKE_BINARY_DIR}/bin`(可执行文件)或 `${CMAKE_BINARY_DIR}/plugins`(插件 DLL - 所有目标输出到 `${CMAKE_BINARY_DIR}/bin`(可执行文件)或 `${CMAKE_BINARY_DIR}/plugins`(插件 DLL
- 新前端通过 `option(DSTALK_BUILD_XXX)` 控制,默认 OFF - 新前端通过 `option(DSTALK_BUILD_XXX)` 控制,默认 OFF
- 插件在 `plugins/CMakeLists.txt` 中按依赖顺序 `add_subdirectory` - 插件在 `plugins_base/CMakeLists.txt``plugins_middle/CMakeLists.txt``plugins_upper/CMakeLists.txt` 中按依赖顺序 `add_subdirectory`