feat(providers): add APIFree support

Add APIFree as a built-in OpenAI-compatible provider. APIFree offers
agent-optimised models such as skywork-ai/skyclaw-v1 through an
OpenAI-compatible API at https://api.apifree.ai/agent/v1.

Changes:
- Register apifree provider in the provider registry
- Add config schema field
- Add documentation with configuration example
- Add provider tests, websocket channel tests, and webui tests
- Add provider icon in settings UI
This commit is contained in:
moran
2026-05-20 12:33:03 +08:00
committed by Xubin Ren
parent 3eebe08dba
commit 61ae869610
7 changed files with 123 additions and 0 deletions
@@ -2150,6 +2150,7 @@ const PROVIDER_ICONS: Record<string, LucideIcon> = {
byteplus_coding_plan: Cloud,
qianfan: Database,
ant_ling: Sparkles,
apifree: Sparkles,
azure_openai: Cloud,
bedrock: Database,
vllm: Cpu,
+10
View File
@@ -580,6 +580,13 @@ describe("App layout", () => {
api_key_required: true,
default_api_base: "https://api.ant-ling.com/v1",
},
{
name: "apifree",
label: "APIFree",
configured: false,
api_key_required: true,
default_api_base: "https://api.apifree.ai/agent/v1",
},
{
name: "azure_openai",
label: "Azure OpenAI",
@@ -739,6 +746,7 @@ describe("App layout", () => {
fireEvent.click(within(settingsNav).getByRole("button", { name: "Providers" }));
expect(screen.getByText("OpenRouter")).toBeInTheDocument();
expect(screen.getByText("Ant Ling")).toBeInTheDocument();
expect(screen.getByText("APIFree")).toBeInTheDocument();
expect(screen.getAllByText("Not configured").length).toBeGreaterThan(0);
fireEvent.click(screen.getByText("OpenAI"));
fireEvent.click(screen.getByRole("button", { name: "Edit" }));
@@ -751,6 +759,8 @@ describe("App layout", () => {
expect(screen.queryByDisplayValue("unsaved-openai-key")).not.toBeInTheDocument();
fireEvent.click(screen.getByText("Ant Ling"));
expect(screen.getByDisplayValue("https://api.ant-ling.com/v1")).toBeInTheDocument();
fireEvent.click(screen.getByText("APIFree"));
expect(screen.getByDisplayValue("https://api.apifree.ai/agent/v1")).toBeInTheDocument();
fireEvent.click(screen.getByText("Atomic Chat"));
expect(screen.getByDisplayValue("http://localhost:1337/v1")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Save" })).toBeEnabled();