Skip to content

feat(models): 新增 Runcrate 内置模型供应商 - #1138

Open
heydryft wants to merge 1 commit into
xerrors:mainfrom
heydryft:feat/runcrate-provider
Open

heydryft wants to merge 1 commit into
xerrors:mainfrom
heydryft:feat/runcrate-provider

Conversation

@heydryft

@heydryft heydryft commented Nov 1, 2026

Copy link
Copy Markdown

变更说明

Adds Runcrate as a built-in model provider template. Runcrate serves open-weight models (DeepSeek, Qwen, Kimi, GLM, gpt-oss and others) behind an OpenAI-compatible API at https://api.runcrate.ai/v1, so the template uses the default openai provider type, the same way opencode-go and modelscope do.

  • 任务类型:feature
  • 目标与非目标:goal is one more built-in OpenAI-compatible template that admins can enable and fill with RUNCRATE_API_KEY. Non-goals: no change to defaults, model selection, request handling or reasoning handling.
  • substantial / trivial 判断:trivial. It is a data-only template entry plus an avatar fallback, so no decision record was added.

Files:

  • backend/yuxi/modules/models/providers/builtin.py: new runcrate entry (base_url, api_key_env, models_endpoint), placed after opencode-go.
  • frontend/src/modules/settings/model/modelIcon.js: runcrate uses the neutral default avatar, so the card does not fall back to the OpenAI logo through provider_type.

工程主张与 Owner

  • 受影响的工程主张:after app init, a runcrate provider row exists, disabled, and an admin can enable it with a key and pick models from the live model list.
  • Owner / commit point / 观察边界:BUILTIN_PROVIDERS owns the template; ensure_builtin_model_providers_in_db seeds it once and never overwrites an edited row; is_enabled stays False because only siliconflow-cn is enabled by default.
  • 决策记录:none, data-only template (see above).

验证情况

The template resolves to the OpenAI-compatible path and lists models

  • 失败面:wrong provider_type, wrong models endpoint.
  • 语义 Owner:_normalize_payload, fetch_remote_models.
  • 直接证据 / 命令:a local script (not committed) normalized the template and called fetch_remote_models on it: provider_type openai, 127 models returned, including deepseek-ai/DeepSeek-V4-Flash.
  • 负向案例:test_builtin_provider_templates_default_to_openai_provider_type covers every builtin template, including this one.
  • 结果:Passed

Chat, tool calls and streaming through load_chat_model

  • 失败面:ChatCompletionsAdapter against the real endpoint.
  • 语义 Owner:yuxi.modules.models.chat.load_chat_model.
  • 直接证据 / 命令:the same script built a ModelInfo for runcrate:deepseek-ai/DeepSeek-V4-Flash with a real key and called load_chat_model: invoke returned ok with finish reason stop; bind_tools with a get_weather tool returned the call get_weather({"city": "Paris"}); stream returned 9 chunks joining to ok.
  • 负向案例:不适用
  • 结果:Passed

Repo checks

  • 直接证据 / 命令:
    • cd backend && uv run pytest test/unit -m "not slow": 3622 passed, 22 failed, 1 skipped. The same 22 tests fail on main without this change on this macOS host (Docker compose slot probe, Cube protocol, office/OCR conversion, renameat2 in workdir copy); the failure lists are identical.
    • uv run pytest test/unit/services/test_model_provider_service.py test/unit/config/test_options.py test/unit/services/test_model_selectors.py -m "not slow": 103 passed.
    • uv run ruff check yuxi, uv run ruff format yuxi --check, uv run ruff check --select I yuxi: clean.
    • python3 scripts/ci/verify_engineering_contracts.py: passed. python3 -m unittest scripts.ci.test_verify_engineering_contracts: 56 tests OK.
    • cd frontend && npx eslint --max-warnings=0 src/modules/settings/model/modelIcon.js: clean. pnpm run test:unit: 1010 passed.
  • 结果:Passed

简化 / 删除验收

不涉及

独立语义 Review

A fresh-context reviewer agent read AGENTS.md, backend/AGENTS.md and the full diff. It confirmed the entry shape, the disabled-by-default seeding, and that .env reaches api and worker without a Compose change. It flagged the OpenAI logo fallback (fixed by the modelIcon.js line) and asked for the full unit suite, engineering contract checks and import sort check (results above).

未验证范围与风险

  • Not run: the integration and E2E suites, and the change inside the Docker stack (the template was exercised through the service functions directly).
  • runcrate is not in the preserve_reasoning set in chat.py, so reasoning text from reasoning models is not carried across turns. Existing tests show a missing reasoning field does not break the tool flow, and the tool call above worked; I left the set unchanged to keep this PR data-only.

事故反馈

不涉及

界面变更

The model provider list shows one more card, "Runcrate", with the neutral default avatar.

关联事项

无

补充说明

No migration: the row is added by the existing builtin seeding on app init, disabled until an admin enables it.

This PR was prepared with AI assistance (omp with Claude): the template entry, the avatar line, the test runs and this description were written with it, and the live calls above were run against the real Runcrate API.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant