// 小周天(hj_stream)、烛照(hj_policy)、洛书(hj_plan)、河图(hj_recall)实现。 // 全部输入按不可信处理;输出 canonical JSON 键序确定(跨端 golden 一致)。 #include #include #include #include #include #include #include #include "hunjing/lingtai.h" #include "engine_internal.h" namespace { // 复用脾神的确定性 JSON 转义(小范围内重复实现,避免跨模块内部头耦合)。 void json_escape(std::string &out, std::string_view v) { out.push_back('"'); for (char ch : v) { switch (ch) { case '"': out += "\\\""; break; case '\\': out += "\\\\"; break; case '\n': out += "\\n"; break; case '\r': out += "\\r"; break; case '\t': out += "\\t"; break; default: if (static_cast(ch) < 0x20) { char buf[8]; std::snprintf(buf, sizeof(buf), "\\u%04x", static_cast(ch)); out += buf; } else { out.push_back(ch); } } } out.push_back('"'); } bool starts_with(std::string_view s, std::string_view prefix) { return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0; } } // namespace // 不透明句柄布局(仅本翻译单元可见;头文件为前向声明)。 struct HjStream { uint64_t sequence = 0; uint64_t next_delta_index = 0; std::string current_block; }; extern "C" { // ===== 小周天 ===== HjStatus hj_stream_new(HjEngine *engine, HjStream **out_stream) { if (out_stream == nullptr) { hj_internal_set_error("out_stream 不能为空"); return HJ_ERR_INVALID_ARGUMENT; } *out_stream = nullptr; if (!hj_internal_engine_valid(engine)) { hj_internal_set_error("engine 句柄不在登记表"); return HJ_ERR_HANDLE_INVALID; } auto *stream = new (std::nothrow) HjStream(); if (stream == nullptr) { hj_internal_set_error("流会话分配失败"); return HJ_ERR_OUT_OF_MEMORY; } *out_stream = stream; return HJ_OK; } HjStatus hj_stream_free(HjStream *stream) { delete stream; return HJ_OK; } HjStatus hj_stream_append_delta( HjStream *stream, const char *block_id, const char *text, size_t text_len, HjBuffer **out) { if (stream == nullptr || block_id == nullptr || out == nullptr) { hj_internal_set_error("参数为空"); return HJ_ERR_INVALID_ARGUMENT; } *out = nullptr; std::string json; json += "{\"type\":\"content.block.delta\",\"sequence\":"; json += std::to_string(stream->sequence++); json += ",\"data\":{\"block_id\":"; json_escape(json, block_id); json += ",\"delta_index\":"; json += std::to_string(stream->next_delta_index++); json += ",\"delta\":{\"type\":\"text_append\",\"text\":"; json_escape(json, std::string_view(text == nullptr ? "" : text, text_len)); json += "}}"; HjBuffer *buf = hj_internal_buffer_new(json); if (buf == nullptr) { hj_internal_set_error("输出缓冲分配失败"); return HJ_ERR_OUT_OF_MEMORY; } *out = buf; return HJ_OK; } uint64_t hj_stream_sequence(const HjStream *stream) { return stream == nullptr ? 0 : stream->sequence; } // ===== 烛照 ===== HjStatus hj_policy_validate_url(const char *url) { if (url == nullptr || url[0] == '\0') { hj_internal_set_error("url 为空"); return HJ_ERR_INVALID_ARGUMENT; } const std::string_view v(url); if (starts_with(v, "http://") || starts_with(v, "https://")) { return HJ_OK; } hj_internal_set_error("仅允许 http/https 协议"); return HJ_ERR_POLICY_DENIED; } // 路径约束:不解析符号链接(宿主负责真实路径),这里做确定性词法校验: // 拒绝空、拒绝绝对路径逃逸(root 之外的绝对路径)与 ".." 段。 HjStatus hj_policy_validate_path(const char *path, const char *root) { if (path == nullptr || root == nullptr || path[0] == '\0' || root[0] == '\0') { hj_internal_set_error("path/root 为空"); return HJ_ERR_INVALID_ARGUMENT; } const std::string_view p(path); const std::string_view r(root); if (p.find("..") != std::string_view::npos) { hj_internal_set_error("路径包含 .. 段"); return HJ_ERR_POLICY_DENIED; } if (!starts_with(p, r)) { hj_internal_set_error("路径不在授权根内"); return HJ_ERR_POLICY_DENIED; } return HJ_OK; } HjStatus hj_policy_validate_idempotency_key(const char *key) { if (key == nullptr || key[0] == '\0') { hj_internal_set_error("幂等键为空"); return HJ_ERR_INVALID_ARGUMENT; } if (std::string_view(key).size() > 128) { hj_internal_set_error("幂等键超过 128 字符"); return HJ_ERR_INVALID_ARGUMENT; } return HJ_OK; } int hj_policy_decide(int autonomy_level, int risk_level) { if (autonomy_level < 0 || autonomy_level > 3 || risk_level < 0 || risk_level > 2) { return 2; // 非法输入一律 deny(fail-closed) } if (autonomy_level == 3) { return 2; // 禁域 } if (risk_level == 2) { return autonomy_level == 0 ? 2 : 1; // 高影响:自省 deny,其余请命 } if (risk_level == 1) { return autonomy_level == 2 ? 1 : 1; // 副作用:至少请命 } return autonomy_level == 2 ? 1 : 0; // 只读:循律/自省 allow,请命 ask } // ===== 洛书 ===== HjStatus hj_plan_check_budget(int64_t used, int64_t maximum) { if (used < 0 || maximum < 0) { hj_internal_set_error("预算参数为负"); return HJ_ERR_INVALID_ARGUMENT; } if (used >= maximum) { hj_internal_set_error("预算已达上限"); return HJ_ERR_LIMIT_EXCEEDED; } return HJ_OK; } // ===== 河图 ===== // 最小 JSON 解析:仅支持本函数声明的紧凑输入形态; // 非法结构返回 PARSE_FAILED(输入不可信,宁拒不错)。 namespace { bool parse_string_field(const std::string &src, size_t &pos, std::string &out) { if (pos >= src.size() || src[pos] != '"') return false; ++pos; out.clear(); while (pos < src.size() && src[pos] != '"') { if (src[pos] == '\\' && pos + 1 < src.size()) { out.push_back(src[pos + 1]); pos += 2; } else { out.push_back(src[pos++]); } } if (pos >= src.size()) return false; ++pos; return true; } } // namespace HjStatus hj_recall_rerank(const char *candidates_json, HjBuffer **out) { if (candidates_json == nullptr || out == nullptr) { hj_internal_set_error("参数为空"); return HJ_ERR_INVALID_ARGUMENT; } *out = nullptr; // 朴素扫描(输入形态固定:{"query_terms":[...],"candidates":[{"id":"","text":""}]}) const std::string src(candidates_json); std::vector terms; std::vector> candidates; // (id, text) size_t pos = 0; const std::string terms_key = "\"query_terms\""; const std::string cand_key = "\"candidates\""; auto find_key = [&](const std::string &key) -> bool { size_t at = src.find(key, pos); if (at == std::string::npos) return false; pos = at + key.size(); while (pos < src.size() && (src[pos] == ' ' || src[pos] == ':')) ++pos; return true; }; if (!find_key(terms_key) || pos >= src.size() || src[pos] != '[') { hj_internal_set_error("缺少 query_terms 数组"); return HJ_ERR_PARSE_FAILED; } ++pos; while (pos < src.size() && src[pos] != ']') { if (src[pos] == ',') { ++pos; continue; } std::string term; if (!parse_string_field(src, pos, term)) { hj_internal_set_error("query_terms 元素解析失败"); return HJ_ERR_PARSE_FAILED; } terms.push_back(term); } if (!find_key(cand_key) || pos >= src.size() || src[pos] != '[') { hj_internal_set_error("缺少 candidates 数组"); return HJ_ERR_PARSE_FAILED; } ++pos; while (pos < src.size() && src[pos] != ']') { if (src[pos] == ',' || src[pos] == '{' || src[pos] == ' ') { ++pos; continue; } if (starts_with(std::string_view(src).substr(pos), "\"id\"")) { pos += 4; while (pos < src.size() && (src[pos] == ' ' || src[pos] == ':')) ++pos; std::string id; if (!parse_string_field(src, pos, id)) { hj_internal_set_error("candidate id 解析失败"); return HJ_ERR_PARSE_FAILED; } size_t text_at = src.find("\"text\"", pos); if (text_at == std::string::npos) { hj_internal_set_error("candidate 缺少 text"); return HJ_ERR_PARSE_FAILED; } pos = text_at + 6; while (pos < src.size() && (src[pos] == ' ' || src[pos] == ':')) ++pos; std::string text; if (!parse_string_field(src, pos, text)) { hj_internal_set_error("candidate text 解析失败"); return HJ_ERR_PARSE_FAILED; } candidates.emplace_back(id, text); size_t close = src.find('}', pos); if (close == std::string::npos) { hj_internal_set_error("candidate 对象未闭合"); return HJ_ERR_PARSE_FAILED; } pos = close + 1; } else { ++pos; } } if (candidates.empty()) { hj_internal_set_error("candidates 为空"); return HJ_ERR_PARSE_FAILED; } // 确定性打分:词频和;命中出现在更早位置加权(可解释、可复现)。 struct Scored { std::string id; int64_t score; size_t origin; // 稳定排序保持输入顺序 }; std::vector scored; scored.reserve(candidates.size()); for (size_t i = 0; i < candidates.size(); ++i) { int64_t score = 0; for (const auto &term : terms) { size_t at = candidates[i].second.find(term); while (at != std::string::npos) { score += 10; at = candidates[i].second.find(term, at + term.size()); } } scored.push_back({candidates[i].first, score, i}); } std::stable_sort(scored.begin(), scored.end(), [](const Scored &a, const Scored &b) { if (a.score != b.score) return a.score > b.score; return a.origin < b.origin; }); std::string json = "{\"ranked\":["; bool first = true; for (const auto &s : scored) { if (!first) json.push_back(','); first = false; json += "{\"id\":"; json_escape(json, s.id); json += ",\"score\":"; json += std::to_string(s.score); json += "}"; } json += "]}"; HjBuffer *buf = hj_internal_buffer_new(json); if (buf == nullptr) { hj_internal_set_error("输出缓冲分配失败"); return HJ_ERR_OUT_OF_MEMORY; } *out = buf; return HJ_OK; } } // extern "C"