use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use serde_json::Value;
use wiremux_auth::{LoadOptions, list_profiles, parse_profile_str};
pub const POPULAR_VENDORS: &[&str] = &[
"groq",
"deepseek",
"togetherai",
"fireworks-ai",
"mistral",
"cerebras",
];
pub const MODELS_DEV_URL: &str = "https://models.dev/api.json";
pub const LITELLM_PROVIDERS_URL: &str = "https://raw.githubusercontent.com/BerriAI/litellm/main/litellm/llms/openai_like/providers.json";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CatalogKind {
ModelsDev,
LiteLlm,
}
impl CatalogKind {
pub fn parse_name(name: &str) -> Result<Self, IngestError> {
match name {
"models-dev" | "models.dev" | "modelsdev" => Ok(Self::ModelsDev),
"litellm" => Ok(Self::LiteLlm),
other => Err(IngestError::Message(format!(
"unknown catalog source `{other}` (models-dev or litellm)"
))),
}
}
pub fn default_url(self) -> &'static str {
match self {
Self::ModelsDev => MODELS_DEV_URL,
Self::LiteLlm => LITELLM_PROVIDERS_URL,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CatalogVendor {
pub id: String,
pub display_name: String,
pub api: Option<String>,
pub env: Vec<String>,
pub npm: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IngestAction {
Wrote(PathBuf),
DryRun(PathBuf),
Skipped { vendor: String, reason: String },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IngestReport {
pub actions: Vec<IngestAction>,
}
#[derive(Debug, thiserror::Error)]
pub enum IngestError {
#[error("{0}")]
Message(String),
#[error("{path}: {source}")]
Io {
path: PathBuf,
source: std::io::Error,
},
}
#[derive(Debug, Clone)]
pub struct IngestRequest {
pub kind: CatalogKind,
pub vendors: Vec<String>,
pub all_compatible: bool,
pub dir: Option<PathBuf>,
pub dry_run: bool,
pub force: bool,
}
impl Default for IngestRequest {
fn default() -> Self {
Self {
kind: CatalogKind::ModelsDev,
vendors: Vec::new(),
all_compatible: false,
dir: None,
dry_run: false,
force: false,
}
}
}
pub fn parse_catalog(kind: CatalogKind, text: &str) -> Result<Vec<CatalogVendor>, IngestError> {
let value: Value = serde_json::from_str(text)
.map_err(|e| IngestError::Message(format!("catalog is not JSON: {e}")))?;
match kind {
CatalogKind::ModelsDev => parse_models_dev(&value),
CatalogKind::LiteLlm => parse_litellm(&value),
}
}
pub fn profile_toml(vendor: &CatalogVendor) -> Result<String, IngestError> {
let draft = draft_for(vendor)?;
refuse_bad_id(&vendor.id)?;
if draft.access_env.is_empty() {
return Err(vendor_err(&vendor.id, "catalog row has no env key"));
}
let display = if vendor.display_name.trim().is_empty() {
vendor.id.clone()
} else {
vendor.display_name.clone()
};
let mut out = String::new();
out.push_str("schema_version = 1\n");
out.push_str(&format!("id = {}\n", toml_string(&vendor.id)));
out.push_str(&format!("display_name = {}\n", toml_string(&display)));
out.push_str(&format!("wire = {}\n", toml_string(draft.wire.toml_name())));
out.push_str(&format!("base_url = {}\n", toml_string(&draft.base_url)));
out.push_str(&format!("chat_path = {}\n", toml_string(&draft.chat_path)));
out.push_str(&format!(
"auth_scheme = {}\n",
toml_string(draft.auth_scheme)
));
out.push_str(&format!(
"access_env = {}\n",
toml_access_env(&draft.access_env)
));
if let Some(service) = draft.aws_service {
out.push_str(&format!("aws_service = {}\n", toml_string(service)));
}
if let Some(env) = draft.gcp_key_env {
out.push_str(&format!("gcp_key_env = {}\n", toml_string(env)));
}
if let Some(region) = draft.aws_region {
out.push_str(&format!("aws_region = {}\n", toml_string(®ion)));
}
if !draft.headers.is_empty() {
out.push_str("\n[headers]\n");
for (k, v) in &draft.headers {
out.push_str(&format!("{k} = {}\n", toml_string(v)));
}
}
if !draft.extra_body.is_empty() {
out.push_str("\n[fingerprint.extra_body]\n");
for (k, v) in &draft.extra_body {
out.push_str(&format!("{k} = {}\n", toml_string(v)));
}
}
Ok(out)
}
struct ProfileDraft {
wire: EmitWire,
base_url: String,
chat_path: String,
auth_scheme: &'static str,
access_env: Vec<String>,
headers: Vec<(String, String)>,
extra_body: Vec<(String, String)>,
aws_service: Option<&'static str>,
aws_region: Option<String>,
gcp_key_env: Option<&'static str>,
}
fn draft_for(vendor: &CatalogVendor) -> Result<ProfileDraft, IngestError> {
if let Some(reason) = skip_reason_opt(&vendor.id, vendor.npm.as_deref()) {
return Err(vendor_err(&vendor.id, reason));
}
if vendor.id == "azure" || vendor.id == "azure-cognitive-services" {
return azure_draft(vendor);
}
if vendor.id == "google-vertex" {
return vertex_gemini_draft(vendor);
}
if vendor.id == "google-vertex-anthropic" {
return vertex_anthropic_draft(vendor);
}
if vendor.id == "amazon-bedrock" {
return Ok(bedrock_draft(vendor));
}
let wire = emit_wire(vendor)?;
let (base_url, chat_path) = endpoint_for(vendor, wire)?;
let access_env = drop_url_placeholder_env(&vendor.env, &base_url, &chat_path);
let auth_scheme = if wire == EmitWire::Messages && vendor.api.is_some() {
"bearer"
} else {
wire.auth_scheme()
};
Ok(ProfileDraft {
wire,
base_url,
chat_path,
auth_scheme,
access_env,
headers: Vec::new(),
extra_body: Vec::new(),
aws_service: None,
aws_region: None,
gcp_key_env: None,
})
}
fn azure_draft(vendor: &CatalogVendor) -> Result<ProfileDraft, IngestError> {
let resource = vendor
.env
.iter()
.find(|n| n.contains("RESOURCE_NAME"))
.ok_or_else(|| vendor_err(&vendor.id, "catalog env has no RESOURCE_NAME"))?;
let key = vendor
.env
.iter()
.find(|n| n.ends_with("API_KEY") || n.ends_with("_KEY"))
.cloned()
.unwrap_or_else(|| "AZURE_API_KEY".into());
Ok(ProfileDraft {
wire: EmitWire::ChatCompletions,
base_url: format!("https://{{env:{resource}}}.openai.azure.com"),
chat_path: "/openai/deployments/{model}/chat/completions?api-version=2024-10-21".into(),
auth_scheme: "header:api-key",
access_env: vec![key],
headers: Vec::new(),
extra_body: Vec::new(),
aws_service: None,
aws_region: None,
gcp_key_env: None,
})
}
fn vertex_gemini_draft(vendor: &CatalogVendor) -> Result<ProfileDraft, IngestError> {
require_vertex_project(vendor)?;
Ok(ProfileDraft {
wire: EmitWire::Gemini,
base_url: "https://{env:GOOGLE_VERTEX_LOCATION}-aiplatform.googleapis.com".into(),
chat_path: "/v1/projects/{env:GOOGLE_VERTEX_PROJECT}/locations/{env:GOOGLE_VERTEX_LOCATION}/publishers/google/models/{model}:generateContent".into(),
auth_scheme: "bearer",
access_env: vertex_access_env(vendor),
headers: Vec::new(),
extra_body: Vec::new(),
aws_service: None,
aws_region: None,
gcp_key_env: Some("GOOGLE_APPLICATION_CREDENTIALS"),
})
}
fn vertex_anthropic_draft(vendor: &CatalogVendor) -> Result<ProfileDraft, IngestError> {
require_vertex_project(vendor)?;
Ok(ProfileDraft {
wire: EmitWire::Messages,
base_url: "https://{env:GOOGLE_VERTEX_LOCATION}-aiplatform.googleapis.com".into(),
chat_path: "/v1/projects/{env:GOOGLE_VERTEX_PROJECT}/locations/{env:GOOGLE_VERTEX_LOCATION}/publishers/anthropic/models/{model}:rawPredict".into(),
auth_scheme: "bearer",
access_env: vertex_access_env(vendor),
headers: vec![("anthropic-version".into(), "vertex-2023-10-16".into())],
extra_body: vec![("anthropic_version".into(), "vertex-2023-10-16".into())],
aws_service: None,
aws_region: None,
gcp_key_env: Some("GOOGLE_APPLICATION_CREDENTIALS"),
})
}
fn require_vertex_project(vendor: &CatalogVendor) -> Result<(), IngestError> {
if vendor
.env
.iter()
.any(|n| n == "GOOGLE_VERTEX_PROJECT" || n.contains("PROJECT"))
{
Ok(())
} else {
Err(vendor_err(
&vendor.id,
"catalog env has no GOOGLE_VERTEX_PROJECT",
))
}
}
fn drop_url_placeholder_env(env: &[String], base_url: &str, chat_path: &str) -> Vec<String> {
let haystack = format!("{base_url}{chat_path}");
env.iter()
.filter(|name| !haystack.contains(&format!("{{env:{name}}}")))
.cloned()
.collect()
}
fn vertex_access_env(vendor: &CatalogVendor) -> Vec<String> {
let mut env = vec!["GOOGLE_OAUTH_ACCESS_TOKEN".into()];
for name in &vendor.env {
if name.contains("API_KEY") || name.contains("TOKEN") {
env.push(name.clone());
}
}
env
}
fn bedrock_draft(vendor: &CatalogVendor) -> ProfileDraft {
let mut env = vec!["AWS_BEARER_TOKEN_BEDROCK".into()];
for name in &vendor.env {
if name.contains("BEARER") || name.contains("TOKEN") {
env.push(name.clone());
}
}
env.dedup();
ProfileDraft {
wire: EmitWire::Converse,
base_url: "https://bedrock-runtime.{env:AWS_REGION}.amazonaws.com".into(),
chat_path: "/model/{model}/converse".into(),
auth_scheme: "bearer",
access_env: env,
headers: Vec::new(),
extra_body: Vec::new(),
aws_service: Some("bedrock"),
aws_region: Some("{env:AWS_REGION}".into()),
gcp_key_env: None,
}
}
fn emit_wire(vendor: &CatalogVendor) -> Result<EmitWire, IngestError> {
match vendor.npm.as_deref() {
Some("@ai-sdk/anthropic") => Ok(EmitWire::Messages),
Some("@ai-sdk/google") => Ok(EmitWire::Gemini),
Some("@ai-sdk/cohere") => Ok(EmitWire::ChatCompletions),
Some(npm) if openai_compat_npm(npm) => Ok(EmitWire::ChatCompletions),
Some(npm) => Err(vendor_err(
&vendor.id,
&format!("npm package `{npm}` is not a known HTTP dialect"),
)),
None => Ok(EmitWire::ChatCompletions),
}
}
pub fn ingest_catalog(text: &str, req: &IngestRequest) -> Result<IngestReport, IngestError> {
let rows = parse_catalog(req.kind, text)?;
let by_id: BTreeMap<String, CatalogVendor> =
rows.into_iter().map(|v| (v.id.clone(), v)).collect();
let wanted = wanted_ids(req, &by_id);
if wanted.is_empty() {
return Err(IngestError::Message("no catalog rows matched".into()));
}
let dir = match &req.dir {
Some(dir) => dir.clone(),
None => ingest_profile_dir().ok_or_else(|| {
IngestError::Message(
"no user profile dir (set --dir, HOME, or WIREMUX_PROFILE_DIR)".into(),
)
})?,
};
if !req.dry_run {
fs::create_dir_all(&dir).map_err(|source| IngestError::Io {
path: dir.clone(),
source,
})?;
}
let mut actions = Vec::new();
let mut pending: Vec<(PathBuf, String)> = Vec::new();
let mut hard: Vec<String> = Vec::new();
for id in wanted {
match ingest_one(&by_id, &id, &dir, req) {
Ok((action, body)) => {
if let IngestAction::Skipped { reason, .. } = &action
&& req.vendors.iter().any(|v| v == &id)
&& reason_is_hard(reason)
{
hard.push(format!("{id}: {reason}"));
}
if let Some(toml) = body {
let IngestAction::Wrote(path) = &action else {
return Err(IngestError::Message(format!(
"{id}: planned a write without a path"
)));
};
pending.push((path.clone(), toml));
}
actions.push(action);
}
Err(err) => hard.push(err.to_string()),
}
}
if !hard.is_empty() {
for action in &actions {
if let IngestAction::Wrote(path) | IngestAction::DryRun(path) = action {
hard.push(format!("not written: {}", path.display()));
}
}
return Err(IngestError::Message(hard.join("\n")));
}
for (path, toml) in pending {
fs::write(&path, toml).map_err(|source| IngestError::Io {
path: path.clone(),
source,
})?;
}
Ok(IngestReport { actions })
}
const MAX_CATALOG_BODY: usize = 16 * 1024 * 1024;
async fn read_catalog_http_body(
url: &str,
response: reqwest::Response,
cap: usize,
) -> Result<String, IngestError> {
crate::capped_body::read_capped_text(response, cap)
.await
.map_err(|err| IngestError::Message(format!("fetch {url}: {err}")))
}
pub async fn fetch_catalog_url(url: &str) -> Result<String, IngestError> {
if url != MODELS_DEV_URL && url != LITELLM_PROVIDERS_URL {
return Err(IngestError::Message(
"refused: ingest only fetches the models.dev or LiteLLM catalog URLs".into(),
));
}
let client = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.timeout(std::time::Duration::from_secs(30))
.user_agent(concat!("wiremux/", env!("CARGO_PKG_VERSION")))
.build()
.map_err(|e| IngestError::Message(format!("http client: {e}")))?;
let response = client
.get(url)
.send()
.await
.map_err(|e| IngestError::Message(format!("fetch {url}: {e}")))?;
let status = response.status();
if !status.is_success() {
return Err(IngestError::Message(format!("fetch {url}: HTTP {status}")));
}
read_catalog_http_body(url, response, MAX_CATALOG_BODY).await
}
fn wanted_ids(req: &IngestRequest, by_id: &BTreeMap<String, CatalogVendor>) -> Vec<String> {
if req.all_compatible {
return by_id
.values()
.filter(|v| openai_compat_row(v) && profile_toml(v).is_ok() && !skip_id(&v.id))
.map(|v| v.id.clone())
.collect();
}
if req.vendors.is_empty() {
return POPULAR_VENDORS.iter().map(|s| (*s).to_string()).collect();
}
req.vendors.clone()
}
fn ingest_one(
by_id: &BTreeMap<String, CatalogVendor>,
id: &str,
dir: &Path,
req: &IngestRequest,
) -> Result<(IngestAction, Option<String>), IngestError> {
if skip_id(id) {
return Ok((
IngestAction::Skipped {
vendor: id.to_string(),
reason: skip_reason(id).to_string(),
},
None,
));
}
let Some(vendor) = by_id.get(id) else {
return Err(vendor_err(id, "not in catalog"));
};
if shipped(id) && !req.force {
return Ok((
IngestAction::Skipped {
vendor: id.to_string(),
reason: "already shipped (pass --force to write a user overlay)".into(),
},
None,
));
}
let toml = profile_toml(vendor)?;
parse_profile_str(&toml)
.map_err(|e| vendor_err(id, &format!("emitted TOML failed parse: {e}")))?;
let path = dir.join(format!("{id}.toml"));
if path.exists() && !req.force {
return Ok((
IngestAction::Skipped {
vendor: id.to_string(),
reason: format!("already exists ({})", path.display()),
},
None,
));
}
if req.dry_run {
return Ok((IngestAction::DryRun(path), None));
}
Ok((IngestAction::Wrote(path), Some(toml)))
}
fn reason_is_hard(reason: &str) -> bool {
reason.starts_with("not a simple") || reason.starts_with("subscription")
}
fn parse_models_dev(value: &Value) -> Result<Vec<CatalogVendor>, IngestError> {
let obj = value
.as_object()
.ok_or_else(|| IngestError::Message("models.dev catalog must be a JSON object".into()))?;
let mut out = Vec::new();
for (key, row) in obj {
let Some(row) = row.as_object() else {
continue;
};
let id = row
.get("id")
.and_then(Value::as_str)
.unwrap_or(key)
.to_string();
let display_name = row
.get("name")
.and_then(Value::as_str)
.unwrap_or(&id)
.to_string();
let api = row
.get("api")
.and_then(Value::as_str)
.map(str::trim)
.filter(|s| !s.is_empty())
.map(ToOwned::to_owned);
let env = match row.get("env") {
Some(Value::Array(items)) => items
.iter()
.filter_map(Value::as_str)
.filter(|s| !s.is_empty())
.map(ToOwned::to_owned)
.collect(),
Some(Value::String(s)) if !s.is_empty() => vec![s.clone()],
_ => Vec::new(),
};
let npm = row
.get("npm")
.and_then(Value::as_str)
.map(str::trim)
.filter(|s| !s.is_empty())
.map(ToOwned::to_owned);
out.push(CatalogVendor {
id,
display_name,
api,
env,
npm,
});
}
Ok(out)
}
fn parse_litellm(value: &Value) -> Result<Vec<CatalogVendor>, IngestError> {
let obj = value.as_object().ok_or_else(|| {
IngestError::Message("LiteLLM providers.json must be a JSON object".into())
})?;
let mut out = Vec::new();
for (key, row) in obj {
let Some(row) = row.as_object() else {
continue;
};
let api = row
.get("base_url")
.and_then(Value::as_str)
.map(str::trim)
.filter(|s| !s.is_empty())
.map(ToOwned::to_owned);
let env = row
.get("api_key_env")
.and_then(Value::as_str)
.map(str::trim)
.filter(|s| !s.is_empty())
.map(|s| vec![s.to_string()])
.unwrap_or_default();
out.push(CatalogVendor {
id: key.clone(),
display_name: key.clone(),
api,
env,
npm: None,
});
}
Ok(out)
}
fn endpoint_for(vendor: &CatalogVendor, wire: EmitWire) -> Result<(String, String), IngestError> {
let raw = vendor
.api
.as_deref()
.map(str::trim)
.filter(|s| !s.is_empty())
.map(ToOwned::to_owned)
.or_else(|| well_known_api(&vendor.id, vendor.npm.as_deref()).map(str::to_string))
.ok_or_else(|| {
vendor_err(
&vendor.id,
"no api URL in catalog and no well-known OpenAI-compat endpoint",
)
})?;
let raw = rewrite_catalog_placeholders(&raw);
split_openai_compat_api(&raw, wire).map_err(|reason| vendor_err(&vendor.id, &reason))
}
fn rewrite_catalog_placeholders(input: &str) -> String {
let mut out = String::new();
let mut rest = input;
while !rest.is_empty() {
if let Some(after) = rest.strip_prefix("{env:")
&& let Some(end) = after.find('}')
{
let take = 5 + end + 1;
out.push_str(&rest[..take]);
rest = &rest[take..];
continue;
}
if let Some(after) = rest.strip_prefix("${")
&& let Some(end) = after.find('}')
{
let var = &after[..end];
if is_env_ident(var) {
out.push_str("{env:");
out.push_str(var);
out.push('}');
let take = 2 + end + 1;
rest = &rest[take..];
continue;
}
}
let ch = rest.chars().next().expect("rest is not empty");
out.push(ch);
rest = &rest[ch.len_utf8()..];
}
out
}
fn is_env_ident(s: &str) -> bool {
let mut chars = s.chars();
match chars.next() {
Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
_ => return false,
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
fn openai_compat_row(vendor: &CatalogVendor) -> bool {
if vendor.id.starts_with("azure")
|| vendor.id == "amazon-bedrock"
|| vendor.id.starts_with("google-vertex")
{
return false;
}
match vendor.npm.as_deref() {
Some(npm) => openai_compat_npm(npm),
None => true,
}
}
fn openai_compat_npm(npm: &str) -> bool {
matches!(
npm,
"@ai-sdk/openai-compatible"
| "@ai-sdk/openai"
| "@ai-sdk/groq"
| "@ai-sdk/togetherai"
| "@ai-sdk/mistral"
| "@ai-sdk/cerebras"
| "@ai-sdk/perplexity"
| "@ai-sdk/xai"
| "@ai-sdk/deepinfra"
| "@openrouter/ai-sdk-provider"
| "@ai-sdk/cohere"
)
}
fn well_known_api(id: &str, npm: Option<&str>) -> Option<&'static str> {
match (npm, id) {
(Some("@ai-sdk/groq"), _) | (_, "groq") => Some("https://api.groq.com/openai/v1"),
(Some("@ai-sdk/togetherai"), _) | (_, "togetherai" | "together") => {
Some("https://api.together.xyz/v1")
}
(Some("@ai-sdk/mistral"), _) | (_, "mistral") => Some("https://api.mistral.ai/v1"),
(Some("@ai-sdk/cerebras"), _) | (_, "cerebras") => Some("https://api.cerebras.ai/v1"),
(Some("@ai-sdk/perplexity"), _) | (_, "perplexity") => Some("https://api.perplexity.ai"),
(Some("@ai-sdk/xai"), _) | (_, "xai") => Some("https://api.x.ai/v1"),
(Some("@ai-sdk/deepinfra"), _) | (_, "deepinfra") => Some("https://api.deepinfra.com/v1"),
(Some("@ai-sdk/cohere"), _) | (_, "cohere") => {
Some("https://api.cohere.ai/compatibility/v1")
}
_ => None,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EmitWire {
ChatCompletions,
Messages,
Gemini,
Converse,
}
impl EmitWire {
fn toml_name(self) -> &'static str {
match self {
Self::ChatCompletions => "chat-completions",
Self::Messages => "messages",
Self::Gemini => "gemini",
Self::Converse => "converse",
}
}
fn auth_scheme(self) -> &'static str {
match self {
Self::Messages => "x-api-key",
Self::Gemini => "header:x-goog-api-key",
Self::ChatCompletions | Self::Converse => "bearer",
}
}
fn chat_suffix(self) -> &'static str {
match self {
Self::ChatCompletions => "/chat/completions",
Self::Messages => "/messages",
Self::Gemini => "/models/{model}:generateContent",
Self::Converse => "/converse",
}
}
}
fn split_openai_compat_api(raw: &str, wire: EmitWire) -> Result<(String, String), String> {
let raw = raw.trim().trim_end_matches('/');
if raw
.chars()
.any(|c| c.is_control() || matches!(c, '"' | '\\' | '<' | '>' | ' '))
{
return Err("URL contains a character that is not allowed".into());
}
let (scheme, rest) = match raw.split_once("://") {
Some(pair) => pair,
None => {
return Err(if scheme_less_hides_userinfo(raw) {
"not an absolute URL".to_string()
} else {
format!("not an absolute URL: {raw}")
});
}
};
if catalog_authority_has_userinfo(rest) {
return Err("api URL must not include userinfo".into());
}
if scheme != "https" && scheme != "http" {
return Err(format!(
"URL scheme must be https (or http on loopback): {raw}"
));
}
let (path_part, query) = split_catalog_query(rest);
let (host_port, path) = match path_part.split_once('/') {
Some((h, p)) => {
let p = p.trim_end_matches('/');
if p.is_empty() {
(h, String::new())
} else {
(h, format!("/{p}"))
}
}
None => (path_part, String::new()),
};
let host = match catalog_host_label(host_port) {
Ok(host) => host,
Err(()) => return Err("URL has no host".into()),
};
if scheme == "http" && !is_loopback_host(host) && !host.is_empty() {
return Err("http is only allowed for loopback hosts".into());
}
if host_port.is_empty() {
return Err("URL has no host".into());
}
let origin = format!("{scheme}://{host_port}");
let suffix = if host.eq_ignore_ascii_case("api.perplexity.ai")
&& matches!(wire, EmitWire::ChatCompletions)
&& (path.is_empty() || path == "/")
{
"/chat/completions".into()
} else if path.is_empty() || path == "/" {
match wire {
EmitWire::ChatCompletions => "/v1/chat/completions".into(),
EmitWire::Messages => "/v1/messages".into(),
EmitWire::Gemini => "/v1beta/models/{model}:generateContent".into(),
EmitWire::Converse => "/model/{model}/converse".into(),
}
} else {
format!("{path}{}", wire.chat_suffix())
};
let suffix = match query {
Some(query) => format!("{suffix}?{query}"),
None => suffix,
};
Ok((origin, suffix))
}
fn split_catalog_query(rest: &str) -> (&str, Option<&str>) {
let no_frag = rest.split_once('#').map(|(path, _)| path).unwrap_or(rest);
match no_frag.split_once('?') {
Some((path, query)) if !query.is_empty() => (path, Some(query)),
Some((path, _)) => (path, None),
None => (no_frag, None),
}
}
fn scheme_less_hides_userinfo(raw: &str) -> bool {
if raw.contains('@') {
return true;
}
raw.trim_start_matches('/')
.split(['/', '?', '#'])
.any(catalog_authority_has_userinfo)
}
fn catalog_authority_has_userinfo(rest: &str) -> bool {
let authority = rest.split(['/', '?', '#']).next().unwrap_or(rest);
let mut current = authority.as_bytes().to_vec();
let mut guard = current.len().saturating_add(1);
loop {
if bytes_contain_userinfo(¤t) {
return true;
}
if guard == 0 {
return false;
}
guard -= 1;
let decoded = percent_decode_bytes(¤t);
if decoded == current {
return false;
}
current = decoded;
}
}
fn bytes_contain_userinfo(bytes: &[u8]) -> bool {
if bytes.contains(&b'@') {
return true;
}
bytes.windows(3).any(|window| {
window[0] == b'%' && window[1].eq_ignore_ascii_case(&b'4') && window[2] == b'0'
})
}
fn percent_decode_bytes(input: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(input.len());
let mut index = 0;
while index < input.len() {
if input[index] == b'%'
&& index + 2 < input.len()
&& let (Some(hi), Some(lo)) =
(hex_nibble(input[index + 1]), hex_nibble(input[index + 2]))
{
out.push((hi << 4) | lo);
index += 3;
continue;
}
out.push(input[index]);
index += 1;
}
out
}
fn hex_nibble(byte: u8) -> Option<u8> {
match byte {
b'0'..=b'9' => Some(byte - b'0'),
b'a'..=b'f' => Some(byte - b'a' + 10),
b'A'..=b'F' => Some(byte - b'A' + 10),
_ => None,
}
}
fn catalog_host_label(host_port: &str) -> Result<&str, ()> {
if let Some(inner) = host_port.strip_prefix('[') {
let (host, rest) = inner.split_once(']').ok_or(())?;
let port_ok = rest.is_empty()
|| rest.strip_prefix(':').is_some_and(|port| {
!port.is_empty() && port.bytes().all(|byte| byte.is_ascii_digit())
});
if !port_ok || host.is_empty() {
return Err(());
}
return Ok(host);
}
if host_port.matches(':').count() > 1 {
return Err(());
}
Ok(host_port
.split(':')
.next()
.unwrap_or(host_port)
.split('{')
.next()
.unwrap_or(host_port))
}
fn is_loopback_host(host: &str) -> bool {
matches!(host, "127.0.0.1" | "localhost" | "::1" | "[::1]")
}
fn skip_id(id: &str) -> bool {
skip_reason_opt(id, None).is_some()
}
fn skip_reason(id: &str) -> &'static str {
skip_reason_opt(id, None).unwrap_or("skipped")
}
fn skip_reason_opt(id: &str, npm: Option<&str>) -> Option<&'static str> {
if id == "github-copilot" || id.contains("copilot") {
return Some("subscription / product-terms host; no shipped Copilot preset");
}
let _ = npm;
None
}
fn ingest_profile_dir() -> Option<PathBuf> {
user_overlay_dirs().into_iter().next_back()
}
fn user_overlay_dirs() -> Vec<PathBuf> {
let mut dirs = Vec::new();
if let Ok(xdg) = std::env::var("XDG_CONFIG_HOME")
&& !xdg.is_empty()
{
dirs.push(PathBuf::from(xdg).join("wiremux").join("profiles"));
} else if let Some(home) = home_dir() {
dirs.push(home.join(".config").join("wiremux").join("profiles"));
}
#[cfg(target_os = "macos")]
if let Some(home) = home_dir() {
dirs.push(
home.join("Library")
.join("Application Support")
.join("wiremux")
.join("profiles"),
);
}
if let Ok(extra) = std::env::var("WIREMUX_PROFILE_DIR") {
for part in std::env::split_paths(&extra) {
if !part.as_os_str().is_empty() {
dirs.push(part);
}
}
}
dirs
}
fn home_dir() -> Option<PathBuf> {
std::env::var_os("HOME")
.or_else(|| std::env::var_os("USERPROFILE"))
.filter(|raw| !raw.is_empty())
.map(PathBuf::from)
}
fn shipped(id: &str) -> bool {
let opts = LoadOptions {
include_user_config: false,
include_shipped: true,
..LoadOptions::default()
};
list_profiles(&opts)
.ok()
.is_some_and(|ids| ids.iter().any(|s| s == id))
}
fn refuse_bad_id(id: &str) -> Result<(), IngestError> {
if id.is_empty() || id.len() > 64 {
return Err(vendor_err(id, "id must be 1..=64 characters"));
}
let mut chars = id.chars();
let Some(first) = chars.next() else {
return Err(vendor_err(id, "empty id"));
};
if !first.is_ascii_lowercase() && !first.is_ascii_digit() {
return Err(vendor_err(id, "id must start with [a-z0-9]"));
}
if !chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-') {
return Err(vendor_err(id, "id must match [a-z0-9][a-z0-9-]*"));
}
Ok(())
}
fn toml_string(s: &str) -> String {
let mut out = String::from("\"");
for c in s.chars() {
match c {
'\\' => out.push_str("\\\\"),
'"' => out.push_str("\\\""),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c => out.push(c),
}
}
out.push('"');
out
}
fn toml_access_env(env: &[String]) -> String {
if env.len() == 1 {
return toml_string(&env[0]);
}
let parts: Vec<String> = env.iter().map(|s| toml_string(s)).collect();
format!("[{}]", parts.join(", "))
}
fn vendor_err(id: &str, reason: &str) -> IngestError {
IngestError::Message(format!("{id}: {reason}"))
}
#[cfg(test)]
mod tests {
use super::*;
const MODELS_DEV_FIXTURE: &str = r#"
{
"groq": {
"id": "groq",
"name": "Groq",
"env": ["GROQ_API_KEY"],
"npm": "@ai-sdk/groq",
"doc": "https://console.groq.com/docs/models"
},
"deepseek": {
"id": "deepseek",
"name": "DeepSeek",
"env": ["DEEPSEEK_API_KEY"],
"npm": "@ai-sdk/openai-compatible",
"api": "https://api.deepseek.com"
},
"togetherai": {
"id": "togetherai",
"name": "Together AI",
"env": ["TOGETHER_API_KEY"],
"npm": "@ai-sdk/togetherai"
},
"fireworks-ai": {
"id": "fireworks-ai",
"name": "Fireworks AI",
"env": ["FIREWORKS_API_KEY"],
"npm": "@ai-sdk/openai-compatible",
"api": "https://api.fireworks.ai/inference/v1/"
},
"mistral": {
"id": "mistral",
"name": "Mistral",
"env": ["MISTRAL_API_KEY"],
"npm": "@ai-sdk/mistral"
},
"cerebras": {
"id": "cerebras",
"name": "Cerebras",
"env": ["CEREBRAS_API_KEY"],
"npm": "@ai-sdk/cerebras"
},
"azure": {
"id": "azure",
"name": "Azure",
"env": ["AZURE_RESOURCE_NAME", "AZURE_API_KEY"],
"npm": "@ai-sdk/azure"
},
"minimax": {
"id": "minimax",
"name": "MiniMax",
"env": ["MINIMAX_API_KEY"],
"npm": "@ai-sdk/anthropic",
"api": "https://api.minimax.io/anthropic/v1"
},
"kimi-for-coding": {
"id": "kimi-for-coding",
"name": "Kimi for coding",
"env": ["KIMI_API_KEY"],
"npm": "@ai-sdk/anthropic",
"api": "https://api.kimi.com/coding/v1"
},
"cloudflare-workers-ai": {
"id": "cloudflare-workers-ai",
"name": "Cloudflare Workers AI",
"env": ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_KEY"],
"npm": "@ai-sdk/openai-compatible",
"api": "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1"
},
"databricks": {
"id": "databricks",
"name": "Databricks",
"env": ["DATABRICKS_HOST", "DATABRICKS_TOKEN"],
"npm": "@ai-sdk/openai-compatible",
"api": "https://${DATABRICKS_HOST}/ai-gateway/mlflow/v1"
},
"google-vertex": {
"id": "google-vertex",
"name": "Vertex",
"env": ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION"],
"npm": "@ai-sdk/google-vertex"
},
"google-vertex-anthropic": {
"id": "google-vertex-anthropic",
"name": "Vertex Anthropic",
"env": ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION"],
"npm": "@ai-sdk/google-vertex/anthropic"
},
"amazon-bedrock": {
"id": "amazon-bedrock",
"name": "Amazon Bedrock",
"env": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_BEARER_TOKEN_BEDROCK"],
"npm": "@ai-sdk/amazon-bedrock"
},
"cohere": {
"id": "cohere",
"name": "Cohere",
"env": ["COHERE_API_KEY"],
"npm": "@ai-sdk/cohere"
},
"openai": {
"id": "openai",
"name": "OpenAI",
"env": ["OPENAI_API_KEY"],
"npm": "@ai-sdk/openai"
},
"github-copilot": {
"id": "github-copilot",
"name": "GitHub Copilot",
"env": ["GITHUB_TOKEN"],
"npm": "@ai-sdk/openai-compatible",
"api": "https://api.githubcopilot.com"
}
}
"#;
const LITELLM_FIXTURE: &str = r#"
{
"publicai": {
"base_url": "https://api.publicai.co/v1",
"api_key_env": "PUBLICAI_API_KEY"
}
}
"#;
#[test]
fn groq_uses_vercel_default_url() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let groq = rows.iter().find(|v| v.id == "groq").unwrap();
let toml = profile_toml(groq).unwrap();
assert!(toml.contains("id = \"groq\""));
assert!(toml.contains("base_url = \"https://api.groq.com\""));
assert!(toml.contains("chat_path = \"/openai/v1/chat/completions\""));
assert!(toml.contains("access_env = \"GROQ_API_KEY\""));
parse_profile_str(&toml).unwrap();
}
#[test]
fn deepseek_splits_catalog_api() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let v = rows.iter().find(|r| r.id == "deepseek").unwrap();
let toml = profile_toml(v).unwrap();
assert!(toml.contains("base_url = \"https://api.deepseek.com\""));
assert!(toml.contains("chat_path = \"/v1/chat/completions\""));
}
#[test]
fn fireworks_keeps_inference_prefix() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let v = rows.iter().find(|r| r.id == "fireworks-ai").unwrap();
let toml = profile_toml(v).unwrap();
assert!(toml.contains("base_url = \"https://api.fireworks.ai\""));
assert!(toml.contains("chat_path = \"/inference/v1/chat/completions\""));
}
#[test]
fn copilot_still_fail_closed() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let copilot = rows.iter().find(|r| r.id == "github-copilot").unwrap();
let err = profile_toml(copilot).unwrap_err().to_string();
assert!(
err.contains("product-terms") || err.contains("Copilot"),
"{err}"
);
}
#[test]
fn minimax_and_kimi_emit_messages_wire() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let mini = rows.iter().find(|r| r.id == "minimax").unwrap();
let toml = profile_toml(mini).unwrap();
assert!(toml.contains("wire = \"messages\""), "{toml}");
assert!(toml.contains("base_url = \"https://api.minimax.io\""));
assert!(toml.contains("chat_path = \"/anthropic/v1/messages\""));
assert!(toml.contains("auth_scheme = \"bearer\""));
parse_profile_str(&toml).unwrap();
let kimi = rows.iter().find(|r| r.id == "kimi-for-coding").unwrap();
let toml = profile_toml(kimi).unwrap();
assert!(toml.contains("wire = \"messages\""));
assert!(toml.contains("https://api.kimi.com"));
assert!(toml.contains("/coding/v1/messages"));
parse_profile_str(&toml).unwrap();
}
#[test]
fn catalog_dollar_env_becomes_env_placeholder() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let cf = rows
.iter()
.find(|r| r.id == "cloudflare-workers-ai")
.unwrap();
let toml = profile_toml(cf).unwrap();
assert!(
toml.contains("/accounts/{env:CLOUDFLARE_ACCOUNT_ID}/ai/v1/chat/completions"),
"{toml}"
);
assert!(!toml.contains("${CLOUDFLARE_ACCOUNT_ID}"), "{toml}");
parse_profile_str(&toml).unwrap();
let vendor = CatalogVendor {
id: "odd".into(),
display_name: "Odd".into(),
api: Some("https://exämple.com/${ODD_HOST}/v1".into()),
env: vec!["ODD_API_KEY".into()],
npm: Some("@ai-sdk/openai-compatible".into()),
};
let toml = profile_toml(&vendor).expect("non-ASCII catalog URL must not panic");
assert!(toml.contains("https://exämple.com"), "{toml}");
assert!(toml.contains("{env:ODD_HOST}"), "{toml}");
assert!(!toml.contains("${ODD_HOST}"), "{toml}");
let db = rows.iter().find(|r| r.id == "databricks").unwrap();
let toml = profile_toml(db).unwrap();
assert!(
toml.contains("base_url = \"https://{env:DATABRICKS_HOST}\""),
"{toml}"
);
assert!(toml.contains("/ai-gateway/mlflow/v1/chat/completions"));
parse_profile_str(&toml).unwrap();
}
#[test]
fn azure_emits_deployment_path_template() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let azure = rows.iter().find(|r| r.id == "azure").unwrap();
let toml = profile_toml(azure).unwrap();
assert!(toml.contains("https://{env:AZURE_RESOURCE_NAME}.openai.azure.com"));
assert!(toml.contains("/openai/deployments/{model}/chat/completions?api-version="));
assert!(toml.contains("auth_scheme = \"header:api-key\""));
parse_profile_str(&toml).unwrap();
}
#[test]
fn vertex_and_bedrock_and_cohere_emit() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let vertex = rows.iter().find(|r| r.id == "google-vertex").unwrap();
let toml = profile_toml(vertex).unwrap();
assert!(toml.contains("wire = \"gemini\""), "{toml}");
assert!(toml.contains("gcp_key_env = \"GOOGLE_APPLICATION_CREDENTIALS\""));
assert!(toml.contains("{env:GOOGLE_VERTEX_PROJECT}"));
assert!(toml.contains(":generateContent"));
parse_profile_str(&toml).unwrap();
let vanth = rows
.iter()
.find(|r| r.id == "google-vertex-anthropic")
.unwrap();
let toml = profile_toml(vanth).unwrap();
assert!(toml.contains("wire = \"messages\""));
assert!(toml.contains(":rawPredict"));
assert!(toml.contains("anthropic-version"));
assert!(toml.contains("anthropic_version = \"vertex-2023-10-16\""));
parse_profile_str(&toml).unwrap();
let bedrock = rows.iter().find(|r| r.id == "amazon-bedrock").unwrap();
let toml = profile_toml(bedrock).unwrap();
assert!(toml.contains("wire = \"converse\""), "{toml}");
assert!(toml.contains("/model/{model}/converse"));
assert!(toml.contains("aws_service = \"bedrock\""));
parse_profile_str(&toml).unwrap();
let cohere = rows.iter().find(|r| r.id == "cohere").unwrap();
let toml = profile_toml(cohere).unwrap();
assert!(toml.contains("https://api.cohere.ai"));
assert!(toml.contains("/compatibility/v1/chat/completions"));
parse_profile_str(&toml).unwrap();
}
#[test]
fn popular_write_skips_shipped_openai() {
let dir = tempfile::tempdir().unwrap();
let report = ingest_catalog(
MODELS_DEV_FIXTURE,
&IngestRequest {
dir: Some(dir.path().to_path_buf()),
..IngestRequest::default()
},
)
.unwrap();
let wrote: Vec<String> = report
.actions
.iter()
.filter_map(|a| match a {
IngestAction::Wrote(p) => p.file_stem()?.to_str().map(str::to_string),
_ => None,
})
.collect();
let expected: Vec<&str> = POPULAR_VENDORS
.iter()
.copied()
.filter(|id| !shipped(id))
.collect();
assert_eq!(wrote, expected);
for id in &expected {
let path = dir.path().join(format!("{id}.toml"));
assert!(path.is_file(), "missing {id}");
parse_profile_str(&fs::read_to_string(path).unwrap()).unwrap();
}
for id in POPULAR_VENDORS.iter().copied().filter(|id| shipped(id)) {
assert!(
!dir.path().join(format!("{id}.toml")).exists(),
"shipped {id} must not be rewritten"
);
}
assert!(!dir.path().join("openai.toml").exists());
assert!(!dir.path().join("azure.toml").exists());
}
#[test]
fn shipped_id_absent_from_catalog_is_not_in_catalog() {
assert!(
shipped("groq"),
"test needs a shipped id missing from LITELLM_FIXTURE"
);
let dir = tempfile::tempdir().unwrap();
let err = ingest_catalog(
LITELLM_FIXTURE,
&IngestRequest {
kind: CatalogKind::LiteLlm,
vendors: vec!["groq".into()],
dir: Some(dir.path().to_path_buf()),
dry_run: true,
..IngestRequest::default()
},
)
.unwrap_err()
.to_string();
assert!(err.contains("groq"), "{err}");
assert!(err.contains("not in catalog"), "{err}");
assert!(
!err.contains("already shipped"),
"catalog miss must win over shipped skip, got {err}"
);
}
#[test]
fn requested_unknown_vendor_is_hard_error() {
let dir = tempfile::tempdir().unwrap();
let err = ingest_catalog(
MODELS_DEV_FIXTURE,
&IngestRequest {
vendors: vec!["no-such-vendor".into()],
dir: Some(dir.path().to_path_buf()),
..IngestRequest::default()
},
)
.unwrap_err()
.to_string();
assert!(err.contains("no-such-vendor"), "{err}");
assert!(err.contains("not in catalog"), "{err}");
}
#[test]
fn mixed_vendor_failure_does_not_write_the_good_profile() {
let dir = tempfile::tempdir().unwrap();
let keep = dir.path().join("odd.toml");
fs::write(&keep, "keep\n").unwrap();
let err = ingest_catalog(
r#"{"odd":{"id":"odd","name":"Odd","env":["ODD_API_KEY"],"npm":"@ai-sdk/openai-compatible","api":"https://odd.example/v1"}}"#,
&IngestRequest {
vendors: vec!["odd".into(), "missing".into()],
dir: Some(dir.path().to_path_buf()),
force: true,
..IngestRequest::default()
},
)
.unwrap_err()
.to_string();
assert!(err.contains("missing: not in catalog"), "{err}");
assert!(err.contains("not written:"), "{err}");
assert_eq!(fs::read_to_string(&keep).unwrap(), "keep\n");
}
#[test]
fn mixed_vendor_dry_run_names_the_profile_it_did_not_write() {
let dir = tempfile::tempdir().unwrap();
let err = ingest_catalog(
r#"{"odd":{"id":"odd","name":"Odd","env":["ODD_API_KEY"],"npm":"@ai-sdk/openai-compatible","api":"https://odd.example/v1"}}"#,
&IngestRequest {
vendors: vec!["odd".into(), "missing".into()],
dir: Some(dir.path().to_path_buf()),
dry_run: true,
..IngestRequest::default()
},
)
.unwrap_err()
.to_string();
assert!(err.contains("missing: not in catalog"), "{err}");
assert!(
err.contains("odd.toml") && err.contains("not written:"),
"{err}"
);
assert!(!dir.path().join("odd.toml").exists());
}
#[test]
fn split_openai_compat_rejects_quote() {
let err = split_openai_compat_api(
"https://example.test/v1\"\nchat_path = \"/owned",
EmitWire::ChatCompletions,
)
.expect_err("quote");
assert!(err.contains("not allowed"), "{err}");
assert!(!err.contains("chat_path"));
}
#[test]
fn split_openai_compat_rejects_userinfo() {
let err = split_openai_compat_api(
"https://user:s3cret@api.example.com/v1",
EmitWire::ChatCompletions,
)
.expect_err("userinfo");
assert!(err.contains("userinfo"), "{err}");
assert!(err.contains("api URL"), "{err}");
assert!(!err.contains("s3cret"), "{err}");
assert!(!err.contains("user:"), "{err}");
let err =
split_openai_compat_api("user:s3cret@api.example.com/v1", EmitWire::ChatCompletions)
.expect_err("no scheme");
assert!(err.contains("absolute"), "{err}");
assert!(!err.contains("s3cret"), "{err}");
let err = profile_toml(&CatalogVendor {
id: "evilauth".into(),
display_name: "evilauth".into(),
api: Some("https://User:S3cret@api.example.com/v1".into()),
env: vec!["EVIL_KEY".into()],
npm: Some("@ai-sdk/openai-compatible".into()),
})
.unwrap_err()
.to_string();
assert!(err.contains("evilauth"), "{err}");
assert!(err.contains("userinfo"), "{err}");
assert!(!err.contains("S3cret"), "{err}");
let (origin, path) =
split_openai_compat_api("https://api.example.com/v1", EmitWire::ChatCompletions)
.expect("plain https");
assert_eq!(origin, "https://api.example.com");
assert_eq!(path, "/v1/chat/completions");
let (origin, path) =
split_openai_compat_api("https://{env:HOST}/v1", EmitWire::ChatCompletions)
.expect("env placeholder");
assert_eq!(origin, "https://{env:HOST}");
assert_eq!(path, "/v1/chat/completions");
let (origin, path) = split_openai_compat_api(
"https://api.example.com/v1?user=a@b",
EmitWire::ChatCompletions,
)
.expect("at sign in query");
assert_eq!(origin, "https://api.example.com");
assert_eq!(path, "/v1/chat/completions?user=a@b");
let err = split_openai_compat_api(
"https://user:s3cret%40api.example.com/v1",
EmitWire::ChatCompletions,
)
.expect_err("encoded at");
assert!(err.contains("userinfo"), "{err}");
assert!(!err.contains("s3cret"), "{err}");
let (origin, path) = split_openai_compat_api(
"https://api.example.com/v1?next=%40",
EmitWire::ChatCompletions,
)
.expect("encoded at in query");
assert_eq!(origin, "https://api.example.com");
assert_eq!(path, "/v1/chat/completions?next=%40");
let (origin, path) = split_openai_compat_api(
"https://api.example.com/v1?api-version=2024-10-21#ignored",
EmitWire::ChatCompletions,
)
.expect("query and fragment");
assert_eq!(origin, "https://api.example.com");
assert_eq!(path, "/v1/chat/completions?api-version=2024-10-21");
assert!(!path.contains('#'));
}
#[test]
fn split_openai_compat_rejects_nested_percent_userinfo() {
fn nest_at(layers: usize) -> String {
let mut token = "@".to_string();
for _ in 0..layers {
let mut encoded = String::new();
for byte in token.bytes() {
match byte {
b'%' => encoded.push_str("%25"),
b'@' => encoded.push_str("%40"),
other => encoded.push(other as char),
}
}
token = encoded;
}
token
}
for layers in 2..=4 {
let url = format!("https://user:s3cret{}api.example.com/v1", nest_at(layers));
let err = split_openai_compat_api(&url, EmitWire::ChatCompletions).expect_err(&url);
assert!(err.contains("userinfo"), "{url}: {err}");
assert!(!err.contains("s3cret"), "{url}: {err}");
assert!(!err.contains("user:"), "{url}: {err}");
}
let mixed = "https://user:s3cret%2540api.example.com/v1";
let err = split_openai_compat_api(mixed, EmitWire::ChatCompletions).expect_err("mixed hex");
assert!(err.contains("userinfo"), "{err}");
assert!(!err.contains("s3cret"), "{err}");
let encoded_colon = "https://user%3As3cret%2540api.example.com/v1";
let err = split_openai_compat_api(encoded_colon, EmitWire::ChatCompletions)
.expect_err("encoded colon");
assert!(err.contains("userinfo"), "{err}");
assert!(!err.contains("s3cret"), "{err}");
let bare = "user:s3cret%2540api.example.com/v1";
let err = split_openai_compat_api(bare, EmitWire::ChatCompletions).expect_err("no scheme");
assert!(err.contains("absolute"), "{err}");
assert!(!err.contains("s3cret"), "{err}");
assert!(!err.contains("%2540"), "{err}");
for bare in [
"//user:s3cret%2540api.example.com/v1",
"http:/user:s3cret%2540api.example.com/v1",
] {
let err = split_openai_compat_api(bare, EmitWire::ChatCompletions).expect_err(bare);
assert!(err.contains("absolute"), "{bare}: {err}");
assert!(!err.contains("s3cret"), "{bare}: {err}");
assert!(!err.contains("%2540"), "{bare}: {err}");
}
let err = split_openai_compat_api("example.com/v1", EmitWire::ChatCompletions)
.expect_err("host without scheme");
assert!(err.contains("example.com/v1"), "{err}");
let (origin, path) = split_openai_compat_api(
"https://api.example.com/v1?user=a%2540b",
EmitWire::ChatCompletions,
)
.expect("nested percent in query");
assert_eq!(origin, "https://api.example.com");
assert_eq!(path, "/v1/chat/completions?user=a%2540b");
let (origin, path) =
split_openai_compat_api("https://api%2Eexample.com/v1", EmitWire::ChatCompletions)
.expect("encoded dot is not userinfo");
assert_eq!(origin, "https://api%2Eexample.com");
assert_eq!(path, "/v1/chat/completions");
let (origin, path) = split_openai_compat_api(
"https://api.example.com/v1#user:s3cret@hidden",
EmitWire::ChatCompletions,
)
.expect("fragment is not authority");
assert_eq!(origin, "https://api.example.com");
assert!(!path.contains("s3cret"), "{path}");
let dir = tempfile::tempdir().unwrap();
let err = ingest_catalog(
r#"{"evilpct":{"id":"evilpct","name":"Evil","env":["EVIL_KEY"],"npm":"@ai-sdk/openai-compatible","api":"https://user:s3cret%2540api.example.com/v1"}}"#,
&IngestRequest {
vendors: vec!["evilpct".into()],
dir: Some(dir.path().to_path_buf()),
..IngestRequest::default()
},
)
.unwrap_err()
.to_string();
assert!(err.contains("evilpct"), "{err}");
assert!(err.contains("userinfo"), "{err}");
assert!(!err.contains("s3cret"), "{err}");
assert!(
!dir.path().join("evilpct.toml").exists(),
"refused userinfo must not write a profile"
);
}
#[test]
fn refuse_http_non_loopback() {
let v = CatalogVendor {
id: "evil".into(),
display_name: "evil".into(),
api: Some("http://example.invalid/v1".into()),
env: vec!["EVIL_KEY".into()],
npm: Some("@ai-sdk/openai-compatible".into()),
};
let err = profile_toml(&v).unwrap_err().to_string();
assert!(err.contains("loopback"), "{err}");
}
#[test]
fn http_ipv6_loopback_is_allowed() {
let (origin, path) =
split_openai_compat_api("http://[::1]:11434/v1", EmitWire::ChatCompletions)
.expect("bracketed ipv6 loopback");
assert_eq!(origin, "http://[::1]:11434");
assert_eq!(path, "/v1/chat/completions");
let (origin, path) = split_openai_compat_api("http://[::1]/", EmitWire::ChatCompletions)
.expect("bracketed ipv6 without port");
assert_eq!(origin, "http://[::1]");
assert_eq!(path, "/v1/chat/completions");
let (origin, path) =
split_openai_compat_api("http://127.0.0.1:11434/v1", EmitWire::ChatCompletions)
.expect("ipv4 loopback keeps its port");
assert_eq!(origin, "http://127.0.0.1:11434");
assert_eq!(path, "/v1/chat/completions");
for raw in [
"http://::1",
"http://::1/v1",
"http://[::1]evil/v1",
"http://[::1]example.com/v1",
"http://[::1]:/v1",
"http://[::1]:abc/v1",
"http://[::1]:80x/v1",
"http://[]",
"http://[]:80/v1",
] {
let err = split_openai_compat_api(raw, EmitWire::ChatCompletions).expect_err(raw);
assert!(err.contains("host"), "{raw}: {err}");
}
}
#[test]
fn refuse_unknown_fetch_url() {
let err = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(fetch_catalog_url("https://example.invalid/catalog.json"))
.unwrap_err()
.to_string();
assert!(err.contains("refused"), "{err}");
}
#[test]
fn litellm_long_tail_row() {
let rows = parse_catalog(CatalogKind::LiteLlm, LITELLM_FIXTURE).unwrap();
assert_eq!(rows.len(), 1);
let toml = profile_toml(&rows[0]).unwrap();
assert!(toml.contains("id = \"publicai\""));
assert!(toml.contains("base_url = \"https://api.publicai.co\""));
assert!(toml.contains("chat_path = \"/v1/chat/completions\""));
assert!(toml.contains("access_env = \"PUBLICAI_API_KEY\""));
}
#[test]
fn existing_file_skipped_without_force() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("togetherai.toml");
fs::write(&path, "stale").unwrap();
let report = ingest_catalog(
MODELS_DEV_FIXTURE,
&IngestRequest {
vendors: vec!["togetherai".into()],
dir: Some(dir.path().to_path_buf()),
..IngestRequest::default()
},
)
.unwrap();
match &report.actions[0] {
IngestAction::Skipped { reason, .. } => {
assert!(reason.contains("already exists"), "{reason}");
}
other => panic!("{other:?}"),
}
assert_eq!(fs::read_to_string(path).unwrap(), "stale");
}
#[test]
fn all_compatible_writes_openai_compat_only() {
let dir = tempfile::tempdir().unwrap();
let report = ingest_catalog(
MODELS_DEV_FIXTURE,
&IngestRequest {
all_compatible: true,
dir: Some(dir.path().to_path_buf()),
..IngestRequest::default()
},
)
.unwrap();
let wrote: Vec<String> = report
.actions
.iter()
.filter_map(|a| match a {
IngestAction::Wrote(p) => p.file_stem()?.to_str().map(str::to_string),
_ => None,
})
.collect();
assert!(
wrote.contains(&"togetherai".into()),
"togetherai missing: {wrote:?}"
);
assert!(dir.path().join("togetherai.toml").is_file());
assert!(
!wrote.contains(&"groq".into()),
"shipped groq must be skipped: {wrote:?}"
);
assert!(!dir.path().join("azure.toml").exists());
assert!(!dir.path().join("amazon-bedrock.toml").exists());
assert!(!dir.path().join("google-vertex.toml").exists());
assert!(!dir.path().join("google-vertex-anthropic.toml").exists());
assert!(!wrote.iter().any(|id| id.starts_with("azure")));
assert!(!wrote.iter().any(|id| id == "amazon-bedrock"));
assert!(!wrote.iter().any(|id| id.starts_with("google-vertex")));
}
#[test]
fn all_compatible_with_no_matching_rows_errors_without_dir() {
let dir = tempfile::tempdir().unwrap();
let out = dir.path().join("out");
let err = ingest_catalog(
"{}",
&IngestRequest {
all_compatible: true,
dir: Some(out.clone()),
..IngestRequest::default()
},
)
.unwrap_err();
let shown = err.to_string();
assert!(shown.contains("no catalog rows matched"), "{shown}");
assert!(!out.exists(), "out must not be created");
}
#[test]
fn unknown_npm_package_is_not_a_profile() {
let dir = tempfile::tempdir().unwrap();
let err = ingest_catalog(
r#"{"odd":{"id":"odd","name":"Odd","env":["ODD_API_KEY"],"npm":"@ai-sdk/not-a-dialect","api":"https://odd.example"}}"#,
&IngestRequest {
vendors: vec!["odd".into()],
dir: Some(dir.path().to_path_buf()),
..IngestRequest::default()
},
)
.unwrap_err();
assert!(
err.to_string().contains("not a known HTTP dialect"),
"{err}"
);
assert!(!dir.path().join("odd.toml").exists());
}
#[test]
fn blank_home_does_not_write_a_relative_profile_dir() {
let _home = wiremux_auth::IsolatedHome::new();
unsafe {
std::env::set_var("HOME", "");
std::env::set_var("USERPROFILE", "");
}
let err = ingest_catalog(
r#"{"groq":{"id":"groq","name":"Groq","env":["GROQ_API_KEY"],"npm":"@ai-sdk/groq"}}"#,
&IngestRequest {
vendors: vec!["groq".into()],
dir: None,
..IngestRequest::default()
},
)
.unwrap_err();
assert!(err.to_string().contains("no user profile dir"), "{err}");
}
#[test]
fn explicit_vendor_azure_still_writes() {
let dir = tempfile::tempdir().unwrap();
ingest_catalog(
MODELS_DEV_FIXTURE,
&IngestRequest {
vendors: vec!["azure".into()],
dir: Some(dir.path().to_path_buf()),
..IngestRequest::default()
},
)
.unwrap();
assert!(dir.path().join("azure.toml").is_file());
}
#[test]
fn databricks_access_env_is_token_only() {
let rows = parse_catalog(CatalogKind::ModelsDev, MODELS_DEV_FIXTURE).unwrap();
let db = rows.iter().find(|r| r.id == "databricks").unwrap();
let toml = profile_toml(db).unwrap();
let access = toml
.lines()
.find(|l| l.starts_with("access_env"))
.expect("access_env");
assert!(access.contains("DATABRICKS_TOKEN"), "{access}");
assert!(
!access.contains("DATABRICKS_HOST"),
"host belongs in the URL, not access_env: {access}"
);
parse_profile_str(&toml).unwrap();
}
#[test]
fn azure_without_resource_name_fails() {
let v = CatalogVendor {
id: "azure".into(),
display_name: "Azure".into(),
api: None,
env: vec!["AZURE_API_KEY".into()],
npm: Some("@ai-sdk/azure".into()),
};
let err = profile_toml(&v).unwrap_err().to_string();
assert!(err.contains("RESOURCE_NAME"), "{err}");
}
#[test]
fn vertex_without_project_fails() {
let v = CatalogVendor {
id: "google-vertex".into(),
display_name: "Vertex".into(),
api: None,
env: vec!["GOOGLE_VERTEX_LOCATION".into()],
npm: Some("@ai-sdk/google-vertex".into()),
};
let err = profile_toml(&v).unwrap_err().to_string();
assert!(
err.contains("GOOGLE_VERTEX_PROJECT") || err.contains("PROJECT"),
"{err}"
);
}
#[tokio::test]
async fn catalog_body_over_caller_cap_is_an_error() {
use std::io::{Read, Write};
assert_eq!(MAX_CATALOG_BODY, 16 * 1024 * 1024);
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let mut body = "a".repeat(80);
body.push_str("TAILMARK");
let handle = std::thread::spawn(move || {
let (mut stream, _) = listener.accept().unwrap();
stream
.set_read_timeout(Some(std::time::Duration::from_secs(2)))
.ok();
let mut buf = [0u8; 1024];
let mut seen = Vec::new();
loop {
match stream.read(&mut buf) {
Ok(0) => break,
Ok(n) => {
seen.extend_from_slice(&buf[..n]);
if seen.windows(4).any(|w| w == b"\r\n\r\n") {
break;
}
}
Err(_) => break,
}
}
let len = body.len();
let resp = format!(
"HTTP/1.1 200 OK\r\nContent-Length: {len}\r\nConnection: close\r\n\r\n{body}"
);
let _ = stream.write_all(resp.as_bytes());
});
let url = format!("http://{addr}/catalog");
let client = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.no_proxy()
.build()
.unwrap();
let response = client.get(&url).send().await.unwrap();
let err = read_catalog_http_body(&url, response, 64)
.await
.expect_err("caller cap");
let shown = err.to_string();
assert!(
shown.contains("too large") || shown.contains("exceeds"),
"{shown}"
);
assert!(shown.contains("fetch "), "{shown}");
assert!(!shown.contains("TAILMARK"), "{shown}");
let _ = handle.join();
}
}