Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/3xddd_serverapp.json
T

1 line
8.3 KiB
JSON
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "3xddd/serverapp", "files": {"AiEval.cpp": {"ca642035dd857b69aaf1a5b7f22f91b29bfa72b2": {"t": 1785746414.3467119, "n": 7069, "h": "d568819cd7297c32", "c": "#include \"pch.h\"\n#include \"AiEval.h\"\n\n#include <cpr/cpr.h>\n#include <nlohmann/json.hpp>\n#include <windows.h>\n\n#include <string>\n\nusing nlohmann::json;\n\nstatic const char* kDeepSeekApiKey = \"sk-d5d2a144f7d244c88f2f329d0ba0c0a6\";\nstatic const char* kDeepSeekModel = \"deepseek-chat\";\n\n// 只限制总字数到 100\nstatic const int kMaxTotalChars = 100;\n\nstatic std::string NormalizeNewlineWin(const std::string& in)\n{\n std::string out;\n out.reserve(in.size() + 16);\n\n for (size_t i = 0; i < in.size(); ++i)\n {\n char ch = in[i];\n if (ch == '\\r')\n {\n if (i + 1 < in.size() && in[i + 1] == '\\n') ++i;\n out += \"\\r\\n\";\n }\n else if (ch == '\\n')\n {\n out += \"\\r\\n\";\n }\n else\n {\n out.push_back(ch);\n }\n }\n return out;\n}\n\n// 只去掉首尾空白,不动中间换行\nstatic void TrimAsciiWhitespace(std::string& s)\n{\n while (!s.empty() && (s.back() == '\\r' || s.back() == '\\n' || s.back() == ' ' || s.back() == '\\t'))\n s.pop_back();\n\n size_t st = 0;\n while (st < s.size() && (s[st] == ' ' || s[st] == '\\t' || s[st] == '\\r' || s[st] == '\\n'))\n ++st;\n\n if (st > 0) s.erase(0, st);\n}\n\nbool AiEval::Run(const CString& evalText, CString& outAiText, CString& outErr)\n{\n try\n {\n outAiText.Empty();\n outErr.Empty();\n\n if (!kDeepSeekApiKey || kDeepSeekApiKey[0] == '\\0' ||\n std::string(kDeepSeekApiKey).find(\"xxxx\") != std::string::npos)\n {\n outErr = _T(\"AiEval.cpp 里没有填写 DeepSeek API Key\");\n return false;\n }\n\n // 评估文本:UTF-16 -> UTF-8\n std::string evalUtf8 = WideToUtf8(std::wstring((LPCWSTR)evalText));\n\n // ★关键:prompt 用宽字符写中文,再转 UTF-8,避免源码编码导致“服务端乱码”\n static const wchar_t* kSystemPromptW =\n LR\"(你是康复建议助手。\n请基于用户提供的佩戴/评估摘要,给出康复建议,表达自然一点即可。基于正常范围,如果有不正常现象请指出并建议改进措施。\n\n硬性要求:\n1) 总字数不超过100字(含标点)。\n2) 必须分行输出:2~4行,每行一句/一小段;行之间用换行分隔,不要空行。\n3) 不要Markdown,不要代码块,不要JSON,不要英文。\n4)能够进行疗效评估。每周压力有效率=(压力在目标区间时长/总佩戴时长)×100%其中合理目标区间为:顶椎30-50N,腰椎20-40N。\n5)正常顶锥压力:30-60N;动态波动±5N\n6)正常腰椎压力范围:20-45N\n7)正常弯曲角度:0-30°)\";\n\n std::string systemPromptUtf8 = WideToUtf8(std::wstring(kSystemPromptW));\n\n json req;\n req[\"model\"] = kDeepSeekModel;\n req[\"stream\"] = false;\n req[\"temperature\"] = 0.8; // 更自由一点(原来 0.2 太“刻板”)\n\n req[\"messages\"] = json::array({\n { {\"role\",\"system\"}, {\"content\", systemPromptUtf8} },\n { {\"role\",\"user\"},\n {\"content\", std::string(\"佩戴/评估摘要:\\n\") + evalUtf8 + \"\\n请按要求输出建议。\"} }\n });\n\n std::string body = req.dump(-1, ' ', false, json::error_handler_t::replace);\n\n cpr::Response r = cpr::Post(\n cpr::Url{ \"https://api.deepseek.com/chat/completions\" },\n cpr::Header{\n {\"Content-Type\",\"application/json; charset=utf-8\"},\n {\"Authorization\", std::string(\"Bearer \") + kDeepSeekApiKey},\n {\"Accept-Encoding\",\"identity\"}\n },\n cpr::Body{ body },\n cpr::ConnectTimeout{ 10000 },\n cpr::Timeout{ 60000 }\n );\n\n if (r.error.code != cpr::ErrorCode::OK)\n {\n CString e;\n e.Format(_T(\"HTTP 请求失败:网络错误\\r\\ncpr错误码:%d\\r\\n错误信息:%S\"),\n (int)r.error.code,\n r.error.message.c_str());\n outErr = e;\n return false;\n }\n\n if (r.status_code < 200 || r.status_code >= 300)\n {\n CString e;\n e.Format(_T(\"HTTP 状态码异常:%d\"), (int)r.status_code);\n outErr = e;\n\n std::string snippet = r.text;\n if (snippet.size() > 800) snippet = snippet.substr(0, 800);\n snippet = NormalizeNewlineWin(snippet);\n\n std::wstring snW = Utf8ToWide(snippet);\n outErr += _T(\"\\r\\n返回体(前800字节):\\r\\n\");\n outErr += CString(snW.c_str());\n return false;\n }\n\n json resp = json::parse(r.text, nullptr, false);\n if (resp.is_discarded() || !resp.is_object())\n {\n outErr = _T(\"返回不是合法JSON\");\n return false;\n }\n\n auto itE = resp.find(\"error\");\n if (itE != resp.end())\n {\n std::string s = itE->dump(-1, ' ', false, json::error_handler_t::replace);\n std::wstring w = Utf8ToWide(NormalizeNewlineWin(s));\n outErr = _T(\"接口返回 error:\\r\\n\");\n outErr += CString(w.c_str());\n return false;\n }\n\n auto itChoices = resp.find(\"choices\");\n if (itChoices == resp.end() || !itChoices->is_array() || itChoices->empty())\n {\n outErr = _T(\"返回缺少 choices 数组\");\n return false;\n }\n\n const json& c0 = (*itChoices)[0];\n auto itMsg = c0.find(\"message\");\n if (itMsg == c0.end() || !itMsg->is_object())\n {\n outErr = _T(\"返回缺少 message 对象\");\n return false;\n }\n\n auto itContent = itMsg->find(\"content\");\n if (itContent == itMsg->end())\n {\n outErr = _T(\"返回缺少 content\");\n return false;\n }\n\n const std::string* pContent = itContent->get_ptr<const std::string*>();\n if (!pContent || pContent->empty())\n {\n outErr = _T(\"AI 返回内容为空或类型不正确\");\n return false;\n }\n\n // content 是 UTF-8(含中文),并保证换行是 \\r\\n\n std::string content = NormalizeNewlineWin(*pContent);\n TrimAsciiWhitespace(content);\n\n // UTF-8 -> UTF-16,避免中文乱码\n std::wstring w = Utf8ToWide(content);\n CString out(w.c_str());\n\n // ★只做总字数限制:<=100(按宽字符计)\n if (out.GetLength() > kMaxTotalChars)\n {\n out = out.Left(kMaxTotalChars);\n // 截断后补一个换行,避免 UI 挤成一行\n if (out.GetLength() >= 2)\n {\n int n = out.GetLength();\n if (!(out.GetAt(n - 2) == '\\r' && out.GetAt(n - 1) == '\\n'))\n out += _T(\"\\r\\n\");\n }\n else\n {\n out += _T(\"\\r\\n\");\n }\n }\n\n outAiText = out;\n return true;\n }\n catch (const std::exception& e)\n {\n CString t;\n t.Format(_T(\"AiEval::Run 捕获异常:%S\"), e.what());\n outErr = t;\n return false;\n }\n catch (...)\n {\n outErr = _T(\"AiEval::Run 捕获未知异常\");\n return false;\n }\n}\n\nstd::string AiEval::WideToUtf8(const std::wstring& w)\n{\n if (w.empty()) return \"\";\n int len = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, nullptr, 0, nullptr, nullptr);\n if (len <= 0) return \"\";\n std::string out(len, '\\0');\n WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, &out[0], len, nullptr, nullptr);\n out.pop_back();\n return out;\n}\n\nstd::wstring AiEval::Utf8ToWide(const std::string& u8)\n{\n if (u8.empty()) return L\"\";\n int wlen = MultiByteToWideChar(CP_UTF8, 0, u8.data(), (int)u8.size(), nullptr, 0);\n if (wlen <= 0) return L\"\";\n std::wstring w(wlen, L'\\0');\n MultiByteToWideChar(CP_UTF8, 0, u8.data(), (int)u8.size(), &w[0], wlen);\n return w;\n}\n"}}}}