46
46
* EXTRA_BODY - Extra body parameters for OpenAI API
47
47
* TOXIC_THRESHOLD - Toxicity threshold for responses 0-1 or 99 disable
48
48
* THINKING - Set to True to enable thinking mode by default
49
+ * MAX_IMAGES - Maximum number of images the chatbot will keep in context (default 1)
49
50
50
51
Running a llama-cpp-python server:
51
52
* CMAKE_ARGS="-DLLAMA_CUBLAS=on" FORCE_CMAKE=1 pip install llama-cpp-python
@@ -141,6 +142,7 @@ def debug(text):
141
142
TOXIC_THRESHOLD = float (os .environ .get ("TOXIC_THRESHOLD" , 99 )) # Toxicity threshold for responses 0-1 or 99 disable
142
143
THINKING = os .environ .get ("THINKING" , "false" ).lower () == "true" # Set to True to enable thinking mode by default
143
144
THINK_FILTER = os .environ .get ("THINK_FILTER" , "false" ).lower () == "true" # Set to True to enable thinking filter
145
+ MAX_IMAGES = int (os .environ .get ("MAX_IMAGES" , 1 )) # Maximum number of images to keep in context
144
146
145
147
# Convert EXTRA_BODY to dictionary if it is proper JSON
146
148
if EXTRA_BODY :
@@ -457,20 +459,24 @@ async def ask(prompt, sid=None):
457
459
client [sid ]["context" ] = base_prompt ()
458
460
# Process image upload if present
459
461
if client [sid ]["image_data" ]:
460
- # Remove previous image data from context
461
- for turn in client [ sid ][ "context" ]:
462
- # if turn["content"] is a list, remove image_url
462
+ # go through context and count images, remove if too many
463
+ image_count = 0
464
+ for turn in reversed ( client [ sid ][ "context" ]):
463
465
if "content" in turn and isinstance (turn ["content" ], list ):
464
- # convert list to string of text
465
- turn ["content" ] = ' ' .join ([x .get ("text" , "" ) for x in turn ["content" ]])
466
+ for item in turn ["content" ]:
467
+ if "image_url" in item :
468
+ image_count += 1
469
+ if image_count >= MAX_IMAGES :
470
+ # remove image from context
471
+ debug ("Too many images - Found image in context, removing 1..." )
472
+ turn ["content" ] = ' ' .join ([x .get ("text" , "" ) for x in turn ["content" ]])
466
473
message = {
467
474
"role" : "user" ,
468
475
"content" : [
469
476
{"type" : "text" , "text" : prompt },
470
477
{"type" : "image_url" , "image_url" : {"url" : f"data:image/jpeg;base64,{ client [sid ]['image_data' ]} " }}
471
478
]
472
479
}
473
- client [sid ]["image_data" ] = ""
474
480
client [sid ]["context" ].append (message )
475
481
else :
476
482
client [sid ]["context" ].append ({"role" : "user" , "content" : prompt })
@@ -484,6 +490,7 @@ async def ask(prompt, sid=None):
484
490
messages = client [sid ]["context" ],
485
491
extra_body = EXTRA_BODY ,
486
492
)
493
+ client [sid ]["image_data" ] = ""
487
494
except openai .OpenAIError as erro :
488
495
# If we get an error, try to recover
489
496
client [sid ]["context" ].pop ()
@@ -494,8 +501,8 @@ async def ask(prompt, sid=None):
494
501
# set client model to default
495
502
client [sid ]["model" ] = MYMODEL
496
503
# update footer
497
- await sio .emit ('update' , {'update' : f"TinyLLM Chatbot { VERSION } - { client [sid ]['model' ]} " ,
498
- 'voice' : 'footer' },room = sid )
504
+ await sio .emit ('update' , {'update' : f"TinyLLM Chatbot { VERSION } - { client [sid ]['model' ]} " ,
505
+ 'voice' : 'footer' , 'model' : client [ sid ][ 'model' ] },room = sid )
499
506
elif "maximum context length" in str (erro ):
500
507
if len (prompt ) > 1000 :
501
508
# assume we have very large prompt - cut out the middle
@@ -509,11 +516,40 @@ async def ask(prompt, sid=None):
509
516
# our context has grown too large, reset
510
517
client [sid ]["context" ] = base_prompt ()
511
518
log (f"Session { sid } - Reset context to base prompt - Now: ~{ len (client [sid ]['context' ])/ 4 } tokens" )
519
+ elif "At most" in str (erro ) and "image" in str (erro ):
520
+ # Remove oldest image from context
521
+ for turn in reversed (client [sid ]["context" ]):
522
+ # if turn["content"] is a list, remove image_url
523
+ if "content" in turn and isinstance (turn ["content" ], list ):
524
+ debug ("Too many images - Found last image in context, removing..." )
525
+ turn ["content" ] = ' ' .join ([x .get ("text" , "" ) for x in turn ["content" ]])
526
+ break
527
+ continue
528
+ elif "Internal Server Error" in str (erro ):
529
+ # Check to see if our context has images - if so, remove them
530
+ debug ("Internal Server Error - Checking for images in context..." )
531
+ removed_image_data = False
532
+ for turn in client [sid ]["context" ]:
533
+ # if turn["content"] is a list, remove image_url
534
+ if "content" in turn and isinstance (turn ["content" ], list ):
535
+ log ("Found image in context, removing..." )
536
+ removed_image_data = True
537
+ turn ["content" ] = ' ' .join ([x .get ("text" , "" ) for x in turn ["content" ]])
538
+ if removed_image_data :
539
+ # remove last turn in context and retry
540
+ await sio .emit ('update' , {'update' : '[Images do not seem to be supported by model... Removing]' , 'voice' : 'user' },room = sid )
541
+ client [sid ]["context" ].pop ()
542
+ continue
543
+ log (f"ERROR: { str (erro )} " )
544
+ stats ["errors" ] += 1
545
+ await sio .emit ('update' , {'update' : str (erro ), 'voice' : 'user' },room = sid )
546
+ break
512
547
else :
548
+ # If all else fails, log the error and break
513
549
log (f"ERROR: { str (erro )} " )
514
550
stats ["errors" ] += 1
515
551
await sio .emit ('update' , {'update' : str (erro ), 'voice' : 'user' },room = sid )
516
-
552
+ break
517
553
if not client [sid ]["remember" ]:
518
554
client [sid ]["remember" ] = True
519
555
client [sid ]["context" ].pop ()
@@ -915,7 +951,7 @@ async def send_update(session_id):
915
951
await sio .sleep (0.1 )
916
952
else :
917
953
# Check to see of CoT is enabled but not while processing a file/image
918
- client_cot = client [session_id ]["cot" ]
954
+ client_cot = client [session_id ]["cot" ]
919
955
client_image_data = client [session_id ]["image_data" ]
920
956
client_visible = client [session_id ]["visible" ]
921
957
if client_cot and not client_image_data and client_visible :
@@ -978,7 +1014,7 @@ async def send_update(session_id):
978
1014
# Update footer with stats
979
1015
await sio .emit ('update' , {'update' :
980
1016
f"TinyLLM Chatbot { VERSION } - { client [session_id ]['model' ]} - Tokens: { tokens } - TPS: { tokens / (time .time ()- stime ):.1f} " ,
981
- 'voice' : 'footer' },room = session_id )
1017
+ 'voice' : 'footer' , 'model' : client [ session_id ][ 'model' ] },room = session_id )
982
1018
# Check for link injection
983
1019
if client [session_id ]["links" ]:
984
1020
await sio .emit ('update' , {'update' : json .dumps (client [session_id ]["links" ]), 'voice' : 'links' },room = session_id )
@@ -1052,7 +1088,7 @@ async def handle_disconnect(session_id):
1052
1088
# shutdown thread
1053
1089
client [session_id ]["stop_thread_flag" ] = True
1054
1090
client .pop (session_id )
1055
-
1091
+
1056
1092
# Change the LLM model
1057
1093
@sio .on ('model' )
1058
1094
async def change_model (session_id , model ):
@@ -1062,13 +1098,18 @@ async def change_model(session_id, model):
1062
1098
list_of_models = get_models ()
1063
1099
if model not in list_of_models :
1064
1100
log (f"Requested invalid model { model } " )
1065
- await sio .emit ('update' , {'update' : f"Model not found: { model } " , 'voice' : 'user' }, room = session_id )
1101
+ if len (client [session_id ]["context" ]) > 2 :
1102
+ await sio .emit ('update' , {'update' : f"Model not found: { model } " , 'voice' : 'user' }, room = session_id )
1066
1103
return
1067
1104
debug (f"Changing model for { session_id } to { model } " )
1068
1105
client [session_id ]["model" ] = model
1069
1106
# Update footer
1070
- await sio .emit ('update' , {'update' : f"TinyLLM Chatbot { VERSION } - { model } " , 'voice' : 'footer' }, room = session_id )
1071
- await sio .emit ('update' , {'update' : f'[Model changed to { model } ]' , 'voice' : 'user' }, room = session_id )
1107
+ await sio .emit ('update' , {'update' : f"TinyLLM Chatbot { VERSION } - { model } " , 'voice' : 'footer' ,
1108
+ 'model' : model }, room = session_id )
1109
+ # Check to see if this is a new session
1110
+ log (f"context length: { len (client [session_id ]['context' ])} " )
1111
+ if len (client [session_id ]["context" ]) > 2 :
1112
+ await sio .emit ('update' , {'update' : f'[Model changed to { model } ]' , 'voice' : 'user' }, room = session_id )
1072
1113
else :
1073
1114
log (f"Invalid session { session_id } " )
1074
1115
await handle_invalid_session (session_id )
@@ -1332,19 +1373,20 @@ async def handle_model_command(session_id, p):
1332
1373
if not args :
1333
1374
# Open Model Dialog
1334
1375
await sio .emit ('model_dialog' , {}, room = session_id )
1335
- #model_list = get_models()
1336
- #msg = f'Current LLM Model: {client[session_id]["model"]}\n'
1337
- #msg += f'- Available Models: {", ".join(model_list)}\n'
1338
- #msg += '- Usage: /model {model_name}'
1339
- #await sio.emit('update', {'update': msg, 'voice': 'user'}, room=session_id)
1340
1376
return
1341
1377
model_list = get_models ()
1342
1378
if not args in model_list :
1343
1379
args = args .lower ()
1344
1380
if args in model_list :
1345
1381
client [session_id ]["model" ] = args
1346
- await sio .emit ('update' , {'update' : f'[LLM Model set to { args } ]' , 'voice' : 'user' }, room = session_id )
1347
- await sio .emit ('update' , {'update' : f"TinyLLM Chatbot { args } - { client [session_id ]['model' ]} " , 'voice' : 'footer' }, room = session_id )
1382
+ await sio .emit ('update' , {'update' : f'[Model changed to { args } ]' , 'voice' : 'user' }, room = session_id )
1383
+ await sio .emit ('update' , {'update' : f"TinyLLM Chatbot { VERSION } - { args } " , 'voice' : 'footer' ,
1384
+ 'model' : args }, room = session_id )
1385
+ elif args in ["list" , "help" ]:
1386
+ msg = f'Current LLM Model: { client [session_id ]["model" ]} \n '
1387
+ msg += f'- Available Models: { ", " .join (model_list )} \n '
1388
+ msg += '- Usage: /model {model_name}'
1389
+ await sio .emit ('update' , {'update' : msg , 'voice' : 'user' }, room = session_id )
1348
1390
else :
1349
1391
await sio .emit ('update' , {'update' : f'[Model { args } not found]' , 'voice' : 'user' }, room = session_id )
1350
1392
0 commit comments