Showing insights with a better foundation
This commit is contained in:
@@ -42,6 +42,8 @@ class CommandsController < ApplicationController
|
||||
redirect_to result.url
|
||||
when Command::Result::ChatResponse
|
||||
respond_with_chat_response(result)
|
||||
when Command::Result::InsightResponse
|
||||
respond_with_insight_response(result)
|
||||
else
|
||||
redirect_back_or_to root_path
|
||||
end
|
||||
@@ -51,8 +53,8 @@ class CommandsController < ApplicationController
|
||||
command = chat_response_to_command(result)
|
||||
|
||||
if confirmed?(command)
|
||||
command.execute
|
||||
redirect_back_or_to root_path
|
||||
result = command.execute
|
||||
respond_with_execution_result result
|
||||
else
|
||||
respond_with_needs_confirmation(command.commands, redirect_to: result.context_url)
|
||||
end
|
||||
@@ -67,4 +69,8 @@ class CommandsController < ApplicationController
|
||||
parser = Command::Parser.new(context)
|
||||
Command::Composite.new(chat_response.command_lines.collect { parser.parse it })
|
||||
end
|
||||
|
||||
def respond_with_insight_response(chat_response)
|
||||
render json: { message: chat_response.content }, status: :accepted
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,10 +44,16 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
handleCommandResponse(event) {
|
||||
const response = event.detail.fetchResponse?.response
|
||||
|
||||
if (event.detail.success) {
|
||||
if (response.headers.get("Content-Type")?.includes("application/json")) {
|
||||
response.json().then((responseJson) => {
|
||||
this.element.querySelector("#chat-insight").innerHTML = marked.parse(responseJson.message)
|
||||
})
|
||||
}
|
||||
this.#reset()
|
||||
} else {
|
||||
const response = event.detail.fetchResponse.response
|
||||
} else if (response) {
|
||||
this.#handleErrorResponse(response)
|
||||
}
|
||||
}
|
||||
@@ -88,7 +94,6 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
#reset(inputValue = "") {
|
||||
console.debug("RESET!")
|
||||
this.inputTarget.value = inputValue
|
||||
this.confirmationTarget.value = ""
|
||||
this.waitingForConfirmationValue = false
|
||||
@@ -104,7 +109,6 @@ export default class extends Controller {
|
||||
|
||||
async #requestConfirmation(jsonResponse) {
|
||||
const message = jsonResponse.commands[0]
|
||||
console.debug("request confirmation", this.inputTarget.value);
|
||||
this.originalInputValue = this.inputTarget.value
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ class Command::ChatQuery < Command
|
||||
|
||||
def execute
|
||||
response = chat.ask query
|
||||
generated_commands = replace_names_with_ids(JSON.parse(response.content))
|
||||
generated_commands = replace_names_with_ids(JSON.parse(response.content)).tap do |commands|
|
||||
Rails.logger.info "*** #{commands}"
|
||||
end
|
||||
build_chat_response_with generated_commands
|
||||
end
|
||||
|
||||
@@ -21,7 +23,7 @@ class Command::ChatQuery < Command
|
||||
# - Don't generate initial /search if not requested. "Assign to JZ" should
|
||||
def prompt
|
||||
<<~PROMPT
|
||||
You are a helpful assistant that translates natural language into commands that Fizzy understand.
|
||||
You are a helpful assistant that translates natural language into one or more commands that Fizzy understand.
|
||||
|
||||
Fizzy supports the following commands:
|
||||
|
||||
@@ -30,7 +32,8 @@ class Command::ChatQuery < Command
|
||||
- Tag cards: /tag [tag-name]. E.g: "/tag performance"
|
||||
- Get insight about cards: /insight [query]. Use this as the default command to satisfy questions and requests
|
||||
about cards. This relies on /search. Example: "/insight summarize performance issues".
|
||||
- Search cards based on certain keywords: /search. See how this works below. E.g: "/search meetup montreal"
|
||||
- Search cards based on certain keywords: /search. See how this works below. E.g: "/search meetup montreal". IMPORTANT: There can
|
||||
only be one /search command in the response.
|
||||
|
||||
If you need to filter a certain set of cards, you can use the /search command to filter.
|
||||
|
||||
@@ -56,14 +59,26 @@ class Command::ChatQuery < Command
|
||||
|
||||
{ command: "/search", indexed_by: "closed", collection_ids: [ "Writebook", "Design" ] }
|
||||
|
||||
Notice that there are overlapping commands (filter by assignee or assign cards). Favor filtering/queries for
|
||||
commands like "cards assigned to someone".
|
||||
Notice that there are similar commands to filter and act on cards (e.g: filter by assignee or assign cards). Favor
|
||||
filtering/queries for commands like "cards assigned to someone".
|
||||
|
||||
For example, to assign a card, you invoke `assign kevin`. For insight about "something", you invoke "/insight something".
|
||||
|
||||
Important: When using the /insight command, ALWAYS add first a /search command that filters out the relevant cards to answer
|
||||
Important:
|
||||
|
||||
- When using the /insight command, consider adding first a /search command that filters out the relevant cards to answer
|
||||
the question. If there are relevant keywords to filter, pass those to /search but avoid passing generic ones. Then, reformulate
|
||||
pass the query itself VERBATIM to /insight as in "/insight <original query>", no additional keys in the JSON.
|
||||
- Only add an /insight command is there is a specific question about the data. Some requests are just about searching some
|
||||
cards. Those are fine.
|
||||
- Sometimes, the current context is a given card or the set of cards in the screen. In that case, there is no need to add a
|
||||
/search command. Assume that's the case if it's missing any description of the the set of cards required. E.g: just "summarise"
|
||||
or "this card".
|
||||
- A response can only contain ONE /search command AT MOST.
|
||||
- A response can only contain ONE /insight command AT MOST.
|
||||
- Unless asking for explicit filtering, always prefer /insight over /search. When asking about "cards" with properties that can
|
||||
be satisfied with /search, then use /search.
|
||||
|
||||
|
||||
For example, for "summarize performance issues", the JSON could be:
|
||||
|
||||
@@ -77,10 +92,6 @@ class Command::ChatQuery < Command
|
||||
}
|
||||
]
|
||||
|
||||
Unless asking for explicit filtering, always prefer /insight over /search. When asking about "cards" with properties that can
|
||||
be satisfied with /search, then use /search.
|
||||
|
||||
A response can contain at most one /search command.
|
||||
|
||||
Please combine commands to satisfy what the user needs. E.g: search with keywords and filters and then apply
|
||||
as many commands as needed. Make sure you don't leave actions mentioned in the query needs unattended.'
|
||||
@@ -89,6 +100,8 @@ class Command::ChatQuery < Command
|
||||
/assign don't support additional JSON keys, they will only contain the "command:" key". For /search, it can contain additional
|
||||
JSON keys matching the /search params described above.
|
||||
|
||||
Don't generate any /search without params. Just don't add it.
|
||||
|
||||
Avoid empty preambles like "Based on the provided cards". Also, prefer a natural a friendly language favoring active voice.
|
||||
|
||||
Make sure to place into double quotes the strings in JSON values and that you generate valid JSON. I want a
|
||||
@@ -122,11 +135,17 @@ class Command::ChatQuery < Command
|
||||
end
|
||||
|
||||
def response_command_lines_from(generated_commands)
|
||||
generated_commands.filter { it["command"] != "/search" }.collect { it["command"] }
|
||||
# We translate standalone /search commands as redirections to execute. Otherwise, they
|
||||
# will be excluded out from the commands to run, as they represent the context url.
|
||||
if generated_commands.size == 1
|
||||
[ "/visit #{cards_path(**generated_commands.first.without("command"))}" ]
|
||||
else
|
||||
generated_commands.filter { it["command"] != "/search" }.collect { it["command"] }
|
||||
end
|
||||
end
|
||||
|
||||
def response_context_url_from(generated_commands)
|
||||
if search_command = generated_commands.find { it["command"] == "/search" }
|
||||
if generated_commands.size > 1 && search_command = generated_commands.find { it["command"] == "/search" }
|
||||
cards_path(**search_command.without("command"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,9 +11,11 @@ class Command::Composite
|
||||
end
|
||||
|
||||
def execute
|
||||
result = nil
|
||||
ApplicationRecord.transaction do
|
||||
commands.each(&:execute)
|
||||
commands.each { result = it.execute }
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def undo
|
||||
|
||||
@@ -9,7 +9,7 @@ class Command::GetInsight < Command
|
||||
|
||||
def execute
|
||||
response = chat.ask query
|
||||
Command::Result::InsightResponse.new({ reply: response.content })
|
||||
Command::Result::InsightResponse.new(response.content)
|
||||
end
|
||||
|
||||
def undoable?
|
||||
@@ -59,6 +59,7 @@ class Command::GetInsight < Command
|
||||
|
||||
def card_context_for(card)
|
||||
<<~CONTEXT
|
||||
==CARD==
|
||||
Title: #{card.title}
|
||||
Card created by: #{card.creator.name}}
|
||||
Id: #{card.id}
|
||||
@@ -73,7 +74,7 @@ class Command::GetInsight < Command
|
||||
|
||||
def comment_context_for(comment)
|
||||
<<~CONTEXT
|
||||
Card: #{comment.card.id}
|
||||
==COMMENT==
|
||||
Id: #{comment.id}
|
||||
Content: #{comment.body.to_plain_text}}
|
||||
Comment created by: #{comment.creator.name}}
|
||||
|
||||
@@ -31,6 +31,8 @@ class Command::Parser
|
||||
Command::ClearFilters.new(params: filter.as_params)
|
||||
when "/close"
|
||||
Command::Close.new(card_ids: cards.ids, reason: command_arguments.join(" "))
|
||||
when "/visit"
|
||||
Command::VisitUrl.new(url: command_arguments.first)
|
||||
when "/tag"
|
||||
Command::Tag.new(tag_title: tag_title_from(command_arguments.join(" ")), card_ids: cards.ids)
|
||||
else
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class Command::VisitUrl < Command
|
||||
store_accessor :data, :url
|
||||
|
||||
def title
|
||||
"Visit #{url}"
|
||||
end
|
||||
|
||||
def execute
|
||||
redirect_to url
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user