Merge pull request #584 from luojiyin1987/fix/ai-suggestion-cache-key
fix(ai): include semantic request inputs in suggestion cache key
This commit is contained in:
6 files changed
+102
-9
No files matched your search
@@ -147,6 +147,8 @@ endif()
|
||||
set(SRC_FILES
|
||||
"./src/ai/ai_assistant.cpp"
|
||||
"./src/ai/ai_assistant.h"
|
||||
"./src/ai/ai_assistant_cache_key.cpp"
|
||||
"./src/ai/ai_assistant_cache_key.h"
|
||||
"./src/clipboard/clipboard_history.cpp"
|
||||
"./src/clipboard/clipboard_history.h"
|
||||
"./src/cloud/cloud_ime.cpp"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ai_assistant.h"
|
||||
#include "ai_assistant_cache_key.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -24,13 +25,6 @@ std::chrono::steady_clock::time_point g_last_input;
|
||||
AiAssistant::ApplyCallback g_callback;
|
||||
std::unordered_map<std::string, std::string> g_candidate_cache;
|
||||
|
||||
std::string BuildCacheKey(const AiAssistant::Request &request)
|
||||
{
|
||||
nlohmann::json segments = request.pinyin_segments;
|
||||
return request.config.provider + "\n" + request.config.endpoint + "\n" + request.config.model + "\n" +
|
||||
segments.dump();
|
||||
}
|
||||
|
||||
size_t WriteResponse(char *data, size_t size, size_t count, void *user)
|
||||
{
|
||||
static_cast<std::string *>(user)->append(data, size * count);
|
||||
@@ -135,7 +129,7 @@ void WorkerLoop()
|
||||
break;
|
||||
observed = g_generation.load();
|
||||
auto request = g_latest;
|
||||
const auto cached = g_candidate_cache.find(BuildCacheKey(request));
|
||||
const auto cached = g_candidate_cache.find(AiAssistant::detail::BuildSuggestionCacheKey(request));
|
||||
if (!request.identity.empty() && cached != g_candidate_cache.end())
|
||||
{
|
||||
const std::string candidate = cached->second;
|
||||
@@ -164,7 +158,7 @@ void WorkerLoop()
|
||||
{
|
||||
{
|
||||
std::lock_guard cache_lock(g_mutex);
|
||||
g_candidate_cache[BuildCacheKey(request)] = candidate;
|
||||
g_candidate_cache[AiAssistant::detail::BuildSuggestionCacheKey(request)] = candidate;
|
||||
}
|
||||
(void)0;
|
||||
g_callback(candidate, request.identity, observed);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "ai_assistant_cache_key.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace AiAssistant::detail
|
||||
{
|
||||
std::string BuildSuggestionCacheKey(const Request &request)
|
||||
{
|
||||
return nlohmann::json{{"provider", request.config.provider},
|
||||
{"endpoint", request.config.endpoint},
|
||||
{"model", request.config.model},
|
||||
{"prompt", request.config.prompt},
|
||||
{"pinyin_segments", request.pinyin_segments},
|
||||
{"context", request.context},
|
||||
{"candidate_limit", request.config.candidate_limit}}
|
||||
// context 与 prompt 是自由文本;键在无 try 的工作线程里构建,非法 UTF-8 不能抛出
|
||||
.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
|
||||
}
|
||||
} // namespace AiAssistant::detail
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "ai_assistant.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace AiAssistant::detail
|
||||
{
|
||||
std::string BuildSuggestionCacheKey(const Request &request);
|
||||
} // namespace AiAssistant::detail
|
||||
@@ -36,6 +36,8 @@ set(TEST_SOURCE_FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_input_key_policy.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_candidate_ui_owner.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_async_request_origin.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_ai_assistant_cache_key.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/ai/ai_assistant_cache_key.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_candidate_ui_action_policy.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_candidate_translation_policy.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_candidate_ui_state.cpp"
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "ai/ai_assistant_cache_key.h"
|
||||
#include "tests/includes/test_framework.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
AiAssistant::Request MakeRequest()
|
||||
{
|
||||
AiAssistant::Request request;
|
||||
request.pinyin_segments = {"shang", "hai"};
|
||||
request.context = "我准备去";
|
||||
request.config.provider = "deepseek";
|
||||
request.config.endpoint = "https://api.example.com/chat/completions";
|
||||
request.config.model = "example-model";
|
||||
request.config.prompt = "Return the best candidate.";
|
||||
request.config.candidate_limit = 3;
|
||||
return request;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE(ai_suggestion_cache_hits_for_the_same_semantic_request)
|
||||
{
|
||||
const auto request = MakeRequest();
|
||||
const auto same_request = request;
|
||||
|
||||
REQUIRE(AiAssistant::detail::BuildSuggestionCacheKey(request) ==
|
||||
AiAssistant::detail::BuildSuggestionCacheKey(same_request));
|
||||
}
|
||||
|
||||
TEST_CASE(ai_suggestion_cache_misses_when_context_changes)
|
||||
{
|
||||
const auto request = MakeRequest();
|
||||
auto changed_request = request;
|
||||
changed_request.context = "他受到了严重";
|
||||
|
||||
REQUIRE(AiAssistant::detail::BuildSuggestionCacheKey(request) !=
|
||||
AiAssistant::detail::BuildSuggestionCacheKey(changed_request));
|
||||
}
|
||||
|
||||
TEST_CASE(ai_suggestion_cache_misses_when_prompt_changes)
|
||||
{
|
||||
const auto request = MakeRequest();
|
||||
auto changed_request = request;
|
||||
changed_request.config.prompt = "Prefer place names.";
|
||||
|
||||
REQUIRE(AiAssistant::detail::BuildSuggestionCacheKey(request) !=
|
||||
AiAssistant::detail::BuildSuggestionCacheKey(changed_request));
|
||||
}
|
||||
|
||||
TEST_CASE(ai_suggestion_cache_key_tolerates_invalid_utf8)
|
||||
{
|
||||
auto request = MakeRequest();
|
||||
request.context = std::string("\xE6\x88", 2);
|
||||
request.config.prompt = std::string("\xFF", 1);
|
||||
|
||||
REQUIRE(!AiAssistant::detail::BuildSuggestionCacheKey(request).empty());
|
||||
}
|
||||
|
||||
TEST_CASE(ai_suggestion_cache_misses_when_candidate_limit_changes)
|
||||
{
|
||||
const auto request = MakeRequest();
|
||||
auto changed_request = request;
|
||||
changed_request.config.candidate_limit = 1;
|
||||
|
||||
REQUIRE(AiAssistant::detail::BuildSuggestionCacheKey(request) !=
|
||||
AiAssistant::detail::BuildSuggestionCacheKey(changed_request));
|
||||
}
|
||||
Reference in new issue
Block a user