feat: Add LSP plugin unit tests and frontend common initialization library
Some checks failed
Some checks failed
- Introduced `dstalk_lsp_plugin_test` for testing LSP plugin functionalities including `lsp_trim`, `lsp_frame_message`, and `lsp_parse_content_length`. - Created `dstalk_frontend_common` static library to encapsulate shared initialization logic for frontend components (CLI, GUI, Web). - Implemented configuration file discovery and service querying in `dstalk_frontend_init`. - Added internal headers for LSP and Anthropic plugins to facilitate unit testing. - Established a mailroom system for asynchronous message passing between stateless agents, enhancing coordination and context management.
This commit is contained in:
124
dstalk_frontend_common/src/frontend_common.cpp
Normal file
124
dstalk_frontend_common/src/frontend_common.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
// ============================================================================
|
||||
// dstalk_frontend_common — 实现
|
||||
// ============================================================================
|
||||
|
||||
#include "dstalk_frontend_common.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
// ---- 配置文件发现 ----
|
||||
|
||||
static const char* discover_config(int argc, char* argv[],
|
||||
const char* default_cfg,
|
||||
const char* const* skip_flags)
|
||||
{
|
||||
// 1) 从 argv 中查找首个非标志参数
|
||||
if (argc >= 2 && argv) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
bool is_skip = false;
|
||||
if (skip_flags) {
|
||||
for (int k = 0; skip_flags[k]; ++k) {
|
||||
if (std::strcmp(argv[i], skip_flags[k]) == 0) {
|
||||
is_skip = true;
|
||||
// 若该标志有值参数则多跳一个
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') ++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_skip) return argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 2) 回退:尝试默认配置文件
|
||||
if (!default_cfg || default_cfg[0] == '\0') return nullptr;
|
||||
|
||||
const char* candidates[] = { default_cfg, nullptr };
|
||||
for (int i = 0; candidates[i]; ++i) {
|
||||
FILE* f = nullptr;
|
||||
#ifdef _WIN32
|
||||
if (fopen_s(&f, candidates[i], "r") == 0 && f) {
|
||||
#else
|
||||
f = std::fopen(candidates[i], "r");
|
||||
if (f) {
|
||||
#endif
|
||||
std::fclose(f);
|
||||
return candidates[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ---- 主初始化 ----
|
||||
|
||||
int dstalk_frontend_init(FrontendServices& svc,
|
||||
int argc, char* argv[],
|
||||
const char* default_cfg,
|
||||
const char* const* skip_flags)
|
||||
{
|
||||
// (1) 发现并加载配置
|
||||
const char* cfg = discover_config(argc, argv, default_cfg, skip_flags);
|
||||
if (dstalk_init(cfg) != 0) {
|
||||
std::fprintf(stderr, "[dstalk] 初始化失败\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// (2) 查询 AI 服务
|
||||
const char* provider = dstalk_config_get("ai.provider");
|
||||
if (!provider || provider[0] == '\0') provider = "ai.deepseek";
|
||||
svc.provider = provider;
|
||||
|
||||
svc.ai = static_cast<const dstalk_ai_service_t*>(
|
||||
dstalk_service_query(provider, 1));
|
||||
svc.session = static_cast<const dstalk_session_service_t*>(
|
||||
dstalk_service_query("session", 1));
|
||||
svc.file_io = static_cast<const dstalk_file_io_service_t*>(
|
||||
dstalk_service_query("file_io", 1));
|
||||
svc.tools = static_cast<const dstalk_tools_service_t*>(
|
||||
dstalk_service_query("tools", 1));
|
||||
|
||||
const dstalk_context_service_t* ctx_svc =
|
||||
static_cast<const dstalk_context_service_t*>(
|
||||
dstalk_service_query("context", 1));
|
||||
(void)ctx_svc; // 不强制使用,保留以备将来使用
|
||||
|
||||
if (!svc.ai) {
|
||||
std::fprintf(stderr, "[dstalk] AI 服务未找到(请检查插件目录)\n");
|
||||
return 2;
|
||||
}
|
||||
if (!svc.session) {
|
||||
std::fprintf(stderr, "[dstalk] Session 服务未找到\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
// (3) 配置 AI 服务的默认值
|
||||
const char* base_url = dstalk_config_get("api.base_url");
|
||||
const char* api_key = dstalk_config_get("api.api_key");
|
||||
const char* model = dstalk_config_get("api.model");
|
||||
|
||||
if (!base_url || base_url[0] == '\0') base_url = "https://api.deepseek.com/v1";
|
||||
if (!model || model[0] == '\0') model = "deepseek-v4-pro";
|
||||
|
||||
svc.base_url = base_url;
|
||||
svc.api_key = api_key ? api_key : "";
|
||||
svc.model = model;
|
||||
|
||||
svc.ai->configure(provider, base_url,
|
||||
api_key ? api_key : "",
|
||||
model, 4096, 0.7);
|
||||
|
||||
svc.initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---- 会话历史回放 ----
|
||||
|
||||
int dstalk_frontend_replay_history(FrontendServices& svc)
|
||||
{
|
||||
if (!svc.session) return 0;
|
||||
int count = 0;
|
||||
const dstalk_message_t* msgs = svc.session->history(&count);
|
||||
return count; // 调用方自行遍历并重建前端消息列表
|
||||
}
|
||||
Reference in New Issue
Block a user