diff --git a/README.jp.md b/README.jp.md
index 6e436231f..5ce06caec 100644
--- a/README.jp.md
+++ b/README.jp.md
@@ -478,6 +478,10 @@ DeepChatはメッセージアプリからリモート操作できるため、デ

Obscurify.ai
+
+ 
+ Wally
+ |
diff --git a/README.md b/README.md
index 37d7c51f3..ae7ff4d41 100644
--- a/README.md
+++ b/README.md
@@ -481,6 +481,10 @@ Common commands include `/start`, `/help`, `/pair`, `/new`, `/sessions`, `/use`,

Obscurify.ai
+
+ 
+ Wally
+ |
diff --git a/README.zh.md b/README.zh.md
index 382995679..03a1a46bc 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -475,6 +475,10 @@ DeepChat 可以通过聊天软件远程控制,让你离开桌面后也能继

Obscurify.ai
+
+ 
+ Wally
+ |
diff --git a/src/main/provider/defaults.ts b/src/main/provider/defaults.ts
index 0b25da295..e6d10c61e 100644
--- a/src/main/provider/defaults.ts
+++ b/src/main/provider/defaults.ts
@@ -706,6 +706,20 @@ const PROVIDER_DEFAULTS: LLM_PROVIDER_BASE[] = [
models: 'https://cloudgpu.app/api?utm_source=deepchat'
}
},
+ {
+ id: 'wally',
+ name: 'Wally',
+ apiType: 'openai-completions',
+ apiKey: '',
+ baseUrl: 'https://inference.runanywhere.ai/v1',
+ enable: false,
+ websites: {
+ official: 'https://www.runwally.com/',
+ apiKey: 'https://console.runanywhere.ai/',
+ docs: 'https://docs.runanywhere.ai/wally-cloud/api',
+ models: 'https://www.runwally.com/pricing'
+ }
+ },
{
id: 'cheapaimodel',
name: 'cheapaimodel',
diff --git a/src/main/provider/providerRegistry.ts b/src/main/provider/providerRegistry.ts
index e3bba4837..66ba61bd4 100644
--- a/src/main/provider/providerRegistry.ts
+++ b/src/main/provider/providerRegistry.ts
@@ -665,6 +665,19 @@ const PROVIDER_ID_REGISTRY = new Map([
checkMaxTokens: 16
})
],
+ [
+ 'wally',
+ createDefinition({
+ ...OPENAI_BASE,
+ checkStrategy: 'generate-text',
+ credentialStrategy: 'api-key',
+ embeddingStrategy: 'none',
+ checkModelId: 'deepseek-v4.1-flash',
+ checkPrompt: 'Hello',
+ checkTemperature: 0.2,
+ checkMaxTokens: 16
+ })
+ ],
[
'obscurify',
createDefinition({
diff --git a/src/renderer/src/assets/llm-icons/wally-color.svg b/src/renderer/src/assets/llm-icons/wally-color.svg
new file mode 100644
index 000000000..6e78052a1
--- /dev/null
+++ b/src/renderer/src/assets/llm-icons/wally-color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/src/components/icons/modelIconRegistry.ts b/src/renderer/src/components/icons/modelIconRegistry.ts
index bbcfc5e9f..f6da27172 100644
--- a/src/renderer/src/components/icons/modelIconRegistry.ts
+++ b/src/renderer/src/components/icons/modelIconRegistry.ts
@@ -56,6 +56,7 @@ import opperColorIcon from '@/assets/llm-icons/opper.svg?url'
import aistupidlevelIcon from '@/assets/llm-icons/aistupidlevel.svg?url'
import runcrateIcon from '@/assets/llm-icons/runcrate.svg?url'
import cloudgpuIcon from '@/assets/llm-icons/cloudgpu.svg?url'
+import wallyColorIcon from '@/assets/llm-icons/wally-color.svg?url'
import cheapaimodelIcon from '@/assets/llm-icons/cheapaimodel.svg?url'
import obscurifyIcon from '@/assets/llm-icons/obscurify.svg?url'
import demonrouteColorIcon from '@/assets/llm-icons/demonroute.svg?url'
@@ -210,6 +211,7 @@ export const modelIcons = {
aistupidlevel: aistupidlevelIcon,
runcrate: runcrateIcon,
cloudgpu: cloudgpuIcon,
+ wally: wallyColorIcon,
cheapaimodel: cheapaimodelIcon,
obscurify: obscurifyIcon,
demonroute: demonrouteColorIcon,
diff --git a/test/main/provider/basicApiKeyProviders.test.ts b/test/main/provider/basicApiKeyProviders.test.ts
index 6cbabc8c6..f612baee6 100644
--- a/test/main/provider/basicApiKeyProviders.test.ts
+++ b/test/main/provider/basicApiKeyProviders.test.ts
@@ -1813,6 +1813,40 @@ describe('basic API-key provider registrations', () => {
)
})
+ it('checks Wally with a chat model instead of its public model list', async () => {
+ const defaults = DEFAULT_PROVIDERS.find((provider) => provider.id === 'wally')!
+ expect(defaults).toBeDefined()
+ const config = { ...defaults, apiKey: 'test-key' }
+ // Wally's /v1/models answers without a key, so listing models cannot validate one.
+ expect(resolveAiSdkProviderDefinition(config)).toMatchObject({
+ runtimeKind: 'openai-compatible',
+ modelSource: 'openai',
+ checkStrategy: 'generate-text',
+ credentialStrategy: 'api-key',
+ embeddingStrategy: 'none',
+ checkModelId: 'deepseek-v4.1-flash'
+ })
+ const provider = new AiSdkProvider(config, createProviderSettings())
+ ;(provider as any).isInitialized = true
+
+ await expect(provider.check()).resolves.toEqual({ isOk: true, errorMsg: null })
+ expect(mockRunAiSdkGenerateText).toHaveBeenCalledWith(
+ expect.objectContaining({
+ providerKind: 'openai-compatible',
+ provider: expect.objectContaining({
+ id: 'wally',
+ apiKey: 'test-key',
+ baseUrl: 'https://inference.runanywhere.ai/v1'
+ })
+ }),
+ [{ role: 'user', content: 'Hello' }],
+ 'deepseek-v4.1-flash',
+ expect.any(Object),
+ 0.2,
+ 16
+ )
+ })
+
it('lists every CloudGPU catalog id except non-chat models', async () => {
const defaults = DEFAULT_PROVIDERS.find(({ id }) => id === 'cloudgpu')!
const fetchMock = vi.fn().mockResolvedValue(
diff --git a/test/main/provider/defaultProviders.test.ts b/test/main/provider/defaultProviders.test.ts
index 7fd2e1256..7afce6024 100644
--- a/test/main/provider/defaultProviders.test.ts
+++ b/test/main/provider/defaultProviders.test.ts
@@ -170,6 +170,25 @@ describe('DEFAULT_PROVIDERS', () => {
)
})
+ it('includes Wally as a disabled built-in OpenAI-compatible provider', () => {
+ expect(DEFAULT_PROVIDERS).toContainEqual(
+ expect.objectContaining({
+ id: 'wally',
+ name: 'Wally',
+ apiType: 'openai-completions',
+ baseUrl: 'https://inference.runanywhere.ai/v1',
+ enable: false,
+ websites: expect.objectContaining({
+ official: 'https://www.runwally.com/',
+ apiKey: 'https://console.runanywhere.ai/',
+ docs: 'https://docs.runanywhere.ai/wally-cloud/api',
+ models: 'https://www.runwally.com/pricing',
+ defaultBaseUrl: 'https://inference.runanywhere.ai/v1'
+ })
+ })
+ )
+ })
+
it('includes Obscurify.ai as a disabled built-in OpenAI-compatible provider', () => {
expect(DEFAULT_PROVIDERS).toContainEqual(
expect.objectContaining({