2026-08-07 23:03:12 +03:00
import ai
2026-05-23 18:09:50 +03:00
from statistics import *
2026-05-22 19:34:48 +03:00
from colors import *
2026-08-07 23:03:12 +03:00
from helpers import *
2026-05-22 19:34:48 +03:00
from config import *
2026-08-07 23:03:12 +03:00
from vars import *
2026-09-16 16:52:32 +03:00
from prompts import TOOLS_PROMPT
2026-05-22 19:34:48 +03:00
2026-09-15 21:34:10 +03:00
from aiogram.exceptions import TelegramBadRequest
2026-05-22 19:34:48 +03:00
async def generate (
2026-09-16 01:06:13 +03:00
prompt : str , bot = None , user_id = None , chat_id = None , message_id = None , images = None , msg = None , process = None , message = None , inline_id = None , system_prompt = None
2026-05-22 19:34:48 +03:00
) -> str :
2026-09-16 16:52:32 +03:00
prompt = apply_time_tags ( prompt )
2026-05-22 19:34:48 +03:00
if user_id :
if user_id not in user_contexts :
user_contexts [ user_id ] = []
config = get_user_config ( user_id )
2026-09-15 21:34:10 +03:00
model_str = config . get ( "model" , "" )
if "*" not in model_str :
print ( f " { RED } -- err model - failed to get prov&model in \" { model_str } \" { RESET } " )
2026-05-22 20:04:26 +03:00
return "произошла ошибка при парсинге вашей выбранной модели. либо хост бота гандон либо тебе надо сменить модель в настройках!!"
2026-07-09 12:34:37 +03:00
2026-09-15 21:34:10 +03:00
is_auto = model_str == "all*auto" and AUTO_MODEL and AUTO_MODELS
model = model_str . split ( "*" )
2026-08-03 09:37:10 +03:00
stream_cf = config . get ( "stream" , "none" )
system_prompt_name = config . get ( "prompt" , "" )
user_prompts = config . get ( "prompts" , {})
prompts = SYSTEM_PROMPTS | user_prompts
2026-09-15 21:34:10 +03:00
2026-08-03 09:37:10 +03:00
if system_prompt_name in prompts :
is_user_prompt = False
if system_prompt_name in SYSTEM_PROMPTS :
system_prompt = prompts [ system_prompt_name ][ "text" ] + ADD_TO_PROMPT
if config [ "markdown" ] == "new" :
system_prompt += NEW_MD
else :
system_prompt += OLD_MD
2026-07-14 08:23:17 +03:00
else :
2026-08-03 09:37:10 +03:00
is_user_prompt = True
system_prompt = prompts [ system_prompt_name ][ "text" ]
2026-07-05 17:06:11 +03:00
else :
text = "❌произошла ошибка, возможные проблемы: \n " \
f "1. у вас выбран удалённый сист промпт (` { system_prompt_name } `) \n " \
f "2. конфиг бота настроен неправильно и надо связаться с тем кто его хостит"
return text
2026-05-22 19:34:48 +03:00
2026-09-16 16:52:32 +03:00
system_prompt += TOOLS_PROMPT
2026-07-09 12:34:37 +03:00
content = []
2026-09-15 21:34:10 +03:00
if images and not is_auto :
provider_name = model [ 0 ]
model_name = model [ 1 ]
settings = MODELS . get ( provider_name , {}) . get ( model_name , {})
2026-07-09 12:34:37 +03:00
if settings . get ( "vision" ):
for image in images :
2026-08-13 18:26:46 +03:00
content += [{ "type" : "image_url" , "image_url" : { "url" : f "data:image/jpeg;base64, { image [ 'data' ] } " }}]
2026-07-09 12:34:37 +03:00
else :
for image in images :
try :
2026-08-07 23:03:12 +03:00
if process and msg and message :
process [ image [ "process" ]] = "make"
await msg . edit_text ( make_graph ( message , process ))
image_text = await image2text ( image [ "data" ])
2026-07-09 12:34:37 +03:00
prompt = f "<фото> { image_text } </фото> \n { prompt } "
except Exception as e :
2026-08-07 23:03:12 +03:00
if process and msg and message :
process [ image [ "process" ]] = "error"
await msg . edit_text ( make_graph ( message , process ))
2026-07-09 12:34:37 +03:00
print ( f " { RED } -- err photo - { e } : { config . get ( 'model' , '' ) }{ RESET } " )
2026-08-07 23:03:12 +03:00
else :
if process and msg and message :
process [ image [ "process" ]] = "done"
await msg . edit_text ( make_graph ( message , process ))
2026-07-09 12:34:37 +03:00
content += [{ "type" : "text" , "text" : prompt }]
2026-09-15 21:34:10 +03:00
2026-07-09 12:34:37 +03:00
user_contexts [ user_id ] . append ({ "role" : "user" , "content" : content })
2026-05-22 19:34:48 +03:00
user_contexts [ user_id ] = user_contexts [ user_id ][ - MAX_CONTEXT :]
2026-08-13 18:26:46 +03:00
context = []
for i , m in enumerate ( user_contexts [ user_id ]):
if m . get ( "role" ) == "user" and i < len ( user_contexts [ user_id ]) - 1 :
m = { ** m , "content" : [ p for p in m [ "content" ] if p . get ( "type" ) != "image_url" ]}
context . append ( m )
messages = [{ "role" : "system" , "content" : system_prompt }] + context
2026-05-22 19:34:48 +03:00
else :
2026-09-15 21:34:10 +03:00
is_auto = True
model = [ "all" , "auto" ]
2026-05-22 19:34:48 +03:00
stream_cf = "none"
2026-07-09 12:34:37 +03:00
content = []
content += [{ "type" : "text" , "text" : prompt }]
2026-09-16 01:06:13 +03:00
if system_prompt :
sp = system_prompt
else :
sp = SYSTEM_PROMPTS [ DEFAULT_PROMPT ][ "text" ] + ADD_TO_PROMPT + OLD_MD
2026-09-16 16:52:32 +03:00
sp += TOOLS_PROMPT
2026-09-16 01:06:13 +03:00
messages = [{ "role" : "system" , "content" : sp }] + [
2026-07-09 12:34:37 +03:00
{ "role" : "user" , "content" : content }
2026-05-22 19:34:48 +03:00
]
text = ""
2026-09-16 16:52:32 +03:00
total_in = 0
total_out = 0
tools_used = []
2026-05-22 19:34:48 +03:00
2026-08-11 23:05:58 +03:00
if str ( user_id ) in temp_benned :
2026-08-11 22:58:25 +03:00
return temp_benned [ str ( user_id )]
2026-05-23 18:09:50 +03:00
add_one_to ( "stats/gens" )
2026-07-05 14:52:57 +03:00
2026-09-15 21:34:10 +03:00
if is_auto :
photo_summary = ""
if images :
for image in images :
try :
if process and msg and message :
process [ image [ "process" ]] = "make"
await msg . edit_text ( make_graph ( message , process ))
image_text = await image2text ( image [ "data" ])
photo_summary += f "<фото> { image_text } </фото> \n "
except Exception as e :
if process and msg and message :
process [ image [ "process" ]] = "error"
await msg . edit_text ( make_graph ( message , process ))
print ( f " { RED } -- err photo - { e } : all*auto { RESET } " )
else :
if process and msg and message :
process [ image [ "process" ]] = "done"
await msg . edit_text ( make_graph ( message , process ))
expanded = expand_auto_models ()
last_type = ""
for entry in expanded :
if last_type == "da" :
break
parts = entry . split ( "*" )
provider_name = parts [ 0 ]
model_name = parts [ 1 ]
provider = PROVIDERS . get ( provider_name )
if not provider :
continue
settings = MODELS . get ( provider_name , {}) . get ( model_name , {})
key = get_key ( provider_name , entry )
auto_content = [{ "type" : "text" , "text" : photo_summary + prompt if images and not settings . get ( "vision" ) else prompt }]
if images and settings . get ( "vision" ):
for image in images :
auto_content += [{ "type" : "image_url" , "image_url" : { "url" : f "data:image/jpeg;base64, { image [ 'data' ] } " }}]
auto_messages = [{ "role" : "system" , "content" : messages [ 0 ][ "content" ]}, { "role" : "user" , "content" : auto_content }] + messages [ 2 :]
2026-09-16 16:52:32 +03:00
sent_messages = auto_messages
2026-09-15 21:34:10 +03:00
text = await openai_generate_text (
api = provider ,
api_key = key ,
bot = bot ,
model = model_name ,
settings = settings ,
messages = auto_messages ,
stream = False ,
stream_cf = stream_cf ,
chat_id = chat_id ,
draft_id = chat_id ,
message_id = message_id ,
inline_id = inline_id
)
2026-09-16 16:52:32 +03:00
total_in += text . get ( "input" ) or 0
total_out += text . get ( "output" ) or 0
if text [ "type" ] == "error" and text . get ( "etype" ) in [ "connection" , "timeout" , "int" , "incomplete" ]:
await alert_admin ( bot , provider_name , text [ "text" ])
2026-09-15 21:34:10 +03:00
last_type = text [ "type" ]
if text == "" :
text = { "type" : "error" , "text" : "все модели в автовыборе вернули ошибку" }
else :
provider_name = model [ 0 ]
model_name = model [ 1 ]
provider = PROVIDERS . get ( provider_name )
if not provider :
return "❌произошла ошибка, возможные проблемы: \n " \
"1. у вас выбрана удалённая модель или провайдер \n " \
"2. конфиг бота настроен неправильно и надо связаться с тем кто его хостит"
settings = MODELS . get ( provider_name , {}) . get ( model_name , {})
key = get_key ( provider_name , model_str )
2026-09-16 16:52:32 +03:00
sent_messages = messages
2026-09-15 21:34:10 +03:00
if stream_cf == "native" and user_id == chat_id and settings . get ( "nostream" , False ) == False :
stream = True
elif stream_cf == "edit" and chat_id and message_id and settings . get ( "nostream" , False ) == False :
stream = True
elif stream_cf == "edit" or stream_cf == "native" and inline_id :
stream = True
else :
stream = False
if process and msg and message :
process [ "text" ] = "make"
await msg . edit_text ( make_graph ( message , process ))
text = await openai_generate_text (
api = provider ,
api_key = key ,
bot = bot ,
model = model_name ,
settings = settings ,
messages = messages ,
stream = stream ,
stream_cf = stream_cf ,
chat_id = chat_id ,
draft_id = chat_id ,
message_id = message_id ,
inline_id = inline_id
)
2026-09-16 16:52:32 +03:00
total_in += text . get ( "input" ) or 0
total_out += text . get ( "output" ) or 0
if text [ "type" ] == "error" and text . get ( "etype" ) in [ "connection" , "timeout" , "int" , "incomplete" ]:
await alert_admin ( bot , provider_name , text [ "text" ])
for _ in range ( 2 ):
if text . get ( "type" ) != "da" :
break
tool = extract_tool ( text . get ( "text" , "" ))
if not tool :
break
kind , query = tool
try :
if kind == "search" :
tool_result = web_search ( query )
else :
tool_result = wiki_search ( query )
tools_used . append ( kind )
except Exception as e :
print ( f " { RED } -- tool { kind } - { e }{ RESET } " )
tool_result = f "поиск не сработал ( { e } )"
tool_exchange = [
{ "role" : "assistant" , "content" : [{ "type" : "text" , "text" : text [ "text" ]}]},
{ "role" : "user" , "content" : [{ "type" : "text" , "text" : f "<результаты поиска> \n { tool_result [: 3000 ] } \n </результаты> \n ответь на вопрос используя эти данные. тег поиска больше не пиши." }]},
]
sent_messages += tool_exchange
if user_id :
user_contexts [ user_id ] = ( user_contexts [ user_id ] + tool_exchange )[ - MAX_CONTEXT :]
text = await openai_generate_text (
api = provider ,
api_key = key ,
bot = bot ,
model = model_name ,
settings = settings ,
messages = sent_messages ,
stream = False ,
stream_cf = stream_cf ,
chat_id = chat_id ,
draft_id = chat_id ,
message_id = message_id ,
inline_id = inline_id
)
total_in += text . get ( "input" ) or 0
total_out += text . get ( "output" ) or 0
if tools_used and text . get ( "type" ) == "da" :
tool_names = []
if "search" in tools_used :
tool_names . append ( "поиск (duckduckgo)" )
if "wiki" in tools_used :
tool_names . append ( "википедия" )
text [ "text" ] = "🔍 использован: " + ", " . join ( tool_names ) + " \n\n " + text [ "text" ]
2026-05-22 19:34:48 +03:00
if user_id :
2026-09-16 16:52:32 +03:00
add_tokens ( user_id , total_in , total_out )
2026-09-16 12:59:20 +03:00
user_contexts [ user_id ] . append ({ "role" : "assistant" , "content" : [{ "type" : "text" , "text" : text [ "text" ]}]})
2026-05-22 19:34:48 +03:00
user_contexts [ user_id ] = user_contexts [ user_id ][ - MAX_CONTEXT :]
2026-09-15 21:34:10 +03:00
return text [ "text" ]
2026-05-22 19:34:48 +03:00
async def image2text ( image ) -> str :
if IMAGE_MODEL == "" :
return ""
2026-08-07 23:03:12 +03:00
model = IMAGE_MODEL . split ( "*" , 1 )
api = PROVIDERS [ model [ 0 ]]
2026-05-22 19:34:48 +03:00
messages = [
{
"role" : "user" ,
"content" : [
2026-08-13 18:26:46 +03:00
{ "type" : "text" , "text" : IMAGE_PROMPT },
{ "type" : "image_url" , "image_url" : { "url" : f "data:image/jpeg;base64, { image } " }},
2026-05-22 19:34:48 +03:00
],
}
]
2026-08-13 18:26:46 +03:00
async for completion in ai . completions (
2026-09-15 21:34:10 +03:00
url = api [ "url" ], api_key = get_key ( model [ 0 ], IMAGE_MODEL ), model = model [ 1 ], messages = messages , stream = False
2026-08-07 23:03:12 +03:00
):
pass
2026-08-13 18:26:46 +03:00
return completion [ "choices" ][ 0 ][ "message" ][ "content" ]
2026-05-22 19:34:48 +03:00
2026-09-15 21:34:10 +03:00
def extract_error ( resp ):
if isinstance ( resp , dict ):
err = resp . get ( "error" )
if isinstance ( err , dict ):
return str ( err . get ( "message" ) or err )
elif err :
return str ( err )
return str ( resp . get ( "message" ) or "" ) if "message" in resp else ""
return ""
2026-05-22 19:34:48 +03:00
async def openai_generate_text (
2026-08-07 23:03:12 +03:00
api ,
2026-05-22 19:34:48 +03:00
bot ,
model ,
settings ,
messages ,
stream ,
stream_cf ,
2026-08-07 23:03:12 +03:00
chat_id = None ,
draft_id = None ,
message_id = None ,
2026-09-15 21:34:10 +03:00
inline_id = None ,
api_key = None
2026-05-22 19:34:48 +03:00
) -> str :
2026-09-16 16:52:32 +03:00
usage_in = 0
usage_out = 0
2026-05-22 19:34:48 +03:00
try :
2026-07-16 13:00:25 +03:00
extra_body = {
"think" : "false"
}
if settings . get ( "noexb" ):
extra_body = None
2026-08-07 23:03:12 +03:00
alo = {
"url" : api [ "url" ],
2026-09-15 21:34:10 +03:00
"api_key" : api_key or api [ "key" ],
2026-08-07 23:03:12 +03:00
"model" : model ,
"messages" : messages ,
2026-09-15 21:34:10 +03:00
"stream" : stream ,
2026-08-07 23:03:12 +03:00
"max_tokens" : MAX_TOKENS ,
"extra" : extra_body
}
2026-05-22 19:34:48 +03:00
if stream :
message = ""
2026-09-16 12:53:35 +03:00
stream_error = ""
2026-08-07 23:03:12 +03:00
t_counter = 0
2026-05-22 19:34:48 +03:00
counter = 0
2026-08-07 23:03:12 +03:00
counter_2 = 1
2026-05-22 19:34:48 +03:00
if not draft_id :
draft_id = chat_id
2026-08-07 23:03:12 +03:00
async for chunk in ai . completions ( ** alo ):
try :
content = chunk [ "choices" ][ 0 ][ "delta" ][ "content" ]
except :
2026-09-16 16:52:32 +03:00
if isinstance ( chunk , dict ) and chunk . get ( "usage" ):
u = chunk [ "usage" ]
usage_in = u . get ( "prompt_tokens" , 0 ) or 0
usage_out = u . get ( "completion_tokens" , 0 ) or 0
continue
2026-09-15 21:34:10 +03:00
err_text = extract_error ( chunk )
if err_text :
error = parse_response ( err_text . lower ())
print ( f " { RED } -- { error [ 'type' ] } - { err_text }{ RESET } " )
return {
"type" : "error" ,
2026-09-16 16:52:32 +03:00
"text" : error [ "message" ],
"etype" : error [ "type" ]
2026-09-15 21:34:10 +03:00
}
2026-09-16 12:53:35 +03:00
if isinstance ( chunk , dict ) and "choices" not in chunk :
stream_error = str ( chunk )[: 300 ]
elif isinstance ( chunk , str ) and chunk . strip ():
stream_error = chunk . strip ()[: 300 ]
2026-08-07 23:03:12 +03:00
continue
2026-05-22 19:34:48 +03:00
if content :
message += content
2026-08-07 23:03:12 +03:00
counter += len ( content )
t_counter += 1
if counter > MAX_SYMBOLS :
break
2026-09-15 21:34:10 +03:00
2026-08-07 23:03:12 +03:00
if int ( counter / STREAMING_SPLIT_SYMBOLS ) == counter_2 :
2026-09-15 21:34:10 +03:00
counter_2 += 1
2026-05-22 19:34:48 +03:00
display_text , s = clean_tail_repeats ( message )
if s > 0 :
display_text += f "*... < { s } обрезано>*"
if display_text . count ( "```" ) % 2 != 0 :
display_text += " \n ```"
2026-08-07 23:03:12 +03:00
if counter < 3950 :
display_text += "▋"
2026-09-15 21:34:10 +03:00
2026-08-07 23:03:12 +03:00
try :
if stream_cf == "native" and chat_id and draft_id :
await bot . send_message_draft (
chat_id = chat_id ,
draft_id = draft_id ,
text = display_text ,
)
elif stream_cf == "edit" and chat_id and message_id :
await bot . edit_message_text (
chat_id = chat_id ,
message_id = message_id ,
text = display_text ,
)
elif stream_cf == "edit" or stream_cf == "native" and inline_id :
await bot . edit_message_text (
inline_message_id = inline_id ,
text = display_text ,
)
except TelegramBadRequest as e :
if "can't parse entities" in str ( e ):
if stream_cf == "native" and chat_id and draft_id :
await bot . send_message_draft (
chat_id = chat_id ,
draft_id = draft_id ,
text = display_text ,
parse_mode = None ,
)
elif stream_cf == "edit" and chat_id and message_id :
await bot . edit_message_text (
chat_id = chat_id ,
message_id = message_id ,
text = display_text ,
parse_mode = None ,
)
elif stream_cf == "edit" or stream_cf == "native" and inline_id :
await bot . edit_message_text (
inline_message_id = inline_id ,
text = display_text ,
parse_mode = None ,
)
else :
print ( f " { YELLOW } -- { e }{ RESET } " )
except Exception as e :
print ( f " { YELLOW } -- { e }{ RESET } " )
2026-05-22 19:34:48 +03:00
text = message
2026-09-16 12:53:35 +03:00
if not text and stream_error :
error = parse_response ( stream_error . lower ())
print ( f " { RED } -- { error [ 'type' ] } - { stream_error }{ RESET } " )
return {
"type" : "error" ,
2026-09-16 16:52:32 +03:00
"text" : error [ "message" ],
"etype" : error [ "type" ]
2026-09-16 12:53:35 +03:00
}
2026-05-22 19:34:48 +03:00
else :
2026-08-07 23:03:12 +03:00
async for completion in ai . completions ( ** alo ):
pass
2026-09-15 21:34:10 +03:00
try :
text = completion [ "choices" ][ 0 ][ "message" ][ "content" ]
except Exception as e :
err_text = extract_error ( completion )
if err_text :
error = parse_response ( err_text . lower ())
print ( f " { RED } -- { error [ 'type' ] } - { err_text }{ RESET } " )
return {
"type" : "error" ,
2026-09-16 16:52:32 +03:00
"text" : error [ "message" ],
"etype" : error [ "type" ]
2026-09-15 21:34:10 +03:00
}
error = parse_response ( str ( completion ) . lower ())
print ( f " { RED } -- { error [ 'type' ] } - { completion }{ RESET } " )
return {
"type" : "error" ,
2026-09-16 16:52:32 +03:00
"text" : error [ "message" ],
"etype" : error [ "type" ]
2026-09-15 21:34:10 +03:00
}
2026-09-16 16:52:32 +03:00
if isinstance ( completion , dict ) and completion . get ( "usage" ):
u = completion [ "usage" ]
usage_in = u . get ( "prompt_tokens" , 0 ) or 0
usage_out = u . get ( "completion_tokens" , 0 ) or 0
2026-05-22 19:34:48 +03:00
except Exception as e :
2026-07-16 12:42:36 +03:00
error = parse_response ( str ( e ) . lower ())
2026-05-22 19:34:48 +03:00
print ( f " { RED } -- { error [ 'type' ] } - { e }{ RESET } " )
2026-09-15 21:34:10 +03:00
return {
"type" : "error" ,
2026-09-16 16:52:32 +03:00
"text" : error [ "message" ],
"etype" : error [ "type" ]
2026-09-15 21:34:10 +03:00
}
2026-05-22 19:34:48 +03:00
else :
text , s = clean_tail_repeats ( text )
if s > 0 :
text += f "*... < { s } обрезано>*"
2026-07-09 12:34:37 +03:00
if text == "" :
2026-09-16 00:35:41 +03:00
return {
"type" : "error" ,
2026-09-16 16:52:32 +03:00
"text" : "модель вернула пустой ответ" ,
"etype" : "empty"
2026-09-16 00:35:41 +03:00
}
2026-09-15 21:34:10 +03:00
return {
"type" : "da" ,
2026-09-16 16:52:32 +03:00
"text" : text ,
"input" : usage_in ,
"output" : usage_out
2026-09-15 21:34:10 +03:00
}