mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-28 09:08:54 -04:00
Add the GEM-X native backend with live pose and root displacement output, bounded duplex WebSocket streaming, session authorization, metrics and usage accounting. Share the embedded bidirectional RPC channel adapter across streaming backends.
Include backend build and gallery integration, API documentation, schemas and tests.
Assisted-by: Codex:gpt-6
Signed-off-by: Richard Palethorpe <io@richiejp.com>
(cherry picked from commit 016dc3d71a)
130 lines
3.4 KiB
Go
130 lines
3.4 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package grpc
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"sync"
|
|
|
|
"google.golang.org/grpc/metadata"
|
|
)
|
|
|
|
// channelStream carries in-process bidirectional RPCs. Publishing the terminal
|
|
// error before closing responses lets callers drain the response tail reliably.
|
|
type channelStream[Request, Response any] struct {
|
|
ctx context.Context
|
|
requests chan Request
|
|
responses chan Response
|
|
done chan struct{}
|
|
err error
|
|
}
|
|
|
|
type channelStreamServer[Request, Response any] struct {
|
|
*channelStream[Request, Response]
|
|
}
|
|
|
|
type channelStreamClient[Request, Response any] struct {
|
|
*channelStream[Request, Response]
|
|
sendMu sync.Mutex
|
|
closed bool
|
|
cleanup *streamCleanup
|
|
}
|
|
|
|
func newChannelStream[Request, Response any](ctx context.Context, capacity int, serve func(*channelStreamServer[Request, Response]) error) *channelStreamClient[Request, Response] {
|
|
stream := &channelStream[Request, Response]{ctx: ctx, requests: make(chan Request, capacity), responses: make(chan Response, capacity), done: make(chan struct{})}
|
|
client := &channelStreamClient[Request, Response]{channelStream: stream, cleanup: newStreamCleanup(ctx, nil)}
|
|
go func() {
|
|
stream.err = serve(&channelStreamServer[Request, Response]{stream})
|
|
close(stream.done)
|
|
close(stream.responses)
|
|
}()
|
|
return client
|
|
}
|
|
|
|
func (s *channelStream[Request, Response]) Context() context.Context { return s.ctx }
|
|
func (s *channelStreamServer[Request, Response]) Send(response Response) error {
|
|
select {
|
|
case s.responses <- response:
|
|
return nil
|
|
case <-s.ctx.Done():
|
|
return s.ctx.Err()
|
|
}
|
|
}
|
|
func (s *channelStreamServer[Request, Response]) Recv() (Request, error) {
|
|
select {
|
|
case request, ok := <-s.requests:
|
|
if ok {
|
|
return request, nil
|
|
}
|
|
case <-s.ctx.Done():
|
|
var zero Request
|
|
return zero, s.ctx.Err()
|
|
}
|
|
var zero Request
|
|
return zero, io.EOF
|
|
}
|
|
func (s *channelStreamServer[Request, Response]) SetHeader(metadata.MD) error { return nil }
|
|
func (s *channelStreamServer[Request, Response]) SendHeader(metadata.MD) error { return nil }
|
|
func (s *channelStreamServer[Request, Response]) SetTrailer(metadata.MD) {}
|
|
func (s *channelStreamServer[Request, Response]) SendMsg(message any) error {
|
|
if response, ok := message.(Response); ok {
|
|
return s.Send(response)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Generated bidirectional handlers use typed Recv directly.
|
|
func (s *channelStreamServer[Request, Response]) RecvMsg(any) error { return nil }
|
|
|
|
func (c *channelStreamClient[Request, Response]) AddCleanup(fn func()) { c.cleanup.add(fn) }
|
|
func (c *channelStreamClient[Request, Response]) Send(request Request) error {
|
|
c.sendMu.Lock()
|
|
defer c.sendMu.Unlock()
|
|
if c.closed {
|
|
return io.EOF
|
|
}
|
|
select {
|
|
case <-c.done:
|
|
return io.EOF
|
|
case <-c.ctx.Done():
|
|
return c.ctx.Err()
|
|
default:
|
|
}
|
|
select {
|
|
case c.requests <- request:
|
|
return nil
|
|
case <-c.done:
|
|
return io.EOF
|
|
case <-c.ctx.Done():
|
|
return c.ctx.Err()
|
|
}
|
|
}
|
|
func (c *channelStreamClient[Request, Response]) Recv() (Response, error) {
|
|
var zero Response
|
|
select {
|
|
case response, ok := <-c.responses:
|
|
if ok {
|
|
return response, nil
|
|
}
|
|
c.cleanup.finish()
|
|
if c.err != nil {
|
|
return zero, c.err
|
|
}
|
|
return zero, io.EOF
|
|
case <-c.ctx.Done():
|
|
c.cleanup.finish()
|
|
return zero, c.ctx.Err()
|
|
}
|
|
}
|
|
|
|
// Half-close only the request side; the server may still produce responses.
|
|
func (c *channelStreamClient[Request, Response]) CloseSend() error {
|
|
c.sendMu.Lock()
|
|
defer c.sendMu.Unlock()
|
|
if !c.closed {
|
|
c.closed = true
|
|
close(c.requests)
|
|
}
|
|
return nil
|
|
}
|