BareGit
#pragma once

#include <chrono>
#include <functional>
#include <optional>
#include <string>
#include <vector>

#include <mw/http_client_async.hpp>
#include <mw/url.hpp>
#include <nlohmann/json.hpp>

#include "message.hpp"
#include "runtime.hpp"
#include "stream_event.hpp"
#include "tool.hpp"

namespace agent_smith
{

/// Sampling controls sent to an OpenAI-compatible chat-completions endpoint.
struct SamplingParams
{
    /// Sampling temperature, when the provider should receive one.
    std::optional<double> temperature;

    /// Nucleus-sampling threshold, when the provider should receive one.
    std::optional<double> top_p;

    /// Maximum number of tokens the provider may generate.
    std::optional<int> max_tokens;

    /// Sequences that cause generation to stop.
    std::vector<std::string> stop;

    /// Opaque structured-output or JSON-mode configuration.
    nlohmann::json response_format = nullptr;

    /// Provider-specific request fields outside the standard API surface.
    nlohmann::json extra = nullptr;
};

/// Connection and request defaults for an LLM client.
struct LlmClientConfig
{
    /// Base URL of the OpenAI-compatible endpoint.
    mw::URL endpoint;

    /// Provider model identifier.
    std::string model;

    /// Optional bearer token for the endpoint.
    std::optional<std::string> api_key;

    /// Requested maximum duration of one HTTP transfer.
    std::chrono::seconds request_timeout{60};

    /// Number of retry attempts after the initial request.
    int max_retries = 3;

    /// Sampling values used when a request does not override them.
    SamplingParams default_sampling;
};

/// One non-streaming LLM completion request.
struct LlmRequest
{
    /// Ordered conversation history sent to the model.
    std::vector<Message> messages;

    /// Function schemas made available to the model for this request.
    std::vector<ToolSchema> tools;

    /// Sampling values that override the client's defaults.
    SamplingParams sampling;
    /// Cooperative cancellation checked before requests and streamed chunks.
    CancellationToken cancel;
};

/// The final assistant message returned by an LLM completion request.
struct LlmResponse
{
    /// Parsed assistant response, including usage when supplied by the server.
    AssistantMessage message;
};

/// Calls an OpenAI-compatible chat-completions endpoint.
class LlmClient
{
public:
    /// Construct a client that uses the runtime's shared HTTP session.
    LlmClient(Runtime& runtime, LlmClientConfig config);

    /// Construct a client with an HTTP session override, primarily for tests.
    LlmClient(Runtime& runtime, mw::HTTPSessionAsyncInterface& http,
              LlmClientConfig config);

    /// Send a non-streaming request to `/v1/chat/completions`.
    mw::Task<mw::E<LlmResponse>> complete(LlmRequest request);

    /// Send a streaming request and synchronously deliver typed stream events.
    mw::Task<mw::E<LlmResponse>>
    completeStream(LlmRequest request,
                   std::function<void(const StreamEvent&)> on_event);

private:
    Runtime* runtime_;
    mw::HTTPSessionAsyncInterface* http_;
    LlmClientConfig config_;
};

} // namespace agent_smith