feat: add aliababa (singapore) provider (#2949)

Andrey Nering created

* Catwalk PR: https://github.com/charmbracelet/catwalk/pull/282
* Closes #2353
* Closes #2544

Change summary

README.md                     | 1 +
go.mod                        | 2 +-
go.sum                        | 4 ++--
internal/agent/coordinator.go | 4 ++++
internal/config/config.go     | 7 +++++++
5 files changed, 15 insertions(+), 3 deletions(-)

Detailed changes

README.md 🔗

@@ -193,6 +193,7 @@ That said, you can also set environment variables for preferred providers.
 | `CEREBRAS_API_KEY`          | Cerebras                                           |
 | `OPENROUTER_API_KEY`        | OpenRouter                                         |
 | `IONET_API_KEY`             | io.net                                             |
+| `ALIBABA_SINGAPORE_API_KEY` | Alibaba (Singapore)                                |
 | `GROQ_API_KEY`              | Groq                                               |
 | `AVIAN_API_KEY`             | Avian                                              |
 | `OPENCODE_API_KEY`          | OpenCode Zen & Go                                  |

go.mod 🔗

@@ -5,7 +5,7 @@ go 1.26.3
 require (
 	charm.land/bubbles/v2 v2.1.0
 	charm.land/bubbletea/v2 v2.0.6
-	charm.land/catwalk v0.40.4
+	charm.land/catwalk v0.41.0
 	charm.land/fang/v2 v2.0.1
 	charm.land/fantasy v0.25.0
 	charm.land/glamour/v2 v2.0.0

go.sum 🔗

@@ -2,8 +2,8 @@ charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g=
 charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY=
 charm.land/bubbletea/v2 v2.0.6 h1:UHN/91OyuhaOFGSrBXQ/hMZD8IO1Uc4BvHlgHXL2WJo=
 charm.land/bubbletea/v2 v2.0.6/go.mod h1:MH/D8ZLlN3op37vQvijKuU29g3rqTp+aQapURFonF9g=
-charm.land/catwalk v0.40.4 h1:rVMn+bzrrqRZjT2MGExeyd4ziqCCVAnRCMNp1A2aaKg=
-charm.land/catwalk v0.40.4/go.mod h1:LmMFJdRqF5F7qKa+xqD9SBq7tph7L98GU3ZFa1TxftA=
+charm.land/catwalk v0.41.0 h1:rGeGrEJLFIFqz+glpCD4ICTo2PzL1GMFqGN+jpQn7O4=
+charm.land/catwalk v0.41.0/go.mod h1:LmMFJdRqF5F7qKa+xqD9SBq7tph7L98GU3ZFa1TxftA=
 charm.land/fang/v2 v2.0.1 h1:zQCM8JQJ1JnQX/66B5jlCYBUxL2as5JXQZ2KJ6EL0mY=
 charm.land/fang/v2 v2.0.1/go.mod h1:S1GmkpcvK+OB5w9caywUnJcsMew45Ot8FXqoz8ALrII=
 charm.land/fantasy v0.25.0 h1:oXOWY1ivmTSnhYGzAolscF8zKtavWZyBWv0LHRSwN5Q=

internal/agent/coordinator.go 🔗

@@ -392,6 +392,10 @@ func getProviderOptions(model Model, providerCfg config.ProviderConfig) fantasy.
 					"type": "disabled",
 				}
 			}
+		case string(catwalk.InferenceProviderAlibabaSingapore):
+			if model.CatwalkCfg.CanReason {
+				extraBody["enable_thinking"] = model.ModelCfg.Think
+			}
 		}
 
 		mergedOptions["extra_body"] = extraBody

internal/config/config.go 🔗

@@ -745,6 +745,13 @@ func (c *ProviderConfig) TestConnection(resolver VariableResolver) error {
 	case catwalk.InferenceProviderMiniMax, catwalk.InferenceProviderMiniMaxChina:
 		// NOTE: MiniMax has no good endpoint we can use to validate the API key.
 		return nil
+	case catwalk.InferenceProviderAlibabaSingapore:
+		// NOTE: Alibaba has no good endpoint we can use to validate the API key.
+		// Let's at least check the pattern.
+		if !strings.HasPrefix(apiKey, "sk-") {
+			return fmt.Errorf("invalid API key format for provider %s", c.ID)
+		}
+		return nil
 	}
 
 	switch c.Type {