unity-websocket icon indicating copy to clipboard operation
unity-websocket copied to clipboard

WebHooks example?

Open yosun opened this issue 7 months ago • 2 comments

Can you provide a webhooks example?

yosun avatar May 25 '25 11:05 yosun

Hi. Can you provide more details on what you're looking for or what problem you faced that led to this issue?

Webhook is just a synonym for one service providing a URL to another to make an HTTP request. It is a fundamentally different use case from a websocket, so I'm curious how this came up.

mikerochip avatar May 25 '25 18:05 mikerochip

Ah, so for async server queries, such as generative AI processing, typically a webhook URL is provided for whenever the server completes a task. I had thought websockets are one such way for any Unity app to have a way for the server to "callback" on it as a webhook?

yosun avatar May 26 '25 02:05 yosun

OK, thank you for the clarification.

Few things: it sounds like you have a server-side orchestration problem and a terminology problem, but there isn't an actual issue with this repo and writing up a sample would be a blog-post-length of content that is mostly unrelated to how to use this repo/package, so I'm going to close out this issue and give some hopefully helpful advice.

As far as terminology: a server pushing a message to a client via a websocket is not referred to as a webhook. That's usually referred to as a one-way push, one-way communication, notification, or push message. There isn't really an official term for this AFAIK, but I've most often heard one-way push. A webhook is just a term for a convention where some service preconfigures an HTTP URL endpoint for another, specific service to invoke as if it were a callback. So the term webhook in your last sentence isn't the right term for 2 reasons: it's not server-to-server and it's not an HTTP request. The underlying tech for webhooks is just REST API over HTTP (meaning, anything on the internet could call your "webhook" by making an http request vs a websocket connection is a direct connection between two machines, so only those two machines can send messages to each other). Hope that clears things up a bit!

Now reading between the lines of what you wrote, it seems the main thing you're blocked on is figuring out how a client that sends a request can go through several server hops, with one of those hops including a delay (your gen AI service that calls your webhook), and somehow have the results of the original client request make it back to the client that made the request.

You'll need these pieces of infrastructure in place:

  1. Client with websocket capability (that's what this package does)
  2. Custom server with capability to accept incoming websocket connections and send messages to them
  3. Your server needs to represent incoming connections as objects and needs to use unique IDs to be able to correlate an ID to a connection, which is what will enable your system to survive the delay from the webhook.
    • IDs can be handled in a dozen or more ways
    • One simple solution is to generate a UUID when you receive an incoming connection
    • You can also insert an authentication mechanism here and have your client send up a User ID to be your correlation ID
  4. The next step is super dependent on your infrastructure setup. Your options vary wildly depending on whether you are managing your own servers vs using a managed service, where your infrastructure is hosted, and what storage solutions are available.
    1. Your server should make an in-memory map of ID -> connection. This mapping MUST be able to handle concurrent access if you're using a web framework that handles requests in threads.
    2. If you have multiple servers, then this becomes a lot harder
      • Only one of your servers is holding on to the connection, and that's the one that needs to respond to your webhook.
      • How you do this is once again very dependent on your infrastructure. One general option is to have whatever service is responding to the webhook add a message into a message queue when it handles the webhook, then have your servers read from that queue, and only the server that has the connection should handle the message from the queue.
  5. Your server needs to manage connection mappings so that when connections drop, you delete the corresponding ID -> connection entries.
  6. Server needs to persist the results of the gen AI request somewhere. This is also pretty open ended since it highly depends on what content you're generating. This could be stored in object storage, a database, or both. If it's small enough to fit inside the payload of the webhook and you don't care if it gets dropped, then you don't need persistence.

This is the general strategy:

  1. First your client establishes a connection to your server
  2. Your server will then generate an ID and store the ID -> connection mapping (OR your client tells your server what ID to use for this mapping)
  3. The client then sends your server some gen Ai request
  4. The server will then generate a new request for your gen AI service, enriched with the correlation ID you generated for your connection, then the server sends the request to the gen AI service
  5. Some time later, your webhook gets called
  6. Presumably your gen AI service sent you back the correlation ID you had sent it earlier.
  7. If you have a multi-server setup, this is the hard part where you have to determine which server has the connection so only that one server handles the request (see above)
  8. Optionally the server handling the webhook needs to persist the content of the gen AI call somewhere. If the webhook contains the content in its payload and you don't care if the content gets dropped before reaching the client, then you can skip this persistence step.
  9. Server then needs to look up the correlation ID in its map so you can make sure the connection is still alive. This is where it's super important to make sure dropped connections get dropped from the map.
  10. Server then does a one-way push to the client via the websocket connection, EITHER with the content of the webhook payload OR just telling the client some ID it can use to send the server a subsequent request to look up the results from the gen AI service.

I'm gonna stop there. It sounded like the primary place you got stuck is the multi-server orchestration so that's mostly what I covered. Hope this helped!

mikerochip avatar May 26 '25 05:05 mikerochip

Right, so, sticking to just the websockets portion, afaik, that's step 10 (to avoid needing to poll every N intervals)...

And this brings me right back to the request for an example - not on everything else, just on step 10.

yosun avatar May 26 '25 06:05 yosun

Hi. Sorry, I guess I'm still not clear what you're looking for? It sounds like either there is a misunderstanding of how websocket vs http clients work? or you're still having trouble with orchestrating the server side, ... or both?

If you're just looking for sample server code, I have a test server linked in the README already but servers are out of scope of this package because servers can be written however you like. I suppose if you really want an example of how to manage websocket connection mappings on a server you can open an issue there.

The only time this package enters the picture in the flow I described is when the package gets an event with a message in it, which is already covered in the samples. So if you're just looking for how to receive a message then that's it.

FYI there's no such thing as a poll with websocket clients. The way websocket works is it keeps a connection open so a server can choose to send data whenever it wants. The purpose of it is to send and receive asymmetrical messages in an open connection vs http where a connection is opened every time you send a request and closed when you receive a response, that's why polling is required there to receive delayed data with http clients.

mikerochip avatar May 26 '25 15:05 mikerochip

Also I'm not sure if this is what you're getting at, but if you're trying to avoid writing a custom server because you're hoping that your gen AI service can directly call in to your Unity client, then that's not possible. A webhook is a URL which means it needs to be publicly available on the internet and it needs to be handled by a web server. You can't somehow have your gen AI service call directly into your client, you need to intercept that with a custom server.

mikerochip avatar May 26 '25 15:05 mikerochip

BTW, all of those steps I described above can't be skipped. That's how data would need to flow through a system you're describing. If you want to avoid writing a custom server then all of those steps would need to be moved inside your gen AI service. So I guess the best advice I can offer you at this point is to make a request to your gen AI service to add websocket server support so you don't have to poll for results with purely http.

mikerochip avatar May 26 '25 15:05 mikerochip