1 import Base 2 import ./setload.bend as Setload 3 import ./store/base.bend as SBase 4 import ./store/seq.bend as SSeq 5 import ./json.bend as Json 6 import ./fs.bend as Fs 7 import ./act.bend as Act 8 import ./cmd.bend as Cmd 9 import ./settings.bend as Settings 10 import ./swbase.bend as SwBase 11 import ./swlive.bend as SwLive 12 import ./switch.bend as Switch 13 14 def delay_of(r: Result<&1, &1, U32 & String, String>) -> U32: 15 match r: 16 case Fail{e}: 17 2000 18 case Done{v}: 19 Maybe.default(&2, U32, U32.read(v), 2000) 20 21 def oracle_of(r: Result<&1, &1, U32 & String, String>) -> SwLive.Oracle: 22 match r: 23 case Fail{e}: 24 SwLive.OHttp{} 25 case Done{p}: 26 SwLive.OFile{p} 27 28 def opts_of(d: Result<&1, &1, U32 & String, String>, o: Result<&1, &1, U32 & String, String>) -> SwLive.Opts: 29 SwLive.Opts{delay_of(d), oracle_of(o)} 30 31 # How long to wait before the post-switch re-read ($CSWAP_SWITCH_VERIFY_DELAY_MS, 32 # default 2000 ms) and where the ownership lookup comes from ($CSWAP_SWITCH_ORACLE_FILE 33 # names a JSON file in tests; unset means the profile endpoint). 34 def switch_opts() -> IO(SwLive.Opts): 35 IO.bind(Result<&1, &1, U32 & String, String>, SwLive.Opts, IO.get_env("CSWAP_SWITCH_VERIFY_DELAY_MS"), 36 d => IO.bind(Result<&1, &1, U32 & String, String>, SwLive.Opts, IO.get_env("CSWAP_SWITCH_ORACLE_FILE"), o => IO.pure(SwLive.Opts, opts_of(d, o)))) 37 38 def warns_text(ws: List<&2, Json.Json>) -> String: 39 match ws: 40 case Nil{}: 41 "" 42 case Con{Json.JStr{w}, t}: 43 "Warning: " ++ w ++ "\n" ++ warns_text(t) 44 case Con{h, t}: 45 warns_text(t) 46 47 def switch_text(+j: Json.Json) -> String: 48 warns_text(Json.to_list(Maybe.default(&2, Json.Json, Json.get("warnings", j), Json.JNull{}))) ++ SSeq.field_str(j, "message") ++ "\n" 49 50 def switch_ok(+json: Bool, +j: Json.Json) -> Cmd.Out: 51 match json: 52 case True{}: 53 Cmd.doc(j) 54 case False{}: 55 Cmd.Out{0, switch_text(j), ""} 56 57 def switch_fail2(+json: Bool, +c: U32, +m: String) -> Cmd.Out: 58 match json: 59 case True{}: 60 Cmd.Out{1, Json.emit_pretty(SwBase.error_envelope(c, m)) ++ "\n", ""} 61 case False{}: 62 Cmd.fail(m) 63 64 def switch_fail(+json: Bool, ce: U32 & String) -> Cmd.Out: 65 (c, m) = ce 66 switch_fail2(json, c, m) 67 68 def switch_out(+json: Bool, r: Fs.Res(Json.Json), +models: List<&2, String>, +source: String) -> Cmd.Out: 69 match r: 70 case Fail{e}: 71 switch_fail(json, e) 72 case Done{j}: 73 switch_ok(json, SwBase.with_models(j, models, source)) 74 75 # The models that gate a strategy switch: --model wins, else the 76 # autoswitch.model setting; none for a plain rotation or a direct switch. 77 type Gate is Data: 78 Gate{models: List<&2, String>, source: String} 79 80 def gate_model(model: Maybe<&2, String>, +cfg: Json.Json) -> Gate: 81 match model: 82 case Some{m}: 83 Gate{Setload.model_names(m), "cli"} 84 case None{}: 85 Gate{Setload.model(cfg), "autoswitch.model"} 86 87 def gate_of(strategy: Maybe<&2, String>, model: Maybe<&2, String>, +cfg: Json.Json) -> Gate: 88 match strategy: 89 case None{}: 90 Gate{[], ""} 91 case Some{s}: 92 gate_model(model, cfg) 93 94 def gate_models(g: Gate) -> List<&2, String>: 95 match g: 96 case Gate{m, s}: 97 m 98 99 def switch_run(+ctx: SBase.Ctx, ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +gate: Gate, +opts: SwLive.Opts) -> Act.of(Json.Json): 100 match ident: 101 case Some{id}: 102 Switch.switch_to(ctx, id, force, opts) 103 case None{}: 104 Switch.switch_auto(ctx, Maybe.default(&2, String, strategy, ""), gate_models(gate), opts) 105 106 def gate_source(g: Gate) -> String: 107 match g: 108 case Gate{m, s}: 109 s 110 111 def switch_cmd2(+ctx: SBase.Ctx, ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +gate: Gate, +json: Bool, +opts: SwLive.Opts) -> IO(Cmd.Out): 112 IO.bind(Fs.Res(Json.Json), Cmd.Out, switch_run(ctx, ident, force, strategy, gate, opts), 113 r => IO.pure(Cmd.Out, switch_out(json, r, gate_models(gate), gate_source(gate)))) 114 115 def switch_cmd(+ctx: SBase.Ctx, ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out): 116 IO.bind(SwLive.Opts, Cmd.Out, switch_opts(), 117 opts => IO.bind(Json.Json, Cmd.Out, Setload.load(SBase.root_of(ctx)), 118 cfg => switch_cmd2(ctx, ident, force, strategy, gate_of(strategy, model, cfg), json, opts)))