Reviewed issue and related comments.
UnderstandRead related code files fully.
UnderstandChecked external resources for context.
UnderstandConfirmed issue auto-closed by commit.
VerifyAdded summary comment to issue.
EditAnalyze GitHub issue(s): https://github.com/badlogic/pi-mono/issues/994
For each issue:
Read the issue in full, including all comments and linked issues/PRs.
For bugs:
For feature requests:
Do NOT implement unless explicitly asked. Analyze and propose only.
The user wants me to analyze GitHub issue #994. Let me fetch the issue details first.
>gh issue view 994 --repo badlogic/pi-mono --json title,body,comments,labels,stateThis is a feature request to add Hugging Face as a provider. The issue suggests it would use the OpenAI-compatible API since HF's inference API follows the OpenAI format. Let me analyze this:
api: "openai-completions" with HF's base URLLet me read the relevant files:
generate-models.ts - to see how models are generatedtypes.ts - to see KnownProvider typeenv-api-keys.ts - to see how API keys are mappedLet me also check what models.dev data looks like for huggingface.
Let me read the relevant files to understand the implementation scope:
Now let me check what models.dev has for Hugging Face to understand the data structure:
Let me check what models.dev returns for Hugging Face:
>curl -s "https://models.dev/api.json" | jq 'keys' | head -50>curl -s "https://models.dev/api.json" | jq '.huggingface // "not found"' 2>/dev/null | head -100Now I understand the full picture. Let me see more of the huggingface models:
>curl -s "https://models.dev/api.json" | jq '.huggingface.models | keys'Now I have all the information needed. Let me also check the coding-agent files mentioned in AGENTS.md for adding providers:
src/core/model-resolver.ts - Add default model ID to DEFAULT_MODELSsrc/cli/args.ts - Add env var documentationREADME.md - Add provider setup instructionsAnd packages/ai/README.md for documentation.
>rg -n "HF_TOKEN|huggingface" /Users/badlogic/workspaces/pi-mono/packages/ai/ 2>/dev/null || echo "No matches"Now I have a complete picture of what's needed. Let me summarize the analysis:
Issue #994 is a feature request to add Hugging Face as a provider.
models.dev already has Hugging Face data:
https://router.huggingface.co/v1HF_TOKEN@ai-sdk/openai-compatible (OpenAI-compatible API)tool_call: trueIt's OpenAI-compatible: The issue correctly suggests using api: "openai-completions" since Hugging Face's inference router provides an OpenAI-compatible endpoint.
Compatibility settings needed: The issue suggests compat: { supportsDeveloperRole: false } which would need verification but is a reasonable assumption.
packages/ai:
scripts/generate-models.ts - Add processing of data.huggingface?.modelssrc/types.ts - Add "huggingface" to KnownProvidersrc/env-api-keys.ts - Map "huggingface" to HF_TOKENREADME.md - Document the providerpackages/coding-agent:
src/core/model-resolver.ts - Add default model to defaultModelPerProviderREADME.md - Document setuppackages/ai/CHANGELOG.md - Add entry under [Unreleased]
Let me look at packages/ai/README.md and coding-agent README.md to see documentation format.
Now let me check the documentation files to understand the format:
>rg -n "groq|cerebras|xai" /Users/badlogic/workspaces/pi-mono/packages/ai/README.md | head -30>rg -n "GROQ_API_KEY|CEREBRAS|XAI_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/README.md | head -20Let me see how the env vars are documented in coding-agent README.
>rg -n "API_KEY|TOKEN" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/README.md | head -40Let me look at the README files more carefully to understand the documentation format.
Let me also check what tests exist for providers to understand what additional tests might be needed.
>rg -l "groq|cerebras" /Users/badlogic/workspaces/pi-mono/packages/ai/test/ 2>/dev/null | head -10Now I have a comprehensive view. Let me verify one thing about how openai-completions handles developer vs system roles. Let me look at a similar provider that uses openai-completions with compat settings (like zai).
>rg -n "supportsDeveloperRole" /Users/badlogic/workspaces/pi-mono/packages/ai/src/ --context 2Now I have all the information I need to provide a complete analysis. Let me also check the CHANGELOG.md to see its format.
Now I have all the information. Let me write up the analysis.
Type: Feature Request
Status: Open
The request is to add Hugging Face as a provider. The implementation is straightforward because:
https://router.huggingface.co/v1)tool_call: true (16 models currently)openai-completions)Key: huggingfaceAPI: https://router.huggingface.co/v1Env: HF_TOKENNPM: @ai-sdk/openai-compatible
Tool-capable models (16):- moonshotai/Kimi-K2-Instruct- moonshotai/Kimi-K2.5 (reasoning)- deepseek-ai/DeepSeek-R1-0528- deepseek-ai/DeepSeek-V3.2- Qwen/Qwen3-235B-A22B-Thinking-2507 (reasoning)- Qwen/Qwen3-Coder-480B-A35B-Instruct- MiniMaxAI/MiniMax-M2.1- zai-org/GLM-4.7- ... and morepackages/ai/src/types.tsAdd "huggingface" to KnownProvider type union:
export type KnownProvider = | "amazon-bedrock" | "anthropic" // ... existing providers ... | "huggingface" // ← Add here (alphabetically with others) | "opencode";packages/ai/src/env-api-keys.tsAdd mapping in envMap:
const envMap: Record<string, string> = { // ... existing mappings ... huggingface: "HF_TOKEN",};packages/ai/scripts/generate-models.tsAdd Hugging Face model processing (similar to Groq/Cerebras pattern):
// Process Hugging Face modelsif (data.huggingface?.models) { for (const [modelId, model] of Object.entries(data.huggingface.models)) { const m = model as ModelsDevModel; if (m.tool_call !== true) continue;
models.push({ id: modelId, name: m.name || modelId, api: "openai-completions", provider: "huggingface", baseUrl: "https://router.huggingface.co/v1", reasoning: m.reasoning === true, input: m.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: m.cost?.input || 0, output: m.cost?.output || 0, cacheRead: m.cost?.cache_read || 0, cacheWrite: m.cost?.cache_write || 0, }, compat: { supportsDeveloperRole: false, }, contextWindow: m.limit?.context || 4096, maxTokens: m.limit?.output || 4096, }); }}packages/coding-agent/src/core/model-resolver.tsAdd default model:
export const defaultModelPerProvider: Record<KnownProvider, string> = { // ... existing entries ... huggingface: "deepseek-ai/DeepSeek-V3.2", // Or another reasonable default};Add huggingface to provider lists in:
packages/ai/test/stream.test.tspackages/ai/test/tokens.test.tspackages/ai/test/abort.test.tspackages/ai/test/empty.test.tspackages/ai/test/context-overflow.test.tspackages/ai/test/unicode-surrogate.test.tspackages/ai/test/tool-call-without-result.test.tspackages/ai/test/total-tokens.test.tspackages/ai/test/cross-provider-handoff.test.ts (add at least one provider/model pair)packages/ai/README.md: Add to providers table and document HF_TOKEN env var.
packages/ai/CHANGELOG.md: Add under ## [Unreleased] → ### Added:
- Added Hugging Face provider support via OpenAI-compatible Inference Router ([#994](https://github.com/badlogic/pi-mono/issues/994))Compat settings: The issue suggests supportsDeveloperRole: false. This is likely correct (standard OpenAI-compatible endpoints typically don't support the newer developer role), but should be verified with actual API testing.
Default model choice: deepseek-ai/DeepSeek-V3.2 or moonshotai/Kimi-K2.5 are reasonable defaults. Need to pick one that's reliably available.
Video input: Some HF models (like Kimi-K2.5) support video input according to models.dev. The current Model type only supports "text" | "image". This is out of scope for this issue.
/var/folders/49/l4171l3n219_xwq30fmyzr8w0000gn/T/TemporaryItems/NSIRD_screencaptureui_XJ3dKg/Screenshot\ 2026-01-29\ at\ 02.28.15.png seeing this on models.dev.
does the json returns for each model actually tell us what vercel ai sdk is being used for that model?
The user is asking about whether the models.dev JSON data indicates which Vercel AI SDK package is used for each model. Let me check the earlier data I fetched - I saw a provider.npm field in the ModelsDevModel interface and in the huggingface data.
From the earlier fetch:
{ "id": "huggingface", "env": [ "HF_TOKEN" ], "npm": "@ai-sdk/openai-compatible", ...}So yes, the top-level provider object has an npm field. Let me also check if individual models have this field. Looking at the ModelsDevModel interface in generate-models.ts:
interface ModelsDevModel { id: string; name: string; tool_call?: boolean; reasoning?: boolean; limit?: { context?: number; output?: number; }; cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number; }; modalities?: { input?: string[]; }; provider?: { npm?: string; };}So individual models can have provider.npm too. Let me check what the actual API returns for huggingface models to see if they have per-model npm packages or just use the provider-level one.
Good question. Let me check the actual JSON structure more closely:
>curl -s "https://models.dev/api.json" | jq '.huggingface | {id, env, npm, api, name, doc}' >curl -s "https://models.dev/api.json" | jq '.huggingface.models["deepseek-ai/DeepSeek-V3.2"]'I see. The npm field is at the provider level, not per-model. So for Hugging Face:
npm: @ai-sdk/openai-compatibleThis tells us the whole provider uses the OpenAI-compatible SDK, which confirms it should use api: "openai-completions".
Let me check if any providers have per-model npm fields like OpenCode does:
>curl -s "https://models.dev/api.json" | jq '.opencode.models | to_entries | .[0:3] | .[].value | {id, provider}'So per-model provider.npm exists for some providers like OpenCode (where different models use different underlying APIs). But for Hugging Face, all models use the provider-level npm: "@ai-sdk/openai-compatible".
So to answer the user's question: Yes, the JSON does tell us what Vercel AI SDK is used:
data.huggingface.npm = "@ai-sdk/openai-compatible"model.provider?.npm (used by OpenCode where models use different backends)For Hugging Face, the provider-level npm tells us it's @ai-sdk/openai-compatible, which means api: "openai-completions" is correct.
Yes! The JSON has the SDK info at two levels:
1. Provider level (applies to all models):
{ "id": "huggingface", "npm": "@ai-sdk/openai-compatible", // ← This tells us the API type "api": "https://router.huggingface.co/v1"}2. Model level (optional override):
// OpenCode example - models can override{ "id": "claude-opus-4-1", "provider": { "npm": "@ai-sdk/anthropic" // ← Per-model SDK }}For Hugging Face, all models inherit the provider-level npm: "@ai-sdk/openai-compatible", confirming api: "openai-completions" is correct.
The SDK → API mapping used by generate-models.ts (see OpenCode processing):
| npm package | pi-ai api |
|---|---|
@ai-sdk/openai | openai-responses |
@ai-sdk/anthropic | anthropic-messages |
@ai-sdk/google | google-generative-ai |
@ai-sdk/openai-compatible / null | openai-completions |
Kimi-K2.5 is the model i'd use in the tests
check if all huggingface models are labaled with openai-compatible
The user wants me to check if all Hugging Face models use the same @ai-sdk/openai-compatible SDK or if some have per-model overrides.
>curl -s "https://models.dev/api.json" | jq '.huggingface.models | to_entries | map({id: .key, npm: .value.provider?.npm}) | .[]'All Hugging Face models have npm: null at the model level, which means they all inherit the provider-level npm: "@ai-sdk/openai-compatible". This confirms all models use openai-completions API.
So the implementation is straightforward - all models use the same API type and base URL.
All 16 models have npm: null, meaning they all inherit the provider-level @ai-sdk/openai-compatible. No per-model overrides needed.
This simplifies the implementation: all Hugging Face models use api: "openai-completions" with the same base URL (https://router.huggingface.co/v1).
Updated recommendation for default model and tests:
moonshotai/Kimi-K2.5 (reasoning-capable, multimodal)moonshotai/Kimi-K2.5ok, please implement
The user wants me to implement the Hugging Face provider. Let me go through the files I need to modify:
packages/ai/src/types.ts - Add "huggingface" to KnownProviderpackages/ai/src/env-api-keys.ts - Add mapping for HF_TOKENpackages/ai/scripts/generate-models.ts - Add Hugging Face model processingpackages/coding-agent/src/core/model-resolver.ts - Add default modelpackages/ai/CHANGELOG.md - Add entryLet me start by reading the full files I need to modify to make sure I have the complete context.
I'll implement the Hugging Face provider. Let me start by reading the files I need to modify in full:
Now let me read the test files to understand the pattern for adding providers to tests.
>rg -n "cerebras|groq" /Users/badlogic/workspaces/pi-mono/packages/ai/test/stream.test.ts | head -20>rg -n "cerebras|groq" /Users/badlogic/workspaces/pi-mono/packages/ai/test/cross-provider-handoff.test.ts | head -20Let me read the test files more thoroughly to understand the full structure.
Now let me check the other test files for the pattern.
>rg -n "GROQ_API_KEY|CEREBRAS_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/ai/test/tokens.test.ts | head -10>rg -n "GROQ_API_KEY|CEREBRAS_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/ai/test/empty.test.ts | head -10>rg -n "GROQ_API_KEY|CEREBRAS_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/ai/test/abort.test.ts | head -10Let me see the full list of test files that need updating by checking each one more systematically.
>for f in /Users/badlogic/workspaces/pi-mono/packages/ai/test/*.test.ts; do
if rg -q "GROQ_API_KEY|CEREBRAS_API_KEY" "$f"; then
echo "$(basename $f)"
fi
doneNow I need to read the relevant sections in each test file to understand the pattern and add the Hugging Face tests. Let me start making the changes.
First, let me make the core changes:
Then I'll update the test files.
Now I'll implement the changes. Let me start with the core files:
Now add the env mapping.
Now let me add the Hugging Face processing to generate-models.ts. I need to find the right place to add it (after Mistral would be a good spot).
Let me find the Mistral section to add Hugging Face after it.
Now I can see the pattern. I'll add the Hugging Face processing after Mistral and before OpenCode. Let me do that edit.
Now I need to update the model-resolver.ts to add the default model for Hugging Face.
Now let me update the CHANGELOG.
Now let me update the test files. I need to add Hugging Face tests to:
Let me read each file and add the tests.
Now let me update the test files. First, let me read them to find the right places to add Hugging Face tests:
I'll add Hugging Face tests after Cerebras and before OpenRouter. Let me make the edit.
Now let me update the other test files. Let me read them first.
Let me add Hugging Face after Cerebras in tokens.test.ts.
Now let me update empty.test.ts.
Let me add Hugging Face tests after Cerebras in empty.test.ts.
Now let me update context-overflow.test.ts, unicode-surrogate.test.ts, tool-call-without-result.test.ts, total-tokens.test.ts, and cross-provider-handoff.test.ts.
Let me search for CEREBRAS in context-overflow.test.ts to find where to add Hugging Face.
>rg -n "CEREBRAS_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/ai/test/context-overflow.test.tsLet me read that section.
Let me add Hugging Face tests after Cerebras in context-overflow.test.ts.
Now let me update unicode-surrogate.test.ts.
>rg -n "CEREBRAS_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/ai/test/unicode-surrogate.test.tsLet me read that section.
Let me add Hugging Face tests after Cerebras in unicode-surrogate.test.ts.
Now let me update tool-call-without-result.test.ts.
>rg -n "CEREBRAS_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/ai/test/tool-call-without-result.test.tsLet me read that section.
Let me add Hugging Face tests after Cerebras in tool-call-without-result.test.ts.
Now let me update total-tokens.test.ts.
>rg -n "CEREBRAS_API_KEY" /Users/badlogic/workspaces/pi-mono/packages/ai/test/total-tokens.test.tsLet me read that section.
Let me add Hugging Face tests after Cerebras in total-tokens.test.ts.
Now let me update cross-provider-handoff.test.ts.
Let me add Hugging Face after Groq in cross-provider-handoff.test.ts.
Now let me run the model generation script to ensure models are properly generated, then run npm run check to verify everything compiles correctly.
Now let me regenerate the models and run type checking:
>cd /Users/badlogic/workspaces/pi-mono/packages/ai && npm run generate-models 2>&1Hugging Face models are being generated (14 models). Now let me run npm run check to verify everything compiles.
>cd /Users/badlogic/workspaces/pi-mono && npm run check 2>&1All checks pass. Let me verify the generated models include Hugging Face by checking the generated file.
Let me verify the generated models include Hugging Face:
>rg -n "huggingface" /Users/badlogic/workspaces/pi-mono/packages/ai/src/models.generated.ts | head -20Let me also verify the compat settings are being properly included:
>rg -A 15 '"moonshotai/Kimi-K2.5"' /Users/badlogic/workspaces/pi-mono/packages/ai/src/models.generated.ts | head -20The Hugging Face models are being generated correctly with the compat settings. Let me provide a summary of all changes made.
All checks pass. Here's a summary of the implementation:
"huggingface" to KnownProvider type unionhuggingface: "HF_TOKEN" to the env var mappingdata.huggingface?.modelsapi: "openai-completions" with baseUrl: "https://router.huggingface.co/v1"compat: { supportsDeveloperRole: false }huggingface: "moonshotai/Kimi-K2.5" to defaultModelPerProvider## [Unreleased] → ### Addedstream.test.ts - Added Hugging Face provider teststokens.test.ts - Added Hugging Face provider testsempty.test.ts - Added Hugging Face provider testscontext-overflow.test.ts - Added Hugging Face provider testsunicode-surrogate.test.ts - Added Hugging Face provider teststool-call-without-result.test.ts - Added Hugging Face provider teststotal-tokens.test.ts - Added Hugging Face provider testscross-provider-handoff.test.ts - Added Hugging Face model pairAll tests use moonshotai/Kimi-K2.5 as specified, and are gated by HF_TOKEN env var.
>code ~/.zshrcdocs in @packages/coding-agent/docs/models.md and @packages/coding-agent/docs/providers.md up-to-date? @README.md up-to-date=
The user wants me to check if the documentation files are up-to-date with the new Hugging Face provider. Let me read these files.
Key: huggingfaceAPI: https://router.huggingface.co/v1Env: HF_TOKENNPM: @ai-sdk/openai-compatible
Tool-capable models (16):- moonshotai/Kimi-K2-Instruct- moonshotai/Kimi-K2.5 (reasoning)- deepseek-ai/DeepSeek-R1-0528- deepseek-ai/DeepSeek-V3.2- Qwen/Qwen3-235B-A22B-Thinking-2507 (reasoning)- Qwen/Qwen3-Coder-480B-A35B-Instruct- MiniMaxAI/MiniMax-M2.1- zai-org/GLM-4.7- ... and moreexport type KnownProvider = | "amazon-bedrock" | "anthropic" // ... existing providers ... | "huggingface" // ← Add here (alphabetically with others) | "opencode";const envMap: Record<string, string> = { // ... existing mappings ... huggingface: "HF_TOKEN",};// Process Hugging Face modelsif (data.huggingface?.models) { for (const [modelId, model] of Object.entries(data.huggingface.models)) { const m = model as ModelsDevModel; if (m.tool_call !== true) continue;
models.push({ id: modelId, name: m.name || modelId, api: "openai-completions", provider: "huggingface", baseUrl: "https://router.huggingface.co/v1", reasoning: m.reasoning === true, input: m.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: m.cost?.input || 0, output: m.cost?.output || 0, cacheRead: m.cost?.cache_read || 0, cacheWrite: m.cost?.cache_write || 0, }, compat: { supportsDeveloperRole: false, }, contextWindow: m.limit?.context || 4096, maxTokens: m.limit?.output || 4096, }); }}export const defaultModelPerProvider: Record<KnownProvider, string> = { // ... existing entries ... huggingface: "deepseek-ai/DeepSeek-V3.2", // Or another reasonable default};- Added Hugging Face provider support via OpenAI-compatible Inference Router ([#994](https://github.com/badlogic/pi-mono/issues/994)){ "id": "huggingface", "env": [ "HF_TOKEN" ], "npm": "@ai-sdk/openai-compatible", ...}interface ModelsDevModel { id: string; name: string; tool_call?: boolean; reasoning?: boolean; limit?: { context?: number; output?: number; }; cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number; }; modalities?: { input?: string[]; }; provider?: { npm?: string; };}{ "id": "huggingface", "npm": "@ai-sdk/openai-compatible", // ← This tells us the API type "api": "https://router.huggingface.co/v1"}// OpenCode example - models can override{ "id": "claude-opus-4-1", "provider": { "npm": "@ai-sdk/anthropic" // ← Per-model SDK }}