Files
ANSCORE/modules/ANSLLM/ANSLLM.cpp

667 lines
23 KiB
C++

#include "ANSLLM.h"
#include <CkGlobal.h>
#include <CkBinData.h>
#include <CkByteData.h>
#include <CkStringTable.h>
#include <CkStringBuilder.h>
#include <CkJsonObject.h>
static bool ansllmLicenceValid = false;
static std::once_flag ansllmLicenseOnceFlag;
namespace ANSCENTER
{
static void VerifyGlobalANSLMLicense(const std::string& licenseKey) {
try {
static const std::vector<std::pair<int, std::string>> licenseChecks = {
{1000, "ANNHUB-LV"},
{1001, "DLHUB-LV"},
{1002, "ODHUB-LV"},
{1003, "ANSVIS"},
{1004, "ANSFR"},
{1005, "ANSOCR"},
{1006, "ANSALPR"},
{1007, "ANSCV"},
{1008, "ANSSRT"}
};
ansllmLicenceValid = false;
for (const auto& [productId, productName] : licenseChecks) {
if (ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, productId, productName)) {
ansllmLicenceValid = true;
break; // Stop at the first valid license
}
}
}
catch (const std::exception& e) {
ansllmLicenceValid = false;
}
}
ANSLLM::ANSLLM() {
_unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d";//
}
ANSLLM::~ANSLLM() noexcept = default;
void ANSLLM::CheckLicense() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Check once globally
std::call_once(ansllmLicenseOnceFlag, [this]() {
VerifyGlobalANSLMLicense(_licenseKey);
});
// Update this instance's local license flag
_isLicenseValid = ansllmLicenceValid;
}
catch (const std::exception& e) {
this->_logger.LogFatal("ANSLLM::CheckLicense. Error:", e.what(), __FILE__, __LINE__);
}
}
void ANSLLM::CheckUnlockCode() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkGlobal glob;
_unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d";
_isUnlockCodeValid = glob.UnlockBundle(_unlockCode.c_str());
if (!_isUnlockCodeValid) {
_logger.LogFatal("ANSLLM::CheckUnlockCode", glob.lastErrorText(), __FILE__, __LINE__);
return;
}
int status = glob.get_UnlockStatus();
if (status != 2) {
_logger.LogDebug("ANSLLM::CheckUnlockCode", "Unlocked in trial mode.", __FILE__, __LINE__);
}
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::CheckUnlockCode", e.what(), __FILE__, __LINE__);
}
}
bool ANSLLM::Initialize(const std::string& licenseKey, bool localLLM) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
_licenseKey = licenseKey;
_localLLM = localLLM;
CheckLicense();
CheckUnlockCode();
if (_localLLM) {
// Auto-configure for local Ollama: use custom provider with ChatCompletions API spec
// Must use "custom" — setting "openai" resets BaseUrl to api.openai.com
ai.put_Provider("custom");
ai.put_ApiSpec("ChatCompletions");
ai.put_BaseUrl(_ollamaBaseUrl.c_str());
ai.put_ApiKey("ollama"); // Ollama doesn't require a real API key
_logger.LogDebug("ANSLLM::Initialize", "Configured for local Ollama at " + _ollamaBaseUrl, __FILE__, __LINE__);
}
return _isLicenseValid && _isUnlockCodeValid;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetLLMProvider(const std::string& llmProvider) {
if (llmProvider.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_Provider(llmProvider.c_str());
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetLLMProvider", e.what(), __FILE__, __LINE__);
return false;
}
}
std::string ANSLLM::GetLLMProvider() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkString str;
ai.get_Provider(str);
const char* cstr = str.getString();
return (cstr != nullptr) ? std::string(cstr) : std::string();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetLLMProvider", e.what(), __FILE__, __LINE__);
return "";
}
}
bool ANSLLM::SetApiKey(const std::string& apiKey) {
if (apiKey.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_ApiKey(apiKey.c_str());
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetApiKey", e.what(), __FILE__, __LINE__);
return false;
}
}
std::vector<std::string> ANSLLM::GetModelList() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
std::vector<std::string> modelList;
try {
CkStringTable st;
if (!ai.GetModels(st)) {
const char* errText = ai.lastErrorText();
const std::string errorMsg = (errText != nullptr) ? errText : "Unknown error";
_logger.LogError("ANSLLM::GetModelList",
"Failed to get models: " + errorMsg,
__FILE__, __LINE__);
return modelList;
}
const int count = st.get_Count();
if (count <= 0) {
return modelList;
}
modelList.reserve(static_cast<size_t>(count));
for (int i = 0; i < count; ++i) {
const char* modelName = st.stringAt(i);
if (modelName != nullptr && modelName[0] != '\0') {
modelList.emplace_back(modelName);
}
}
return modelList;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetModelList",
std::string("Exception: ") + e.what(),
__FILE__, __LINE__);
return modelList;
}
catch (...) {
_logger.LogFatal("ANSLLM::GetModelList",
"Unknown exception occurred",
__FILE__, __LINE__);
return modelList;
}
}
bool ANSLLM::SetModel(const std::string& modelName) {
if (modelName.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_Model(modelName.c_str());
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetModel", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetTranscriptMode(bool transcriptMode) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_FullTranscript(transcriptMode);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetTranscriptMode", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::InputAddText(const std::string& inputText) {
if (inputText.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.InputAddText(inputText.c_str());
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::InputAddText", e.what(), __FILE__, __LINE__);
return false;
}
}
std::string ANSLLM::Ask(const std::string& prompt) {
if (prompt.empty()) return "";
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Ask the AI for text output.
bool success = ai.Ask(prompt.c_str());
if (success == false) {
const char* errText = ai.lastErrorText();
return (errText != nullptr) ? std::string(errText) : std::string("Unknown error");
}
// Get the text response.
CkStringBuilder sbResponse;
ai.GetOutputTextSb(sbResponse);
const char* cstr = sbResponse.getAsString();
return (cstr != nullptr) ? std::string(cstr) : std::string();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::Ask", e.what(), __FILE__, __LINE__);
return "";
}
}
bool ANSLLM::CreateConversation(const std::string& systemMessage, const std::string& developerMessage, const std::string& conversationName) {
if (systemMessage.empty() || developerMessage.empty() || conversationName.empty()) {
return false;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.NewConvo(conversationName.c_str(), systemMessage.c_str(), developerMessage.c_str());
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::CreateConversation", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetConversation(const std::string& conversationName) {
if (conversationName.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_SelectedConvo(conversationName.c_str());
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetConversation", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::InputAddImageUrl(const std::string& imageURL, const std::string& summary) {
if (imageURL.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.InputAddImageUrl(imageURL.c_str(), summary.c_str());
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::InputAddImageUrl", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::InputAddImageFromPath(const std::string& filePath, const char* summary) {
if (filePath.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkBinData bd;
bool success = bd.LoadFile(filePath.c_str());
if (!success) {
const char* errText = bd.lastErrorText();
_logger.LogError("ANSLLM::InputAddImageFromPath",
(errText != nullptr) ? errText : "Failed to load file",
__FILE__, __LINE__);
return false;
}
return ai.InputAddImageData(bd, summary);
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::InputAddImageFromPath", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::InputAddImageFromBase64(const std::string& jpegBase64String, const char* summary) {
if (jpegBase64String.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkBinData bd;
// Load base64 encoded JPEG data into CkBinData
bool success = bd.AppendEncoded(jpegBase64String.c_str(), "base64");
if (!success) {
_logger.LogError("ANSLLM::InputAddImage",
bd.lastErrorText(),
__FILE__, __LINE__);
return false;
}
return ai.InputAddImageData(bd, summary);
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::InputAddImage", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::InputAddFileData(const std::string& filePath, const char* summary) {
if (filePath.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkBinData bd;
bool success = bd.LoadFile(filePath.c_str());
if (!success) {
const char* errText = bd.lastErrorText();
_logger.LogError("ANSLLM::InputAddFileData",
(errText != nullptr) ? errText : "Failed to load file",
__FILE__, __LINE__);
return false;
}
return ai.InputAddImageData(bd, summary);
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::InputAddFileData", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::InputAddFileURL(const std::string& fileURL, const char* summary) {
if (fileURL.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.InputAddFileUrl(fileURL.c_str(), summary);
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::InputAddFileURL", e.what(), __FILE__, __LINE__);
return false;
}
}
// Extra methods
bool ANSLLM::ClearInputs() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.InputClear();
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::ClearInputs", e.what(), __FILE__, __LINE__);
return false;
}
}
std::vector<std::string> ANSLLM::GetConversationList() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
std::vector<std::string> convList;
try {
CkStringTable st;
if (!ai.ListConvos(st)) {
const char* errText = ai.lastErrorText();
const std::string errorMsg = (errText != nullptr) ? errText : "Unknown error";
_logger.LogError("ANSLLM::GetConversationList",
"Failed to get conversations: " + errorMsg,
__FILE__, __LINE__);
return convList;
}
const int count = st.get_Count();
if (count <= 0) {
return convList;
}
convList.reserve(static_cast<size_t>(count));
for (int i = 0; i < count; ++i) {
const char* convName = st.stringAt(i);
if (convName != nullptr && convName[0] != '\0') {
convList.emplace_back(convName);
}
}
return convList;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetConversationList", e.what(), __FILE__, __LINE__);
return {};
}
}
bool ANSLLM::DeleteConversation(const std::string& conversationName) {
if (conversationName.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.DeleteConvo(conversationName.c_str());
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::DeleteConversation", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetUTF8(bool b) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_Utf8(b);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetUTF8", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::GetUTF8() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.get_Utf8();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetUTF8", e.what(), __FILE__, __LINE__);
return false;
}
}
int ANSLLM::PollAi(bool abort) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.PollAi(abort);
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::PollAi", e.what(), __FILE__, __LINE__);
return -1;
}
}
bool ANSLLM::SetLocalLLM(bool localLLM) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
_localLLM = localLLM;
if (_localLLM) {
ai.put_Provider("custom");
ai.put_ApiSpec("ChatCompletions");
ai.put_BaseUrl(_ollamaBaseUrl.c_str());
ai.put_ApiKey("ollama");
_logger.LogDebug("ANSLLM::SetLocalLLM", "Configured for local Ollama at " + _ollamaBaseUrl, __FILE__, __LINE__);
}
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetLocalLLM", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetOllamaBaseUrl(const std::string& baseUrl) {
if (baseUrl.empty()) return false;
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
_ollamaBaseUrl = baseUrl;
if (_localLLM) {
ai.put_BaseUrl(_ollamaBaseUrl.c_str());
}
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetOllamaBaseUrl", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetBaseURL(const std::string& baseURL) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_BaseUrl(baseURL.c_str());
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetBaseURL", e.what(), __FILE__, __LINE__);
return false;
}
}
int ANSLLM::GetHeartbeatMs() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.get_HeartbeatMs();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetHeartbeatMs", e.what(), __FILE__, __LINE__);
return -1;
}
}
bool ANSLLM::SetHeartbeatMs(int heartbeatMs) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_HeartbeatMs(heartbeatMs);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetHeartbeatMs", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::GetHostedConversation() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.get_HostedConvo();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetHostedConversation", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetHostedConversation(bool hostedConversation) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_HostedConvo(hostedConversation);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetHostedConversation", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::GetStreamingMode() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.get_Streaming();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetStreamingMode", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetStreamingMode(bool streamingMode) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_Streaming(streamingMode);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetStreamingMode", e.what(), __FILE__, __LINE__);
return false;
}
}
int ANSLLM::GetIdleTimeoutMs() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return ai.get_IdleTimeoutMs();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetIdleTimeoutMs", e.what(), __FILE__, __LINE__);
return -1;
}
}
bool ANSLLM::SetIdleTimeoutMs(int idleTimeoutMs) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.put_IdleTimeoutMs(idleTimeoutMs);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetIdleTimeoutMs", e.what(), __FILE__, __LINE__);
return false;
}
}
std::string ANSLLM::GetLastErrorMessage() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
const char* errorText = ai.lastErrorText();
return (errorText != nullptr) ? std::string(errorText) : std::string();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetLastErrorMessage", e.what(), __FILE__, __LINE__);
return e.what();
}
}
std::string ANSLLM::GetOutputText() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkString str;
ai.GetOutputText(str);
const char* cstr = str.getString();
return (cstr != nullptr) ? std::string(cstr) : std::string();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetOutputText", e.what(), __FILE__, __LINE__);
return "";
}
}
std::string ANSLLM::GetOutputTextSb() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkStringBuilder sb;
ai.GetOutputTextSb(sb);
const char* cstr = sb.getAsString();
return (cstr != nullptr) ? std::string(cstr) : std::string();
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::GetOutputTextSb", e.what(), __FILE__, __LINE__);
return "";
}
}
bool ANSLLM::SetSleepMs(int sleepMs) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
ai.SleepMs(sleepMs);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetSleepMs", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSLLM::SetAskParams(double temperature) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
CkJsonObject obj;
std::string strTemp = std::to_string(temperature);
obj.UpdateString("temperature", strTemp.c_str());
ai.SetAskParams(obj);
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSLLM::SetAskParams", e.what(), __FILE__, __LINE__);
return false;
}
}
}