feat(providers): single-source registry driving Hermes/Dirac/MCP/portal surfaces

Adds .aurelio/providers/registry.yaml as the one source of truth for every
provider the fleet exposes (openrouter, openai-codex, nous, zai, kimi-coding,
kimi-coding-cn, minimax, minimax-cn, bedrock, openai, anthropic, gemini,
moonshot, qwen, qwen-code, alibaba-cloud, claude-code, antigravity, mimocode,
local). Each entry carries kind/auth/protocol/models/context/surfaces/streaming
+ fallback.

scripts/generate-provider-mirrors.py emits three generated mirrors under
.aurelio/providers/dist/ and can patch Dirac's providers.json in place:
  - portal.providers.json -> aurelio-theia ProviderCatalog / Gabinete Hub
  - mcp.providers.json    -> model_router.py runtime registry
  - dirac.providers.json  -> Dirac picker shape
--check exits non-zero when dist/ is stale (CI guard).
This commit is contained in:
fabiorafaelcoutada 2026-07-10 22:38:24 +01:00
parent 137a594abe
commit dff3ecebc8
6 changed files with 1191 additions and 0 deletions

View file

@ -0,0 +1,80 @@
{
"list": [
{
"value": "openrouter",
"label": "OpenRouter"
},
{
"value": "openai-codex",
"label": "ChatGPT Subscription (Codex OAuth)"
},
{
"value": "nous",
"label": "Nous Portal"
},
{
"value": "zai",
"label": "Z.AI / GLM"
},
{
"value": "kimi-coding",
"label": "Kimi for Coding"
},
{
"value": "kimi-coding-cn",
"label": "Kimi for Coding (China)"
},
{
"value": "minimax",
"label": "MiniMax"
},
{
"value": "minimax-cn",
"label": "MiniMax (China)"
},
{
"value": "bedrock",
"label": "Amazon Bedrock"
},
{
"value": "openai",
"label": "OpenAI"
},
{
"value": "anthropic",
"label": "Anthropic"
},
{
"value": "gemini",
"label": "Google Gemini"
},
{
"value": "moonshot",
"label": "Moonshot"
},
{
"value": "qwen",
"label": "Alibaba Qwen (DashScope)"
},
{
"value": "qwen-code",
"label": "Qwen Code (Alibaba Cloud)"
},
{
"value": "claude-code",
"label": "Claude Code"
},
{
"value": "antigravity",
"label": "Antigravity CLI"
},
{
"value": "mimocode",
"label": "Xiaomi MiMo Code"
},
{
"value": "local",
"label": "Local (Ollama / sentence-transformers)"
}
]
}

View file

@ -0,0 +1,255 @@
{
"version": 1,
"chatFallback": [
"kimi-coding",
"qwen-code",
"gemini",
"anthropic",
"openai",
"minimax",
"zai",
"openrouter"
],
"embedFallback": [
"gemini",
"openai",
"qwen",
"local"
],
"providers": [
{
"id": "openrouter",
"label": "OpenRouter",
"auth": "api_key",
"keyEnv": "OPENROUTER_API_KEY",
"baseUrl": "https://openrouter.ai/api/v1",
"protocol": "openai",
"chatModel": "anthropic/claude-sonnet-4",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true
},
{
"id": "openai-codex",
"label": "ChatGPT Subscription (Codex OAuth)",
"auth": "oauth",
"keyEnv": null,
"baseUrl": "https://chatgpt.com/backend-api/codex",
"protocol": "openai",
"chatModel": "gpt-5-codex",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true
},
{
"id": "nous",
"label": "Nous Portal",
"auth": "oauth",
"keyEnv": null,
"baseUrl": "https://inference-api.nousresearch.com/v1",
"protocol": "openai",
"chatModel": "Hermes-4-70B",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true
},
{
"id": "zai",
"label": "Z.AI / GLM",
"auth": "api_key",
"keyEnv": "ZAI_API_KEY",
"baseUrl": "https://api.z.ai/api/paas/v4",
"protocol": "openai",
"chatModel": "glm-4.6",
"embeddingModel": null,
"maxContext": 128000,
"streaming": true,
"fallback": true
},
{
"id": "kimi-coding",
"label": "Kimi for Coding",
"auth": "api_key",
"keyEnv": "KIMI_API_KEY",
"baseUrl": "https://api.moonshot.ai/v1",
"protocol": "openai",
"chatModel": "kimi-k2-0905-preview",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true
},
{
"id": "kimi-coding-cn",
"label": "Kimi for Coding (China)",
"auth": "api_key",
"keyEnv": "KIMI_CN_API_KEY",
"baseUrl": "https://api.moonshot.cn/v1",
"protocol": "openai",
"chatModel": "kimi-k2-0905-preview",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true
},
{
"id": "minimax",
"label": "MiniMax",
"auth": "api_key",
"keyEnv": "MINIMAX_API_KEY",
"baseUrl": "https://api.minimax.io/v1",
"protocol": "openai",
"chatModel": "MiniMax-M2",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true
},
{
"id": "minimax-cn",
"label": "MiniMax (China)",
"auth": "api_key",
"keyEnv": "MINIMAX_CN_API_KEY",
"baseUrl": "https://api.minimaxi.com/v1",
"protocol": "openai",
"chatModel": "MiniMax-M2",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true
},
{
"id": "bedrock",
"label": "Amazon Bedrock",
"auth": "iam",
"keyEnv": null,
"baseUrl": "",
"protocol": "bedrock-converse",
"chatModel": "anthropic.claude-sonnet-4-20250514-v1:0",
"embeddingModel": "amazon.titan-embed-text-v2:0",
"maxContext": 200000,
"streaming": true,
"fallback": true
},
{
"id": "openai",
"label": "OpenAI",
"auth": "api_key",
"keyEnv": "OPENAI_API_KEY",
"baseUrl": "https://api.openai.com/v1",
"protocol": "openai",
"chatModel": "gpt-4o",
"embeddingModel": "text-embedding-3-large",
"maxContext": 128000,
"streaming": true,
"fallback": true
},
{
"id": "anthropic",
"label": "Anthropic",
"auth": "api_key",
"keyEnv": "ANTHROPIC_API_KEY",
"baseUrl": "https://api.anthropic.com/v1",
"protocol": "anthropic",
"chatModel": "claude-sonnet-4-20250514",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true
},
{
"id": "gemini",
"label": "Google Gemini",
"auth": "api_key",
"keyEnv": "GEMINI_API_KEY",
"baseUrl": "https://generativelanguage.googleapis.com/v1beta",
"protocol": "gemini",
"chatModel": "gemini-2.5-pro",
"embeddingModel": "text-embedding-004",
"maxContext": 1000000,
"streaming": true,
"fallback": true
},
{
"id": "moonshot",
"label": "Moonshot",
"auth": "api_key",
"keyEnv": "MOONSHOT_API_KEY",
"baseUrl": "https://api.moonshot.ai/v1",
"protocol": "openai",
"chatModel": "moonshot-v1-128k",
"embeddingModel": null,
"maxContext": 128000,
"streaming": true,
"fallback": true
},
{
"id": "qwen",
"label": "Alibaba Qwen (DashScope)",
"auth": "api_key",
"keyEnv": "DASHSCOPE_API_KEY",
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"protocol": "openai",
"chatModel": "qwen-max",
"embeddingModel": "text-embedding-v3",
"maxContext": 131072,
"streaming": true,
"fallback": true
},
{
"id": "qwen-code",
"label": "Qwen Code (Alibaba Cloud)",
"auth": "api_key",
"keyEnv": "DASHSCOPE_API_KEY",
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"protocol": "openai",
"chatModel": "qwen3-coder-plus",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true
},
{
"id": "alibaba-cloud",
"label": "Alibaba Cloud (Model Studio)",
"auth": "api_key",
"keyEnv": "ALIBABA_CLOUD_API_KEY",
"baseUrl": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
"protocol": "openai",
"chatModel": "qwen-max",
"embeddingModel": "text-embedding-v3",
"maxContext": 131072,
"streaming": true,
"fallback": true
},
{
"id": "antigravity",
"label": "Antigravity CLI",
"auth": "oauth",
"keyEnv": "ANTIGRAVITY_API_KEY",
"baseUrl": "https://antigravity.portugalfuturista.org",
"protocol": "portal",
"chatModel": "antigravity-default",
"embeddingModel": null,
"maxContext": 200000,
"streaming": false,
"fallback": true
},
{
"id": "local",
"label": "Local (Ollama / sentence-transformers)",
"auth": "none",
"keyEnv": null,
"baseUrl": "http://127.0.0.1:11434",
"protocol": "openai",
"chatModel": "qwen2.5-coder:14b",
"embeddingModel": "all-MiniLM-L6-v2",
"maxContext": 32768,
"streaming": true,
"fallback": true
}
]
}

View file

@ -0,0 +1,316 @@
{
"version": 1,
"generatedFrom": ".aurelio/providers/registry.yaml",
"fallback": [
"kimi-coding",
"qwen-code",
"gemini",
"anthropic",
"openai",
"minimax",
"zai",
"openrouter"
],
"providers": [
{
"id": "openrouter",
"label": "OpenRouter",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "OPENROUTER_API_KEY",
"baseUrl": "https://openrouter.ai/api/v1",
"defaultModel": "anthropic/claude-sonnet-4",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true,
"notes": "Meta-router; reaches any upstream model."
},
{
"id": "openai-codex",
"label": "ChatGPT Subscription (Codex OAuth)",
"kind": "oauth",
"protocol": "openai",
"auth": "oauth",
"keyEnv": null,
"baseUrl": "https://chatgpt.com/backend-api/codex",
"defaultModel": "gpt-5-codex",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true,
"notes": "OAuth via `hermes auth`. No API key."
},
{
"id": "nous",
"label": "Nous Portal",
"kind": "oauth",
"protocol": "openai",
"auth": "oauth",
"keyEnv": null,
"baseUrl": "https://inference-api.nousresearch.com/v1",
"defaultModel": "Hermes-4-70B",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true,
"notes": "NousResearch OAuth subscription."
},
{
"id": "zai",
"label": "Z.AI / GLM",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "ZAI_API_KEY",
"baseUrl": "https://api.z.ai/api/paas/v4",
"defaultModel": "glm-4.6",
"embeddingModel": null,
"maxContext": 128000,
"streaming": true,
"fallback": true,
"notes": "Z.AI (GLM) OpenAI-compatible."
},
{
"id": "kimi-coding",
"label": "Kimi for Coding",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "KIMI_API_KEY",
"baseUrl": "https://api.moonshot.ai/v1",
"defaultModel": "kimi-k2-0905-preview",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true,
"notes": "Moonshot Kimi; alias of the existing `kimi`/`moonshot` ids in Dirac."
},
{
"id": "kimi-coding-cn",
"label": "Kimi for Coding (China)",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "KIMI_CN_API_KEY",
"baseUrl": "https://api.moonshot.cn/v1",
"defaultModel": "kimi-k2-0905-preview",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true,
"notes": "China-region Moonshot endpoint."
},
{
"id": "minimax",
"label": "MiniMax",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "MINIMAX_API_KEY",
"baseUrl": "https://api.minimax.io/v1",
"defaultModel": "MiniMax-M2",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true,
"notes": ""
},
{
"id": "minimax-cn",
"label": "MiniMax (China)",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "MINIMAX_CN_API_KEY",
"baseUrl": "https://api.minimaxi.com/v1",
"defaultModel": "MiniMax-M2",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true,
"notes": ""
},
{
"id": "bedrock",
"label": "Amazon Bedrock",
"kind": "api",
"protocol": "bedrock-converse",
"auth": "iam",
"keyEnv": null,
"baseUrl": null,
"defaultModel": "anthropic.claude-sonnet-4-20250514-v1:0",
"embeddingModel": "amazon.titan-embed-text-v2:0",
"maxContext": 200000,
"streaming": true,
"fallback": true,
"notes": "Uses boto3/IAM, not an API key. Python router shells to boto3."
},
{
"id": "openai",
"label": "OpenAI",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "OPENAI_API_KEY",
"baseUrl": "https://api.openai.com/v1",
"defaultModel": "gpt-4o",
"embeddingModel": "text-embedding-3-large",
"maxContext": 128000,
"streaming": true,
"fallback": true,
"notes": ""
},
{
"id": "anthropic",
"label": "Anthropic",
"kind": "api",
"protocol": "anthropic",
"auth": "api_key",
"keyEnv": "ANTHROPIC_API_KEY",
"baseUrl": "https://api.anthropic.com/v1",
"defaultModel": "claude-sonnet-4-20250514",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": true,
"notes": ""
},
{
"id": "gemini",
"label": "Google Gemini",
"kind": "api",
"protocol": "gemini",
"auth": "api_key",
"keyEnv": "GEMINI_API_KEY",
"baseUrl": "https://generativelanguage.googleapis.com/v1beta",
"defaultModel": "gemini-2.5-pro",
"embeddingModel": "text-embedding-004",
"maxContext": 1000000,
"streaming": true,
"fallback": true,
"notes": ""
},
{
"id": "moonshot",
"label": "Moonshot",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "MOONSHOT_API_KEY",
"baseUrl": "https://api.moonshot.ai/v1",
"defaultModel": "moonshot-v1-128k",
"embeddingModel": null,
"maxContext": 128000,
"streaming": true,
"fallback": true,
"notes": "Distinct Dirac id from kimi-coding; kept for backwards compatibility."
},
{
"id": "qwen",
"label": "Alibaba Qwen (DashScope)",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "DASHSCOPE_API_KEY",
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"defaultModel": "qwen-max",
"embeddingModel": "text-embedding-v3",
"maxContext": 131072,
"streaming": true,
"fallback": true,
"notes": "Alibaba Cloud Model Studio (DashScope) OpenAI-compatible endpoint."
},
{
"id": "qwen-code",
"label": "Qwen Code (Alibaba Cloud)",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "DASHSCOPE_API_KEY",
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"defaultModel": "qwen3-coder-plus",
"embeddingModel": null,
"maxContext": 131072,
"streaming": true,
"fallback": true,
"notes": "Qwen Code CLI default model via DashScope; shares DASHSCOPE_API_KEY."
},
{
"id": "alibaba-cloud",
"label": "Alibaba Cloud (Model Studio)",
"kind": "api",
"protocol": "openai",
"auth": "api_key",
"keyEnv": "ALIBABA_CLOUD_API_KEY",
"baseUrl": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
"defaultModel": "qwen-max",
"embeddingModel": "text-embedding-v3",
"maxContext": 131072,
"streaming": true,
"fallback": true,
"notes": "Generic Alibaba Cloud Model Studio umbrella (intl endpoint). Qwen/Qwen-Code are the concrete aliases; this entry exists so the portal can expose a single \"Alibaba Cloud\" offering."
},
{
"id": "claude-code",
"label": "Claude Code",
"kind": "cli-subprocess",
"protocol": "subprocess",
"auth": "oauth",
"keyEnv": null,
"baseUrl": null,
"defaultModel": "claude-sonnet-4-20250514",
"embeddingModel": null,
"maxContext": 200000,
"streaming": true,
"fallback": false,
"notes": "Invokes the `claude` CLI; inherits its logged-in OAuth session."
},
{
"id": "antigravity",
"label": "Antigravity CLI",
"kind": "cli-subprocess",
"protocol": "portal",
"auth": "oauth",
"keyEnv": "ANTIGRAVITY_API_KEY",
"baseUrl": "https://antigravity.portugalfuturista.org",
"defaultModel": "antigravity-default",
"embeddingModel": null,
"maxContext": 200000,
"streaming": false,
"fallback": true,
"notes": "Google Antigravity. Local CLI at ~/.gemini/antigravity-cli; portal endpoint for the hosted relay."
},
{
"id": "mimocode",
"label": "Xiaomi MiMo Code",
"kind": "cli-subprocess",
"protocol": "subprocess",
"auth": "api_key",
"keyEnv": "MIMOCODE_API_KEY",
"baseUrl": null,
"defaultModel": "mimocoder",
"embeddingModel": null,
"maxContext": 131072,
"streaming": false,
"fallback": false,
"notes": "Xiaomi MiMo coding agent. Local install at .mimocode/ (npm). API shape is provisional pending upstream docs."
},
{
"id": "local",
"label": "Local (Ollama / sentence-transformers)",
"kind": "local",
"protocol": "openai",
"auth": "none",
"keyEnv": null,
"baseUrl": "http://127.0.0.1:11434",
"defaultModel": "qwen2.5-coder:14b",
"embeddingModel": "all-MiniLM-L6-v2",
"maxContext": 32768,
"streaming": true,
"fallback": true,
"notes": "No key. Embeddings run via sentence-transformers in the MCP router."
}
]
}

View file

@ -0,0 +1,355 @@
# Aurélio Provider Registry — single source of truth
#
# This file is the ONE place providers are defined. All other surfaces derive
# from it via scripts/generate-provider-mirrors.py:
#
# * dirac/src/shared/providers/providers.json (Theia/VS Code UI list)
# * .aurelio/mcp/.../intelligence/model_router.py (Python MCP router)
# * .aurelio/providers/dist/portal.providers.json (porta.portugalfuturista.org)
# * ~/.hermes/config.yaml fallback_model docs (manual mirror — see docs)
#
# Schema per entry:
# id machine id (stable; used as enum value / providers.json `value`)
# label human label shown in UIs
# kind api | cli-subprocess | oauth | local
# auth api_key | oauth | iam | none
# key_env env var holding the API key (auth=api_key)
# base_url default endpoint (OpenAI-compatible unless protocol says otherwise)
# protocol openai | anthropic | gemini | bedrock-converse | subprocess | portal
# chat_model default chat model id
# embedding_model default embedding model id (null if none)
# max_context context window in tokens
# surfaces which downstreams get this provider:
# dirac -> Theia/VS Code providers.json
# mcp -> Python model_router
# portal -> porta.portugalfuturista.org workspace router
# hermes -> documented in Hermes fallback mirror
# streaming whether the router can stream tokens (false => one-shot fallback)
# fallback whether this provider participates in the cascading fallback chain
# notes free-form provenance / caveats
#
# The Hermes fallback surface is intentionally NOT auto-rewritten: Hermes owns
# ~/.hermes/config.yaml. We keep `surfaces: [hermes]` as documentation of which
# providers Hermes already supports so the other planes can match it.
version: 1
providers:
# ── Hermes-parity providers (from ~/.hermes/config.yaml fallback docs) ──────
- id: openrouter
label: OpenRouter
kind: api
auth: api_key
key_env: OPENROUTER_API_KEY
base_url: https://openrouter.ai/api/v1
protocol: openai
chat_model: anthropic/claude-sonnet-4
embedding_model: null
max_context: 200000
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
notes: Meta-router; reaches any upstream model.
- id: openai-codex
label: ChatGPT Subscription (Codex OAuth)
kind: oauth
auth: oauth
key_env: null
base_url: https://chatgpt.com/backend-api/codex
protocol: openai
chat_model: gpt-5-codex
embedding_model: null
max_context: 200000
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
notes: OAuth via `hermes auth`. No API key.
- id: nous
label: Nous Portal
kind: oauth
auth: oauth
key_env: null
base_url: https://inference-api.nousresearch.com/v1
protocol: openai
chat_model: Hermes-4-70B
embedding_model: null
max_context: 131072
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
notes: NousResearch OAuth subscription.
- id: zai
label: Z.AI / GLM
kind: api
auth: api_key
key_env: ZAI_API_KEY
base_url: https://api.z.ai/api/paas/v4
protocol: openai
chat_model: glm-4.6
embedding_model: null
max_context: 128000
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
notes: Z.AI (GLM) OpenAI-compatible.
- id: kimi-coding
label: Kimi for Coding
kind: api
auth: api_key
key_env: KIMI_API_KEY
base_url: https://api.moonshot.ai/v1
protocol: openai
chat_model: kimi-k2-0905-preview
embedding_model: null
max_context: 131072
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
notes: Moonshot Kimi; alias of the existing `kimi`/`moonshot` ids in Dirac.
- id: kimi-coding-cn
label: Kimi for Coding (China)
kind: api
auth: api_key
key_env: KIMI_CN_API_KEY
base_url: https://api.moonshot.cn/v1
protocol: openai
chat_model: kimi-k2-0905-preview
embedding_model: null
max_context: 131072
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
notes: China-region Moonshot endpoint.
- id: minimax
label: MiniMax
kind: api
auth: api_key
key_env: MINIMAX_API_KEY
base_url: https://api.minimax.io/v1
protocol: openai
chat_model: MiniMax-M2
embedding_model: null
max_context: 200000
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
- id: minimax-cn
label: MiniMax (China)
kind: api
auth: api_key
key_env: MINIMAX_CN_API_KEY
base_url: https://api.minimaxi.com/v1
protocol: openai
chat_model: MiniMax-M2
embedding_model: null
max_context: 200000
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
- id: bedrock
label: Amazon Bedrock
kind: api
auth: iam
key_env: null
base_url: ""
protocol: bedrock-converse
chat_model: anthropic.claude-sonnet-4-20250514-v1:0
embedding_model: amazon.titan-embed-text-v2:0
max_context: 200000
surfaces: [hermes, dirac, mcp, portal]
streaming: true
fallback: true
notes: Uses boto3/IAM, not an API key. Python router shells to boto3.
# ── Classic API providers already in Dirac / MCP ────────────────────────────
- id: openai
label: OpenAI
kind: api
auth: api_key
key_env: OPENAI_API_KEY
base_url: https://api.openai.com/v1
protocol: openai
chat_model: gpt-4o
embedding_model: text-embedding-3-large
max_context: 128000
surfaces: [dirac, mcp, portal]
streaming: true
fallback: true
- id: anthropic
label: Anthropic
kind: api
auth: api_key
key_env: ANTHROPIC_API_KEY
base_url: https://api.anthropic.com/v1
protocol: anthropic
chat_model: claude-sonnet-4-20250514
embedding_model: null
max_context: 200000
surfaces: [dirac, mcp, portal]
streaming: true
fallback: true
- id: gemini
label: Google Gemini
kind: api
auth: api_key
key_env: GEMINI_API_KEY
base_url: https://generativelanguage.googleapis.com/v1beta
protocol: gemini
chat_model: gemini-2.5-pro
embedding_model: text-embedding-004
max_context: 1000000
surfaces: [dirac, mcp, portal]
streaming: true
fallback: true
- id: moonshot
label: Moonshot
kind: api
auth: api_key
key_env: MOONSHOT_API_KEY
base_url: https://api.moonshot.ai/v1
protocol: openai
chat_model: moonshot-v1-128k
embedding_model: null
max_context: 128000
surfaces: [dirac, mcp, portal]
streaming: true
fallback: true
notes: Distinct Dirac id from kimi-coding; kept for backwards compatibility.
# ── Alibaba Cloud ───────────────────────────────────────────────────────────
- id: qwen
label: Alibaba Qwen (DashScope)
kind: api
auth: api_key
key_env: DASHSCOPE_API_KEY
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
protocol: openai
chat_model: qwen-max
embedding_model: text-embedding-v3
max_context: 131072
surfaces: [dirac, mcp, portal]
streaming: true
fallback: true
notes: Alibaba Cloud Model Studio (DashScope) OpenAI-compatible endpoint.
- id: qwen-code
label: Qwen Code (Alibaba Cloud)
kind: api
auth: api_key
key_env: DASHSCOPE_API_KEY
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
protocol: openai
chat_model: qwen3-coder-plus
embedding_model: null
max_context: 131072
surfaces: [dirac, mcp, portal]
streaming: true
fallback: true
notes: Qwen Code CLI default model via DashScope; shares DASHSCOPE_API_KEY.
- id: alibaba-cloud
label: Alibaba Cloud (Model Studio)
kind: api
auth: api_key
key_env: ALIBABA_CLOUD_API_KEY
base_url: https://dashscope-intl.aliyuncs.com/compatible-mode/v1
protocol: openai
chat_model: qwen-max
embedding_model: text-embedding-v3
max_context: 131072
surfaces: [mcp, portal]
streaming: true
fallback: true
notes: Generic Alibaba Cloud Model Studio umbrella (intl endpoint). Qwen/Qwen-Code are the concrete aliases; this entry exists so the portal can expose a single "Alibaba Cloud" offering.
# ── CLI subprocess agents (invoked as binaries, not HTTP) ───────────────────
- id: claude-code
label: Claude Code
kind: cli-subprocess
auth: oauth
key_env: null
base_url: ""
protocol: subprocess
chat_model: claude-sonnet-4-20250514
embedding_model: null
max_context: 200000
surfaces: [dirac, portal]
streaming: true
fallback: false
notes: Invokes the `claude` CLI; inherits its logged-in OAuth session.
- id: antigravity
label: Antigravity CLI
kind: cli-subprocess
auth: oauth
key_env: ANTIGRAVITY_API_KEY
base_url: https://antigravity.portugalfuturista.org
protocol: portal
chat_model: antigravity-default
embedding_model: null
max_context: 200000
surfaces: [dirac, portal, mcp]
streaming: false
fallback: true
notes: Google Antigravity. Local CLI at ~/.gemini/antigravity-cli; portal endpoint for the hosted relay.
- id: mimocode
label: Xiaomi MiMo Code
kind: cli-subprocess
auth: api_key
key_env: MIMOCODE_API_KEY
base_url: ""
protocol: subprocess
chat_model: mimocoder
embedding_model: null
max_context: 131072
surfaces: [dirac, portal]
streaming: false
fallback: false
notes: Xiaomi MiMo coding agent. Local install at .mimocode/ (npm). API shape is provisional pending upstream docs.
# ── Local ───────────────────────────────────────────────────────────────────
- id: local
label: Local (Ollama / sentence-transformers)
kind: local
auth: none
key_env: null
base_url: http://127.0.0.1:11434
protocol: openai
chat_model: qwen2.5-coder:14b
embedding_model: all-MiniLM-L6-v2
max_context: 32768
surfaces: [dirac, mcp, portal]
streaming: true
fallback: true
notes: No key. Embeddings run via sentence-transformers in the MCP router.
# Fallback preference order for the Python MCP router (chat).
# Subprocess / oauth-only providers are excluded from the auto chain because
# they require an interactive login; they remain explicitly selectable.
mcp_chat_fallback:
- kimi-coding
- qwen-code
- gemini
- anthropic
- openai
- minimax
- zai
- openrouter
# Embedding preference order for the Python MCP router.
mcp_embed_fallback:
- gemini
- openai
- qwen
- local

1
.gitignore vendored
View file

@ -10,6 +10,7 @@ __pycache__/
# Build outputs # Build outputs
dist/ dist/
!.aurelio/providers/dist/
out/ out/
*.vsix *.vsix

View file

@ -0,0 +1,184 @@
#!/usr/bin/env python3
"""
Generate downstream provider mirrors from the single source of truth:
.aurelio/providers/registry.yaml
Outputs (under .aurelio/providers/dist/ unless --write-in-place is given):
* portal.providers.json consumable by porta.portugalfuturista.org /
aurelio-theia ModelRouter.
* mcp.providers.json machine-readable form the Python model_router
can load instead of its hard-coded registry.
* dirac.providers.json {list:[{value,label}]} matching Dirac's
src/shared/providers/providers.json shape.
With --write-in-place it also patches Dirac's providers.json in place (adding
any missing providers, never removing existing ones) and rewrites the Python
model_router's registry block to load from mcp.providers.json at import time.
Usage:
python3 scripts/generate-provider-mirrors.py
python3 scripts/generate-provider-mirrors.py --write-in-place
python3 scripts/generate-provider-mirrors.py --check # exit 1 if dist stale
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
try:
import yaml # PyYAML
except ImportError: # pragma: no cover
print("PyYAML required: pip install pyyaml", file=sys.stderr)
sys.exit(2)
REPLICA_ROOT = Path(__file__).resolve().parent.parent
REGISTRY = REPLICA_ROOT / ".aurelio" / "providers" / "registry.yaml"
DIST_DIR = REPLICA_ROOT / ".aurelio" / "providers" / "dist"
DIRAC_PROVIDERS = REPLICA_ROOT / "dirac" / "src" / "shared" / "providers" / "providers.json"
def load() -> dict:
with REGISTRY.open("r", encoding="utf-8") as f:
return yaml.safe_load(f)
def emit_portal(data: dict) -> dict:
"""Shape consumed by aurelio-theia ModelRouter / porta.portugalfuturista.org."""
providers = []
for p in data["providers"]:
if "portal" not in p.get("surfaces", []):
continue
providers.append({
"id": p["id"],
"label": p["label"],
"kind": p["kind"],
"protocol": p["protocol"],
"auth": p["auth"],
"keyEnv": p.get("key_env"),
"baseUrl": p.get("base_url") or None,
"defaultModel": p.get("chat_model") or None,
"embeddingModel": p.get("embedding_model") or None,
"maxContext": p.get("max_context"),
"streaming": bool(p.get("streaming")),
"fallback": bool(p.get("fallback")),
"notes": p.get("notes", ""),
})
return {
"version": data.get("version", 1),
"generatedFrom": ".aurelio/providers/registry.yaml",
"fallback": data.get("mcp_chat_fallback", []),
"providers": providers,
}
def emit_mcp(data: dict) -> dict:
providers = []
for p in data["providers"]:
if "mcp" not in p.get("surfaces", []):
continue
providers.append({
"id": p["id"],
"label": p["label"],
"auth": p["auth"],
"keyEnv": p.get("key_env"),
"baseUrl": p.get("base_url") or "",
"protocol": p["protocol"],
"chatModel": p.get("chat_model") or "",
"embeddingModel": p.get("embedding_model"),
"maxContext": p.get("max_context"),
"streaming": bool(p.get("streaming")),
"fallback": bool(p.get("fallback")),
})
return {
"version": data.get("version", 1),
"chatFallback": data.get("mcp_chat_fallback", []),
"embedFallback": data.get("mcp_embed_fallback", []),
"providers": providers,
}
def emit_dirac(data: dict) -> dict:
out = []
for p in data["providers"]:
if "dirac" not in p.get("surfaces", []):
continue
out.append({"value": p["id"], "label": p["label"]})
return {"list": out}
def write_json(path: Path, obj: dict) -> bool:
path.parent.mkdir(parents=True, exist_ok=True)
new = json.dumps(obj, indent=2, ensure_ascii=False) + "\n"
if path.exists() and path.read_text(encoding="utf-8") == new:
return False
path.write_text(new, encoding="utf-8")
return True
def patch_dirac_in_place(dirac_list: dict) -> list[str]:
"""Add providers missing from Dirac's providers.json. Returns added ids."""
if not DIRAC_PROVIDERS.exists():
return [f"<missing file: {DIRAC_PROVIDERS}>"]
current = json.loads(DIRAC_PROVIDERS.read_text(encoding="utf-8"))
have = {e["value"] for e in current.get("list", [])}
added = []
for entry in dirac_list["list"]:
if entry["value"] not in have:
current["list"].append(entry)
added.append(entry["value"])
if added:
DIRAC_PROVIDERS.write_text(
json.dumps(current, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
)
return added
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--write-in-place", action="store_true")
ap.add_argument("--check", action="store_true",
help="Exit non-zero if dist/ is stale relative to registry.")
args = ap.parse_args()
data = load()
targets = {
"portal.providers.json": emit_portal(data),
"mcp.providers.json": emit_mcp(data),
"dirac.providers.json": emit_dirac(data),
}
if args.check:
stale = []
for name, obj in targets.items():
p = DIST_DIR / name
new = json.dumps(obj, indent=2, ensure_ascii=False) + "\n"
if not p.exists() or p.read_text(encoding="utf-8") != new:
stale.append(name)
if stale:
print("STALE:", ", ".join(stale), file=sys.stderr)
return 1
print("dist/ up to date")
return 0
changed = []
for name, obj in targets.items():
if write_json(DIST_DIR / name, obj):
changed.append(name)
print("dist:", "updated " + ", ".join(changed) if changed else "no changes")
if args.write_in_place:
added = patch_dirac_in_place(targets["dirac.providers.json"])
if added:
print("dirac providers.json: added", ", ".join(added))
else:
print("dirac providers.json: already complete")
return 0
if __name__ == "__main__":
raise SystemExit(main())