///|
/// `/model` command for the provider-catalog router.
///
/// With no argument the command returns a machine-readable slot catalog. With
/// a slot id it changes router selection. An optional `effort` field selects a
/// provider-advertised reasoning effort through the slot's rebuild hook.
///|
/// Machine-readable slot catalog. `quota` enriches each entry of a provider
/// that has readings under its id with a `quota_readings` array; the key is
/// omitted entirely when no quota map is given or the provider has none.
/// `pricing` enriches each entry of a provider whose registered source states
/// a phase with a `pricing` object; the key is omitted entirely when no
/// pricing map is given or the provider has none.
fn RouterModelPort::slot_catalog_json(
self : RouterModelPort,
quota? : Map[String, Array[@devkit.QuotaReading]]? = None,
pricing? : Map[String, @devkit.PricingPhase]? = None,
) -> Json {
let entries : Array[Json] = []
for slot in self.list_slots() {
let effort_values : Array[Json] = []
for effort in slot.thinking_efforts {
effort_values.push(Json::string(effort))
}
let capability_values : Array[Json] = []
for capability in slot.capabilities {
capability_values.push(Json::string(capability))
}
let pairs : Array[(String, Json)] = [
("id", Json::string(slot.id)),
("label", Json::string(slot.label)),
("provider", Json::string(slot.provider_id)),
("model", Json::string(slot.model_id)),
("active", Json::boolean(slot.id == self.active_id)),
(
"active_effort",
match self.active_effort {
Some(effort) => Json::string(effort)
None => Json::null()
},
),
("thinking_efforts", Json::array(effort_values)),
(
"context_window",
match slot.context_window {
Some(value) => Json::number(value.to_double())
None => Json::null()
},
),
("capabilities", Json::array(capability_values)),
(
"default_effort",
match slot.default_effort {
Some(value) => Json::string(value)
None => Json::null()
},
),
(
"group",
match slot.display_group {
Some(value) => Json::string(value)
None => Json::null()
},
),
]
match quota {
Some(map) =>
match map.get(slot.provider_id) {
Some(readings) => {
let reading_values : Array[Json] = []
for reading in readings {
reading_values.push(quota_reading_json(reading))
}
pairs.push(("quota_readings", Json::array(reading_values)))
}
None => ()
}
None => ()
}
match pricing {
Some(map) =>
match map.get(slot.provider_id) {
Some(phase) => pairs.push(("pricing", pricing_phase_json(phase)))
None => ()
}
None => ()
}
entries.push(Json::object(Map::from_array(pairs)))
}
Json::array(entries)
}
///|
priv struct ModelSelection {
slot_id : String?
effort : String?
}
///|
fn parse_optional_string(
command : String,
fields : Map[String, Json],
key : String,
) -> String? raise @posoco.CommandError {
match fields.get(key) {
None => None
Some(Json::String(value)) =>
if value.length() == 0 {
raise @posoco.CommandError::InvalidArgs(
reason=command + " " + key + " must be a non-empty string",
)
} else {
Some(value)
}
Some(_) =>
raise @posoco.CommandError::InvalidArgs(
reason=command + " " + key + " must be a string",
)
}
}
///|
fn model_command_selection(
args : Json,
) -> ModelSelection raise @posoco.CommandError {
match args {
Json::Null => { slot_id: None, effort: None, }
Json::String(slot_id) =>
if slot_id.length() == 0 {
{ slot_id: None, effort: None, }
} else {
{ slot_id: Some(slot_id), effort: None, }
}
Json::Object(fields) =>
{
slot_id: parse_optional_string("/model", fields, "slot"),
effort: parse_optional_string("/model", fields, "effort"),
}
_ =>
raise @posoco.CommandError::InvalidArgs(
reason="/model expects no arguments, a slot string, or {slot, effort}",
)
}
}
///|
/// Parse `/effort` arguments. Accepts no arguments (query the active slot),
/// a bare effort string, or an object carrying either `effort` or the
/// `_positional` string a host slash parser produces.
fn effort_command_selection(args : Json) -> String? raise @posoco.CommandError {
match args {
Json::Null => None
Json::String(value) => if value.length() == 0 { None } else { Some(value) }
Json::Object(fields) =>
match parse_optional_string("/effort", fields, "effort") {
Some(value) => Some(value)
None => parse_optional_string("/effort", fields, "_positional")
}
_ =>
raise @posoco.CommandError::InvalidArgs(
reason="/effort expects no arguments or an effort string",
)
}
}
///|
/// Select a provider-advertised reasoning effort on one slot through its
/// rebuild hook. Public so hosts can restore a persisted effort at composition
/// and apply ACP/config-driven effort switches; the `/effort` command remains
/// the interactive entry point.
pub fn RouterModelPort::select_effort(
self : RouterModelPort,
slot_id : String,
effort : String,
) -> Unit raise @posoco.CommandError {
let slot = match self.slots.get(slot_id) {
Some(slot) => slot
None =>
raise @posoco.CommandError::InvalidArgs(
reason="unknown model slot: " + slot_id,
)
}
if !slot.thinking_efforts.contains(effort) {
raise @posoco.CommandError::InvalidArgs(
reason="unsupported effort '" + effort + "' for model slot " + slot_id,
)
}
match slot.rebuild_on_effort {
Some(rebuild) => self.slots[slot_id] = rebuild(effort)
None =>
raise @posoco.CommandError::ExecutionFailed(
"model slot does not expose an effort selector: " + slot_id,
)
}
self.active_effort = Some(effort)
}
///|
pub impl @posoco.CommandPort for RouterModelPort with fn commands(
self : RouterModelPort,
) -> Array[@posoco.CommandDef] {
let slot_choices : Array[String] = [quick_pick_keyword]
for id in self.list_slot_ids() {
slot_choices.push(id)
}
[
@posoco.CommandDef::{
id: "model",
label: "Switch Model",
description: "Switch the active model slot and optional effort",
category: "model",
ctype: @posoco.CommandType::Select,
params: [
@posoco.CommandParam::{
name: "slot",
label: "Model",
description: "Target slot id; `quick-pick` ranks models by remaining quota; ` pick` auto-picks by intelligence-per-dollar metrics; omit to list available models",
ptype: @posoco.ParamType::Str,
required: false,
default: None,
choices: Some(slot_choices),
positional: true,
},
@posoco.CommandParam::{
name: "effort",
label: "Effort",
description: "Provider-advertised reasoning effort",
ptype: @posoco.ParamType::Str,
required: false,
default: None,
choices: match self.slots.get(self.active_id) {
Some(slot) => Some(slot.thinking_efforts.copy())
None => Some([])
},
positional: false,
},
],
aliases: [],
shortcut: None,
icon: None,
visible: true,
metadata: None,
},
@posoco.CommandDef::{
id: "login",
label: "Login",
description: "Authenticate with a provider through its OAuth hook",
category: "auth",
ctype: @posoco.CommandType::Action,
params: [
@posoco.CommandParam::{
name: "provider",
label: "Provider",
description: "Provider id; omit to list authentication methods",
ptype: @posoco.ParamType::Str,
required: false,
default: None,
choices: None,
positional: true,
},
@posoco.CommandParam::{
name: "method",
label: "Method",
description: "Provider-owned authentication method",
ptype: @posoco.ParamType::Str,
required: false,
default: None,
choices: Some(["api_key", "oauth"]),
positional: false,
},
],
aliases: [],
shortcut: None,
icon: None,
visible: true,
metadata: None,
},
@posoco.CommandDef::{
id: "effort",
label: "Switch Effort",
description: "Show or switch reasoning effort for the active model slot",
category: "model",
ctype: @posoco.CommandType::Select,
params: [
@posoco.CommandParam::{
name: "effort",
label: "Effort",
description: "Provider-advertised reasoning effort; omit to show the current effort",
ptype: @posoco.ParamType::Str,
required: false,
default: None,
choices: match self.slots.get(self.active_id) {
Some(slot) => Some(slot.thinking_efforts.copy())
None => Some([])
},
positional: true,
},
],
aliases: [],
shortcut: None,
icon: None,
visible: true,
metadata: None,
},
@posoco.CommandDef::{
id: "status",
label: "Status",
description: "Active provider status: model, context window, quota windows",
category: "model",
ctype: @posoco.CommandType::Action,
params: [],
aliases: [],
shortcut: None,
icon: None,
visible: true,
metadata: None,
},
]
}
///|
pub impl @posoco.CommandPort for RouterModelPort with fn invoke(
self : RouterModelPort,
id : String,
args : Json,
) -> @posoco.CommandOutcome raise @posoco.CommandError {
match id {
"model" =>
match model_command_selection(args) {
{ slot_id: None, effort: None, } =>
@posoco.CommandOutcome::Success(
feedback="Available model slots",
structured=Some(
self.slot_catalog_json(
pricing=Some(self.collect_provider_pricing()),
),
),
ui_hint=Some(@posoco.UiHint::RefreshModelList),
)
{ slot_id: Some(slot_id), effort, } =>
if slot_id == quick_pick_keyword {
match effort {
Some(_) =>
@posoco.CommandOutcome::Failure(
reason="/model quick-pick takes no effort",
)
None => {
let readings = self.collect_provider_quota()
// Mirror /status: a successful pull for the active provider
// doubles as the bar refresh.
match readings.get(self.current_provider_id()) {
Some(list) => {
self.quota_readings = list
self.quota_fetched_at = @async.now()
self.publish_quota_segments()
}
None => ()
}
@posoco.CommandOutcome::Success(
feedback="Quick-pick model slots",
structured=Some(self.slot_catalog_json(quota=Some(readings))),
ui_hint=Some(@posoco.UiHint::RefreshModelList),
)
}
}
} else if !self.slots.contains(slot_id) &&
effort == Some(pick_keyword) {
// `/model pick`: the id is not a slot but names a
// provider with a registered metrics source. Real slots always
// win over this intercept.
match @devkit.metrics_registry_lookup(slot_id) {
Some(source) =>
self.run_provider_pick(slot_id, source) catch {
err =>
return @posoco.CommandOutcome::Failure(
reason=err.to_string(),
)
}
None =>
@posoco.CommandOutcome::Failure(
reason="no metrics source registered for provider '" +
slot_id +
"'",
)
}
} else if !self.slots.contains(slot_id) {
@posoco.CommandOutcome::Failure(
reason="unknown model slot: " + slot_id,
)
} else {
match effort {
Some(value) =>
self.select_effort(slot_id, value) catch {
err =>
return @posoco.CommandOutcome::Failure(
reason=err.to_string(),
)
}
None => self.active_effort = None
}
self.active_id = slot_id
self.publish_model_status()
self.publish_provider_event()
// Immediate probe so the bar reflects the new provider's quota
// as soon as the command returns.
self.refresh_quota_now()
@posoco.CommandOutcome::Success(
feedback="switched to model slot: " + slot_id,
structured=Some(self.slot_catalog_json()),
ui_hint=Some(@posoco.UiHint::RefreshModelList),
)
}
{ slot_id: None, effort: Some(_), } =>
@posoco.CommandOutcome::Failure(
reason="/model effort requires a slot",
)
}
"effort" =>
match effort_command_selection(args) {
None => {
let slot = match self.active_slot_opt() {
Some(slot) => slot
None =>
return @posoco.CommandOutcome::Failure(
reason="active model slot '" +
self.active_id +
"' is not configured; switch with /model or log in to the provider",
)
}
let current = match self.active_effort {
Some(value) => Some(value)
None => slot.default_effort
}
let feedback = match current {
Some(value) =>
"current effort: " + value + " (" + self.active_id + ")"
None => "model slot has no effort selection: " + self.active_id
}
@posoco.CommandOutcome::Success(
feedback~,
structured=Some(self.slot_catalog_json()),
ui_hint=Some(@posoco.UiHint::None),
)
}
Some(level) => {
let slot = match self.active_slot_opt() {
Some(slot) => slot
None =>
return @posoco.CommandOutcome::Failure(
reason="active model slot '" +
self.active_id +
"' is not configured; switch with /model or log in to the provider",
)
}
if slot.thinking_efforts.is_empty() {
@posoco.CommandOutcome::Failure(
reason="model slot does not support effort selection: " +
self.active_id,
)
} else {
self.select_effort(self.active_id, level) catch {
err =>
return @posoco.CommandOutcome::Failure(reason=err.to_string())
}
self.publish_model_status()
self.publish_provider_event()
// Immediate probe: the switch may land on a different provider.
self.refresh_quota_now()
@posoco.CommandOutcome::Success(
feedback="switched effort: " + level + " (" + self.active_id + ")",
structured=Some(self.slot_catalog_json()),
ui_hint=Some(@posoco.UiHint::RefreshModelList),
)
}
}
}
"status" => {
expect_no_args(args)
// Command-triggered live pull from the active provider's registered
// quota source: the source applies its own timeout; Err states the
// reading is unavailable and is skipped, never estimated.
let now = @async.now()
let readings : Array[@devkit.QuotaReading] = match
@devkit.quota_registry_lookup(self.current_provider_id()) {
Some(source) => {
let result = source.read() catch { _ => Err("quota pull failed") }
match result {
Ok(list) => {
// The live pull doubles as the bar refresh: cache what
// /status fetched so the statusbar segments stay in sync
// with this report.
self.quota_readings = list
self.quota_fetched_at = now
self.publish_quota_segments()
list
}
Err(_) => {
// A failed pull backs off like the chat path and keeps the
// published segments: a failed /status never retracts bar
// data that was already there.
self.quota_failed_at = now
[]
}
}
}
None => []
}
let (feedback, structured) = self.status_report(readings~)
@posoco.CommandOutcome::Success(
feedback~,
structured=Some(structured),
ui_hint=Some(@posoco.UiHint::None),
)
}
"login" => self.invoke_login(args)
other => raise @posoco.CommandError::NotFound(other)
}
}