Skip to content

Commit

Permalink
xassist implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
tharun571 committed Jun 24, 2024
1 parent 4745f72 commit bc36b14
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ __pycache__/
build/

bld

# API Keys for LLM
*_api_key.txt
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ set(XEUS_CPP_SRC
src/xoptions.cpp
src/xparser.cpp
src/xutils.cpp
src/xmagics/xassist.hpp
src/xmagics/xassist.cpp
)

if(EMSCRIPTEN)
Expand Down Expand Up @@ -310,7 +312,7 @@ macro(xeus_cpp_create_target target_name linkage output_name)
set(XEUS_CPP_XEUS_TARGET xeus-static)
endif ()

target_link_libraries(${target_name} PUBLIC ${XEUS_CPP_XEUS_TARGET} clangCppInterOp pugixml argparse::argparse)
target_link_libraries(${target_name} PUBLIC ${XEUS_CPP_XEUS_TARGET} clangCppInterOp pugixml argparse::argparse curl)

if (WIN32 OR CYGWIN)
#
Expand Down
Binary file added docs/source/gemini.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The Xeus-Cpp is a Jupyter kernel for the C++ programming language
InstallationAndUsage
UsingXeus-Cpp
tutorials
magics
dev-build-options
debug
FAQ
Expand Down
35 changes: 35 additions & 0 deletions docs/source/magics.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Magics commands
--------------------

Magics are special commands for the kernel that are not part of the C++
programming language.

There are defined with the symbol ``%`` for a line magic and ``%%`` for a cell
magic.

Here are the magics available in xeus-cpp.

%%xassist
========================

Leverage the large language models to assist in your development process. Currently supported models are Gemini - gemini-1.5-flash, OpenAI - gpt-3.5-turbo-16k.

- Save the api key

.. code::
%%xassist model --save-key
key
- Use the model

.. code::
%%xassist model
prompt
- Example

.. image:: gemini.png

A new prompt is sent to the model everytime and the functionality to use previous context will be added soon.
2 changes: 2 additions & 0 deletions src/xinterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "xinput.hpp"
#include "xinspect.hpp"
#include "xmagics/os.hpp"
#include "xmagics/xassist.hpp"
#include "xparser.hpp"
#include "xsystem.hpp"

Expand Down Expand Up @@ -404,5 +405,6 @@ __get_cxx_version ()
// preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("file", writefile());
// preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("timeit", timeit(&m_interpreter));
// preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("python", pythonexec());
preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("xassist", xassist());
}
}
229 changes: 229 additions & 0 deletions src/xmagics/xassist.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/************************************************************************************
* Copyright (c) 2023, xeus-cpp contributors *
* Copyright (c) 2023, Johan Mabille, Loic Gouarin, Sylvain Corlay, Wolf Vollprecht *
* *
* Distributed under the terms of the BSD 3-Clause License. *
* *
* The full license is in the file LICENSE, distributed with this software. *
************************************************************************************/
#include "xassist.hpp"

#include <curl/curl.h>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <string>
#include <unordered_set>

using json = nlohmann::json;

namespace xcpp
{
class APIKeyManager
{
public:

static void saveApiKey(const std::string& model, const std::string& apiKey)
{
std::string apiKeyFilePath = model + "_api_key.txt";
std::ofstream out(apiKeyFilePath);
if (out)
{
out << apiKey;
out.close();
std::cout << "API key saved for model " << model << std::endl;
}
else
{
std::cerr << "Failed to open file for writing API key for model " << model << std::endl;
}
}

// Method to load the API key for a specific model
static std::string loadApiKey(const std::string& model)
{
std::string apiKeyFilePath = model + "_api_key.txt";
std::ifstream in(apiKeyFilePath);
std::string apiKey;
if (in)
{
std::getline(in, apiKey);
in.close();
return apiKey;
}

std::cerr << "Failed to open file for reading API key for model " << model << std::endl;
return "";
}
};

class CurlHelper
{
private:

CURL* m_curl;
curl_slist* m_headers;

public:

CurlHelper()
: m_curl(curl_easy_init())
, m_headers(curl_slist_append(nullptr, "Content-Type: application/json"))
{
}

~CurlHelper()
{
if (m_curl)
{
curl_easy_cleanup(m_curl);
}
if (m_headers)
{
curl_slist_free_all(m_headers);
}
}

// Delete copy constructor and copy assignment operator
CurlHelper(const CurlHelper&) = delete;
CurlHelper& operator=(const CurlHelper&) = delete;

// Delete move constructor and move assignment operator
CurlHelper(CurlHelper&&) = delete;
CurlHelper& operator=(CurlHelper&&) = delete;

std::string
performRequest(const std::string& url, const std::string& postData, const std::string& authHeader = "")
{
if (!authHeader.empty())
{
m_headers = curl_slist_append(m_headers, authHeader.c_str());
}

curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headers);
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, postData.c_str());

std::string response;
curl_easy_setopt(
m_curl,
CURLOPT_WRITEFUNCTION,
+[](const char* in, size_t size, size_t num, std::string* out)
{
const size_t totalBytes(size * num);
out->append(in, totalBytes);
return totalBytes;
}
);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);

CURLcode res = curl_easy_perform(m_curl);
if (res != CURLE_OK)
{
std::cerr << "CURL request failed: " << curl_easy_strerror(res) << std::endl;
return "";
}

return response;
}
};

std::string gemini(const std::string& cell, const std::string& key)
{
CurlHelper curlHelper;
const std::string url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key="
+ key;
const std::string postData = R"({"contents": [{"parts":[{"text": ")" + cell + R"("}]}]})";

std::string response = curlHelper.performRequest(url, postData);

json j = json::parse(response);
if (j.find("error") != j.end())
{
std::cerr << "Error: " << j["error"]["message"] << std::endl;
return "";
}

return j["candidates"][0]["content"]["parts"][0]["text"];
}

std::string openai(const std::string& cell, const std::string& key)
{
CurlHelper curlHelper;
const std::string url = "https://api.openai.com/v1/chat/completions";
const std::string postData = R"({
"model": "gpt-3.5-turbo-16k",
"messages": [{"role": "user", "content": ")"
+ cell + R"("}],
"temperature": 0.7
})";
std::string authHeader = "Authorization: Bearer " + key;

std::string response = curlHelper.performRequest(url, postData, authHeader);

json j = json::parse(response);

if (j.find("error") != j.end())
{
std::cerr << "Error: " << j["error"]["message"] << std::endl;
return "";
}

return j["choices"][0]["message"]["content"];
}

void xassist::operator()(const std::string& line, const std::string& cell)
{
try
{
std::istringstream iss(line);
std::vector<std::string> tokens(
std::istream_iterator<std::string>{iss},
std::istream_iterator<std::string>()
);

std::vector<std::string> models = {"gemini", "openai"};
std::string model = tokens[1];

if (std::find(models.begin(), models.end(), model) == models.end())
{
std::cerr << "Model not found." << std::endl;
return;
}

APIKeyManager api;
if (tokens[2] == "--save-key")
{
xcpp::APIKeyManager::saveApiKey(model, cell);
return;
}

std::string key = xcpp::APIKeyManager::loadApiKey(model);
if (key.empty())
{
std::cerr << "API key for model " << model << " is not available." << std::endl;
return;
}

std::string response;
if (model == "gemini")
{
response = gemini(cell, key);
}
else if (model == "openai")
{
response = openai(cell, key);
}

std::cout << response;
}
catch (const std::runtime_error& e)
{
std::cerr << "Caught an exception: " << e.what() << std::endl;
}
catch (...)
{
std::cerr << "Caught an unknown exception" << std::endl;
}
}
} // namespace xcpp
25 changes: 25 additions & 0 deletions src/xmagics/xassist.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/************************************************************************************
* Copyright (c) 2023, xeus-cpp contributors *
* *
* Distributed under the terms of the BSD 3-Clause License. *
* *
* The full license is in the file LICENSE, distributed with this software. *
************************************************************************************/

#ifndef XEUS_CPP_XASSIST_MAGIC_HPP
#define XEUS_CPP_XASSIST_MAGIC_HPP

#include <string>

#include "xeus-cpp/xmagics.hpp"

namespace xcpp
{
class xassist : public xmagic_cell
{
public:

void operator()(const std::string& line, const std::string& cell) override;
};
} // namespace xcpp
#endif

0 comments on commit bc36b14

Please sign in to comment.