@@ -84,6 +84,21 @@ struct Part {
8484 text : String ,
8585}
8686
87+ #[ derive( Deserialize ) ]
88+ struct NvidiaChatResponse {
89+ choices : Vec < NvidiaChoice > ,
90+ }
91+
92+ #[ derive( Deserialize ) ]
93+ struct NvidiaChoice {
94+ message : NvidiaMessage ,
95+ }
96+
97+ #[ derive( Deserialize ) ]
98+ struct NvidiaMessage {
99+ content : String ,
100+ }
101+
87102// ── Commands ────────────────────────────────────────────────────────
88103#[ derive( BotCommands , Clone ) ]
89104#[ command(
@@ -133,6 +148,12 @@ enum Command {
133148
134149 #[ command( description = "/gemini2 <prompt> → ask Gemini 2.5 Flash AI" ) ]
135150 Gemini2 ( String ) ,
151+
152+ #[ command( description = "/glm5ai <prompt> → ask GLM-5 AI without reasoning" ) ]
153+ Glm5Ai ( String ) ,
154+
155+ #[ command( description = "/glm5aireasoning <prompt> → ask GLM-5 AI with reasoning" ) ]
156+ Glm5AiReasoning ( String ) ,
136157}
137158
138159async fn command_handler ( bot : Bot , msg : Message , cmd : Command ) -> ResponseResult < ( ) > {
@@ -241,6 +262,14 @@ async fn command_handler(bot: Bot, msg: Message, cmd: Command) -> ResponseResult
241262 Command :: Gemini2 ( prompt) => {
242263 handle_gemini ( bot, msg, prompt, "gemini-2.5-flash" ) . await ?;
243264 }
265+
266+ Command :: Glm5Ai ( prompt) => {
267+ handle_glm5 ( bot, msg, prompt, false ) . await ?;
268+ }
269+
270+ Command :: Glm5AiReasoning ( prompt) => {
271+ handle_glm5 ( bot, msg, prompt, true ) . await ?;
272+ }
244273 }
245274 Ok ( ( ) )
246275}
@@ -641,6 +670,132 @@ async fn handle_gemini(bot: Bot, msg: Message, prompt: String, model: &str) -> R
641670 Ok ( ( ) )
642671}
643672
673+ async fn handle_glm5 (
674+ bot : Bot ,
675+ msg : Message ,
676+ prompt : String ,
677+ enable_thinking : bool ,
678+ ) -> ResponseResult < ( ) > {
679+ let trimmed = prompt. trim ( ) ;
680+ if trimmed. is_empty ( ) {
681+ let cmd_name = if enable_thinking {
682+ "glm5aireasoning"
683+ } else {
684+ "glm5ai"
685+ } ;
686+ reply_markdown (
687+ bot,
688+ msg,
689+ format ! ( "❌ Usage: /{cmd_name} <your prompt here> (prompt is required)" ) ,
690+ )
691+ . await ?;
692+ return Ok ( ( ) ) ;
693+ }
694+
695+ let api_key = match std:: env:: var ( "NVIDIA_API_KEY" ) {
696+ Ok ( key) if !key. trim ( ) . is_empty ( ) => key,
697+ _ => {
698+ reply_markdown (
699+ bot,
700+ msg,
701+ "❌ NVIDIA_API_KEY environment variable is not set.\n Please add it to your .env file and restart the bot." . to_string ( ) ,
702+ )
703+ . await ?;
704+ return Ok ( ( ) ) ;
705+ }
706+ } ;
707+
708+ let client = reqwest:: Client :: new ( ) ;
709+ let url = "https://integrate.api.nvidia.com/v1/chat/completions" ;
710+
711+ let body = json ! ( {
712+ "model" : "z-ai/glm5" ,
713+ "messages" : [
714+ {
715+ "role" : "user" ,
716+ "content" : trimmed
717+ }
718+ ] ,
719+ "temperature" : 1 ,
720+ "top_p" : 1 ,
721+ "max_tokens" : 16384 ,
722+ "seed" : 42 ,
723+ "stream" : false ,
724+ "chat_template_kwargs" : {
725+ "enable_thinking" : enable_thinking,
726+ "clear_thinking" : true
727+ }
728+ } ) ;
729+
730+ let res = match client
731+ . post ( url)
732+ . header ( "Authorization" , format ! ( "Bearer {}" , api_key) )
733+ . json ( & body)
734+ . send ( )
735+ . await
736+ {
737+ Ok ( response) => response,
738+ Err ( e) => {
739+ reply_markdown (
740+ bot,
741+ msg,
742+ format ! ( "❌ Network error while contacting NVIDIA API: {}" , e) ,
743+ )
744+ . await ?;
745+ return Ok ( ( ) ) ;
746+ }
747+ } ;
748+
749+ if !res. status ( ) . is_success ( ) {
750+ let status = res. status ( ) ;
751+ let err_text = match res. text ( ) . await {
752+ Ok ( text) => text,
753+ Err ( _) => "Unknown error" . to_string ( ) ,
754+ } ;
755+ reply_markdown (
756+ bot,
757+ msg,
758+ format ! ( "❌ NVIDIA API error (HTTP {}): {}" , status, err_text) ,
759+ )
760+ . await ?;
761+ return Ok ( ( ) ) ;
762+ }
763+
764+ let nvidia_resp: NvidiaChatResponse = match res. json ( ) . await {
765+ Ok ( parsed) => parsed,
766+ Err ( e) => {
767+ reply_markdown (
768+ bot,
769+ msg,
770+ format ! ( "❌ Failed to parse NVIDIA API response: {}" , e) ,
771+ )
772+ . await ?;
773+ return Ok ( ( ) ) ;
774+ }
775+ } ;
776+
777+ let response_text = nvidia_resp
778+ . choices
779+ . first ( )
780+ . map ( |choice| choice. message . content . as_str ( ) )
781+ . unwrap_or ( "No response text from GLM-5." ) ;
782+
783+ let model_display = if enable_thinking {
784+ "GLM-5 AI (with reasoning)"
785+ } else {
786+ "GLM-5 AI (without reasoning)"
787+ } ;
788+
789+ reply_markdown (
790+ bot,
791+ msg,
792+ format ! ( "🤖 {}:\n {}" , model_display, response_text) ,
793+ )
794+ . await ?;
795+
796+ Ok ( ( ) )
797+ }
798+
644799/// Escapes **any** text for safe use with Telegram `MarkdownV2`.
645800fn markdown_v2_escape ( text : & str ) -> String {
646801 let mut escaped = String :: with_capacity ( text. len ( ) * 2 ) ;
0 commit comments