aimo_app_amico/
lib.rs

1use std::sync::Arc;
2
3use amico_core::{
4    Agent,
5    types::{Chat, ChatMessage},
6};
7use tokio::sync::Mutex;
8use tokio_with_wasm::alias as tokio;
9use wasm_bindgen::prelude::*;
10use wasm_bindgen_futures::spawn_local;
11
12mod agent;
13mod log;
14mod service;
15
16use agent::{AppStrategy, ChatHandler, create_agent};
17
18/// A WASM-bindgen compatible message structure that can be converted to ChatMessage.
19#[wasm_bindgen]
20#[derive(Clone, Debug)]
21pub struct Message {
22    content: String,
23    role: String,
24}
25
26#[wasm_bindgen]
27impl Message {
28    #[wasm_bindgen(constructor)]
29    pub fn new(content: String, role: String) -> Message {
30        Message { content, role }
31    }
32
33    #[wasm_bindgen(getter)]
34    pub fn content(&self) -> String {
35        self.content.clone()
36    }
37
38    #[wasm_bindgen(getter)]
39    pub fn role(&self) -> String {
40        self.role.clone()
41    }
42
43    #[wasm_bindgen(setter)]
44    pub fn set_content(&mut self, content: String) {
45        self.content = content;
46    }
47
48    #[wasm_bindgen(setter)]
49    pub fn set_role(&mut self, role: String) {
50        self.role = role;
51    }
52}
53
54impl From<Message> for ChatMessage {
55    fn from(message: Message) -> Self {
56        ChatMessage {
57            content: message.content,
58            role: message.role,
59        }
60    }
61}
62
63/// The WASM runtime for the agent.
64#[wasm_bindgen]
65pub struct AgentWasmRuntime {
66    agent: Option<Agent<AppStrategy>>,
67    chat_handler: Arc<Mutex<ChatHandler>>,
68    running: bool,
69}
70
71#[wasm_bindgen]
72impl AgentWasmRuntime {
73    #[wasm_bindgen(constructor)]
74    pub fn new(jwt: String) -> AgentWasmRuntime {
75        let (agent, chat_handler) = create_agent(jwt);
76
77        AgentWasmRuntime {
78            agent: Some(agent),
79            chat_handler: Arc::new(Mutex::new(chat_handler)),
80            running: false,
81        }
82    }
83
84    #[wasm_bindgen]
85    pub fn start(&mut self) {
86        if self.running {
87            tracing::warn!("Agent is already running");
88            return;
89        }
90
91        if let Some(mut agent) = self.agent.take() {
92            let _chat_handler = self.chat_handler.clone();
93            spawn_local(async move {
94                tracing::info!("Starting agent runtime");
95                agent.run().await;
96            });
97            self.running = true;
98            tracing::info!("Agent runtime started");
99        }
100    }
101
102    #[wasm_bindgen]
103    pub async fn chat(&self, messages: Vec<Message>) -> Result<String, JsValue> {
104        if !self.running {
105            return Err(JsValue::from_str(
106                "Agent is not running. Call start() first.",
107            ));
108        }
109
110        // Convert Vec<Message> to Vec<ChatMessage>
111        let chat_messages: Vec<ChatMessage> = messages.into_iter().map(|msg| msg.into()).collect();
112
113        let chat = Chat {
114            messages: chat_messages,
115
116            // We don't use session_id here
117            session_id: 0,
118        };
119
120        let mut handler = self.chat_handler.lock().await;
121        match handler.chat(chat).await {
122            Ok(response) => Ok(response),
123            Err(e) => Err(JsValue::from_str(&format!("Chat error: {}", e))),
124        }
125    }
126
127    #[wasm_bindgen]
128    pub fn is_running(&self) -> bool {
129        self.running
130    }
131}
132
133/// Initialize the WASM module.
134#[wasm_bindgen(start)]
135pub fn start() {
136    log::init();
137    tracing::info!("WASM module initialized");
138}