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-01-04 06:00:03 +01:00
#include "openai.h"
#include "markdown.h"
#include "plugin.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-01-04 06:00:03 +01:00
2025-01-04 08:40:31 +01:00
char * get_prompt_from_args ( int c , char ** argv ) {
char * prompt = malloc ( 1024 * 1024 + 1 );
2025-01-04 06:00:03 +01:00
prompt [ 0 ] = 0 ;
2025-01-04 08:40:31 +01:00
for ( int i = 1 ; i < c ; i ++ ) {
if ( argv [ i ][ 0 ] == '-' )
2025-01-04 06:00:03 +01:00
break ;
2025-01-04 08:40:31 +01:00
strncat ( prompt , argv [ i ], 1024 * 1024 );
if ( i < c - 1 ) {
strncat ( prompt , " " , 1024 * 1024 );
2025-01-04 08:35:39 +01:00
} else {
2025-01-04 08:40:31 +01:00
strncat ( prompt , "." , 1024 * 1024 );
2025-01-04 06:00:03 +01:00
}
}
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-04 06:00:03 +01:00
parse_markdown_to_ansi ( response );
printf ( " \n " );
free ( response );
free ( prompt );
return true ;
}
return false ;
}
void help ();
void render ( char * );
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-01-04 08:40:31 +01:00
void render ( char * content ) {
2025-01-04 06:00:03 +01:00
parse_markdown_to_ansi ( content );
printf ( " \n\n " );
}
2025-01-04 08:35:39 +01:00
void repl () {
2025-01-04 06:00:03 +01:00
line_init ();
char * line ;
char * previous_line = NULL ;
2025-01-04 08:40:31 +01:00
while (( line = line_read ( "> " ))) {
if ( ! line || !* line ) {
2025-01-04 06:00:03 +01:00
line = previous_line ;
}
2025-01-04 08:40:31 +01:00
if ( ! line || !* line )
2025-01-04 06:00:03 +01:00
continue ;
previous_line = line ;
2025-01-04 08:40:31 +01:00
if ( line [ 0 ] == '!' ) {
2025-01-04 06:00:03 +01:00
plugin_run ( line + 1 );
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-01-04 08:40:31 +01:00
if ( ! strncmp ( line , "serve" , 5 )) {
2025-01-04 06:00:03 +01:00
serve ();
}
2025-01-04 08:40:31 +01:00
if ( ! strncmp ( line , "spar " , 5 )) {
char * response = line + 5 ;
while ( true ) {
2025-01-04 06:00:03 +01:00
render ( response );
sleep ( 2 );
2025-01-04 08:40:31 +01:00
response = openai_chat ( "user" , response );
2025-01-04 08:35:39 +01:00
}
2025-01-04 06:00:03 +01:00
}
2025-01-04 08:40:31 +01:00
if ( ! strncmp ( line , "ls" , 2 ) || ! strncmp ( line , "list" , 4 )) {
2025-01-04 06:00:03 +01:00
int offset = 2 ;
2025-01-04 08:40:31 +01:00
if ( ! strncmp ( line , "list" , 4 )) {
2025-01-04 06:00:03 +01:00
offset = 4 ;
}
2025-01-04 08:40:31 +01:00
char * command = ( char * ) malloc ( strlen ( line ) + 42 );
2025-01-04 06:00:03 +01:00
command [ 0 ] = 0 ;
strcpy ( command , "ls " );
strcat ( command , line + offset );
int res = system ( command );
( void ) res ;
free ( command );
continue ;
}
line_add_history ( line );
2025-01-04 08:40:31 +01:00
char * response = openai_chat ( "user" , line );
2025-01-04 06:00:03 +01:00
render ( response );
free ( response );
}
}
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 " ;
sprintf ( help_text , template , prompt_temperature , 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-01-04 16:54:48 +01:00
bool openai_include ( char * path ) {
2025-01-04 08:40:31 +01:00
FILE * file = fopen ( path , "r" );
if ( file == NULL ) {
2025-01-04 16:54:48 +01:00
return false ;
2025-01-04 06:00:03 +01:00
}
fseek ( file , 0 , SEEK_END );
long size = ftell ( file );
fseek ( file , 0 , SEEK_SET );
2025-01-04 08:40:31 +01:00
char * buffer = ( char * ) malloc ( size );
size_t read = fread ( buffer , 1 , size , file );
if ( read == 0 ) {
2025-01-04 16:54:48 +01:00
return false ;
2025-01-04 06:00:03 +01:00
}
fclose ( file );
buffer [ read ] = 0 ;
openai_system ( buffer );
2025-01-04 08:40:31 +01:00
2025-01-04 06:00:03 +01:00
free ( buffer );
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 ();
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 );
printf ( "%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" );
}
printf ( "%s" , " \r ✅ Type help for features. \n " );
2025-01-04 06:00:03 +01:00
}
2025-01-04 08:35:39 +01:00
int main ( int argc , char * argv []) {
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-04 08:35:39 +01:00
}