Skip to content

Commit 2135e36

Browse files
committed
Add xassist
Check workflow Check workflow Check workflow Check workflow WIP WIP WIP
1 parent 4745f72 commit 2135e36

12 files changed

+416
-3
lines changed

.github/workflows/main.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ jobs:
7474
run: |
7575
micromamba shell hook -s cmd.exe -p C:\Users\runneradmin\micromamba-root
7676
77+
- name: activate environment
78+
if: ${{ runner.os != 'windows' }}
79+
shell: bash -l {0}
80+
run: |
81+
micromamba activate xeus-cpp
82+
7783
- name: install cxx compiler
7884
if: ${{ runner.os != 'windows' }}
7985
shell: bash -l {0}
@@ -118,13 +124,17 @@ jobs:
118124
cd build
119125
make install -j ${{ env.ncpus }}
120126
127+
# - name: Setup tmate session
128+
# if: ${{ runner.os != 'windows' }}
129+
# uses: mxschmitt/action-tmate@v3
130+
121131
- name: Test xeus-cpp C++ Unix Systems
122132
if: ${{ runner.os != 'windows' }}
123133
shell: bash -l {0}
124134
run: |
125135
cd build/test
126136
./test_xeus_cpp
127-
timeout-minutes: 4
137+
timeout-minutes: 30
128138

129139
- name: Test xeus-cpp C++ Windows Systems
130140
if: ${{ runner.os == 'windows' }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,6 @@ __pycache__/
3838
build/
3939

4040
bld
41+
42+
# API Keys for LLM
43+
*_api_key.txt

CMakeLists.txt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ set(XEUS_CPP_SRC
203203
src/xoptions.cpp
204204
src/xparser.cpp
205205
src/xutils.cpp
206+
src/xmagics/xassist.hpp
207+
src/xmagics/xassist.cpp
206208
)
207209

208210
if(EMSCRIPTEN)
@@ -309,9 +311,36 @@ macro(xeus_cpp_create_target target_name linkage output_name)
309311
else ()
310312
set(XEUS_CPP_XEUS_TARGET xeus-static)
311313
endif ()
314+
315+
if(WIN32)
316+
# Set the MSVC runtime library
317+
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
312318

313-
target_link_libraries(${target_name} PUBLIC ${XEUS_CPP_XEUS_TARGET} clangCppInterOp pugixml argparse::argparse)
319+
# Find libcurl
320+
find_package(CURL REQUIRED)
314321

322+
# Add CURL_STATICLIB definition if linking statically
323+
if (CURL_STATICLIB)
324+
target_compile_definitions(${target_name} PUBLIC CURL_STATICLIB)
325+
endif()
326+
327+
# Link against the correct libcurl target
328+
if (CURL_FOUND)
329+
target_include_directories(${target_name} PRIVATE ${CURL_INCLUDE_DIRS})
330+
target_link_libraries(${target_name} PRIVATE ${CURL_LIBRARIES})
331+
endif()
332+
333+
# Existing target_link_libraries call, adjusted for clarity
334+
target_link_libraries(${target_name} PUBLIC ${XEUS_CPP_XEUS_TARGET} clangCppInterOp pugixml argparse::argparse)
335+
336+
# Ensure all linked libraries use the same runtime library
337+
if (MSVC)
338+
target_compile_options(${target_name} PRIVATE "/MD$<$<CONFIG:Debug>:d>")
339+
endif()
340+
else()
341+
target_link_libraries(${target_name} PUBLIC ${XEUS_CPP_XEUS_TARGET} clangCppInterOp pugixml argparse::argparse curl)
342+
endif()
343+
315344
if (WIN32 OR CYGWIN)
316345
#
317346
elseif (APPLE)

docs/source/gemini.png

6.44 KB
Loading

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ The Xeus-Cpp is a Jupyter kernel for the C++ programming language
3434
InstallationAndUsage
3535
UsingXeus-Cpp
3636
tutorials
37+
magics
3738
dev-build-options
3839
debug
3940
FAQ

docs/source/magics.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Magics commands
2+
--------------------
3+
4+
Magics are special commands for the kernel that are not part of the C++
5+
programming language.
6+
7+
There are defined with the symbol ``%`` for a line magic and ``%%`` for a cell
8+
magic.
9+
10+
Here are the magics available in xeus-cpp.
11+
12+
%%xassist
13+
========================
14+
15+
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.
16+
17+
- Save the api key
18+
19+
.. code::
20+
21+
%%xassist model --save-key
22+
key
23+
24+
- Use the model
25+
26+
.. code::
27+
28+
%%xassist model
29+
prompt
30+
31+
- Example
32+
33+
.. image:: gemini.png
34+
35+
A new prompt is sent to the model everytime and the functionality to use previous context will be added soon.

environment-wasm-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ dependencies:
55
- cmake
66
- emsdk >=3.1.11
77
- empack >=2.0.1
8+
- curl

environment-wasm-host.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ dependencies:
88
- xeus
99
- CppInterOp>=1.3.0
1010
- cpp-argparse
11-
- pugixml
11+
- pugixml

src/xinterpreter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "xinput.hpp"
2929
#include "xinspect.hpp"
3030
#include "xmagics/os.hpp"
31+
#include "xmagics/xassist.hpp"
3132
#include "xparser.hpp"
3233
#include "xsystem.hpp"
3334

@@ -404,5 +405,6 @@ __get_cxx_version ()
404405
// preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("file", writefile());
405406
// preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("timeit", timeit(&m_interpreter));
406407
// preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("python", pythonexec());
408+
preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("xassist", xassist());
407409
}
408410
}

src/xmagics/xassist.cpp

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/************************************************************************************
2+
* Copyright (c) 2023, xeus-cpp contributors *
3+
* Copyright (c) 2023, Johan Mabille, Loic Gouarin, Sylvain Corlay, Wolf Vollprecht *
4+
* *
5+
* Distributed under the terms of the BSD 3-Clause License. *
6+
* *
7+
* The full license is in the file LICENSE, distributed with this software. *
8+
************************************************************************************/
9+
#include "xassist.hpp"
10+
11+
#define CURL_STATICLIB
12+
#include <curl/curl.h>
13+
#include <fstream>
14+
#include <iostream>
15+
#include <nlohmann/json.hpp>
16+
#include <string>
17+
#include <unordered_set>
18+
19+
using json = nlohmann::json;
20+
21+
namespace xcpp
22+
{
23+
class APIKeyManager
24+
{
25+
public:
26+
27+
static void saveApiKey(const std::string& model, const std::string& apiKey)
28+
{
29+
std::string apiKeyFilePath = model + "_api_key.txt";
30+
std::ofstream out(apiKeyFilePath);
31+
if (out)
32+
{
33+
out << apiKey;
34+
out.close();
35+
std::cout << "API key saved for model " << model << std::endl;
36+
}
37+
else
38+
{
39+
std::cerr << "Failed to open file for writing API key for model " << model << std::endl;
40+
}
41+
}
42+
43+
// Method to load the API key for a specific model
44+
static std::string loadApiKey(const std::string& model)
45+
{
46+
std::string apiKeyFilePath = model + "_api_key.txt";
47+
std::ifstream in(apiKeyFilePath);
48+
std::string apiKey;
49+
if (in)
50+
{
51+
std::getline(in, apiKey);
52+
in.close();
53+
return apiKey;
54+
}
55+
56+
std::cerr << "Failed to open file for reading API key for model " << model << std::endl;
57+
return "";
58+
}
59+
};
60+
61+
class CurlHelper
62+
{
63+
private:
64+
65+
CURL* m_curl;
66+
curl_slist* m_headers;
67+
68+
public:
69+
70+
CurlHelper()
71+
: m_curl(curl_easy_init())
72+
, m_headers(curl_slist_append(nullptr, "Content-Type: application/json"))
73+
{
74+
}
75+
76+
~CurlHelper()
77+
{
78+
if (m_curl)
79+
{
80+
curl_easy_cleanup(m_curl);
81+
}
82+
if (m_headers)
83+
{
84+
curl_slist_free_all(m_headers);
85+
}
86+
}
87+
88+
// Delete copy constructor and copy assignment operator
89+
CurlHelper(const CurlHelper&) = delete;
90+
CurlHelper& operator=(const CurlHelper&) = delete;
91+
92+
// Delete move constructor and move assignment operator
93+
CurlHelper(CurlHelper&&) = delete;
94+
CurlHelper& operator=(CurlHelper&&) = delete;
95+
96+
std::string
97+
performRequest(const std::string& url, const std::string& postData, const std::string& authHeader = "")
98+
{
99+
if (!authHeader.empty())
100+
{
101+
m_headers = curl_slist_append(m_headers, authHeader.c_str());
102+
}
103+
104+
curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
105+
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headers);
106+
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, postData.c_str());
107+
108+
std::string response;
109+
curl_easy_setopt(
110+
m_curl,
111+
CURLOPT_WRITEFUNCTION,
112+
+[](const char* in, size_t size, size_t num, std::string* out)
113+
{
114+
const size_t totalBytes(size * num);
115+
out->append(in, totalBytes);
116+
return totalBytes;
117+
}
118+
);
119+
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);
120+
121+
CURLcode res = curl_easy_perform(m_curl);
122+
if (res != CURLE_OK)
123+
{
124+
std::cerr << "CURL request failed: " << curl_easy_strerror(res) << std::endl;
125+
return "";
126+
}
127+
128+
return response;
129+
}
130+
};
131+
132+
std::string gemini(const std::string& cell, const std::string& key)
133+
{
134+
CurlHelper curlHelper;
135+
const std::string url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key="
136+
+ key;
137+
const std::string postData = R"({"contents": [{"parts":[{"text": ")" + cell + R"("}]}]})";
138+
139+
std::string response = curlHelper.performRequest(url, postData);
140+
141+
json j = json::parse(response);
142+
if (j.find("error") != j.end())
143+
{
144+
std::cerr << "Error: " << j["error"]["message"] << std::endl;
145+
return "";
146+
}
147+
148+
return j["candidates"][0]["content"]["parts"][0]["text"];
149+
}
150+
151+
std::string openai(const std::string& cell, const std::string& key)
152+
{
153+
CurlHelper curlHelper;
154+
const std::string url = "https://api.openai.com/v1/chat/completions";
155+
const std::string postData = R"({
156+
"model": "gpt-3.5-turbo-16k",
157+
"messages": [{"role": "user", "content": ")"
158+
+ cell + R"("}],
159+
"temperature": 0.7
160+
})";
161+
std::string authHeader = "Authorization: Bearer " + key;
162+
163+
std::string response = curlHelper.performRequest(url, postData, authHeader);
164+
165+
json j = json::parse(response);
166+
167+
if (j.find("error") != j.end())
168+
{
169+
std::cerr << "Error: " << j["error"]["message"] << std::endl;
170+
return "";
171+
}
172+
173+
return j["choices"][0]["message"]["content"];
174+
}
175+
176+
void xassist::operator()(const std::string& line, const std::string& cell)
177+
{
178+
try
179+
{
180+
std::istringstream iss(line);
181+
std::vector<std::string> tokens(
182+
std::istream_iterator<std::string>{iss},
183+
std::istream_iterator<std::string>()
184+
);
185+
186+
std::vector<std::string> models = {"gemini", "openai"};
187+
std::string model = tokens[1];
188+
189+
if (std::find(models.begin(), models.end(), model) == models.end())
190+
{
191+
std::cerr << "Model not found." << std::endl;
192+
return;
193+
}
194+
195+
APIKeyManager api;
196+
if (tokens[2] == "--save-key")
197+
{
198+
xcpp::APIKeyManager::saveApiKey(model, cell);
199+
return;
200+
}
201+
202+
std::string key = xcpp::APIKeyManager::loadApiKey(model);
203+
if (key.empty())
204+
{
205+
std::cerr << "API key for model " << model << " is not available." << std::endl;
206+
return;
207+
}
208+
209+
std::string response;
210+
if (model == "gemini")
211+
{
212+
response = gemini(cell, key);
213+
}
214+
else if (model == "openai")
215+
{
216+
response = openai(cell, key);
217+
}
218+
219+
std::cout << response;
220+
}
221+
catch (const std::runtime_error& e)
222+
{
223+
std::cerr << "Caught an exception: " << e.what() << std::endl;
224+
}
225+
catch (...)
226+
{
227+
std::cerr << "Caught an unknown exception" << std::endl;
228+
}
229+
}
230+
} // namespace xcpp

0 commit comments

Comments
 (0)