diff --git a/src/Home/Net/Programs/Skynet/HowToHost.TXT b/src/Home/Net/Programs/Skynet/HowToHost.TXT new file mode 100755 index 00000000..527741e0 --- /dev/null +++ b/src/Home/Net/Programs/Skynet/HowToHost.TXT @@ -0,0 +1,51 @@ +// This is an NGINX reverse proxy configuration file for the Skynet Middleware + +server { + // set whatever port you'd like + listen 9000; + // replace the server name with yours + server_name skynet.middleware.com; + + # Custom log locations + access_log /var/log/nginx/skynet_access.log; + error_log /var/log/nginx/skynet_error.log; + + location /gpt3 { + proxy_pass https://api.openai.com/v1/completions; # GPT-3 endpoint + + # Required for SSL verification with the upstream server (OpenAI) + proxy_ssl_server_name on; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + proxy_ssl_session_reuse off; + + # Headers for OpenAI + // replace the sk-XXXX with your API key + proxy_set_header Authorization "Bearer sk-XXXX"; + proxy_set_header Content-Type "application/json"; + + # Standard proxy headers + proxy_set_header Host api.openai.com; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /chat { + proxy_pass https://api.openai.com/v1/chat/completions; # ChatGPT endpoint + + # Required for SSL verification with the upstream server (OpenAI) + proxy_ssl_server_name on; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + proxy_ssl_session_reuse off; + + # Headers for OpenAI + // replace the sk-XXXX with your API key + proxy_set_header Authorization "Bearer sk-XXXX"; + proxy_set_header Content-Type "application/json"; + + # Standard proxy headers + proxy_set_header Host api.openai.com; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + +} \ No newline at end of file diff --git a/src/Home/Net/Programs/Skynet/Skynet.ZC b/src/Home/Net/Programs/Skynet/Skynet.ZC new file mode 100755 index 00000000..82c05dc0 --- /dev/null +++ b/src/Home/Net/Programs/Skynet/Skynet.ZC @@ -0,0 +1,170 @@ +#define SERVER_ADDR "skynet.middleware.com" +#define SERVER_PORT 9000 +#define INITIAL_BUF_SIZE 2048 +#define DEFAULT_SYSTEMPROMPT "You are a helpful assistant but roleplay as skynet from Terminator." +#include "::/Home/Net/Utilities/JSON/JSON" +class Message { + U8 role[10]; // "user" or "assistant" + U8 content[1024]; +} history[100]; +I64 currentHistoryCount = 0; + +U0 AppendToHistory(U8 *role, U8 *content) { + if (currentHistoryCount < 100) { + StrCopy(history[currentHistoryCount].role, role); + StrCopy(history[currentHistoryCount].content, content); + currentHistoryCount++; + } +} + +U8 *StrChr(U8 *Str,U8 Pivot) +{ + //U8 *Orig=Str; + while (*Str!=Pivot&&*Str!=0)Str++; + if (*Str==Pivot) return Str; + else return 0; +} +U8* RemoveBeforeJSON(U8 *input) { + U8 *jsonStart = StrChr(input, '{'); + + if (jsonStart != NULL) { + return jsonStart; + } + return NULL; // No JSON detected +} + +I64 Skynet(U8 *message, U8 *system, U8 *model) +{ + I64 sock, index, bufferSize = INITIAL_BUF_SIZE; + U8 *responseBuf = MAlloc(bufferSize); // Dynamic allocation of the buffer + + sock = TCPConnectionCreate(SERVER_ADDR, SERVER_PORT); + if (sock <= 0) + { + PrintErr("Failed to connect to middleware server"); + return sock; + } + + U8 *messages = StrPrint(NULL, "{\"role\": \"system\",\"content\": \"%s\"},", system); + + for (index = 0; index < currentHistoryCount; index++) { + U8 *msg = StrPrint(NULL, "{\"role\": \"%s\",\"content\": \"%s\"},", history[index].role, history[index].content); + messages = CatPrint(messages, msg); + Free(msg); + } + + U8 *currentUserMsg = StrPrint(NULL, "{\"role\": \"user\",\"content\": \"%s\"}", message); + messages = CatPrint(messages, currentUserMsg); + Free(currentUserMsg); + + U8 *payload = StrPrint(NULL, "{\"model\": \"%s\",\"messages\": [%s]}", model, messages); + Free(messages); + + //SysLog(payload); + + U8 *requestHeader = StrPrint(NULL, + "POST /chat HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "User-Agent: ZealOSClient/1.0\r\n" + "Accept: */*\r\n" + "Content-Type: application/json\r\n" + "Content-Length: %d\r\n" + "\r\n", + SERVER_ADDR, + SERVER_PORT, + StrLen(payload) + ); + + U8 *fullRequest = StrPrint(NULL, "%s%s", requestHeader, payload); + TCPSocketSendString(sock, fullRequest); + + Free(requestHeader); + Free(payload); + Free(fullRequest); + + I64 responseLength = TCPSocketReceive(sock, responseBuf, bufferSize - 1); + responseBuf[responseLength] = 0; // Null-terminate the buffer for safety + + if (responseLength == sizeof(responseBuf) - 1) { + PrintErr("Warning: responseBuf might be full. Some data could be truncated."); + } + // Check if buffer might be full + while (responseLength == bufferSize - 1) + { + // Double the buffer size and reallocate + bufferSize *= 2; + Free(responseBuf); + responseBuf = MAlloc(bufferSize); + + // Continue receiving data from where you left off + responseLength += TCPSocketReceive(sock, responseBuf + responseLength, bufferSize - responseLength - 1); + responseBuf[responseLength] = 0; + } + //SysLog(responseBuf); + + CCompCtrl *cc = CompCtrlNew(MStrPrint("%s", RemoveBeforeJSON(responseBuf))); + CJSONDataEntry *jsonData = JSONParse(cc); + + // Retrieve the 'choices' array + CJSONDataEntry *choicesEntry = JSONKeyValueGet(jsonData, "choices"); + if (!choicesEntry || choicesEntry->type != JSONT_ARRAY) { + // Handle error: choices key not found or not an array + return; + } + + // Retrieve the first object from the 'choices' array + CJSONDataEntry *firstChoice = JSONIndexValueGet(choicesEntry, 0); + if (!firstChoice || firstChoice->type != JSONT_OBJ) { + // Handle error: First choice not found or not an object + return; + } + + // Retrieve the 'message' object from the first choice + CJSONDataEntry *messageEntry = JSONKeyValueGet(firstChoice, "message"); + if (!messageEntry || messageEntry->type != JSONT_OBJ) { + // Handle error: message key not found or not an object + return; + } + + // Retrieve the 'content' string from the 'message' object + CJSONDataEntry *contentEntry = JSONKeyValueGet(messageEntry, "content"); + if (!contentEntry || contentEntry->type != JSONT_STRING) { + // Handle error: content key not found or not a string + return; + } + + U8 *completion = contentEntry->string_data; + //SysLog(completion); + CompCtrlDel(cc); + + "\n$$RED$$ Skynet: $$YELLOW$$%s$$FG$$\n", completion; + + AppendToHistory("user", message); + AppendToHistory("assistant", completion); + + TCPSocketClose(sock); + Free(responseBuf); + return 0; +} + +// gpt-3.5-turbo || gpt-4 +public U0 ChatUI(U8 *system=DEFAULT_SYSTEMPROMPT, U8 *model="gpt-4") { + U8 *userInput; + DocClear; + + try{ + "\t\t\t\t\t\t\t$$LTRED$$Welcome to Skynet$$FG$$\n"; + + while(1) { + "\n$$LTBLUE$$ You: $$GREEN$$"; + + LBts(&Fs->task_flags, TASKf_CMD_LINE_PROMPT); + userInput = StrGet(,, SGF_SHIFT_ESC_EXIT); + LBtr(&Fs->task_flags, TASKf_CMD_LINE_PROMPT); + Skynet(userInput, system, model); + } + } + catch + PutExcept; +} +ChatUI; \ No newline at end of file diff --git a/src/Home/Net/Programs/Skynet/Skynet3.ZC b/src/Home/Net/Programs/Skynet/Skynet3.ZC new file mode 100755 index 00000000..175178ad --- /dev/null +++ b/src/Home/Net/Programs/Skynet/Skynet3.ZC @@ -0,0 +1,73 @@ +#define SERVER_ADDR "skynet.middleware.com" +#define SERVER_PORT 9000 + +// GPT2 - GPT3 version +U8* ExtractGPTCompletion(U8 *response) { + U8 *start = StrFind("\"text\": \"", response); + if (!start) { + return NULL; // Pattern not found + } + + start += 10; // Move pointer after '"text": "' + + U8 *end = StrFind("\",", start); // Find the closing double quote + if (!end) { + return NULL; + } + + *end = 0; // Null terminate the completion string + + return start; +} + +public I64 Skynet3(U8 *prompt) +{ + U8 responseBuf[8192]; + I64 sock; + + sock = TCPConnectionCreate(SERVER_ADDR, SERVER_PORT); + if (sock <= 0) + { + PrintErr("Failed to connect to middleware server"); + return sock; + } + + U8 *payload = StrPrint(NULL, "{\"model\": \"text-davinci-003\",\"prompt\": \"%s\",\"max_tokens\": 100,\"temperature\": 1}", prompt); + U8 *requestHeader = StrPrint(NULL, + "POST /gpt3 HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "User-Agent: ZealOSClient/1.0\r\n" + "Accept: */*\r\n" + "Content-Type: application/json\r\n" + "Content-Length: %d\r\n" + "\r\n", + SERVER_ADDR, + SERVER_PORT, + StrLen(payload) + ); + U8 *fullRequest = StrPrint(NULL, "%s%s\n\n", requestHeader, payload); + + TCPSocketSendString(sock, fullRequest); + Free(requestHeader); + Free(payload); + Free(fullRequest); + + + I64 responseLength = TCPSocketReceive(sock, responseBuf, sizeof(responseBuf) - 1); // -1 to ensure space for null terminator + + responseBuf[responseLength] = 0; // Null-terminate the response + + // Assuming the headers and payload are separated by two newline sequences (standard HTTP) + U8 *jsonPayload = StrFind("\r\n\r\n", responseBuf); + if (!jsonPayload) { + TCPSocketClose(sock); + return -1; // or some error code + } + jsonPayload += 4; // Move past the header separator + + U8 *completion = ExtractGPTCompletion(jsonPayload); + Print("$$RED$$Skynet: $$YELLOW$$%s$$FG$$\n", completion); + + TCPSocketClose(sock); + return 0; +}