spb/zyquo-cloud-web Public MIT
Zyquo Cloud Web — every cloud model, one beautiful chat, entirely in your browser.
TypeScript 81.9%
CSS 8.9%
JavaScript 7.5%
Shell 1.1%
HTML 0.6%
1/*2 * registry.ts3 * Zyquo Cloud Web4 *5 * Author: Simon-Pierre Boucher6 * Mail: contact@spboucher.ai7 *8 * Provider metadata + client resolution, ported from ProviderID.swift and9 * ProviderRegistry.swift. The only place that knows which wire format each10 * provider speaks and where its API lives.11 */1213import type { AIModel, Provider, WireFormat } from '../types'1415export interface ProviderMeta {16 displayName: string17 wireFormat: WireFormat18 /** Base URL of the provider's API (chat + models live under this root). */19 defaultBaseURL: string | null20 /** Whether the provider exposes a usable `/models` listing endpoint. */21 supportsModelListing: boolean22 /**23 * Providers whose final streamed chunk carries usage only when asked via24 * stream_options. Mistral rejects unknown params; Qwen/DeepInfra/Perplexity25 * include usage automatically.26 */27 wantsStreamOptions: boolean28 /** CORS verdict from docs/CORS-MATRIX.md (all 12 work; Anthropic needs a header). */29 corsDirect: boolean30}3132export const PROVIDER_META: Record<Provider, ProviderMeta> = {33 openai: {34 displayName: 'OpenAI',35 wireFormat: 'openAIChatCompletions',36 defaultBaseURL: 'https://api.openai.com/v1',37 supportsModelListing: true,38 wantsStreamOptions: true,39 corsDirect: true,40 },41 anthropic: {42 displayName: 'Anthropic',43 wireFormat: 'anthropicMessages',44 defaultBaseURL: 'https://api.anthropic.com/v1',45 supportsModelListing: true,46 wantsStreamOptions: false,47 corsDirect: true,48 },49 xai: {50 displayName: 'xAI',51 wireFormat: 'openAIChatCompletions',52 defaultBaseURL: 'https://api.x.ai/v1',53 supportsModelListing: true,54 wantsStreamOptions: true,55 corsDirect: true,56 },57 mistral: {58 displayName: 'Mistral',59 wireFormat: 'openAIChatCompletions',60 defaultBaseURL: 'https://api.mistral.ai/v1',61 supportsModelListing: true,62 wantsStreamOptions: false,63 corsDirect: true,64 },65 gemini: {66 displayName: 'Google Gemini',67 wireFormat: 'openAIChatCompletions',68 defaultBaseURL: 'https://generativelanguage.googleapis.com/v1beta/openai',69 supportsModelListing: true,70 wantsStreamOptions: true,71 corsDirect: true,72 },73 qwen: {74 displayName: 'Alibaba Qwen',75 wireFormat: 'openAIChatCompletions',76 defaultBaseURL: 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',77 supportsModelListing: true,78 wantsStreamOptions: false,79 corsDirect: true,80 },81 deepseek: {82 displayName: 'DeepSeek',83 wireFormat: 'openAIChatCompletions',84 defaultBaseURL: 'https://api.deepseek.com',85 supportsModelListing: true,86 wantsStreamOptions: true,87 corsDirect: true,88 },89 kimi: {90 displayName: 'Kimi',91 wireFormat: 'openAIChatCompletions',92 defaultBaseURL: 'https://api.moonshot.ai/v1',93 supportsModelListing: true,94 wantsStreamOptions: true,95 corsDirect: true,96 },97 perplexity: {98 displayName: 'Perplexity',99 wireFormat: 'openAIChatCompletions',100 defaultBaseURL: 'https://api.perplexity.ai',101 supportsModelListing: false,102 wantsStreamOptions: false,103 corsDirect: true,104 },105 together: {106 displayName: 'Together AI',107 wireFormat: 'openAIChatCompletions',108 defaultBaseURL: 'https://api.together.xyz/v1',109 supportsModelListing: true,110 wantsStreamOptions: true,111 corsDirect: true,112 },113 deepinfra: {114 displayName: 'DeepInfra',115 wireFormat: 'openAIChatCompletions',116 defaultBaseURL: 'https://api.deepinfra.com/v1/openai',117 supportsModelListing: true,118 wantsStreamOptions: false,119 corsDirect: true,120 },121 cerebras: {122 displayName: 'Cerebras',123 wireFormat: 'openAIChatCompletions',124 defaultBaseURL: 'https://api.cerebras.ai/v1',125 supportsModelListing: true,126 wantsStreamOptions: true,127 corsDirect: true,128 },129 custom: {130 displayName: 'Custom',131 wireFormat: 'openAIChatCompletions',132 defaultBaseURL: null,133 supportsModelListing: true,134 wantsStreamOptions: true,135 corsDirect: true,136 },137}138139import type { ProviderClient } from './types'140import { OpenAICompatibleClient } from './openaiCompatible'141import { AnthropicClient } from './anthropic'142143/** Resolves the right client for a provider or custom model. */144export function clientFor(providerOrModel: Provider | AIModel): ProviderClient {145 const provider =146 typeof providerOrModel === 'string' ? providerOrModel : providerOrModel.provider147 if (PROVIDER_META[provider].wireFormat === 'anthropicMessages') {148 return new AnthropicClient()149 }150 return new OpenAICompatibleClient(provider)151}152