Program Listing for File llm_context.cpp#

Return to documentation for file (python/morpheus_llm/morpheus_llm/_lib/src/llm/llm_context.cpp)

/*
 * SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "morpheus_llm/llm/llm_context.hpp"

#include "morpheus/messages/control.hpp"       // IWYU pragma: keep
#include "morpheus/utilities/string_util.hpp"  // for MORPHEUS_CONCAT_STR

#include <nlohmann/json.hpp>  // for basic_json, json

#include <algorithm>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <utility>

namespace morpheus::llm {

LLMContext::LLMContext() : m_state(std::make_shared<LLMContextState>()) {}

LLMContext::LLMContext(LLMTask task, std::shared_ptr<ControlMessage> message) : LLMContext()
{
    m_state->task    = std::move(task);
    m_state->message = std::move(message);
}

LLMContext::LLMContext(std::shared_ptr<LLMContext> parent, std::string name, input_mappings_t inputs) :
  m_parent(std::move(parent)),
  m_name(std::move(name)),
  m_inputs(std::move(inputs))
{}

LLMContext::~LLMContext() = default;

std::shared_ptr<LLMContext> LLMContext::parent() const
{
    return m_parent;
}

const std::string& LLMContext::name() const
{
    return m_name;
}

const input_mappings_t& LLMContext::input_map() const
{
    return m_inputs;
}

const LLMTask& LLMContext::task() const
{
    if (m_parent)
    {
        return m_parent->task();
    }

    return m_state->task;
}

std::shared_ptr<ControlMessage>& LLMContext::message() const
{
    if (m_parent)
    {
        return m_parent->message();
    }

    return m_state->message;
}

const utilities::json_t& LLMContext::all_outputs() const
{
    return m_outputs;
}

std::string LLMContext::full_name() const
{
    // Determine the full name
    if (m_parent)
    {
        return m_parent->full_name() + "/" + m_name;
    }

    // If we dont have a parent, we are the root context. So return nothing
    return "";
}

std::shared_ptr<LLMContext> LLMContext::push(std::string name, input_mappings_t inputs)
{
    return std::make_shared<LLMContext>(this->shared_from_this(), std::move(name), std::move(inputs));
}

void LLMContext::pop()
{
    // Copy the outputs from the child context to the parent
    if (m_output_names.empty())
    {
        // Use them all by default
        m_parent->set_output(m_name, std::move(m_outputs));
    }
    else if (m_output_names.size() == 1)
    {
        // Treat only a single output as the output
        m_parent->set_output(m_name, std::move(m_outputs[m_output_names[0]]));
    }
    else
    {
        // Build a new json object with only the specified keys
        utilities::json_t new_outputs;

        for (const auto& output_name : m_output_names)
        {
            new_outputs[output_name] = m_outputs[output_name];
        }

        m_parent->set_output(m_name, std::move(new_outputs));
    }
}

const utilities::json_t& LLMContext::get_input() const
{
    if (m_inputs.size() > 1)
    {
        throw std::runtime_error(
            "LLMContext::get_input() called on a context with multiple inputs. Use get_input(input_name) instead.");
    }

    return this->get_input(m_inputs[0].internal_name);
}

const utilities::json_t& LLMContext::get_input(const std::string& node_name) const
{
    if (node_name[0] == '/')
    {
        nlohmann::json::json_pointer node_json_ptr(node_name);

        if (!m_outputs.contains(node_json_ptr))
        {
            throw std::runtime_error(MORPHEUS_CONCAT_STR("Input '" << node_name << "' not found in the output map"));
        }

        // Get the value from a sibling output
        return m_outputs[node_json_ptr];
    }
    else
    {
        // Must be on the parent, so find the mapping between this namespace and the parent
        auto found = std::find_if(m_inputs.begin(), m_inputs.end(), [&node_name](const auto& map_iterator) {
            return map_iterator.internal_name == node_name;
        });

        if (found == m_inputs.end())
        {
            std::stringstream error_msg;
            error_msg << "Input '" << node_name << "' not found in the input list.";

            if (!m_inputs.empty())
            {
                error_msg << " Available inputs are:";
                for (const auto& input : m_inputs)
                {
                    error_msg << " '" << input.internal_name << "'";
                }
            }
            else
            {
                error_msg << " Input list is empty.";
            }

            throw std::runtime_error(error_msg.str());
        }

        auto& input_name = found->external_name;

        // Get the value from a parent output
        return m_parent->get_input(input_name);
    }
}

utilities::json_t LLMContext::get_inputs() const
{
    utilities::json_t inputs = nlohmann::json::object();

    for (const auto& in_map : m_inputs)
    {
        inputs[in_map.internal_name] = this->get_input(in_map.internal_name);
    }

    return inputs;
}

void LLMContext::set_output(utilities::json_t outputs)
{
    m_outputs = std::move(outputs);
    this->outputs_complete();
}

void LLMContext::set_output(const std::string& output_name, utilities::json_t output)
{
    m_outputs[output_name] = std::move(output);
}

void LLMContext::set_output_names(std::vector<std::string> output_names)
{
    m_output_names = std::move(output_names);
}

void LLMContext::outputs_complete()
{
    // m_outputs_promise.set_value();
}

const utilities::json_t& LLMContext::view_outputs() const
{
    // // Wait for the outputs to be available
    // m_outputs_future.wait();

    return m_outputs;
}

}  // namespace morpheus::llm