Skip to main content

add-llm-api-provider

Add a new LLM API provider to inference.sh grid with native SDK, streaming, shared helper, deploy, test, and pricing. Use when creating a batch of LLM model apps from a provider like Anthropic, OpenAI, Google, xAI.

Source facts

Repository
inference-sh/grid
Last source activity
May 20, 2026 at 16:00
Detected SKILL.md language
English
Stars
32
Forks
2

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
add-llm-api-provider
description
Add a new LLM API provider to inference.sh grid with native SDK, streaming, shared helper, deploy, test, and pricing. Use when creating a batch of LLM model apps from a provider like Anthropic, OpenAI, Google, xAI.
allowed-tools
Bash, Read, Write, Edit, Glob, Grep, Agent, WebFetch
## Add LLM API Provider Use when adding a new LLM provider (Anthropic, OpenAI, Google, xAI, etc.) to the inference.sh grid as a set of chat apps with a shared helper module. ### Process 1. **Research the provider API** — Fetch official documentation. For each model, extract: - Model ID (exact API string, e.g. `claude-sonnet-4-6`) - Context window and max output tokens - Pricing per million tokens (input/output) - Capabilities: vision, tool use, reasoning/thinking, streaming format - Auth method and header format - Collect into `README.md` and `pricing.md` at the provider root 2. **Create provider directory and shared helper** — ```bash mkdir -p tmp/grid/api/{provider} ``` Write `{provider}_helper.py` with: - **Message conversion**: Provider-specific format from `LLMInput` (context messages, images, tool calls, tool results). Handle consecutive same-role message merging if required. - **Tool conversion**: OpenAI format → provider format - **Streaming function**: `stream_completion(client, input_data, model)` → yields output dicts with `response`, `reasoning`, `tool_calls`, `output_meta` - **Non-streaming function**: `complete(client, input_data, model)` → returns output dict - **Token tracking**: Extract `input_tokens`/`output_tokens` from API response, emit as `OutputMeta(inputs=[TextMeta(tokens=N)], outputs=[TextMeta(tokens=N)])` - **Error handling**: `handle_api_error()` that extracts provider error messages - **Reasoning/thinking config**: Map `ReasoningEffortEnum` to provider's thinking parameter format 3. **Scaffold each model app** — Use `belt app init`, never create files manually: ```bash cd tmp/grid/api/{provider} belt app init {model-name} --lang python # repeat for each model ``` 4. **Symlink the shared helper** into each app: ```bash for app in model-a model-b model-c; do ln -sf ../{provider}_helper.py "$app/{provider}_helper.py" done ``` 5. **Implement each app's inference.py** — Thin wrapper (~50 lines): - `AppInput` extends `LLMInput` + capability mixins (`ReasoningCapabilityMixin`, `ToolsCapabilityMixin`, `ImageCapabilityMixin`) - `AppOutput` extends `ReasoningMixin, ToolCallsMixin, LLMOutput, BaseAppOutput` - `App.setup()` — no parameters, init the SDK client from `os.getenv("{PROVIDER}_KEY")` - `App.run()` — delegates to `stream_completion()` or `complete()` based on `input_data.stream` - Set `DEFAULT_MODEL`, `context_size`, and `max_tokens` per model 6. **Update inf.yml for each app** — Set: - `namespace: {provider}`, `name: {model-name}` - `category: chat` - `metadata.capabilities: [reasoning, image]` (as appropriate) - `secrets: [{key: {PROVIDER}_KEY}]` - `resources: gpu: {count: 0, type: none}, ram: 0` (API-only) 7. **Update requirements.txt** for each app: ``` pydantic >= 2.0.0 inferencesh >= 0.6.30 {provider-sdk} >= {version} ``` 8. **Deploy all apps**: ```bash for app in model-a model-b model-c; do cd tmp/grid/api/{provider}/$app && belt app deploy done ``` 9. **Cloud-test every app** — Not just one, ALL of them: ```bash belt app run {provider}/{model-name} --json --input '{"text": "simple test prompt"}' ``` Verify: `status_text: "completed"`, non-empty `response`, `output_meta` with token counts. 10. **Run pricing agents for all apps** — Get app/version IDs, then run: ```bash belt app get {provider}/{model-name} --json # extract app.id and app.version_id belt agent run infsh/pricing-agent "<pricing instructions>" \ --context version_id=<vid> --context app_id=<aid> ``` Group by pricing tier (e.g. all $5/$25 models together). After agents draft and evaluate, approve publish: ```bash belt chat pending # list pending approvals belt chat approve <invocation-id> # approve each publish ``` ### Rules - **NEVER create inf.yml, inference.py, __init__.py, or app directories manually.** Always use `belt app init`. The scaffolded `__init__.py` and package structure are required for the validator. Reason: manual creation misses required fields, uses wrong field names, and breaks the deployment validator. - **setup() takes no parameters.** The `metadata` parameter is deprecated. Use `self.context.metadata` if needed. `self.logger` is NOT available in `setup()` — use `print()` for setup logging. Reason: both caused runtime errors during testing. - **LLMInput field is `text`, not `prompt`.** The input schema inherits from `LLMInput` which uses `text` as the primary field. Reason: `prompt` causes a Pydantic validation error at runtime. - **AppOutput MUST extend BaseAppOutput** (not just BaseModel) when using `output_meta`. Without it, `output_meta` is silently dropped from the response. - **Read files before overwriting with Write tool.** The Write tool requires a prior Read of the file in the same conversation. After `belt app init`, read the scaffolded file first. - **Use relative imports for the symlinked helper.** `from .{provider}_helper import func` — not absolute imports. The app runs as a package inside the deployment container. - **Deploy and cloud-test EVERY app, not just one.** Different model IDs can have different API behaviors. A working sonnet doesn't guarantee a working haiku. Reason: we shipped with only partial testing initially. - **Run pricing agents AFTER deployment**, not before. The pricing agent needs the app submitted to the store, which happens during deploy. If the agent says "not submitted to store", tell it to submit first. - **belt secrets are masked locally.** `belt secrets get` doesn't return the raw value — you can't use it for local testing env vars. Deploy and cloud-test instead. - **Pricing CEL expressions need double() casts.** `text_tokens()` returns int; dividing by a double causes a CEL type error. Always use `double(text_tokens(inputs)) / 1000000.0 * double(prices.var)`. Reason: multiple pricing agents failed on this before self-correcting. - **Group models by pricing tier** for pricing agent efficiency. Don't run 6 identical pricing agents — group the 3 Opus models, 2 Sonnet models, etc. by their $/MTok rate.
View on GitHub