llm.hanzo.ai is dead — api.hanzo.ai is the one gateway; harden live HF tests

Migrate the last llm.hanzo.ai references (vscode MCP llm tool provider URL
+ gateway fallback, auth-chat e2e doc comments) to the canonical
https://api.hanzo.ai/v1 (OpenAI-compatible, IAM JWT at the gateway — see
hanzoai/ai). The browser extension chat paths already pointed at
API_BASE_URL = api.hanzo.ai.

model-hub's HuggingFace tests are real live-API integration tests, which
means they fail on HF's weather: repeated runs rate-limit and the 5s
default timeout reads as 6 test failures with nothing of ours broken.
Probe HF once at module load and describe.skipIf with the status logged
when HF itself refuses service; give live calls a 15s budget. A failure
while HF answers 200 is still a real failure. 243/243 across repeated runs.
This commit is contained in:
hanzo-dev
2026-07-02 23:53:04 -07:00
parent c783b89c35
commit 8f8f8b5ae2
3 changed files with 26 additions and 12 deletions
+2 -2
View File
@@ -8,11 +8,11 @@ import { test, expect } from './fixtures';
* 1. Login tab opens correctly
* 2. Password auth succeeds
* 3. Token is stored and user info is fetched
* 4. Chat sends a message to a Zen model via llm.hanzo.ai
* 4. Chat sends a message to a Zen model via api.hanzo.ai
*
* Requirements:
* - IAM at iam.hanzo.ai / hanzo.id must be reachable
* - LLM gateway at llm.hanzo.ai must be reachable
* - LLM gateway at api.hanzo.ai must be reachable
* - Extension must be built: npm run build
*/
+21 -7
View File
@@ -81,9 +81,23 @@ describe('searchOllamaLibrary', () => {
// ---------------------------------------------------------------------------
// HuggingFace API integration tests (real network calls)
//
// These verify OUR client against the live API. When HF itself refuses
// service (rate-limit 429, outage, no network) there is nothing of ours to
// verify — skip with the status logged instead of failing on the weather.
// Any failure while HF answers 200 is still a real failure. Live calls get
// a 15s budget (HF routinely exceeds vitest's 5s default under load).
// ---------------------------------------------------------------------------
describe('HuggingFace API', () => {
const HF_LIVE_TIMEOUT = 15_000;
const hfStatus = await fetch('https://huggingface.co/api/models?limit=1', {
signal: AbortSignal.timeout(5000),
}).then((r) => r.status, () => 0);
if (hfStatus !== 200) {
console.warn(`[model-hub.test] skipping live HF tests — huggingface.co status ${hfStatus || 'unreachable'}`);
}
describe.skipIf(hfStatus !== 200)('HuggingFace API', () => {
it('searchHuggingFace returns models', async () => {
const models = await searchHuggingFace('llama', { limit: 2 });
expect(models.length).toBeGreaterThan(0);
@@ -92,7 +106,7 @@ describe('HuggingFace API', () => {
expect(m.id).toBeTruthy();
expect(m.source).toBe('huggingface');
}
});
}, HF_LIVE_TIMEOUT);
it('searchGGUF returns GGUF-tagged models', async () => {
const models = await searchGGUF('qwen', 3);
@@ -100,12 +114,12 @@ describe('HuggingFace API', () => {
for (const m of models) {
expect(m.tags).toBeInstanceOf(Array);
}
});
}, HF_LIVE_TIMEOUT);
it('searchMLX returns MLX models', async () => {
const models = await searchMLX('llama', 3);
expect(models.length).toBeGreaterThan(0);
});
}, HF_LIVE_TIMEOUT);
it('getHuggingFaceModel returns model with files', async () => {
// Use a well-known public model that doesn't require auth
@@ -124,18 +138,18 @@ describe('HuggingFace API', () => {
).toBe(true);
expect(f.downloadUrl).toContain('huggingface.co');
}
});
}, HF_LIVE_TIMEOUT);
it('getModelCard returns markdown', async () => {
const card = await getModelCard('TheBloke/Llama-2-7B-Chat-GGUF');
expect(typeof card).toBe('string');
// Card may be empty for some models; just verify it's a string
expect(card).toBeDefined();
});
}, HF_LIVE_TIMEOUT);
it('getModelStats returns download count', async () => {
const stats = await getModelStats('TheBloke/Llama-2-7B-Chat-GGUF');
expect(stats.downloads).toBeGreaterThan(0);
expect(stats.tags).toBeInstanceOf(Array);
});
}, HF_LIVE_TIMEOUT);
});
+3 -3
View File
@@ -32,7 +32,7 @@ function detectProviders(): ProviderConfig[] {
const found: ProviderConfig[] = [];
if (process.env.OPENAI_API_KEY) found.push({ name: 'OpenAI', url: 'https://api.openai.com/v1', models: ['gpt-4o', 'gpt-4o-mini', 'o3-mini'] });
if (process.env.ANTHROPIC_API_KEY) found.push({ name: 'Anthropic', url: 'https://api.anthropic.com', models: ['claude-opus-4-6', 'claude-sonnet-4-6'] });
if (process.env.HANZO_API_KEY) found.push({ name: 'Hanzo', url: 'https://llm.hanzo.ai/v1', models: ['zen-400b', 'zen-70b'] });
if (process.env.HANZO_API_KEY) found.push({ name: 'Hanzo', url: 'https://api.hanzo.ai/v1', models: ['zen-400b', 'zen-70b'] });
try { execSync('curl -sf http://localhost:11434/api/tags', { timeout: 2000, stdio: 'pipe' }); found.push({ name: 'Ollama', url: 'http://localhost:11434' }); } catch {}
try { execSync('curl -sf http://localhost:1234/v1/models', { timeout: 2000, stdio: 'pipe' }); found.push({ name: 'LM Studio', url: 'http://localhost:1234/v1' }); } catch {}
return found;
@@ -71,7 +71,7 @@ export function createLLMTools(context: vscode.ExtensionContext): MCPTool[] {
// Route to Hanzo LLM gateway (unified proxy for all providers)
const apiKey = process.env.HANZO_API_KEY || process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
const baseUrl = process.env.HANZO_LLM_URL || 'https://llm.hanzo.ai/v1';
const baseUrl = process.env.HANZO_LLM_URL || 'https://api.hanzo.ai/v1';
try {
const body = JSON.stringify({
@@ -108,7 +108,7 @@ export function createLLMTools(context: vscode.ExtensionContext): MCPTool[] {
return `Need at least 2 providers for consensus. Found: ${providers.map(p => p.name).join(', ') || 'none'}. Configure API keys for multiple providers.`;
}
const models = args.models || providers.slice(0, 3).flatMap(p => p.models?.slice(0, 1) || [p.name]);
return `Consensus query across ${models.length} models: ${models.join(', ')}\n\nTo use consensus, configure the Hanzo LLM gateway at https://llm.hanzo.ai which supports routing to 100+ providers with built-in consensus and fallback.`;
return `Consensus query across ${models.length} models: ${models.join(', ')}\n\nTo use consensus, configure the Hanzo LLM gateway at https://api.hanzo.ai which supports routing to 100+ providers with built-in consensus and fallback.`;
}
},