- Add PerplexityProvider (OpenAI-compatible, reads PERPLEXITY_API_KEY) - Add GeminiProvider (Google Generative Language API adapter, reads GEMINI_API_KEY) - Add createFallbackChain() — ordered provider chain, skips unconfigured, aggregates errors; allows any app to replace custom LLM fallback loops - Extend LLMProviderType with 'perplexity' | 'gemini' - Update factory to resolve and instantiate new provider types - Add PAID_PROVIDERS to llm-router registry (OpenAI, Perplexity) for apps using round-robin routing alongside free-tier providers - 27 tests covering fallback chain, new providers, error/edge cases Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
/**
|
|
* Perplexity LLM provider.
|
|
*
|
|
* Uses Perplexity's OpenAI-compatible API with real-time web search.
|
|
* Reads config from PERPLEXITY_API_KEY, PERPLEXITY_MODEL.
|
|
*/
|
|
|
|
import type { ChatCompletionRequest, ChatCompletionResponse, LLMProvider } from '../types.js';
|
|
|
|
export interface PerplexityConfig {
|
|
apiKey: string;
|
|
model?: string;
|
|
}
|
|
|
|
export class PerplexityProvider implements LLMProvider {
|
|
private config: PerplexityConfig;
|
|
|
|
constructor(config?: Partial<PerplexityConfig>) {
|
|
this.config = {
|
|
apiKey: config?.apiKey || process.env.PERPLEXITY_API_KEY || '',
|
|
model: config?.model || process.env.PERPLEXITY_MODEL || 'sonar',
|
|
};
|
|
}
|
|
|
|
isConfigured(): boolean {
|
|
return Boolean(this.config.apiKey);
|
|
}
|
|
|
|
async chatCompletion(req: ChatCompletionRequest): Promise<ChatCompletionResponse> {
|
|
if (!this.isConfigured()) {
|
|
throw new Error('Perplexity is not configured (missing PERPLEXITY_API_KEY)');
|
|
}
|
|
|
|
const body = {
|
|
model: req.model || this.config.model,
|
|
messages: req.messages,
|
|
temperature: req.temperature,
|
|
max_tokens: req.maxTokens,
|
|
top_p: req.topP,
|
|
};
|
|
|
|
const response = await fetch('https://api.perplexity.ai/chat/completions', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text();
|
|
throw new Error(`Perplexity error ${response.status}: ${text}`);
|
|
}
|
|
|
|
const data = (await response.json()) as {
|
|
choices: Array<{ message: { content: string }; finish_reason: string }>;
|
|
model: string;
|
|
usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number };
|
|
};
|
|
|
|
return {
|
|
content: data.choices[0]?.message?.content ?? '',
|
|
model: data.model,
|
|
finishReason:
|
|
(data.choices[0]?.finish_reason as ChatCompletionResponse['finishReason']) ?? null,
|
|
usage: {
|
|
promptTokens: data.usage?.prompt_tokens ?? 0,
|
|
completionTokens: data.usage?.completion_tokens ?? 0,
|
|
totalTokens: data.usage?.total_tokens ?? 0,
|
|
},
|
|
};
|
|
}
|
|
}
|