Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/bubble-core/src/bubbles/service-bubble/ai-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CredentialType,
BUBBLE_CREDENTIAL_OPTIONS,
RECOMMENDED_MODELS,
decodeCredentialPayload,
} from '@bubblelab/shared-schemas';
import { StateGraph, MessagesAnnotation } from '@langchain/langgraph';
import { ChatOpenAI } from '@langchain/openai';
Expand Down Expand Up @@ -1129,6 +1130,8 @@ export class AIAgentBubble extends ServiceBubble<
return CredentialType.ANTHROPIC_CRED;
case 'openrouter':
return CredentialType.OPENROUTER_CRED;
case 'custom':
return CredentialType.CUSTOM_LLM_CRED;
default:
throw new Error(`Unsupported model provider: ${provider}`);
}
Expand Down Expand Up @@ -1156,6 +1159,8 @@ export class AIAgentBubble extends ServiceBubble<
return credentials[CredentialType.ANTHROPIC_CRED];
case 'openrouter':
return credentials[CredentialType.OPENROUTER_CRED];
case 'custom':
return credentials[CredentialType.CUSTOM_LLM_CRED];
default:
throw new Error(`Unsupported model provider: ${provider}`);
}
Expand Down Expand Up @@ -1362,6 +1367,9 @@ export class AIAgentBubble extends ServiceBubble<
case 'openrouter':
apiKey = credentials[CredentialType.OPENROUTER_CRED];
break;
case 'custom':
apiKey = credentials[CredentialType.CUSTOM_LLM_CRED];
break;
default:
throw new Error(`Unsupported model provider: ${provider}`);
}
Expand Down Expand Up @@ -1392,6 +1400,24 @@ export class AIAgentBubble extends ServiceBubble<
streaming: enableStreaming,
maxRetries: retries,
});
case 'custom': {
const payload = decodeCredentialPayload<{
baseUrl: string;
apiKey: string;
modelName: string;
}>(apiKey);
return new ChatOpenAI({
model: payload.modelName,
temperature,
maxTokens,
apiKey: payload.apiKey,
configuration: {
baseURL: payload.baseUrl,
},
streaming: enableStreaming,
maxRetries: retries,
});
}
case 'google': {
const thinkingConfig = reasoningEffort
? {
Expand Down
2 changes: 2 additions & 0 deletions packages/bubble-shared-schemas/src/ai-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const AvailableModels = z.enum([
'openrouter/openai/gpt-oss-120b',
'openrouter/openai/o3-deep-research',
'openrouter/openai/o4-mini-deep-research',
// Custom provider
'custom/custom',
]);

export type AvailableModel = z.infer<typeof AvailableModels>;
Expand Down
29 changes: 29 additions & 0 deletions packages/bubble-shared-schemas/src/credential-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,33 @@ export const CREDENTIAL_TYPE_CONFIG: Record<CredentialType, CredentialConfig> =
ignoreSSL: false,
},
},
[CredentialType.CUSTOM_LLM_CRED]: {
label: 'Custom LLM Provider',
description: 'Custom OpenAI-Compatible Provider',
placeholder: '',
namePlaceholder: 'My Custom LLM Provider',
credentialConfigurations: {},
fields: [
{
key: 'baseUrl',
label: 'Base URL',
placeholder: 'https://api.groq.com/openai/v1',
type: 'text',
},
{
key: 'apiKey',
label: 'API Key',
placeholder: 'Your API key',
type: 'password',
},
{
key: 'modelName',
label: 'Model Name',
placeholder: 'llama3-70b-8192',
type: 'text',
},
],
},
[CredentialType.CLOUDFLARE_R2_ACCESS_KEY]: {
label: 'Cloudflare R2 Access Key',
description: 'Access key for Cloudflare R2 storage',
Expand Down Expand Up @@ -732,6 +759,7 @@ export const CREDENTIAL_ENV_MAP: Record<CredentialType, string> = {
[CredentialType.TELEGRAM_BOT_TOKEN]: 'TELEGRAM_BOT_TOKEN',
[CredentialType.RESEND_CRED]: 'RESEND_API_KEY',
[CredentialType.OPENROUTER_CRED]: 'OPENROUTER_API_KEY',
[CredentialType.CUSTOM_LLM_CRED]: '', // Multi-field credential
[CredentialType.CLOUDFLARE_R2_ACCESS_KEY]: 'CLOUDFLARE_R2_ACCESS_KEY',
[CredentialType.CLOUDFLARE_R2_SECRET_KEY]: 'CLOUDFLARE_R2_SECRET_KEY',
[CredentialType.CLOUDFLARE_R2_ACCOUNT_ID]: 'CLOUDFLARE_R2_ACCOUNT_ID',
Expand Down Expand Up @@ -792,6 +820,7 @@ export const SYSTEM_CREDENTIALS = new Set<CredentialType>([
CredentialType.ANTHROPIC_CRED,
CredentialType.RESEND_CRED,
CredentialType.OPENROUTER_CRED,
CredentialType.CUSTOM_LLM_CRED,
// Cloudflare R2 Storage credentials
CredentialType.CLOUDFLARE_R2_ACCESS_KEY,
CredentialType.CLOUDFLARE_R2_SECRET_KEY,
Expand Down
2 changes: 2 additions & 0 deletions packages/bubble-shared-schemas/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export enum CredentialType {
GOOGLE_GEMINI_CRED = 'GOOGLE_GEMINI_CRED',
ANTHROPIC_CRED = 'ANTHROPIC_CRED',
OPENROUTER_CRED = 'OPENROUTER_CRED',
// Custom LLM Provider
CUSTOM_LLM_CRED = 'CUSTOM_LLM_CRED',
// Search Credentials
FIRECRAWL_API_KEY = 'FIRECRAWL_API_KEY',
// Database Credentials
Expand Down