defaults are read from llm.yml
This commit is contained in:
@@ -20,7 +20,7 @@ module Captain::ChatHelper
|
||||
private
|
||||
|
||||
def build_chat
|
||||
llm_chat = chat(model: @model, temperature: temperature)
|
||||
llm_chat = chat(model: model, temperature: temperature)
|
||||
llm_chat = llm_chat.with_params(response_format: { type: 'json_object' })
|
||||
|
||||
llm_chat = setup_tools(llm_chat)
|
||||
@@ -74,7 +74,7 @@ module Captain::ChatHelper
|
||||
account_id: resolved_account_id,
|
||||
conversation_id: @conversation_id,
|
||||
feature_name: feature_name,
|
||||
model: @model,
|
||||
model: model,
|
||||
messages: chat ? chat.messages.map { |m| { role: m.role.to_s, content: m.content.to_s } } : @messages,
|
||||
temperature: temperature,
|
||||
metadata: {
|
||||
|
||||
@@ -45,4 +45,8 @@ class Captain::Llm::AssistantChatService < Llm::BaseAiService
|
||||
def feature_name
|
||||
'assistant'
|
||||
end
|
||||
|
||||
def account
|
||||
@assistant&.account
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,8 +33,8 @@ class Captain::Llm::ContactAttributesService < Llm::BaseAiService
|
||||
def instrumentation_params
|
||||
{
|
||||
span_name: 'llm.captain.contact_attributes',
|
||||
model: @model,
|
||||
temperature: @temperature,
|
||||
model: model,
|
||||
temperature: temperature,
|
||||
account_id: @conversation.account_id,
|
||||
feature_name: 'contact_attributes',
|
||||
messages: [
|
||||
|
||||
@@ -34,8 +34,8 @@ class Captain::Llm::ContactNotesService < Llm::BaseAiService
|
||||
def instrumentation_params
|
||||
{
|
||||
span_name: 'llm.captain.contact_notes',
|
||||
model: @model,
|
||||
temperature: @temperature,
|
||||
model: model,
|
||||
temperature: temperature,
|
||||
account_id: @conversation.account_id,
|
||||
feature_name: 'contact_notes',
|
||||
messages: [
|
||||
|
||||
@@ -97,8 +97,8 @@ class Captain::Llm::ConversationFaqService < Llm::BaseAiService
|
||||
def instrumentation_params
|
||||
{
|
||||
span_name: 'llm.captain.conversation_faq',
|
||||
model: @model,
|
||||
temperature: @temperature,
|
||||
model: model,
|
||||
temperature: temperature,
|
||||
account_id: @conversation.account_id,
|
||||
conversation_id: @conversation.id,
|
||||
feature_name: 'conversation_faq',
|
||||
|
||||
@@ -33,8 +33,8 @@ class Captain::Llm::FaqGeneratorService < Llm::BaseAiService
|
||||
def instrumentation_params
|
||||
{
|
||||
span_name: 'llm.captain.faq_generator',
|
||||
model: @model,
|
||||
temperature: @temperature,
|
||||
model: model,
|
||||
temperature: temperature,
|
||||
feature_name: 'faq_generator',
|
||||
account_id: @account_id,
|
||||
messages: [
|
||||
|
||||
@@ -72,7 +72,7 @@ class Captain::Onboarding::WebsiteAnalyzerService < Llm::BaseAiService
|
||||
def instrumentation_params
|
||||
{
|
||||
span_name: 'llm.captain.website_analyzer',
|
||||
model: @model,
|
||||
model: model,
|
||||
temperature: 0.1,
|
||||
feature_name: 'website_analyzer',
|
||||
messages: [
|
||||
|
||||
@@ -2,27 +2,41 @@
|
||||
|
||||
# Base service for LLM operations using RubyLLM.
|
||||
# New features should inherit from this class.
|
||||
#
|
||||
# Subclasses should implement:
|
||||
# - feature_name: Returns the feature key (e.g., 'assistant', 'copilot') for model lookup
|
||||
# - account: Returns the Account instance for per-account model configuration
|
||||
#
|
||||
# If these methods are not implemented, falls back to InstallationConfig or DEFAULT_MODEL.
|
||||
class Llm::BaseAiService
|
||||
DEFAULT_MODEL = Llm::Config::DEFAULT_MODEL
|
||||
DEFAULT_TEMPERATURE = 1.0
|
||||
|
||||
attr_reader :model, :temperature
|
||||
attr_reader :temperature
|
||||
|
||||
def initialize
|
||||
Llm::Config.initialize!
|
||||
setup_model
|
||||
setup_temperature
|
||||
end
|
||||
|
||||
def chat(model: @model, temperature: @temperature)
|
||||
def model
|
||||
@model ||= determine_model
|
||||
end
|
||||
|
||||
def chat(model: self.model, temperature: @temperature)
|
||||
RubyLLM.chat(model: model).with_temperature(temperature)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_model
|
||||
def determine_model
|
||||
if respond_to?(:feature_name, true) && respond_to?(:account, true) && account.present?
|
||||
account_model = account.captain_preferences.dig(:models, feature_name.to_s)
|
||||
return account_model if account_model.present?
|
||||
end
|
||||
|
||||
config_value = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_MODEL')&.value
|
||||
@model = (config_value.presence || DEFAULT_MODEL)
|
||||
config_value.presence || DEFAULT_MODEL
|
||||
end
|
||||
|
||||
def setup_temperature
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
module Enterprise::Integrations::OpenaiProcessorService
|
||||
ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion label_suggestion fix_spelling_grammar shorten expand
|
||||
make_friendly make_formal simplify].freeze
|
||||
CACHEABLE_EVENTS = %w[label_suggestion].freeze
|
||||
ALLOWED_EVENT_NAMES = (Integrations::LlmBaseService::ALLOWED_EVENT_NAMES + Integrations::LlmBaseService::LABEL_SUGGESTION_EVENTS).freeze
|
||||
CACHEABLE_EVENTS = Integrations::LlmBaseService::LABEL_SUGGESTION_EVENTS.freeze
|
||||
|
||||
def label_suggestion_message
|
||||
payload = label_suggestion_body
|
||||
@@ -49,7 +48,7 @@ module Enterprise::Integrations::OpenaiProcessorService
|
||||
|
||||
def summarize_body
|
||||
{
|
||||
model: self.class::GPT_MODEL,
|
||||
model: model_for_event,
|
||||
messages: [
|
||||
{ role: 'system',
|
||||
content: prompt_from_file('summary', enterprise: true) },
|
||||
@@ -65,7 +64,7 @@ module Enterprise::Integrations::OpenaiProcessorService
|
||||
return value_from_cache if content.blank?
|
||||
|
||||
{
|
||||
model: self.class::GPT_MODEL,
|
||||
model: model_for_event,
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
|
||||
@@ -7,11 +7,25 @@ class Integrations::LlmBaseService
|
||||
# 120000 * 4 = 480,000 characters (rounding off downwards to 400,000 to be safe)
|
||||
TOKEN_LIMIT = 400_000
|
||||
GPT_MODEL = Llm::Config::DEFAULT_MODEL
|
||||
ALLOWED_EVENT_NAMES = %w[rephrase summarize reply_suggestion fix_spelling_grammar shorten expand make_friendly make_formal simplify].freeze
|
||||
CACHEABLE_EVENTS = %w[].freeze
|
||||
|
||||
# Maps event names to feature keys in config/llm.yml
|
||||
EDITOR_EVENTS = %w[rephrase fix_spelling_grammar shorten expand make_friendly make_formal simplify].freeze
|
||||
ASSISTANT_EVENTS = %w[summarize reply_suggestion].freeze
|
||||
LABEL_SUGGESTION_EVENTS = %w[label_suggestion].freeze
|
||||
|
||||
ALLOWED_EVENT_NAMES = (EDITOR_EVENTS + ASSISTANT_EVENTS).freeze
|
||||
|
||||
pattr_initialize [:hook!, :event!]
|
||||
|
||||
def model_for_event
|
||||
feature = feature_name_for_event
|
||||
return self.class::GPT_MODEL unless feature && hook&.account
|
||||
|
||||
account_model = hook.account.captain_preferences.dig(:models, feature)
|
||||
account_model.presence || self.class::GPT_MODEL
|
||||
end
|
||||
|
||||
def perform
|
||||
return nil unless valid_event_name?
|
||||
|
||||
@@ -25,6 +39,14 @@ class Integrations::LlmBaseService
|
||||
|
||||
private
|
||||
|
||||
def feature_name_for_event
|
||||
return 'editor' if EDITOR_EVENTS.include?(event_name)
|
||||
return 'assistant' if ASSISTANT_EVENTS.include?(event_name)
|
||||
return 'label_suggestion' if LABEL_SUGGESTION_EVENTS.include?(event_name)
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def event_name
|
||||
event['name']
|
||||
end
|
||||
|
||||
@@ -53,7 +53,7 @@ class Integrations::Openai::ProcessorService < Integrations::LlmBaseService
|
||||
|
||||
def build_api_call_body(system_content, user_content = event['data']['content'])
|
||||
{
|
||||
model: GPT_MODEL,
|
||||
model: model_for_event,
|
||||
messages: [
|
||||
{ role: 'system', content: system_content },
|
||||
{ role: 'user', content: user_content }
|
||||
@@ -115,7 +115,7 @@ class Integrations::Openai::ProcessorService < Integrations::LlmBaseService
|
||||
|
||||
def summarize_body
|
||||
{
|
||||
model: GPT_MODEL,
|
||||
model: model_for_event,
|
||||
messages: [
|
||||
{ role: 'system',
|
||||
content: prompt_from_file('summary', enterprise: false) },
|
||||
@@ -126,7 +126,7 @@ class Integrations::Openai::ProcessorService < Integrations::LlmBaseService
|
||||
|
||||
def reply_suggestion_body
|
||||
{
|
||||
model: GPT_MODEL,
|
||||
model: model_for_event,
|
||||
messages: [
|
||||
{ role: 'system',
|
||||
content: prompt_from_file('reply', enterprise: false) }
|
||||
|
||||
Reference in New Issue
Block a user