feat: support ollama cloud anthropic compatible api (#1175)

* feat(droid-provider): support ollama cloud anthropic compatible api

* fix(droid-provider): move ollama.com to anthropic block to prevent model inference override

* fix(droid-provider): move pathname-based generic checks before host-based anthropic check

* test(proxy): cover ollama cloud native routing boundaries

---------

Co-authored-by: Tam Nhu Tran <kaitran.ntt@gmail.com>
This commit is contained in:
Wei He
2026-05-03 22:28:38 -04:00
committed by GitHub
co-authored by Tam Nhu Tran
parent 0070f79b90
commit 4e2def6769
3 changed files with 69 additions and 6 deletions
+14 -6
View File
@@ -76,7 +76,19 @@ export function inferDroidProviderFromBaseUrl(
return 'openai';
}
if (host.includes('anthropic.com') || pathname.includes('/anthropic')) {
if (
pathname.includes('/compatible-mode') ||
pathname.includes('/openai') ||
pathname.includes('/chat/completions')
) {
return 'generic-chat-completion-api';
}
if (
host.includes('anthropic.com') ||
pathname.includes('/anthropic') ||
host.includes('ollama.com')
) {
return 'anthropic';
}
@@ -87,11 +99,7 @@ export function inferDroidProviderFromBaseUrl(
host.includes('api.fireworks.ai') ||
host.includes('inference.baseten.co') ||
host.includes('dashscope') ||
host.includes('huggingface.co') ||
host.includes('ollama.com') ||
pathname.includes('/compatible-mode') ||
pathname.includes('/openai') ||
pathname.includes('/chat/completions')
host.includes('huggingface.co')
) {
return 'generic-chat-completion-api';
}
+29
View File
@@ -50,4 +50,33 @@ describe('resolveOpenAICompatProfileConfig', () => {
expect(result?.provider).toBe('generic-chat-completion-api');
expect(result?.model).toBe('qwen3.6-plus');
});
it('does not proxy bare ollama.com profiles that are anthropic-native', () => {
const result = resolveOpenAICompatProfileConfig(
'ollama-cloud',
'/tmp/ollama-cloud.settings.json',
{
ANTHROPIC_BASE_URL: 'https://ollama.com',
ANTHROPIC_AUTH_TOKEN: 'ollama-token',
ANTHROPIC_MODEL: 'qwen3-coder-plus',
}
);
expect(result).toBeNull();
});
it('still proxies explicit ollama.com chat-completions endpoints', () => {
const result = resolveOpenAICompatProfileConfig(
'ollama-cloud-chat-completions',
'/tmp/ollama-cloud-chat-completions.settings.json',
{
ANTHROPIC_BASE_URL: 'https://ollama.com/v1/chat/completions',
ANTHROPIC_AUTH_TOKEN: 'ollama-token',
ANTHROPIC_MODEL: 'qwen3-coder-plus',
}
);
expect(result).not.toBeNull();
expect(result?.provider).toBe('generic-chat-completion-api');
});
});
+26
View File
@@ -94,5 +94,31 @@ describe('droid-provider', () => {
expect(resolveDroidProvider({ baseUrl: 'http://127.0.0.1:8317' })).toBe('anthropic');
expect(resolveDroidProvider({})).toBe('anthropic');
});
it('defaults ollama.com to anthropic', () => {
expect(resolveDroidProvider({ baseUrl: 'https://ollama.com' })).toBe('anthropic');
expect(resolveDroidProvider({ baseUrl: 'https://ollama.com/v1/messages' })).toBe('anthropic');
});
it('keeps ollama.com anthropic even for generic model families', () => {
expect(
resolveDroidProvider({
baseUrl: 'https://ollama.com',
model: 'qwen3-coder-plus',
})
).toBe('anthropic');
expect(
resolveDroidProvider({
baseUrl: 'https://ollama.com/v1/messages',
model: 'deepseek-v3.1',
})
).toBe('anthropic');
});
it('routes ollama.com /chat/completions to generic', () => {
expect(resolveDroidProvider({ baseUrl: 'https://ollama.com/v1/chat/completions' })).toBe(
'generic-chat-completion-api'
);
});
});
});