feat: Replace Void branding with Orcide, integrate OllamaFreeAPI as default provider

- Replace Void cube logo with Orcide SVG logo on welcome page and watermark
- Remove API key requirement from orcestAI provider (uses OllamaFreeAPI)
- Set orcestAI endpoint to https://ollamafreeapi.orcest.ai/v1 (free, no auth)
- Add orcestAI to "Free" tab as first option in onboarding
- Default to orcestAI for smart/cheap/all provider selections
- Update default models to ollamafreeapi models (llama3, mistral, deepseek, etc.)
- Update provider display name and descriptions for Orcest ecosystem

https://claude.ai/code/session_01CjDqzV3ECQxE1g4jFn7PBu
This commit is contained in:
Claude 2026-02-21 12:20:32 +00:00
parent 331449034e
commit dddf47893e
No known key found for this signature in database
6 changed files with 84 additions and 89 deletions

View file

@ -55,7 +55,7 @@
width: 100%;
max-height: 100%;
aspect-ratio: 1/1;
background-image: url('./void_cube_noshadow.png'); /* // Void */
background-image: url('./orcide_logo.svg'); /* Orcide */
background-size: contain;
background-position-x: center;
background-repeat: no-repeat;
@ -63,17 +63,17 @@
.void-void-icon,
.monaco-workbench.vs-dark .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
background-image: url('./void_cube_noshadow.png'); /* // Void */
background-image: url('./orcide_logo.svg'); /* Orcide */
}
.void-void-icon,
.monaco-workbench.hc-light .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
background-image: url('./void_cube_noshadow.png'); /* // Void */
background-image: url('./orcide_logo.svg'); /* Orcide */
}
.void-void-icon,
.monaco-workbench.hc-black .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
background-image: url('./void_cube_noshadow.png'); /* // Void */
background-image: url('./orcide_logo.svg'); /* Orcide */
}
.monaco-workbench .part.editor > .content:not(.empty) .editor-group-container > .editor-group-watermark > .shortcuts,

View file

@ -0,0 +1,19 @@
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Outer hexagonal frame -->
<path d="M100 10 L180 50 L180 140 L100 180 L20 140 L20 50 Z" stroke="#888888" stroke-width="2" fill="none" opacity="0.25"/>
<!-- Inner hexagonal frame -->
<path d="M100 30 L165 60 L165 130 L100 160 L35 130 L35 60 Z" stroke="#888888" stroke-width="1.5" fill="none" opacity="0.15"/>
<!-- Central O circle -->
<circle cx="100" cy="95" r="36" stroke="#60a5fa" stroke-width="2.5" fill="none"/>
<circle cx="100" cy="95" r="26" stroke="#60a5fa" stroke-width="1.2" fill="none" opacity="0.4"/>
<!-- Connection nodes -->
<circle cx="100" cy="59" r="2.5" fill="#60a5fa"/>
<circle cx="136" cy="95" r="2.5" fill="#60a5fa"/>
<circle cx="100" cy="131" r="2.5" fill="#60a5fa"/>
<circle cx="64" cy="95" r="2.5" fill="#60a5fa"/>
<!-- Connecting lines to hexagon -->
<line x1="100" y1="59" x2="100" y2="30" stroke="#60a5fa" stroke-width="0.8" opacity="0.35"/>
<line x1="136" y1="95" x2="165" y2="95" stroke="#60a5fa" stroke-width="0.8" opacity="0.35"/>
<line x1="100" y1="131" x2="100" y2="160" stroke="#60a5fa" stroke-width="0.8" opacity="0.35"/>
<line x1="64" y1="95" x2="35" y2="95" stroke="#60a5fa" stroke-width="0.8" opacity="0.35"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -3,15 +3,13 @@
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import { useAccessor, useIsDark, useSettingsState } from '../util/services.js';
import { Brain, Check, ChevronRight, DollarSign, ExternalLink, Lock, X } from 'lucide-react';
import { displayInfoOfProviderName, ProviderName, providerNames, localProviderNames, featureNames, FeatureName, isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js';
import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js';
import { OllamaSetupInstructions, OneClickSwitchButton, SettingsForProvider, ModelDump } from '../void-settings-tsx/Settings.js';
import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js';
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js';
import { isLinux } from '../../../../../../../base/common/platform.js';
const OVERRIDE_VALUE = false
@ -39,29 +37,32 @@ export const VoidOnboarding = () => {
)
}
const VoidIcon = () => {
const accessor = useAccessor()
const themeService = accessor.get('IThemeService')
const OrcideIcon = () => {
const isDark = useIsDark()
const divRef = useRef<HTMLDivElement | null>(null)
useEffect(() => {
// void icon style
const updateTheme = () => {
const theme = themeService.getColorTheme().type
const isDark = theme === ColorScheme.DARK || theme === ColorScheme.HIGH_CONTRAST_DARK
if (divRef.current) {
divRef.current.style.maxWidth = '220px'
divRef.current.style.opacity = '50%'
divRef.current.style.filter = isDark ? '' : 'invert(1)' //brightness(.5)
}
}
updateTheme()
const d = themeService.onDidColorThemeChange(updateTheme)
return () => d.dispose()
}, [])
return <div ref={divRef} className='@@void-void-icon' />
return (
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ opacity: 0.7 }}>
{/* Outer hexagonal frame */}
<path d="M110 10 L195 55 L195 145 L110 190 L25 145 L25 55 Z" stroke={isDark ? '#ffffff' : '#1a1a2e'} strokeWidth="2" fill="none" opacity="0.3" />
{/* Inner hexagonal frame */}
<path d="M110 35 L175 68 L175 132 L110 165 L45 132 L45 68 Z" stroke={isDark ? '#ffffff' : '#1a1a2e'} strokeWidth="1.5" fill="none" opacity="0.2" />
{/* Central "O" letterform with circuit-like details */}
<circle cx="110" cy="100" r="38" stroke={isDark ? '#60a5fa' : '#2563eb'} strokeWidth="3" fill="none" />
<circle cx="110" cy="100" r="28" stroke={isDark ? '#60a5fa' : '#2563eb'} strokeWidth="1.5" fill="none" opacity="0.5" />
{/* Connection nodes */}
<circle cx="110" cy="62" r="3" fill={isDark ? '#60a5fa' : '#2563eb'} />
<circle cx="148" cy="100" r="3" fill={isDark ? '#60a5fa' : '#2563eb'} />
<circle cx="110" cy="138" r="3" fill={isDark ? '#60a5fa' : '#2563eb'} />
<circle cx="72" cy="100" r="3" fill={isDark ? '#60a5fa' : '#2563eb'} />
{/* Connecting lines to hexagon */}
<line x1="110" y1="62" x2="110" y2="35" stroke={isDark ? '#60a5fa' : '#2563eb'} strokeWidth="1" opacity="0.4" />
<line x1="148" y1="100" x2="175" y2="100" stroke={isDark ? '#60a5fa' : '#2563eb'} strokeWidth="1" opacity="0.4" />
<line x1="110" y1="138" x2="110" y2="165" stroke={isDark ? '#60a5fa' : '#2563eb'} strokeWidth="1" opacity="0.4" />
<line x1="72" y1="100" x2="45" y2="100" stroke={isDark ? '#60a5fa' : '#2563eb'} strokeWidth="1" opacity="0.4" />
{/* Brand text */}
<text x="110" y="200" textAnchor="middle" fill={isDark ? '#ffffff' : '#1a1a2e'} fontSize="18" fontWeight="300" fontFamily="system-ui, -apple-system, sans-serif" letterSpacing="4" opacity="0.6">ORCIDE</text>
</svg>
)
}
const FADE_DURATION_MS = 2000
@ -104,14 +105,14 @@ const cloudProviders: ProviderName[] = ['googleVertex', 'liteLLM', 'microsoftAzu
// Data structures for provider tabs
const providerNamesOfTab: Record<TabName, ProviderName[]> = {
Free: ['gemini', 'openRouter'],
Free: ['orcestAI', 'gemini', 'openRouter'],
Local: localProviderNames,
Paid: providerNames.filter(pn => !(['gemini', 'openRouter', ...localProviderNames, ...cloudProviders] as string[]).includes(pn)) as ProviderName[],
Paid: providerNames.filter(pn => !(['orcestAI', 'gemini', 'openRouter', ...localProviderNames, ...cloudProviders] as string[]).includes(pn)) as ProviderName[],
'Cloud/Other': cloudProviders,
};
const descriptionOfTab: Record<TabName, string> = {
Free: `Providers with a 100% free tier. Add as many as you'd like!`,
Free: `Free providers — Orcest AI works out of the box with no API key!`,
Paid: `Connect directly with any provider (bring your own key).`,
Local: `Active providers should appear automatically. Add as many as you'd like! `,
'Cloud/Other': `Add as many as you'd like! Reach out for custom configuration requests.`,
@ -204,6 +205,14 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
<div key={providerName} className="w-full max-w-xl mb-10">
<div className="text-xl mb-2">
Add {displayInfoOfProviderName(providerName).title}
{providerName === 'orcestAI' && (
<span
data-tooltip-id="void-tooltip-provider-info"
data-tooltip-content="Orcest AI provides 650+ free models via OllamaFreeAPI. No API key required — just works out of the box."
data-tooltip-place="right"
className="ml-1 text-xs align-top text-blue-400"
>*</span>
)}
{providerName === 'gemini' && (
<span
data-tooltip-id="void-tooltip-provider-info"
@ -484,10 +493,10 @@ const VoidOnboardingContent = () => {
// Replace the single selectedProviderName with four separate states
// page 2 state - each tab gets its own state
const [selectedIntelligentProvider, setSelectedIntelligentProvider] = useState<ProviderName>('anthropic');
const [selectedIntelligentProvider, setSelectedIntelligentProvider] = useState<ProviderName>('orcestAI');
const [selectedPrivateProvider, setSelectedPrivateProvider] = useState<ProviderName>('ollama');
const [selectedAffordableProvider, setSelectedAffordableProvider] = useState<ProviderName>('gemini');
const [selectedAllProvider, setSelectedAllProvider] = useState<ProviderName>('anthropic');
const [selectedAffordableProvider, setSelectedAffordableProvider] = useState<ProviderName>('orcestAI');
const [selectedAllProvider, setSelectedAllProvider] = useState<ProviderName>('orcestAI');
// Helper function to get the current selected provider based on active tab
const getSelectedProvider = (): ProviderName => {
@ -510,9 +519,9 @@ const VoidOnboardingContent = () => {
}
const providerNamesOfWantToUseOption: { [wantToUseOption in WantToUseOption]: ProviderName[] } = {
smart: ['anthropic', 'openAI', 'gemini', 'openRouter'],
smart: ['orcestAI', 'anthropic', 'openAI', 'gemini', 'openRouter'],
private: ['ollama', 'vLLM', 'openAICompatible', 'lmStudio'],
cheap: ['gemini', 'deepseek', 'openRouter', 'ollama', 'vLLM'],
cheap: ['orcestAI', 'gemini', 'deepseek', 'openRouter', 'ollama', 'vLLM'],
all: providerNames,
}
@ -598,9 +607,9 @@ const VoidOnboardingContent = () => {
<div className='flex flex-col items-center gap-8'>
<div className="text-5xl font-light text-center">Welcome to Orcide</div>
{/* Slice of Void image */}
{/* Orcide logo */}
<div className='max-w-md w-full h-[30vh] mx-auto flex items-center justify-center'>
{!isLinux && <VoidIcon />}
<OrcideIcon />
</div>

View file

@ -66,8 +66,7 @@ export const defaultProviderSettings = {
endpoint: '', // optionally allow overriding default
},
orcestAI: {
endpoint: 'https://rm.orcest.ai/v1',
apiKey: '', // Will be populated from environment/SSO
endpoint: 'https://ollamafreeapi.orcest.ai/v1',
},
} as const
@ -149,15 +148,13 @@ export const defaultModelsOfProvider = {
awsBedrock: [],
liteLLM: [],
orcestAI: [
'rainymodel-pro',
'rainymodel-standard',
'rainymodel-lite',
'gpt-4o',
'gpt-4o-mini',
'claude-3.5-sonnet',
'gemini-1.5-pro',
'llama-3.1-70b',
'mixtral-8x7b',
'llama3.2:latest',
'llama3.1:latest',
'llama3.3:latest',
'mistral:latest',
'deepseek-r1:latest',
'qwen2.5-coder:7b',
'gemma:latest',
],
@ -1455,48 +1452,19 @@ const openRouterSettings: VoidStaticProviderInfo = {
// ---------------- ORCEST AI (RainyModel) ----------------
// ---------------- ORCEST AI (OllamaFreeAPI) ----------------
const orcestAIModelOptions = {
'rainymodel-pro': {
contextWindow: 128_000,
reservedOutputTokenSpace: 8_192,
cost: { input: 0, output: 0 },
downloadable: false,
supportsFIM: false,
supportsSystemMessage: 'system-role',
specialToolFormat: 'openai-style',
reasoningCapabilities: false,
},
'rainymodel-standard': {
contextWindow: 128_000,
reservedOutputTokenSpace: 8_192,
cost: { input: 0, output: 0 },
downloadable: false,
supportsFIM: false,
supportsSystemMessage: 'system-role',
specialToolFormat: 'openai-style',
reasoningCapabilities: false,
},
'rainymodel-lite': {
contextWindow: 64_000,
reservedOutputTokenSpace: 4_096,
cost: { input: 0, output: 0 },
downloadable: false,
supportsFIM: false,
supportsSystemMessage: 'system-role',
specialToolFormat: 'openai-style',
reasoningCapabilities: false,
},
} as const satisfies { [s: string]: VoidStaticModelInfo }
const orcestAISettings: VoidStaticProviderInfo = {
modelOptions: orcestAIModelOptions,
modelOptionsFallback: (modelName) => {
// For non-RainyModel models served through the aggregator, use the extensive fallback
return extensiveModelOptionsFallback(modelName)
// OllamaFreeAPI serves 650+ open-source models, use extensive fallback for capability detection
return extensiveModelOptionsFallback(modelName, { cost: { input: 0, output: 0 } })
},
providerReasoningIOSettings: {
input: { includeInPayload: openAICompatIncludeInPayloadReasoning },
output: { needsManualParse: true },
},
}

View file

@ -206,7 +206,7 @@ if (!isWeb) {
groq: 'https://api.groq.com/openai/v1',
xAI: 'https://api.x.ai/v1',
mistral: 'https://api.mistral.ai/v1',
orcestAI: 'https://rm.orcest.ai/v1',
orcestAI: 'https://ollamafreeapi.orcest.ai/v1',
};
class LLMMessageServiceWeb extends Disposable implements ILLMMessageService {
@ -220,7 +220,7 @@ if (!isWeb) {
sendLLMMessage = (params: ServiceSendLLMMessageParams): string | null => {
const { onText, onFinalMessage, onError, modelSelection } = params;
if (modelSelection === null) {
onError({ message: `Please add a provider in Void's Settings.`, fullError: null });
onError({ message: `Please add a provider in Orcide's Settings.`, fullError: null });
return null;
}
const requestId = generateUuid();

View file

@ -107,7 +107,7 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn
return { title: 'AWS Bedrock', }
}
else if (providerName === 'orcestAI') {
return { title: 'Orcest AI (RainyModel)', }
return { title: 'Orcest AI (Free)', }
}
throw new Error(`descOfProviderName: Unknown provider name: "${providerName}"`)
@ -131,7 +131,7 @@ export const subTextMdOfProviderName = (providerName: ProviderName): string => {
if (providerName === 'vLLM') return 'Read more about custom [Endpoints here](https://docs.vllm.ai/en/latest/getting_started/quickstart.html#openai-compatible-server).'
if (providerName === 'lmStudio') return 'Read more about custom [Endpoints here](https://lmstudio.ai/docs/app/api/endpoints/openai).'
if (providerName === 'liteLLM') return 'Read more about endpoints [here](https://docs.litellm.ai/docs/providers/openai_compatible).'
if (providerName === 'orcestAI') return 'Orcest AI integrated API. Models are available automatically through your SSO login. Powered by [RainyModel](https://rm.orcest.ai).'
if (providerName === 'orcestAI') return 'Orcest AI free API. No API key required. Powered by [OllamaFreeAPI](https://ollamafreeapi.orcest.ai) with 650+ open-source models.'
throw new Error(`subTextMdOfProviderName: Unknown provider name: "${providerName}"`)
}
@ -160,7 +160,6 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
providerName === 'googleVertex' ? 'AIzaSy...' :
providerName === 'microsoftAzure' ? 'key-...' :
providerName === 'awsBedrock' ? 'key-...' :
providerName === 'orcestAI' ? 'sk-orcest-key...' :
'',
isPasswordField: true,
@ -186,7 +185,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
: providerName === 'liteLLM' ? 'http://localhost:4000'
: providerName === 'awsBedrock' ? 'http://localhost:4000/v1'
: providerName === 'orcestAI' ? defaultProviderSettings.orcestAI.endpoint
: '(never)',
: '(never)'
}