# frozen_string_literal: true module DiscourseAi module AiBot module Tools class UpdateArtifact < Tool def self.name "update_artifact" end # this is not working that well, we support it, but I am leaving it dormant for now def self.unified_diff_tip <<~TIP When updating and artifact in diff mode unified diffs can be applied: If editing:
Some text
Some text
+Some new text
Some new text
Some text
+Some new text
@@ -5,3 +5,3 @@ - +more text
If you supply text without @@ seperators or + and - prefixes, the entire text will be appended to the artifact section. TIP end def self.signature { name: "update_artifact", description: "Updates an existing web artifact with new HTML, CSS, or JavaScript content. Note either html, css, or js MUST be provided. You may provide all three if desired.", parameters: [ { name: "artifact_id", description: "The ID of the artifact to update", type: "integer", required: true, }, { name: "html", description: "new HTML content for the artifact", type: "string" }, { name: "css", description: "new CSS content for the artifact", type: "string" }, { name: "js", description: "new JavaScript content for the artifact", type: "string", }, { name: "change_description", description: "A brief description of the changes being made. Note: This only documents the change - you must provide the actual content in html/css/js parameters to make changes.", type: "string", }, ], } end def self.allow_partial_tool_calls? true end def chain_next_response? @chain_next_response end def partial_invoke @selected_tab = :html if @prev_parameters @selected_tab = parameters.keys.find { |k| @prev_parameters[k] != parameters[k] } end update_custom_html @prev_parameters = parameters.dup end def invoke yield "Updating Artifact" post = Post.find_by(id: context[:post_id]) return error_response("No post context found") unless post artifact = AiArtifact.find_by(id: parameters[:artifact_id]) return error_response("Artifact not found") unless artifact if artifact.post.topic.id != post.topic.id return error_response("Attempting to update an artifact you are not allowed to") end last_version = artifact.versions.order(version_number: :desc).first begin version = artifact.create_new_version( html: parameters[:html] || last_version&.html || artifact.html, css: parameters[:css] || last_version&.css || artifact.css, js: parameters[:js] || last_version&.js || artifact.js, change_description: parameters[:change_description].to_s, ) update_custom_html(artifact, version) success_response(artifact, version) rescue DiscourseAi::Utils::DiffUtils::DiffError => e error_response(e.to_llm_message) rescue => e error_response(e.message) end end private def update_custom_html(artifact = nil, version = nil) content = [] if parameters[:html].present? content << [:html, "### HTML Changes\n\n```html\n#{parameters[:html]}\n```"] end if parameters[:css].present? content << [:css, "### CSS Changes\n\n```css\n#{parameters[:css]}\n```"] end if parameters[:js].present? content << [:js, "### JavaScript Changes\n\n```javascript\n#{parameters[:js]}\n```"] end if parameters[:change_description].present? content.unshift( [:description, "### Change Description\n\n#{parameters[:change_description]}"], ) end content.sort_by! { |c| c[0] === @selected_tab ? 1 : 0 } if !artifact if artifact content.unshift([nil, "[details='#{I18n.t("discourse_ai.ai_artifact.view_changes")}']"]) content << [nil, "[/details]"] content << [ :preview, "### Preview\n\n", ] end content.unshift("\n\n") self.custom_raw = content.map { |c| c[1] }.join("\n\n") end def success_response(artifact, version) @chain_next_response = false hash = { status: "success", artifact_id: artifact.id, version: version.version_number, message: "Artifact updated successfully and rendered to user.", } hash end def error_response(message) @chain_next_response = true self.custom_raw = "" { status: "error", error: message } end def help "Updates an existing web artifact with changes to its HTML, CSS, or JavaScript content. " \ "Requires the artifact ID and at least one change diff. " \ "Changes are applied using unified diff format. " \ "A description of the changes is required for version history." end end end end end