mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
Merge e65c2f9b30
into 05d7ef0ab5
This commit is contained in:
commit
b0b786abb4
@ -1,5 +1,14 @@
|
||||
# bolt.diy
|
||||
|
||||
> ## 📢 Add A free API from [Pollinations.ai] in the Bolt DIY!
|
||||
> **A free API from [Pollinations.ai](https://pollinations.ai) has been added to the project.**
|
||||
> This API provides access to several models that can be used to enhance and expand the functionalities of the project.
|
||||
>
|
||||
> 🔗 **API Link**: [Pollinations.ai](https://pollinations.ai)
|
||||
> You can now access a variety of models to integrate into your project.
|
||||
>
|
||||
> 💬 *If you have any feedback or questions, feel free to reach out!*
|
||||
|
||||
[](https://bolt.diy)
|
||||
|
||||
Welcome to bolt.diy, the official open source version of Bolt.new, which allows you to choose the LLM that you use for each prompt! Currently, you can use OpenAI, Anthropic, Ollama, OpenRouter, Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, or Groq models - and it is easily extended to use any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models.
|
||||
|
@ -12,7 +12,7 @@ import { SiAmazon, SiGoogle, SiHuggingface, SiPerplexity, SiOpenai } from 'react
|
||||
import { BsRobot, BsCloud } from 'react-icons/bs';
|
||||
import { TbBrain, TbCloudComputing } from 'react-icons/tb';
|
||||
import { BiCodeBlock, BiChip } from 'react-icons/bi';
|
||||
import { FaCloud, FaBrain } from 'react-icons/fa';
|
||||
import { FaCloud, FaBrain, FaRegLemon } from 'react-icons/fa';
|
||||
import type { IconType } from 'react-icons';
|
||||
|
||||
// Add type for provider names to ensure type safety
|
||||
@ -29,6 +29,7 @@ type ProviderName =
|
||||
| 'OpenAI'
|
||||
| 'OpenRouter'
|
||||
| 'Perplexity'
|
||||
| 'Pollinations'
|
||||
| 'Together'
|
||||
| 'XAI';
|
||||
|
||||
@ -46,6 +47,7 @@ const PROVIDER_ICONS: Record<ProviderName, IconType> = {
|
||||
OpenAI: SiOpenai,
|
||||
OpenRouter: FaCloud,
|
||||
Perplexity: SiPerplexity,
|
||||
Pollinations: FaRegLemon,
|
||||
Together: BsCloud,
|
||||
XAI: BsRobot,
|
||||
};
|
||||
|
@ -14,15 +14,17 @@ import { providerBaseUrlEnvKeys } from '~/utils/constants';
|
||||
import { useToast } from '~/components/ui/use-toast';
|
||||
import { Progress } from '~/components/ui/Progress';
|
||||
import OllamaModelInstaller from './OllamaModelInstaller';
|
||||
import { FaRegLemon } from 'react-icons/fa';
|
||||
|
||||
// Add type for provider names to ensure type safety
|
||||
type ProviderName = 'Ollama' | 'LMStudio' | 'OpenAILike';
|
||||
type ProviderName = 'Ollama' | 'LMStudio' | 'OpenAILike' | 'Pollinations';
|
||||
|
||||
// Update the PROVIDER_ICONS type to use the ProviderName type
|
||||
const PROVIDER_ICONS: Record<ProviderName, IconType> = {
|
||||
Ollama: BsRobot,
|
||||
LMStudio: BsRobot,
|
||||
OpenAILike: TbBrandOpenai,
|
||||
Pollinations: FaRegLemon,
|
||||
};
|
||||
|
||||
// Update PROVIDER_DESCRIPTIONS to use the same type
|
||||
@ -30,6 +32,7 @@ const PROVIDER_DESCRIPTIONS: Record<ProviderName, string> = {
|
||||
Ollama: 'Run open-source models locally on your machine',
|
||||
LMStudio: 'Local model inference with LM Studio',
|
||||
OpenAILike: 'Connect to OpenAI-compatible API endpoints',
|
||||
Pollinations: 'Free public AI API with no authentication required',
|
||||
};
|
||||
|
||||
// Add a constant for the Ollama API base URL
|
||||
|
@ -11,6 +11,7 @@ import { HyperbolicStatusChecker } from './providers/hyperbolic';
|
||||
import { MistralStatusChecker } from './providers/mistral';
|
||||
import { OpenRouterStatusChecker } from './providers/openrouter';
|
||||
import { PerplexityStatusChecker } from './providers/perplexity';
|
||||
import { PollinationsStatusChecker } from './providers/pollinations';
|
||||
import { TogetherStatusChecker } from './providers/together';
|
||||
import { XAIStatusChecker } from './providers/xai';
|
||||
|
||||
@ -76,6 +77,12 @@ export class ProviderStatusCheckerFactory {
|
||||
headers: {},
|
||||
testModel: 'pplx-7b-chat',
|
||||
},
|
||||
Pollinations: {
|
||||
statusUrl: 'https://pollinations.ai/',
|
||||
apiUrl: 'https://www.pollinations.ai/api/v1/models',
|
||||
headers: {},
|
||||
testModel: 'haiku',
|
||||
},
|
||||
Together: {
|
||||
statusUrl: 'https://status.together.ai/',
|
||||
apiUrl: 'https://api.together.xyz/v1/models',
|
||||
@ -118,6 +125,8 @@ export class ProviderStatusCheckerFactory {
|
||||
return new OpenRouterStatusChecker(config);
|
||||
case 'Perplexity':
|
||||
return new PerplexityStatusChecker(config);
|
||||
case 'Pollinations':
|
||||
return new PollinationsStatusChecker(config);
|
||||
case 'Together':
|
||||
return new TogetherStatusChecker(config);
|
||||
case 'XAI':
|
||||
|
@ -0,0 +1,31 @@
|
||||
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
|
||||
|
||||
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
|
||||
|
||||
export class PollinationsStatusChecker extends BaseProviderChecker {
|
||||
async checkStatus(): Promise<StatusCheckResult> {
|
||||
try {
|
||||
// Check the main pollinations website
|
||||
const endpointStatus = await this.checkEndpoint(this.config.statusUrl);
|
||||
|
||||
// Check the API endpoint
|
||||
const apiStatus = await this.checkEndpoint(this.config.apiUrl);
|
||||
|
||||
// Determine overall status based on both checks
|
||||
const status = endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded';
|
||||
|
||||
return {
|
||||
status,
|
||||
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
|
||||
incidents: ['Note: Limited status information due to CORS restrictions'],
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error checking Pollinations status:', error);
|
||||
return {
|
||||
status: 'degraded',
|
||||
message: 'Error checking service status',
|
||||
incidents: ['Error occurred while checking service status'],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ export type ProviderName =
|
||||
| 'Mistral'
|
||||
| 'OpenRouter'
|
||||
| 'Perplexity'
|
||||
| 'Pollinations'
|
||||
| 'Together'
|
||||
| 'XAI';
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { SiAmazon, SiGoogle, SiHuggingface, SiPerplexity, SiOpenai } from 'react
|
||||
import { BsRobot, BsCloud } from 'react-icons/bs';
|
||||
import { TbBrain } from 'react-icons/tb';
|
||||
import { BiChip, BiCodeBlock } from 'react-icons/bi';
|
||||
import { FaCloud, FaBrain } from 'react-icons/fa';
|
||||
import { FaCloud, FaBrain, FaRegLemon } from 'react-icons/fa';
|
||||
import type { IconType } from 'react-icons';
|
||||
import { useSettings } from '~/lib/hooks/useSettings';
|
||||
import { useToast } from '~/components/ui/use-toast';
|
||||
@ -25,6 +25,7 @@ type ProviderName =
|
||||
| 'OpenAI'
|
||||
| 'OpenRouter'
|
||||
| 'Perplexity'
|
||||
| 'Pollinations'
|
||||
| 'Together'
|
||||
| 'XAI';
|
||||
|
||||
@ -170,6 +171,12 @@ const PROVIDER_STATUS_URLS: Record<ProviderName, ProviderConfig> = {
|
||||
},
|
||||
testModel: 'deepseek-chat',
|
||||
},
|
||||
Pollinations: {
|
||||
statusUrl: 'https://pollinations.ai/',
|
||||
apiUrl: 'https://text.pollinations.ai/openai/v1/models',
|
||||
headers: {},
|
||||
testModel: 'haiku',
|
||||
},
|
||||
};
|
||||
|
||||
const PROVIDER_ICONS: Record<ProviderName, IconType> = {
|
||||
@ -186,6 +193,7 @@ const PROVIDER_ICONS: Record<ProviderName, IconType> = {
|
||||
Together: BsCloud,
|
||||
XAI: BsRobot,
|
||||
Deepseek: BiCodeBlock,
|
||||
Pollinations: FaRegLemon,
|
||||
};
|
||||
|
||||
const ServiceStatusTab = () => {
|
||||
@ -220,6 +228,7 @@ const ServiceStatusTab = () => {
|
||||
OpenRouter: 'OPEN_ROUTER_API_KEY',
|
||||
XAI: 'XAI_API_KEY',
|
||||
Deepseek: 'DEEPSEEK_API_KEY',
|
||||
Pollinations: 'POLLINATIONS_API_BASE_URL',
|
||||
};
|
||||
|
||||
const envKey = envKeyMap[provider];
|
||||
|
@ -460,7 +460,8 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
||||
/>
|
||||
{(providerList || []).length > 0 &&
|
||||
provider &&
|
||||
(!LOCAL_PROVIDERS.includes(provider.name) || 'OpenAILike') && (
|
||||
(!LOCAL_PROVIDERS.includes(provider.name) || provider.name === 'OpenAILike') &&
|
||||
provider.name !== 'Pollinations' && (
|
||||
<APIKeyManager
|
||||
provider={provider}
|
||||
apiKey={apiKeys[provider.name] || ''}
|
||||
|
219
app/lib/modules/llm/providers/pollinations.ts
Normal file
219
app/lib/modules/llm/providers/pollinations.ts
Normal file
@ -0,0 +1,219 @@
|
||||
import { BaseProvider } from '~/lib/modules/llm/base-provider';
|
||||
import type { ModelInfo } from '~/lib/modules/llm/types';
|
||||
import type { IProviderSetting } from '~/types/model';
|
||||
import type { LanguageModelV1 } from 'ai';
|
||||
import { createOpenAI } from '@ai-sdk/openai';
|
||||
import { createScopedLogger } from '~/utils/logger';
|
||||
|
||||
const logger = createScopedLogger('PollinationsProvider');
|
||||
|
||||
// Define response type for the Pollinations API
|
||||
interface PollinationsModelResponse {
|
||||
data: Array<{
|
||||
id: string;
|
||||
object: string;
|
||||
created: number;
|
||||
owned_by: string;
|
||||
context_length?: number;
|
||||
}>;
|
||||
object: string;
|
||||
}
|
||||
|
||||
export default class PollinationsProvider extends BaseProvider {
|
||||
name = 'Pollinations';
|
||||
getApiKeyLink = 'https://pollinations.ai/';
|
||||
labelForGetApiKey = 'Visit Pollinations.ai';
|
||||
icon = 'i-ph:flower-fill';
|
||||
|
||||
config = {
|
||||
baseUrlKey: 'POLLINATIONS_API_BASE_URL',
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
logger.info('Initializing Pollinations Provider');
|
||||
}
|
||||
|
||||
staticModels: ModelInfo[] = [
|
||||
{
|
||||
name: 'haiku',
|
||||
label: 'Haiku',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 4096,
|
||||
},
|
||||
{
|
||||
name: 'mistral-medium',
|
||||
label: 'Mistral Medium',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 8000,
|
||||
},
|
||||
{
|
||||
name: 'llama3-8b',
|
||||
label: 'Llama 3 8B',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 4096,
|
||||
},
|
||||
{
|
||||
name: 'llama3-70b',
|
||||
label: 'Llama 3 70B',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 8000,
|
||||
},
|
||||
{
|
||||
name: 'phi-3-mini',
|
||||
label: 'Phi-3 Mini',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 4096,
|
||||
},
|
||||
{
|
||||
name: 'phi-3-medium',
|
||||
label: 'Phi-3 Medium',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 4096,
|
||||
},
|
||||
{
|
||||
name: 'gemma-7b',
|
||||
label: 'Gemma 7B',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 4096,
|
||||
},
|
||||
{
|
||||
name: 'mistral-small',
|
||||
label: 'Mistral Small',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 4096,
|
||||
},
|
||||
{
|
||||
name: 'mistral-large',
|
||||
label: 'Mistral Large',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 8192,
|
||||
},
|
||||
{
|
||||
name: 'gpt-4o',
|
||||
label: 'GPT-4o',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 8192,
|
||||
},
|
||||
{
|
||||
name: 'claude-3-haiku',
|
||||
label: 'Claude 3 Haiku',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 4096,
|
||||
},
|
||||
{
|
||||
name: 'claude-3-sonnet',
|
||||
label: 'Claude 3 Sonnet',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 8192,
|
||||
},
|
||||
{
|
||||
name: 'claude-3-opus',
|
||||
label: 'Claude 3 Opus',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 32768,
|
||||
},
|
||||
{
|
||||
name: 'deepseek-coder',
|
||||
label: 'DeepSeek Coder',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 8192,
|
||||
},
|
||||
{
|
||||
name: 'qwen-72b',
|
||||
label: 'Qwen 72B',
|
||||
provider: 'Pollinations',
|
||||
maxTokenAllowed: 8192,
|
||||
},
|
||||
];
|
||||
|
||||
// You can also add a dynamic models function if needed
|
||||
async getDynamicModels(
|
||||
apiKeys?: Record<string, string>,
|
||||
settings?: IProviderSetting,
|
||||
serverEnv: Record<string, string> = {},
|
||||
): Promise<ModelInfo[]> {
|
||||
logger.info('Getting dynamic models for Pollinations');
|
||||
|
||||
let { baseUrl } = this.getProviderBaseUrlAndKey({
|
||||
apiKeys,
|
||||
providerSettings: settings,
|
||||
serverEnv,
|
||||
defaultBaseUrlKey: 'POLLINATIONS_API_BASE_URL',
|
||||
defaultApiTokenKey: '',
|
||||
});
|
||||
|
||||
// Default base URL if not provided
|
||||
baseUrl = baseUrl || 'https://text.pollinations.ai/openai';
|
||||
|
||||
try {
|
||||
// Fetch models from Pollinations API
|
||||
const response = await fetch(`${baseUrl}/v1/models`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
logger.warn(`Failed to fetch models from Pollinations API: ${response.status}`);
|
||||
return this.staticModels;
|
||||
}
|
||||
|
||||
const data = (await response.json()) as PollinationsModelResponse;
|
||||
|
||||
if (data && Array.isArray(data.data)) {
|
||||
const models = data.data.map((model) => ({
|
||||
name: model.id,
|
||||
label: model.id.split('/').pop() || model.id,
|
||||
provider: this.name,
|
||||
maxTokenAllowed: model.context_length || 4096,
|
||||
}));
|
||||
|
||||
logger.info(`Retrieved ${models.length} models from Pollinations API`);
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
logger.warn('Invalid response format from Pollinations API, falling back to static models');
|
||||
|
||||
return this.staticModels;
|
||||
} catch (error) {
|
||||
logger.error('Error getting dynamic models for Pollinations:', error);
|
||||
return this.staticModels;
|
||||
}
|
||||
}
|
||||
|
||||
getModelInstance(options: {
|
||||
model: string;
|
||||
serverEnv?: Env;
|
||||
apiKeys?: Record<string, string>;
|
||||
providerSettings?: Record<string, IProviderSetting>;
|
||||
}): LanguageModelV1 {
|
||||
const { model, serverEnv, providerSettings } = options;
|
||||
|
||||
let { baseUrl } = this.getProviderBaseUrlAndKey({
|
||||
providerSettings: providerSettings?.[this.name],
|
||||
serverEnv: serverEnv as any,
|
||||
defaultBaseUrlKey: 'POLLINATIONS_API_BASE_URL',
|
||||
defaultApiTokenKey: '',
|
||||
});
|
||||
|
||||
// Default base URL if not provided
|
||||
baseUrl = baseUrl || 'https://text.pollinations.ai/openai';
|
||||
|
||||
logger.info(`Creating OpenAI instance for Pollinations with model: ${model} at ${baseUrl}`);
|
||||
|
||||
try {
|
||||
const openai = createOpenAI({
|
||||
baseURL: baseUrl,
|
||||
apiKey: 'not-needed', // No real API key required for Pollinations.ai
|
||||
});
|
||||
|
||||
return openai(model);
|
||||
} catch (error) {
|
||||
logger.error(`Error creating Pollinations model instance: ${error}`);
|
||||
throw new Error(`Failed to initialize Pollinations provider: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import OpenRouterProvider from './providers/open-router';
|
||||
import OpenAILikeProvider from './providers/openai-like';
|
||||
import OpenAIProvider from './providers/openai';
|
||||
import PerplexityProvider from './providers/perplexity';
|
||||
import PollinationsProvider from './providers/pollinations';
|
||||
import TogetherProvider from './providers/together';
|
||||
import XAIProvider from './providers/xai';
|
||||
import HyperbolicProvider from './providers/hyperbolic';
|
||||
@ -31,6 +32,7 @@ export {
|
||||
OpenRouterProvider,
|
||||
OpenAILikeProvider,
|
||||
PerplexityProvider,
|
||||
PollinationsProvider,
|
||||
XAIProvider,
|
||||
TogetherProvider,
|
||||
LMStudioProvider,
|
||||
|
@ -29,8 +29,8 @@ export interface Shortcuts {
|
||||
toggleTerminal: Shortcut;
|
||||
}
|
||||
|
||||
export const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike'];
|
||||
export const LOCAL_PROVIDERS = ['OpenAILike', 'LMStudio', 'Ollama'];
|
||||
export const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike', 'Pollinations'];
|
||||
export const LOCAL_PROVIDERS = ['OpenAILike', 'LMStudio', 'Ollama', 'Pollinations'];
|
||||
|
||||
export type ProviderSetting = Record<string, IProviderConfig>;
|
||||
|
||||
@ -71,8 +71,8 @@ const getInitialProviderSettings = (): ProviderSetting => {
|
||||
initialSettings[provider.name] = {
|
||||
...provider,
|
||||
settings: {
|
||||
// Local providers should be disabled by default
|
||||
enabled: !LOCAL_PROVIDERS.includes(provider.name),
|
||||
// Local providers should be disabled by default, except Pollinations
|
||||
enabled: !LOCAL_PROVIDERS.includes(provider.name) || provider.name === 'Pollinations',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user