diff --git a/ee/lib/gitlab/llm/chain/agents/zero_shot.rb b/ee/lib/gitlab/llm/chain/agents/zero_shot.rb index 990ba30687709c875c2edcb773d1d9c2b6800c2b..65e9ab8ab46c8d6dd76f4bdf5f489f281c194afe 100644 --- a/ee/lib/gitlab/llm/chain/agents/zero_shot.rb +++ b/ee/lib/gitlab/llm/chain/agents/zero_shot.rb @@ -18,6 +18,7 @@ def initialize(user_input:, tools:, context:) @tools = tools @context = context @iterations = 0 + @logger = Gitlab::Llm::Logger.build end PROMPT_TEMPLATE = [ @@ -55,6 +56,7 @@ def execute input_variables[:agent_scratchpad] << answer.content.to_s << answer.suggestions.to_s tool_class = answer.tool + logger.debug(message: "Picked tool", tool: tool_class.to_s) tool = tool_class.new( context: context, @@ -76,6 +78,8 @@ def execute private + attr_reader :logger + # This method should not be memoized because the input variables change over time def prompt Utils::Prompt.no_role_text(PROMPT_TEMPLATE, input_variables) diff --git a/ee/lib/gitlab/llm/chain/answer.rb b/ee/lib/gitlab/llm/chain/answer.rb index 8e0b0909621cbed86875478daf9c81722a8a08e5..92fe0e6e38d9fd78eba3f990c60d22bfe6160373 100644 --- a/ee/lib/gitlab/llm/chain/answer.rb +++ b/ee/lib/gitlab/llm/chain/answer.rb @@ -31,6 +31,8 @@ def self.from_response(response_body:, tools:, context:) return final_answer(context: context, content: default_final_answer) unless tool + logger.debug(message: "Answer", content: content) + new( status: :ok, context: context, @@ -42,6 +44,8 @@ def self.from_response(response_body:, tools:, context:) end def self.final_answer(context:, content:) + logger.debug(message: "Final answer", content: content) + new( status: :ok, context: context, @@ -53,10 +57,14 @@ def self.final_answer(context:, content:) end def self.default_final_answer + logger.debug(message: "Default final answer") + s_("AI|I don't see how I can help. Please give better instructions!") end def self.error_answer(context:, content:) + logger.error(message: "Error", error: content) + new( status: :error, content: content, @@ -65,6 +73,10 @@ def self.error_answer(context:, content:) is_final: true ) end + + private_class_method def self.logger + Gitlab::Llm::Logger.build + end end end end diff --git a/ee/lib/gitlab/llm/chain/tools/issue_identifier.rb b/ee/lib/gitlab/llm/chain/tools/issue_identifier.rb index f5c1a3b9434ffd297e1089abc062de68eb6a4df2..686003545b69b647ce3fd391172c217555caf27a 100644 --- a/ee/lib/gitlab/llm/chain/tools/issue_identifier.rb +++ b/ee/lib/gitlab/llm/chain/tools/issue_identifier.rb @@ -80,7 +80,9 @@ def execute return already_identified_answer if already_identified? MAX_RETRIES.times do + logger.debug(message: "Prompt", class: self.class.to_s, content: prompt) response = request(prompt) + json = extract_json(response) issue = identify_issue(json[:ResourceIdentifierType], json[:ResourceIdentifier]) @@ -91,15 +93,20 @@ def execute context.resource = issue content = "I now have the JSON information about the issue ##{issue.iid}." + + logger.debug(message: "Answer", class: self.class.to_s, content: content) return Answer.new(status: :ok, context: context, content: content, tool: nil) rescue JSON::ParserError # try to help out AI to fix the JSON format by adding the error as an observation self.retries += 1 error_message = "\nObservation: JSON has an invalid format. Please retry" + logger.error(message: "Error", class: self.class.to_s, error: error_message) + options[:suggestions] += error_message - rescue StandardError - # todo: add exception logging + rescue StandardError => e + logger.error(message: "Error", error: e.message, class: self.class.to_s) + return Answer.error_answer(context: context, content: _("Unexpected error")) end @@ -170,6 +177,7 @@ def prompt def already_identified_answer resource = context.resource content = "You already have identified the issue ##{resource.iid}, read carefully." + logger.debug(message: "Answer", class: self.class.to_s, content: content) ::Gitlab::Llm::Chain::Answer.new( status: :ok, context: context, content: content, tool: nil, is_final: false diff --git a/ee/lib/gitlab/llm/chain/tools/summarize_comments.rb b/ee/lib/gitlab/llm/chain/tools/summarize_comments.rb index c2f1345c821201b6dba8cb15c96352cde1ba1423..881252450572caf96e57bcc5a64eac75b9bbe293 100644 --- a/ee/lib/gitlab/llm/chain/tools/summarize_comments.rb +++ b/ee/lib/gitlab/llm/chain/tools/summarize_comments.rb @@ -23,6 +23,7 @@ def execute "#{resource_name(resource)} ##{resource.iid} has no comments to be summarized." end + logger.debug(message: "Answer", class: self.class.to_s, content: content) ::Gitlab::Llm::Chain::Answer.new( status: :ok, context: context, content: content, tool: nil, is_final: false ) diff --git a/ee/lib/gitlab/llm/chain/tools/tool.rb b/ee/lib/gitlab/llm/chain/tools/tool.rb index a39cf174ef26847e51ef0fa3a21333afbd848554..14e23d1888769d0e9f5e365dd1f022883f5b970e 100644 --- a/ee/lib/gitlab/llm/chain/tools/tool.rb +++ b/ee/lib/gitlab/llm/chain/tools/tool.rb @@ -15,6 +15,7 @@ class Tool def initialize(context:, options:) @context = context @options = options + @logger = Gitlab::Llm::Logger.build end def execute @@ -36,6 +37,10 @@ def projects_from_context end end strong_memoize_attr :projects_from_context + + private + + attr_reader :logger end end end