use std::sync::Arc;
use sim_kernel::{
Args, Callable, CapabilityName, CapabilitySet, ClassRef, Cx, Error, Expr, Object, ObjectCompat,
RawArgs, Result, Symbol, Value, eval_fabric_capability,
};
use sim_value::kind::expr_kind;
use crate::{
capabilities::{openai_gateway_admin_capability, openai_gateway_serve_capabilities},
ops_admin::{call_admin_state_expr, call_run_get},
plan::{check_plan, eval_plan, explain_plan, parse_plan, plan_combinators_expr},
routes::{
admin::{
cache_stats_expr, capability_report_expr, events_expr, model_health_expr, runs_expr,
storage_stats_expr,
},
health::health_response,
models::models_response_for_runner_args,
},
runtime::{OpenAiGatewayFabric, global_openai_key_table},
server::{GatewayResponseValue, GatewayRoutesValue, configure_routes},
};
#[derive(Clone)]
pub struct OpenAiGatewayFunction {
kind: OpenAiGatewayFunctionKind,
}
#[derive(Clone, Copy)]
enum OpenAiGatewayFunctionKind {
Serve,
Health,
Models,
PlanParse,
PlanCheck,
PlanRun,
PlanExplain,
PlanCombinators,
Fabric,
KeyAdd,
KeyList,
Runs,
RunGet,
Events,
StorageStats,
ModelHealth,
CacheStats,
CapabilityReport,
}
impl OpenAiGatewayFunction {
fn new(kind: OpenAiGatewayFunctionKind) -> Self {
Self { kind }
}
pub fn serve() -> Self {
Self::new(OpenAiGatewayFunctionKind::Serve)
}
pub fn health() -> Self {
Self::new(OpenAiGatewayFunctionKind::Health)
}
pub fn models() -> Self {
Self::new(OpenAiGatewayFunctionKind::Models)
}
pub fn plan_parse() -> Self {
Self::new(OpenAiGatewayFunctionKind::PlanParse)
}
pub fn plan_check() -> Self {
Self::new(OpenAiGatewayFunctionKind::PlanCheck)
}
pub fn plan_run() -> Self {
Self::new(OpenAiGatewayFunctionKind::PlanRun)
}
pub fn plan_explain() -> Self {
Self::new(OpenAiGatewayFunctionKind::PlanExplain)
}
pub fn plan_combinators() -> Self {
Self::new(OpenAiGatewayFunctionKind::PlanCombinators)
}
pub fn fabric() -> Self {
Self::new(OpenAiGatewayFunctionKind::Fabric)
}
pub fn key_add() -> Self {
Self::new(OpenAiGatewayFunctionKind::KeyAdd)
}
pub fn key_list() -> Self {
Self::new(OpenAiGatewayFunctionKind::KeyList)
}
pub fn runs() -> Self {
Self::new(OpenAiGatewayFunctionKind::Runs)
}
pub fn run_get() -> Self {
Self::new(OpenAiGatewayFunctionKind::RunGet)
}
pub fn events() -> Self {
Self::new(OpenAiGatewayFunctionKind::Events)
}
pub fn storage_stats() -> Self {
Self::new(OpenAiGatewayFunctionKind::StorageStats)
}
pub fn model_health() -> Self {
Self::new(OpenAiGatewayFunctionKind::ModelHealth)
}
pub fn cache_stats() -> Self {
Self::new(OpenAiGatewayFunctionKind::CacheStats)
}
pub fn capability_report() -> Self {
Self::new(OpenAiGatewayFunctionKind::CapabilityReport)
}
pub fn symbol(&self) -> Symbol {
self.kind.symbol()
}
}
impl Object for OpenAiGatewayFunction {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<function {}>", self.kind.symbol()))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for OpenAiGatewayFunction {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
cx.resolve_class(&Symbol::qualified("core", "Function"))
}
fn as_callable(&self) -> Option<&dyn Callable> {
Some(self)
}
}
impl Callable for OpenAiGatewayFunction {
fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
self.kind.call(cx, args.into_vec())
}
fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
let values = args
.into_exprs()
.into_iter()
.map(|expr| cx.eval_expr(expr))
.collect::<Result<Vec<_>>>()?;
self.kind.call(cx, values)
}
}
impl OpenAiGatewayFunctionKind {
fn symbol(self) -> Symbol {
match self {
Self::Serve => serve_symbol(),
Self::Health => health_symbol(),
Self::Models => models_symbol(),
Self::PlanParse => plan_parse_symbol(),
Self::PlanCheck => plan_check_symbol(),
Self::PlanRun => plan_run_symbol(),
Self::PlanExplain => plan_explain_symbol(),
Self::PlanCombinators => plan_combinators_symbol(),
Self::Fabric => fabric_symbol(),
Self::KeyAdd => key_add_symbol(),
Self::KeyList => key_list_symbol(),
Self::Runs => runs_symbol(),
Self::RunGet => run_get_symbol(),
Self::Events => events_symbol(),
Self::StorageStats => storage_stats_symbol(),
Self::ModelHealth => model_health_symbol(),
Self::CacheStats => cache_stats_symbol(),
Self::CapabilityReport => capability_report_symbol(),
}
}
fn call(self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
match self {
Self::Serve | Self::Health | Self::PlanCombinators | Self::Fabric | Self::KeyList
if !args.is_empty() =>
{
Err(Error::Eval(format!(
"{} expects no arguments",
self.symbol()
)))
}
Self::Serve => call_serve(cx),
Self::Health => call_health(cx),
Self::Models => call_models(cx, args),
Self::PlanParse => call_plan_parse(cx, args),
Self::PlanCheck => call_plan_check(cx, args),
Self::PlanRun => call_plan_run(cx, args),
Self::PlanExplain => call_plan_explain(cx, args),
Self::PlanCombinators => call_plan_combinators(cx),
Self::Fabric => call_fabric(cx),
Self::KeyAdd => call_key_add(cx, args),
Self::KeyList => call_key_list(cx),
Self::Runs => call_admin_state_expr(cx, self.symbol(), args, runs_expr),
Self::RunGet => call_run_get(cx, self.symbol(), args),
Self::Events => call_admin_state_expr(cx, self.symbol(), args, events_expr),
Self::StorageStats => {
call_admin_state_expr(cx, self.symbol(), args, storage_stats_expr)
}
Self::ModelHealth => call_admin_state_expr(cx, self.symbol(), args, model_health_expr),
Self::CacheStats => call_admin_state_expr(cx, self.symbol(), args, cache_stats_expr),
Self::CapabilityReport => {
call_admin_state_expr(cx, self.symbol(), args, capability_report_expr)
}
}
}
}
pub fn serve_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "serve")
}
pub fn health_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "health")
}
pub fn models_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "models")
}
pub fn plan_parse_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "plan-parse")
}
pub fn plan_check_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "plan-check")
}
pub fn plan_run_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "plan-run")
}
pub fn plan_explain_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "plan-explain")
}
pub fn plan_combinators_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "plan-combinators")
}
pub fn fabric_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "fabric")
}
pub fn key_add_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "key-add")
}
pub fn key_list_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "key-list")
}
pub fn runs_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "runs")
}
pub fn run_get_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "run-get")
}
pub fn events_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "events")
}
pub fn storage_stats_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "storage-stats")
}
pub fn model_health_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "model-health")
}
pub fn cache_stats_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "cache-stats")
}
pub fn capability_report_symbol() -> Symbol {
Symbol::qualified("openai-gateway", "capability-report")
}
fn call_serve(cx: &mut Cx) -> Result<Value> {
cx.require_all(&openai_gateway_serve_capabilities())?;
cx.factory()
.opaque(Arc::new(GatewayRoutesValue::new(configure_routes())))
}
fn call_health(cx: &mut Cx) -> Result<Value> {
cx.factory()
.opaque(Arc::new(GatewayResponseValue::new(health_response())))
}
fn call_models(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
let response = models_response_for_runner_args(cx, args)?;
cx.factory()
.opaque(Arc::new(GatewayResponseValue::new(response)))
}
fn call_plan_parse(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
let input = string_arg(cx, plan_parse_symbol(), args)?;
cx.factory().expr(parse_plan(&input)?)
}
fn call_plan_check(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
let plan = expr_arg(cx, plan_check_symbol(), args)?;
check_plan(&plan)?;
cx.factory().bool(true)
}
fn call_plan_run(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
let mut args = expect_arg_count(plan_run_symbol(), args, 2)?.into_iter();
let plan = value_expr(cx, args.next().expect("plan-run arg count checked"))?;
let request = value_expr(cx, args.next().expect("plan-run arg count checked"))?;
let response = eval_plan(cx, &plan, &request)?;
cx.factory().expr(response)
}
fn call_plan_explain(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
let plan = expr_arg(cx, plan_explain_symbol(), args)?;
cx.factory().string(explain_plan(&plan)?)
}
fn call_plan_combinators(cx: &mut Cx) -> Result<Value> {
cx.factory().expr(plan_combinators_expr())
}
fn call_fabric(cx: &mut Cx) -> Result<Value> {
cx.require(&eval_fabric_capability())?;
cx.factory().opaque(Arc::new(OpenAiGatewayFabric::memory()))
}
fn call_key_add(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
cx.require(&openai_gateway_admin_capability())?;
if args.is_empty() {
return Err(Error::Eval(format!(
"{} expects at least 1 argument",
key_add_symbol()
)));
}
let mut args = args.into_iter();
let secret = value_string(
cx,
args.next().expect("key-add arg count checked as non-empty"),
)?;
let mut capabilities = CapabilitySet::new();
for arg in args {
capabilities.insert(value_capability(cx, arg)?);
}
let key = global_openai_key_table().add_secret(&secret, capabilities)?;
cx.factory().opaque(Arc::new(key))
}
fn call_key_list(cx: &mut Cx) -> Result<Value> {
cx.require(&openai_gateway_admin_capability())?;
let keys = global_openai_key_table().list_keys()?;
cx.factory().list(
keys.into_iter()
.map(|key| cx.factory().opaque(Arc::new(key)))
.collect::<Result<Vec<_>>>()?,
)
}
fn string_arg(cx: &mut Cx, symbol: Symbol, args: Vec<Value>) -> Result<String> {
match expr_arg(cx, symbol.clone(), args)? {
Expr::String(value) => Ok(value),
other => Err(Error::TypeMismatch {
expected: "string argument",
found: expr_kind(&other),
}),
}
}
fn value_string(cx: &mut Cx, value: Value) -> Result<String> {
match value_expr(cx, value)? {
Expr::String(value) => Ok(value),
other => Err(Error::TypeMismatch {
expected: "string argument",
found: expr_kind(&other),
}),
}
}
fn value_capability(cx: &mut Cx, value: Value) -> Result<CapabilityName> {
match value_expr(cx, value)? {
Expr::String(value) => Ok(CapabilityName::new(value)),
Expr::Symbol(symbol) => Ok(CapabilityName::new(symbol.to_string())),
other => Err(Error::TypeMismatch {
expected: "capability string or symbol",
found: expr_kind(&other),
}),
}
}
fn expr_arg(cx: &mut Cx, symbol: Symbol, args: Vec<Value>) -> Result<Expr> {
let mut args = expect_arg_count(symbol, args, 1)?.into_iter();
value_expr(cx, args.next().expect("arg count checked"))
}
fn expect_arg_count(symbol: Symbol, args: Vec<Value>, expected: usize) -> Result<Vec<Value>> {
if args.len() == expected {
Ok(args)
} else {
Err(Error::Eval(format!(
"{symbol} expects {expected} argument(s), found {}",
args.len()
)))
}
}
fn value_expr(cx: &mut Cx, value: Value) -> Result<Expr> {
value.object().as_expr(cx)
}