Class: Chat

Inherits:
ApplicationRecord
  • Object
show all
Includes:
ChatManager::TitleGeneratable
Defined in:
lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb

Instance Method Summary collapse

Instance Method Details

#add_assistant_response(prompt_execution, jwt_token, tool_ids: [], generation_settings: {}) ⇒ Object

Add assistant response by sending to LLM



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb', line 38

def add_assistant_response(prompt_execution, jwt_token, tool_ids: [], generation_settings: {})
  response_content = send_to_llm(prompt_execution, jwt_token, tool_ids: tool_ids, generation_settings: generation_settings)
  prompt_execution.update!(
    llm_platform: resolve_llm_type(prompt_execution.llm_uuid, jwt_token),
    response: response_content
  )
  new_message = messages.create!(
    role: "assistant",
    prompt_navigator_prompt_execution: prompt_execution
  )

  new_message
end

#add_user_message(message, llm_uuid, model, branch_from_execution_id = nil, llm_platform: nil, image: nil) ⇒ Object

Add a user message to the chat



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb', line 10

def add_user_message(message, llm_uuid, model, branch_from_execution_id = nil, llm_platform: nil, image: nil)
  previous_id = if branch_from_execution_id.present?
    PromptNavigator::PromptExecution.find_by(execution_id: branch_from_execution_id)&.id
  else
    messages.where(role: "user").order(:created_at).last&.prompt_navigator_prompt_execution_id
  end
  # Prepend the attached image as a data-URI markdown image so the saved
  # prompt renders the image on reload, and so the streaming controller
  # (reached over a GET EventSource) can recover the image from pe.prompt.
  prompt_with_image = image.present? ? "![](data:#{image[:mime]};base64,#{image[:data_b64]})\n\n#{message}" : message
  prompt_execution = PromptNavigator::PromptExecution.create!(
    prompt: prompt_with_image,
    llm_uuid: llm_uuid,
    model: model,
    llm_platform: llm_platform,
    configuration: "",
    previous_id: previous_id
  )

  new_message = messages.create!(
    role: "user",
    prompt_navigator_prompt_execution: prompt_execution
  )

  [ prompt_execution, new_message ]
end

#finalize_streamed_response(prompt_execution, content, jwt_token) ⇒ Object

Persist the streamed assistant response. Skips persistence if content is blank.



94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb', line 94

def finalize_streamed_response(prompt_execution, content, jwt_token)
  return nil if content.blank?

  prompt_execution.update!(
    llm_platform: prompt_execution.llm_platform.presence || resolve_llm_type(prompt_execution.llm_uuid, jwt_token),
    response: content
  )
  messages.create!(
    role: "assistant",
    prompt_navigator_prompt_execution: prompt_execution
  )
end

#ordered_by_descending_prompt_executionsObject



114
115
116
117
118
119
120
121
122
# File 'lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb', line 114

def ordered_by_descending_prompt_executions
  messages
    .where(role: "user")
    .includes(:prompt_navigator_prompt_execution)
    .order(created_at: :desc)
    .to_a
    .select { |msg| msg.prompt_navigator_prompt_execution }
    .map(&:prompt_navigator_prompt_execution)
end

#ordered_messagesObject

Get all messages in order



108
109
110
111
112
# File 'lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb', line 108

def ordered_messages
  messages
    .includes(:prompt_navigator_prompt_execution)
    .order(:created_at)
end

#stream_assistant_response(prompt_execution, jwt_token, tool_ids: [], generation_settings: {}, &block) ⇒ Object

Stream the assistant response from the LLM. Yields each parsed SSE event. Returns the assembled content (with markdown “Tool calls” section appended if tools fired). Caller is responsible for persistence.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb', line 55

def stream_assistant_response(prompt_execution, jwt_token, tool_ids: [], generation_settings: {}, &block)
  last_msg = ordered_messages.last
  pe = last_msg.prompt_navigator_prompt_execution
  # Separate any attached image (data-URI markdown at the head of the
  # prompt) from the text. The image flows as a structured field; the text
  # goes through the usual prompt path.
  text_prompt, attached_image = extract_attached_image(pe.prompt)
  prompt = { role: last_msg.role, prompt: text_prompt }

  if image_model?(prompt_execution.model)
    image_context = pe.build_context(limit: Rails.configuration.summarize_conversation_count)
    LlmMetaClient::ServerQuery.new.stream(
      jwt_token,
      prompt_execution.llm_uuid,
      prompt_execution.model,
      "",
      prompt,
      generation_settings: generation_settings,
      image_context: image_context,
      image: attached_image,
      &block
    )
  else
    summarized_context, prompt = build_streaming_context(prompt_execution, jwt_token, with_tools: tool_ids.any?)
    LlmMetaClient::ServerQuery.new.stream(
      jwt_token,
      prompt_execution.llm_uuid,
      prompt_execution.model,
      summarized_context,
      prompt,
      tool_ids: tool_ids,
      generation_settings: generation_settings,
      image: attached_image,
      &block
    )
  end
end