blob: d0e72eddb0ce50e4b882c8e7f5bfaec946c6b4d0 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "peridot/bin/suggestion_engine/query_processor.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include "peridot/bin/suggestion_engine/suggestion_engine_helper.h"
#include "peridot/lib/fidl/json_xdr.h"
namespace modular {
namespace {
constexpr char kQueryContextKey[] = "/suggestion_engine/current_query";
} // namespace
QueryProcessor::QueryProcessor(std::shared_ptr<SuggestionDebugImpl> debug)
: debug_(debug) {}
QueryProcessor::~QueryProcessor() = default;
void QueryProcessor::Initialize(
fidl::InterfaceHandle<fuchsia::modular::ContextWriter> context_writer) {
context_writer_.Bind(std::move(context_writer));
}
void QueryProcessor::ExecuteQuery(
fuchsia::modular::UserInput input, int count,
fidl::InterfaceHandle<fuchsia::modular::QueryListener> listener) {
// Process:
// 1. Close out and clean up any existing query process
// 2. Update the context engine with the new query
// 3. Set up the ask variables in suggestion engine
// 4. Get suggestions from each of the QueryHandlers
// 5. Filter and Rank the suggestions as received
// 6. Send "done" to SuggestionListener
// Step 1
CleanUpPreviousQuery();
// Step 2
std::string query = input.text;
if (!query.empty() && context_writer_.is_bound()) {
// Update context engine
std::string formattedQuery;
XdrFilterType<std::string> filter_list[] = {
XdrFilter<std::string>,
nullptr,
};
XdrWrite(&formattedQuery, &query, filter_list);
context_writer_->WriteEntityTopic(kQueryContextKey, formattedQuery);
// Update suggestion engine debug interface
debug_->OnAskStart(query, &suggestions_);
}
// Steps 3 - 6
activity_ = debug_->GetIdleWaiter()->RegisterOngoingActivity();
active_query_ = std::make_unique<QueryRunner>(std::move(listener),
std::move(input), count);
active_query_->SetResponseCallback(
[this, input](const std::string& handler_url,
fuchsia::modular::QueryResponse response) {
OnQueryResponse(input, handler_url, std::move(response));
});
active_query_->SetEndRequestCallback(
[this, input] { OnQueryEndRequest(input); });
active_query_->Run(query_handlers_);
}
void QueryProcessor::RegisterQueryHandler(
fidl::StringPtr url, fidl::InterfaceHandle<fuchsia::modular::QueryHandler>
query_handler_handle) {
auto query_handler = query_handler_handle.Bind();
query_handlers_.emplace_back(std::move(query_handler), url);
fuchsia::modular::QueryHandlerPtr& handler = query_handlers_.back().handler;
handler.set_error_handler([this, ptr = handler.get()](zx_status_t status) {
auto it = std::find_if(query_handlers_.begin(), query_handlers_.end(),
[ptr](const QueryHandlerRecord& record) {
return record.handler.get() == ptr;
});
FXL_DCHECK(it != query_handlers_.end());
auto to_erase = query_handlers_.end() - 1;
std::iter_swap(it, to_erase);
query_handlers_.erase(to_erase);
});
}
void QueryProcessor::SetFilters(
std::vector<std::unique_ptr<SuggestionActiveFilter>>&& active_filters,
std::vector<std::unique_ptr<SuggestionPassiveFilter>>&& passive_filters) {
suggestions_.SetActiveFilters(std::move(active_filters));
suggestions_.SetPassiveFilters(std::move(passive_filters));
}
void QueryProcessor::SetRanker(std::unique_ptr<Ranker> ranker) {
suggestions_.SetRanker(std::move(ranker));
}
RankedSuggestion* QueryProcessor::GetSuggestion(
const std::string& suggestion_uuid) const {
return suggestions_.GetSuggestion(suggestion_uuid);
}
void QueryProcessor::CleanUpPreviousQuery() {
active_query_.reset();
suggestions_.RemoveAllSuggestions();
}
void QueryProcessor::AddProposal(const std::string& source_url,
fuchsia::modular::Proposal proposal) {
suggestions_.RemoveProposal(source_url, proposal.id);
SuggestionPrototype* suggestion =
CreateSuggestionPrototype(&query_prototypes_, source_url,
"" /* Emtpy story_id */, std::move(proposal));
suggestions_.AddSuggestion(suggestion);
}
void QueryProcessor::OnQueryResponse(fuchsia::modular::UserInput input,
const std::string& handler_url,
fuchsia::modular::QueryResponse response) {
// Ranking currently happens as each set of proposals are added.
for (size_t i = 0; i < response.proposals.size(); ++i) {
AddProposal(handler_url, std::move(response.proposals.at(i)));
}
suggestions_.Refresh(input);
// Update the fuchsia::modular::QueryListener with new results
NotifyOfResults();
// Update the suggestion engine debug interface
debug_->OnAskStart(input.text, &suggestions_);
}
void QueryProcessor::OnQueryEndRequest(fuchsia::modular::UserInput input) {
debug_->OnAskStart(input.text, &suggestions_);
// Idle immediately, we no longer handle audio responses here.
activity_ = nullptr;
}
void QueryProcessor::NotifyOfResults() {
const auto& suggestion_vector = suggestions_.Get();
std::vector<fuchsia::modular::Suggestion> window;
for (size_t i = 0;
i < active_query_->max_results() && i < suggestion_vector.size(); i++) {
window.push_back(CreateSuggestion(*suggestion_vector[i]));
}
if (!window.empty()) {
active_query_->listener()->OnQueryResults(std::move(window));
}
}
} // namespace modular