How do you send signals from client to server?
Unanswered
Alexandru posted this in #help-forum
I want to abort a server action from the client via a button basically...
38 Replies
Hey @Arinji
Sup
Ok so
Hmm, it's not possible to directly stop a server action in the middle of it's invocation
What are you trying to do anyways
You can't just stop an ongoing process, be it an api route or a server action
Well, I was actually using
ai lib and the conte xt is very much the same as this template: https://github.com/vercel/ai-chatbot/blob/main/lib/chat/actions.tsx#L109simple server action that further calls the streamUI function from the lib.
Interestingly enough, this function has an abort param. Ref: https://sdk.vercel.ai/docs/reference/ai-sdk-rsc/stream-ui
But then how do you control it if not from the UI (an interface is that I am building)
And what I am specifically doing is a 'stop' button that stops the llm from outputting text
As far as I know, there's no way to abort a server action
Well, theoretically you could have multiple server actions, one for starting an action, one for stopping an action. combining this with some state management and you'll have what your looking for.
Super simple example (its not exactly 'cancelling' a server action, kind of a bypass), this code will not directly work bcuz of multiple reasons, its just for demonstration purposes:
Super simple example (its not exactly 'cancelling' a server action, kind of a bypass), this code will not directly work bcuz of multiple reasons, its just for demonstration purposes:
"use server"
let isRunning = false;
let conditionX = false;
let controller: ReadableStreamDefaultController<string> | null = null;
export function startAction(streamController: ReadableStreamDefaultController<string>) {
if (conditionX) {
isRunning = true;
controller = streamController;
streamText();
} else {
streamController.close();
}
}
export function stopAction() {
isRunning = false;
if (controller) {
controller.close();
controller = null;
}
}
function streamText() { // for example purposes
if (isRunning && controller) {
controller.enqueue('Streaming some text...\n');
setTimeout(streamText, 1000); // Stream text every second
}
}It's a good point and I will try it myself. I am worried if the requests would always go to the same server, and perhaps there is a one-function cleaner solution for this.
Maybe
A signal is not serializable
You could have a table in a db, with a id to an action, and before the server makes any changes to the db it checks if there exists any like record in the db to stop?
Well, the point of the 'stop generating' button is to also reduce costs (since the response so for isn't appropriate) and to end it right there.
The db is updated after the entire message is streamed to client
Tell me if that's what you suggested, cause I think it doesn't end it on the spot
@Pearls Well, theoretically you could have multiple server actions, one for starting an action, one for stopping an action. combining this with some state management and you'll have what your looking for.
Super simple example (its not exactly 'cancelling' a server action, kind of a bypass), this code will not directly work bcuz of multiple reasons, its just for demonstration purposes:
tsx
"use server"
let isRunning = false;
let conditionX = false;
let controller: ReadableStreamDefaultController<string> | null = null;
export function startAction(streamController: ReadableStreamDefaultController<string>) {
if (conditionX) {
isRunning = true;
controller = streamController;
streamText();
} else {
streamController.close();
}
}
export function stopAction() {
isRunning = false;
if (controller) {
controller.close();
controller = null;
}
}
function streamText() { // for example purposes
if (isRunning && controller) {
controller.enqueue('Streaming some text...\n');
setTimeout(streamText, 1000); // Stream text every second
}
}
this is a work around, but its still a pretty clean solution.
@Alexandru If you dont need any more help, please mark a solution.
@Alexandru If you dont need any more help, please mark a solution.
This is what I understood from what you suggested. However, on abortUserMessage the controller aborts as it should, but then the entire app crashes and the client disconnects from the call with the error:
This operation was aborted. Here's the main bits of my code:Im not sure why that happens but maybe try a final cleanup when everything is done.
Add this after the catch statement.
Add this after the catch statement.
...
abortSignal: abortController.signal
})
} catch (error) {
console.log(error)
} finally {
abortController = null; // Ensure the abort controller is reset
}Unfortunately, this doesn't help it. This is the error I am getting and in my opinion it makes no sense.. The abort action is intentional, it is meant to interrupt the streamUI function and I can see that at the right moment in the logs of my LLM API local provider (i.e. LMStudio). The error is vague and it feels like it is meaningless, but there might be something deeper going on that prevents this fundamentally.
@Alexandru Click to see attachment
I'm waiting to hear other ideas and would really appreciate any opinions you have. The issue stays that I am trying to provide an abortSignal to cancel or abort streaming UI on the server from the client.
The source code looks something like this, but it's mostly what you get with the chatbot Vercel template.
The source code looks something like this, but it's mostly what you get with the chatbot Vercel template.
Hmm, do you have react strict mode enabled? (its enabled by default)
I do. However, I don't see any difference with
reactStrictMode: false either, with the current implementation.well, its calling the abort twice, the second time it tries to do it theres a chance that it shows the error that you are getting because the abortController is already aborted. you know what i mean? (https://tryitands.ee/ set reactStrictMode to false )
I doubt that's the case since strictMode is about double rendering and not double function calling (that are on the server)
@Alexandru I doubt that's the case since strictMode is about double rendering and not double function calling (that are on the server)
Mini Rex
in my opinion, u are right
I've come across an issue on Vercel's ai package that hasn't been addressed yet, but it's closely tied to Nextjs functionality. I truly believe there must be a way to enable the implementation of the
https://github.com/vercel/ai/issues/1122
stop button.https://github.com/vercel/ai/issues/1122
Let me know what you think and how we can continue forward