정리: edge 앱의 main.go에서 불필요한 코드 정리 및 콘솔 모듈 분리

This commit is contained in:
toki 2026-05-03 13:09:52 +09:00
parent b4c6550eab
commit 6a2b4dfa46
2 changed files with 169 additions and 160 deletions

View file

@ -0,0 +1,169 @@
package main
import (
"bufio"
"context"
"fmt"
"io"
"strings"
"time"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/structpb"
edgenode "iop/apps/edge/internal/node"
"iop/apps/edge/internal/transport"
"iop/packages/config"
"iop/packages/observability"
iop "iop/proto/gen/iop"
)
func runConsole(ctx context.Context, cfg *config.EdgeConfig, in io.Reader, out io.Writer) error {
logger, err := observability.NewLogger(cfg.Logging.Level, cfg.Logging.Pretty)
if err != nil {
return err
}
defer func() { _ = logger.Sync() }()
registry := edgenode.NewRegistry()
nodeStore, err := edgenode.LoadFromConfig(cfg.Nodes)
if err != nil {
return fmt.Errorf("edge: seed node store: %w", err)
}
server, err := transport.NewServer(cfg.Server.Listen, registry, nodeStore, logger)
if err != nil {
return err
}
events := make(chan *iop.RunEvent, 256)
server.SetRunEventHandler(func(event *iop.RunEvent) {
select {
case events <- event:
default:
logger.Warn("run event dropped", zap.String("run_id", event.GetRunId()))
}
})
if err := server.Start(ctx); err != nil {
return err
}
defer func() { _ = server.Stop() }()
go func() {
if err := observability.ServeMetrics(cfg.Metrics.Port); err != nil {
logger.Warn("metrics server exited", zap.Error(err))
}
}()
fmt.Fprintf(out, "IOP Edge console listening on %s\n", cfg.Server.Listen)
fmt.Fprintf(out, "Console target adapter=%s model=%s\n", cfg.Console.Adapter, cfg.Console.Model)
fmt.Fprintln(out, "Start node.sh on another host, then type a message here.")
fmt.Fprintln(out, "Commands: /nodes, /exit")
scanner := bufio.NewScanner(in)
for {
fmt.Fprint(out, "edge> ")
if !scanner.Scan() {
fmt.Fprintln(out)
return scanner.Err()
}
message := strings.TrimSpace(scanner.Text())
switch strings.ToLower(message) {
case "":
continue
case "/exit", "/quit", "exit", "quit":
fmt.Fprintln(out, "bye")
return nil
case "/nodes":
printNodes(out, registry)
default:
if err := sendConsoleRun(ctx, registry, events, out, cfg.Console.Adapter, cfg.Console.Model, message); err != nil {
fmt.Fprintf(out, "error: %v\n", err)
}
}
}
}
func printNodes(out io.Writer, registry *edgenode.Registry) {
nodes := registry.All()
if len(nodes) == 0 {
fmt.Fprintln(out, "no nodes connected")
return
}
for _, node := range nodes {
fmt.Fprintf(out, "- %s (%s)\n", node.NodeID, node.Alias)
}
}
func sendConsoleRun(ctx context.Context, registry *edgenode.Registry, events <-chan *iop.RunEvent, out io.Writer, adapter, model, message string) error {
entry, err := registry.Pick()
if err != nil {
return err
}
input, err := structpb.NewStruct(map[string]any{"prompt": message})
if err != nil {
return err
}
runID := fmt.Sprintf("manual-%d", time.Now().UnixNano())
req := &iop.RunRequest{
RunId: runID,
Adapter: adapter,
Model: model,
Input: input,
TimeoutSec: 30,
Metadata: map[string]string{
"source": "edge-console",
},
}
nodeAlias := entry.Alias
fmt.Fprintf(out, "[edge] sent run_id=%s node=%s adapter=%s model=%s\n", runID, nodeAlias, adapter, model)
if err := entry.Client.Send(req); err != nil {
return err
}
timer := time.NewTimer(35 * time.Second)
defer timer.Stop()
var response strings.Builder
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
return fmt.Errorf("timed out waiting for node response")
case event := <-events:
if event.GetRunId() != runID {
continue
}
switch event.GetType() {
case "start":
fmt.Fprintf(out, "[node-%s-event] start run_id=%s\n", nodeAlias, runID)
case "delta":
response.WriteString(event.GetDelta())
case "complete":
fmt.Fprintf(out, "[node-%s-event] complete run_id=%s detail=%q\n", nodeAlias, runID, event.GetMessage())
printNodeMessage(out, nodeAlias, response.String())
return nil
case "error":
fmt.Fprintf(out, "[node-%s-event] error run_id=%s detail=%q\n", nodeAlias, runID, event.GetError())
printNodeMessage(out, nodeAlias, response.String())
return fmt.Errorf("node reported error")
default:
fmt.Fprintf(out, "[node-%s-event] %s run_id=%s detail=%q\n", nodeAlias, event.GetType(), runID, event.GetMessage())
}
}
}
}
func printNodeMessage(out io.Writer, alias, message string) {
message = strings.TrimSpace(message)
if message == "" {
fmt.Fprintf(out, "[node-%s-message] <empty>\n", alias)
return
}
fmt.Fprintf(out, "[node-%s-message] %s\n", alias, message)
}

View file

@ -1,25 +1,15 @@
package main
import (
"bufio"
"context"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"go.uber.org/fx"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/structpb"
"iop/apps/edge/internal/bootstrap"
edgenode "iop/apps/edge/internal/node"
"iop/apps/edge/internal/transport"
"iop/packages/config"
"iop/packages/observability"
iop "iop/proto/gen/iop"
)
var cfgFile string
@ -69,153 +59,3 @@ func consoleCmd() *cobra.Command {
},
}
}
func runConsole(ctx context.Context, cfg *config.EdgeConfig, in io.Reader, out io.Writer) error {
logger, err := observability.NewLogger(cfg.Logging.Level, cfg.Logging.Pretty)
if err != nil {
return err
}
defer func() { _ = logger.Sync() }()
registry := edgenode.NewRegistry()
nodeStore, err := edgenode.LoadFromConfig(cfg.Nodes)
if err != nil {
return fmt.Errorf("edge: seed node store: %w", err)
}
server, err := transport.NewServer(cfg.Server.Listen, registry, nodeStore, logger)
if err != nil {
return err
}
events := make(chan *iop.RunEvent, 256)
server.SetRunEventHandler(func(event *iop.RunEvent) {
select {
case events <- event:
default:
logger.Warn("run event dropped", zap.String("run_id", event.GetRunId()))
}
})
if err := server.Start(ctx); err != nil {
return err
}
defer func() { _ = server.Stop() }()
go func() {
if err := observability.ServeMetrics(cfg.Metrics.Port); err != nil {
logger.Warn("metrics server exited", zap.Error(err))
}
}()
fmt.Fprintf(out, "IOP Edge console listening on %s\n", cfg.Server.Listen)
fmt.Fprintf(out, "Console target adapter=%s model=%s\n", cfg.Console.Adapter, cfg.Console.Model)
fmt.Fprintln(out, "Start node.sh on another host, then type a message here.")
fmt.Fprintln(out, "Commands: /nodes, /exit")
scanner := bufio.NewScanner(in)
for {
fmt.Fprint(out, "edge> ")
if !scanner.Scan() {
fmt.Fprintln(out)
return scanner.Err()
}
message := strings.TrimSpace(scanner.Text())
switch strings.ToLower(message) {
case "":
continue
case "/exit", "/quit", "exit", "quit":
fmt.Fprintln(out, "bye")
return nil
case "/nodes":
printNodes(out, registry)
default:
if err := sendConsoleRun(ctx, registry, events, out, cfg.Console.Adapter, cfg.Console.Model, message); err != nil {
fmt.Fprintf(out, "error: %v\n", err)
}
}
}
}
func printNodes(out io.Writer, registry *edgenode.Registry) {
nodes := registry.All()
if len(nodes) == 0 {
fmt.Fprintln(out, "no nodes connected")
return
}
for _, node := range nodes {
fmt.Fprintf(out, "- %s (%s)\n", node.NodeID, node.Alias)
}
}
func sendConsoleRun(ctx context.Context, registry *edgenode.Registry, events <-chan *iop.RunEvent, out io.Writer, adapter, model, message string) error {
entry, err := registry.Pick()
if err != nil {
return err
}
input, err := structpb.NewStruct(map[string]any{"prompt": message})
if err != nil {
return err
}
runID := fmt.Sprintf("manual-%d", time.Now().UnixNano())
req := &iop.RunRequest{
RunId: runID,
Adapter: adapter,
Model: model,
Input: input,
TimeoutSec: 30,
Metadata: map[string]string{
"source": "edge-console",
},
}
nodeAlias := entry.Alias
fmt.Fprintf(out, "[edge] sent run_id=%s node=%s adapter=%s model=%s\n", runID, nodeAlias, adapter, model)
if err := entry.Client.Send(req); err != nil {
return err
}
timer := time.NewTimer(35 * time.Second)
defer timer.Stop()
var response strings.Builder
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
return fmt.Errorf("timed out waiting for node response")
case event := <-events:
if event.GetRunId() != runID {
continue
}
switch event.GetType() {
case "start":
fmt.Fprintf(out, "[node-%s-event] start run_id=%s\n", nodeAlias, runID)
case "delta":
response.WriteString(event.GetDelta())
case "complete":
fmt.Fprintf(out, "[node-%s-event] complete run_id=%s detail=%q\n", nodeAlias, runID, event.GetMessage())
printNodeMessage(out, nodeAlias, response.String())
return nil
case "error":
fmt.Fprintf(out, "[node-%s-event] error run_id=%s detail=%q\n", nodeAlias, runID, event.GetError())
printNodeMessage(out, nodeAlias, response.String())
return fmt.Errorf("node reported error")
default:
fmt.Fprintf(out, "[node-%s-event] %s run_id=%s detail=%q\n", nodeAlias, event.GetType(), runID, event.GetMessage())
}
}
}
}
func printNodeMessage(out io.Writer, alias, message string) {
message = strings.TrimSpace(message)
if message == "" {
fmt.Fprintf(out, "[node-%s-message] <empty>\n", alias)
return
}
fmt.Fprintf(out, "[node-%s-message] %s\n", alias, message)
}