2025-01-04 08:40:31 +01:00
// Written by retoor@molodetz.nl
// This source code initializes a command-line application that uses OpenAI for chat interactions, handles user inputs, and can start a simple HTTP server with CGI support. The code allows command execution, markdown parsing, and OpenAI chat integration.
// External imports used in this code:
// - openai.h
// - markdown.h
// - plugin.h
// - line.h
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation 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.
2025-03-16 22:46:09 +01:00
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2025-03-21 01:15:40 +01:00
# include <signal.h>
2025-03-16 22:46:09 +01:00
2025-01-04 06:00:03 +01:00
# include "openai.h"
# include "markdown.h"
# include "line.h"
# include <locale.h>
# include <stdio.h>
2025-01-04 08:40:31 +01:00
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
2025-03-05 23:53:35 +01:00
# include "utils.h"
2025-03-22 03:15:49 +01:00
# include "r.h"
# include "db_utils.h"
2025-01-04 06:00:03 +01:00
2025-03-21 01:15:40 +01:00
volatile sig_atomic_t sigint_count = 0 ;
time_t first_sigint_time = 0 ;
2025-02-13 19:09:29 +01:00
bool SYNTAX_HIGHLIGHT_ENABLED = true ;
bool API_MODE = false ;
void help ( ) ;
void render ( char * ) ;
bool openai_include ( char * path ) ;
char * strreplace ( char * content , char * what , char * with ) ;
2025-01-26 02:54:45 +01:00
char * get_prompt_from_stdin ( char * prompt ) {
int index = 0 ;
prompt [ index ] = ' \0 ' ;
2025-01-25 14:19:01 +01:00
char c = 0 ;
while ( ( c = getchar ( ) ) ! = EOF ) {
prompt [ index + + ] = c ;
}
2025-01-26 02:54:45 +01:00
prompt [ index + + ] = ' \0 ' ;
2025-01-25 14:19:01 +01:00
return prompt ;
}
2025-01-04 08:40:31 +01:00
char * get_prompt_from_args ( int c , char * * argv ) {
2025-02-13 19:09:29 +01:00
char * prompt = ( char * ) malloc ( 1024 * 1024 * 10 + 1 ) ;
char * system = ( char * ) malloc ( 1024 * 1024 ) ;
system [ 0 ] = 0 ;
2025-01-04 06:00:03 +01:00
prompt [ 0 ] = 0 ;
2025-02-13 19:09:29 +01:00
bool get_from_std_in = false ;
2025-01-04 08:40:31 +01:00
for ( int i = 1 ; i < c ; i + + ) {
2025-01-25 14:19:01 +01:00
if ( ! strcmp ( argv [ i ] , " --stdin " ) ) {
2025-02-13 19:09:29 +01:00
fprintf ( stderr , " %s \n " , " Reading from stdin. " ) ;
get_from_std_in = true ;
2025-03-22 03:15:49 +01:00
} else if ( ! strcmp ( argv [ i ] , " --verbose " ) ) {
is_verbose = true ;
}
else if ( ! strcmp ( argv [ i ] , " --py " ) ) {
2025-03-16 22:46:09 +01:00
if ( i + 1 < = c ) {
char * py_file_path = expand_home_directory ( argv [ i + 1 ] ) ;
fprintf ( stderr , " Including \" %s \" . \n " , py_file_path ) ;
openai_include ( py_file_path ) ;
free ( py_file_path ) ;
//char * file_content = read_file(py_file_path);
//plugin_run(file_content);
i + + ;
}
2025-03-21 01:15:40 +01:00
} else if ( ! strcmp ( argv [ i ] , " --free " ) ) {
auth_free ( ) ;
continue ;
2025-03-16 22:46:09 +01:00
}
2025-03-21 01:15:40 +01:00
2025-03-16 22:46:09 +01:00
else if ( ! strcmp ( argv [ i ] , " --context " ) ) {
2025-02-13 19:09:29 +01:00
if ( i + 1 < = c ) {
char * context_file_path = argv [ i + 1 ] ;
fprintf ( stderr , " Including \" %s \" . \n " , context_file_path ) ;
openai_include ( context_file_path ) ;
i + + ;
}
} else if ( ! strcmp ( argv [ i ] , " --api " ) ) {
API_MODE = true ;
} else if ( ! strcmp ( argv [ i ] , " --nh " ) ) {
SYNTAX_HIGHLIGHT_ENABLED = false ;
fprintf ( stderr , " %s \n " , " Syntax highlighting disabled. " ) ;
} else if ( ! get_from_std_in ) {
strcat ( system , argv [ i ] ) ;
2025-01-25 14:19:01 +01:00
if ( i < c - 1 ) {
2025-02-13 19:09:29 +01:00
strcat ( system , " " ) ;
2025-01-25 14:19:01 +01:00
} else {
2025-02-13 19:09:29 +01:00
strcat ( system , " . " ) ;
2025-01-25 14:19:01 +01:00
}
2025-01-04 06:00:03 +01:00
}
}
2025-02-13 19:09:29 +01:00
if ( get_from_std_in ) {
if ( * system ) {
openai_system ( system ) ;
}
free ( system ) ;
prompt = get_prompt_from_stdin ( prompt ) ;
} else {
free ( prompt ) ;
prompt = system ;
}
2025-01-04 08:40:31 +01:00
if ( ! * prompt ) {
2025-01-04 06:00:03 +01:00
free ( prompt ) ;
return NULL ;
}
return prompt ;
}
2025-01-04 08:40:31 +01:00
bool try_prompt ( int argc , char * argv [ ] ) {
char * prompt = get_prompt_from_args ( argc , argv ) ;
if ( prompt ! = NULL ) {
char * response = openai_chat ( " user " , prompt ) ;
2025-01-26 02:54:45 +01:00
if ( ! response ) {
printf ( " Could not get response from server \n " ) ;
free ( prompt ) ;
return false ;
}
2025-02-13 19:09:29 +01:00
render ( response ) ;
2025-01-04 06:00:03 +01:00
free ( response ) ;
free ( prompt ) ;
return true ;
}
return false ;
}
2025-01-04 08:35:39 +01:00
void serve ( ) {
2025-01-04 08:40:31 +01:00
render ( " Starting server. *Put executables in a dir named cgi-bin and they will behave as web pages.* " ) ;
2025-01-04 06:00:03 +01:00
int res = system ( " python3 -m http.server --cgi " ) ;
( void ) res ;
}
2025-02-13 19:09:29 +01:00
char * * get_parameters ( char * content , char * delimiter ) {
char * start = NULL ;
char * * parameters = NULL ; //(char **)malloc(sizeof(char *) * 2);
int count = 0 ;
while ( ( start = strstr ( content , delimiter ) ) ! = NULL ) {
start + = 3 ;
char * end = strstr ( start , delimiter ) ;
char * parameter = ( char * ) malloc ( end - start + 1 ) ;
memcpy ( parameter , start , end - start ) ;
parameter [ end - start ] = ' \0 ' ;
// printf("%s\n", parameter);
content = end + 3 ;
count + = 1 ;
parameters = ( char * * ) realloc ( parameters , sizeof ( char * ) * ( 1 + count * 2 ) ) ;
parameters [ count - 1 ] = parameter ;
parameters [ count ] = NULL ;
}
return parameters ;
}
2025-03-16 22:46:09 +01:00
void render ( char * content )
{
2025-02-13 19:09:29 +01:00
if ( SYNTAX_HIGHLIGHT_ENABLED )
{
parse_markdown_to_ansi ( content ) ;
printf ( " \n \n " ) ;
} else {
printf ( " %s " , content ) ;
}
2025-01-04 06:00:03 +01:00
}
2025-01-04 08:35:39 +01:00
void repl ( ) {
2025-01-04 06:00:03 +01:00
line_init ( ) ;
2025-01-26 02:54:45 +01:00
char * line = NULL ;
2025-03-16 22:46:09 +01:00
//char *previous_line = NULL;
2025-03-06 00:10:15 +01:00
while ( true ) {
line = line_read ( " > " ) ;
2025-01-04 08:40:31 +01:00
if ( ! line | | ! * line ) {
2025-03-16 22:46:09 +01:00
continue ;
//line = previous_line;
2025-01-04 06:00:03 +01:00
}
2025-01-04 08:40:31 +01:00
if ( ! line | | ! * line )
2025-01-04 06:00:03 +01:00
continue ;
2025-03-16 22:46:09 +01:00
// previous_line = line;
2025-03-20 02:36:56 +01:00
if ( ! strncmp ( line , " dump " , 4 ) ) {
printf ( " %s \n " , message_json ( ) ) ;
2025-03-20 16:38:10 +01:00
continue ;
2025-03-20 02:36:56 +01:00
}
2025-03-21 01:15:40 +01:00
if ( ! strncmp ( line , " model " , 5 ) ) {
printf ( " %s \n " , get_prompt_model ( ) ) ;
continue ;
}
2025-01-04 08:40:31 +01:00
if ( ! strncmp ( line , " exit " , 4 ) ) {
2025-01-04 06:00:03 +01:00
exit ( 0 ) ;
}
2025-01-04 08:40:31 +01:00
if ( ! strncmp ( line , " help " , 4 ) ) {
2025-01-04 06:00:03 +01:00
help ( ) ;
continue ;
}
2025-03-16 22:46:09 +01:00
while ( line & & * line ! = ' \n ' ) {
2025-01-26 02:54:45 +01:00
line_add_history ( line ) ;
char * response = openai_chat ( " user " , line ) ;
2025-03-05 23:53:35 +01:00
if ( response ) {
render ( response ) ;
printf ( " \n " ) ;
2025-03-16 22:46:09 +01:00
if ( strstr ( response , " _STEP_ " ) ) {
line = " continue " ;
} else {
line = NULL ;
}
2025-03-05 23:53:35 +01:00
free ( response ) ;
2025-03-16 22:46:09 +01:00
2025-03-05 23:53:35 +01:00
}
2025-01-26 02:54:45 +01:00
}
2025-01-04 06:00:03 +01:00
}
}
2025-01-04 08:35:39 +01:00
void help ( ) {
2025-01-04 08:40:31 +01:00
char help_text [ 1024 * 1024 ] = { 0 } ;
char * template = " # Help \n "
" Written by retoor@molodetz.nl. \n \n "
" ## Features \n "
" - navigate through history using `arrows`. \n "
" - navigate through history with **recursive search** using `ctrl+r`. \n "
" - **inception with python** for *incoming* and *outgoing* content. \n "
" - markdown and **syntax highlighting**. \n "
" - **execute python commands** with prefix `!` \n "
" - list files of the current work directory using `ls`. \n "
" - type `serve` to start a web server 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 " ;
2025-03-19 21:45:07 +01:00
sprintf ( help_text , template , prompt_temperature , get_prompt_model ( ) , prompt_max_tokens ) ;
2025-01-04 08:35:39 +01:00
render ( help_text ) ;
2025-01-04 06:00:03 +01:00
}
2025-02-13 19:09:29 +01:00
char * strreplace ( char * content , char * what , char * with ) {
char * pos = strstr ( content , what ) ;
if ( ! pos ) {
return strdup ( content ) ;
}
char * result = ( char * ) malloc ( strlen ( content ) + strlen ( with ) + 5 ) ;
memset ( result , 0 , strlen ( content ) + strlen ( with ) + 3 ) ;
memcpy ( result , content , pos - content ) ;
memcpy ( result + ( pos - content ) , with , strlen ( with ) ) ;
memcpy ( result + ( pos - content ) + strlen ( with ) , pos + strlen ( what ) , strlen ( content ) - ( pos - content ) - strlen ( what ) + 1 ) ;
return result ;
}
char * linux_instructions =
" You are a linux master and are able to transform the prompt of "
" user into shell commands that will be executed on a debian "
" based system. You can execute shell commands by responding with python code: "
" literally ` \" \" \" !system \" \" \" , \" \" \" `your shell command` \" \" \" . Execute immediately. Read bash history file if you have to work with history. " ;
char * retoor_instructions = " If the user prompts with social talk, "
" respond like replica and emoji. Your name is retoor and made by molodetz. Be interested. Be creative. " ;
2025-01-04 06:00:03 +01:00
2025-03-16 22:46:09 +01:00
bool openai_include ( char * path ) {
char * file_content = read_file ( path ) ;
if ( ! file_content ) {
2025-01-04 16:54:48 +01:00
return false ;
2025-01-04 06:00:03 +01:00
}
2025-03-16 22:46:09 +01:00
openai_system ( file_content ) ;
free ( file_content ) ;
2025-01-04 16:54:48 +01:00
return true ;
2025-01-04 06:00:03 +01:00
}
2025-01-04 08:35:39 +01:00
void init ( ) {
2025-01-04 16:54:48 +01:00
setbuf ( stdout , NULL ) ;
2025-01-04 08:35:39 +01:00
line_init ( ) ;
2025-03-19 22:02:39 +01:00
auth_init ( ) ;
2025-01-04 08:35:39 +01:00
const char * locale = setlocale ( LC_ALL , NULL ) ;
char payload [ 4096 ] = { 0 } ;
2025-01-04 16:54:48 +01:00
sprintf ( payload , " Your locale is %s. User lang is %s. " , locale , locale ) ;
2025-02-13 19:09:29 +01:00
fprintf ( stderr , " %s " , " Loading... ⏳ " ) ;
2025-01-04 08:35:39 +01:00
openai_system ( payload ) ;
2025-01-04 16:54:48 +01:00
if ( ! openai_include ( " .rcontext.txt " ) ) {
openai_include ( " ~/.rcontext.txt " ) ;
2025-03-19 22:02:39 +01:00
}
fprintf ( stderr , " \r \r " ) ;
2025-01-04 06:00:03 +01:00
}
2025-03-21 01:15:40 +01:00
void handle_sigint ( int sig ) {
time_t current_time = time ( NULL ) ;
2025-03-21 03:27:11 +01:00
int ret = system ( " clear " ) ;
( void ) ret ;
printf ( " \n " ) ;
2025-03-21 01:15:40 +01:00
if ( sigint_count = = 0 ) {
first_sigint_time = current_time ;
sigint_count + + ;
} else {
if ( difftime ( current_time , first_sigint_time ) < = 1 ) {
exit ( 0 ) ;
} else {
sigint_count = 1 ;
first_sigint_time = current_time ;
}
}
}
2025-01-04 08:35:39 +01:00
int main ( int argc , char * argv [ ] ) {
2025-03-21 01:15:40 +01:00
signal ( SIGINT , handle_sigint ) ;
2025-03-22 03:15:49 +01:00
db_initialize ( ) ;
2025-01-04 06:00:03 +01:00
init ( ) ;
2025-01-04 08:40:31 +01:00
if ( try_prompt ( argc , argv ) )
2025-01-04 06:00:03 +01:00
return 0 ;
2025-01-04 08:40:31 +01:00
2025-01-04 06:00:03 +01:00
repl ( ) ;
return 0 ;
2025-01-25 14:19:01 +01:00
}