2024-12-13 06:28:42 +00:00
import time
import logging
import sys
2025-02-05 02:33:22 +00:00
import os
import base64
2025-06-05 15:21:37 +00:00
import textwrap
2024-12-13 06:28:42 +00:00
2024-12-19 09:00:32 +00:00
import asyncio
2024-12-13 06:28:42 +00:00
from aiocache import cached
from typing import Any , Optional
import random
import json
2025-02-03 08:03:41 +00:00
import html
2024-12-13 06:28:42 +00:00
import inspect
2025-02-03 04:50:54 +00:00
import re
2025-02-05 08:01:24 +00:00
import ast
2025-02-03 04:50:54 +00:00
2024-12-19 09:00:32 +00:00
from uuid import uuid4
2024-12-25 00:56:46 +00:00
from concurrent . futures import ThreadPoolExecutor
2024-12-19 09:00:32 +00:00
2024-12-13 06:28:42 +00:00
2025-03-28 07:52:13 +00:00
from fastapi import Request , HTTPException
2025-09-19 06:38:44 +00:00
from fastapi . responses import HTMLResponse
2025-08-10 20:37:06 +00:00
from starlette . responses import Response , StreamingResponse , JSONResponse
2024-12-13 06:28:42 +00:00
2025-11-25 07:31:34 +00:00
from open_webui . utils . misc import is_string_allowed
2025-09-25 06:49:16 +00:00
from open_webui . models . oauth_sessions import OAuthSessions
2024-12-19 09:00:32 +00:00
from open_webui . models . chats import Chats
2025-07-12 21:26:56 +00:00
from open_webui . models . folders import Folders
2024-12-21 06:54:43 +00:00
from open_webui . models . users import Users
2024-12-13 06:28:42 +00:00
from open_webui . socket . main import (
get_event_call ,
get_event_emitter ,
)
2024-12-19 09:00:32 +00:00
from open_webui . routers . tasks import (
generate_queries ,
generate_title ,
2025-06-03 14:47:49 +00:00
generate_follow_ups ,
2025-01-16 08:06:37 +00:00
generate_image_prompt ,
2024-12-19 09:00:32 +00:00
generate_chat_tags ,
)
2025-10-04 07:02:26 +00:00
from open_webui . routers . retrieval import (
process_web_search ,
SearchForm ,
)
2025-06-08 08:26:58 +00:00
from open_webui . routers . images import (
image_generations ,
2025-11-04 18:30:59 +00:00
CreateImageForm ,
2025-11-05 08:31:37 +00:00
image_edits ,
EditImageForm ,
2025-06-08 08:26:58 +00:00
)
2025-02-16 06:25:18 +00:00
from open_webui . routers . pipelines import (
process_pipeline_inlet_filter ,
process_pipeline_outlet_filter ,
)
2025-05-22 23:26:14 +00:00
from open_webui . routers . memories import query_memory , QueryMemoryForm
2025-01-16 07:32:13 +00:00
2024-12-21 06:54:43 +00:00
from open_webui . utils . webhook import post_webhook
2025-09-23 07:05:38 +00:00
from open_webui . utils . files import (
2025-11-20 09:00:02 +00:00
convert_markdown_base64_images ,
2025-09-23 07:05:38 +00:00
get_file_url_from_base64 ,
get_image_url_from_base64 ,
)
2024-12-13 06:28:42 +00:00
from open_webui . models . users import UserModel
from open_webui . models . functions import Functions
from open_webui . models . models import Models
2025-07-11 08:00:21 +00:00
from open_webui . retrieval . utils import get_sources_from_items
2024-12-13 06:28:42 +00:00
from open_webui . utils . chat import generate_chat_completion
from open_webui . utils . task import (
get_task_model_id ,
rag_template ,
tools_function_calling_generation_template ,
)
from open_webui . utils . misc import (
2025-02-05 07:05:14 +00:00
deep_update ,
2025-10-04 07:02:26 +00:00
extract_urls ,
2024-12-19 09:00:32 +00:00
get_message_list ,
2024-12-13 06:28:42 +00:00
add_or_update_system_message ,
2025-02-03 09:14:38 +00:00
add_or_update_user_message ,
2024-12-13 06:28:42 +00:00
get_last_user_message ,
2025-10-02 03:49:25 +00:00
get_last_user_message_item ,
2024-12-25 06:45:21 +00:00
get_last_assistant_message ,
2025-08-20 23:38:26 +00:00
get_system_message ,
2024-12-13 06:28:42 +00:00
prepend_to_first_user_message_content ,
2025-03-01 14:56:24 +00:00
convert_logit_bias_input_to_json ,
2025-09-29 16:50:01 +00:00
get_content_from_message ,
2024-12-13 06:28:42 +00:00
)
2025-11-06 04:46:30 +00:00
from open_webui . utils . tools import get_tools , get_updated_tool_function
2024-12-13 06:28:42 +00:00
from open_webui . utils . plugin import load_function_module_by_id
2025-02-06 23:01:43 +00:00
from open_webui . utils . filter import (
get_sorted_filter_ids ,
process_filter_functions ,
)
2025-02-10 10:25:02 +00:00
from open_webui . utils . code_interpreter import execute_code_jupyter
2025-08-20 23:38:26 +00:00
from open_webui . utils . payload import apply_system_prompt_to_body
2025-09-23 06:03:26 +00:00
from open_webui . utils . mcp . client import MCPClient
2024-12-13 06:28:42 +00:00
2024-12-19 09:00:32 +00:00
2025-02-03 09:14:38 +00:00
from open_webui . config import (
2025-02-05 02:33:22 +00:00
CACHE_DIR ,
2025-11-14 01:23:13 +00:00
DEFAULT_VOICE_MODE_PROMPT_TEMPLATE ,
2025-02-03 09:14:38 +00:00
DEFAULT_TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE ,
DEFAULT_CODE_INTERPRETER_PROMPT ,
2025-08-17 00:15:13 +00:00
CODE_INTERPRETER_BLOCKED_MODULES ,
2025-02-03 09:14:38 +00:00
)
2024-12-21 06:54:43 +00:00
from open_webui . env import (
SRC_LOG_LEVELS ,
GLOBAL_LOG_LEVEL ,
2025-11-20 09:00:02 +00:00
ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION ,
2025-08-09 19:49:56 +00:00
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE ,
2025-08-26 22:58:25 +00:00
CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES ,
2024-12-21 06:54:43 +00:00
BYPASS_MODEL_ACCESS_CONTROL ,
2024-12-28 06:36:14 +00:00
ENABLE_REALTIME_CHAT_SAVE ,
2025-08-26 23:07:21 +00:00
ENABLE_QUERIES_CACHE ,
2025-11-28 16:21:00 +00:00
ENABLE_WRAP_TOOL_RESULT ,
TOOL_RESULT_INDENT_SIZE ,
2024-12-21 06:54:43 +00:00
)
2024-12-13 06:28:42 +00:00
from open_webui . constants import TASKS
logging . basicConfig ( stream = sys . stdout , level = GLOBAL_LOG_LEVEL )
log = logging . getLogger ( __name__ )
log . setLevel ( SRC_LOG_LEVELS [ " MAIN " ] )
2025-08-27 13:24:16 +00:00
DEFAULT_REASONING_TAGS = [
( " <think> " , " </think> " ) ,
( " <thinking> " , " </thinking> " ) ,
( " <reason> " , " </reason> " ) ,
( " <reasoning> " , " </reasoning> " ) ,
( " <thought> " , " </thought> " ) ,
( " <Thought> " , " </Thought> " ) ,
( " <|begin_of_thought|> " , " <|end_of_thought|> " ) ,
( " ◁think▷ " , " ◁/think▷ " ) ,
]
DEFAULT_SOLUTION_TAGS = [ ( " <|begin_of_solution|> " , " <|end_of_solution|> " ) ]
DEFAULT_CODE_INTERPRETER_TAGS = [ ( " <code_interpreter> " , " </code_interpreter> " ) ]
2025-09-28 19:46:01 +00:00
def process_tool_result (
request ,
tool_function_name ,
tool_result ,
tool_type ,
direct_tool = False ,
metadata = None ,
user = None ,
) :
tool_result_embeds = [ ]
if isinstance ( tool_result , HTMLResponse ) :
content_disposition = tool_result . headers . get ( " Content-Disposition " , " " )
if " inline " in content_disposition :
2025-09-29 08:16:50 +00:00
content = tool_result . body . decode ( " utf-8 " , " replace " )
2025-09-28 19:46:01 +00:00
tool_result_embeds . append ( content )
if 200 < = tool_result . status_code < 300 :
tool_result = {
" status " : " success " ,
" code " : " ui_component " ,
" message " : f " { tool_function_name } : Embedded UI result is active and visible to the user. " ,
}
elif 400 < = tool_result . status_code < 500 :
tool_result = {
" status " : " error " ,
" code " : " ui_component " ,
" message " : f " { tool_function_name } : Client error { tool_result . status_code } from embedded UI result. " ,
}
elif 500 < = tool_result . status_code < 600 :
tool_result = {
" status " : " error " ,
" code " : " ui_component " ,
" message " : f " { tool_function_name } : Server error { tool_result . status_code } from embedded UI result. " ,
}
else :
tool_result = {
" status " : " error " ,
" code " : " ui_component " ,
" message " : f " { tool_function_name } : Unexpected status code { tool_result . status_code } from embedded UI result. " ,
}
else :
2025-09-29 08:16:50 +00:00
tool_result = tool_result . body . decode ( " utf-8 " , " replace " )
2025-09-28 19:46:01 +00:00
elif ( tool_type == " external " and isinstance ( tool_result , tuple ) ) or (
direct_tool and isinstance ( tool_result , list ) and len ( tool_result ) == 2
) :
tool_result , tool_response_headers = tool_result
try :
if not isinstance ( tool_response_headers , dict ) :
tool_response_headers = dict ( tool_response_headers )
except Exception as e :
tool_response_headers = { }
log . debug ( e )
if tool_response_headers and isinstance ( tool_response_headers , dict ) :
content_disposition = tool_response_headers . get (
" Content-Disposition " ,
tool_response_headers . get ( " content-disposition " , " " ) ,
)
if " inline " in content_disposition :
content_type = tool_response_headers . get (
" Content-Type " ,
tool_response_headers . get ( " content-type " , " " ) ,
)
location = tool_response_headers . get (
" Location " ,
tool_response_headers . get ( " location " , " " ) ,
)
if " text/html " in content_type :
# Display as iframe embed
tool_result_embeds . append ( tool_result )
tool_result = {
" status " : " success " ,
" code " : " ui_component " ,
" message " : f " { tool_function_name } : Embedded UI result is active and visible to the user. " ,
}
elif location :
tool_result_embeds . append ( location )
tool_result = {
" status " : " success " ,
" code " : " ui_component " ,
" message " : f " { tool_function_name } : Embedded UI result is active and visible to the user. " ,
}
tool_result_files = [ ]
if isinstance ( tool_result , list ) :
if tool_type == " mcp " : # MCP
tool_response = [ ]
for item in tool_result :
if isinstance ( item , dict ) :
if item . get ( " type " ) == " text " :
text = item . get ( " text " , " " )
if isinstance ( text , str ) :
try :
text = json . loads ( text )
except json . JSONDecodeError :
pass
tool_response . append ( text )
elif item . get ( " type " ) in [ " image " , " audio " ] :
file_url = get_file_url_from_base64 (
request ,
f " data: { item . get ( ' mimeType ' ) } ;base64, { item . get ( ' data ' , item . get ( ' blob ' , ' ' ) ) } " ,
{
" chat_id " : metadata . get ( " chat_id " , None ) ,
" message_id " : metadata . get ( " message_id " , None ) ,
" session_id " : metadata . get ( " session_id " , None ) ,
" result " : item ,
} ,
user ,
)
tool_result_files . append (
{
" type " : item . get ( " type " , " data " ) ,
" url " : file_url ,
}
)
tool_result = tool_response [ 0 ] if len ( tool_response ) == 1 else tool_response
else : # OpenAPI
for item in tool_result :
if isinstance ( item , str ) and item . startswith ( " data: " ) :
tool_result_files . append (
{
" type " : " data " ,
" content " : item ,
}
)
tool_result . remove ( item )
2025-11-28 16:21:00 +00:00
if isinstance ( tool_result , list ) and ENABLE_WRAP_TOOL_RESULT :
2025-09-28 19:46:01 +00:00
tool_result = { " results " : tool_result }
if isinstance ( tool_result , dict ) or isinstance ( tool_result , list ) :
2025-11-28 16:21:00 +00:00
tool_result = dump_tool_result_to_json ( tool_result , ensure_ascii = False )
2025-09-28 19:46:01 +00:00
return tool_result , tool_result_files , tool_result_embeds
2025-11-28 17:45:11 +00:00
2025-11-28 16:21:00 +00:00
def dump_tool_result_to_json ( model , ensure_ascii = True ) :
indent_size = None if TOOL_RESULT_INDENT_SIZE == 0 else TOOL_RESULT_INDENT_SIZE
2025-11-28 17:45:11 +00:00
separators = None if indent_size and indent_size > 0 else ( " , " , " : " )
return json . dumps (
model , indent = indent_size , separators = separators , ensure_ascii = ensure_ascii
)
2025-09-28 19:46:01 +00:00
2024-12-13 06:28:42 +00:00
async def chat_completion_tools_handler (
2025-03-26 07:40:24 +00:00
request : Request , body : dict , extra_params : dict , user : UserModel , models , tools
2024-12-13 06:28:42 +00:00
) - > tuple [ dict , dict ] :
async def get_content_from_response ( response ) - > Optional [ str ] :
content = None
if hasattr ( response , " body_iterator " ) :
async for chunk in response . body_iterator :
2025-09-29 08:16:50 +00:00
data = json . loads ( chunk . decode ( " utf-8 " , " replace " ) )
2024-12-13 06:28:42 +00:00
content = data [ " choices " ] [ 0 ] [ " message " ] [ " content " ]
# Cleanup any remaining background tasks if necessary
if response . background is not None :
await response . background ( )
else :
content = response [ " choices " ] [ 0 ] [ " message " ] [ " content " ]
return content
def get_tools_function_calling_payload ( messages , task_model_id , content ) :
user_message = get_last_user_message ( messages )
2025-09-23 21:51:31 +00:00
2025-11-11 19:35:25 +00:00
if user_message and messages and messages [ - 1 ] [ " role " ] == " user " :
# Remove the last user message to avoid duplication
messages = messages [ : - 1 ]
2025-09-23 21:51:31 +00:00
recent_messages = messages [ - 4 : ] if len ( messages ) > 4 else messages
chat_history = " \n " . join (
2025-09-29 16:50:01 +00:00
f " { message [ ' role ' ] . upper ( ) } : \" \" \" { get_content_from_message ( message ) } \" \" \" "
2025-09-23 21:51:31 +00:00
for message in recent_messages
2024-12-13 06:28:42 +00:00
)
2025-11-13 04:39:27 +00:00
prompt = (
f " History: \n { chat_history } \n Query: { user_message } "
if chat_history
else f " Query: { user_message } "
)
2024-12-13 06:28:42 +00:00
return {
" model " : task_model_id ,
" messages " : [
{ " role " : " system " , " content " : content } ,
2025-11-11 19:35:25 +00:00
{ " role " : " user " , " content " : prompt } ,
2024-12-13 06:28:42 +00:00
] ,
" stream " : False ,
" metadata " : { " task " : str ( TASKS . FUNCTION_CALLING ) } ,
}
2025-03-26 07:40:24 +00:00
event_caller = extra_params [ " __event_call__ " ]
2025-09-28 19:46:01 +00:00
event_emitter = extra_params [ " __event_emitter__ " ]
2025-03-26 07:40:24 +00:00
metadata = extra_params [ " __metadata__ " ]
2024-12-13 06:28:42 +00:00
task_model_id = get_task_model_id (
body [ " model " ] ,
request . app . state . config . TASK_MODEL ,
request . app . state . config . TASK_MODEL_EXTERNAL ,
models ,
)
2025-02-05 05:01:53 +00:00
skip_files = False
sources = [ ]
2024-12-13 06:28:42 +00:00
specs = [ tool [ " spec " ] for tool in tools . values ( ) ]
tools_specs = json . dumps ( specs )
if request . app . state . config . TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE != " " :
template = request . app . state . config . TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
else :
template = DEFAULT_TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
tools_function_calling_prompt = tools_function_calling_generation_template (
template , tools_specs
)
payload = get_tools_function_calling_payload (
body [ " messages " ] , task_model_id , tools_function_calling_prompt
)
try :
response = await generate_chat_completion ( request , form_data = payload , user = user )
log . debug ( f " { response =} " )
content = await get_content_from_response ( response )
log . debug ( f " { content =} " )
if not content :
return body , { }
try :
content = content [ content . find ( " { " ) : content . rfind ( " } " ) + 1 ]
if not content :
raise Exception ( " No JSON object found in the response " )
result = json . loads ( content )
2025-02-02 05:01:06 +00:00
async def tool_call_handler ( tool_call ) :
2025-02-05 05:01:53 +00:00
nonlocal skip_files
2025-02-02 05:01:06 +00:00
log . debug ( f " { tool_call =} " )
2024-12-13 06:28:42 +00:00
2025-02-02 05:01:06 +00:00
tool_function_name = tool_call . get ( " name " , None )
if tool_function_name not in tools :
return body , { }
2024-12-13 06:28:42 +00:00
2025-02-02 05:01:06 +00:00
tool_function_params = tool_call . get ( " parameters " , { } )
2024-12-13 06:28:42 +00:00
2025-09-28 19:46:01 +00:00
tool = None
tool_type = " "
direct_tool = False
2025-02-02 05:01:06 +00:00
try :
2025-03-26 07:40:24 +00:00
tool = tools [ tool_function_name ]
2025-09-28 19:46:01 +00:00
tool_type = tool . get ( " type " , " " )
direct_tool = tool . get ( " direct " , False )
2025-03-26 07:40:24 +00:00
spec = tool . get ( " spec " , { } )
2025-03-07 20:37:22 +00:00
allowed_params = (
spec . get ( " parameters " , { } ) . get ( " properties " , { } ) . keys ( )
2024-12-13 06:28:42 +00:00
)
2025-02-02 05:01:06 +00:00
tool_function_params = {
k : v
for k , v in tool_function_params . items ( )
2025-03-07 20:37:22 +00:00
if k in allowed_params
2025-02-02 05:01:06 +00:00
}
2025-03-26 07:40:24 +00:00
if tool . get ( " direct " , False ) :
2025-03-30 07:44:09 +00:00
tool_result = await event_caller (
2025-03-26 07:40:24 +00:00
{
" type " : " execute:tool " ,
" data " : {
" id " : str ( uuid4 ( ) ) ,
2025-03-27 09:27:56 +00:00
" name " : tool_function_name ,
2025-03-26 07:40:24 +00:00
" params " : tool_function_params ,
2025-03-27 08:38:35 +00:00
" server " : tool . get ( " server " , { } ) ,
2025-03-26 07:40:24 +00:00
" session_id " : metadata . get ( " session_id " , None ) ,
} ,
}
)
2025-03-27 09:50:53 +00:00
else :
tool_function = tool [ " callable " ]
2025-03-30 07:44:09 +00:00
tool_result = await tool_function ( * * tool_function_params )
2025-02-02 05:01:06 +00:00
except Exception as e :
2025-03-30 07:44:09 +00:00
tool_result = str ( e )
2025-02-02 05:01:06 +00:00
2025-09-28 19:46:01 +00:00
tool_result , tool_result_files , tool_result_embeds = (
process_tool_result (
request ,
tool_function_name ,
tool_result ,
tool_type ,
direct_tool ,
metadata ,
user ,
2025-09-26 20:50:16 +00:00
)
2025-09-28 19:46:01 +00:00
)
2025-09-26 20:57:03 +00:00
2025-09-28 19:46:01 +00:00
if event_emitter :
if tool_result_files :
await event_emitter (
{
" type " : " files " ,
" data " : {
" files " : tool_result_files ,
} ,
}
2025-09-26 20:50:16 +00:00
)
2025-09-28 19:46:01 +00:00
if tool_result_embeds :
await event_emitter (
2025-09-28 18:22:39 +00:00
{
" type " : " embeds " ,
" data " : {
" embeds " : tool_result_embeds ,
} ,
}
)
2025-09-28 19:46:01 +00:00
print (
f " Tool { tool_function_name } result: { tool_result } " ,
tool_result_files ,
tool_result_embeds ,
)
if tool_result :
2025-03-27 09:50:53 +00:00
tool = tools [ tool_function_name ]
2025-04-05 11:03:15 +00:00
tool_id = tool . get ( " tool_id " , " " )
2025-04-13 00:26:35 +00:00
tool_name = (
f " { tool_id } / { tool_function_name } "
if tool_id
else f " { tool_function_name } "
)
2025-06-25 09:36:41 +00:00
2025-07-07 07:42:52 +00:00
# Citation is enabled for this tool
sources . append (
{
" source " : {
2025-09-28 19:46:01 +00:00
" name " : ( f " { tool_name } " ) ,
2025-07-07 07:42:52 +00:00
} ,
2025-09-28 19:46:01 +00:00
" document " : [ str ( tool_result ) ] ,
2025-07-07 07:42:52 +00:00
" metadata " : [
{
2025-09-28 19:46:01 +00:00
" source " : ( f " { tool_name } " ) ,
2025-07-07 07:42:52 +00:00
" parameters " : tool_function_params ,
}
] ,
" tool_result " : True ,
}
)
2025-09-28 19:46:01 +00:00
2025-07-07 07:42:52 +00:00
# Citation is not enabled for this tool
body [ " messages " ] = add_or_update_user_message (
f " \n Tool ` { tool_name } ` Output: { tool_result } " ,
body [ " messages " ] ,
)
2025-02-02 05:01:06 +00:00
2025-04-05 10:49:07 +00:00
if (
tools [ tool_function_name ]
. get ( " metadata " , { } )
. get ( " file_handler " , False )
) :
2025-02-02 05:01:06 +00:00
skip_files = True
2024-12-13 06:28:42 +00:00
2025-02-02 05:01:06 +00:00
# check if "tool_calls" in result
if result . get ( " tool_calls " ) :
for tool_call in result . get ( " tool_calls " ) :
await tool_call_handler ( tool_call )
else :
await tool_call_handler ( result )
2024-12-13 06:28:42 +00:00
except Exception as e :
2025-03-28 07:25:00 +00:00
log . debug ( f " Error: { e } " )
2024-12-13 06:28:42 +00:00
content = None
except Exception as e :
2025-03-28 07:25:00 +00:00
log . debug ( f " Error: { e } " )
2024-12-13 06:28:42 +00:00
content = None
log . debug ( f " tool_contexts: { sources } " )
if skip_files and " files " in body . get ( " metadata " , { } ) :
del body [ " metadata " ] [ " files " ]
return body , { " sources " : sources }
2025-05-22 23:26:14 +00:00
async def chat_memory_handler (
request : Request , form_data : dict , extra_params : dict , user
) :
2025-05-29 20:12:28 +00:00
try :
results = await query_memory (
request ,
QueryMemoryForm (
* * {
" content " : get_last_user_message ( form_data [ " messages " ] ) or " " ,
" k " : 3 ,
}
) ,
user ,
)
except Exception as e :
log . debug ( e )
results = None
2025-05-22 23:26:14 +00:00
user_context = " "
if results and hasattr ( results , " documents " ) :
if results . documents and len ( results . documents ) > 0 :
for doc_idx , doc in enumerate ( results . documents [ 0 ] ) :
created_at_date = " Unknown Date "
if results . metadatas [ 0 ] [ doc_idx ] . get ( " created_at " ) :
created_at_timestamp = results . metadatas [ 0 ] [ doc_idx ] [ " created_at " ]
created_at_date = time . strftime (
" % Y- % m- %d " , time . localtime ( created_at_timestamp )
)
user_context + = f " { doc_idx + 1 } . [ { created_at_date } ] { doc } \n "
form_data [ " messages " ] = add_or_update_system_message (
f " User Context: \n { user_context } \n " , form_data [ " messages " ] , append = True
)
return form_data
2024-12-25 00:52:57 +00:00
async def chat_web_search_handler (
request : Request , form_data : dict , extra_params : dict , user
) :
event_emitter = extra_params [ " __event_emitter__ " ]
await event_emitter (
{
" type " : " status " ,
" data " : {
" action " : " web_search " ,
2025-09-07 00:21:46 +00:00
" description " : " Searching the web " ,
2024-12-25 00:52:57 +00:00
" done " : False ,
} ,
}
)
messages = form_data [ " messages " ]
user_message = get_last_user_message ( messages )
queries = [ ]
try :
res = await generate_queries (
request ,
{
" model " : form_data [ " model " ] ,
" messages " : messages ,
" prompt " : user_message ,
" type " : " web_search " ,
} ,
user ,
)
response = res [ " choices " ] [ 0 ] [ " message " ] [ " content " ]
try :
bracket_start = response . find ( " { " )
bracket_end = response . rfind ( " } " ) + 1
if bracket_start == - 1 or bracket_end == - 1 :
raise Exception ( " No JSON object found in the response " )
response = response [ bracket_start : bracket_end ]
queries = json . loads ( response )
queries = queries . get ( " queries " , [ ] )
except Exception as e :
queries = [ response ]
2025-08-26 23:07:21 +00:00
if ENABLE_QUERIES_CACHE :
request . state . cached_queries = queries
2024-12-25 00:52:57 +00:00
except Exception as e :
log . exception ( e )
queries = [ user_message ]
2025-05-16 13:11:42 +00:00
# Check if generated queries are empty
if len ( queries ) == 1 and queries [ 0 ] . strip ( ) == " " :
queries = [ user_message ]
# Check if queries are not found
2024-12-25 00:52:57 +00:00
if len ( queries ) == 0 :
await event_emitter (
{
" type " : " status " ,
" data " : {
" action " : " web_search " ,
" description " : " No search query generated " ,
" done " : True ,
} ,
}
)
2025-02-04 11:13:54 +00:00
return form_data
2024-12-25 00:52:57 +00:00
2025-04-21 14:18:05 +00:00
await event_emitter (
{
" type " : " status " ,
" data " : {
2025-09-07 00:21:46 +00:00
" action " : " web_search_queries_generated " ,
" queries " : queries ,
2025-04-21 14:18:05 +00:00
" done " : False ,
} ,
}
)
2024-12-25 00:52:57 +00:00
2025-05-10 13:54:41 +00:00
try :
results = await process_web_search (
request ,
SearchForm ( queries = queries ) ,
user = user ,
)
2024-12-25 00:52:57 +00:00
2025-05-10 13:54:41 +00:00
if results :
files = form_data . get ( " files " , [ ] )
if results . get ( " collection_names " ) :
for col_idx , collection_name in enumerate (
results . get ( " collection_names " )
) :
files . append (
{
" collection_name " : collection_name ,
" name " : " , " . join ( queries ) ,
" type " : " web_search " ,
" urls " : results [ " filenames " ] ,
2025-05-23 21:12:11 +00:00
" queries " : queries ,
2025-05-10 13:54:41 +00:00
}
)
elif results . get ( " docs " ) :
# Invoked when bypass embedding and retrieval is set to True
docs = results [ " docs " ]
files . append (
{
" docs " : docs ,
" name " : " , " . join ( queries ) ,
" type " : " web_search " ,
" urls " : results [ " filenames " ] ,
2025-05-23 21:12:11 +00:00
" queries " : queries ,
2025-05-10 13:54:41 +00:00
}
)
2025-02-19 05:29:27 +00:00
2025-05-10 13:54:41 +00:00
form_data [ " files " ] = files
2025-02-26 23:42:19 +00:00
2024-12-25 00:52:57 +00:00
await event_emitter (
{
" type " : " status " ,
" data " : {
" action " : " web_search " ,
2025-05-10 13:54:41 +00:00
" description " : " Searched {{ count}} sites " ,
" urls " : results [ " filenames " ] ,
2025-09-06 23:00:28 +00:00
" items " : results . get ( " items " , [ ] ) ,
2025-05-10 13:54:41 +00:00
" done " : True ,
} ,
}
)
else :
await event_emitter (
{
" type " : " status " ,
" data " : {
" action " : " web_search " ,
" description " : " No search results found " ,
2024-12-25 00:52:57 +00:00
" done " : True ,
" error " : True ,
} ,
}
)
2025-02-19 05:29:27 +00:00
2025-05-10 13:54:41 +00:00
except Exception as e :
log . exception ( e )
2025-02-19 05:29:27 +00:00
await event_emitter (
{
" type " : " status " ,
" data " : {
" action " : " web_search " ,
2025-05-10 13:54:41 +00:00
" description " : " An error occurred while searching the web " ,
" queries " : queries ,
2024-12-25 00:52:57 +00:00
" done " : True ,
" error " : True ,
} ,
}
)
return form_data
2025-11-05 08:31:37 +00:00
def get_last_images ( message_list ) :
images = [ ]
for message in reversed ( message_list ) :
images_flag = False
for file in message . get ( " files " , [ ] ) :
if file . get ( " type " ) == " image " :
images . append ( file . get ( " url " ) )
images_flag = True
if images_flag :
break
return images
2025-11-19 07:16:09 +00:00
def get_image_urls ( delta_images , request , metadata , user ) - > list [ str ] :
2025-11-19 07:07:56 +00:00
if not isinstance ( delta_images , list ) :
return [ ]
2025-11-19 07:16:09 +00:00
image_urls = [ ]
2025-11-19 07:07:56 +00:00
for img in delta_images :
if not isinstance ( img , dict ) or img . get ( " type " ) != " image_url " :
continue
url = img . get ( " image_url " , { } ) . get ( " url " )
if not url :
continue
if url . startswith ( " data:image/png;base64 " ) :
url = get_image_url_from_base64 ( request , url , metadata , user )
2025-11-19 07:16:09 +00:00
image_urls . append ( url )
2025-11-19 07:07:56 +00:00
2025-11-19 07:16:09 +00:00
return image_urls
2025-11-19 07:07:56 +00:00
2025-01-16 07:32:13 +00:00
async def chat_image_generation_handler (
request : Request , form_data : dict , extra_params : dict , user
) :
2025-11-05 08:31:37 +00:00
metadata = extra_params . get ( " __metadata__ " , { } )
chat_id = metadata . get ( " chat_id " , None )
if not chat_id :
return form_data
2025-01-16 07:32:13 +00:00
__event_emitter__ = extra_params [ " __event_emitter__ " ]
2025-11-28 16:11:56 +00:00
if chat_id . startswith ( " local: " ) :
message_list = form_data . get ( " messages " , [ ] )
else :
chat = Chats . get_chat_by_id_and_user_id ( chat_id , user . id )
await __event_emitter__ (
{
" type " : " status " ,
" data " : { " description " : " Creating image " , " done " : False } ,
}
)
messages_map = chat . chat . get ( " history " , { } ) . get ( " messages " , { } )
message_id = chat . chat . get ( " history " , { } ) . get ( " currentId " )
message_list = get_message_list ( messages_map , message_id )
2025-11-05 08:31:37 +00:00
user_message = get_last_user_message ( message_list )
2025-01-16 07:32:13 +00:00
2025-01-16 08:13:02 +00:00
prompt = user_message
2025-11-05 08:31:37 +00:00
input_images = get_last_images ( message_list )
2025-01-16 08:06:37 +00:00
2025-11-05 08:31:37 +00:00
system_message_content = " "
2025-01-16 08:06:37 +00:00
2025-11-21 00:52:31 +00:00
if len ( input_images ) > 0 and request . app . state . config . ENABLE_IMAGE_EDIT :
# Edit image(s)
2025-11-05 08:31:37 +00:00
try :
2025-11-21 00:52:31 +00:00
images = await image_edits (
2025-11-05 08:31:37 +00:00
request = request ,
2025-11-21 00:52:31 +00:00
form_data = EditImageForm ( * * { " prompt " : prompt , " image " : input_images } ) ,
2025-11-05 08:31:37 +00:00
user = user ,
)
await __event_emitter__ (
{
" type " : " status " ,
" data " : { " description " : " Image created " , " done " : True } ,
}
)
await __event_emitter__ (
{
" type " : " files " ,
" data " : {
" files " : [
{
" type " : " image " ,
" url " : image [ " url " ] ,
}
for image in images
]
} ,
}
)
system_message_content = " <context>The requested image has been created and is now being shown to the user. Let them know that it has been generated.</context> "
2025-01-16 08:06:37 +00:00
except Exception as e :
2025-11-05 08:31:37 +00:00
log . debug ( e )
2025-01-16 08:06:37 +00:00
2025-11-05 08:31:37 +00:00
error_message = " "
if isinstance ( e , HTTPException ) :
if e . detail and isinstance ( e . detail , dict ) :
error_message = e . detail . get ( " message " , str ( e . detail ) )
else :
error_message = str ( e . detail )
2025-01-16 07:32:13 +00:00
2025-11-05 08:31:37 +00:00
await __event_emitter__ (
{
" type " : " status " ,
" data " : {
" description " : f " An error occurred while generating an image " ,
" done " : True ,
} ,
}
)
2025-01-16 07:32:13 +00:00
2025-11-05 08:31:37 +00:00
system_message_content = f " <context>Image generation was attempted but failed. The system is currently unable to generate the image. Tell the user that an error occurred: { error_message } </context> "
2025-11-21 00:52:31 +00:00
2025-11-05 08:31:37 +00:00
else :
2025-11-21 00:52:31 +00:00
# Create image(s)
if request . app . state . config . ENABLE_IMAGE_PROMPT_GENERATION :
try :
res = await generate_image_prompt (
request ,
{
" model " : form_data [ " model " ] ,
" messages " : form_data [ " messages " ] ,
} ,
user ,
)
response = res [ " choices " ] [ 0 ] [ " message " ] [ " content " ]
try :
bracket_start = response . find ( " { " )
bracket_end = response . rfind ( " } " ) + 1
if bracket_start == - 1 or bracket_end == - 1 :
raise Exception ( " No JSON object found in the response " )
response = response [ bracket_start : bracket_end ]
response = json . loads ( response )
prompt = response . get ( " prompt " , [ ] )
except Exception as e :
prompt = user_message
except Exception as e :
log . exception ( e )
prompt = user_message
2025-11-05 08:31:37 +00:00
try :
2025-11-21 00:52:31 +00:00
images = await image_generations (
2025-11-05 08:31:37 +00:00
request = request ,
2025-11-21 00:52:31 +00:00
form_data = CreateImageForm ( * * { " prompt " : prompt } ) ,
2025-11-05 08:31:37 +00:00
user = user ,
)
2025-01-16 07:32:13 +00:00
2025-11-05 08:31:37 +00:00
await __event_emitter__ (
{
" type " : " status " ,
" data " : { " description " : " Image created " , " done " : True } ,
}
)
2025-01-16 07:32:13 +00:00
2025-11-05 08:31:37 +00:00
await __event_emitter__ (
{
" type " : " files " ,
" data " : {
" files " : [
{
" type " : " image " ,
" url " : image [ " url " ] ,
}
for image in images
]
} ,
}
)
system_message_content = " <context>The requested image has been created and is now being shown to the user. Let them know that it has been generated.</context> "
except Exception as e :
log . debug ( e )
error_message = " "
if isinstance ( e , HTTPException ) :
if e . detail and isinstance ( e . detail , dict ) :
error_message = e . detail . get ( " message " , str ( e . detail ) )
else :
error_message = str ( e . detail )
await __event_emitter__ (
{
" type " : " status " ,
" data " : {
" description " : f " An error occurred while generating an image " ,
" done " : True ,
} ,
}
)
2025-01-16 07:32:13 +00:00
2025-11-05 08:31:37 +00:00
system_message_content = f " <context>Image generation was attempted but failed. The system is currently unable to generate the image. Tell the user that an error occurred: { error_message } </context> "
2025-01-16 07:32:13 +00:00
if system_message_content :
form_data [ " messages " ] = add_or_update_system_message (
system_message_content , form_data [ " messages " ]
)
return form_data
2024-12-13 06:28:42 +00:00
async def chat_completion_files_handler (
2025-09-07 00:21:46 +00:00
request : Request , body : dict , extra_params : dict , user : UserModel
2024-12-13 06:28:42 +00:00
) - > tuple [ dict , dict [ str , list ] ] :
2025-09-07 00:21:46 +00:00
__event_emitter__ = extra_params [ " __event_emitter__ " ]
2024-12-13 06:28:42 +00:00
sources = [ ]
if files := body . get ( " metadata " , { } ) . get ( " files " , None ) :
2025-09-25 15:54:58 +00:00
# Check if all files are in full context mode
2025-10-09 16:55:12 +00:00
all_full_context = all ( item . get ( " context " ) == " full " for item in files )
2024-12-13 06:28:42 +00:00
2025-09-25 15:54:58 +00:00
queries = [ ]
if not all_full_context :
2024-12-13 06:28:42 +00:00
try :
2025-09-25 15:54:58 +00:00
queries_response = await generate_queries (
request ,
{
" model " : body [ " model " ] ,
" messages " : body [ " messages " ] ,
" type " : " retrieval " ,
} ,
user ,
)
queries_response = queries_response [ " choices " ] [ 0 ] [ " message " ] [ " content " ]
2024-12-13 06:28:42 +00:00
2025-09-25 15:54:58 +00:00
try :
bracket_start = queries_response . find ( " { " )
bracket_end = queries_response . rfind ( " } " ) + 1
2024-12-13 06:28:42 +00:00
2025-09-25 15:54:58 +00:00
if bracket_start == - 1 or bracket_end == - 1 :
raise Exception ( " No JSON object found in the response " )
queries_response = queries_response [ bracket_start : bracket_end ]
queries_response = json . loads ( queries_response )
except Exception as e :
queries_response = { " queries " : [ queries_response ] }
2024-12-13 06:28:42 +00:00
2025-09-25 15:54:58 +00:00
queries = queries_response . get ( " queries " , [ ] )
except :
pass
2024-12-13 06:28:42 +00:00
2025-09-25 15:54:58 +00:00
await __event_emitter__ (
{
" type " : " status " ,
" data " : {
" action " : " queries_generated " ,
" queries " : queries ,
" done " : False ,
} ,
}
)
2025-09-07 00:21:46 +00:00
2025-10-04 07:02:26 +00:00
if len ( queries ) == 0 :
queries = [ get_last_user_message ( body [ " messages " ] ) ]
2025-01-16 19:11:23 +00:00
try :
2025-11-23 01:54:59 +00:00
# Directly await async get_sources_from_items (no thread needed - fully async now)
sources = await get_sources_from_items (
request = request ,
items = files ,
queries = queries ,
embedding_function = lambda query , prefix : request . app . state . EMBEDDING_FUNCTION (
query , prefix = prefix , user = user
) ,
k = request . app . state . config . TOP_K ,
reranking_function = (
(
lambda query , documents : request . app . state . RERANKING_FUNCTION (
query , documents , user = user
)
)
if request . app . state . RERANKING_FUNCTION
else None
) ,
k_reranker = request . app . state . config . TOP_K_RERANKER ,
r = request . app . state . config . RELEVANCE_THRESHOLD ,
hybrid_bm25_weight = request . app . state . config . HYBRID_BM25_WEIGHT ,
hybrid_search = request . app . state . config . ENABLE_RAG_HYBRID_SEARCH ,
full_context = all_full_context
or request . app . state . config . RAG_FULL_CONTEXT ,
user = user ,
)
2025-01-16 19:11:23 +00:00
except Exception as e :
log . exception ( e )
2024-12-13 06:28:42 +00:00
log . debug ( f " rag_contexts:sources: { sources } " )
2025-01-16 19:11:23 +00:00
2025-09-07 01:17:38 +00:00
unique_ids = set ( )
for source in sources or [ ] :
if not source or len ( source . keys ( ) ) == 0 :
continue
documents = source . get ( " document " ) or [ ]
metadatas = source . get ( " metadata " ) or [ ]
src_info = source . get ( " source " ) or { }
for index , _ in enumerate ( documents ) :
metadata = metadatas [ index ] if index < len ( metadatas ) else None
_id = (
( metadata or { } ) . get ( " source " )
or ( src_info or { } ) . get ( " id " )
or " N/A "
)
unique_ids . add ( _id )
sources_count = len ( unique_ids )
2025-09-07 01:06:03 +00:00
await __event_emitter__ (
{
" type " : " status " ,
" data " : {
" action " : " sources_retrieved " ,
" count " : sources_count ,
" done " : True ,
} ,
}
)
2024-12-13 06:28:42 +00:00
return body , { " sources " : sources }
2024-12-16 21:27:54 +00:00
def apply_params_to_form_data ( form_data , model ) :
params = form_data . pop ( " params " , { } )
2025-05-28 23:33:11 +00:00
custom_params = params . pop ( " custom_params " , { } )
2025-05-29 08:57:58 +00:00
open_webui_params = {
" stream_response " : bool ,
2025-08-09 19:43:27 +00:00
" stream_delta_chunk_size " : int ,
2025-05-29 08:57:58 +00:00
" function_calling " : str ,
2025-08-27 13:24:16 +00:00
" reasoning_tags " : list ,
2025-05-29 08:57:58 +00:00
" system " : str ,
}
for key in list ( params . keys ( ) ) :
if key in open_webui_params :
del params [ key ]
2025-05-28 23:33:11 +00:00
if custom_params :
2025-05-29 19:32:14 +00:00
# Attempt to parse custom_params if they are strings
for key , value in custom_params . items ( ) :
if isinstance ( value , str ) :
try :
# Attempt to parse the string as JSON
custom_params [ key ] = json . loads ( value )
except json . JSONDecodeError :
# If it fails, keep the original string
pass
2025-05-28 23:33:11 +00:00
# If custom_params are provided, merge them into params
params = deep_update ( params , custom_params )
2025-06-16 13:18:43 +00:00
if model . get ( " owned_by " ) == " ollama " :
2025-06-10 08:48:34 +00:00
# Ollama specific parameters
2024-12-16 21:27:54 +00:00
form_data [ " options " ] = params
else :
2025-05-28 22:56:37 +00:00
if isinstance ( params , dict ) :
for key , value in params . items ( ) :
if value is not None :
form_data [ key ] = value
2025-04-03 02:56:39 +00:00
if " logit_bias " in params and params [ " logit_bias " ] is not None :
2025-02-20 00:23:58 +00:00
try :
2025-03-01 14:56:24 +00:00
form_data [ " logit_bias " ] = json . loads (
convert_logit_bias_input_to_json ( params [ " logit_bias " ] )
)
2025-02-28 07:13:30 +00:00
except Exception as e :
2025-05-12 14:18:47 +00:00
log . exception ( f " Error parsing logit_bias: { e } " )
2025-01-22 20:07:04 +00:00
2024-12-16 21:27:54 +00:00
return form_data
2025-03-01 14:56:24 +00:00
async def process_chat_payload ( request , form_data , user , metadata , model ) :
2025-06-25 08:20:08 +00:00
# Pipeline Inlet -> Filter Inlet -> Chat Memory -> Chat Web Search -> Chat Image Generation
# -> Chat Code Interpreter (Form Data Update) -> (Default) Chat Tools Function Calling
# -> Chat Files
2024-12-16 21:27:54 +00:00
form_data = apply_params_to_form_data ( form_data , model )
log . debug ( f " form_data: { form_data } " )
2025-08-20 23:38:26 +00:00
system_message = get_system_message ( form_data . get ( " messages " , [ ] ) )
2025-10-04 06:19:33 +00:00
if system_message : # Chat Controls/User Settings
2025-08-22 10:01:57 +00:00
try :
form_data = apply_system_prompt_to_body (
2025-10-02 07:57:54 +00:00
system_message . get ( " content " ) , form_data , metadata , user , replace = True
2025-10-04 06:19:33 +00:00
) # Required to handle system prompt variables
2025-08-22 10:01:57 +00:00
except :
pass
2025-08-20 23:38:26 +00:00
2024-12-24 23:49:32 +00:00
event_emitter = get_event_emitter ( metadata )
2025-11-21 09:32:49 +00:00
event_caller = get_event_call ( metadata )
2024-12-24 23:49:32 +00:00
2025-09-08 14:05:43 +00:00
oauth_token = None
try :
2025-09-11 17:56:59 +00:00
if request . cookies . get ( " oauth_session_id " , None ) :
2025-09-19 05:10:48 +00:00
oauth_token = await request . app . state . oauth_manager . get_oauth_token (
2025-09-11 17:56:59 +00:00
user . id ,
request . cookies . get ( " oauth_session_id " , None ) ,
)
2025-09-08 14:05:43 +00:00
except Exception as e :
log . error ( f " Error getting OAuth token: { e } " )
2024-12-13 06:28:42 +00:00
extra_params = {
2024-12-24 23:49:32 +00:00
" __event_emitter__ " : event_emitter ,
2025-11-21 09:32:49 +00:00
" __event_call__ " : event_caller ,
2025-06-04 11:53:07 +00:00
" __user__ " : user . model_dump ( ) if isinstance ( user , UserModel ) else { } ,
2024-12-13 06:28:42 +00:00
" __metadata__ " : metadata ,
2025-11-21 09:32:49 +00:00
" __oauth_token__ " : oauth_token ,
2024-12-14 06:51:43 +00:00
" __request__ " : request ,
2025-02-08 09:07:05 +00:00
" __model__ " : model ,
2024-12-13 06:28:42 +00:00
}
# Initialize events to store additional event to be sent to the client
# Initialize contexts and citation
2025-02-13 07:26:47 +00:00
if getattr ( request . state , " direct " , False ) and hasattr ( request . state , " model " ) :
2025-02-13 06:56:33 +00:00
models = {
request . state . model [ " id " ] : request . state . model ,
}
else :
models = request . app . state . MODELS
2025-02-05 05:01:53 +00:00
task_model_id = get_task_model_id (
form_data [ " model " ] ,
request . app . state . config . TASK_MODEL ,
request . app . state . config . TASK_MODEL_EXTERNAL ,
models ,
)
2024-12-24 23:49:32 +00:00
2024-12-13 06:28:42 +00:00
events = [ ]
sources = [ ]
2025-07-12 21:26:56 +00:00
# Folder "Project" handling
# Check if the request has chat_id and is inside of a folder
chat_id = metadata . get ( " chat_id " , None )
if chat_id and user :
chat = Chats . get_chat_by_id_and_user_id ( chat_id , user . id )
if chat and chat . folder_id :
folder = Folders . get_folder_by_id_and_user_id ( chat . folder_id , user . id )
if folder and folder . data :
if " system_prompt " in folder . data :
2025-08-20 23:38:26 +00:00
form_data = apply_system_prompt_to_body (
2025-08-06 09:32:28 +00:00
folder . data [ " system_prompt " ] , form_data , metadata , user
2025-07-12 21:26:56 +00:00
)
if " files " in folder . data :
form_data [ " files " ] = [
* folder . data [ " files " ] ,
* form_data . get ( " files " , [ ] ) ,
]
# Model "Knowledge" handling
2024-12-24 23:49:32 +00:00
user_message = get_last_user_message ( form_data [ " messages " ] )
model_knowledge = model . get ( " info " , { } ) . get ( " meta " , { } ) . get ( " knowledge " , False )
if model_knowledge :
await event_emitter (
{
" type " : " status " ,
" data " : {
" action " : " knowledge_search " ,
" query " : user_message ,
" done " : False ,
} ,
}
)
knowledge_files = [ ]
for item in model_knowledge :
if item . get ( " collection_name " ) :
knowledge_files . append (
{
" id " : item . get ( " collection_name " ) ,
" name " : item . get ( " name " ) ,
" legacy " : True ,
}
)
elif item . get ( " collection_names " ) :
knowledge_files . append (
{
" name " : item . get ( " name " ) ,
" type " : " collection " ,
" collection_names " : item . get ( " collection_names " ) ,
" legacy " : True ,
}
)
else :
knowledge_files . append ( item )
files = form_data . get ( " files " , [ ] )
files . extend ( knowledge_files )
form_data [ " files " ] = files
2025-01-30 05:56:51 +00:00
variables = form_data . pop ( " variables " , None )
2025-02-16 06:25:18 +00:00
# Process the form_data through the pipeline
try :
form_data = await process_pipeline_inlet_filter (
request , form_data , user , models
)
except Exception as e :
raise e
try :
2025-03-05 02:04:55 +00:00
filter_functions = [
Functions . get_function_by_id ( filter_id )
2025-05-16 19:33:02 +00:00
for filter_id in get_sorted_filter_ids (
request , model , metadata . get ( " filter_ids " , [ ] )
)
2025-03-05 02:04:55 +00:00
]
2025-02-16 06:25:18 +00:00
form_data , flags = await process_filter_functions (
request = request ,
2025-03-05 02:04:55 +00:00
filter_functions = filter_functions ,
2025-02-16 06:25:18 +00:00
filter_type = " inlet " ,
form_data = form_data ,
extra_params = extra_params ,
)
except Exception as e :
2025-09-01 10:14:20 +00:00
raise Exception ( f " { e } " )
2025-02-16 06:25:18 +00:00
2024-12-25 00:52:57 +00:00
features = form_data . pop ( " features " , None )
if features :
2025-11-14 01:23:13 +00:00
if " voice " in features and features [ " voice " ] :
if request . app . state . config . VOICE_MODE_PROMPT_TEMPLATE != None :
if request . app . state . config . VOICE_MODE_PROMPT_TEMPLATE != " " :
template = request . app . state . config . VOICE_MODE_PROMPT_TEMPLATE
else :
template = DEFAULT_VOICE_MODE_PROMPT_TEMPLATE
form_data [ " messages " ] = add_or_update_system_message (
template ,
form_data [ " messages " ] ,
)
2025-05-22 23:26:14 +00:00
if " memory " in features and features [ " memory " ] :
form_data = await chat_memory_handler (
request , form_data , extra_params , user
)
2024-12-25 00:52:57 +00:00
if " web_search " in features and features [ " web_search " ] :
form_data = await chat_web_search_handler (
request , form_data , extra_params , user
2025-01-16 07:32:13 +00:00
)
if " image_generation " in features and features [ " image_generation " ] :
form_data = await chat_image_generation_handler (
request , form_data , extra_params , user
2024-12-25 00:52:57 +00:00
)
2025-02-03 09:14:38 +00:00
if " code_interpreter " in features and features [ " code_interpreter " ] :
form_data [ " messages " ] = add_or_update_user_message (
2025-02-12 05:36:16 +00:00
(
request . app . state . config . CODE_INTERPRETER_PROMPT_TEMPLATE
if request . app . state . config . CODE_INTERPRETER_PROMPT_TEMPLATE != " "
else DEFAULT_CODE_INTERPRETER_PROMPT
) ,
form_data [ " messages " ] ,
2025-02-03 09:14:38 +00:00
)
2024-12-13 06:28:42 +00:00
tool_ids = form_data . pop ( " tool_ids " , None )
files = form_data . pop ( " files " , None )
2025-02-26 23:42:19 +00:00
2025-10-04 07:02:26 +00:00
prompt = get_last_user_message ( form_data [ " messages " ] )
2025-10-07 15:59:53 +00:00
# TODO: re-enable URL extraction from prompt
# urls = []
# if prompt and len(prompt or "") < 500 and (not files or len(files) == 0):
# urls = extract_urls(prompt)
2025-10-04 07:02:26 +00:00
2025-10-07 15:59:53 +00:00
if files :
2025-10-04 07:02:26 +00:00
if not files :
files = [ ]
2025-10-05 07:48:08 +00:00
for file_item in files :
if file_item . get ( " type " , " file " ) == " folder " :
# Get folder files
folder_id = file_item . get ( " id " , None )
if folder_id :
folder = Folders . get_folder_by_id_and_user_id ( folder_id , user . id )
if folder and folder . data and " files " in folder . data :
files = [ f for f in files if f . get ( " id " , None ) != folder_id ]
files = [ * files , * folder . data [ " files " ] ]
2025-10-07 15:59:53 +00:00
# files = [*files, *[{"type": "url", "url": url, "name": url} for url in urls]]
2025-10-04 07:02:26 +00:00
# Remove duplicate files based on their content
2024-12-25 00:01:17 +00:00
files = list ( { json . dumps ( f , sort_keys = True ) : f for f in files } . values ( ) )
2024-12-24 23:49:32 +00:00
2024-12-13 06:28:42 +00:00
metadata = {
* * metadata ,
" tool_ids " : tool_ids ,
" files " : files ,
}
form_data [ " metadata " ] = metadata
2025-03-26 07:40:24 +00:00
# Server side tools
2025-02-05 05:01:53 +00:00
tool_ids = metadata . get ( " tool_ids " , None )
2025-03-26 07:40:24 +00:00
# Client side tools
2025-09-23 06:03:26 +00:00
direct_tool_servers = metadata . get ( " tool_servers " , None )
2025-03-26 07:40:24 +00:00
2025-02-05 05:01:53 +00:00
log . debug ( f " { tool_ids =} " )
2025-09-23 06:03:26 +00:00
log . debug ( f " { direct_tool_servers =} " )
2025-03-26 07:40:24 +00:00
tools_dict = { }
2025-02-05 05:01:53 +00:00
2025-09-28 17:42:02 +00:00
mcp_clients = { }
2025-09-23 06:03:26 +00:00
mcp_tools_dict = { }
2025-02-05 05:01:53 +00:00
if tool_ids :
2025-09-23 06:03:26 +00:00
for tool_id in tool_ids :
if tool_id . startswith ( " server:mcp: " ) :
try :
server_id = tool_id [ len ( " server:mcp: " ) : ]
mcp_server_connection = None
for (
server_connection
) in request . app . state . config . TOOL_SERVER_CONNECTIONS :
if (
server_connection . get ( " type " , " " ) == " mcp "
and server_connection . get ( " info " , { } ) . get ( " id " ) == server_id
) :
mcp_server_connection = server_connection
break
if not mcp_server_connection :
log . error ( f " MCP server with id { server_id } not found " )
continue
auth_type = mcp_server_connection . get ( " auth_type " , " " )
headers = { }
if auth_type == " bearer " :
headers [ " Authorization " ] = (
f " Bearer { mcp_server_connection . get ( ' key ' , ' ' ) } "
)
elif auth_type == " none " :
# No authentication
pass
elif auth_type == " session " :
headers [ " Authorization " ] = (
f " Bearer { request . state . token . credentials } "
)
elif auth_type == " system_oauth " :
oauth_token = extra_params . get ( " __oauth_token__ " , None )
if oauth_token :
headers [ " Authorization " ] = (
f " Bearer { oauth_token . get ( ' access_token ' , ' ' ) } "
)
2025-09-25 06:49:16 +00:00
elif auth_type == " oauth_2.1 " :
try :
splits = server_id . split ( " : " )
server_id = splits [ - 1 ] if len ( splits ) > 1 else server_id
oauth_token = await request . app . state . oauth_client_manager . get_oauth_token (
user . id , f " mcp: { server_id } "
)
if oauth_token :
headers [ " Authorization " ] = (
f " Bearer { oauth_token . get ( ' access_token ' , ' ' ) } "
)
except Exception as e :
log . error ( f " Error getting OAuth token: { e } " )
oauth_token = None
2025-09-23 06:03:26 +00:00
2025-11-13 04:39:27 +00:00
connection_headers = mcp_server_connection . get ( " headers " , None )
2025-11-16 05:30:03 +00:00
if connection_headers and isinstance ( connection_headers , dict ) :
2025-11-13 04:39:27 +00:00
for key , value in connection_headers . items ( ) :
headers [ key ] = value
2025-09-28 17:42:02 +00:00
mcp_clients [ server_id ] = MCPClient ( )
await mcp_clients [ server_id ] . connect (
2025-09-23 06:03:26 +00:00
url = mcp_server_connection . get ( " url " , " " ) ,
headers = headers if headers else None ,
)
2025-11-25 21:27:27 +00:00
function_name_filter_list = mcp_server_connection . get (
" config " , { }
) . get ( " function_name_filter_list " , " " )
if isinstance ( function_name_filter_list , str ) :
function_name_filter_list = function_name_filter_list . split ( " , " )
2025-11-25 08:12:21 +00:00
2025-09-28 17:42:02 +00:00
tool_specs = await mcp_clients [ server_id ] . list_tool_specs ( )
2025-09-23 06:03:26 +00:00
for tool_spec in tool_specs :
2025-09-28 17:42:02 +00:00
def make_tool_function ( client , function_name ) :
2025-09-23 06:03:26 +00:00
async def tool_function ( * * kwargs ) :
2025-09-28 17:42:02 +00:00
return await client . call_tool (
2025-09-23 06:03:26 +00:00
function_name ,
function_args = kwargs ,
)
return tool_function
2025-11-25 08:12:21 +00:00
if function_name_filter_list :
2025-11-25 07:31:34 +00:00
if not is_string_allowed (
tool_spec [ " name " ] , function_name_filter_list
) :
# Skip this function
continue
2025-09-28 17:42:02 +00:00
tool_function = make_tool_function (
mcp_clients [ server_id ] , tool_spec [ " name " ]
)
2025-09-23 06:03:26 +00:00
2025-09-28 17:22:11 +00:00
mcp_tools_dict [ f " { server_id } _ { tool_spec [ ' name ' ] } " ] = {
" spec " : {
* * tool_spec ,
" name " : f " { server_id } _ { tool_spec [ ' name ' ] } " ,
} ,
2025-09-23 06:03:26 +00:00
" callable " : tool_function ,
" type " : " mcp " ,
2025-09-28 17:42:02 +00:00
" client " : mcp_clients [ server_id ] ,
2025-09-23 06:03:26 +00:00
" direct " : False ,
}
except Exception as e :
log . debug ( e )
2025-11-04 18:48:23 +00:00
if event_emitter :
await event_emitter (
{
" type " : " chat:message:error " ,
" data " : {
" error " : {
" content " : f " Failed to connect to MCP server ' { server_id } ' "
}
} ,
}
)
2025-09-23 06:03:26 +00:00
continue
2025-08-18 16:53:46 +00:00
tools_dict = await get_tools (
2025-02-05 05:01:53 +00:00
request ,
tool_ids ,
user ,
{
* * extra_params ,
" __model__ " : models [ task_model_id ] ,
" __messages__ " : form_data [ " messages " ] ,
" __files__ " : metadata . get ( " files " , [ ] ) ,
} ,
)
2025-11-25 07:31:34 +00:00
2025-09-23 06:03:26 +00:00
if mcp_tools_dict :
tools_dict = { * * tools_dict , * * mcp_tools_dict }
2025-03-26 07:40:24 +00:00
2025-09-23 06:03:26 +00:00
if direct_tool_servers :
for tool_server in direct_tool_servers :
2025-03-27 08:38:35 +00:00
tool_specs = tool_server . pop ( " specs " , [ ] )
for tool in tool_specs :
tools_dict [ tool [ " name " ] ] = {
" spec " : tool ,
" direct " : True ,
" server " : tool_server ,
}
2025-02-05 05:01:53 +00:00
2025-09-23 06:03:26 +00:00
if mcp_clients :
metadata [ " mcp_clients " ] = mcp_clients
2025-03-26 07:40:24 +00:00
if tools_dict :
2025-08-09 19:43:27 +00:00
if metadata . get ( " params " , { } ) . get ( " function_calling " ) == " native " :
2025-02-05 05:01:53 +00:00
# If the function calling is native, then call the tools function calling handler
2025-03-26 07:40:24 +00:00
metadata [ " tools " ] = tools_dict
2025-02-05 05:01:53 +00:00
form_data [ " tools " ] = [
{ " type " : " function " , " function " : tool . get ( " spec " , { } ) }
2025-03-26 07:40:24 +00:00
for tool in tools_dict . values ( )
2025-02-05 05:01:53 +00:00
]
else :
# If the function calling is not native, then call the tools function calling handler
try :
form_data , flags = await chat_completion_tools_handler (
2025-03-26 07:40:24 +00:00
request , form_data , extra_params , user , models , tools_dict
2025-02-05 05:01:53 +00:00
)
sources . extend ( flags . get ( " sources " , [ ] ) )
except Exception as e :
log . exception ( e )
2024-12-13 06:28:42 +00:00
try :
2025-09-07 00:21:46 +00:00
form_data , flags = await chat_completion_files_handler (
request , form_data , extra_params , user
)
2024-12-13 06:28:42 +00:00
sources . extend ( flags . get ( " sources " , [ ] ) )
except Exception as e :
log . exception ( e )
# If context is not empty, insert it into the messages
if len ( sources ) > 0 :
context_string = " "
2025-06-25 08:20:08 +00:00
citation_idx_map = { }
2025-04-17 14:27:22 +00:00
for source in sources :
2025-09-28 19:46:01 +00:00
if " document " in source :
2025-06-25 08:20:08 +00:00
for document_text , document_metadata in zip (
2025-04-10 16:20:18 +00:00
source [ " document " ] , source [ " metadata " ]
) :
2025-05-23 20:52:09 +00:00
source_name = source . get ( " source " , { } ) . get ( " name " , None )
2025-06-25 08:20:08 +00:00
source_id = (
document_metadata . get ( " source " , None )
2025-04-17 14:27:22 +00:00
or source . get ( " source " , { } ) . get ( " id " , None )
or " N/A "
)
2025-06-25 08:20:08 +00:00
if source_id not in citation_idx_map :
citation_idx_map [ source_id ] = len ( citation_idx_map ) + 1
2025-05-23 20:52:09 +00:00
context_string + = (
2025-06-25 08:20:08 +00:00
f ' <source id= " { citation_idx_map [ source_id ] } " '
2025-05-23 20:52:09 +00:00
+ ( f ' name= " { source_name } " ' if source_name else " " )
2025-06-25 08:20:08 +00:00
+ f " > { document_text } </source> \n "
2025-05-23 20:52:09 +00:00
)
2024-12-13 06:28:42 +00:00
context_string = context_string . strip ( )
if prompt is None :
raise Exception ( " No user message found " )
2025-08-14 00:04:20 +00:00
if context_string != " " :
2025-09-24 14:38:14 +00:00
form_data [ " messages " ] = add_or_update_user_message (
rag_template (
request . app . state . config . RAG_TEMPLATE ,
context_string ,
prompt ,
) ,
form_data [ " messages " ] ,
append = False ,
)
2024-12-13 06:28:42 +00:00
# If there are citations, add them to the data_items
2025-05-05 13:43:51 +00:00
sources = [
source
for source in sources
if source . get ( " source " , { } ) . get ( " name " , " " )
or source . get ( " source " , { } ) . get ( " id " , " " )
]
2024-12-13 06:28:42 +00:00
if len ( sources ) > 0 :
events . append ( { " sources " : sources } )
2024-12-24 23:49:32 +00:00
if model_knowledge :
await event_emitter (
{
" type " : " status " ,
" data " : {
" action " : " knowledge_search " ,
" query " : user_message ,
" done " : True ,
" hidden " : True ,
} ,
}
)
2025-02-05 05:01:53 +00:00
return form_data , metadata , events
2024-12-13 06:28:42 +00:00
2024-12-25 06:45:21 +00:00
async def process_chat_response (
2025-03-01 14:56:24 +00:00
request , response , form_data , user , metadata , model , events , tasks
2024-12-25 06:45:21 +00:00
) :
2024-12-26 06:21:44 +00:00
async def background_tasks_handler ( ) :
2025-10-02 03:49:25 +00:00
message = None
messages = [ ]
if " chat_id " in metadata and not metadata [ " chat_id " ] . startswith ( " local: " ) :
messages_map = Chats . get_messages_map_by_chat_id ( metadata [ " chat_id " ] )
message = messages_map . get ( metadata [ " message_id " ] ) if messages_map else None
2024-12-26 06:21:44 +00:00
2025-09-14 08:26:46 +00:00
message_list = get_message_list ( messages_map , metadata [ " message_id " ] )
2024-12-26 06:21:44 +00:00
2025-05-16 14:01:55 +00:00
# Remove details tags and files from the messages.
2025-05-08 06:51:50 +00:00
# as get_message_list creates a new list, it does not affect
2025-05-08 06:27:07 +00:00
# the original messages outside of this handler
2025-05-16 17:29:50 +00:00
messages = [ ]
for message in message_list :
2025-05-16 14:19:28 +00:00
content = message . get ( " content " , " " )
if isinstance ( content , list ) :
for item in content :
if item . get ( " type " ) == " text " :
content = item [ " text " ]
break
if isinstance ( content , str ) :
content = re . sub (
2025-05-22 21:33:08 +00:00
r " <details \ b[^>]*>.*?< \ /details>|! \ [.*? \ ] \ (.*? \ ) " ,
2025-05-16 14:19:28 +00:00
" " ,
content ,
flags = re . S | re . I ,
) . strip ( )
2025-05-16 17:29:50 +00:00
messages . append (
{
2025-05-20 19:55:11 +00:00
* * message ,
2025-05-26 22:18:43 +00:00
" role " : message . get (
" role " , " assistant "
) , # Safe fallback for missing role
2025-05-16 17:29:50 +00:00
" content " : content ,
}
)
2025-10-02 03:49:25 +00:00
else :
# Local temp chat, get the model and message from the form_data
message = get_last_user_message_item ( form_data . get ( " messages " , [ ] ) )
messages = form_data . get ( " messages " , [ ] )
if message :
message [ " model " ] = form_data . get ( " model " )
2025-05-08 06:27:07 +00:00
2025-10-02 03:49:25 +00:00
if message and " model " in message :
2025-02-04 02:36:49 +00:00
if tasks and messages :
2025-06-03 17:52:25 +00:00
if (
TASKS . FOLLOW_UP_GENERATION in tasks
and tasks [ TASKS . FOLLOW_UP_GENERATION ]
) :
res = await generate_follow_ups (
request ,
{
" model " : message [ " model " ] ,
" messages " : messages ,
" message_id " : metadata [ " message_id " ] ,
" chat_id " : metadata [ " chat_id " ] ,
} ,
user ,
)
if res and isinstance ( res , dict ) :
if len ( res . get ( " choices " , [ ] ) ) == 1 :
2025-10-07 12:59:21 +00:00
response_message = res . get ( " choices " , [ ] ) [ 0 ] . get (
" message " , { }
)
follow_ups_string = response_message . get (
2025-10-13 21:22:47 +00:00
" content "
) or response_message . get ( " reasoning_content " , " " )
2025-06-03 17:52:25 +00:00
else :
follow_ups_string = " "
follow_ups_string = follow_ups_string [
follow_ups_string . find ( " { " ) : follow_ups_string . rfind ( " } " )
+ 1
]
try :
follow_ups = json . loads ( follow_ups_string ) . get (
" follow_ups " , [ ]
)
await event_emitter (
{
" type " : " chat:message:follow_ups " ,
" data " : {
" follow_ups " : follow_ups ,
} ,
}
)
2025-10-02 03:49:25 +00:00
if not metadata . get ( " chat_id " , " " ) . startswith ( " local: " ) :
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
{
" followUps " : follow_ups ,
} ,
)
2025-06-03 17:52:25 +00:00
except Exception as e :
pass
2025-10-02 03:49:25 +00:00
if not metadata . get ( " chat_id " , " " ) . startswith (
" local: "
) : # Only update titles and tags for non-temp chats
2025-11-02 23:52:53 +00:00
if TASKS . TITLE_GENERATION in tasks :
2025-10-02 03:49:25 +00:00
user_message = get_last_user_message ( messages )
if user_message and len ( user_message ) > 100 :
user_message = user_message [ : 100 ] + " ... "
2025-06-16 08:37:41 +00:00
2025-11-06 18:20:03 +00:00
title = None
2025-10-02 03:49:25 +00:00
if tasks [ TASKS . TITLE_GENERATION ] :
res = await generate_title (
request ,
{
" model " : message [ " model " ] ,
" messages " : messages ,
" chat_id " : metadata [ " chat_id " ] ,
} ,
user ,
)
2024-12-13 06:28:42 +00:00
2025-10-02 03:49:25 +00:00
if res and isinstance ( res , dict ) :
if len ( res . get ( " choices " , [ ] ) ) == 1 :
2025-10-07 12:59:21 +00:00
response_message = res . get ( " choices " , [ ] ) [ 0 ] . get (
" message " , { }
)
2025-10-13 21:22:47 +00:00
title_string = (
response_message . get ( " content " )
or response_message . get (
2025-10-07 12:59:21 +00:00
" reasoning_content " ,
2025-10-13 21:22:47 +00:00
)
or message . get ( " content " , user_message )
2025-06-16 08:37:41 +00:00
)
2025-10-02 03:49:25 +00:00
else :
title_string = " "
2025-01-29 22:40:36 +00:00
2025-10-02 03:49:25 +00:00
title_string = title_string [
title_string . find ( " { " ) : title_string . rfind ( " } " ) + 1
]
2025-01-29 22:40:36 +00:00
2025-10-02 03:49:25 +00:00
try :
title = json . loads ( title_string ) . get (
" title " , user_message
)
except Exception as e :
title = " "
if not title :
title = messages [ 0 ] . get ( " content " , user_message )
Chats . update_chat_title_by_id (
metadata [ " chat_id " ] , title
2025-01-29 22:40:36 +00:00
)
2024-12-27 07:51:30 +00:00
2025-10-02 03:49:25 +00:00
await event_emitter (
{
" type " : " chat:title " ,
" data " : title ,
}
)
2025-11-06 18:20:03 +00:00
if title == None and len ( messages ) == 2 :
2025-10-02 03:49:25 +00:00
title = messages [ 0 ] . get ( " content " , user_message )
2024-12-26 06:21:44 +00:00
Chats . update_chat_title_by_id ( metadata [ " chat_id " ] , title )
await event_emitter (
{
" type " : " chat:title " ,
2025-10-02 03:49:25 +00:00
" data " : message . get ( " content " , user_message ) ,
2024-12-26 06:21:44 +00:00
}
)
2025-10-02 03:49:25 +00:00
if TASKS . TAGS_GENERATION in tasks and tasks [ TASKS . TAGS_GENERATION ] :
res = await generate_chat_tags (
request ,
2024-12-26 06:21:44 +00:00
{
2025-10-02 03:49:25 +00:00
" model " : message [ " model " ] ,
" messages " : messages ,
" chat_id " : metadata [ " chat_id " ] ,
} ,
user ,
2024-12-26 06:21:44 +00:00
)
2025-10-02 03:49:25 +00:00
if res and isinstance ( res , dict ) :
if len ( res . get ( " choices " , [ ] ) ) == 1 :
2025-10-07 12:59:21 +00:00
response_message = res . get ( " choices " , [ ] ) [ 0 ] . get (
" message " , { }
)
tags_string = response_message . get (
2025-10-13 21:22:47 +00:00
" content "
) or response_message . get ( " reasoning_content " , " " )
2025-10-02 03:49:25 +00:00
else :
tags_string = " "
2024-12-26 06:21:44 +00:00
2025-10-02 03:49:25 +00:00
tags_string = tags_string [
tags_string . find ( " { " ) : tags_string . rfind ( " } " ) + 1
]
2024-12-26 06:21:44 +00:00
2025-10-02 03:49:25 +00:00
try :
tags = json . loads ( tags_string ) . get ( " tags " , [ ] )
Chats . update_chat_tags_by_id (
metadata [ " chat_id " ] , tags , user
)
2024-12-26 06:21:44 +00:00
2025-10-02 03:49:25 +00:00
await event_emitter (
{
" type " : " chat:tags " ,
" data " : tags ,
}
)
except Exception as e :
pass
2024-12-13 06:28:42 +00:00
2024-12-19 09:00:32 +00:00
event_emitter = None
2025-02-03 04:50:54 +00:00
event_caller = None
2024-12-21 02:37:25 +00:00
if (
" session_id " in metadata
and metadata [ " session_id " ]
and " chat_id " in metadata
and metadata [ " chat_id " ]
and " message_id " in metadata
and metadata [ " message_id " ]
) :
2024-12-19 09:00:32 +00:00
event_emitter = get_event_emitter ( metadata )
2025-02-03 04:50:54 +00:00
event_caller = get_event_call ( metadata )
2024-12-13 06:28:42 +00:00
2025-02-03 04:50:54 +00:00
# Non-streaming response
2024-12-26 06:21:44 +00:00
if not isinstance ( response , StreamingResponse ) :
if event_emitter :
2025-09-05 13:28:05 +00:00
try :
if isinstance ( response , dict ) or isinstance ( response , JSONResponse ) :
if isinstance ( response , list ) and len ( response ) == 1 :
# If the response is a single-item list, unwrap it #17213
response = response [ 0 ]
2025-08-31 19:52:50 +00:00
2025-09-05 13:28:05 +00:00
if isinstance ( response , JSONResponse ) and isinstance (
response . body , bytes
) :
try :
2025-09-29 08:16:50 +00:00
response_data = json . loads (
response . body . decode ( " utf-8 " , " replace " )
)
2025-09-05 13:28:05 +00:00
except json . JSONDecodeError :
response_data = {
" error " : { " detail " : " Invalid JSON response " }
2025-09-01 10:14:20 +00:00
}
2025-09-05 13:28:05 +00:00
else :
response_data = response
2025-08-10 20:37:06 +00:00
2025-09-05 13:28:05 +00:00
if " error " in response_data :
error = response_data . get ( " error " )
2025-08-10 20:37:06 +00:00
2025-09-05 13:28:05 +00:00
if isinstance ( error , dict ) :
error = error . get ( " detail " , error )
else :
error = str ( error )
2025-08-10 20:37:06 +00:00
2025-09-05 13:28:05 +00:00
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
2025-08-10 20:37:06 +00:00
{
2025-09-05 13:28:05 +00:00
" error " : { " content " : error } ,
} ,
2025-08-10 20:37:06 +00:00
)
2025-09-05 13:28:05 +00:00
if isinstance ( error , str ) or isinstance ( error , dict ) :
await event_emitter (
{
" type " : " chat:message:error " ,
" data " : { " error " : { " content " : error } } ,
}
)
2024-12-26 06:21:44 +00:00
2025-09-05 13:28:05 +00:00
if " selected_model_id " in response_data :
2025-08-10 20:37:06 +00:00
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
{
2025-09-05 13:28:05 +00:00
" selectedModelId " : response_data [ " selected_model_id " ] ,
2025-08-10 20:37:06 +00:00
} ,
)
2024-12-26 06:21:44 +00:00
2025-09-05 13:28:05 +00:00
choices = response_data . get ( " choices " , [ ] )
if choices and choices [ 0 ] . get ( " message " , { } ) . get ( " content " ) :
content = response_data [ " choices " ] [ 0 ] [ " message " ] [ " content " ]
if content :
await event_emitter (
{
" type " : " chat:completion " ,
" data " : response_data ,
}
)
title = Chats . get_chat_title_by_id ( metadata [ " chat_id " ] )
await event_emitter (
{
" type " : " chat:completion " ,
" data " : {
" done " : True ,
" content " : content ,
2025-08-10 20:37:06 +00:00
" title " : title ,
} ,
2025-09-05 13:28:05 +00:00
}
)
2025-05-26 18:22:37 +00:00
2025-09-05 13:28:05 +00:00
# Save message in the database
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
{
" role " : " assistant " ,
" content " : content ,
} ,
)
2025-08-10 20:37:06 +00:00
2025-09-05 13:28:05 +00:00
# Send a webhook notification if the user is not active
2025-11-28 12:39:02 +00:00
if not Users . is_user_active ( user . id ) :
2025-09-05 13:28:05 +00:00
webhook_url = Users . get_user_webhook_url_by_id ( user . id )
if webhook_url :
await post_webhook (
request . app . state . WEBUI_NAME ,
webhook_url ,
f " { title } - { request . app . state . config . WEBUI_URL } /c/ { metadata [ ' chat_id ' ] } \n \n { content } " ,
{
" action " : " chat " ,
" message " : content ,
" title " : title ,
" url " : f " { request . app . state . config . WEBUI_URL } /c/ { metadata [ ' chat_id ' ] } " ,
} ,
)
2025-08-10 20:37:06 +00:00
2025-09-05 13:28:05 +00:00
await background_tasks_handler ( )
2025-08-10 20:37:06 +00:00
2025-09-05 13:28:05 +00:00
if events and isinstance ( events , list ) :
extra_response = { }
for event in events :
if isinstance ( event , dict ) :
extra_response . update ( event )
else :
extra_response [ event ] = True
response_data = {
* * extra_response ,
* * response_data ,
}
if isinstance ( response , dict ) :
response = response_data
if isinstance ( response , JSONResponse ) :
response = JSONResponse (
content = response_data ,
headers = response . headers ,
status_code = response . status_code ,
)
except Exception as e :
log . debug ( f " Error occurred while processing request: { e } " )
pass
2025-05-26 18:22:37 +00:00
2024-12-26 06:21:44 +00:00
return response
else :
2025-05-26 18:22:37 +00:00
if events and isinstance ( events , list ) and isinstance ( response , dict ) :
extra_response = { }
for event in events :
if isinstance ( event , dict ) :
extra_response . update ( event )
else :
extra_response [ event ] = True
response = {
* * extra_response ,
* * response ,
}
2024-12-26 06:21:44 +00:00
return response
2025-02-03 04:50:54 +00:00
# Non standard response
2024-12-26 06:21:44 +00:00
if not any (
content_type in response . headers [ " Content-Type " ]
for content_type in [ " text/event-stream " , " application/x-ndjson " ]
) :
return response
2025-09-08 14:50:23 +00:00
oauth_token = None
try :
2025-09-11 17:56:59 +00:00
if request . cookies . get ( " oauth_session_id " , None ) :
2025-09-19 05:10:48 +00:00
oauth_token = await request . app . state . oauth_manager . get_oauth_token (
2025-09-11 17:56:59 +00:00
user . id ,
request . cookies . get ( " oauth_session_id " , None ) ,
)
2025-09-08 14:50:23 +00:00
except Exception as e :
log . error ( f " Error getting OAuth token: { e } " )
2025-02-25 09:00:29 +00:00
extra_params = {
" __event_emitter__ " : event_emitter ,
" __event_call__ " : event_caller ,
2025-06-04 11:53:07 +00:00
" __user__ " : user . model_dump ( ) if isinstance ( user , UserModel ) else { } ,
2025-02-25 09:00:29 +00:00
" __metadata__ " : metadata ,
2025-09-08 14:50:23 +00:00
" __oauth_token__ " : oauth_token ,
2025-02-25 09:00:29 +00:00
" __request__ " : request ,
2025-03-01 14:56:24 +00:00
" __model__ " : model ,
2025-02-25 09:00:29 +00:00
}
2025-03-05 02:04:55 +00:00
filter_functions = [
Functions . get_function_by_id ( filter_id )
2025-05-16 19:33:02 +00:00
for filter_id in get_sorted_filter_ids (
request , model , metadata . get ( " filter_ids " , [ ] )
)
2025-03-05 02:04:55 +00:00
]
2025-03-01 14:56:24 +00:00
2025-02-03 04:50:54 +00:00
# Streaming response
if event_emitter and event_caller :
2024-12-19 09:00:32 +00:00
task_id = str ( uuid4 ( ) ) # Create a unique task ID.
2025-02-03 06:38:19 +00:00
model_id = form_data . get ( " model " , " " )
2024-12-13 06:28:42 +00:00
2025-02-06 05:38:35 +00:00
def split_content_and_whitespace ( content ) :
content_stripped = content . rstrip ( )
2025-02-08 06:57:39 +00:00
original_whitespace = (
content [ len ( content_stripped ) : ]
if len ( content ) > len ( content_stripped )
else " "
)
2025-02-06 05:38:35 +00:00
return content_stripped , original_whitespace
def is_opening_code_block ( content ) :
2025-02-08 06:57:39 +00:00
backtick_segments = content . split ( " ``` " )
2025-02-06 05:38:35 +00:00
# Even number of segments means the last backticks are opening a new block
return len ( backtick_segments ) > 1 and len ( backtick_segments ) % 2 == 0
2024-12-19 09:00:32 +00:00
# Handle as a background task
2025-06-25 09:36:41 +00:00
async def response_handler ( response , events ) :
2025-02-03 08:03:41 +00:00
def serialize_content_blocks ( content_blocks , raw = False ) :
2025-02-03 04:50:54 +00:00
content = " "
for block in content_blocks :
if block [ " type " ] == " text " :
2025-08-06 11:02:39 +00:00
block_content = block [ " content " ] . strip ( )
if block_content :
content = f " { content } { block_content } \n "
2025-02-05 07:05:14 +00:00
elif block [ " type " ] == " tool_calls " :
attributes = block . get ( " attributes " , { } )
2025-03-28 09:27:40 +00:00
tool_calls = block . get ( " content " , [ ] )
2025-02-05 07:05:14 +00:00
results = block . get ( " results " , [ ] )
2025-08-06 11:02:39 +00:00
if content and not content . endswith ( " \n " ) :
content + = " \n "
2025-02-05 07:05:14 +00:00
if results :
2025-02-05 07:20:09 +00:00
2025-03-28 09:27:40 +00:00
tool_calls_display_content = " "
for tool_call in tool_calls :
2025-02-05 07:20:09 +00:00
2025-03-28 19:18:27 +00:00
tool_call_id = tool_call . get ( " id " , " " )
tool_name = tool_call . get ( " function " , { } ) . get (
" name " , " "
)
tool_arguments = tool_call . get ( " function " , { } ) . get (
" arguments " , " "
)
2025-03-28 09:27:40 +00:00
tool_result = None
2025-04-03 06:46:39 +00:00
tool_result_files = None
2025-03-28 09:27:40 +00:00
for result in results :
2025-03-28 19:18:27 +00:00
if tool_call_id == result . get ( " tool_call_id " , " " ) :
tool_result = result . get ( " content " , None )
2025-04-03 06:46:39 +00:00
tool_result_files = result . get ( " files " , None )
2025-02-05 07:20:09 +00:00
break
2025-09-03 09:57:14 +00:00
if tool_result is not None :
2025-09-19 01:55:23 +00:00
tool_result_embeds = result . get ( " embeds " , " " )
2025-11-28 16:21:00 +00:00
tool_calls_display_content = f ' { tool_calls_display_content } <details type= " tool_calls " done= " true " id= " { tool_call_id } " name= " { tool_name } " arguments= " { html . escape ( dump_tool_result_to_json ( tool_arguments ) ) } " result= " { html . escape ( dump_tool_result_to_json ( tool_result , ensure_ascii = True ) ) } " files= " { html . escape ( dump_tool_result_to_json ( tool_result_files ) ) if tool_result_files else " " } " embeds= " { html . escape ( dump_tool_result_to_json ( tool_result_embeds ) ) } " > \n <summary>Tool Executed</summary> \n </details> \n '
2025-03-28 09:27:40 +00:00
else :
2025-11-28 16:21:00 +00:00
tool_calls_display_content = f ' { tool_calls_display_content } <details type= " tool_calls " done= " false " id= " { tool_call_id } " name= " { tool_name } " arguments= " { html . escape ( dump_tool_result_to_json ( tool_arguments ) ) } " > \n <summary>Executing...</summary> \n </details> \n '
2025-02-05 07:20:09 +00:00
2025-02-05 07:05:14 +00:00
if not raw :
2025-08-06 11:02:39 +00:00
content = f " { content } { tool_calls_display_content } "
2025-02-05 07:05:14 +00:00
else :
2025-02-05 09:03:16 +00:00
tool_calls_display_content = " "
2025-03-28 09:27:40 +00:00
for tool_call in tool_calls :
2025-03-28 19:23:25 +00:00
tool_call_id = tool_call . get ( " id " , " " )
2025-03-28 19:18:27 +00:00
tool_name = tool_call . get ( " function " , { } ) . get (
" name " , " "
)
tool_arguments = tool_call . get ( " function " , { } ) . get (
" arguments " , " "
)
2025-11-28 16:21:00 +00:00
tool_calls_display_content = f ' { tool_calls_display_content } \n <details type= " tool_calls " done= " false " id= " { tool_call_id } " name= " { tool_name } " arguments= " { html . escape ( dump_tool_result_to_json ( tool_arguments ) ) } " > \n <summary>Executing...</summary> \n </details> \n '
2025-02-05 09:03:16 +00:00
2025-02-05 07:05:14 +00:00
if not raw :
2025-08-06 11:02:39 +00:00
content = f " { content } { tool_calls_display_content } "
2025-02-05 05:20:03 +00:00
2025-02-03 04:50:54 +00:00
elif block [ " type " ] == " reasoning " :
2025-11-02 23:52:53 +00:00
reasoning_display_content = html . escape (
" \n " . join (
( f " > { line } " if not line . startswith ( " > " ) else line )
for line in block [ " content " ] . splitlines ( )
)
2025-02-03 04:50:54 +00:00
)
reasoning_duration = block . get ( " duration " , None )
2025-08-06 09:48:43 +00:00
start_tag = block . get ( " start_tag " , " " )
end_tag = block . get ( " end_tag " , " " )
2025-08-06 11:02:39 +00:00
if content and not content . endswith ( " \n " ) :
content + = " \n "
2025-02-05 22:26:09 +00:00
if reasoning_duration is not None :
2025-02-04 00:18:07 +00:00
if raw :
2025-08-06 11:02:39 +00:00
content = (
f ' { content } { start_tag } { block [ " content " ] } { end_tag } \n '
)
2025-02-04 00:18:07 +00:00
else :
2025-08-06 11:02:39 +00:00
content = f ' { content } <details type= " reasoning " done= " true " duration= " { reasoning_duration } " > \n <summary>Thought for { reasoning_duration } seconds</summary> \n { reasoning_display_content } \n </details> \n '
2025-02-03 04:50:54 +00:00
else :
2025-02-04 00:18:07 +00:00
if raw :
2025-08-06 11:02:39 +00:00
content = (
f ' { content } { start_tag } { block [ " content " ] } { end_tag } \n '
)
2025-02-04 00:18:07 +00:00
else :
2025-08-06 11:02:39 +00:00
content = f ' { content } <details type= " reasoning " done= " false " > \n <summary>Thinking…</summary> \n { reasoning_display_content } \n </details> \n '
2025-02-03 04:50:54 +00:00
2025-02-03 06:38:19 +00:00
elif block [ " type " ] == " code_interpreter " :
attributes = block . get ( " attributes " , { } )
2025-02-03 08:03:41 +00:00
output = block . get ( " output " , None )
2025-02-03 06:38:19 +00:00
lang = attributes . get ( " lang " , " " )
2025-02-08 06:57:39 +00:00
content_stripped , original_whitespace = (
split_content_and_whitespace ( content )
)
2025-02-06 05:38:35 +00:00
if is_opening_code_block ( content_stripped ) :
# Remove trailing backticks that would open a new block
2025-02-08 06:57:39 +00:00
content = (
content_stripped . rstrip ( " ` " ) . rstrip ( )
+ original_whitespace
)
2025-02-06 05:26:13 +00:00
else :
2025-02-06 05:38:35 +00:00
# Keep content as is - either closing backticks or no backticks
2025-02-06 05:26:13 +00:00
content = content_stripped + original_whitespace
2025-08-06 11:02:39 +00:00
if content and not content . endswith ( " \n " ) :
content + = " \n "
2025-02-03 07:35:58 +00:00
if output :
2025-02-03 08:03:41 +00:00
output = html . escape ( json . dumps ( output ) )
if raw :
2025-08-06 11:02:39 +00:00
content = f ' { content } <code_interpreter type= " code " lang= " { lang } " > \n { block [ " content " ] } \n </code_interpreter> \n ```output \n { output } \n ``` \n '
2025-02-03 08:03:41 +00:00
else :
2025-08-06 11:02:39 +00:00
content = f ' { content } <details type= " code_interpreter " done= " true " output= " { output } " > \n <summary>Analyzed</summary> \n ``` { lang } \n { block [ " content " ] } \n ``` \n </details> \n '
2025-02-03 07:35:58 +00:00
else :
2025-02-04 00:18:07 +00:00
if raw :
2025-08-06 11:02:39 +00:00
content = f ' { content } <code_interpreter type= " code " lang= " { lang } " > \n { block [ " content " ] } \n </code_interpreter> \n '
2025-02-04 00:18:07 +00:00
else :
2025-08-06 11:02:39 +00:00
content = f ' { content } <details type= " code_interpreter " done= " false " > \n <summary>Analyzing...</summary> \n ``` { lang } \n { block [ " content " ] } \n ``` \n </details> \n '
2025-02-03 07:35:58 +00:00
2025-02-03 04:50:54 +00:00
else :
2025-02-03 06:38:19 +00:00
block_content = str ( block [ " content " ] ) . strip ( )
2025-08-06 11:02:39 +00:00
if block_content :
content = f " { content } { block [ ' type ' ] } : { block_content } \n "
2025-02-03 04:50:54 +00:00
2025-02-05 22:20:51 +00:00
return content . strip ( )
2025-02-03 04:50:54 +00:00
2025-08-06 09:48:43 +00:00
def convert_content_blocks_to_messages ( content_blocks , raw = False ) :
2025-02-13 22:50:46 +00:00
messages = [ ]
temp_blocks = [ ]
for idx , block in enumerate ( content_blocks ) :
if block [ " type " ] == " tool_calls " :
messages . append (
{
" role " : " assistant " ,
2025-08-06 09:48:43 +00:00
" content " : serialize_content_blocks ( temp_blocks , raw ) ,
2025-02-13 22:50:46 +00:00
" tool_calls " : block . get ( " content " ) ,
}
)
results = block . get ( " results " , [ ] )
for result in results :
messages . append (
{
" role " : " tool " ,
" tool_call_id " : result [ " tool_call_id " ] ,
2025-09-03 09:57:14 +00:00
" content " : result . get ( " content " , " " ) or " " ,
2025-02-13 22:50:46 +00:00
}
)
temp_blocks = [ ]
else :
temp_blocks . append ( block )
if temp_blocks :
2025-08-06 09:48:43 +00:00
content = serialize_content_blocks ( temp_blocks , raw )
2025-02-13 23:17:41 +00:00
if content :
messages . append (
{
" role " : " assistant " ,
" content " : content ,
}
)
2025-02-13 22:50:46 +00:00
return messages
2025-02-03 04:50:54 +00:00
def tag_content_handler ( content_type , tags , content , content_blocks ) :
2025-02-03 08:24:09 +00:00
end_flag = False
2025-02-03 04:50:54 +00:00
def extract_attributes ( tag_content ) :
""" Extract attributes from a tag if they exist. """
attributes = { }
2025-02-05 22:10:53 +00:00
if not tag_content : # Ensure tag_content is not None
return attributes
2025-02-03 04:50:54 +00:00
# Match attributes in the format: key="value" (ignores single quotes for simplicity)
matches = re . findall ( r ' ( \ w+) \ s*= \ s* " ([^ " ]+) " ' , tag_content )
for key , value in matches :
attributes [ key ] = value
return attributes
if content_blocks [ - 1 ] [ " type " ] == " text " :
2025-02-21 14:12:34 +00:00
for start_tag , end_tag in tags :
2025-07-16 11:20:03 +00:00
2025-07-18 07:56:40 +00:00
start_tag_pattern = rf " { re . escape ( start_tag ) } "
if start_tag . startswith ( " < " ) and start_tag . endswith ( " > " ) :
2025-07-16 11:20:03 +00:00
# Match start tag e.g., <tag> or <tag attr="value">
# remove both '<' and '>' from start_tag
# Match start tag with attributes
2025-07-18 07:56:40 +00:00
start_tag_pattern = (
rf " < { re . escape ( start_tag [ 1 : - 1 ] ) } ( \ s.*?)?> "
)
2025-07-16 11:20:03 +00:00
2025-02-03 04:50:54 +00:00
match = re . search ( start_tag_pattern , content )
if match :
2025-08-14 00:04:34 +00:00
try :
attr_content = (
match . group ( 1 ) if match . group ( 1 ) else " "
) # Ensure it's not None
except :
attr_content = " "
2025-02-05 22:10:53 +00:00
attributes = extract_attributes (
attr_content
) # Extract attributes safely
2025-02-05 22:20:51 +00:00
# Capture everything before and after the matched tag
before_tag = content [
: match . start ( )
] # Content before opening tag
after_tag = content [
match . end ( ) :
] # Content after opening tag
2025-02-07 20:15:54 +00:00
# Remove the start tag and after from the currently handling text block
2025-02-03 04:50:54 +00:00
content_blocks [ - 1 ] [ " content " ] = content_blocks [ - 1 ] [
" content "
2025-02-07 20:15:54 +00:00
] . replace ( match . group ( 0 ) + after_tag , " " )
2025-02-05 10:33:40 +00:00
2025-02-05 22:20:51 +00:00
if before_tag :
content_blocks [ - 1 ] [ " content " ] = before_tag
2025-02-03 04:50:54 +00:00
if not content_blocks [ - 1 ] [ " content " ] :
content_blocks . pop ( )
2025-02-05 10:10:28 +00:00
2025-02-03 04:50:54 +00:00
# Append the new block
content_blocks . append (
{
" type " : content_type ,
2025-02-21 14:12:34 +00:00
" start_tag " : start_tag ,
" end_tag " : end_tag ,
2025-02-03 04:50:54 +00:00
" attributes " : attributes ,
" content " : " " ,
" started_at " : time . time ( ) ,
}
)
2025-02-05 22:20:51 +00:00
if after_tag :
content_blocks [ - 1 ] [ " content " ] = after_tag
2025-05-06 22:37:23 +00:00
tag_content_handler (
content_type , tags , after_tag , content_blocks
)
2025-02-05 22:20:51 +00:00
2025-02-03 04:50:54 +00:00
break
2025-05-06 22:37:23 +00:00
elif content_blocks [ - 1 ] [ " type " ] == content_type :
2025-02-21 14:12:34 +00:00
start_tag = content_blocks [ - 1 ] [ " start_tag " ]
end_tag = content_blocks [ - 1 ] [ " end_tag " ]
2025-07-16 11:20:03 +00:00
if end_tag . startswith ( " < " ) and end_tag . endswith ( " > " ) :
# Match end tag e.g., </tag>
end_tag_pattern = rf " { re . escape ( end_tag ) } "
else :
# Handle cases where end_tag is just a tag name
end_tag_pattern = rf " { re . escape ( end_tag ) } "
2025-02-05 10:33:40 +00:00
2025-02-05 22:10:53 +00:00
# Check if the content has the end tag
2025-02-03 04:50:54 +00:00
if re . search ( end_tag_pattern , content ) :
2025-02-05 10:38:05 +00:00
end_flag = True
2025-02-03 04:50:54 +00:00
block_content = content_blocks [ - 1 ] [ " content " ]
# Strip start and end tags from the content
2025-02-21 14:12:34 +00:00
start_tag_pattern = rf " < { re . escape ( start_tag ) } (.*?)> "
2025-02-03 04:50:54 +00:00
block_content = re . sub (
start_tag_pattern , " " , block_content
) . strip ( )
2025-02-05 10:33:40 +00:00
end_tag_regex = re . compile ( end_tag_pattern , re . DOTALL )
split_content = end_tag_regex . split ( block_content , maxsplit = 1 )
# Content inside the tag
block_content = (
split_content [ 0 ] . strip ( ) if split_content else " "
)
# Leftover content (everything after `</tag>`)
leftover_content = (
split_content [ 1 ] . strip ( ) if len ( split_content ) > 1 else " "
)
2025-02-03 04:50:54 +00:00
if block_content :
content_blocks [ - 1 ] [ " content " ] = block_content
content_blocks [ - 1 ] [ " ended_at " ] = time . time ( )
content_blocks [ - 1 ] [ " duration " ] = int (
content_blocks [ - 1 ] [ " ended_at " ]
- content_blocks [ - 1 ] [ " started_at " ]
)
2025-02-05 22:20:51 +00:00
2025-02-03 04:50:54 +00:00
# Reset the content_blocks by appending a new text block
2025-02-05 22:20:51 +00:00
if content_type != " code_interpreter " :
if leftover_content :
content_blocks . append (
{
" type " : " text " ,
" content " : leftover_content ,
}
)
2025-02-05 22:26:09 +00:00
else :
content_blocks . append (
{
" type " : " text " ,
" content " : " " ,
}
)
2025-02-03 04:50:54 +00:00
else :
# Remove the block if content is empty
content_blocks . pop ( )
2025-02-05 10:33:40 +00:00
if leftover_content :
content_blocks . append (
{
" type " : " text " ,
" content " : leftover_content ,
}
)
2025-02-05 22:26:09 +00:00
else :
content_blocks . append (
{
" type " : " text " ,
" content " : " " ,
}
)
2025-02-05 10:33:40 +00:00
# Clean processed content
2025-07-18 07:56:40 +00:00
start_tag_pattern = rf " { re . escape ( start_tag ) } "
if start_tag . startswith ( " < " ) and start_tag . endswith ( " > " ) :
# Match start tag e.g., <tag> or <tag attr="value">
# remove both '<' and '>' from start_tag
# Match start tag with attributes
start_tag_pattern = (
rf " < { re . escape ( start_tag [ 1 : - 1 ] ) } ( \ s.*?)?> "
)
2025-02-05 10:33:40 +00:00
content = re . sub (
2025-07-18 07:56:40 +00:00
rf " { start_tag_pattern } (.| \ n)*? { re . escape ( end_tag ) } " ,
2025-02-05 10:33:40 +00:00
" " ,
content ,
flags = re . DOTALL ,
)
2025-02-03 08:24:09 +00:00
return content , content_blocks , end_flag
2025-02-03 04:50:54 +00:00
2024-12-29 03:31:03 +00:00
message = Chats . get_message_by_id_and_message_id (
metadata [ " chat_id " ] , metadata [ " message_id " ]
)
2025-02-03 04:50:54 +00:00
2025-02-05 07:05:14 +00:00
tool_calls = [ ]
2025-02-18 02:40:40 +00:00
2025-02-18 17:57:12 +00:00
last_assistant_message = None
try :
if form_data [ " messages " ] [ - 1 ] [ " role " ] == " assistant " :
last_assistant_message = get_last_assistant_message (
form_data [ " messages " ]
)
except Exception as e :
pass
2025-02-18 02:40:40 +00:00
content = (
message . get ( " content " , " " )
if message
else last_assistant_message if last_assistant_message else " "
)
2025-02-03 04:50:54 +00:00
content_blocks = [
{
" type " : " text " ,
" content " : content ,
}
]
2025-08-27 13:24:16 +00:00
reasoning_tags_param = metadata . get ( " params " , { } ) . get ( " reasoning_tags " )
DETECT_REASONING_TAGS = reasoning_tags_param is not False
2025-02-05 02:33:22 +00:00
DETECT_CODE_INTERPRETER = metadata . get ( " features " , { } ) . get (
" code_interpreter " , False
)
2025-02-03 04:50:54 +00:00
2025-08-27 13:24:16 +00:00
reasoning_tags = [ ]
if DETECT_REASONING_TAGS :
if (
isinstance ( reasoning_tags_param , list )
and len ( reasoning_tags_param ) == 2
) :
reasoning_tags = [
( reasoning_tags_param [ 0 ] , reasoning_tags_param [ 1 ] )
]
else :
reasoning_tags = DEFAULT_REASONING_TAGS
2024-12-28 06:46:50 +00:00
2024-12-19 09:00:32 +00:00
try :
for event in events :
await event_emitter (
{
2024-12-19 09:05:47 +00:00
" type " : " chat:completion " ,
2024-12-19 09:00:32 +00:00
" data " : event ,
}
)
2024-12-21 16:45:52 +00:00
# Save message in the database
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
{
* * event ,
} ,
)
2025-06-23 08:54:50 +00:00
async def stream_body_handler ( response , form_data ) :
2025-02-03 06:38:19 +00:00
nonlocal content
nonlocal content_blocks
2024-12-19 09:00:32 +00:00
2025-02-05 07:05:14 +00:00
response_tool_calls = [ ]
2025-08-09 19:43:27 +00:00
delta_count = 0
delta_chunk_size = max (
2025-08-09 19:49:56 +00:00
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE ,
2025-08-09 19:43:27 +00:00
int (
metadata . get ( " params " , { } ) . get ( " stream_delta_chunk_size " )
or 1
) ,
)
2025-08-16 03:43:32 +00:00
last_delta_data = None
async def flush_pending_delta_data ( threshold : int = 0 ) :
nonlocal delta_count
nonlocal last_delta_data
if delta_count > = threshold and last_delta_data :
await event_emitter (
{
" type " : " chat:completion " ,
" data " : last_delta_data ,
}
)
delta_count = 0
last_delta_data = None
2025-08-09 19:43:27 +00:00
2025-02-03 06:38:19 +00:00
async for line in response . body_iterator :
2025-09-29 08:16:50 +00:00
line = (
line . decode ( " utf-8 " , " replace " )
if isinstance ( line , bytes )
else line
)
2025-02-03 06:38:19 +00:00
data = line
2024-12-19 09:00:32 +00:00
2025-02-03 06:38:19 +00:00
# Skip empty lines
if not data . strip ( ) :
continue
2024-12-19 09:00:32 +00:00
2025-02-03 06:38:19 +00:00
# "data:" is the prefix for each event
if not data . startswith ( " data: " ) :
continue
2024-12-19 09:00:32 +00:00
2025-02-03 06:38:19 +00:00
# Remove the prefix
data = data [ len ( " data: " ) : ] . strip ( )
2024-12-19 09:00:32 +00:00
2025-02-03 06:38:19 +00:00
try :
data = json . loads ( data )
2024-12-25 02:34:56 +00:00
2025-02-25 09:00:29 +00:00
data , _ = await process_filter_functions (
request = request ,
2025-03-05 02:04:55 +00:00
filter_functions = filter_functions ,
2025-02-25 09:00:29 +00:00
filter_type = " stream " ,
form_data = data ,
2025-06-23 08:54:50 +00:00
extra_params = { " __body__ " : form_data , * * extra_params } ,
2025-02-25 09:00:29 +00:00
)
2025-02-05 10:33:40 +00:00
2025-02-25 09:03:15 +00:00
if data :
2025-10-19 22:30:36 +00:00
if " event " in data and not getattr (
request . state , " direct " , False
) :
2025-04-10 16:20:18 +00:00
await event_emitter ( data . get ( " event " , { } ) )
2025-02-25 09:03:15 +00:00
if " selected_model_id " in data :
model_id = data [ " selected_model_id " ]
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
{
" selectedModelId " : model_id ,
} ,
2025-02-03 04:50:54 +00:00
)
2025-08-17 00:55:45 +00:00
await event_emitter (
{
" type " : " chat:completion " ,
" data " : data ,
}
)
2025-02-25 09:03:15 +00:00
else :
choices = data . get ( " choices " , [ ] )
2025-09-16 15:28:25 +00:00
# 17421
2025-09-17 14:19:56 +00:00
usage = data . get ( " usage " , { } ) or { }
2025-09-16 15:28:25 +00:00
usage . update ( data . get ( " timings " , { } ) ) # llama.cpp
if usage :
await event_emitter (
{
" type " : " chat:completion " ,
" data " : {
" usage " : usage ,
} ,
}
)
2025-02-25 09:03:15 +00:00
if not choices :
2025-03-28 07:25:00 +00:00
error = data . get ( " error " , { } )
if error :
await event_emitter (
{
" type " : " chat:completion " ,
" data " : {
" error " : error ,
} ,
}
)
2025-02-25 09:03:15 +00:00
continue
2025-01-22 08:13:24 +00:00
2025-02-25 09:03:15 +00:00
delta = choices [ 0 ] . get ( " delta " , { } )
delta_tool_calls = delta . get ( " tool_calls " , None )
if delta_tool_calls :
for delta_tool_call in delta_tool_calls :
tool_call_index = delta_tool_call . get (
" index "
2025-02-03 08:24:09 +00:00
)
2025-02-03 06:38:19 +00:00
2025-02-25 09:03:15 +00:00
if tool_call_index is not None :
2025-04-11 03:01:07 +00:00
# Check if the tool call already exists
current_response_tool_call = None
for (
response_tool_call
) in response_tool_calls :
if (
response_tool_call . get ( " index " )
== tool_call_index
) :
current_response_tool_call = (
response_tool_call
)
break
if current_response_tool_call is None :
# Add the new tool call
2025-04-20 15:25:08 +00:00
delta_tool_call . setdefault (
" function " , { }
)
2025-04-23 07:05:15 +00:00
delta_tool_call [
" function "
] . setdefault ( " name " , " " )
delta_tool_call [
" function "
] . setdefault ( " arguments " , " " )
2025-02-25 09:03:15 +00:00
response_tool_calls . append (
delta_tool_call
)
else :
2025-04-11 03:01:07 +00:00
# Update the existing tool call
2025-02-25 09:03:15 +00:00
delta_name = delta_tool_call . get (
" function " , { }
) . get ( " name " )
delta_arguments = (
delta_tool_call . get (
" function " , { }
) . get ( " arguments " )
)
if delta_name :
2025-04-11 03:01:07 +00:00
current_response_tool_call [
" function "
] [ " name " ] + = delta_name
2025-02-25 09:03:15 +00:00
if delta_arguments :
2025-04-11 03:01:07 +00:00
current_response_tool_call [
" function "
] [
2025-02-25 09:03:15 +00:00
" arguments "
] + = delta_arguments
2025-11-19 07:16:09 +00:00
image_urls = get_image_urls (
2025-11-19 07:07:56 +00:00
delta . get ( " images " , [ ] ) , request , metadata , user
)
2025-11-19 07:16:09 +00:00
if image_urls :
message_files = Chats . add_message_files_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
[
{ " type " : " image " , " url " : url }
for url in image_urls
] ,
)
2025-11-19 07:07:56 +00:00
await event_emitter (
{
" type " : " files " ,
2025-11-19 07:16:09 +00:00
" data " : { " files " : message_files } ,
2025-11-19 07:07:56 +00:00
}
)
2025-02-25 09:03:15 +00:00
value = delta . get ( " content " )
2025-03-04 04:34:17 +00:00
2025-06-10 09:10:31 +00:00
reasoning_content = (
delta . get ( " reasoning_content " )
or delta . get ( " reasoning " )
or delta . get ( " thinking " )
)
2025-03-03 08:47:35 +00:00
if reasoning_content :
2025-03-04 04:34:17 +00:00
if (
not content_blocks
or content_blocks [ - 1 ] [ " type " ] != " reasoning "
) :
2025-03-03 08:47:35 +00:00
reasoning_block = {
" type " : " reasoning " ,
2025-08-06 09:48:43 +00:00
" start_tag " : " <think> " ,
" end_tag " : " </think> " ,
2025-03-04 04:34:17 +00:00
" attributes " : {
" type " : " reasoning_content "
} ,
2025-03-03 08:47:35 +00:00
" content " : " " ,
2025-03-04 04:34:17 +00:00
" started_at " : time . time ( ) ,
2025-03-03 08:47:35 +00:00
}
content_blocks . append ( reasoning_block )
else :
reasoning_block = content_blocks [ - 1 ]
2025-03-04 04:34:17 +00:00
2025-03-03 08:47:35 +00:00
reasoning_block [ " content " ] + = reasoning_content
2025-03-04 04:34:17 +00:00
2025-03-03 08:47:35 +00:00
data = {
2025-03-04 04:34:17 +00:00
" content " : serialize_content_blocks (
content_blocks
)
2025-03-03 08:47:35 +00:00
}
2025-03-04 04:34:17 +00:00
2025-02-25 09:03:15 +00:00
if value :
2025-03-04 04:34:17 +00:00
if (
content_blocks
and content_blocks [ - 1 ] [ " type " ]
== " reasoning "
and content_blocks [ - 1 ]
. get ( " attributes " , { } )
. get ( " type " )
== " reasoning_content "
) :
2025-03-03 08:47:35 +00:00
reasoning_block = content_blocks [ - 1 ]
reasoning_block [ " ended_at " ] = time . time ( )
2025-03-04 04:34:17 +00:00
reasoning_block [ " duration " ] = int (
reasoning_block [ " ended_at " ]
- reasoning_block [ " started_at " ]
)
2025-02-25 09:03:15 +00:00
2025-03-03 08:47:35 +00:00
content_blocks . append (
{
" type " : " text " ,
" content " : " " ,
}
)
2025-03-04 04:34:17 +00:00
2025-11-20 09:00:02 +00:00
if ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION :
value = convert_markdown_base64_images (
request , value , metadata , user
)
2025-03-03 08:47:35 +00:00
content = f " { content } { value } "
2025-02-25 09:03:15 +00:00
if not content_blocks :
content_blocks . append (
{
" type " : " text " ,
" content " : " " ,
}
2025-02-03 08:24:09 +00:00
)
2025-02-25 09:03:15 +00:00
content_blocks [ - 1 ] [ " content " ] = (
content_blocks [ - 1 ] [ " content " ] + value
2025-02-03 06:38:19 +00:00
)
2025-08-27 13:24:16 +00:00
if DETECT_REASONING_TAGS :
2025-02-25 09:03:15 +00:00
content , content_blocks , _ = (
tag_content_handler (
" reasoning " ,
reasoning_tags ,
content ,
content_blocks ,
)
)
2025-02-03 08:24:09 +00:00
2025-08-27 13:24:16 +00:00
content , content_blocks , _ = (
2025-02-25 09:03:15 +00:00
tag_content_handler (
2025-08-27 13:24:16 +00:00
" solution " ,
DEFAULT_SOLUTION_TAGS ,
2025-02-25 09:03:15 +00:00
content ,
content_blocks ,
)
2025-02-21 14:12:34 +00:00
)
2025-02-03 08:24:09 +00:00
2025-08-27 13:24:16 +00:00
if DETECT_CODE_INTERPRETER :
content , content_blocks , end = (
2025-02-25 09:03:15 +00:00
tag_content_handler (
2025-08-27 13:24:16 +00:00
" code_interpreter " ,
DEFAULT_CODE_INTERPRETER_TAGS ,
2025-02-25 09:03:15 +00:00
content ,
content_blocks ,
)
)
2025-08-27 13:24:16 +00:00
if end :
break
2025-02-25 09:03:15 +00:00
if ENABLE_REALTIME_CHAT_SAVE :
# Save message in the database
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
{
" content " : serialize_content_blocks (
content_blocks
) ,
} ,
)
else :
data = {
2025-02-03 06:38:19 +00:00
" content " : serialize_content_blocks (
content_blocks
) ,
2025-02-25 09:03:15 +00:00
}
2025-02-03 06:38:19 +00:00
2025-08-09 19:43:27 +00:00
if delta :
delta_count + = 1
2025-08-16 03:43:32 +00:00
last_delta_data = data
2025-08-09 19:43:27 +00:00
if delta_count > = delta_chunk_size :
2025-08-16 03:43:32 +00:00
await flush_pending_delta_data ( delta_chunk_size )
2025-08-09 19:43:27 +00:00
else :
await event_emitter (
{
" type " : " chat:completion " ,
" data " : data ,
}
)
2025-02-03 06:38:19 +00:00
except Exception as e :
done = " data: [DONE] " in line
if done :
pass
else :
2025-07-19 08:17:35 +00:00
log . debug ( f " Error: { e } " )
2025-02-03 06:38:19 +00:00
continue
2025-08-16 03:43:32 +00:00
await flush_pending_delta_data ( )
2025-02-03 06:38:19 +00:00
2025-02-05 10:10:28 +00:00
if content_blocks :
# Clean up the last text block
if content_blocks [ - 1 ] [ " type " ] == " text " :
content_blocks [ - 1 ] [ " content " ] = content_blocks [ - 1 ] [
" content "
] . strip ( )
if not content_blocks [ - 1 ] [ " content " ] :
content_blocks . pop ( )
2025-02-03 08:24:09 +00:00
2025-02-05 10:10:28 +00:00
if not content_blocks :
content_blocks . append (
{
" type " : " text " ,
" content " : " " ,
}
)
2025-02-03 08:24:09 +00:00
2025-08-06 11:06:43 +00:00
if content_blocks [ - 1 ] [ " type " ] == " reasoning " :
2025-08-06 09:32:28 +00:00
reasoning_block = content_blocks [ - 1 ]
if reasoning_block . get ( " ended_at " ) is None :
reasoning_block [ " ended_at " ] = time . time ( )
reasoning_block [ " duration " ] = int (
reasoning_block [ " ended_at " ]
- reasoning_block [ " started_at " ]
)
2025-02-05 07:05:14 +00:00
if response_tool_calls :
tool_calls . append ( response_tool_calls )
if response . background :
await response . background ( )
2025-06-23 08:54:50 +00:00
await stream_body_handler ( response , form_data )
2025-02-05 07:05:14 +00:00
tool_call_retries = 0
2025-08-26 22:58:25 +00:00
while (
len ( tool_calls ) > 0
and tool_call_retries < CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES
) :
2025-08-06 09:32:28 +00:00
2025-02-05 07:05:14 +00:00
tool_call_retries + = 1
response_tool_calls = tool_calls . pop ( 0 )
content_blocks . append (
{
" type " : " tool_calls " ,
" content " : response_tool_calls ,
}
)
2025-02-04 00:21:44 +00:00
await event_emitter (
{
" type " : " chat:completion " ,
" data " : {
" content " : serialize_content_blocks ( content_blocks ) ,
} ,
}
)
2025-02-05 07:05:14 +00:00
tools = metadata . get ( " tools " , { } )
2025-02-03 06:38:19 +00:00
2025-02-05 07:05:14 +00:00
results = [ ]
2025-06-11 07:16:27 +00:00
2025-02-05 07:05:14 +00:00
for tool_call in response_tool_calls :
tool_call_id = tool_call . get ( " id " , " " )
2025-09-28 19:46:01 +00:00
tool_function_name = tool_call . get ( " function " , { } ) . get (
" name " , " "
)
2025-06-16 11:29:40 +00:00
tool_args = tool_call . get ( " function " , { } ) . get ( " arguments " , " {} " )
2025-02-05 07:05:14 +00:00
tool_function_params = { }
try :
2025-02-05 08:01:24 +00:00
# json.loads cannot be used because some models do not produce valid JSON
2025-06-16 11:29:40 +00:00
tool_function_params = ast . literal_eval ( tool_args )
2025-02-05 07:05:14 +00:00
except Exception as e :
log . debug ( e )
2025-03-30 09:39:23 +00:00
# Fallback to JSON parsing
try :
2025-06-16 11:29:40 +00:00
tool_function_params = json . loads ( tool_args )
2025-03-30 09:39:23 +00:00
except Exception as e :
2025-06-11 07:16:27 +00:00
log . error (
2025-06-16 11:29:40 +00:00
f " Error parsing tool call arguments: { tool_args } "
2025-03-30 09:39:23 +00:00
)
2025-02-05 07:05:14 +00:00
2025-06-11 07:16:27 +00:00
# Mutate the original tool call response params as they are passed back to the passed
2025-06-16 11:29:40 +00:00
# back to the LLM via the content blocks. If they are in a json block and are invalid json,
2025-06-11 07:16:27 +00:00
# this can cause downstream LLM integrations to fail (e.g. bedrock gateway) where response
# params are not valid json.
# Main case so far is no args = "" = invalid json.
2025-06-16 11:29:40 +00:00
log . debug (
f " Parsed args from { tool_args } to { tool_function_params } "
)
tool_call . setdefault ( " function " , { } ) [ " arguments " ] = json . dumps (
tool_function_params
)
2025-06-11 07:16:27 +00:00
2025-02-05 07:05:14 +00:00
tool_result = None
2025-09-28 19:46:01 +00:00
tool = None
tool_type = None
direct_tool = False
2025-02-05 07:05:14 +00:00
2025-09-28 19:46:01 +00:00
if tool_function_name in tools :
tool = tools [ tool_function_name ]
2025-02-05 07:05:14 +00:00
spec = tool . get ( " spec " , { } )
2025-09-28 19:46:01 +00:00
tool_type = tool . get ( " type " , " " )
direct_tool = tool . get ( " direct " , False )
2025-02-05 07:05:14 +00:00
try :
2025-03-07 20:37:22 +00:00
allowed_params = (
spec . get ( " parameters " , { } )
. get ( " properties " , { } )
. keys ( )
2025-02-05 07:05:14 +00:00
)
2025-03-27 09:50:53 +00:00
2025-02-05 07:05:14 +00:00
tool_function_params = {
k : v
for k , v in tool_function_params . items ( )
2025-03-07 20:07:36 +00:00
if k in allowed_params
2025-02-05 07:05:14 +00:00
}
2025-03-26 07:40:24 +00:00
2025-09-28 19:46:01 +00:00
if direct_tool :
2025-03-26 07:40:24 +00:00
tool_result = await event_caller (
{
" type " : " execute:tool " ,
" data " : {
" id " : str ( uuid4 ( ) ) ,
2025-09-28 19:46:01 +00:00
" name " : tool_function_name ,
2025-03-26 07:40:24 +00:00
" params " : tool_function_params ,
2025-03-27 08:38:35 +00:00
" server " : tool . get ( " server " , { } ) ,
2025-03-26 07:40:24 +00:00
" session_id " : metadata . get (
" session_id " , None
) ,
} ,
}
)
2025-03-27 09:50:53 +00:00
else :
2025-11-06 21:35:19 +00:00
tool_function = get_updated_tool_function (
2025-11-06 04:46:30 +00:00
function = tool [ " callable " ] ,
extra_params = {
" __messages__ " : form_data . get (
" messages " , [ ]
) ,
" __files__ " : metadata . get ( " files " , [ ] ) ,
} ,
)
2025-03-27 09:50:53 +00:00
tool_result = await tool_function (
* * tool_function_params
)
2025-02-05 07:05:14 +00:00
except Exception as e :
tool_result = str ( e )
2025-09-28 19:46:01 +00:00
tool_result , tool_result_files , tool_result_embeds = (
process_tool_result (
request ,
tool_function_name ,
tool_result ,
tool_type ,
direct_tool ,
metadata ,
user ,
2025-07-11 08:00:21 +00:00
)
2025-09-28 19:46:01 +00:00
)
2025-03-28 07:07:00 +00:00
2025-02-05 07:05:14 +00:00
results . append (
{
" tool_call_id " : tool_call_id ,
2025-09-03 09:57:14 +00:00
" content " : tool_result or " " ,
2025-04-03 06:46:39 +00:00
* * (
{ " files " : tool_result_files }
if tool_result_files
else { }
) ,
2025-09-19 01:55:23 +00:00
* * (
{ " embeds " : tool_result_embeds }
if tool_result_embeds
else { }
) ,
2025-02-05 07:05:14 +00:00
}
)
content_blocks [ - 1 ] [ " results " ] = results
content_blocks . append (
{
" type " : " text " ,
" content " : " " ,
}
)
await event_emitter (
{
" type " : " chat:completion " ,
" data " : {
" content " : serialize_content_blocks ( content_blocks ) ,
} ,
}
)
try :
2025-06-23 08:54:50 +00:00
new_form_data = {
2025-10-11 19:54:07 +00:00
* * form_data ,
2025-06-23 08:54:50 +00:00
" model " : model_id ,
" stream " : True ,
" messages " : [
* form_data [ " messages " ] ,
2025-08-06 09:48:43 +00:00
* convert_content_blocks_to_messages (
content_blocks , True
) ,
2025-06-23 08:54:50 +00:00
] ,
}
2025-02-05 07:05:14 +00:00
res = await generate_chat_completion (
request ,
2025-06-23 08:54:50 +00:00
new_form_data ,
2025-02-05 07:05:14 +00:00
user ,
)
if isinstance ( res , StreamingResponse ) :
2025-06-23 08:54:50 +00:00
await stream_body_handler ( res , new_form_data )
2025-02-05 07:05:14 +00:00
else :
break
except Exception as e :
log . debug ( e )
break
2025-02-03 06:38:19 +00:00
2025-02-05 02:33:22 +00:00
if DETECT_CODE_INTERPRETER :
MAX_RETRIES = 5
retries = 0
2025-02-03 06:38:19 +00:00
2025-02-05 02:33:22 +00:00
while (
content_blocks [ - 1 ] [ " type " ] == " code_interpreter "
and retries < MAX_RETRIES
) :
2025-07-07 19:51:01 +00:00
2025-02-10 21:53:16 +00:00
await event_emitter (
{
" type " : " chat:completion " ,
" data " : {
" content " : serialize_content_blocks ( content_blocks ) ,
} ,
}
)
2025-02-05 02:33:22 +00:00
retries + = 1
log . debug ( f " Attempt count: { retries } " )
2025-02-03 06:38:19 +00:00
2025-02-05 02:33:22 +00:00
output = " "
try :
if content_blocks [ - 1 ] [ " attributes " ] . get ( " type " ) == " code " :
2025-02-10 10:25:02 +00:00
code = content_blocks [ - 1 ] [ " content " ]
2025-08-17 00:15:13 +00:00
if CODE_INTERPRETER_BLOCKED_MODULES :
blocking_code = textwrap . dedent (
f """
2025-06-05 15:21:37 +00:00
import builtins
2025-08-17 00:15:13 +00:00
BLOCKED_MODULES = { CODE_INTERPRETER_BLOCKED_MODULES }
2025-06-05 15:21:37 +00:00
_real_import = builtins . __import__
def restricted_import ( name , globals = None , locals = None , fromlist = ( ) , level = 0 ) :
2025-08-17 00:15:13 +00:00
if name . split ( ' . ' ) [ 0 ] in BLOCKED_MODULES :
2025-06-05 15:21:37 +00:00
importer_name = globals . get ( ' __name__ ' ) if globals else None
if importer_name == ' __main__ ' :
raise ImportError (
f " Direct import of module {{ name }} is restricted. "
)
return _real_import ( name , globals , locals , fromlist , level )
builtins . __import__ = restricted_import
2025-08-17 00:15:13 +00:00
"""
)
2025-06-05 15:21:37 +00:00
code = blocking_code + " \n " + code
2025-02-10 10:25:02 +00:00
if (
request . app . state . config . CODE_INTERPRETER_ENGINE
== " pyodide "
) :
output = await event_caller (
{
" type " : " execute:python " ,
" data " : {
" id " : str ( uuid4 ( ) ) ,
" code " : code ,
2025-02-13 06:56:33 +00:00
" session_id " : metadata . get (
" session_id " , None
) ,
2025-02-10 10:25:02 +00:00
} ,
}
)
elif (
request . app . state . config . CODE_INTERPRETER_ENGINE
== " jupyter "
) :
output = await execute_code_jupyter (
request . app . state . config . CODE_INTERPRETER_JUPYTER_URL ,
code ,
(
request . app . state . config . CODE_INTERPRETER_JUPYTER_AUTH_TOKEN
if request . app . state . config . CODE_INTERPRETER_JUPYTER_AUTH
== " token "
else None
) ,
(
request . app . state . config . CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
if request . app . state . config . CODE_INTERPRETER_JUPYTER_AUTH
== " password "
else None
) ,
2025-02-20 01:05:37 +00:00
request . app . state . config . CODE_INTERPRETER_JUPYTER_TIMEOUT ,
2025-02-10 10:25:02 +00:00
)
else :
output = {
" stdout " : " Code interpreter engine not configured. "
2025-02-05 02:33:22 +00:00
}
2025-02-03 06:38:19 +00:00
2025-02-13 08:40:04 +00:00
log . debug ( f " Code interpreter output: { output } " )
2025-02-05 02:33:22 +00:00
if isinstance ( output , dict ) :
stdout = output . get ( " stdout " , " " )
2025-02-13 08:40:04 +00:00
if isinstance ( stdout , str ) :
2025-02-05 02:33:22 +00:00
stdoutLines = stdout . split ( " \n " )
for idx , line in enumerate ( stdoutLines ) :
2025-09-23 06:40:59 +00:00
2025-02-05 02:33:22 +00:00
if " data:image/png;base64 " in line :
2025-09-23 06:40:59 +00:00
image_url = get_image_url_from_base64 (
request ,
line ,
metadata ,
user ,
2025-06-08 16:58:31 +00:00
)
2025-09-23 06:40:59 +00:00
if image_url :
stdoutLines [ idx ] = (
f "  "
2025-02-05 02:33:22 +00:00
)
output [ " stdout " ] = " \n " . join ( stdoutLines )
2025-02-10 21:12:05 +00:00
result = output . get ( " result " , " " )
2025-02-13 08:40:04 +00:00
if isinstance ( result , str ) :
2025-02-10 21:12:05 +00:00
resultLines = result . split ( " \n " )
for idx , line in enumerate ( resultLines ) :
if " data:image/png;base64 " in line :
2025-09-23 06:40:59 +00:00
image_url = get_image_url_from_base64 (
request ,
line ,
metadata ,
user ,
2025-02-10 21:12:05 +00:00
)
resultLines [ idx ] = (
2025-06-08 08:26:58 +00:00
f "  "
2025-02-10 21:12:05 +00:00
)
output [ " result " ] = " \n " . join ( resultLines )
2025-02-05 02:33:22 +00:00
except Exception as e :
output = str ( e )
2025-02-03 07:35:58 +00:00
2025-02-05 02:33:22 +00:00
content_blocks [ - 1 ] [ " output " ] = output
2025-02-05 07:05:14 +00:00
2025-02-05 02:33:22 +00:00
content_blocks . append (
{
" type " : " text " ,
" content " : " " ,
}
)
2025-02-03 06:38:19 +00:00
2025-02-05 02:33:22 +00:00
await event_emitter (
2025-02-03 06:38:19 +00:00
{
2025-02-05 02:33:22 +00:00
" type " : " chat:completion " ,
" data " : {
" content " : serialize_content_blocks ( content_blocks ) ,
} ,
}
2024-12-30 23:39:35 +00:00
)
2025-02-03 06:38:19 +00:00
2025-02-05 02:33:22 +00:00
try :
2025-07-07 19:51:01 +00:00
new_form_data = {
2025-10-11 19:54:07 +00:00
* * form_data ,
2025-07-07 19:51:01 +00:00
" model " : model_id ,
" stream " : True ,
" messages " : [
* form_data [ " messages " ] ,
{
" role " : " assistant " ,
" content " : serialize_content_blocks (
content_blocks , raw = True
) ,
} ,
] ,
}
2025-02-05 02:33:22 +00:00
res = await generate_chat_completion (
request ,
2025-07-07 19:51:01 +00:00
new_form_data ,
2025-02-05 02:33:22 +00:00
user ,
)
if isinstance ( res , StreamingResponse ) :
2025-07-07 19:51:01 +00:00
await stream_body_handler ( res , new_form_data )
2025-02-05 02:33:22 +00:00
else :
break
except Exception as e :
log . debug ( e )
2025-02-03 06:38:19 +00:00
break
2024-12-30 23:39:35 +00:00
title = Chats . get_chat_title_by_id ( metadata [ " chat_id " ] )
2025-02-03 04:50:54 +00:00
data = {
" done " : True ,
" content " : serialize_content_blocks ( content_blocks ) ,
" title " : title ,
}
2024-12-30 23:39:35 +00:00
if not ENABLE_REALTIME_CHAT_SAVE :
# Save message in the database
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
2024-12-19 09:00:32 +00:00
{
2025-02-03 04:50:54 +00:00
" content " : serialize_content_blocks ( content_blocks ) ,
2024-12-30 23:39:35 +00:00
} ,
2024-12-19 09:00:32 +00:00
)
2024-12-30 23:39:35 +00:00
# Send a webhook notification if the user is not active
2025-11-28 12:39:02 +00:00
if not Users . is_user_active ( user . id ) :
2024-12-30 23:39:35 +00:00
webhook_url = Users . get_user_webhook_url_by_id ( user . id )
if webhook_url :
2025-08-14 20:07:02 +00:00
await post_webhook (
2025-02-16 08:11:18 +00:00
request . app . state . WEBUI_NAME ,
2024-12-30 23:39:35 +00:00
webhook_url ,
f " { title } - { request . app . state . config . WEBUI_URL } /c/ { metadata [ ' chat_id ' ] } \n \n { content } " ,
{
" action " : " chat " ,
" message " : content ,
" title " : title ,
" url " : f " { request . app . state . config . WEBUI_URL } /c/ { metadata [ ' chat_id ' ] } " ,
} ,
)
await event_emitter (
{
" type " : " chat:completion " ,
" data " : data ,
}
)
2024-12-26 06:21:44 +00:00
await background_tasks_handler ( )
2024-12-19 09:00:32 +00:00
except asyncio . CancelledError :
2025-02-25 14:36:25 +00:00
log . warning ( " Task was cancelled! " )
2025-09-01 10:14:20 +00:00
await event_emitter ( { " type " : " chat:tasks:cancel " } )
2024-12-19 09:00:32 +00:00
2024-12-28 06:46:50 +00:00
if not ENABLE_REALTIME_CHAT_SAVE :
# Save message in the database
Chats . upsert_message_to_chat_by_id_and_message_id (
metadata [ " chat_id " ] ,
metadata [ " message_id " ] ,
{
2025-02-03 08:24:09 +00:00
" content " : serialize_content_blocks ( content_blocks ) ,
2024-12-28 06:46:50 +00:00
} ,
)
2024-12-19 09:00:32 +00:00
if response . background is not None :
await response . background ( )
2025-08-18 21:24:53 +00:00
return await response_handler ( response , events )
2024-12-19 09:00:32 +00:00
else :
# Fallback to the original response
async def stream_wrapper ( original_generator , events ) :
def wrap_item ( item ) :
return f " data: { item } \n \n "
for event in events :
2025-02-25 09:00:29 +00:00
event , _ = await process_filter_functions (
request = request ,
2025-03-05 02:04:55 +00:00
filter_functions = filter_functions ,
2025-02-25 09:00:29 +00:00
filter_type = " stream " ,
form_data = event ,
extra_params = extra_params ,
)
2025-02-25 09:03:15 +00:00
if event :
yield wrap_item ( json . dumps ( event ) )
2024-12-19 09:00:32 +00:00
async for data in original_generator :
2025-02-25 09:00:29 +00:00
data , _ = await process_filter_functions (
request = request ,
2025-03-05 02:04:55 +00:00
filter_functions = filter_functions ,
2025-02-25 09:00:29 +00:00
filter_type = " stream " ,
form_data = data ,
extra_params = extra_params ,
)
2025-02-25 09:03:15 +00:00
if data :
yield data
2024-12-19 09:00:32 +00:00
return StreamingResponse (
stream_wrapper ( response . body_iterator , events ) ,
headers = dict ( response . headers ) ,
2024-12-27 04:38:27 +00:00
background = response . background ,
2024-12-19 09:00:32 +00:00
)