Skip to main content

auto_derive/
lib.rs

1// Copyright 2026 Mahmoud Harmouch.
2//
3// Licensed under the MIT license
4// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
5// option. This file may not be copied, modified, or distributed
6// except according to those terms.
7
8extern crate proc_macro;
9
10use quote::quote;
11use syn::{DeriveInput, parse_macro_input};
12
13#[proc_macro_derive(Auto)]
14pub fn derive_agent(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
15    let input = parse_macro_input!(input as DeriveInput);
16    let name = &input.ident;
17
18    let expanded = quote! {
19        impl Agent for #name {
20            fn new(persona: Cow<'static, str>, behavior: Cow<'static, str>) -> Self {
21                let mut agent = Self::default();
22                agent.agent.persona = persona;
23                agent.agent.behavior = behavior;
24                agent
25            }
26
27            fn update(&mut self, status: Status) {
28                self.agent.update(status);
29            }
30
31            fn behavior(&self) -> &std::borrow::Cow<'static, str> {
32                &self.agent.behavior
33            }
34
35            fn persona(&self) -> &std::borrow::Cow<'static, str> {
36                &self.agent.persona
37            }
38
39            fn status(&self) -> &Status {
40                &self.agent.status
41            }
42
43            fn memory(&self) -> &Vec<Message> {
44                &self.agent.memory
45            }
46
47            fn tools(&self) -> &Vec<Tool> {
48                &self.agent.tools
49            }
50
51            fn knowledge(&self) -> &Knowledge {
52                &self.agent.knowledge
53            }
54
55            fn planner(&self) -> Option<&Planner> {
56                self.agent.planner.as_ref()
57            }
58
59            fn profile(&self) -> &Persona {
60                &self.agent.profile
61            }
62
63            #[cfg(feature = "net")]
64            fn collaborators(&self) -> Vec<Collaborator> {
65                let mut all = Vec::new();
66                all.extend(self.agent.local_collaborators.values().cloned());
67                all.extend(self.agent.remote_collaborators.values().cloned());
68                all
69            }
70
71            fn reflection(&self) -> Option<&Reflection> {
72                self.agent.reflection.as_ref()
73            }
74
75            fn scheduler(&self) -> Option<&TaskScheduler> {
76                self.agent.scheduler.as_ref()
77            }
78
79            fn capabilities(&self) -> &std::collections::HashSet<Capability> {
80                &self.agent.capabilities
81            }
82
83            fn context(&self) -> &ContextManager {
84                &self.agent.context
85            }
86
87            fn tasks(&self) -> &Vec<Task> {
88                &self.agent.tasks
89            }
90
91            fn memory_mut(&mut self) -> &mut Vec<Message> {
92                &mut self.agent.memory
93            }
94
95            fn planner_mut(&mut self) -> Option<&mut Planner> {
96                self.agent.planner.as_mut()
97            }
98
99            fn context_mut(&mut self) -> &mut ContextManager {
100                &mut self.agent.context
101            }
102        }
103
104        impl Functions for #name {
105            fn get_agent(&self) -> &AgentGPT {
106                &self.agent
107            }
108        }
109
110        #[async_trait]
111        impl AsyncFunctions for #name {
112            async fn execute<'a>(
113                &'a mut self,
114                task: &'a mut Task,
115                execute: bool,
116                browse: bool,
117                max_tries: u64,
118            ) -> Result<()> {
119                <#name as Executor>::execute(self, task, execute, browse, max_tries).await
120            }
121
122            /// Saves a communication to long-term memory for the agent.
123            ///
124            /// # Arguments
125            ///
126            /// * `communication` - The communication to save, which contains the role and content.
127            ///
128            /// # Returns
129            ///
130            /// (`Result<()>`): Result indicating the success or failure of saving the communication.
131            ///
132            /// # Business Logic
133            ///
134            /// - This method uses the `save_long_term_memory` util function to save the communication into the agent's long-term memory.
135            /// - The communication is embedded and stored using the agent's unique ID as the namespace.
136            /// - It handles the embedding and metadata for the communication, ensuring it's stored correctly.
137            #[cfg(feature = "mem")]
138            async fn save_ltm(&mut self, message: Message) -> Result<()> {
139                save_long_term_memory(&mut self.client, self.agent.id.clone(), message).await
140            }
141
142            /// Retrieves all communications stored in the agent's long-term memory.
143            ///
144            /// # Returns
145            ///
146            /// (`Result<Vec<Message>>`): A result containing a vector of communications retrieved from the agent's long-term memory.
147            ///
148            /// # Business Logic
149            ///
150            /// - This method fetches the stored communications for the agent by interacting with the `load_long_term_memory` function.
151            /// - The function will return a list of communications that are indexed by the agent's unique ID.
152            /// - It handles the retrieval of the stored metadata and content for each communication.
153            #[cfg(feature = "mem")]
154            async fn get_ltm(&self) -> Result<Vec<Message>> {
155                load_long_term_memory(self.agent.id.clone()).await
156            }
157
158            /// Retrieves the concatenated context of all communications in the agent's long-term memory.
159            ///
160            /// # Returns
161            ///
162            /// (`String`): A string containing the concatenated role and content of all communications stored in the agent's long-term memory.
163            ///
164            /// # Business Logic
165            ///
166            /// - This method calls the `long_term_memory_context` function to generate a string representation of the agent's entire long-term memory.
167            /// - The context string is composed of each communication's role and content, joined by new lines.
168            /// - It provides a quick overview of the agent's memory in a human-readable format.
169            #[cfg(feature = "mem")]
170            async fn ltm_context(&self) -> String {
171                long_term_memory_context(self.agent.id.clone()).await
172            }
173
174            async fn generate(&mut self, request: &str) -> Result<String> {
175                #[cfg(feature = "gem")]
176                use gems::{chat::ChatBuilder, messages::Content, traits::CTrait};
177
178                #[cfg(feature = "oai")]
179                use openai_dive::v1::{models::Gpt4Model, resources::chat::{ChatMessage, ChatMessageContent, ChatCompletionResponseFormat}};
180
181                #[cfg(feature = "cld")]
182                use anthropic_ai_sdk::types::message::{
183                    ContentBlock, CreateMessageParams, Message as AnthMessage,
184                    MessageClient, RequiredMessageParams, Role,
185                };
186
187                #[cfg(feature = "xai")]
188                use x_ai::chat_compl::{ChatCompletionsRequestBuilder, Message as XaiMessage};
189
190                #[cfg(feature = "co")]
191                use cohere_rust::api::chat::ChatRequest;
192
193                #[cfg(feature = "hf")]
194                use ::autogpt::prelude::serde_json::{Value as JsonValue, json};
195
196                match &mut self.client {
197                    #[cfg(feature = "gem")]
198                    ClientType::Gemini(gem_client) => {
199                        let parameters = ChatBuilder::default()
200                            .messages(vec![gems::messages::Message::User {
201                                content: Content::Text(request.to_string()),
202                                name: None,
203                            }])
204                            .build()?;
205
206                        let result = gem_client.chat().generate(parameters).await;
207                        Ok(result.unwrap_or_default())
208                    }
209
210                    #[cfg(feature = "oai")]
211                    ClientType::OpenAI(oai_client) => {
212                        use openai_dive::v1::resources::chat::ChatCompletionParametersBuilder;
213
214                        let parameters = ChatCompletionParametersBuilder::default()
215                            .model(Gpt4Model::Gpt4O.to_string())
216                            .messages(vec![ChatMessage::User {
217                                content: ChatMessageContent::Text(request.to_string()),
218                                name: None,
219                            }])
220                            .response_format(ChatCompletionResponseFormat::Text)
221                            .build()?;
222
223                        let result = oai_client.chat().create(parameters).await?;
224                        let message = &result.choices[0].message;
225
226                        Ok(match message {
227                            ChatMessage::Assistant {
228                                content: Some(chat_content),
229                                ..
230                            } => chat_content.to_string(),
231                            ChatMessage::User { content, .. } => content.to_string(),
232                            ChatMessage::System { content, .. } => content.to_string(),
233                            ChatMessage::Developer { content, .. } => content.to_string(),
234                            ChatMessage::Tool { content, .. } => content.to_string(),
235                            _ => String::new(),
236                        })
237                    }
238
239                    #[cfg(feature = "cld")]
240                    ClientType::Anthropic(client) => {
241                        let body = CreateMessageParams::new(RequiredMessageParams {
242                            model: "claude-opus-4-6".to_string(),
243                            messages: vec![AnthMessage::new_text(Role::User, request.to_string())],
244                            max_tokens: 1024,
245                        });
246
247                        let chat_response = client.create_message(Some(&body)).await?;
248                        Ok(chat_response
249                            .content
250                            .iter()
251                            .filter_map(|block| match block {
252                                ContentBlock::Text { text, .. } => Some(text.as_str()),
253                                _ => None,
254                            })
255                            .collect::<Vec<_>>()
256                            .join("\n"))
257                    }
258
259                    #[cfg(feature = "xai")]
260                    ClientType::Xai(xai_client) => {
261                        use x_ai::traits::ChatCompletionsFetcher;
262
263                        let messages = vec![XaiMessage::text("user", request)];
264
265                        let rb = ChatCompletionsRequestBuilder::new(
266                            xai_client.clone(),
267                            "grok-4".into(),
268                            messages,
269                        )
270                        .temperature(0.0)
271                        .stream(false);
272
273                        let req = rb.clone().build()?;
274                        let chat = rb.create_chat_completion(req).await?;
275                        Ok(chat.choices[0].message.content.to_string())
276                    }
277
278                    #[cfg(feature = "co")]
279                    ClientType::Cohere(co_client) => {
280                        let chat_request = ChatRequest {
281                            message: request,
282                            ..Default::default()
283                        };
284
285                        let mut receiver = match co_client.chat(&chat_request).await {
286                            Ok(rx) => rx,
287                            Err(e) => return Err(::autogpt::prelude::anyhow!("Cohere API initialization failed: {}", e)),
288                        };
289                        let mut full_text = String::new();
290                        while let Some(res) = receiver.recv().await {
291                            match res {
292                                Ok(cohere_rust::api::chat::ChatStreamResponse::ChatTextGeneration { text, .. }) => {
293                                    full_text.push_str(&text);
294                                }
295                                Ok(_) => {}
296                                // Err(e) => return Err(::autogpt::prelude::anyhow!("Cohere chat error: {:?}", e)),
297                                Err(_) => {},
298                            }
299                        }
300                        Ok(full_text)
301                    }
302
303                    #[cfg(feature = "hf")]
304                    ClientType::HuggingFace(hf_client) => {
305                        let model_id = hf_model_from_str(&hf_client.model);
306                        let result = hf_client
307                            .client
308                            .inference()
309                            .create(request, model_id)
310                            .await
311                            .map_err(|e| ::autogpt::prelude::anyhow!("HuggingFace inference failed: {}", e))?;
312                        let text = match result {
313                            api_huggingface::components::inference_shared::InferenceResponse::Single(output) => output.generated_text,
314                            api_huggingface::components::inference_shared::InferenceResponse::Batch(mut batch) => {
315                                batch.pop().map(|o| o.generated_text).unwrap_or_default()
316                            }
317                            _ => String::new(),
318                        };
319                        Ok(text)
320                    }
321
322                    #[allow(unreachable_patterns)]
323                    _ => {
324                        return Err(::autogpt::prelude::anyhow!(
325                            "No valid AI client configured. Enable `hf`, `co`, `gem`, `oai`, `cld`, or `xai` feature."
326                        ));
327                    }
328                }
329            }
330
331            async fn imagen(&mut self, request: &str) -> Result<Vec<u8>> {
332                #[cfg(feature = "gem")]
333                use gems::{imagen::ImageGenBuilder, messages::Content, models::Model, traits::CTrait};
334
335                #[cfg(feature = "hf")]
336                use ::autogpt::prelude::serde_json::json;
337
338                match &mut self.client {
339                    #[cfg(feature = "gem")]
340                    ClientType::Gemini(gem_client) => {
341                        gem_client.set_model(Model::Imagen4);
342
343                        let input = gems::messages::Message::User {
344                            content: Content::Text(request.into()),
345                            name: None,
346                        };
347
348                        let params = ImageGenBuilder::default()
349                            .model(Model::Imagen4)
350                            .input(input)
351                            .build()?;
352
353                        let image_bytes = gem_client.images().generate(params).await;
354                        Ok(image_bytes.unwrap_or_default())
355                    }
356
357                    #[cfg(feature = "oai")]
358                    ClientType::OpenAI(oai_client) => {
359                        // TODO: Implement this
360                        Ok(Default::default())
361                    }
362
363                    #[cfg(feature = "cld")]
364                    ClientType::Anthropic(client) => {
365                        // TODO: Implement this
366                        Ok(Default::default())
367                    }
368
369                    #[cfg(feature = "xai")]
370                    ClientType::Xai(xai_client) => {
371                        // TODO: Implement this
372                        Ok(Default::default())
373                    }
374
375                    #[cfg(feature = "co")]
376                    ClientType::Cohere(_co_client) => {
377                        // Cohere does not support image generation
378                        Ok(Default::default())
379                    }
380
381                    #[cfg(feature = "hf")]
382                    ClientType::HuggingFace(hf_client) => {
383                        let model_id = hf_model_from_str(&hf_client.model);
384                        let result = hf_client
385                            .client
386                            .inference()
387                            .create(request, model_id)
388                            .await
389                            .map_err(|e| ::autogpt::prelude::anyhow!("HuggingFace imagen failed: {}", e))?;
390                        let text = match result {
391                            api_huggingface::components::inference_shared::InferenceResponse::Single(output) => output.generated_text,
392                            api_huggingface::components::inference_shared::InferenceResponse::Batch(mut batch) => {
393                                batch.pop().map(|o| o.generated_text).unwrap_or_default()
394                            }
395                            _ => String::new(),
396                        };
397                        Ok(text.into_bytes())
398                    }
399
400                    #[allow(unreachable_patterns)]
401                    _ => {
402                        return Err(::autogpt::prelude::anyhow!(
403                            "No valid AI client configured. Enable `hf`, `co`, `gem`, `oai`, `cld`, or `xai` feature."
404                        ));
405                    }
406                }
407            }
408
409            async fn stream(&mut self, request: &str) -> Result<ReqResponse> {
410                #[cfg(feature = "gem")]
411                use gems::{messages::Content, models::Model, stream::StreamBuilder, traits::CTrait};
412
413                #[cfg(any(feature = "oai", feature = "cld", feature = "hf"))]
414                use futures::StreamExt;
415
416                #[cfg(feature = "oai")]
417                use {
418                    openai_dive::v1::resources::chat::{
419                        ChatCompletionParametersBuilder, ChatMessage, ChatMessageContent,
420                    },
421                };
422
423                #[cfg(feature = "cld")]
424                use {
425                    anthropic_ai_sdk::types::message::{
426                        ContentBlockDelta, CreateMessageParams, Message as AnthMessage,
427                        MessageClient, RequiredMessageParams, Role, StreamEvent,
428                    },
429                };
430
431                #[cfg(feature = "xai")]
432                use {
433                    x_ai::chat_compl::{ChatCompletionsRequestBuilder, Message as XaiMessage},
434                    x_ai::traits::ClientConfig,
435                };
436
437                #[cfg(feature = "hf")]
438                use {
439                    ::autogpt::prelude::serde_json::{Value as JsonValue, json},
440                };
441
442                let request_owned = request.to_string();
443                match &mut self.client {
444                    #[cfg(feature = "gem")]
445                    ClientType::Gemini(gem_client) => {
446                        let parameters = StreamBuilder::default()
447                            .model(Model::Flash3Preview)
448                            .input(gems::messages::Message::User {
449                                content: Content::Text(request_owned.clone()),
450                                name: None,
451                            })
452                            .build()?;
453
454                        let resp = gem_client.stream().generate(parameters).await?;
455                        let (tx, rx) = tokio::sync::mpsc::channel::<String>(100);
456
457                        tokio::spawn(async move {
458                            let mut resp = resp;
459                            let mut buffer = String::new();
460
461                            while let Ok(Some(chunk)) = resp.chunk().await {
462                                if let Ok(text) = std::str::from_utf8(&chunk) {
463                                    buffer.push_str(text);
464                                    let mut parts: Vec<&str> =
465                                        buffer.split("\n\n").collect();
466                                    let new_buffer = if !buffer.ends_with("\n\n") {
467                                        parts.pop().unwrap_or("").to_string()
468                                    } else {
469                                        String::new()
470                                    };
471
472                                    for part in parts {
473                                        for line in part.lines() {
474                                            if let Some(data) =
475                                                line.strip_prefix("data: ")
476                                            {
477                                                let data = data.trim();
478                                                if data == "[DONE]" {
479                                                    continue;
480                                                }
481                                                if let Ok(json) =
482                                                    ::autogpt::prelude::serde_json::from_str::<::autogpt::prelude::serde_json::Value>(data)
483                                                {
484                                                    if let Some(text) = json
485                                                        .get("candidates")
486                                                        .and_then(|c| c.get(0))
487                                                        .and_then(|c| c.get("content"))
488                                                        .and_then(|c| c.get("parts"))
489                                                        .and_then(|p| p.get(0))
490                                                        .and_then(|p| p.get("text"))
491                                                        .and_then(|t| t.as_str())
492                                                    {
493                                                        let _ = tx
494                                                            .send(text.to_string())
495                                                            .await;
496                                                    }
497                                                }
498                                            }
499                                        }
500                                    }
501
502                                    buffer = new_buffer;
503                                }
504                            }
505                        });
506
507                        Ok(ReqResponse(Some(rx)))
508                    }
509
510                    #[cfg(feature = "oai")]
511                    ClientType::OpenAI(oai_client) => {
512                        let oai_client = oai_client.clone();
513                        let request_owned = request_owned.clone();
514                        let (tx, rx) = tokio::sync::mpsc::channel::<String>(100);
515
516                        tokio::spawn(async move {
517                            let parameters =
518                                ChatCompletionParametersBuilder::default()
519                                    .model("gpt-5")
520                                    .messages(vec![ChatMessage::User {
521                                        content: ChatMessageContent::Text(
522                                            request_owned,
523                                        ),
524                                        name: None,
525                                    }])
526                                    .build()
527                                    .unwrap();
528
529                            if let Ok(mut stream) =
530                                oai_client.chat().create_stream(parameters).await
531                            {
532                                while let Some(response) = stream.next().await {
533                                    match response {
534                                        Ok(chat_response) => {
535                                            for choice in chat_response.choices {
536                                                let text_opt = match &choice.delta {
537                                                    openai_dive::v1::resources::chat::DeltaChatMessage::Assistant {
538                                                        content: Some(
539                                                            openai_dive::v1::resources::chat::ChatMessageContent::Text(text),
540                                                        ),
541                                                        ..
542                                                    } => Some(text.clone()),
543                                                    openai_dive::v1::resources::chat::DeltaChatMessage::Untagged {
544                                                        content: Some(
545                                                            openai_dive::v1::resources::chat::ChatMessageContent::Text(text),
546                                                        ),
547                                                        ..
548                                                    } => Some(text.clone()),
549                                                    _ => None,
550                                                };
551                                                if let Some(t) = text_opt {
552                                                    let _ = tx.send(t).await;
553                                                }
554                                            }
555                                        }
556                                        Err(_) => break,
557                                    }
558                                }
559                            }
560                        });
561
562                        Ok(ReqResponse(Some(rx)))
563                    }
564
565                    #[cfg(feature = "cld")]
566                    ClientType::Anthropic(client) => {
567                        let client = client.clone();
568                        let request_owned = request_owned.clone();
569                        let (tx, rx) = tokio::sync::mpsc::channel::<String>(100);
570
571                        tokio::spawn(async move {
572                            let body = CreateMessageParams::new(
573                                RequiredMessageParams {
574                                    model: "claude-opus-4-6".to_string(),
575                                    messages: vec![AnthMessage::new_text(
576                                        Role::User,
577                                        request_owned,
578                                    )],
579                                    max_tokens: 1024,
580                                },
581                            )
582                            .with_stream(true);
583
584                            if let Ok(mut stream) =
585                                client.create_message_streaming(&body).await
586                            {
587                                while let Some(event_result) = stream.next().await {
588                                    if let Ok(StreamEvent::ContentBlockDelta { delta, .. }) = event_result {
589                                        if let ContentBlockDelta::TextDelta { text } = delta {
590                                            let _ = tx.send(text).await;
591                                        }
592                                    }
593                                }
594                            }
595                        });
596
597                        Ok(ReqResponse(Some(rx)))
598                    }
599
600                    #[cfg(feature = "xai")]
601                    ClientType::Xai(xai_client) => {
602                        let messages = vec![XaiMessage::text("user", request_owned)];
603                        let req = ChatCompletionsRequestBuilder::new(
604                            xai_client.clone(),
605                            "grok-4".into(),
606                            messages,
607                        )
608                        .stream(true)
609                        .build()?;
610
611                        let resp = ClientConfig::request(
612                            &*xai_client,
613                            reqwest::Method::POST,
614                            "chat/completions",
615                        )
616                        .map_err(|e| {
617                            ::autogpt::prelude::anyhow!("Failed to build xAI request: {}", e)
618                        })?
619                        .json(&req)
620                        .send()
621                        .await?;
622
623                        let (tx, rx) = tokio::sync::mpsc::channel::<String>(100);
624
625                        tokio::spawn(async move {
626                            let mut resp = resp;
627                            let mut buffer = String::new();
628
629                            while let Ok(Some(chunk)) = resp.chunk().await {
630                                if let Ok(text) = std::str::from_utf8(&chunk) {
631                                    buffer.push_str(text);
632                                    let mut parts: Vec<&str> =
633                                        buffer.split("\n\n").collect();
634                                    let new_buffer = if !buffer.ends_with("\n\n") {
635                                        parts.pop().unwrap_or("").to_string()
636                                    } else {
637                                        String::new()
638                                    };
639
640                                    for part in parts {
641                                        for line in part.lines() {
642                                            if let Some(data) =
643                                                line.strip_prefix("data: ")
644                                            {
645                                                let data = data.trim();
646                                                if data == "[DONE]" {
647                                                    continue;
648                                                }
649                                                if let Ok(json) =
650                                                    ::autogpt::prelude::serde_json::from_str::<::autogpt::prelude::serde_json::Value>(data)
651                                                {
652                                                    if let Some(content) = json
653                                                        .get("choices")
654                                                        .and_then(|c| c.get(0))
655                                                        .and_then(|c| c.get("delta"))
656                                                        .and_then(|d| d.get("content"))
657                                                        .and_then(|c| c.as_str())
658                                                    {
659                                                        let _ = tx
660                                                            .send(content.to_string())
661                                                            .await;
662                                                    }
663                                                }
664                                            }
665                                        }
666                                    }
667
668                                    buffer = new_buffer;
669                                }
670                            }
671                        });
672
673                        Ok(ReqResponse(Some(rx)))
674                    }
675
676                    #[cfg(feature = "co")]
677                    ClientType::Cohere(co_client) => {
678                        let chat_request = cohere_rust::api::chat::ChatRequest {
679                            message: request,
680                            ..Default::default()
681                        };
682                        let co_result = co_client.chat(&chat_request).await;
683                        let (tx, rx) = tokio::sync::mpsc::channel::<String>(100);
684
685                        if let Ok(mut receiver) = co_result {
686                            tokio::spawn(async move {
687                                while let Some(res) = receiver.recv().await {
688                                    if let Ok(resp) = res {
689                                        if let cohere_rust::api::chat::ChatStreamResponse::ChatTextGeneration {
690                                            text, ..
691                                        } = resp
692                                        {
693                                            let _ = tx.send(text).await;
694                                        }
695                                    }
696                                }
697                            });
698                        }
699
700                        Ok(ReqResponse(Some(rx)))
701                    }
702
703                    #[cfg(feature = "hf")]
704                    ClientType::HuggingFace(hf_client) => {
705                        let model_id = hf_model_from_str(&hf_client.model);
706                        let (tx, rx) = tokio::sync::mpsc::channel::<String>(256);
707                        let client = hf_client.client.clone();
708                        let model_id_owned = model_id.to_string();
709                        let request_owned_clone = request_owned.clone();
710
711                        tokio::spawn(async move {
712                            match client
713                                .inference()
714                                .create(&request_owned_clone, &model_id_owned)
715                                .await
716                            {
717                                Ok(result) => {
718                                    use api_huggingface::components::inference_shared::InferenceResponse;
719                                    let text = match result {
720                                        InferenceResponse::Single(o) => o.generated_text,
721                                        InferenceResponse::Batch(mut v) => {
722                                            v.pop().map(|o| o.generated_text).unwrap_or_default()
723                                        }
724                                        _ => String::new(),
725                                    };
726                                    for word in text.split_whitespace() {
727                                        let _ = tx.send(format!("{word} ")).await;
728                                    }
729                                }
730                                Err(e) => {
731                                    let _ = tx
732                                        .send(format!("[HuggingFace error: {}]", e))
733                                        .await;
734                                }
735                            }
736                        });
737
738                        Ok(ReqResponse(Some(rx)))
739                    }
740
741                    #[allow(unreachable_patterns)]
742                    _ => {
743                        return Err(::autogpt::prelude::anyhow!(
744                            "No valid AI client configured. \
745                             Enable `hf`, `co`, `gem`, `oai`, `cld`, or `xai` feature."
746                        ));
747                    }
748                }
749            }
750        }
751    };
752
753    proc_macro::TokenStream::from(expanded)
754}
755
756// Copyright 2026 Mahmoud Harmouch.
757//
758// Licensed under the MIT license
759// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
760// option. This file may not be copied, modified, or distributed
761// except according to those terms.