Skip to content

Commit 6d21316

Browse files
authored
chore(sponsors): retire ApiSmart sponsorship and provider preset entry (#1532)
Remove ApiSmart from both READMEs and mark its preset deprecated so it is no longer offered when adding a provider. The preset stays in the bundle so providers already saved against it keep resolving defaultEnv, context windows and image generation defaults.
1 parent 5ae544d commit 6d21316

6 files changed

Lines changed: 42 additions & 85 deletions

File tree

‎README.en.md‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,6 @@ This project is maintained in the author's spare time. Corporate or individual s
6464
Thanks to <a href="https://aruhub.com/sign-up?aff=Z54g">AruHub</a> for sponsoring this project! AruHub focuses on providing developers with a stable, long-term primary API upstream service. Built for high-frequency AI coding workflows such as Codex and Claude Code, it offers official GPT API access through enterprise groups and stable Claude enterprise groups for sustained use, with support for leading models including GPT and Claude. Image 2 / 2.5 is available around the clock, starting at CNY 0.04 per image. Pay-as-you-go billing supports high-volume enterprise usage, invoicing, and refunds. Register through the <a href="https://aruhub.com/sign-up?aff=Z54g">exclusive link</a> to receive US$1 in credit usable across all models, with no model restrictions.
6565
</td>
6666
</tr>
67-
<tr>
68-
<td align="center" valign="middle">
69-
<a href="https://www.apismart.ai">
70-
<img src="docs/images/sponsors/apismart-logo.png" width="180" alt="ApiSmart">
71-
</a>
72-
</td>
73-
<td valign="middle">
74-
Thanks to <a href="https://www.apismart.ai">ApiSmart</a> for sponsoring this project. ApiSmart provides unified access to leading AI models through a single API. Use one API key to connect with LLM, image, and video models through an OpenAI-compatible interface. Easily switch between models, simplify billing, and improve reliability with intelligent routing and automatic failover.
75-
</td>
76-
</tr>
7767
</tbody>
7868
</table>
7969

‎README.md‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,6 @@ cc-haha 是一个**桌面端 Claude Code 工作台**:多会话与全局搜索
6464
感谢 <a href="https://aruhub.com/sign-up?aff=Z54g">AruHub</a> 对本项目的赞助!AruHub 专注为开发者提供长期稳定的主力 API 上游服务。面向 Codex / Claude Code 等高频 AI Coding 场景,提供 GPT 企业分组官方接口、Claude 稳定企业分组,适合长时间持续调用;同时覆盖 GPT、Claude 等主流模型。Image 2 / 2.5 全天稳定供应,低至 0.04 元/张。按量付费,支持企业大用量、开发票及退款。通过 <a href="https://aruhub.com/sign-up?aff=Z54g">专属链接</a>注册即送 1 美元全模型通用额度,不限制模型使用。
6565
</td>
6666
</tr>
67-
<tr>
68-
<td align="center" valign="middle">
69-
<a href="https://www.apismart.ai">
70-
<img src="docs/images/sponsors/apismart-logo.png" width="180" alt="ApiSmart">
71-
</a>
72-
</td>
73-
<td valign="middle">
74-
感谢 <a href="https://www.apismart.ai">ApiSmart</a> 赞助本项目!ApiSmart 通过单一 API 提供对主流 AI 模型的统一访问。只需一个 API Key,即可通过兼容 OpenAI 的接口连接大语言模型、图像模型和视频模型。轻松切换模型、简化账单管理,并通过智能路由和自动故障转移提升可靠性。
75-
</td>
76-
</tr>
7767
</tbody>
7868
</table>
7969

‎desktop/src/config/providerPresets.test.ts‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,22 @@ describe('bundled provider presets', () => {
128128

129129
it('keeps active sponsors selectable and ordered like the README', () => {
130130
expect(selectableProviderPresets(BUNDLED_PROVIDER_PRESETS).filter((preset) => preset.featured).map((preset) => preset.id))
131-
.toEqual(['aruhub', 'atlascloud', 'apismart'])
131+
.toEqual(['aruhub', 'atlascloud'])
132+
})
133+
134+
it('retires ApiSmart without removing what saved providers resolve from the preset', () => {
135+
const preset = BUNDLED_PROVIDER_PRESETS.find((candidate) => candidate.id === 'apismart')
136+
137+
expect(preset?.deprecated).toBe(true)
138+
expect(preset?.apiKeyUrl).toBeUndefined()
139+
expect(preset?.promoText).toBeUndefined()
140+
expect(preset?.featured).toBeUndefined()
141+
expect(preset?.baseUrl).toBe('https://gw.apismart.ai/v1')
142+
expect(preset?.apiFormat).toBe('openai_chat')
143+
expect(preset?.defaultImageGeneration).toEqual({ model: 'doubao-seedream-5-0' })
144+
expect(preset?.modelContextWindows).toBeDefined()
145+
expect(selectableProviderPresets(BUNDLED_PROVIDER_PRESETS).map((candidate) => candidate.id))
146+
.not.toContain('apismart')
132147
})
133148

134149
it('keeps the retired 接口AI preset resolvable but not selectable', () => {

‎desktop/src/pages/settings/ProviderSettings.test.tsx‎

Lines changed: 17 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const savedProviders: SavedProvider[] = ([
2828
models: { main: model, haiku: model, sonnet: model, opus: model },
2929
}))
3030

31-
describe('ApiSmart sponsor provider', () => {
31+
describe('active sponsor providers', () => {
3232
beforeEach(() => {
3333
useSettingsStore.setState({ locale: 'en' })
3434
vi.spyOn(useSettingsStore.getState(), 'fetchAll').mockResolvedValue()
@@ -55,9 +55,7 @@ describe('ApiSmart sponsor provider', () => {
5555
expect(sponsor.parentElement).toBe(dialog.getByRole('button', { name: 'Atlas Cloud' }).parentElement)
5656
expect(within(sponsor).getByText('New')).toBeInTheDocument()
5757
expect(within(sponsor).getByLabelText('Sponsor')).toBeInTheDocument()
58-
for (const name of ['Atlas Cloud', 'ApiSmart']) {
59-
expect(within(dialog.getByRole('button', { name })).queryByLabelText('Sponsor')).not.toBeInTheDocument()
60-
}
58+
expect(within(dialog.getByRole('button', { name: 'Atlas Cloud' })).queryByLabelText('Sponsor')).not.toBeInTheDocument()
6159
fireEvent.click(sponsor)
6260
expect(dialog.getByDisplayValue('https://agent.aruhub.com')).toBeInTheDocument()
6361
expect(dialog.getAllByDisplayValue('claude-opus-5')).toHaveLength(2)
@@ -118,66 +116,12 @@ describe('ApiSmart sponsor provider', () => {
118116
render(<ProviderSettings />)
119117
fireEvent.click(await screen.findByRole('button', { name: /Add Model/ }))
120118
const dialog = within(screen.getByRole('dialog'))
121-
fireEvent.click(dialog.getByRole('button', { name: 'ApiSmart' }))
119+
fireEvent.click(dialog.getByRole('button', { name: 'Atlas Cloud' }))
122120

123121
expect(dialog.getByRole('button', { name: /OpenAI Chat Completions \(proxy\)/ })).toBeInTheDocument()
124122
expect(dialog.queryByText(/point the base URL at an endpoint that serves it/)).not.toBeInTheDocument()
125123
})
126124

127-
it('prefills the sponsor connection, opens its landing page, and saves the selected models', async () => {
128-
const open = vi.spyOn(getDesktopHost().shell, 'open').mockResolvedValue()
129-
const create = vi.spyOn(providersApi, 'create').mockImplementation(async (input) => ({
130-
provider: { ...input, id: 'saved-apismart', apiFormat: input.apiFormat ?? 'anthropic' },
131-
}))
132-
render(<ProviderSettings />)
133-
fireEvent.click(await screen.findByRole('button', { name: /Add Model/ }))
134-
const dialog = within(screen.getByRole('dialog'))
135-
const sponsor = dialog.getByRole('button', { name: 'ApiSmart' })
136-
expect(sponsor.parentElement).toBe(dialog.getByRole('button', { name: 'Atlas Cloud' }).parentElement)
137-
fireEvent.click(sponsor)
138-
expect(dialog.getByDisplayValue('https://gw.apismart.ai/v1')).toBeInTheDocument()
139-
expect(dialog.getAllByDisplayValue('deepseek-v4-pro-0813')).toHaveLength(3)
140-
expect(dialog.getByDisplayValue('deepseek-v4-flash-0731-tem')).toBeInTheDocument()
141-
expect(dialog.getByRole('switch', { name: 'Enable image generation' })).toBeChecked()
142-
expect(dialog.getByDisplayValue('doubao-seedream-5-0')).toBeInTheDocument()
143-
fireEvent.change(dialog.getByRole('textbox', { name: 'Reply output budget' }), { target: { value: '48000' } })
144-
145-
fireEvent.click(dialog.getByRole('button', { name: /Get API Key/ }))
146-
expect(open).toHaveBeenCalledWith('https://www.apismart.ai')
147-
fireEvent.change(dialog.getAllByPlaceholderText('sk-...')[0]!, { target: { value: 'fake-apismart-key' } })
148-
fireEvent.click(dialog.getByRole('button', { name: 'Add' }))
149-
await waitFor(() => expect(create).toHaveBeenCalledWith(expect.objectContaining({
150-
presetId: 'apismart',
151-
name: 'ApiSmart',
152-
baseUrl: 'https://gw.apismart.ai/v1',
153-
apiFormat: 'openai_chat',
154-
authStrategy: 'api_key',
155-
apiKey: 'fake-apismart-key',
156-
imageGeneration: { model: 'doubao-seedream-5-0' },
157-
requestCompatibility: { maxOutputTokens: 48000 },
158-
models: {
159-
main: 'deepseek-v4-pro-0813',
160-
haiku: 'deepseek-v4-flash-0731-tem',
161-
sonnet: 'deepseek-v4-pro-0813',
162-
opus: 'deepseek-v4-pro-0813',
163-
},
164-
})))
165-
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument())
166-
})
167-
168-
it('resets image credentials and defaults when switching presets', async () => {
169-
render(<ProviderSettings />)
170-
fireEvent.click(await screen.findByRole('button', { name: /Add Model/ }))
171-
const dialog = within(screen.getByRole('dialog'))
172-
fireEvent.click(dialog.getByRole('button', { name: 'ApiSmart' }))
173-
fireEvent.change(dialog.getAllByPlaceholderText('sk-...')[1]!, { target: { value: 'fake-image-only-key' } })
174-
fireEvent.click(dialog.getByRole('button', { name: 'Atlas Cloud' }))
175-
expect(dialog.getByRole('switch', { name: 'Enable image generation' })).not.toBeChecked()
176-
fireEvent.click(dialog.getByRole('button', { name: 'ApiSmart' }))
177-
expect(dialog.getByDisplayValue('doubao-seedream-5-0')).toBeInTheDocument()
178-
expect(dialog.getAllByPlaceholderText('sk-...')[1]).toHaveValue('')
179-
})
180-
181125
it('preserves image generation disabled on an older saved ApiSmart provider', async () => {
182126
vi.mocked(providersApi.list).mockResolvedValue({ providers: [{
183127
...savedProviders[0]!, id: 'old-apismart', presetId: 'apismart', name: 'ApiSmart',
@@ -189,6 +133,20 @@ describe('ApiSmart sponsor provider', () => {
189133
expect(within(screen.getByRole('dialog')).getByRole('switch', { name: 'Enable image generation' }))
190134
.not.toBeChecked()
191135
})
136+
137+
it('keeps a saved ApiSmart provider but no longer offers it as a new provider', async () => {
138+
vi.mocked(providersApi.list).mockResolvedValue({ providers: [{
139+
...savedProviders[0]!, id: 'old-apismart', presetId: 'apismart', name: 'ApiSmart',
140+
baseUrl: 'https://gw.apismart.ai/v1', apiFormat: 'openai_chat',
141+
}], activeId: null })
142+
render(<ProviderSettings />)
143+
expect(await screen.findByTestId('provider-old-apismart')).toHaveTextContent('ApiSmart')
144+
145+
fireEvent.click(screen.getByRole('button', { name: /Add Model/ }))
146+
const dialog = within(screen.getByRole('dialog'))
147+
expect(dialog.getByRole('button', { name: 'Atlas Cloud' })).toBeInTheDocument()
148+
expect(dialog.queryByRole('button', { name: 'ApiSmart' })).not.toBeInTheDocument()
149+
})
192150
})
193151

194152
describe('retired sponsor providers', () => {

‎src/server/__tests__/provider-presets.test.ts‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('provider presets API', () => {
7777

7878
// ApiSmart /v1/models and live calls verified these exact IDs on 2026-09-09.
7979
// The unsuffixed names in its docs return 503 provider_not_available.
80-
test('exposes ApiSmart with its live-verified Chat Completions defaults and sponsor link', async () => {
80+
test('keeps retired ApiSmart resolvable with its live-verified Chat Completions defaults but off the sponsor row', async () => {
8181
const { req, url, segments } = makeRequest('GET', '/api/providers/presets')
8282
const response = await handleProvidersApi(req, url, segments)
8383
const { presets } = await response.json()
@@ -94,10 +94,15 @@ describe('provider presets API', () => {
9494
sonnet: 'deepseek-v4-pro-0813',
9595
opus: 'deepseek-v4-pro-0813',
9696
},
97-
apiKeyUrl: 'https://www.apismart.ai',
9897
websiteUrl: 'https://www.apismart.ai',
99-
featured: true,
98+
deprecated: true,
10099
})
100+
const apismart = presets.find((preset: { id: string }) => preset.id === 'apismart')
101+
expect(apismart.apiKeyUrl).toBeUndefined()
102+
expect(apismart.promoText).toBeUndefined()
103+
expect(apismart.featured).toBeUndefined()
104+
expect(presets.filter((preset: { featured?: boolean }) => preset.featured).map((preset: { id: string }) => preset.id))
105+
.not.toContain('apismart')
101106
})
102107

103108
test('GET /api/providers/presets returns the configured presets', async () => {

‎src/server/config/providerPresets.json‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,7 @@
317317
},
318318
"needsApiKey": true,
319319
"websiteUrl": "https://www.apismart.ai",
320-
"apiKeyUrl": "https://www.apismart.ai",
321-
"featured": true,
320+
"deprecated": true,
322321
"authStrategy": "api_key",
323322
"modelContextWindows": {
324323
"deepseek-v4-pro-0813": 1000000,

0 commit comments

Comments
 (0)