use anyhow::Result;
use super::{Capability, CapabilityEdit, add_named_imports};
use crate::infra::in_place;
pub(crate) static MODELS: Capability = Capability {
id: "models",
resource_type: "models",
summary: "AI models through the platform's model gateway: no provider key, the product's model allowlist and token budget",
registration_key: "ModelGatewayClient",
receives_events: false,
workers: true,
add,
remove,
};
const HTTP_SOURCE: &str = "@forklaunch/core/http";
const HTTP_IMPORTS: &[&str] = &["createModelGatewayClient", "type ModelGatewayClient"];
const RUNTIME_BLOCK: &str = "const configInjector = createConfigInjector(SchemaValidator(), {
ModelGatewayClient: {
lifetime: Lifetime.Scoped,
type: type<ModelGatewayClient>(),
factory: ({ PLATFORM_GATEWAY_URL, INSTANCE_ID, INSTANCE_HMAC_KEY }) =>
createModelGatewayClient({
gatewayUrl: PLATFORM_GATEWAY_URL,
instanceId: INSTANCE_ID,
hmacKey: INSTANCE_HMAC_KEY
})
}
});";
fn add(edit: &mut CapabilityEdit) -> Result<()> {
edit.inject_registration("", HTTP_SOURCE, None, Some(RUNTIME_BLOCK))?;
let path = edit.registrations_path();
if let Some(before) = edit.read(&path)? {
let core_source = format!("@{}/core", edit.app_name);
let text = add_named_imports(&before, HTTP_SOURCE, HTTP_IMPORTS);
let text = add_named_imports(&text, &core_source, &["type"]);
if text != before {
edit.write(path, text);
}
}
Ok(())
}
fn remove(edit: &mut CapabilityEdit) -> Result<()> {
let path = edit.registrations_path();
let Some(text) = edit.read(&path)? else {
return Ok(());
};
if text.contains("ModelGatewayClient:")
&& !in_place::entry_matches_snippet(
&text,
"runtimeDependencies",
"ModelGatewayClient",
RUNTIME_BLOCK,
)
{
eprintln!(
"note: registrations.ts builds ModelGatewayClient its own way; kept (remove it by hand if nothing calls it)"
);
return Ok(());
}
edit.remove_registration(None, &[], &["ModelGatewayClient"])
}
#[cfg(test)]
mod tests;