Skip to content

Commit c9a46d8

Browse files
feat: add WebSocket, Streaming, and Inference protocol config UI
Complete the protocol-specific configuration forms for all service types. WebSocket (ping interval, max message size, max connections), Streaming (format, keepalive, duration, buffer), and Inference (provider, token limits, budget, streaming toggle) are now configurable via the create, edit, and show pages.
1 parent f5660df commit c9a46d8

4 files changed

Lines changed: 703 additions & 10 deletions

File tree

lib/sentinel_cp_web/live/services_live/edit.ex

Lines changed: 230 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,16 @@ defmodule SentinelCpWeb.ServicesLive.Edit do
5656
(service.graphql != %{} && service.graphql != nil),
5757
show_grpc:
5858
service.service_type == "grpc" ||
59-
(service.grpc != %{} && service.grpc != nil)
59+
(service.grpc != %{} && service.grpc != nil),
60+
show_websocket:
61+
service.service_type == "websocket" ||
62+
(service.websocket != %{} && service.websocket != nil),
63+
show_streaming:
64+
service.service_type == "streaming" ||
65+
(service.streaming != %{} && service.streaming != nil),
66+
show_inference:
67+
service.service_type == "inference" ||
68+
(service.inference != %{} && service.inference != nil)
6069
)}
6170
else
6271
_ ->
@@ -75,7 +84,10 @@ defmodule SentinelCpWeb.ServicesLive.Edit do
7584
assign(socket,
7685
service_type: type,
7786
show_graphql: type == "graphql",
78-
show_grpc: type == "grpc"
87+
show_grpc: type == "grpc",
88+
show_websocket: type == "websocket",
89+
show_streaming: type == "streaming",
90+
show_inference: type == "inference"
7991
)}
8092
end
8193

@@ -153,6 +165,9 @@ defmodule SentinelCpWeb.ServicesLive.Edit do
153165

154166
attrs = maybe_put_map(attrs, :graphql, params, "graphql")
155167
attrs = maybe_put_map(attrs, :grpc, params, "grpc")
168+
attrs = maybe_put_map(attrs, :websocket, params, "websocket")
169+
attrs = maybe_put_map(attrs, :streaming, params, "streaming")
170+
attrs = maybe_put_map(attrs, :inference, params, "inference")
156171
attrs = maybe_put_map(attrs, :retry, params, "retry")
157172
attrs = maybe_put_map(attrs, :cache, params, "cache")
158173
attrs = maybe_put_map(attrs, :rate_limit, params, "rate_limit")
@@ -358,7 +373,10 @@ defmodule SentinelCpWeb.ServicesLive.Edit do
358373
</div>
359374
360375
<%!-- Protocol Configuration --%>
361-
<div :if={@service_type in ~w(graphql grpc)} class="divider text-xs text-base-content/50">
376+
<div
377+
:if={@service_type in ~w(graphql grpc websocket streaming inference)}
378+
class="divider text-xs text-base-content/50"
379+
>
362380
Protocol Configuration
363381
</div>
364382
@@ -501,6 +519,215 @@ defmodule SentinelCpWeb.ServicesLive.Edit do
501519
</div>
502520
</div>
503521
522+
<div
523+
:if={@service_type == "websocket" or @show_websocket}
524+
data-testid="websocket-config"
525+
>
526+
<button
527+
type="button"
528+
phx-click="toggle_section"
529+
phx-value-section="websocket"
530+
class="btn btn-ghost btn-xs"
531+
>
532+
{if @show_websocket, do: "▼", else: "▶"} WebSocket Settings
533+
</button>
534+
<div :if={@show_websocket} class="ml-4 mt-2 space-y-2">
535+
<div class="form-control">
536+
<label class="label">
537+
<span class="label-text text-xs">Ping Interval (seconds)</span>
538+
</label>
539+
<input
540+
type="number"
541+
name="websocket[ping_interval]"
542+
class="input input-bordered input-xs w-24"
543+
placeholder="30"
544+
min="1"
545+
value={@service.websocket["ping_interval"]}
546+
/>
547+
</div>
548+
<div class="form-control">
549+
<label class="label">
550+
<span class="label-text text-xs">Max Message Size (bytes)</span>
551+
</label>
552+
<input
553+
type="number"
554+
name="websocket[max_message_size]"
555+
class="input input-bordered input-xs w-32"
556+
placeholder="65536"
557+
min="1"
558+
value={@service.websocket["max_message_size"]}
559+
/>
560+
</div>
561+
<div class="form-control">
562+
<label class="label">
563+
<span class="label-text text-xs">Max Connections</span>
564+
</label>
565+
<input
566+
type="number"
567+
name="websocket[max_connections]"
568+
class="input input-bordered input-xs w-28"
569+
placeholder="10000"
570+
min="1"
571+
value={@service.websocket["max_connections"]}
572+
/>
573+
</div>
574+
</div>
575+
</div>
576+
577+
<div
578+
:if={@service_type == "streaming" or @show_streaming}
579+
data-testid="streaming-config"
580+
>
581+
<button
582+
type="button"
583+
phx-click="toggle_section"
584+
phx-value-section="streaming"
585+
class="btn btn-ghost btn-xs"
586+
>
587+
{if @show_streaming, do: "▼", else: "▶"} Streaming Settings
588+
</button>
589+
<div :if={@show_streaming} class="ml-4 mt-2 space-y-2">
590+
<div class="form-control">
591+
<label class="label"><span class="label-text text-xs">Format</span></label>
592+
<select name="streaming[format]" class="select select-bordered select-xs w-40">
593+
<option value=""></option>
594+
<option value="sse" selected={@service.streaming["format"] == "sse"}>SSE</option>
595+
<option value="ndjson" selected={@service.streaming["format"] == "ndjson"}>
596+
NDJSON
597+
</option>
598+
<option value="chunked" selected={@service.streaming["format"] == "chunked"}>
599+
Chunked
600+
</option>
601+
</select>
602+
</div>
603+
<div class="form-control">
604+
<label class="label">
605+
<span class="label-text text-xs">Keepalive Interval (seconds)</span>
606+
</label>
607+
<input
608+
type="number"
609+
name="streaming[keepalive_interval]"
610+
class="input input-bordered input-xs w-24"
611+
placeholder="15"
612+
min="1"
613+
value={@service.streaming["keepalive_interval"]}
614+
/>
615+
</div>
616+
<div class="form-control">
617+
<label class="label">
618+
<span class="label-text text-xs">Max Connection Duration (seconds)</span>
619+
</label>
620+
<input
621+
type="number"
622+
name="streaming[max_connection_duration]"
623+
class="input input-bordered input-xs w-28"
624+
placeholder="3600"
625+
min="1"
626+
value={@service.streaming["max_connection_duration"]}
627+
/>
628+
</div>
629+
<div class="form-control">
630+
<label class="label">
631+
<span class="label-text text-xs">Buffer Size (bytes)</span>
632+
</label>
633+
<input
634+
type="number"
635+
name="streaming[buffer_size]"
636+
class="input input-bordered input-xs w-28"
637+
placeholder="1024"
638+
min="1"
639+
value={@service.streaming["buffer_size"]}
640+
/>
641+
</div>
642+
</div>
643+
</div>
644+
645+
<div
646+
:if={@service_type == "inference" or @show_inference}
647+
data-testid="inference-config"
648+
>
649+
<button
650+
type="button"
651+
phx-click="toggle_section"
652+
phx-value-section="inference"
653+
class="btn btn-ghost btn-xs"
654+
>
655+
{if @show_inference, do: "▼", else: "▶"} Inference Settings
656+
</button>
657+
<div :if={@show_inference} class="ml-4 mt-2 space-y-2">
658+
<div class="form-control">
659+
<label class="label"><span class="label-text text-xs">Provider</span></label>
660+
<select name="inference[provider]" class="select select-bordered select-xs w-40">
661+
<option value=""></option>
662+
<option value="openai" selected={@service.inference["provider"] == "openai"}>
663+
OpenAI
664+
</option>
665+
<option
666+
value="anthropic"
667+
selected={@service.inference["provider"] == "anthropic"}
668+
>
669+
Anthropic
670+
</option>
671+
<option value="generic" selected={@service.inference["provider"] == "generic"}>
672+
Generic
673+
</option>
674+
</select>
675+
</div>
676+
<div class="form-control">
677+
<label class="label">
678+
<span class="label-text text-xs">Tokens per Minute</span>
679+
</label>
680+
<input
681+
type="number"
682+
name="inference[tokens_per_minute]"
683+
class="input input-bordered input-xs w-32"
684+
placeholder="100000"
685+
min="1"
686+
value={@service.inference["tokens_per_minute"]}
687+
/>
688+
</div>
689+
<div class="form-control">
690+
<label class="label">
691+
<span class="label-text text-xs">Monthly Token Budget</span>
692+
</label>
693+
<input
694+
type="number"
695+
name="inference[monthly_token_budget]"
696+
class="input input-bordered input-xs w-32"
697+
placeholder="10000000"
698+
min="1"
699+
value={@service.inference["monthly_token_budget"]}
700+
/>
701+
</div>
702+
<div class="form-control">
703+
<label class="label">
704+
<span class="label-text text-xs">Budget Alert Threshold (%)</span>
705+
</label>
706+
<input
707+
type="number"
708+
name="inference[budget_alert_threshold]"
709+
class="input input-bordered input-xs w-24"
710+
placeholder="80"
711+
min="1"
712+
max="100"
713+
value={@service.inference["budget_alert_threshold"]}
714+
/>
715+
</div>
716+
<div class="form-control">
717+
<label class="label cursor-pointer gap-2 justify-start">
718+
<input
719+
type="checkbox"
720+
name="inference[streaming_enabled]"
721+
value="true"
722+
checked={@service.inference["streaming_enabled"] in ["true", true]}
723+
class="checkbox checkbox-xs"
724+
/>
725+
<span class="label-text text-xs">Enable Streaming</span>
726+
</label>
727+
</div>
728+
</div>
729+
</div>
730+
504731
<%!-- Advanced Sections --%>
505732
<div class="divider text-xs text-base-content/50">Advanced Settings</div>
506733

0 commit comments

Comments
 (0)