Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,40 @@ describe('non-subscription account creation', () => {
);
});

it('offers a dedicated StepFun API key entry', async () => {
const onPick = vi.fn();
render(<ServiceCatalogView onPick={onPick} />);
fireEvent.click(screen.getByText('serviceName.stepfun'));
expect(onPick).toHaveBeenCalledWith('stepfun');
cleanup();

const onSubmit = vi.fn();
render(
<AddAccountForm
serviceId="stepfun"
runtimes={undefined}
onboarding={onboarding()}
busy={false}
onBack={vi.fn()}
onSubmit={onSubmit}
/>,
);

const secret = document.querySelector<HTMLInputElement>('input[type="password"]');
if (!secret) throw new Error('credential input missing');
fireEvent.change(secret, { target: { value: 'stepfun-test-key' } });
fireEvent.click(screen.getByRole('button', { name: 'form.submit' }));

await waitFor(() =>
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
service: 'stepfun',
credential: { type: 'api-key', key: 'stepfun-test-key' },
}),
),
);
});

it('asks nothing about protocol for a service serving several shapes', () => {
render(
<AddAccountForm
Expand Down
33 changes: 33 additions & 0 deletions packages/foundation/providers/src/__tests__/resolve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,35 @@ describe('resolveBinding: variant chosen per agent', () => {
});
});

it('routes a StepFun key over each supported native protocol', () => {
const stepfun = account({ service: 'stepfun' });
expect(resolveBinding(stepfun, 'claude-code')).toEqual({
tier: 'native',
protocol: 'anthropic',
baseUrl: 'https://api.stepfun.com',
});
expect(resolveBinding(stepfun, 'codex')).toEqual({
tier: 'native',
protocol: 'openai-responses',
baseUrl: 'https://api.stepfun.com/v1',
});
expect(resolveBinding(stepfun, 'opencode')).toEqual({
tier: 'native',
protocol: 'openai-chat',
baseUrl: 'https://api.stepfun.com/v1',
knownProvider: 'stepfun',
});
expect(resolveBinding(stepfun, 'pi')).toEqual({
tier: 'native',
protocol: 'anthropic',
baseUrl: 'https://api.stepfun.com',
});
expect(resolveBinding(stepfun, 'grok-build')).toEqual({
tier: 'unavailable',
reason: 'protocol-unsupported',
});
});

it('reaches codex on every service whose endpoint serves the Responses API', () => {
// Verified against vendor docs 2026-08: xAI, OpenRouter and Vercel all serve POST /responses
// at the base URL declared here, so codex must have a target on each.
Expand Down Expand Up @@ -305,6 +334,10 @@ describe('catalog helpers', () => {
url: 'https://api.deepseek.com/models',
wire: 'openai',
});
expect(modelListSource('stepfun')).toEqual({
url: 'https://api.stepfun.com/v1/models',
wire: 'openai',
});
expect(modelListSource('anthropic-api')?.wire).toBe('anthropic');
expect(modelListSource('linkcode-gateway')).toEqual({
url: 'https://gateway.linkcode.ai/v1/models',
Expand Down
18 changes: 18 additions & 0 deletions packages/foundation/providers/src/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,24 @@ export const SERVICE_CATALOG: ServiceDescriptor[] = [
models: { url: 'https://api.deepseek.com/models', wire: 'openai' },
secretPlaceholder: 'sk-…',
},
{
id: 'stepfun',
label: 'StepFun',
group: 'direct',
kind: 'endpoint',
credentialType: 'api-key',
variants: {
anthropic: { baseUrl: 'https://api.stepfun.com' },
// Responses serves `step-3.7-flash` only (2026-08).
'openai-responses': { baseUrl: 'https://api.stepfun.com/v1' },
Comment thread
lucas77778 marked this conversation as resolved.
Comment thread
lucas77778 marked this conversation as resolved.
'openai-chat': {
baseUrl: 'https://api.stepfun.com/v1',
knownProvider: { opencode: 'stepfun' },
},
},
models: { url: 'https://api.stepfun.com/v1/models', wire: 'openai' },
secretPlaceholder: 'sk-…',
},
{
id: LINKCODE_GATEWAY_SERVICE_ID,
label: 'LinkCode Gateway',
Expand Down
2 changes: 2 additions & 0 deletions packages/presentation/i18n/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,7 @@ export const en = {
'openai-api': 'OpenAI API',
xai: 'xAI (Grok)',
deepseek: 'DeepSeek',
stepfun: 'StepFun',
openrouter: 'OpenRouter',
'vercel-gateway': 'Vercel AI Gateway',
'cloudflare-gateway': 'Cloudflare AI Gateway',
Expand All @@ -1130,6 +1131,7 @@ export const en = {
'openai-api': 'A key from platform.openai.com',
xai: 'A key from console.x.ai',
deepseek: 'A key from platform.deepseek.com',
stepfun: 'A key from platform.stepfun.com',
'linkcode-gateway': 'Use LinkCode credits without bringing an API key',
openrouter: 'Aggregation gateway',
'vercel-gateway': 'Server-side translating gateway',
Expand Down
2 changes: 2 additions & 0 deletions packages/presentation/i18n/src/locales/zh-cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,7 @@ export const zhCN = {
'openai-api': 'OpenAI API',
xai: 'xAI(Grok)',
deepseek: 'DeepSeek',
stepfun: '阶跃星辰 StepFun',
openrouter: 'OpenRouter',
'vercel-gateway': 'Vercel AI Gateway',
'cloudflare-gateway': 'Cloudflare AI Gateway',
Expand All @@ -1103,6 +1104,7 @@ export const zhCN = {
'openai-api': 'platform.openai.com 的密钥',
xai: 'console.x.ai 的密钥',
deepseek: 'platform.deepseek.com 的密钥',
stepfun: 'platform.stepfun.com 的密钥',
'linkcode-gateway': '无需自带 API 密钥,直接使用 LinkCode 额度',
openrouter: '聚合网关',
'vercel-gateway': '服务端翻译网关',
Expand Down
2 changes: 2 additions & 0 deletions packages/presentation/ui/src/shell/service-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import CloudflareColorGlyph from '~icons/lobe-icons/cloudflare-color';
import DeepSeekColorGlyph from '~icons/lobe-icons/deepseek-color';
import OpenAiGlyph from '~icons/lobe-icons/openai';
import OpenRouterGlyph from '~icons/lobe-icons/openrouter';
import StepFunColorGlyph from '~icons/lobe-icons/stepfun-color';
import VercelGlyph from '~icons/lobe-icons/vercel';
import XaiGlyph from '~icons/lobe-icons/xai';
import { cn } from '../lib/cn';
Expand All @@ -32,6 +33,7 @@ const SERVICE_GLYPHS: Record<string, typeof AnthropicGlyph> = {
'openai-api': OpenAiGlyph,
xai: XaiGlyph,
deepseek: DeepSeekColorGlyph,
stepfun: StepFunColorGlyph,
openrouter: OpenRouterGlyph,
'vercel-gateway': VercelGlyph,
'cloudflare-gateway': CloudflareColorGlyph,
Expand Down
Loading