fix(goose-acp): heap allocations (#7322)
This commit is contained in:
@@ -1193,18 +1193,23 @@ impl JrMessageHandler for GooseAcpHandler {
|
|||||||
"goose-acp"
|
"goose-acp"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_message(
|
fn handle_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
message: MessageCx,
|
message: MessageCx,
|
||||||
cx: JrConnectionCx<AgentToClient>,
|
cx: JrConnectionCx<AgentToClient>,
|
||||||
) -> Result<Handled<MessageCx>, sacp::Error> {
|
) -> impl std::future::Future<Output = Result<Handled<MessageCx>, sacp::Error>> + Send {
|
||||||
use sacp::util::MatchMessageFrom;
|
use sacp::util::MatchMessageFrom;
|
||||||
use sacp::JrRequestCx;
|
use sacp::JrRequestCx;
|
||||||
|
|
||||||
|
let agent = self.agent.clone();
|
||||||
|
|
||||||
|
// The MatchMessageFrom chain produces an ~85KB async state machine.
|
||||||
|
// Box::pin moves it to the heap so it doesn't overflow the tokio worker stack.
|
||||||
|
Box::pin(async move {
|
||||||
MatchMessageFrom::new(message, &cx)
|
MatchMessageFrom::new(message, &cx)
|
||||||
.if_request(
|
.if_request(
|
||||||
|req: InitializeRequest, req_cx: JrRequestCx<InitializeResponse>| async {
|
|req: InitializeRequest, req_cx: JrRequestCx<InitializeResponse>| async {
|
||||||
req_cx.respond(self.agent.on_initialize(req).await?)
|
req_cx.respond(agent.on_initialize(req).await?)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -1216,19 +1221,19 @@ impl JrMessageHandler for GooseAcpHandler {
|
|||||||
.await
|
.await
|
||||||
.if_request(
|
.if_request(
|
||||||
|req: NewSessionRequest, req_cx: JrRequestCx<NewSessionResponse>| async {
|
|req: NewSessionRequest, req_cx: JrRequestCx<NewSessionResponse>| async {
|
||||||
req_cx.respond(self.agent.on_new_session(req).await?)
|
req_cx.respond(agent.on_new_session(req).await?)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.if_request(
|
.if_request(
|
||||||
|req: LoadSessionRequest, req_cx: JrRequestCx<LoadSessionResponse>| async {
|
|req: LoadSessionRequest, req_cx: JrRequestCx<LoadSessionResponse>| async {
|
||||||
req_cx.respond(self.agent.on_load_session(req, &cx).await?)
|
req_cx.respond(agent.on_load_session(req, &cx).await?)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.if_request(
|
.if_request(
|
||||||
|req: PromptRequest, req_cx: JrRequestCx<PromptResponse>| async {
|
|req: PromptRequest, req_cx: JrRequestCx<PromptResponse>| async {
|
||||||
let agent = self.agent.clone();
|
let agent = agent.clone();
|
||||||
let cx_clone = cx.clone();
|
let cx_clone = cx.clone();
|
||||||
cx.spawn(async move {
|
cx.spawn(async move {
|
||||||
match agent.on_prompt(req, &cx_clone).await {
|
match agent.on_prompt(req, &cx_clone).await {
|
||||||
@@ -1245,27 +1250,28 @@ impl JrMessageHandler for GooseAcpHandler {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.if_notification(|notif: CancelNotification| async {
|
.if_notification(|notif: CancelNotification| async { agent.on_cancel(notif).await })
|
||||||
self.agent.on_cancel(notif).await
|
|
||||||
})
|
|
||||||
.await
|
.await
|
||||||
// Handle methods not yet in the sacp typed API.
|
// Handle methods not yet in the sacp typed API.
|
||||||
// - session/set_model: typed support pending in sacp
|
// - session/set_model: typed support pending in sacp
|
||||||
// - _<method>: custom requests that will eventually route to goose-server
|
// - _<method>: custom requests that will eventually route to goose-server
|
||||||
.otherwise({
|
.otherwise({
|
||||||
let agent = self.agent.clone();
|
let agent = agent.clone();
|
||||||
|message: MessageCx| async move {
|
|message: MessageCx| async move {
|
||||||
match message {
|
match message {
|
||||||
MessageCx::Request(req, request_cx)
|
MessageCx::Request(req, request_cx)
|
||||||
if req.method == "session/set_model" =>
|
if req.method == "session/set_model" =>
|
||||||
{
|
{
|
||||||
let params: SetSessionModelRequest = serde_json::from_value(req.params)
|
let params: SetSessionModelRequest =
|
||||||
.map_err(|e| sacp::Error::invalid_params().data(e.to_string()))?;
|
serde_json::from_value(req.params).map_err(|e| {
|
||||||
|
sacp::Error::invalid_params().data(e.to_string())
|
||||||
|
})?;
|
||||||
let resp = agent
|
let resp = agent
|
||||||
.on_set_model(¶ms.session_id.0, ¶ms.model_id.0)
|
.on_set_model(¶ms.session_id.0, ¶ms.model_id.0)
|
||||||
.await?;
|
.await?;
|
||||||
let json = serde_json::to_value(resp)
|
let json = serde_json::to_value(resp).map_err(|e| {
|
||||||
.map_err(|e| sacp::Error::internal_error().data(e.to_string()))?;
|
sacp::Error::internal_error().data(e.to_string())
|
||||||
|
})?;
|
||||||
request_cx.respond(json)?;
|
request_cx.respond(json)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1282,14 +1288,20 @@ impl JrMessageHandler for GooseAcpHandler {
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map(|()| Handled::Yes)
|
.map(|()| Handled::Yes)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve<R, W>(agent: Arc<GooseAcpAgent>, read: R, write: W) -> Result<()>
|
pub fn serve<R, W>(
|
||||||
|
agent: Arc<GooseAcpAgent>,
|
||||||
|
read: R,
|
||||||
|
write: W,
|
||||||
|
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<()>> + Send>>
|
||||||
where
|
where
|
||||||
R: futures::AsyncRead + Unpin + Send + 'static,
|
R: futures::AsyncRead + Unpin + Send + 'static,
|
||||||
W: futures::AsyncWrite + Unpin + Send + 'static,
|
W: futures::AsyncWrite + Unpin + Send + 'static,
|
||||||
{
|
{
|
||||||
|
Box::pin(async move {
|
||||||
let handler = GooseAcpHandler { agent };
|
let handler = GooseAcpHandler { agent };
|
||||||
|
|
||||||
AgentToClient::builder()
|
AgentToClient::builder()
|
||||||
@@ -1299,6 +1311,7 @@ where
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(builtins: Vec<String>) -> Result<()> {
|
pub async fn run(builtins: Vec<String>) -> Result<()> {
|
||||||
|
|||||||
@@ -41,13 +41,11 @@ impl HttpState {
|
|||||||
|
|
||||||
let acp_session_id = uuid::Uuid::new_v4().to_string();
|
let acp_session_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
let handle = tokio::spawn(async move {
|
|
||||||
let read_stream = ReceiverToAsyncRead::new(to_agent_rx);
|
let read_stream = ReceiverToAsyncRead::new(to_agent_rx);
|
||||||
let write_stream = SenderToAsyncWrite::new(from_agent_tx);
|
let write_stream = SenderToAsyncWrite::new(from_agent_tx);
|
||||||
|
let fut = crate::server::serve(agent, read_stream.compat(), write_stream.compat_write());
|
||||||
if let Err(e) =
|
let handle = tokio::spawn(async move {
|
||||||
crate::server::serve(agent, read_stream.compat(), write_stream.compat_write()).await
|
if let Err(e) = fut.await {
|
||||||
{
|
|
||||||
error!("ACP session error: {}", e);
|
error!("ACP session error: {}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,13 +36,11 @@ impl WsState {
|
|||||||
|
|
||||||
let acp_session_id = uuid::Uuid::new_v4().to_string();
|
let acp_session_id = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
let handle = tokio::spawn(async move {
|
|
||||||
let read_stream = ReceiverToAsyncRead::new(to_agent_rx);
|
let read_stream = ReceiverToAsyncRead::new(to_agent_rx);
|
||||||
let write_stream = SenderToAsyncWrite::new(from_agent_tx);
|
let write_stream = SenderToAsyncWrite::new(from_agent_tx);
|
||||||
|
let fut = crate::server::serve(agent, read_stream.compat(), write_stream.compat_write());
|
||||||
if let Err(e) =
|
let handle = tokio::spawn(async move {
|
||||||
crate::server::serve(agent, read_stream.compat(), write_stream.compat_write()).await
|
if let Err(e) = fut.await {
|
||||||
{
|
|
||||||
error!("ACP WebSocket session error: {}", e);
|
error!("ACP WebSocket session error: {}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user