Skip to content

feat: add iFlytek Spark and Astron MaaS providers - #9837

Merged
DOsinga merged 5 commits into
aaif-goose:mainfrom
FenjuFu:feat/iflytek-spark-astron-providers
Jun 30, 2026
Merged

feat: add iFlytek Spark and Astron MaaS providers#9837
DOsinga merged 5 commits into
aaif-goose:mainfrom
FenjuFu:feat/iflytek-spark-astron-providers

Conversation

@FenjuFu

@FenjuFu FenjuFu commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What

Adds two declarative OpenAI-compatible providers for iFlytek:

  • iFlytek Spark (iflytek.json) — 讯飞星火 via the Spark HTTP API
    (https://spark-api-open.xf-yun.com/v1). Ships 4.0Ultra, generalv3.5,
    max-32k, generalv3, pro-128k and lite. Auth uses the Spark HTTP API
    password (SPARK_API_PASSWORD).
  • iFlytek Astron MaaS (iflytek_astron.json) — 讯飞星辰 MaaS. Defaults to the
    Token Plan endpoint (https://maas-token-api.cn-huabei-1.xf-yun.com/v2) and
    exposes ASTRON_BASE_URL so users can switch to the Coding Plan
    (https://maas-coding-api.cn-huabei-1.xf-yun.com/v2, e.g. astron-code-latest).
    Ships Spark X2, DeepSeek, GLM, Kimi, MiniMax, Qwen and the Astron coding model.
    Auth uses ASTRON_API_KEY.

Both follow the existing declarative-provider pattern (cf. zhipu.json,
moonshot.json, alibaba.json).

Why

iFlytek Spark and Astron MaaS are widely used in China and both expose
OpenAI-compatible endpoints, so shipping them as built-in declarative providers
lets users select them directly from the provider list instead of hand-rolling a
custom provider.

Notes

  • base_url uses only the /vN path segment; derive_base_path resolves it to
    …/vN/chat/completions, which both endpoints expect.
  • skip_canonical_filtering: true is set because the model ids are
    provider-specific (4.0Ultra, xsparkx2, …) and are not in the canonical
    catalog; without it the inventory filter would drop every model. Each model
    ships an explicit context_limit instead.
  • dynamic_models: false keeps the static lists authoritative and avoids a
    runtime dependency on a /models endpoint.

Testing

  • Added test_iflytek_json_deserializes and test_iflytek_astron_json_deserializes
    alongside the existing per-provider deserialization tests.
  • Validated both JSON files parse.

References

Add declarative OpenAI-compatible providers for iFlytek Spark (讯飞星火)
and iFlytek Astron MaaS (讯飞星辰):

- iflytek.json: Spark HTTP API (spark-api-open.xf-yun.com/v1) with
  4.0Ultra, generalv3.5, max-32k, generalv3, pro-128k and lite, using
  SPARK_API_PASSWORD for auth.
- iflytek_astron.json: Astron MaaS, defaulting to the Token Plan
  endpoint with ASTRON_BASE_URL to switch to the Coding Plan; ships
  Spark X2, DeepSeek, GLM, Kimi, MiniMax, Qwen and astron-code-latest.

Both use static model lists and skip canonical filtering since the model
ids are provider-specific. Adds deserialization tests and documents the
providers in the supported providers table.

Signed-off-by: FenjuFu <fufenjupku@gmail.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d3f4940da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +14 to +16
{"name": "generalv3", "context_limit": 8192},
{"name": "pro-128k", "context_limit": 131072},
{"name": "lite", "context_limit": 4096}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove Spark models without system/tool support

The official Spark HTTP docs note that only Spark 4.0Ultra and Max support system messages and Function Call (https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html). When a goose session selects these Pro/Pro-128K/Lite entries, the OpenAI provider can send the normal system prompt and tools, so those requests are liable to be rejected even though the models are advertised as supported; please omit these entries or otherwise prevent tool/system-using sessions from selecting them.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in d03354d — dropped the Pro/Pro-128K/Lite entries and kept only 4.0Ultra and the Max family (generalv3.5, max-32k), which the Spark HTTP docs list as supporting system messages and Function Call.

@DOsinga

DOsinga commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the contribution! A couple of things before this can land:

  1. Please address the Codex review comment. It points out that per the Spark HTTP docs only 4.0Ultra and max support system messages and function calling, while pro/pro-128k/lite may reject goose's normal system-prompt + tools requests. Either drop those models or explain why they're fine — a one-line reply is enough if you think it's a non-issue.

  2. Please remove the two new test_iflytek_*_deserializes unit tests. These don't really add value: the canonical/fixed providers are embedded in the binary and parsed by load_fixed_providers() on startup, so any JSON that doesn't deserialize would surface immediately. Asserting individual field values per provider just duplicates the JSON in Rust and rots over time. If we want coverage here, it belongs at a higher level (a single test that asserts all bundled providers parse — currently load_fixed_providers silently skips invalid ones with a warn!, which is the real gap), not as a per-file deserialization test.

(Also for context: the earlier "closing, external repo" comment doesn't apply to this PR — it looks like it was posted in error.)

I'll snooze this for a few days to give you a chance to respond.

…t all bundled providers parse

Per the Spark HTTP docs only the 4.0Ultra and Max families accept system
messages and Function Call, so drop the Pro/Pro-128K/Lite entries that would
reject goose's system-prompt + tools requests.

Replace the per-file iflytek deserialization tests with a single test that
asserts every bundled declarative provider parses through the same path
load_fixed_providers uses, which otherwise silently skips invalid files.
@FenjuFu

FenjuFu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review — both addressed in d03354d:

  1. Spark models — dropped generalv3 (Pro), pro-128k (Pro-128K) and lite (Lite), keeping only the families the Spark HTTP docs list as accepting system messages + Function Call: 4.0Ultra and Max (generalv3.5, max-32k). goose always sends a system prompt and tools, so the dropped entries would have been liable to rejection. Noted the rationale in the provider description too.

  2. Tests — removed the two per-file test_iflytek_*_deserializes tests and replaced them with a single test_all_bundled_providers_deserialize that iterates every bundled JSON through the same deserialize_provider_config path load_fixed_providers uses. That closes the real gap you flagged: previously an invalid bundled provider would be silently skipped with a warn! rather than failing the suite.

(And understood re: the earlier "closing, external repo" comment — thanks for clarifying.)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d03354d81b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"name": "iflytek",
"engine": "openai",
"display_name": "iFlytek Spark",
"description": "iFlytek Spark (讯飞星火) models via the OpenAI-compatible HTTP API. Authenticate with your Spark HTTP API password (APIPassword). Only the 4.0Ultra and Max model families are listed, since per the Spark HTTP docs they are the versions that accept system messages and Function Call (tools) — which goose always sends.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Set Spark's tool_calls switch before enabling tools

For sessions with tools, selecting only Max/Ultra is not enough for Spark to behave like OpenAI: the Spark HTTP docs say tool_calls_switch defaults off and must be true to return function calls in the tool_calls array, while goose's OpenAI formatter only drives tool execution from tool_calls. This declarative config has no provider-level request param to add that switch, so Spark function calls can come back as JSON content and goose will not execute the requested tools; please add support for sending tool_calls_switch: true (or don't advertise Spark as tool-capable until it can be sent).

Useful? React with 👍 / 👎.

@DOsinga

DOsinga commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the quick turnaround on the model list and the consolidated test — that's a nice improvement.

Two things still open before this can land:

  1. The second codex comment is still unaddressed: Spark's HTTP API needs tool_calls_switch: true in the request body to return function calls in the tool_calls array, and the declarative provider config currently has no way to inject that. So tool calls from Spark may come back as plain JSON content that goose won't execute. Could you either confirm tool-calling actually works as-is with these models, or note that Spark is chat-only here (and not advertise tools)? A one-line reply either way is fine.

  2. Minor: the providers.md table still lists lite for Spark ("including 4.0Ultra, generalv3.5, and lite") but lite was dropped from iflytek.json in the fix commit. Please update the docs line to match.

Snoozing for a few days to give you a chance to reply.

Spark only returns OpenAI-style tool_calls when the request body sets
tool_calls_switch=true, which the declarative config cannot inject, so the
iflytek provider is best used for chat. Note this in the provider
description and the providers table, and drop the stale 'lite' mention
(lite was removed from iflytek.json earlier).

Signed-off-by: FenjuFu <fufenjupku@gmail.com>
@FenjuFu

FenjuFu commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — you're right, and the codex point checks out against the Spark HTTP docs.

On tool calling (1): Spark's chat-completions endpoint gates OpenAI-style tool calls behind a request-body flag: tool_calls_switch defaults to false, in which case function calls come back as JSON inside the message content rather than in a tool_calls array (设置为true时,触发function call结果中tool_calls以数组格式返回,默认为 false,则以json格式返回). The declarative/OpenAI provider only lets me set custom headers, not extra body params, so I can't inject tool_calls_switch: true here — meaning tool calls won't surface in a form goose executes.

So I've framed this provider as chat-oriented rather than claiming tool support:

  • Updated the iflytek.json description to note the tool_calls_switch limitation.
  • Updated the providers table accordingly.

On the docs (2): good catch — dropped the stale lite mention; the table now lists 4.0Ultra, generalv3.5, max-32k to match iflytek.json.

Both in 8b0b02e.

If you'd be open to it, I'm happy to follow up with a separate PR adding an optional request-body-params field to declarative providers (e.g. extra_body), which would let Spark return real tool_calls and benefit other OpenAI-compatible providers with vendor-specific switches too.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b0b02e5a7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

{"name": "xopdeepseekv4flash", "context_limit": 131072},
{"name": "xopdeepseekv32", "context_limit": 131072},
{"name": "xopkimik26", "context_limit": 262144},
{"name": "xminimaxm25", "context_limit": 204800},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Lower MiniMax context limit to the Token Plan cap

When xminimaxm25 is used with the default Astron Token Plan endpoint, this advertises a 204800-token window, but the official Token Plan table lists MiniMax-M2.5 as 128K (https://www.xfyun.cn/doc/spark/TokenPlan.html). The registry copies this value into ModelConfig and context management uses it for compaction decisions, so goose can keep sending more than the provider accepts and hit 10907/10910 token-limit errors instead of compacting earlier; please cap this entry to the documented 128K limit.

Useful? React with 👍 / 👎.

Douwe M Osinga added 2 commits June 30, 2026 15:35
The xminimaxm25 model was listed with a 204800 context limit, but the
Astron Token Plan documents a 128K (131072) cap for this model. Lower it
to match.
@DOsinga

DOsinga commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Pushed a fix for the last codex comment: lowered xminimaxm25 from 204800 to the documented 128K (131072) Token Plan cap. Also merged main into the branch to keep it current.

Thanks for the iterations here — the consolidated test_all_bundled_providers_deserialize test and the tool-calling clarification all look good.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c80f20f81d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"dynamic_models": false,
"skip_canonical_filtering": true,
"models": [
{"name": "4.0Ultra", "context_limit": 8192},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Raise Spark Ultra's context limit

For 4.0Ultra, the Spark HTTP docs list Ultra's maximum input length as 32K (https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html), but this config advertises only 8192. ProviderEntry::normalize_model_config backfills ModelConfig.context_limit from these JSON ModelInfo entries, so 4.0Ultra sessions will compact and drop context around 8K even though the provider accepts much more; please set this entry to the documented window.

Useful? React with 👍 / 👎.

Comment on lines +24 to +25
{"name": "xopdeepseekv4pro", "context_limit": 131072},
{"name": "xopdeepseekv4flash", "context_limit": 131072},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Raise Astron V4 context limits

The Astron Token Plan model table documents DeepSeek-V4-Pro and DeepSeek-V4-Flash with 1M context windows (https://www.xfyun.cn/doc/spark/TokenPlan.html), but both entries here are capped at 131072. Because these static limits are copied into the session model config before compaction decisions, users of either V4 model will unnecessarily lose most of the usable context instead of running near the provider's advertised 1M window.

Useful? React with 👍 / 👎.

@DOsinga DOsinga left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Two clean OpenAI-compatible declarative providers, the test consolidation is a real improvement, and the last codex comment on the MiniMax context limit is now addressed. Thanks for the thoughtful iterations!

@DOsinga
DOsinga added this pull request to the merge queue Jun 30, 2026
Merged via the queue into aaif-goose:main with commit 04da8e8 Jun 30, 2026
25 checks passed
lifeizhou-ap added a commit that referenced this pull request Jul 1, 2026
* main: (26 commits)
  Fix MCP app sandbox bridge lifecycle (#10064)
  fix(bedrock): send inference config (max_tokens, temperature) on Converse (#9889)
  feat(providers): support OpenRouter request parameters (#9276)
  Migrate local inference model management to ACP (#10124)
  (attempt to) fix disk space errors in linux release builds (#10024)
  feat: add --edit session flag to edit conversation before forking (#9799)
  feat: add iFlytek Spark and Astron MaaS providers (#9837)
  fix(desktop): dedupe Nostr session deep link imports (#9918)
  [codex] Add SessionStart hook parity outside CLI (#9970)
  feat(providers): add Fireworks AI declarative provider (#9990)
  fix(providers): don't retry deterministically-permanent 400s (thinking-block immutability) (#10005)
  fix(deps): downgrade pkcs8 to v0.10 to match sec1/pkcs1 v0.7 (#10119)
  chore(deps): bump actions/cache from 5.0.2 to 6.0.0 (#10051)
  Make OpenAI Responses API store param configurable (#10040)
  remove unsupported model (#10121)
  chore(release): bump version to 1.40.0 (minor) (#10099)
  move ollama provider into goose-providers (#9986)
  UI acp migratoin: Decouple desktop UI types from generated OpenAPI types (#10109)
  fix(otel): use async reqwest client so OTLP export works in `goose serve` mode (#10100)
  feat (acp): exposed available tools in acp schema (#10097)
  ...
lifeizhou-ap added a commit that referenced this pull request Jul 1, 2026
* main: (42 commits)
  Fix MCP app sandbox bridge lifecycle (#10064)
  fix(bedrock): send inference config (max_tokens, temperature) on Converse (#9889)
  feat(providers): support OpenRouter request parameters (#9276)
  Migrate local inference model management to ACP (#10124)
  (attempt to) fix disk space errors in linux release builds (#10024)
  feat: add --edit session flag to edit conversation before forking (#9799)
  feat: add iFlytek Spark and Astron MaaS providers (#9837)
  fix(desktop): dedupe Nostr session deep link imports (#9918)
  [codex] Add SessionStart hook parity outside CLI (#9970)
  feat(providers): add Fireworks AI declarative provider (#9990)
  fix(providers): don't retry deterministically-permanent 400s (thinking-block immutability) (#10005)
  fix(deps): downgrade pkcs8 to v0.10 to match sec1/pkcs1 v0.7 (#10119)
  chore(deps): bump actions/cache from 5.0.2 to 6.0.0 (#10051)
  Make OpenAI Responses API store param configurable (#10040)
  remove unsupported model (#10121)
  chore(release): bump version to 1.40.0 (minor) (#10099)
  move ollama provider into goose-providers (#9986)
  UI acp migratoin: Decouple desktop UI types from generated OpenAPI types (#10109)
  fix(otel): use async reqwest client so OTLP export works in `goose serve` mode (#10100)
  feat (acp): exposed available tools in acp schema (#10097)
  ...
lifeizhou-ap added a commit that referenced this pull request Jul 1, 2026
* main: (31 commits)
  test: generic validator for declarative providers (#10010)
  UI acp migratoin: Decouple desktop UI types from generated OpenAPI types (Part 2) (#10149)
  Remove MCP sampling support (#10087)
  Support TLS for ACP serve (#10088)
  feat (ui): Migrate dictation local model manager to ACP (#10131)
  Fix MCP app sandbox bridge lifecycle (#10064)
  fix(bedrock): send inference config (max_tokens, temperature) on Converse (#9889)
  feat(providers): support OpenRouter request parameters (#9276)
  Migrate local inference model management to ACP (#10124)
  (attempt to) fix disk space errors in linux release builds (#10024)
  feat: add --edit session flag to edit conversation before forking (#9799)
  feat: add iFlytek Spark and Astron MaaS providers (#9837)
  fix(desktop): dedupe Nostr session deep link imports (#9918)
  [codex] Add SessionStart hook parity outside CLI (#9970)
  feat(providers): add Fireworks AI declarative provider (#9990)
  fix(providers): don't retry deterministically-permanent 400s (thinking-block immutability) (#10005)
  fix(deps): downgrade pkcs8 to v0.10 to match sec1/pkcs1 v0.7 (#10119)
  chore(deps): bump actions/cache from 5.0.2 to 6.0.0 (#10051)
  Make OpenAI Responses API store param configurable (#10040)
  remove unsupported model (#10121)
  ...
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.

2 participants