Step-by-step checklist for adding a new inference provider to openshell-image-builder, covering the trait implementation, agent wiring, unit tests, integration tests, and README
التثبيت
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Step-by-step checklist for adding a new inference provider to openshell-image-builder, covering the trait implementation, agent wiring, unit tests, integration tests, and README
argument-hint
<provider-name>
Add Inference Provider
End-to-end checklist for making a new LLM backend available via --inference.
Description
Adding a new inference provider touches six layers: the inference module, main.rs, each agent that will support it, unit tests throughout, integration tests, and the README. The existing providers are the canonical reference:
anthropic — cloud, fixed default endpoint, supports --endpoint override, bakes env var into image for Claude.
policy_yaml returns a YAML fragment that is merged into the sandbox policy. It receives:
agent_binary — the absolute path to the agent binary (scope the policy to it).
base_url — the resolved endpoint URL when --endpoint was passed (and the provider supports it); None otherwise.
Follow the existing pattern closest to the new provider:
Cloud provider with --endpoint support (anthropic pattern): if base_url is Some, parse its host and port with super::parse_host_port and use them; otherwise use the provider's default hostnames.
Local provider (ollama pattern): call base_url.and_then(super::parse_host_port).unwrap_or_else(|| (DEFAULT_HOST, DEFAULT_PORT)). Export DEFAULT_BASE_URL as a pub(crate) const so main.rs can reference it for the localhost-rewrite logic.
The YAML shape must follow the existing structure — one network policy with name, endpoints, and binaries:
Unit tests to write in the same file (inside #[cfg(test)] mod tests):
policy_yaml_contains_<provider>_endpoint — default host appears in output.
policy_yaml_embeds_agent_binary — agent binary path appears in output.
policy_yaml_has_<provider>_name — name: myprovider appears in output.
If --endpoint is supported: policy_yaml_with_custom_endpoint_uses_proxy_host, policy_yaml_with_custom_endpoint_omits_default_host, policy_yaml_with_custom_endpoint_custom_port.
If endpoint is fixed: one test confirming base_url is ignored (pass a URL and assert the fixed host still appears).
Step 2 — register in src/inference/mod.rs
Four additions:
mod myprovider; // 1. declare the modulepub(crate) use myprovider::DEFAULT_BASE_URL as MYPROVIDER_DEFAULT_BASE_URL; // 2. re-export default URL (if local provider)#[cfg(test)]pubuse myprovider::MyProviderInference; // 3. export for test use// 4. add variant to the enum#[derive(Clone, PartialEq, ValueEnum)]pubenumInferenceKind {
Anthropic,
Ollama,
#[value(name = "vertexai")]
VertexAi,
MyProvider, // add here
}
// 5. add arm to from_kind()pubfnfrom_kind(kind: InferenceKind) ->Box<dyn Inference> {
match kind {
...
InferenceKind::MyProvider => Box::new(myprovider::MyProviderInference),
}
}
If the CLI value should differ from the Rust variant name (e.g. vertexai instead of VertexAi), add #[value(name = "myprovider")] above the variant.
Step 3 — wire into main.rs
resolve_base_url() — local providers only
If the new provider has a default local URL that needs localhost → host.openshell.internal rewriting, add a match arm following the Ollama pattern:
For cloud providers this is not needed — resolve_base_url should return None unless --endpoint is given (anthropic pattern) or always None (vertexai pattern).
Validation in run() — if --endpoint is unsupported
If the new provider has a proprietary fixed endpoint and must reject --endpoint, add an early check at the top of run():
if endpoint.is_some() && inference_kind == Some(inference::InferenceKind::MyProvider) {
returnErr("--endpoint is not supported for the myprovider inference provider".into());
}
Step 4 — update each agent that supports the new inference
For each agent that should work with the new provider:
set_inference() — if the provider requires agent config files
If using the new provider with a particular agent should write config files into the image (like Ollama writes .config/opencode/config.json for opencode), add an arm to set_inference():
The configure function lives in src/agent/opencode/myprovider.rs (a new submodule), following the structure of src/agent/opencode/anthropic.rs and src/agent/opencode/ollama.rs.
env_vars() — if the provider requires baked-in environment variables
If the new provider requires an env var baked into the image (like ANTHROPIC_BASE_URL for Claude + Anthropic), add a branch to env_vars():
If the new inference produces a distinguishable policy entry, add a check_myprovider_policy(image, expected) helper following check_anthropic_policy / check_ollama_policy.
Then add has_myprovider to the image_tests! macro — add the parameter, add a generated policy_has_myprovider_rules test inside the macro body, and update every existing image_tests! call to pass has_myprovider: false (or true for the new combinations).
Image singletons and accessors
Add one OnceLock + accessor per agent that supports the new inference, following the naming convention <base_image>_<agent>_<provider>_image. At minimum add ubuntu variants:
Add the new provider to the canonical reference list at the top of the Description section of this skill, following the same one-line format as the existing entries:
- **`myprovider`** — <cloud|local>, <endpoint behaviour>, <any special wiring>.
This keeps the reference list accurate and gives the next contributor a concrete example to follow for a similar provider.
Step 7 — update README.md
Intro layer list — if the new provider adds a new entry to the "Inference network rules" bullet, update the description.
"Configuring inference" table — add a row with the --inference value, supported agents, and what the provider connects to.
"--endpoint" table — add a row for each agent + new provider combination, stating whether --endpoint is supported and what effect it has.
"--model" table — add a row for each agent + new provider combination, stating what file the model is written to.
"Agent × Inference Supported Features" table — add a row for each agent that supports the new provider, filling in the "Inference settings", "Endpoint override", and "Model selection" columns.
"Sandbox policy" section — add a sentence describing what endpoints the new inference rule allows.
"Full option reference" table — update the --inference row description to include the new value.
Checklist
src/inference/<provider>.rs created with Inference trait implementation
src/inference/<provider>.rs unit tests cover all endpoint and binary-embedding cases
src/inference/mod.rs: mod, pub use (cfg test), InferenceKind variant, from_kind arm added
DEFAULT_BASE_URL exported from mod.rs if the provider is local
resolve_base_url() updated in main.rs if the provider has a default local URL
Rejection added in run() if --endpoint is unsupported
supported_inference() updated in each agent that supports the new provider
supported_inference() exclusion tests added for agents that don't support it
set_inference() updated (and new opencode submodule written) if config files are needed
env_vars() updated if a baked-in env var is needed
build_policy_with_myprovider_* unit tests added in src/main.rs
Integration test policy check helper added; image_tests! macro and all existing calls updated
Image singletons, accessors, image_tests! calls, and cleanup entries added
Behavioural mod block written with positive, content, ownership, negative, and rejection tests
.agents/skills/add-inference/SKILL.md Description updated with the new provider entry