87 lines
3.4 KiB
C++
87 lines
3.4 KiB
C++
#pragma once
|
|
// CapModelCatalog: which CAP agents exist on this machine and which models each
|
|
// of them offers, so `@agent` and `@agent:model` mentions resolve in one place.
|
|
//
|
|
// Model discovery protocol (docs/cap.md "Model catalog"):
|
|
// 1. `[models] list = [...]` and `default = "..."` in the agent manifest.
|
|
// 2. `[models] probe = ["bin", "--cap-models"]` (or `capabilities.models = true`
|
|
// for `<binary> --cap-models`): the agent prints a JSON array of ids or
|
|
// objects {id|name|model}, or {"models": [...], "default": "..."}, or one id
|
|
// per line. Probes are bounded by CAP_MODEL_PROBE_TIMEOUT seconds.
|
|
// Every failure lands in the agent's entry (status, error); nothing throws out.
|
|
#include <functional>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "cap_registry.h"
|
|
|
|
namespace detz::cap {
|
|
|
|
inline constexpr double CAP_MODEL_PROBE_TIMEOUT = 5.0; // seconds per probe
|
|
inline constexpr size_t CAP_MODEL_PROBE_MAX_BYTES = 1u << 20;
|
|
inline constexpr size_t CAP_MODEL_MAX = 500; // models kept per agent
|
|
inline constexpr size_t CAP_AGENT_MAX = 64; // agents kept in the catalog
|
|
inline constexpr double CAP_CATALOG_TTL = 60.0; // seconds an entry stays cached
|
|
|
|
struct CapAgentEntry {
|
|
std::string name;
|
|
std::string status; // ready | no_binary | bad_manifest | no_manifest
|
|
std::string error; // why status is not ready
|
|
std::string binary;
|
|
std::string manifestSource;
|
|
std::string binding;
|
|
std::vector<std::string> models;
|
|
std::string defaultModel;
|
|
std::string modelSource; // manifest | probe | manifest+probe | none
|
|
std::string modelError; // probe failure, empty when fine
|
|
double loadedAt{0};
|
|
bool ready() const { return status == "ready"; }
|
|
};
|
|
|
|
struct CapSelection {
|
|
bool ok{false};
|
|
std::string agent;
|
|
std::string model; // empty: the agent's own default
|
|
std::string note; // set when the model could not be verified
|
|
std::string error; // detailed report when !ok
|
|
};
|
|
|
|
class CapModelCatalog {
|
|
public:
|
|
// Runs argv, returns stdout; throws std::exception on failure or timeout.
|
|
using Prober = std::function<std::string(const std::vector<std::string>& argv, double timeout)>;
|
|
|
|
explicit CapModelCatalog(CapRegistry registry = CapRegistry(), Prober prober = nullptr);
|
|
|
|
std::vector<std::string> agentNames() const;
|
|
CapAgentEntry entry(const std::string& name);
|
|
std::vector<CapAgentEntry> entries();
|
|
CapManifest manifest(const std::string& name) const;
|
|
|
|
// True when the first word of `spec` (before ':') names a catalog agent.
|
|
bool isAgent(const std::string& spec) const;
|
|
// `agent`, `agent:model`, or a bare model id found on exactly one agent.
|
|
CapSelection resolve(const std::string& spec);
|
|
// Detailed per-agent listing used by every failure message.
|
|
std::string report();
|
|
static std::string formatReport(const std::vector<CapAgentEntry>& all);
|
|
|
|
void invalidate();
|
|
|
|
private:
|
|
static std::string defaultProbe(const std::vector<std::string>& argv, double timeout);
|
|
CapAgentEntry load(const std::string& name) const;
|
|
void probeInto(CapAgentEntry& e, const CapManifest& m) const;
|
|
static std::string match(const CapAgentEntry& e, const std::string& want, std::string& problem);
|
|
std::string findName(const std::string& word) const;
|
|
|
|
CapRegistry registry_;
|
|
Prober prober_;
|
|
std::mutex mu_;
|
|
std::map<std::string, CapAgentEntry> cache_;
|
|
};
|
|
|
|
} // namespace detz::cap
|