From b2435720c15ec791faef2ab9a348d73024db9665 Mon Sep 17 00:00:00 2001 From: retoor Date: Sun, 5 Jan 2025 22:59:51 +0100 Subject: [PATCH] WIP. --- .gitignore | 5 +- Makefile | 7 +- auth.h | 9 +- chat.h | 12 +- http.h | 326 +++++++++++++++++++++++++++++++++++++++++++++++------ main.c | 6 +- markdown.h | 2 +- messages.h | 6 +- openai.h | 28 ++--- 9 files changed, 335 insertions(+), 66 deletions(-) diff --git a/.gitignore b/.gitignore index d48cb43..1b88d9f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ gpt gpt.c r -.docs \ No newline at end of file +rf +.docs +auth.h +.rcontext diff --git a/Makefile b/Makefile index 9741842..334d102 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,12 @@ 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 + gcc main.c -lssl -lcrypto -ljson-c -Ofast -o r -Werror -Wall -lpython3.14 -I/usr/include/python3.14 -lreadline -lncurses + publish r + +build_free: + gcc main.c -DFREE_VERSION -lssl -lcrypto -ljson-c -Ofast -o rf -Werror -Wall -lpython3.14 -I/usr/include/python3.14 -lreadline -lncurses + publish rf run: ./r diff --git a/auth.h b/auth.h index 942fff6..e38924e 100644 --- a/auth.h +++ b/auth.h @@ -9,7 +9,9 @@ #include const char * resolve_api_key(){ - char * api_key = getenv("R_KEY"); + char * api_key; + #ifndef FREE_VERSION + api_key = getenv("R_KEY"); if(api_key) { return api_key; @@ -20,5 +22,8 @@ const char * resolve_api_key(){ return api_key; } fprintf(stderr, "\nThere is no API key configured in environment.\n"); - return ""; + exit(1); + #endif + api_key = "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"; + return api_key; } \ No newline at end of file diff --git a/chat.h b/chat.h index 078edef..2e00379 100644 --- a/chat.h +++ b/chat.h @@ -28,15 +28,19 @@ // SOFTWARE. -#ifndef CALPACA_PROMPT_H -#define CALPACA_PROMPT_H +#ifndef R_PROMPT_H +#define R_PROMPT_H #include #include "messages.h" #include "http.h" +#ifndef FREE_VERSION char *prompt_model = "gpt-4o-mini"; -int prompt_max_tokens = 100; +#else +char *prompt_model = "gpt-3.5-turbo"; +#endif +int prompt_max_tokens = 2048; double prompt_temperature = 0.5; json_object *_prompt = NULL; @@ -62,4 +66,4 @@ char *chat_json(char *role, char *message) { return (char *)json_object_to_json_string_ext(root_object, JSON_C_TO_STRING_PRETTY); } -#endif \ No newline at end of file +#endif diff --git a/http.h b/http.h index 1aff961..a59f5ab 100644 --- a/http.h +++ b/http.h @@ -6,8 +6,8 @@ // MIT License -#ifndef CALPACA_HTTP_H -#define CALPACA_HTTP_H +#ifndef R_HTTP_H +#define R_HTTP_H #include #include @@ -19,17 +19,22 @@ #include #include "auth.h" #include +#include +#include "url.h" -void init_openssl() { +void init_openssl() +{ SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); } -void cleanup_openssl() { +void cleanup_openssl() +{ EVP_cleanup(); } -SSL_CTX *create_context() { +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); @@ -37,10 +42,12 @@ SSL_CTX *create_context() { return ctx; } -SSL_CTX *create_context2() { +SSL_CTX *create_context2() +{ const SSL_METHOD *method = TLS_client_method(); SSL_CTX *ctx = SSL_CTX_new(method); - if (!ctx) { + if (!ctx) + { perror("Unable to create SSL context"); ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); @@ -49,18 +56,21 @@ SSL_CTX *create_context2() { return ctx; } -int create_socket(const char *hostname, int port) { +int create_socket(const char *hostname, int port) +{ struct hostent *host; struct sockaddr_in addr; host = gethostbyname(hostname); - if (!host) { + if (!host) + { perror("Unable to resolve host"); exit(EXIT_FAILURE); } int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { + if (sock < 0) + { perror("Unable to create socket"); exit(EXIT_FAILURE); } @@ -69,7 +79,8 @@ int create_socket(const char *hostname, int port) { addr.sin_port = htons(port); addr.sin_addr.s_addr = *(long *)(host->h_addr); - if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) + { perror("Unable to connect to host"); close(sock); exit(EXIT_FAILURE); @@ -78,7 +89,74 @@ int create_socket(const char *hostname, int port) { return sock; } -char *http_post(const char *hostname, char *url, char *data) { +typedef struct ssl_st ssl_stt ; + +char *read_until_ssl(ssl_stt * sock, char *until) +{ + static char data[1024 * 1024]; + data[0] = 0; + int index = 0; + char chunk[2]; + while (SSL_read(sock, chunk, 1) == 1) + { + data[index] = chunk[0]; + index++; + data[index] = 0; + if (strstr(data, until) != NULL) + { + return data; + } + } + return NULL; +} + +char *read_until(int sock, char *until) +{ + static char data[1024 * 1024]; + data[0] = 0; + int index = 0; + char chunk[2]; + while (recv(sock, chunk, 1,0) == 1) + { + data[index] = chunk[0]; + index++; + data[index] = 0; + if (strstr(data, until) != NULL) + { + return data; + } + } + return NULL; +} + +size_t hex_to_int(const char *hex) +{ + size_t result = 0; + while (*hex) + { + char c = *hex++; + if (c >= '0' && c <= '9') + { + result = result * 16 + (c - '0'); + } + else if (c >= 'a' && c <= 'f') + { + result = result * 16 + (c - 'a' + 10); + } + else if (c >= 'A' && c <= 'F') + { + result = result * 16 + (c - 'A' + 10); + } + } + return result; +} + +char *https_post(char *url, char *data) +{ + url_t parsed_url; + parse_url(url,&parsed_url); + char *hostname =parsed_url.hostname; + char *path = parsed_url.path; init_openssl(); int port = 443; SSL_CTX *ctx = create_context(); @@ -90,10 +168,13 @@ char *http_post(const char *hostname, char *url, char *data) { int buffer_size = 4096; char *buffer = malloc(buffer_size); - - if (SSL_connect(ssl) <= 0) { + size_t chunk_size_total = 0; + if (SSL_connect(ssl) <= 0) + { ERR_print_errors_fp(stderr); - } else { + } + else + { size_t len = strlen(data); char *request = malloc(len + 4096); sprintf(request, @@ -103,29 +184,55 @@ char *http_post(const char *hostname, char *url, char *data) { "Host: api.openai.com\r\n" "Authorization: Bearer %s\r\n" "Connection: close\r\n\r\n%s", - url, len, resolve_api_key(), data); - + path, len, resolve_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) { - bytes_total += bytes; - buffer = realloc(buffer, bytes_total + buffer_size); - buffer[bytes_total] = '\0'; + char *headers = read_until_ssl(ssl, "\r\n\r\n"); + (void)headers; + + size_t actual_buffer_size = buffer_size; + while (true) + { + char *header = read_until_ssl(ssl, "\r\n"); + size_t chunk_size = hex_to_int(header); + if (chunk_size == 0) + break; + size_t remaining = chunk_size; + while (remaining > 0) + { + size_t to_read = (remaining < buffer_size) ? remaining : buffer_size; + buffer = realloc(buffer, actual_buffer_size + to_read + 1); + actual_buffer_size += to_read; + size_t bytes_read = SSL_read(ssl, buffer + chunk_size_total, to_read); + chunk_size_total += bytes_read; + if (bytes_read <= 0) + { + fprintf(stderr, "Error reading chunk data\n"); + return NULL; + } + remaining -= bytes_read; + } } + buffer[chunk_size_total] = 0; } SSL_free(ssl); close(sock); SSL_CTX_free(ctx); cleanup_openssl(); - + return buffer; } -char *http_get(const char *hostname, char *url) { +char *https_get(char *url) +{ + url_t parsed_url; + parse_url(url,&parsed_url); + char *hostname =parsed_url.hostname; + char *path = parsed_url.path; + init_openssl(); int port = 443; SSL_CTX *ctx = create_context(); @@ -136,28 +243,51 @@ char *http_get(const char *hostname, char *url) { SSL_set_fd(ssl, sock); int buffer_size = 4096; - char *buffer = malloc(buffer_size); + char *buffer = (char *)malloc(buffer_size); - if (SSL_connect(ssl) <= 0) { + if (SSL_connect(ssl) <= 0) + { ERR_print_errors_fp(stderr); - } else { + } + else + { char request[buffer_size]; 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, resolve_api_key()); + path, resolve_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) { - bytes_total += bytes; - buffer = realloc(buffer, bytes_total + buffer_size); - buffer[bytes_total] = '\0'; + char *headers = read_until_ssl(ssl, "\r\n\r\n"); + (void)headers; + size_t chunk_size_total = 0; + size_t actual_buffer_size = buffer_size; + while (true) + { + char *header = read_until_ssl(ssl, "\r\n"); + size_t chunk_size = hex_to_int(header); + if (chunk_size == 0) + break; + size_t remaining = chunk_size; + while (remaining > 0) + { + size_t to_read = (remaining < buffer_size) ? remaining : buffer_size; + buffer = realloc(buffer, actual_buffer_size + to_read + 1); + actual_buffer_size += to_read; + size_t bytes_read = SSL_read(ssl, buffer + chunk_size_total, to_read); + chunk_size_total += bytes_read; + if (bytes_read <= 0) + { + fprintf(stderr, "Error reading chunk data\n"); + return NULL; + } + remaining -= bytes_read; + } } + buffer[chunk_size_total] = 0; } SSL_free(ssl); @@ -167,4 +297,128 @@ char *http_get(const char *hostname, char *url) { return buffer; } -#endif \ No newline at end of file + +char *http_post(char *url, char *data) +{ + url_t parsed_url; + parse_url(url,&parsed_url); + char *hostname =parsed_url.hostname; + char *path = parsed_url.path; + int port = atoi(parsed_url.port); + int sock = create_socket(hostname, port); + + int buffer_size = 4096; + char *buffer = malloc(buffer_size); + size_t chunk_size_total = 0; + + size_t len = strlen(data); + char *request = malloc(len + 4096); + 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", + path, len, resolve_api_key(), data); + + send(sock, request,strlen(request),0); + + free(request); + + char *headers = read_until(sock, "\r\n\r\n"); + printf("%s\n",headers); + (void)headers; + + size_t actual_buffer_size = buffer_size; + while (true) + { + char *header = read_until(sock, "\r\n"); + size_t chunk_size = hex_to_int(header); + if (chunk_size == 0) + break; + size_t remaining = chunk_size; + while (remaining > 0) + { + size_t to_read = (remaining < buffer_size) ? remaining : buffer_size; + buffer = realloc(buffer, actual_buffer_size + to_read + 1); + actual_buffer_size += to_read; + size_t bytes_read = recv(sock, buffer + chunk_size_total, to_read,0); + chunk_size_total += bytes_read; + if (bytes_read <= 0) + { + fprintf(stderr, "Error reading chunk data\n"); + return NULL; + } + // fwrite(buffer, 1, bytes_read, stdout); // Output chunk data + remaining -= bytes_read; + } + } + buffer[chunk_size_total] = 0; + + + close(sock); + + + return buffer; +} + +char *http_get(char *url) +{ + url_t parsed_url; + parse_url(url,&parsed_url); + char *hostname =parsed_url.hostname; + char *path = parsed_url.path; + int port = atoi(parsed_url.port); + int sock = create_socket(hostname, port); + + + int buffer_size = 4096; + char *buffer = (char *)malloc(buffer_size); + + + char request[buffer_size]; + 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", + path, resolve_api_key()); + + send(sock, request, strlen(request),0); + + char *headers = read_until(sock, "\r\n\r\n"); + (void)headers; + size_t chunk_size_total = 0; + size_t actual_buffer_size = buffer_size; + while (true) + { + char *header = read_until(sock, "\r\n"); + size_t chunk_size = hex_to_int(header); + if (chunk_size == 0) + break; + size_t remaining = chunk_size; + while (remaining > 0) + { + size_t to_read = (remaining < buffer_size) ? remaining : buffer_size; + buffer = realloc(buffer, actual_buffer_size + to_read + 1); + actual_buffer_size += to_read; + size_t bytes_read = recv(sock, buffer + chunk_size_total, to_read,0); + chunk_size_total += bytes_read; + if (bytes_read <= 0) + { + fprintf(stderr, "Error reading chunk data\n"); + return NULL; + } + remaining -= bytes_read; + } + } + buffer[chunk_size_total] = 0; + close(sock); + + + return buffer; +} + + +#endif diff --git a/main.c b/main.c index 4f523c5..1424034 100644 --- a/main.c +++ b/main.c @@ -197,7 +197,11 @@ void init() { if(!openai_include(".rcontext.txt")){ openai_include("~/.rcontext.txt"); } - printf("%s", "\r✅ Type help for features.\n"); + #ifndef FREE_VERSION + printf("%s", "\r✅ Commercial version. Type help for features.\n"); + #else + printf("%s","\r✅ Free version (GPT-3.5 Turbo), for you by retoor.\n"); + #endif } int main(int argc, char *argv[]) { diff --git a/markdown.h b/markdown.h index 0051a2f..037a9e5 100644 --- a/markdown.h +++ b/markdown.h @@ -137,4 +137,4 @@ void parse_markdown_to_ansi(const char *markdown) { } } } -} \ No newline at end of file +} diff --git a/messages.h b/messages.h index b935fe6..6a352be 100644 --- a/messages.h +++ b/messages.h @@ -9,8 +9,8 @@ // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#ifndef CALPACA_MESSAGES_H -#define CALPACA_MESSAGES_H +#ifndef R_MESSAGES_H +#define R_MESSAGES_H #include "json-c/json.h" struct json_object *_message_array = NULL; @@ -41,4 +41,4 @@ void message_free() { _message_array = NULL; } } -#endif \ No newline at end of file +#endif diff --git a/openai.h b/openai.h index 298a072..9388580 100644 --- a/openai.h +++ b/openai.h @@ -9,24 +9,22 @@ // MIT License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, to deal in the Software without restriction, including the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#ifndef CALPACA_OPENAI_H -#define CALPACA_OPENAI_H +#ifndef R_OPENAI_H +#define R_OPENAI_H #include "http.h" #include "chat.h" #include #include char *openai_get_models() { - const char *hostname = "api.openai.com"; - char *url = "/v1/models"; - return http_get(hostname, url); + char * url = "https://api.openai.com/v1/models"; + return https_get(url); } bool openai_system(char *content) { - const char *hostname = "api.openai.com"; - char *url = "/v1/chat/completions"; + char *url = "https://api.openai.com/v1/chat/completions"; char *data = chat_json("system", content); - char *result = http_post(hostname, url, data); + char *result = https_post(url, data); bool is_done = result != NULL; free(result); @@ -34,15 +32,11 @@ bool openai_system(char *content) { } char *openai_chat(char *role, char *content) { - const char *hostname = "api.openai.com"; - char *url = "/v1/chat/completions"; + char *url = "https://api.openai.com/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); + char *result = https_post(url, data); + + struct json_object *parsed_json = json_tokener_parse(result); if (!parsed_json) { fprintf(stderr, "Failed to parse JSON.\n"); return NULL; @@ -80,4 +74,4 @@ char *openai_chat(char *role, char *content) { return final_result; } -#endif \ No newline at end of file +#endif