Add metadata validation script and module documentation

- Introduced a new Python script `check_agents_metadata.py` for validating agent metadata, including YAML parsing, rating ranges, and cross-references.
- Added usage instructions and exit codes for the script.
- Created a new markdown file `模块目录和功能说明.md` to outline the directory structure and functionality of the modules.
- Added a text file `说明此文件不可AI修改.txt` to specify that certain files should not be modified by AI, including important information about the `dstalk` framework and its modules.
This commit is contained in:
2026-05-31 00:00:58 +08:00
parent 3cc9ee95e4
commit f2da0f2ed4
43 changed files with 2467 additions and 800 deletions

View File

@@ -1,8 +1,12 @@
// ============================================================================
// anthropic_plugin_test.cpp — Anthropic AI 插件单元测试
// W21.6 (qa-wang): 覆盖 SSE 解析 / JSON 请求构建 / URL 解析 / 安全擦除
// 通过 #include plugin source 访问 file-scope static 函数
// ============================================================================
/*
* @file anthropic_plugin_test.cpp
* @brief Anthropic AI plugin unit tests: SSE parsing (parse_sse_data edge cases),
* request building (build_request_json), header construction, URL parsing
* (extract_host_port), secure_zero, and null-safety for free_result/configure.
* Anthropic AI 插件单元测试SSE 解析parse_sse_data 边界情况、请求构建build_request_json
* 头部构造、URL 解析extract_host_port、secure_zero、free_result/configure 空指针安全。
* Copyright (c) 2026 dstalk contributors. GPLv3.
*/
#define BOOST_JSON_HEADER_ONLY
#define BOOST_ALL_NO_LIB
#include "../plugins/anthropic/src/anthropic_plugin.cpp"
@@ -12,6 +16,7 @@
#include <string>
static int g_failures = 0;
// Lightweight assertion macro: increments g_failures counter on failure
#define CHECK(cond, msg) do { \
if (cond) { \
std::cout << "[OK] " << (msg) << "\n"; \
@@ -22,6 +27,8 @@ static int g_failures = 0;
} while (0)
// Test helper: populate g_cfg for build functions
// Test helper: populate g_cfg with valid anthropic defaults before build_* tests
// 测试辅助函数:为 build_* 测试填充 g_cfg 的有效 anthropic 默认值
static void setup_config() {
g_cfg.provider = "anthropic";
g_cfg.base_url = "https://api.anthropic.com";
@@ -31,10 +38,18 @@ static void setup_config() {
g_cfg.temperature = 0.7;
}
// Anthropic 插件测试 (W21.6)parse_sse_data 畸形/无效 JSON、content_block_delta 文本提取、
// message_stop/忽略类型、深层/边界结构、build_request_json 基础+边界、build_headers_json、
// extract_host_port、secure_zero、my_free_result 空指针安全、my_configure 空指针安全。
// Anthropic plugin tests (W21.6): parse_sse_data for malformed/invalid JSON,
// content_block_delta text extraction, message_stop/ignored types, deep/edge structures,
// build_request_json basics+edges, build_headers_json, extract_host_port,
// secure_zero, my_free_result null-safety, and my_configure null-safety.
int main()
{
// ================================================================
// Test Block 1: parse_sse_data — invalid/malformed inputs
// 测试块 1parse_sse_data — 无效/畸形输入
// ================================================================
std::cout << "\n--- Block 1: parse_sse_data invalid/malformed ---\n";
@@ -69,14 +84,14 @@ int main()
}
{
// Malformed JSON: unclosed brace
// Malformed JSON: unclosed brace / 畸形 JSON未闭合的花括号
std::string token;
bool ret = parse_sse_data("{\"type\":\"ping\"", token, nullptr);
CHECK(!ret, "T1.6: malformed JSON (unclosed brace) returns false (no crash)");
}
{
// Random garbage bytes
// Random garbage bytes / 随机垃圾字节
std::string token;
bool ret = parse_sse_data("\x00\x01\xFF\xFE", token, nullptr);
CHECK(!ret, "T1.7: binary garbage returns false (no crash)");
@@ -84,6 +99,7 @@ int main()
// ================================================================
// Test Block 2: parse_sse_data — content_block_delta
// 测试块 2parse_sse_data — content_block_delta
// ================================================================
std::cout << "\n--- Block 2: parse_sse_data content_block_delta ---\n";
@@ -146,6 +162,7 @@ int main()
// ================================================================
// Test Block 3: parse_sse_data — message_stop / ignored types
// 测试块 3parse_sse_data — message_stop / 忽略的类型
// ================================================================
std::cout << "\n--- Block 3: parse_sse_data message_stop / ignored types ---\n";
@@ -194,11 +211,12 @@ int main()
// ================================================================
// Test Block 4: parse_sse_data — deeply nested / edge structures
// 测试块 4parse_sse_data — 深层嵌套 / 边界结构
// ================================================================
std::cout << "\n--- Block 4: parse_sse_data deep/edge structures ---\n";
{
// Unrecognized event type should just be ignored
// Unrecognized event type should just be ignored / 未识别的事件类型应被忽略
std::string token;
const char* json = "{\"type\":\"some_unknown_future_type\"}";
bool ret = parse_sse_data(json, token, nullptr);
@@ -206,7 +224,7 @@ int main()
}
{
// text_delta with unicode content (Japanese)
// text_delta with unicode content (Japanese) / 含 unicode 内容的 text_delta日语
std::string token;
const char* json =
"{\"type\":\"content_block_delta\","
@@ -221,6 +239,7 @@ int main()
{
// Realistic Anthropic SSE chunk (content_block_delta + text_delta)
// 真实的 Anthropic SSE 数据块content_block_delta + text_delta
std::string token;
const char* json =
"{\"type\":\"content_block_delta\","
@@ -233,12 +252,13 @@ int main()
// ================================================================
// Test Block 5: build_request_json — basic cases
// 测试块 5build_request_json — 基础用例
// ================================================================
setup_config();
std::cout << "\n--- Block 5: build_request_json basic ---\n";
{
// Single user input, no history, stream=false
// Single user input, no history, stream=false / 单一用户输入无历史stream=false
std::string json = build_request_json(nullptr, 0, "Hello", "", false);
CHECK(!json.empty(), "T5.1: non-empty JSON produced");
CHECK(json.find("\"messages\"") != std::string::npos,
@@ -257,7 +277,7 @@ int main()
}
{
// With system message in history
// With system message in history / 历史中包含系统消息
dstalk_message_t msgs[1] = {
{"system", "You are a helpful assistant", nullptr, nullptr}
};
@@ -268,6 +288,7 @@ int main()
"T5.9: system prompt content present");
// messages should NOT contain the system role
// (since system messages are stripped from messages[] and put in system field)
// messages 不应包含 system 角色(系统消息从 messages[] 中提取出来,放入 system 字段)
// Actually, the code puts non-system into msgs. Let me check if system is in messages...
// The loop skips system: `if (m.role && strcmp(m.role, "system")==0) { ... continue; }`
// So system should NOT be in the messages array.
@@ -276,7 +297,7 @@ int main()
}
{
// With user+assistant history
// With user+assistant history / 包含 user+assistant 历史
dstalk_message_t msgs[2] = {
{"user", "What is 2+2?", nullptr, nullptr},
{"assistant", "It is 4.", nullptr, nullptr}
@@ -292,11 +313,12 @@ int main()
// ================================================================
// Test Block 6: build_request_json — edge cases
// 测试块 6build_request_json — 边界情况
// ================================================================
std::cout << "\n--- Block 6: build_request_json edge cases ---\n";
{
// Empty user input
// Empty user input / 空用户输入
std::string json = build_request_json(nullptr, 0, "", "", false);
CHECK(!json.empty(), "T6.1: empty user input produces valid JSON");
CHECK(json.find("\"role\":\"user\"") != std::string::npos,
@@ -314,7 +336,7 @@ int main()
}
{
// Temperature in valid range -> should be included
// Temperature in valid range -> should be included / 有效范围内的 temperature -> 应包含
g_cfg.temperature = 1.0;
std::string json = build_request_json(nullptr, 0, "Hi", "", false);
CHECK(json.find("\"temperature\"") != std::string::npos,
@@ -323,7 +345,7 @@ int main()
}
{
// Temperature out of range -> should NOT be included
// Temperature out of range -> should NOT be included / 超出范围的 temperature -> 不应包含
g_cfg.temperature = 1.5;
std::string json = build_request_json(nullptr, 0, "Hi", "", false);
CHECK(json.find("\"temperature\"") == std::string::npos,
@@ -336,7 +358,7 @@ int main()
}
{
// History with null role (should default to "")
// History with null role (should default to "") / null 角色的历史(应默认为 ""
dstalk_message_t msgs[1] = {
{nullptr, "some content", nullptr, nullptr}
};
@@ -345,7 +367,7 @@ int main()
}
{
// History with null content
// History with null content / null 内容的历史
dstalk_message_t msgs[1] = {
{"user", nullptr, nullptr, nullptr}
};
@@ -355,6 +377,7 @@ int main()
{
// Very long message (>2000 chars) — validate no truncation / crash
// 超长消息 (>2000 字符) — 验证无截断/崩溃
std::string long_input(5000, 'A');
std::string json = build_request_json(nullptr, 0, long_input, "", false);
CHECK(!json.empty(), "T6.10: 5000-char input produces valid JSON");
@@ -362,7 +385,7 @@ int main()
}
{
// Multiple system messages concatenated
// Multiple system messages concatenated / 多条系统消息拼接
dstalk_message_t msgs[2] = {
{"system", "Rule 1: be polite", nullptr, nullptr},
{"system", "Rule 2: be concise", nullptr, nullptr}
@@ -376,6 +399,7 @@ int main()
// ================================================================
// Test Block 7: build_headers_json
// 测试块 7build_headers_json
// ================================================================
std::cout << "\n--- Block 7: build_headers_json ---\n";
@@ -392,7 +416,7 @@ int main()
}
{
// With empty API key
// With empty API key / 空 API key
std::string saved = g_cfg.api_key;
g_cfg.api_key = "";
std::string headers = build_headers_json();
@@ -405,6 +429,7 @@ int main()
// ================================================================
// Test Block 8: extract_host_port
// 测试块 8extract_host_port
// ================================================================
std::cout << "\n--- Block 8: extract_host_port ---\n";
@@ -473,6 +498,7 @@ int main()
// ================================================================
// Test Block 9: secure_zero
// 测试块 9secure_zero
// ================================================================
std::cout << "\n--- Block 9: secure_zero ---\n";
@@ -488,7 +514,7 @@ int main()
}
{
// Zero-length should not crash
// Zero-length should not crash / 零长度不应崩溃
char buf[4] = {1,2,3,4};
secure_zero(buf, 0);
CHECK(buf[0] == 1 && buf[3] == 4,
@@ -496,18 +522,20 @@ int main()
}
{
// Null pointer + zero length = no-op
// Null pointer + zero length = no-op / 空指针 + 零长度 = 空操作
secure_zero(nullptr, 0);
CHECK(true, "T9.3: secure_zero(nullptr, 0) does not crash");
}
// ================================================================
// Test Block 10: my_free_result — null safety
// 测试块 10my_free_result — 空指针安全
// ================================================================
std::cout << "\n--- Block 10: my_free_result null safety ---\n";
{
// g_host is nullptr, so free_result should early-return
// g_host 为 nullptrfree_result 应提前返回
my_free_result(nullptr);
CHECK(true, "T10.1: free_result(nullptr) does not crash (null host)");
}
@@ -524,11 +552,13 @@ int main()
// ================================================================
// Test Block 11: my_configure — null host safety
// 测试块 11my_configure — null host 安全
// ================================================================
std::cout << "\n--- Block 11: my_configure null host safety ---\n";
{
// g_host is nullptr, configure should still return 0 (log skipped)
// g_host 为 nullptrconfigure 仍应返回 0跳过日志
int ret = my_configure(
"anthropic", "https://api.anthropic.com",
"sk-key", "claude-sonnet", 2048, 0.5);
@@ -539,13 +569,13 @@ int main()
}
{
// Null string params — should not crash
// Null string params — should not crash / null 字符串参数 — 不应崩溃
int ret = my_configure(nullptr, nullptr, nullptr, nullptr, 4096, 0.7);
CHECK(ret == 0, "T11.5: my_configure with all-null strings returns 0");
}
// ================================================================
// Summary
// Summary / 总结
// ================================================================
std::cout << "\n";
if (g_failures == 0) {