Initial commit.

This commit is contained in:
retoor 2025-01-04 06:00:03 +01:00
commit 4d82322545
13 changed files with 870 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.vscode
.venv
.history
.backup*
auth.h
context.txt
gpt
gpt.c
r
.docs

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
all: build run
build:
gcc main.c -lssl -lcrypto -ljson-c -Ofast -o r -Werror -Wall -lpython3.14 -I/usr/include/python3.14 -lreadline -lncurses
run:
./r

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# R
## Project description
R

32
cgi-bin/gpt.py Executable file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env python3
# Not written by retoor! This is generated boiler plate to give an example!
import cgi
import cgitb
from xmlrpc.client import ServerProxy
client = ServerProxy("https://api.molodetz.nl/rpc")
ask_gpt = client.gpt4o_mini
cgitb.enable()
print("Content-Type: text/html")
print()
import pathlib
form = cgi.FieldStorage()
question = form.getvalue("question", "")
page_source = pathlib.Path(__file__).parent.joinpath("gpt_template.html").read_text()
if question:
try:
response = ask_gpt(question)
except Exception as e:
response = f"Error: {e}"
page_source = page_source.replace("...", response)
page_source = page_source.replace("display:none;","")
print(page_source)

64
cgi-bin/gpt_template.html Normal file
View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GPT Example</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f9;
}
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
}
textarea, input {
width: 100%;
padding: 10px;
margin: 10px 0;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
}
input[type="submit"] {
background-color: #4CAF50;
color: white;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #45a049;
}
.response-box {
padding: 10px;
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 4px;
min-height: 100px;
}
</style>
</head>
<body>
<div class="container">
<h1>Ask GPT</h1>
<div style="display:none;" class="response-box">
<p id="response">...</p>
</div>
<form action="/cgi-bin/gpt.py" method="post">
<textarea name="question" rows="4" placeholder="Your prompt.."></textarea>
<input type="submit" value="Get Answer">
</form>
</div>
</body>
</html>

31
chat.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef CALPACA_PROMPT_H
#define CALPACA_PROMPT_H
#include <json-c/json.h>
#include "messages.h"
#include "http.h"
char * prompt_model = "gpt-4o-mini";
int prompt_max_tokens = 100;
double prompt_temperature = 0.5;
json_object * _prompt =NULL;
void chat_free(){
if(_prompt == NULL)
return;
json_object_put(_prompt);
_prompt = NULL;
}
char * chat_json(char * role, char * message){
chat_free();
message_add(role,message);
struct json_object *root_object = json_object_new_object();
json_object_object_add(root_object, "model", json_object_new_string(prompt_model));
json_object_object_add(root_object, "messages", message_list());
json_object_object_add(root_object, "max_tokens", json_object_new_int(prompt_max_tokens));
json_object_object_add(root_object, "temperature", json_object_new_double(prompt_temperature));
return (char *)json_object_to_json_string_ext(root_object, JSON_C_TO_STRING_PRETTY);
}
#endif

210
http.h Normal file
View File

@ -0,0 +1,210 @@
#ifndef CALPACA_HTTP_H
#define CALPACA_HTTP_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "auth.h"
#include <json-c/json.h>
void init_openssl()
{
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl()
{
EVP_cleanup();
}
SSL_CTX *create_context()
{
const SSL_METHOD *method = TLS_method();
SSL_CTX *ctx = SSL_CTX_new(method);
SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/ca-certificates.crt", NULL);
return ctx;
}
SSL_CTX *create_context2()
{
const SSL_METHOD *method;
SSL_CTX *ctx;
method = TLS_client_method();
ctx = SSL_CTX_new(method);
if (!ctx)
{
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
int create_socket(const char *hostname, int port)
{
struct hostent *host;
struct sockaddr_in addr;
host = gethostbyname(hostname);
if (!host)
{
perror("Unable to resolve host");
exit(EXIT_FAILURE);
}
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror("Unable to create socket");
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long *)(host->h_addr);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
perror("Unable to connect to host");
close(sock);
exit(EXIT_FAILURE);
}
return sock;
}
char *http_post(const char *hostname, char *url, char *data)
{
init_openssl();
int port = 443;
SSL_CTX *ctx = create_context();
int sock = create_socket(hostname, port);
SSL *ssl = SSL_new(ctx);
SSL_set_connect_state(ssl);
SSL_set_tlsext_host_name(ssl, hostname);
SSL_set_fd(ssl, sock);
int buffer_size = 4096;
char *buffer = (char *)malloc(buffer_size);
if (SSL_connect(ssl) <= 0)
{
ERR_print_errors_fp(stderr);
}
else
{
//printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
size_t len = strlen(data);
char *request = (char *)malloc(len + 4096);
request[0] = 0;
sprintf(request,
"POST %s HTTP/1.1\r\n"
"Content-Length: %ld\r\n"
"Content-Type: application/json\r\n"
"Host: api.openai.com\r\n"
"Authorization: Bearer %s\r\n"
"Connection: close\r\n\r\n%s",
url, len, api_key, data);
SSL_write(ssl, request, strlen(request));
free(request);
int bytes;
int bytes_total = 0;
while ((bytes = SSL_read(ssl, buffer + bytes_total, buffer_size - 1)) > 0)
{
if (bytes <= 0)
{
break;
}
bytes_total += bytes;
buffer = realloc(buffer, bytes_total + buffer_size);
buffer[bytes_total] = '\0';
}
buffer[bytes_total] = '\0';
}
SSL_free(ssl);
close(sock);
SSL_CTX_free(ctx);
cleanup_openssl();
return buffer;
}
char *http_get(const char *hostname, char *url)
{
init_openssl();
int port = 443;
SSL_CTX *ctx = create_context();
int sock = create_socket(hostname, port);
SSL *ssl = SSL_new(ctx);
SSL_set_connect_state(ssl);
SSL_set_tlsext_host_name(ssl, hostname);
SSL_set_fd(ssl, sock);
int buffer_size = 4096;
char *buffer = (char *)malloc(buffer_size * sizeof(char));
if (SSL_connect(ssl) <= 0)
{
ERR_print_errors_fp(stderr);
}
else
{
//printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
char request[buffer_size];
request[0] = 0;
sprintf(request,
"GET %s HTTP/1.1\r\n"
"Host: api.openai.com\r\n"
"Authorization: Bearer %s\r\n"
"Connection: close\r\n\r\n",
url, api_key);
SSL_write(ssl, request, strlen(request));
int bytes;
int bytes_total = 0;
while ((bytes = SSL_read(ssl, buffer + bytes_total, buffer_size - 1)) > 0)
{
if (bytes <= 0)
{
break;
}
bytes_total += bytes;
buffer = realloc(buffer, bytes_total + buffer_size);
buffer[bytes_total] = '\0';
}
}
SSL_free(ssl);
close(sock);
SSL_CTX_free(ctx);
cleanup_openssl();
return buffer;
}
#endif

53
line.h Normal file
View File

@ -0,0 +1,53 @@
#include <readline/readline.h>
#include <readline/history.h>
#define HISTORY_FILE "~/.calpaca_history"
bool line_initialized =false;
char *line_command_generator(const char *text, int state) {
static int list_index, len;
const char *commands[] = {"help", "exit", "list", "review","refactor","opfuscate", NULL};
if (!state) {
list_index = 0;
len = strlen(text);
}
while (commands[list_index]) {
const char *command = commands[list_index++];
if (strncmp(command, text, len) == 0) {
return strdup(command);
}
}
return NULL;
}
char **line_command_completion(const char *text, int start, int end) {
rl_attempted_completion_over = 1;
return rl_completion_matches(text, line_command_generator);
}
void line_init(){
if(!line_initialized)
{
rl_attempted_completion_function = line_command_completion;
line_initialized = true;
read_history(HISTORY_FILE);
}
}
char * line_read(char * prefix) {
char * data = readline(prefix);
if(!(data || *data)){
return NULL;
}
return data;
}
void line_add_history(char * data){
read_history(HISTORY_FILE);
add_history(data);
write_history(HISTORY_FILE);
}

185
main.c Normal file
View File

@ -0,0 +1,185 @@
#include "openai.h"
#include "markdown.h"
#include "plugin.h"
#include "line.h"
#include <locale.h>
#include <stdio.h>
char * get_prompt_from_args(int c, char **argv){
char * prompt = malloc(1024*1024 + 1);
prompt[0] = 0;
for(int i = 1; i < c; i++){
if(argv[i][0] == '-')
break;
strncat(prompt, argv[i], 1024*1024);
if(i < c - 1){
strncat(prompt, " ", 1024*1024);
}else{
strncat(prompt, ".", 1024*1024);
}
}
if(!*prompt){
free(prompt);
return NULL;
}
return prompt;
}
bool try_prompt(int argc,char*argv[]){
char * prompt = get_prompt_from_args(argc, argv);
if(prompt != NULL){
char * response = openai_chat("user",prompt);
parse_markdown_to_ansi(response);
printf("\n");
free(response);
free(prompt);
return true;
}
return false;
}
void help();
void render(char *);
void serve(){
render("Starting server. *Put executables in a dir named cgi-bin and they will behave as webpages.*");
int res = system("python3 -m http.server --cgi");
// Thanks tsoding!
(void)res;
}
void render(char * content){
parse_markdown_to_ansi(content);
printf("\n\n");
}
void repl(){
line_init();
setbuf(stdout, NULL);
char *line;
char *previous_line = NULL;
while((line = line_read("> "))){
if(!line || !*line){
line = previous_line;
}
if(!line || !*line)
continue;
previous_line = line;
if(line[0] == '!'){
plugin_run(line + 1);
continue;
}
if(!strncmp(line,"exit", 4)){
exit(0);
}
if(!strncmp(line,"help",4)){
help();
continue;
}
if(!strncmp(line,"serve",5)){
serve();
}
if(!strncmp(line,"spar ",5)){
char * response = line+5;
while(true){
render(response);
sleep(2);
//line = line_read("> ");
//if(!*line)
response = openai_chat("user",response);
}
}
if(!strncmp(line,"ls",2) || !strncmp(line,"list",4)){
int offset = 2;
if(!strncmp(line,"list",4)){
offset = 4;
}
char * command = (char *)malloc(strlen(line) + 42);
command[0] = 0;
strcpy(command, "ls ");
strcat(command, line + offset);
int res = system(command);
(void)res;
free(command);
continue;
}
line_add_history(line);
char * response = openai_chat("user", line);
render(response);
free(response);
}
}
void help(){
char help_text[1024*1024] = {0};
char * template = "# Help\n"
"Written by retoor@molodetz.nl.\n\n"
"## Features\n"
" - navigate trough history using `arrows`.\n"
" - navigate trough history with **recursive search** using `ctrl+r`.\n"
" - **inception with python** for *incomming* and *outgoing* content.\n"
" - markdown and **syntax highlighting**.\n"
" - **execute python commands** with prefixing `!`\n"
" - list files of current workdirectory using `ls`.\n"
" - type `serve` to start a webserver with directory listing. Easy for network transfers.\n\n"
"## Configuration\n"
" - model temperature is %f.\n"
" - model name is %s.\n"
" - max tokens is %d.\n\n"
"## In development\n"
" - **google search** and actions with those results.\n"
" - **reminders**.\n"
" - predefined **templates** for **reviewing** / **refactoring** so you can personalize.\n";
sprintf(help_text,template,prompt_temperature,prompt_model,prompt_max_tokens);
render(help_text);
}
void openai_include(char * path){
FILE * file = fopen(path,"r");
if(file == NULL){
return;
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char * buffer = (char *)malloc(size);
size_t read = fread(buffer,1,size,file);
if(read == 0){
return;
}
fclose(file);
buffer[read] = 0;
openai_system(buffer);
free(buffer);
}
void init(){
line_init();
const char *locale = setlocale(LC_ALL, NULL);
char payload[4096] = {0};
sprintf(payload, "User locale is %s. User lang is %s.\n"
"You are Retoor. Use a lot of markdown in response.\n"
"Be confident and short in answers.\n"
"You divide things by zero if you have to."
, locale, locale);
printf("%s","Loading...");
openai_system(payload);
openai_include("context.txt");
printf("%s", "\rLoaded! Type help for feautures.\n");
}
int main(int argc, char *argv[]){
init();
if(try_prompt(argc,argv))
return 0;
repl();
return 0;
}

115
markdown.h Normal file
View File

@ -0,0 +1,115 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define RESET "\033[0m"
#define BOLD "\033[1m"
#define ITALIC "\033[3m"
#define FG_YELLOW "\033[33m"
#define FG_BLUE "\033[34m"
#define FG_CYAN "\033[36m"
int is_keyword(const char *word) {
const char *keywords[] = {"int", "float", "double", "char", "void", "if", "else", "while", "for", "return", "struct", "printf"};
for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
if (strcmp(word, keywords[i]) == 0) {
return 1;
}
}
return 0;
}
void highlight_code(const char *code) {
const char *ptr = code;
char buffer[256];
size_t index = 0;
while (*ptr) {
if ((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z') || (*ptr == '_')) {
while ((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z') || (*ptr >= '0' && *ptr <= '9') || (*ptr == '_')) {
buffer[index++] = *ptr++;
}
buffer[index] = '\0';
if (is_keyword(buffer)) {
printf(FG_BLUE "%s" RESET, buffer);
} else {
printf("%s", buffer);
}
index = 0;
} else if (*ptr >= '0' && *ptr <= '9') {
while (*ptr >= '0' && *ptr <= '9') {
buffer[index++] = *ptr++;
}
buffer[index] = '\0';
printf(FG_CYAN "%s" RESET, buffer);
index = 0;
} else {
putchar(*ptr);
ptr++;
}
}
}
void parse_markdown_to_ansi(const char *markdown) {
const char *ptr = markdown;
bool inside_code = false;
while (*ptr) {
if (*ptr == '`') {
inside_code = !inside_code;
if (inside_code) {
printf(FG_YELLOW);
} else {
printf(RESET);
}
ptr++;
continue;
}
if (inside_code) {
char code_buffer[256];
size_t index = 0;
while (*ptr && *ptr != '`') {
code_buffer[index++] = *ptr++;
}
code_buffer[index] = '\0';
highlight_code(code_buffer);
} else {
if (strncmp(ptr, "**", 2) == 0) {
printf(BOLD);
ptr += 2;
while (*ptr && strncmp(ptr, "**", 2) != 0) putchar(*ptr++);
if (*ptr == '*' && *(ptr + 1) == '*') ptr += 2;
printf(RESET);
}
else if (*ptr == '*' && (ptr == markdown || *(ptr - 1) != '*')) {
printf(ITALIC);
ptr++;
while (*ptr && *ptr != '*') putchar(*ptr++);
if (*ptr == '*') ptr++;
printf(RESET);
}
else if (strncmp(ptr, "### ", 4) == 0) {
printf(BOLD FG_YELLOW);
ptr += 4;
while (*ptr && *ptr != '\n') putchar(*ptr++);
printf(RESET "\n");
} else if (strncmp(ptr, "## ", 3) == 0) {
printf(BOLD FG_YELLOW);
ptr += 3;
while (*ptr && *ptr != '\n') putchar(*ptr++);
printf(RESET "\n");
} else if (strncmp(ptr, "# ", 2) == 0) {
printf(BOLD FG_YELLOW);
ptr += 2;
while (*ptr && *ptr != '\n') putchar(*ptr++);
printf(RESET "\n");
} else {
putchar(*ptr);
ptr++;
}
}
}
}

32
messages.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef CALPACA_MESSAGES_H
#define CALPACA_MESSAGES_H
#include "json-c/json.h"
struct json_object *_message_array = NULL;
struct json_object *message_list(){
if(_message_array == NULL){
_message_array = json_object_new_array();
}
return _message_array;
}
struct json_object *message_add(char * role, char * content){
struct json_object *messages = message_list();
struct json_object *message = json_object_new_object();
json_object_object_add(message, "role", json_object_new_string(role));
json_object_object_add(message, "content", json_object_new_string(content));
json_object_array_add(messages, message);
return message;
}
char * message_json(){
return (char *)json_object_to_json_string_ext(message_list(), JSON_C_TO_STRING_PRETTY);
}
void message_free(){
if(_message_array != NULL){
json_object_put(_message_array);
_message_array = NULL;
}
}
#endif

84
openai.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef CALPACA_OPENAI_H
#define CALPACA_OPENAI_H
#include "http.h"
#include "chat.h"
#include <string.h>
#include <stdbool.h>
char *openai_get_models()
{
const char *hostname = "api.openai.com";
char *url = "/v1/models";
char *result = http_get(hostname, url);
return result;
}
bool openai_system(char * content){
bool is_done = false;
const char *hostname = "api.openai.com";
char *url = "/v1/chat/completions";
char *data = chat_json("system", content);
char *result = http_post(hostname, url, data);
if(result){
is_done = true;
free(result);
}
return is_done;
}
char *openai_chat(char * role, char * content){
const char *hostname = "api.openai.com";
char *url = "/v1/chat/completions";
char *data = chat_json(role, content);
char *result = http_post(hostname, url, data);
char * body = strstr(result,"\r\n\r\n") +4;
body = strstr(body,"\r\n");
body = strstr(body,"\r\n");
*(body - 5) = 0;
struct json_object *parsed_json = json_tokener_parse(body);
if (!parsed_json) {
fprintf(stderr, "Failed to parse JSON.\n");
return NULL;
}
struct json_object *choices_array;
if (!json_object_object_get_ex(parsed_json, "choices", &choices_array)) {
fprintf(stderr, "Failed to get 'choices' array.\n");
json_object_put(parsed_json);
return NULL;
}
// Get the first element of the "choices" array
struct json_object *first_choice = json_object_array_get_idx(choices_array, 0);
if (!first_choice) {
fprintf(stderr, "Failed to get the first element of 'choices'.\n");
json_object_put(parsed_json);
return NULL;
}
// Extract the "message" object
struct json_object *message_object;
if (!json_object_object_get_ex(first_choice, "message", &message_object)) {
fprintf(stderr, "Failed to get 'message' object.\n");
json_object_put(parsed_json);
return NULL;
}
// Print the "message" object
// printf("Message object:\n%s\n", json_object_to_json_string_ext(message_object, JSON_C_TO_STRING_PRETTY));
message_add("assistant",(char *)json_object_get_string(json_object_object_get(message_object, "content")));
// Clean up
free(data);
free(result);
result = strdup((char *)json_object_get_string(json_object_object_get(message_object, "content")));
json_object_put(parsed_json);
//printf("Parsed JSON:\n%s\n", json_object_to_json_string_ext(parsed_json, JSON_C_TO_STRING_PRETTY));
return result;
}
#endif

43
plugin.h Normal file
View File

@ -0,0 +1,43 @@
#include <python3.14/Python.h>
#include <python3.14/structmember.h>
#include <stdbool.h>
bool plugin_initialized = false;
bool plugin_construct(){
if(plugin_initialized)
return true;
Py_Initialize();
// Check if Python initialized successfully
if (!Py_IsInitialized()) {
fprintf(stderr, "Failed to initialize Python interpreter\n");
return plugin_initialized;
}
plugin_initialized = true;
return plugin_initialized;
}
void plugin_run(char * src){
plugin_construct();
char * basics = "import sys\n"
"import os\n"
"import math\n"
"import pathlib\n"
"import subprocess\n"
"import time\n"
"from datetime import datetime\n"
"%s";
size_t length = strlen(basics) + strlen(src);
char * script = (char *)malloc(length + 1);
sprintf(script, basics, src);
script[length] = 0;
PyRun_SimpleString(script);
free(script);
}
void plugin_destruct(){
if(plugin_initialized)
Py_Finalize();
}