mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-14 02:09:22 -04:00
refactor: add to template agent roles
This commit is contained in:
@@ -2,7 +2,6 @@ add_executable(QodeAssistTest
|
||||
../CodeHandler.cpp
|
||||
../LLMSuggestion.cpp
|
||||
CodeHandlerTest.cpp
|
||||
ClaudeCacheControlTest.cpp
|
||||
DocumentContextReaderTest.cpp
|
||||
LLMSuggestionTest.cpp
|
||||
# LLMClientInterfaceTests.cpp
|
||||
@@ -17,7 +16,7 @@ target_link_libraries(QodeAssistTest PRIVATE
|
||||
GTest::Main
|
||||
QtCreator::LanguageClient
|
||||
Context
|
||||
PluginLLMCore
|
||||
Common
|
||||
LLMQore
|
||||
)
|
||||
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
// Copyright (C) 2024-2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "providers/ClaudeCacheControl.hpp"
|
||||
|
||||
using namespace QodeAssist::Providers::ClaudeCacheControl;
|
||||
|
||||
namespace {
|
||||
|
||||
QJsonObject expectedEphemeral(bool extendedTtl)
|
||||
{
|
||||
QJsonObject obj{{"type", "ephemeral"}};
|
||||
if (extendedTtl)
|
||||
obj["ttl"] = "1h";
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ClaudeCacheControlTest, BreakpointWithoutExtendedTTL)
|
||||
{
|
||||
const QJsonObject cc = buildBreakpoint(false);
|
||||
EXPECT_EQ(cc.value("type").toString(), "ephemeral");
|
||||
EXPECT_FALSE(cc.contains("ttl"));
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, BreakpointWithExtendedTTL)
|
||||
{
|
||||
const QJsonObject cc = buildBreakpoint(true);
|
||||
EXPECT_EQ(cc.value("type").toString(), "ephemeral");
|
||||
EXPECT_EQ(cc.value("ttl").toString(), "1h");
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, SystemAsStringWrappedIntoArray)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["system"] = "you are a helpful agent";
|
||||
|
||||
apply(request, false);
|
||||
|
||||
ASSERT_TRUE(request.value("system").isArray());
|
||||
const QJsonArray sys = request.value("system").toArray();
|
||||
ASSERT_EQ(sys.size(), 1);
|
||||
|
||||
const QJsonObject block = sys.first().toObject();
|
||||
EXPECT_EQ(block.value("type").toString(), "text");
|
||||
EXPECT_EQ(block.value("text").toString(), "you are a helpful agent");
|
||||
EXPECT_EQ(block.value("cache_control").toObject(), expectedEphemeral(false));
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, EmptySystemStringIsNotWrapped)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["system"] = "";
|
||||
|
||||
apply(request, false);
|
||||
|
||||
EXPECT_TRUE(request.value("system").isString());
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, SystemAsArrayMarksLastBlock)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["system"] = QJsonArray{
|
||||
QJsonObject{{"type", "text"}, {"text", "a"}},
|
||||
QJsonObject{{"type", "text"}, {"text", "b"}}};
|
||||
|
||||
apply(request, false);
|
||||
|
||||
const QJsonArray sys = request.value("system").toArray();
|
||||
ASSERT_EQ(sys.size(), 2);
|
||||
EXPECT_FALSE(sys[0].toObject().contains("cache_control"));
|
||||
EXPECT_EQ(sys[1].toObject().value("cache_control").toObject(), expectedEphemeral(false));
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, ToolsLastEntryGetsCacheControl)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["tools"] = QJsonArray{
|
||||
QJsonObject{{"name", "read_file"}},
|
||||
QJsonObject{{"name", "edit_file"}},
|
||||
QJsonObject{{"name", "search"}}};
|
||||
|
||||
apply(request, true);
|
||||
|
||||
const QJsonArray tools = request.value("tools").toArray();
|
||||
ASSERT_EQ(tools.size(), 3);
|
||||
EXPECT_FALSE(tools[0].toObject().contains("cache_control"));
|
||||
EXPECT_FALSE(tools[1].toObject().contains("cache_control"));
|
||||
EXPECT_EQ(tools[2].toObject().value("cache_control").toObject(), expectedEphemeral(true));
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, SingleMessageHistorySkipped)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["messages"]
|
||||
= QJsonArray{QJsonObject{{"role", "user"}, {"content", "first message"}}};
|
||||
|
||||
apply(request, false);
|
||||
|
||||
const QJsonArray msgs = request.value("messages").toArray();
|
||||
ASSERT_EQ(msgs.size(), 1);
|
||||
EXPECT_TRUE(msgs[0].toObject().value("content").isString());
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, HistoryBreakpointOnSecondToLastMessage)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["messages"] = QJsonArray{
|
||||
QJsonObject{{"role", "user"}, {"content", "u1"}},
|
||||
QJsonObject{{"role", "assistant"}, {"content", "a1"}},
|
||||
QJsonObject{{"role", "user"}, {"content", "u2-current"}}};
|
||||
|
||||
apply(request, false);
|
||||
|
||||
const QJsonArray msgs = request.value("messages").toArray();
|
||||
ASSERT_EQ(msgs.size(), 3);
|
||||
|
||||
EXPECT_TRUE(msgs[0].toObject().value("content").isString());
|
||||
|
||||
const QJsonArray a1Content = msgs[1].toObject().value("content").toArray();
|
||||
ASSERT_EQ(a1Content.size(), 1);
|
||||
EXPECT_EQ(a1Content.first().toObject().value("text").toString(), "a1");
|
||||
EXPECT_EQ(
|
||||
a1Content.first().toObject().value("cache_control").toObject(),
|
||||
expectedEphemeral(false));
|
||||
|
||||
EXPECT_TRUE(msgs[2].toObject().value("content").isString());
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, HistoryArrayContentMarksLastBlock)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["messages"] = QJsonArray{
|
||||
QJsonObject{
|
||||
{"role", "user"},
|
||||
{"content",
|
||||
QJsonArray{
|
||||
QJsonObject{{"type", "text"}, {"text", "describe this"}},
|
||||
QJsonObject{{"type", "image"}}}}},
|
||||
QJsonObject{{"role", "assistant"}, {"content", "ok"}}};
|
||||
|
||||
apply(request, false);
|
||||
|
||||
const QJsonArray msgs = request.value("messages").toArray();
|
||||
const QJsonArray content = msgs[0].toObject().value("content").toArray();
|
||||
ASSERT_EQ(content.size(), 2);
|
||||
EXPECT_FALSE(content[0].toObject().contains("cache_control"));
|
||||
EXPECT_EQ(content[1].toObject().value("cache_control").toObject(), expectedEphemeral(false));
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, NoSystemNoToolsNoMessagesIsNoop)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["model"] = "claude-sonnet-4-5";
|
||||
request["max_tokens"] = 1024;
|
||||
|
||||
apply(request, false);
|
||||
|
||||
EXPECT_EQ(request.value("model").toString(), "claude-sonnet-4-5");
|
||||
EXPECT_EQ(request.value("max_tokens").toInt(), 1024);
|
||||
EXPECT_FALSE(request.contains("system"));
|
||||
EXPECT_FALSE(request.contains("tools"));
|
||||
EXPECT_FALSE(request.contains("messages"));
|
||||
}
|
||||
|
||||
TEST(ClaudeCacheControlTest, EmptyToolsArrayIsNoop)
|
||||
{
|
||||
QJsonObject request;
|
||||
request["tools"] = QJsonArray{};
|
||||
|
||||
apply(request, false);
|
||||
|
||||
EXPECT_TRUE(request.value("tools").isArray());
|
||||
EXPECT_TRUE(request.value("tools").toArray().isEmpty());
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <QSharedPointer>
|
||||
#include <QTextDocument>
|
||||
|
||||
namespace QodeAssist::PluginLLMCore {
|
||||
namespace QodeAssist::Templates {
|
||||
|
||||
void PrintTo(const ContextData &data, std::ostream *os)
|
||||
{
|
||||
@@ -20,10 +20,10 @@ void PrintTo(const ContextData &data, std::ostream *os)
|
||||
<< "}";
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::PluginLLMCore
|
||||
} // namespace QodeAssist::Templates
|
||||
|
||||
using namespace QodeAssist::Context;
|
||||
using namespace QodeAssist::PluginLLMCore;
|
||||
using namespace QodeAssist::Templates;
|
||||
using namespace QodeAssist::Settings;
|
||||
|
||||
class DocumentContextReaderTest : public QObject, public testing::Test
|
||||
@@ -367,7 +367,7 @@ TEST_F(DocumentContextReaderTest, testPrepareContext)
|
||||
|
||||
EXPECT_EQ(
|
||||
reader.prepareContext(2, 3, *createSettingsForWholeFile()),
|
||||
(QodeAssist::PluginLLMCore::ContextData{
|
||||
(QodeAssist::Templates::ContextData{
|
||||
.prefix = "Line 0\nLine 1\nLin",
|
||||
.suffix = "e 2\nLine 3\nLine 4",
|
||||
.fileContext = "\n Language: (MIME: text/python) filepath: /path/to/file()\n\n"
|
||||
@@ -375,7 +375,7 @@ TEST_F(DocumentContextReaderTest, testPrepareContext)
|
||||
|
||||
EXPECT_EQ(
|
||||
reader.prepareContext(2, 3, *createSettingsForLines(1, 1)),
|
||||
(QodeAssist::PluginLLMCore::ContextData{
|
||||
(QodeAssist::Templates::ContextData{
|
||||
.prefix = "Line 1\nLin",
|
||||
.suffix = "e 2\nLine 3",
|
||||
.fileContext = "\n Language: (MIME: text/python) filepath: /path/to/file()\n\n"
|
||||
@@ -383,7 +383,7 @@ TEST_F(DocumentContextReaderTest, testPrepareContext)
|
||||
|
||||
EXPECT_EQ(
|
||||
reader.prepareContext(2, 3, *createSettingsForLines(2, 2)),
|
||||
(QodeAssist::PluginLLMCore::ContextData{
|
||||
(QodeAssist::Templates::ContextData{
|
||||
.prefix = "Line 0\nLine 1\nLin",
|
||||
.suffix = "e 2\nLine 3\nLine 4",
|
||||
.fileContext = "\n Language: (MIME: text/python) filepath: /path/to/file()\n\n"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
|
||||
|
||||
#include <iostream>
|
||||
#include <pluginllmcore/ContextData.hpp>
|
||||
#include <sources/common/ContextData.hpp>
|
||||
#include <QString>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -44,12 +44,11 @@ std::ostream &operator<<(std::ostream &out, const std::optional<T> &value)
|
||||
return out;
|
||||
}
|
||||
|
||||
namespace QodeAssist::PluginLLMCore {
|
||||
namespace QodeAssist::Templates {
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &out, const Message &value)
|
||||
{
|
||||
out << "Message{"
|
||||
<< "role=" << value.role << "content=" << value.content << "}";
|
||||
out << "Message{role=" << value.role << ", blocks=" << value.blocks.size() << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -62,4 +61,4 @@ inline std::ostream &operator<<(std::ostream &out, const ContextData &value)
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::PluginLLMCore
|
||||
} // namespace QodeAssist::Templates
|
||||
|
||||
Reference in New Issue
Block a user