Compare commits

...
Author SHA1 Message Date
aakashb95 d421da3537 remove 4o and 4o mini 2025-12-17 17:48:42 +05:30
b31b220885 Update lib/llm/config_service.rb
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2025-12-17 17:26:08 +05:30
5f689518af Update lib/llm/config_service.rb
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2025-12-17 17:25:55 +05:30
c630a609aa Update lib/llm/config_service.rb
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2025-12-17 17:25:46 +05:30
aakashb95 5d1713485c fix rubocop violation 2025-12-17 15:33:12 +05:30
aakashb95 1dde9329dd remove error handling if yaml load fails 2025-12-17 15:19:43 +05:30
aakashb95 bf76fd3769 add spec 2025-12-17 15:19:31 +05:30
aakashb95 a9a113bace add config routes 2025-12-17 15:19:24 +05:30
aakashb95 65e3f8231f captain preference in account settings 2025-12-17 15:18:26 +05:30
aakashb95 f2d3a6ee4f load llm.yml 2025-12-17 14:48:46 +05:30
aakashb95 b67dbf3761 add gpt-5-nano 2025-12-15 17:14:14 +05:30
aakashb95 a49521c9d6 add model config for global captain settings 2025-12-15 17:09:28 +05:30
14 changed files with 478 additions and 183 deletions
@@ -0,0 +1,11 @@
class Api::V1::Accounts::Captain::ConfigController < Api::V1::Accounts::BaseController
before_action :current_account
def show
render json: {
providers: Llm::ConfigService.providers,
models: Llm::ConfigService.models,
features: Llm::ConfigService.all_features_config
}
end
end
@@ -92,7 +92,10 @@ class Api::V1::AccountsController < Api::BaseController
end
def settings_params
params.permit(:auto_resolve_after, :auto_resolve_message, :auto_resolve_ignore_waiting, :audio_transcriptions, :auto_resolve_label)
permitted = params.permit(:auto_resolve_after, :auto_resolve_message, :auto_resolve_ignore_waiting, :audio_transcriptions, :auto_resolve_label)
permitted[:captain_models] = params[:captain_models].to_unsafe_h if params[:captain_models].present?
permitted[:captain_features] = params[:captain_features].to_unsafe_h if params[:captain_features].present?
permitted
end
def check_signup_enabled
+40
View File
@@ -53,9 +53,11 @@ class Account < ApplicationRecord
validates_with JsonSchemaValidator,
schema: SETTINGS_PARAMS_SCHEMA,
attribute_resolver: ->(record) { record.settings }
validate :validate_captain_models
store_accessor :settings, :auto_resolve_after, :auto_resolve_message, :auto_resolve_ignore_waiting
store_accessor :settings, :audio_transcriptions, :auto_resolve_label
store_accessor :settings, :captain_models, :captain_features
has_many :account_users, dependent: :destroy_async
has_many :agent_bot_inboxes, dependent: :destroy_async
@@ -159,8 +161,46 @@ class Account < ApplicationRecord
ISO_639.find(account_locale)&.english_name&.downcase || 'english'
end
def captain_preferences
{
models: captain_models_with_defaults,
features: captain_features_with_defaults
}.with_indifferent_access
end
private
def captain_models_with_defaults
stored_models = captain_models || {}
Llm::ConfigService.feature_keys.each_with_object({}) do |feature_key, result|
stored_value = stored_models[feature_key]
result[feature_key] = if stored_value.present? && Llm::ConfigService.valid_model_for_feature?(feature_key, stored_value)
stored_value
else
Llm::ConfigService.default_model_for_feature(feature_key)
end
end
end
def captain_features_with_defaults
stored_features = captain_features || {}
Llm::ConfigService.feature_keys.index_with do |feature_key|
stored_features[feature_key] == true
end
end
def validate_captain_models
return if captain_models.blank?
captain_models.each do |feature_key, model_name|
next if model_name.blank?
next if Llm::ConfigService.valid_model_for_feature?(feature_key, model_name)
allowed_models = Llm::ConfigService.models_for_feature(feature_key)
errors.add(:captain_models, "'#{model_name}' is not a valid model for #{feature_key}. Allowed: #{allowed_models.join(', ')}")
end
end
def notify_creation
Rails.configuration.dispatcher.dispatch(ACCOUNT_CREATED, Time.zone.now, account: self)
end
@@ -25,3 +25,4 @@ json.name @account.name
json.support_email @account.support_email
json.status @account.status
json.cache_keys @account.cache_keys
json.captain_preferences @account.captain_preferences
+177 -177
View File
@@ -15,58 +15,58 @@
# ------- Branding Related Config ------- #
- name: INSTALLATION_NAME
value: 'Chatwoot'
display_title: 'Installation Name'
description: 'The installation wide name that would be used in the dashboard, title etc.'
value: "Chatwoot"
display_title: "Installation Name"
description: "The installation wide name that would be used in the dashboard, title etc."
- name: LOGO_THUMBNAIL
value: '/brand-assets/logo_thumbnail.svg'
display_title: 'Logo Thumbnail'
description: 'The thumbnail that would be used for favicon (512px X 512px)'
value: "/brand-assets/logo_thumbnail.svg"
display_title: "Logo Thumbnail"
description: "The thumbnail that would be used for favicon (512px X 512px)"
- name: LOGO
value: '/brand-assets/logo.svg'
display_title: 'Logo'
description: 'The logo that would be used on the dashboard, login page etc.'
value: "/brand-assets/logo.svg"
display_title: "Logo"
description: "The logo that would be used on the dashboard, login page etc."
- name: LOGO_DARK
value: '/brand-assets/logo_dark.svg'
display_title: 'Logo Dark Mode'
description: 'The logo that would be used on the dashboard, login page etc. for dark mode'
value: "/brand-assets/logo_dark.svg"
display_title: "Logo Dark Mode"
description: "The logo that would be used on the dashboard, login page etc. for dark mode"
- name: BRAND_URL
value: 'https://www.chatwoot.com'
display_title: 'Brand URL'
description: 'The URL that would be used in emails under the section “Powered By”'
value: "https://www.chatwoot.com"
display_title: "Brand URL"
description: "The URL that would be used in emails under the section “Powered By”"
- name: WIDGET_BRAND_URL
value: 'https://www.chatwoot.com'
display_title: 'Widget Brand URL'
description: 'The URL that would be used in the widget under the section “Powered By”'
value: "https://www.chatwoot.com"
display_title: "Widget Brand URL"
description: "The URL that would be used in the widget under the section “Powered By”"
- name: BRAND_NAME
value: 'Chatwoot'
display_title: 'Brand Name'
description: 'The name that would be used in emails and the widget'
value: "Chatwoot"
display_title: "Brand Name"
description: "The name that would be used in emails and the widget"
- name: TERMS_URL
value: 'https://www.chatwoot.com/terms-of-service'
display_title: 'Terms URL'
description: 'The terms of service URL displayed in Signup Page'
value: "https://www.chatwoot.com/terms-of-service"
display_title: "Terms URL"
description: "The terms of service URL displayed in Signup Page"
- name: PRIVACY_URL
value: 'https://www.chatwoot.com/privacy-policy'
display_title: 'Privacy URL'
description: 'The privacy policy URL displayed in the app'
value: "https://www.chatwoot.com/privacy-policy"
display_title: "Privacy URL"
description: "The privacy policy URL displayed in the app"
- name: DISPLAY_MANIFEST
value: true
display_title: 'Chatwoot Metadata'
description: 'Display default Chatwoot metadata like favicons and upgrade warnings'
display_title: "Chatwoot Metadata"
description: "Display default Chatwoot metadata like favicons and upgrade warnings"
type: boolean
# ------- End of Branding Related Config ------- #
# ------- Signup & Account Related Config ------- #
- name: ENABLE_ACCOUNT_SIGNUP
display_title: 'Enable Account Signup'
display_title: "Enable Account Signup"
value: false
description: 'Allow users to signup for new accounts'
description: "Allow users to signup for new accounts"
locked: false
type: boolean
- name: CREATE_NEW_ACCOUNT_FROM_DASHBOARD
value: false
description: 'Allow users to create new accounts from the dashboard'
description: "Allow users to create new accounts from the dashboard"
locked: false
- name: HCAPTCHA_SITE_KEY
value:
@@ -76,127 +76,127 @@
locked: false
- name: INSTALLATION_EVENTS_WEBHOOK_URL
value:
display_title: 'System events Webhook URL'
description: 'The URL to which the system events like new accounts created will be sent'
display_title: "System events Webhook URL"
description: "The URL to which the system events like new accounts created will be sent"
locked: false
- name: WEBHOOK_TIMEOUT
value: 5
display_title: 'Webhook request timeout (seconds)'
description: 'Maximum time Chatwoot waits for a webhook response before failing the request'
display_title: "Webhook request timeout (seconds)"
description: "Maximum time Chatwoot waits for a webhook response before failing the request"
locked: false
- name: DIRECT_UPLOADS_ENABLED
type: boolean
value: false
description: 'Enable direct uploads to cloud storage'
description: "Enable direct uploads to cloud storage"
locked: false
- name: MAXIMUM_FILE_UPLOAD_SIZE
value: 40
display_title: 'Attachment size limit (MB)'
description: 'Maximum attachment size in MB allowed for uploads'
display_title: "Attachment size limit (MB)"
description: "Maximum attachment size in MB allowed for uploads"
locked: false
# ------- End of Account Related Config ------- #
# ------- Email Related Config ------- #
- name: MAILER_INBOUND_EMAIL_DOMAIN
display_title: 'Inbound Email Domain'
display_title: "Inbound Email Domain"
value:
description: 'The domain name to be used for generating conversation continuity emails (reply+id@domain.com)'
description: "The domain name to be used for generating conversation continuity emails (reply+id@domain.com)"
locked: false
- name: MAILER_SUPPORT_EMAIL
display_title: 'Support Email'
display_title: "Support Email"
value:
description: 'The support email address for your installation'
description: "The support email address for your installation"
locked: false
# ------- End of Email Related Config ------- #
# ------- Facebook Channel Related Config ------- #
- name: FB_APP_ID
display_title: 'Facebook App ID'
display_title: "Facebook App ID"
locked: false
- name: FB_VERIFY_TOKEN
display_title: 'Facebook Verify Token'
description: 'The verify token used for Facebook Messenger Webhook'
display_title: "Facebook Verify Token"
description: "The verify token used for Facebook Messenger Webhook"
locked: false
type: secret
- name: FB_APP_SECRET
display_title: 'Facebook App Secret'
display_title: "Facebook App Secret"
locked: false
type: secret
- name: IG_VERIFY_TOKEN
display_title: 'Instagram Verify Token'
description: 'The verify token used for Instagram Webhook'
display_title: "Instagram Verify Token"
description: "The verify token used for Instagram Webhook"
locked: false
type: secret
- name: FACEBOOK_API_VERSION
display_title: 'Facebook API Version'
description: 'Configure this if you want to use a different Facebook API version. Make sure its prefixed with `v`'
value: 'v18.0'
display_title: "Facebook API Version"
description: "Configure this if you want to use a different Facebook API version. Make sure its prefixed with `v`"
value: "v18.0"
locked: false
- name: ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT
display_title: 'Enable human agent'
display_title: "Enable human agent"
value: false
locked: false
description: 'Enable human agent for messenger channel for longer message back period. Needs additional app approval: https://developers.facebook.com/docs/features-reference/human-agent/'
description: "Enable human agent for messenger channel for longer message back period. Needs additional app approval: https://developers.facebook.com/docs/features-reference/human-agent/"
type: boolean
# ------- End of Facebook Channel Related Config ------- #
# ------- WhatsApp Channel Related Config ------- #
- name: WHATSAPP_APP_ID
display_title: 'WhatsApp App ID'
description: 'The Facebook App ID for WhatsApp Business API integration'
display_title: "WhatsApp App ID"
description: "The Facebook App ID for WhatsApp Business API integration"
locked: false
- name: WHATSAPP_CONFIGURATION_ID
display_title: 'WhatsApp Configuration ID'
description: 'The Configuration ID for WhatsApp Embedded Signup flow (required for embedded signup)'
display_title: "WhatsApp Configuration ID"
description: "The Configuration ID for WhatsApp Embedded Signup flow (required for embedded signup)"
locked: false
- name: WHATSAPP_APP_SECRET
display_title: 'WhatsApp App Secret'
description: 'The App Secret for WhatsApp Embedded Signup flow (required for embedded signup)'
display_title: "WhatsApp App Secret"
description: "The App Secret for WhatsApp Embedded Signup flow (required for embedded signup)"
locked: false
- name: WHATSAPP_API_VERSION
display_title: 'WhatsApp API Version'
description: 'Configure this if you want to use a different WhatsApp API version. Make sure its prefixed with `v`'
value: 'v22.0'
display_title: "WhatsApp API Version"
description: "Configure this if you want to use a different WhatsApp API version. Make sure its prefixed with `v`"
value: "v22.0"
locked: false
# ------- End of WhatsApp Channel Related Config ------- #
# MARK: Microsoft Email Channel Config
- name: AZURE_APP_ID
display_title: 'Azure App ID'
description: 'The App ID that will be used to authenticate with customer Microsoft accounts. Find more details on setting up Azure here: https://chwt.app/dev/ms'
display_title: "Azure App ID"
description: "The App ID that will be used to authenticate with customer Microsoft accounts. Find more details on setting up Azure here: https://chwt.app/dev/ms"
locked: false
- name: AZURE_APP_SECRET
display_title: 'Azure App Secret'
display_title: "Azure App Secret"
locked: false
type: secret
# End of Microsoft Email Channel Config
# MARK: Captain Config
- name: CAPTAIN_OPEN_AI_API_KEY
display_title: 'OpenAI API Key'
description: 'The API key used to authenticate requests to OpenAI services for Captain AI.'
display_title: "OpenAI API Key"
description: "The API key used to authenticate requests to OpenAI services for Captain AI."
locked: false
type: secret
- name: CAPTAIN_OPEN_AI_MODEL
display_title: 'OpenAI Model'
description: 'The OpenAI model configured for use in Captain AI. Default: gpt-4o-mini'
display_title: "OpenAI Model"
description: "The OpenAI model configured for use in Captain AI. Default: gpt-4.1-mini"
locked: false
- name: CAPTAIN_OPEN_AI_ENDPOINT
display_title: 'OpenAI API Endpoint (optional)'
description: 'The OpenAI endpoint configured for use in Captain AI. Default: https://api.openai.com/'
display_title: "OpenAI API Endpoint (optional)"
description: "The OpenAI endpoint configured for use in Captain AI. Default: https://api.openai.com/"
locked: false
- name: CAPTAIN_EMBEDDING_MODEL
display_title: 'Embedding Model (optional)'
description: 'The embedding model configured for use in Captain AI. Default: text-embedding-3-small'
display_title: "Embedding Model (optional)"
description: "The embedding model configured for use in Captain AI. Default: text-embedding-3-small"
locked: false
- name: CAPTAIN_FIRECRAWL_API_KEY
display_title: 'FireCrawl API Key (optional)'
description: 'The FireCrawl API key for the Captain AI service'
display_title: "FireCrawl API Key (optional)"
description: "The FireCrawl API key for the Captain AI service"
locked: false
type: secret
- name: CAPTAIN_CLOUD_PLAN_LIMITS
display_title: 'Captain Cloud Plan Limits'
description: 'The limits for the Captain AI service for different plans'
display_title: "Captain Cloud Plan Limits"
description: "The limits for the Captain AI service for different plans"
value:
type: code
# End of Captain Config
@@ -204,65 +204,65 @@
# ------- Chatwoot Internal Config for Cloud ----#
- name: CHATWOOT_INBOX_TOKEN
value:
display_title: 'Inbox Token'
description: 'The Chatwoot Inbox Token for Contact Support in Cloud'
display_title: "Inbox Token"
description: "The Chatwoot Inbox Token for Contact Support in Cloud"
locked: false
type: secret
- name: CHATWOOT_INBOX_HMAC_KEY
value:
display_title: 'Inbox HMAC Key'
description: 'The Chatwoot Inbox HMAC Key for Contact Support in Cloud'
display_title: "Inbox HMAC Key"
description: "The Chatwoot Inbox HMAC Key for Contact Support in Cloud"
locked: false
type: secret
- name: CHATWOOT_CLOUD_PLANS
display_title: 'Cloud Plans'
display_title: "Cloud Plans"
value:
description: 'Config to store stripe plans for cloud'
description: "Config to store stripe plans for cloud"
- name: CHATWOOT_CLOUD_PLAN_FEATURES
display_title: 'Planwise Features List'
display_title: "Planwise Features List"
value:
description: 'Config to features and their associated plans'
description: "Config to features and their associated plans"
- name: DEPLOYMENT_ENV
value: self-hosted
description: 'The deployment environment of the installation, to differentiate between Chatwoot cloud and self-hosted'
description: "The deployment environment of the installation, to differentiate between Chatwoot cloud and self-hosted"
- name: ANALYTICS_TOKEN
value:
display_title: 'Analytics Token'
description: 'The PostHog analytics token for Chatwoot cloud'
display_title: "Analytics Token"
description: "The PostHog analytics token for Chatwoot cloud"
type: secret
- name: CLEARBIT_API_KEY
value:
display_title: 'Clearbit API Key'
description: 'This API key is used for onboarding the users, to pre-fill account data.'
display_title: "Clearbit API Key"
description: "This API key is used for onboarding the users, to pre-fill account data."
type: secret
- name: DASHBOARD_SCRIPTS
value:
display_title: 'Dashboard Scripts'
description: 'Scripts are loaded as the last item in the <body> tag'
display_title: "Dashboard Scripts"
description: "Scripts are loaded as the last item in the <body> tag"
type: code
- name: BLOCKED_EMAIL_DOMAINS
value:
display_title: 'Blocked Email Domains'
description: 'Add a domain per line to block them from signing up, accepts Regex'
display_title: "Blocked Email Domains"
description: "Add a domain per line to block them from signing up, accepts Regex"
type: code
- name: SKIP_INCOMING_BCC_PROCESSING
value:
display_title: 'Skip BCC Processing For'
description: 'Comma-separated list of account IDs that should be skipped from incoming BCC processing'
display_title: "Skip BCC Processing For"
description: "Comma-separated list of account IDs that should be skipped from incoming BCC processing"
- name: INACTIVE_WHATSAPP_NUMBERS
value: ''
display_title: 'Inactive WhatsApp Numbers'
description: 'Comma-separated list of WhatsApp numbers that should be rejected with a 422 error'
value: ""
display_title: "Inactive WhatsApp Numbers"
description: "Comma-separated list of WhatsApp numbers that should be rejected with a 422 error"
type: code
# ------- End of Chatwoot Internal Config for Cloud ----#
# ------- Chatwoot Internal Config for Self Hosted ----#
- name: INSTALLATION_PRICING_PLAN
value: 'community'
description: 'The pricing plan for the installation, retrieved from the billing API'
value: "community"
description: "The pricing plan for the installation, retrieved from the billing API"
- name: INSTALLATION_PRICING_PLAN_QUANTITY
value: 0
description: 'The number of licenses purchased for the installation, retrieved from the billing API'
description: "The number of licenses purchased for the installation, retrieved from the billing API"
- name: CHATWOOT_SUPPORT_WEBSITE_TOKEN
value:
description: 'The Chatwoot website token, used to identify the Chatwoot inbox and display the "Contact Support" option on the billing page'
@@ -272,7 +272,7 @@
description: 'The Chatwoot script base URL, to display the "Contact Support" option on the billing page'
- name: CHATWOOT_SUPPORT_IDENTIFIER_HASH
value:
description: 'The Chatwoot identifier hash, to validate the contact in the live chat window.'
description: "The Chatwoot identifier hash, to validate the contact in the live chat window."
type: secret
- name: ACCOUNT_SECURITY_NOTIFICATION_WEBHOOK_URL
display_title: Webhook URL to post security analysis
@@ -282,221 +282,221 @@
# ------- Compliance Related Config ----#
- name: CHATWOOT_INSTANCE_ADMIN_EMAIL
display_title: 'Instance Admin Email'
display_title: "Instance Admin Email"
value:
description: 'The email of the instance administrator to receive compliance-related notifications'
description: "The email of the instance administrator to receive compliance-related notifications"
locked: false
# ------- End of Compliance Related Config ----#
## ------ Configs added for enterprise clients ------ ##
- name: API_CHANNEL_NAME
value:
description: 'Custom name for the API channel'
description: "Custom name for the API channel"
- name: API_CHANNEL_THUMBNAIL
value:
description: 'Custom thumbnail for the API channel'
description: "Custom thumbnail for the API channel"
- name: LOGOUT_REDIRECT_LINK
value: /app/login
locked: false
description: 'Redirect to a different link after logout'
description: "Redirect to a different link after logout"
- name: DISABLE_USER_PROFILE_UPDATE
value: false
locked: false
description: 'Disable rendering profile update page for users'
description: "Disable rendering profile update page for users"
## ------ End of Configs added for enterprise clients ------ ##
## ------ Configs added for FCM v1 notifications ------ ##
- name: FIREBASE_PROJECT_ID
display_title: 'Firebase Project ID'
display_title: "Firebase Project ID"
value:
locked: false
description: 'Firebase project ID'
description: "Firebase project ID"
- name: FIREBASE_CREDENTIALS
display_title: 'Firebase Credentials'
display_title: "Firebase Credentials"
value:
locked: false
type: secret
description: 'Contents on your firebase credentials json file'
description: "Contents on your firebase credentials json file"
## ------ End of Configs added for FCM v1 notifications ------ ##
## ------ Configs added for Linear ------ ##
- name: LINEAR_CLIENT_ID
display_title: 'Linear Client ID'
display_title: "Linear Client ID"
value:
locked: false
description: 'Linear client ID'
description: "Linear client ID"
- name: LINEAR_CLIENT_SECRET
display_title: 'Linear Client Secret'
display_title: "Linear Client Secret"
value:
locked: false
description: 'Linear client secret'
description: "Linear client secret"
type: secret
## ------ End of Configs added for Linear ------ ##
## ------ Configs added for Notion ------ ##
- name: NOTION_CLIENT_ID
display_title: 'Notion Client ID'
display_title: "Notion Client ID"
value:
locked: false
description: 'Notion client ID'
description: "Notion client ID"
- name: NOTION_CLIENT_SECRET
display_title: 'Notion Client Secret'
display_title: "Notion Client Secret"
value:
locked: false
description: 'Notion client secret'
description: "Notion client secret"
type: secret
- name: NOTION_VERSION
display_title: 'Notion Version'
value: '2022-06-28'
display_title: "Notion Version"
value: "2022-06-28"
locked: false
description: 'Notion version'
description: "Notion version"
## ------ End of Configs added for Notion ------ ##
## ------ Configs added for Slack ------ ##
- name: SLACK_CLIENT_ID
display_title: 'Slack Client ID'
display_title: "Slack Client ID"
value:
locked: false
description: 'Slack client ID'
description: "Slack client ID"
- name: SLACK_CLIENT_SECRET
display_title: 'Slack Client Secret'
display_title: "Slack Client Secret"
value:
locked: false
description: 'Slack client secret'
description: "Slack client secret"
type: secret
## ------ End of Configs added for Slack ------ ##
# ------- Shopify Integration Config ------- #
- name: SHOPIFY_CLIENT_ID
display_title: 'Shopify Client ID'
description: 'The Client ID (API Key) from your Shopify Partner account'
display_title: "Shopify Client ID"
description: "The Client ID (API Key) from your Shopify Partner account"
locked: false
type: secret
- name: SHOPIFY_CLIENT_SECRET
display_title: 'Shopify Client Secret'
description: 'The Client Secret (API Secret Key) from your Shopify Partner account'
display_title: "Shopify Client Secret"
description: "The Client Secret (API Secret Key) from your Shopify Partner account"
locked: false
type: secret
# ------- End of Shopify Related Config ------- #
# ------- Instagram Channel Related Config ------- #
- name: INSTAGRAM_APP_ID
display_title: 'Instagram App ID'
display_title: "Instagram App ID"
locked: false
- name: INSTAGRAM_APP_SECRET
display_title: 'Instagram App Secret'
description: 'The App Secret used for Instagram authentication'
display_title: "Instagram App Secret"
description: "The App Secret used for Instagram authentication"
locked: false
type: secret
- name: INSTAGRAM_VERIFY_TOKEN
display_title: 'Instagram Verify Token'
description: 'The verify token used for Instagram Webhook'
display_title: "Instagram Verify Token"
description: "The verify token used for Instagram Webhook"
locked: false
type: secret
- name: ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT
display_title: 'Enable human agent for instagram channel'
display_title: "Enable human agent for instagram channel"
value: false
locked: false
description: 'Enable human agent for instagram channel for longer message back period. Needs additional app approval: https://developers.facebook.com/docs/features-reference/human-agent/'
description: "Enable human agent for instagram channel for longer message back period. Needs additional app approval: https://developers.facebook.com/docs/features-reference/human-agent/"
type: boolean
- name: INSTAGRAM_API_VERSION
display_title: 'Instagram API Version'
description: 'Configure this if you want to use a different Instagram API version. Make sure its prefixed with `v`'
value: 'v22.0'
display_title: "Instagram API Version"
description: "Configure this if you want to use a different Instagram API version. Make sure its prefixed with `v`"
value: "v22.0"
locked: true
# ------- End of Instagram Channel Related Config ------- #
# ------- OG Image Related Config ------- #
- name: OG_IMAGE_CDN_URL
display_title: 'OG Image CDN URL'
description: 'The CDN URL for serving OG images'
value: ''
display_title: "OG Image CDN URL"
description: "The CDN URL for serving OG images"
value: ""
locked: false
- name: OG_IMAGE_CLIENT_REF
display_title: 'OG Image Client Reference'
description: 'Token used to block unauthorized access to OG images'
value: ''
display_title: "OG Image Client Reference"
description: "Token used to block unauthorized access to OG images"
value: ""
locked: false
type: secret
# ------- End of OG Image Related Config ------- #
## ------ Configs added for Google OAuth ------ ##
- name: GOOGLE_OAUTH_CLIENT_ID
display_title: 'Google OAuth Client ID'
display_title: "Google OAuth Client ID"
value:
locked: false
description: 'Google OAuth Client ID for email authentication'
description: "Google OAuth Client ID for email authentication"
- name: GOOGLE_OAUTH_CLIENT_SECRET
display_title: 'Google OAuth Client Secret'
display_title: "Google OAuth Client Secret"
value:
locked: false
description: 'Google OAuth Client Secret for email authentication'
description: "Google OAuth Client Secret for email authentication"
type: secret
- name: GOOGLE_OAUTH_REDIRECT_URI
display_title: 'Google OAuth Redirect URI'
display_title: "Google OAuth Redirect URI"
value:
locked: false
description: 'The redirect URI configured in your Google OAuth app'
description: "The redirect URI configured in your Google OAuth app"
- name: ENABLE_GOOGLE_OAUTH_LOGIN
display_title: 'Enable Google OAuth login'
display_title: "Enable Google OAuth login"
value: true
locked: false
description: 'Show Google OAuth as a login option when credentials are configured'
description: "Show Google OAuth as a login option when credentials are configured"
type: boolean
## ------ End of Configs added for Google OAuth ------ ##
## ------ Configs added for SAML SSO ------ ##
- name: ENABLE_SAML_SSO_LOGIN
display_title: 'Enable SAML SSO login'
display_title: "Enable SAML SSO login"
value: true
locked: false
description: 'Show SAML SSO as a login option. Cannot be disabled if any users are using SAML authentication.'
description: "Show SAML SSO as a login option. Cannot be disabled if any users are using SAML authentication."
type: boolean
## ------ End of Configs added for SAML SSO ------ ##
## ------ Configs added for Cloudflare ------ ##
- name: CLOUDFLARE_API_KEY
display_title: 'Cloudflare API Key'
display_title: "Cloudflare API Key"
value:
locked: false
description: 'API key for Cloudflare account authentication'
description: "API key for Cloudflare account authentication"
type: secret
- name: CLOUDFLARE_ZONE_ID
display_title: 'Cloudflare Zone ID'
display_title: "Cloudflare Zone ID"
value:
locked: false
description: 'Zone ID for the Cloudflare domain'
description: "Zone ID for the Cloudflare domain"
## ------ End of Configs added for Cloudflare ------ ##
## ------ Customizations for Customers ------ ##
- name: WIDGET_TOKEN_EXPIRY
display_title: 'Widget Token Expiry'
display_title: "Widget Token Expiry"
value: 180
locked: false
description: 'Token expiry in days'
description: "Token expiry in days"
## ------ End of Customizations for Customers ------ ##
## ----- LLM Observability ---- ##
- name: OTEL_PROVIDER
display_title: 'OpenTelemetry Provider'
description: 'LLM observability provider (langfuse, langsmith, etc.)'
value: ''
display_title: "OpenTelemetry Provider"
description: "LLM observability provider (langfuse, langsmith, etc.)"
value: ""
locked: false
- name: LANGFUSE_PUBLIC_KEY
display_title: 'Langfuse Public Key'
description: 'Public key for Langfuse authentication'
value: ''
display_title: "Langfuse Public Key"
description: "Public key for Langfuse authentication"
value: ""
locked: false
type: secret
- name: LANGFUSE_SECRET_KEY
display_title: 'Langfuse Secret Key'
description: 'Secret key for Langfuse authentication'
value: ''
display_title: "Langfuse Secret Key"
description: "Secret key for Langfuse authentication"
value: ""
locked: false
type: secret
- name: LANGFUSE_BASE_URL
display_title: 'Langfuse Base URL'
description: 'Langfuse endpoint (US: https://us.cloud.langfuse.com, EU: https://cloud.langfuse.com)'
value: 'https://us.cloud.langfuse.com'
display_title: "Langfuse Base URL"
description: "Langfuse endpoint (US: https://us.cloud.langfuse.com, EU: https://cloud.langfuse.com)"
value: "https://us.cloud.langfuse.com"
locked: false
## ---- End of LLM Observability ---- ##
+54
View File
@@ -0,0 +1,54 @@
providers:
openai:
display_name: "OpenAI"
logo: openai.png
models:
gpt-4.1:
provider: openai
display_name: "GPT-4.1"
credit_multiplier: 0.5
gpt-4.1-mini:
provider: openai
display_name: "GPT-4.1 Mini"
credit_multiplier: 0.3
gpt-4.1-nano:
provider: openai
display_name: "GPT-4.1 Nano"
credit_multiplier: 0.1
gpt-5:
provider: openai
display_name: "GPT-5"
credit_multiplier: 1
gpt-5.1:
provider: openai
display_name: "GPT-5.1"
credit_multiplier: 1
gpt-5-mini:
provider: openai
display_name: "GPT-5 Mini"
credit_multiplier: 0.8
gpt-5-nano:
provider: openai
display_name: "GPT-5 Nano"
credit_multiplier: 0.3
gpt-5.2:
provider: openai
display_name: "GPT-5.2"
credit_multiplier: 1
features:
editor:
models:
[gpt-4.1-mini, gpt-4.1-nano, gpt-5-mini, gpt-4.1, gpt-5, gpt-5.1, gpt-5.2]
default: gpt-5-mini
assistant:
models: [gpt-5-mini, gpt-4.1, gpt-5, gpt-5.1, gpt-5.2]
default: gpt-5-mini
copilot:
models: [gpt-5-mini, gpt-4.1, gpt-5, gpt-5.1, gpt-5.2]
default: gpt-5-mini
label_suggestion:
models: [gpt-4.1-nano, gpt-4.1-mini, gpt-5-mini]
default: gpt-4.1-nano
# add deprecation_map later
+1
View File
@@ -52,6 +52,7 @@ Rails.application.routes.draw do
post :bulk_create, on: :collection
end
namespace :captain do
resource :config, only: [:show], controller: 'config'
resources :assistants do
member do
post :playground
@@ -7,7 +7,7 @@
#
# For all other LLM operations, use Llm::BaseAiService with RubyLLM instead.
class Llm::LegacyBaseOpenAiService
DEFAULT_MODEL = 'gpt-4o-mini'
DEFAULT_MODEL = 'gpt-4.1-mini'
attr_reader :client, :model
+1 -1
View File
@@ -1,7 +1,7 @@
require 'ruby_llm'
module Llm::Config
DEFAULT_MODEL = 'gpt-4o-mini'.freeze
DEFAULT_MODEL = 'gpt-4.1-mini'.freeze
class << self
def initialized?
@initialized ||= false
+80
View File
@@ -0,0 +1,80 @@
# frozen_string_literal: true
module Llm::ConfigService
CONFIG_PATH = Rails.root.join('config/llm.yml').freeze
class << self
def config
@config ||= load_config
end
def reload!
@config = load_config
end
%w[providers models features].each do |key|
define_method(key) do
config[key] || {}
end
end
def models_for_feature(feature_key)
features.dig(feature_key.to_s, 'models') || []
end
def default_model_for_feature(feature_key)
features.dig(feature_key.to_s, 'default')
end
def model_info(model_name)
models[model_name.to_s]
end
def provider_info(provider_name)
providers[provider_name.to_s]
end
def feature_keys
features.keys
end
def valid_model_for_feature?(feature_key, model_name)
allowed_models = models_for_feature(feature_key)
allowed_models.include?(model_name.to_s)
end
def feature_config(feature_key)
feature = features[feature_key.to_s]
return nil unless feature
{
models: feature['models'].filter_map do |model_name|
model = models[model_name]
next nil unless model
{
id: model_name,
display_name: model['display_name'],
provider: model['provider'],
credit_multiplier: model['credit_multiplier']
}
end,
default: feature['default']
}
end
def all_features_config
features.keys.index_with do |feature_key|
feature_config(feature_key)
end
end
private
def load_config
return {} unless File.exist?(CONFIG_PATH)
YAML.load_file(CONFIG_PATH) || {}
end
end
end
@@ -0,0 +1,50 @@
require 'rails_helper'
RSpec.describe 'Api::V1::Accounts::Captain::Config', type: :request do
let(:account) { create(:account) }
let(:admin) { create(:user, account: account, role: :administrator) }
let(:agent) { create(:user, account: account, role: :agent) }
def json_response
JSON.parse(response.body, symbolize_names: true)
end
describe 'GET /api/v1/accounts/{account.id}/captain/config' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
get "/api/v1/accounts/#{account.id}/captain/config",
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
it 'returns available AI model options for each feature' do
get "/api/v1/accounts/#{account.id}/captain/config",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
%w[editor assistant copilot label_suggestion].each do |feature|
expect(json_response.dig(:features, feature.to_sym, :models)).to be_present
expect(json_response.dig(:features, feature.to_sym, :default)).to be_present
end
end
end
context 'when it is an admin' do
it 'returns providers, models, and features configuration' do
get "/api/v1/accounts/#{account.id}/captain/config",
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
expect(json_response).to have_key(:providers)
expect(json_response).to have_key(:models)
expect(json_response).to have_key(:features)
end
end
end
end
@@ -9,7 +9,7 @@ RSpec.describe Messages::AudioTranscriptionService, type: :service do
before do
# Create required installation configs
create(:installation_config, name: 'CAPTAIN_OPEN_AI_API_KEY', value: 'test-api-key')
create(:installation_config, name: 'CAPTAIN_OPEN_AI_MODEL', value: 'gpt-4o-mini')
create(:installation_config, name: 'CAPTAIN_OPEN_AI_MODEL', value: 'gpt-4.1-mini')
# Mock usage limits for transcription to be available
allow(account).to receive(:usage_limits).and_return({ captain: { responses: { current_available: 100 } } })
@@ -20,7 +20,7 @@ RSpec.describe Integrations::LlmInstrumentation do
account_id: 123,
conversation_id: 456,
feature_name: 'reply_suggestion',
model: 'gpt-4o-mini',
model: 'gpt-4.1-mini',
messages: [{ 'role' => 'user', 'content' => 'Hello' }],
temperature: 0.7
}
@@ -105,7 +105,7 @@ RSpec.describe Integrations::LlmInstrumentation do
instance.instrument_llm_call(params) { 'result' }
expect(mock_span).to have_received(:set_attribute).with('gen_ai.provider.name', 'openai')
expect(mock_span).to have_received(:set_attribute).with('gen_ai.request.model', 'gpt-4o-mini')
expect(mock_span).to have_received(:set_attribute).with('gen_ai.request.model', 'gpt-4.1-mini')
expect(mock_span).to have_received(:set_attribute).with('gen_ai.request.temperature', 0.7)
end
+55
View File
@@ -218,4 +218,59 @@ RSpec.describe Account do
end
end
end
describe 'captain_preferences' do
let(:account) { create(:account) }
describe 'with no saved preferences' do
it 'returns defaults from llm.yml' do
prefs = account.captain_preferences
expect(prefs[:features].values).to all(be false)
Llm::ConfigService.feature_keys.each do |feature|
expect(prefs[:models][feature]).to eq(Llm::ConfigService.default_model_for_feature(feature))
end
end
end
describe 'with saved model preferences' do
it 'returns saved preferences merged with defaults' do
account.update!(captain_models: { 'editor' => 'gpt-4o', 'assistant' => 'gpt-5' })
prefs = account.captain_preferences
expect(prefs[:models]['editor']).to eq('gpt-4o')
expect(prefs[:models]['assistant']).to eq('gpt-5')
expect(prefs[:models]['copilot']).to eq(Llm::ConfigService.default_model_for_feature('copilot'))
end
end
describe 'with saved feature preferences' do
it 'returns saved feature states' do
account.update!(captain_features: { 'editor' => true, 'assistant' => true })
prefs = account.captain_preferences
expect(prefs[:features]['editor']).to be true
expect(prefs[:features]['assistant']).to be true
expect(prefs[:features]['copilot']).to be false
end
end
describe 'validation' do
it 'rejects invalid model for a feature' do
account.captain_models = { 'label_suggestion' => 'gpt-5' }
expect(account).not_to be_valid
expect(account.errors[:captain_models].first).to include('not a valid model for label_suggestion')
end
it 'accepts valid model for a feature' do
account.captain_models = { 'editor' => 'gpt-4o', 'label_suggestion' => 'gpt-4.1-nano' }
expect(account).to be_valid
end
end
end
end