Skip to content

Design: managed metrics collection - #363

Open
dennis-upbound wants to merge 19 commits into
modelplaneai:mainfrom
dennis-upbound:design/metrics
Open

Design: managed metrics collection#363
dennis-upbound wants to merge 19 commits into
modelplaneai:mainfrom
dennis-upbound:design/metrics

Conversation

@dennis-upbound

@dennis-upbound dennis-upbound commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Description of your changes

A design doc, no code. Addresses #269.

Collecting a deployment's metrics is hand-wired today. vLLM exposes /metrics on its serving port, the serving stack runs a Prometheus per workload cluster with open PodMonitor discovery, and an operator writes a PodMonitor by hand, keeps it in sync with the serving shape, and removes it on teardown. Each engine also names its metrics its own way, so an operator reading two deployments reads two vocabularies, and each cluster is an island they stitch together themselves.

The doc proposes four things:

Collect on every cluster, always on. Modelplane collects from every source it owns — engines, endpoint pickers, the substrate — with no per-deployment toggle. A cluster-wide selector on modelplane.ai/serving already spans standalone, leader/worker and prefill/decode, so the shape needs no special casing. Scraping the engine port by name rather than number also fixes a latent prefill/decode gap: the decode engine serves on 8001 because the pd-sidecar takes 8000, so a manual targetPort: 8000 scrapes the sidecar.

Normalize to modelplane_*. A MetricMapping kind carries the rename per engine, selected by an engine-type label rather than by detecting the engine — the same pattern GAIE's model-server-protocol uses and Modelplane's routing already depends on. Built-ins ship for vLLM, SGLang and Triton/TensorRT-LLM; a platform team applies one more for a forked engine, with no Modelplane release. An unmapped engine still gets scraped under its native names and Modelplane says so rather than guessing.

Aggregate to one view. Each cluster's collector pushes outbound to the control plane, so a workload cluster needs only egress and nothing inbound. The roll-up is computed in memory, so Modelplane runs no store and the control plane stays stateless, which is what lets it run in a Space.

Collect with OpenTelemetry, not a per-cluster Prometheus. The normalization target is already an OpenTelemetry standard, the rename happens in the pipeline instead of through per-cluster recording rules, and one pipeline carries metrics, the #77 traces, and logs.

Approving this means agreeing that normalization and aggregation are Modelplane's job rather than the platform team's, that collection is always on, and that the collector is OpenTelemetry.

Two earlier shapes are written up as rejected rather than dropped quietly, because both appeared in earlier revisions of this PR: a PodMonitor composed per replica, which buys nothing over one cluster-wide selector and composes N monitors where one does the job; and a per-deployment enabled toggle, which covers only the data plane and asks an MD author to opt in or out of collection the platform team consumes.

Status

The MetricMapping kind is implemented. Nothing consumes it yet — the collector this doc hands mappings to does not exist, so applying one reports Ready and changes no behaviour. The reading side also sits in its own compose-metric-mapping function rather than in compose-serving-stack as the doc describes, which is a difference to close rather than keep. Both are noted in the doc.

Where I want judgement

The engine port name (http against a dedicated metrics), disabling EPP metrics auth against composing the ClusterRole and token, and whether the fleet roll-up series are the right set. Two further open questions are flagged inline.

On upgrade, an existing hand-written PodMonitor has to be deleted or it double-scrapes alongside the composed one. That wants a release note.

I have:

  • Read and followed Modelplane's contribution process.
  • Run nix flake check (design doc only, no code paths)
  • Added or updated tests (design doc only)
  • Signed off every commit with git commit -s.

🤖 Generated with Claude Code

Collecting a deployment's metrics is hand-wired: an operator writes a
PodMonitor per deployment, keeps it in sync with the serving shape
(leader/worker, prefill/decode), and removes it on teardown. This design
has Modelplane compose the collection instead, per source it owns, the
engine and the endpoint picker where present, on by default with an
opt-out. It covers modelplaneai#269, with a section per source and a diagram.

Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Comment thread design/metrics.md Outdated

## Interaction with #264

The [#264](https://github.com/modelplaneai/modelplane/issues/264) example

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Release notes we need to add a line telling existing users to delete their hand-written podmonitor.yaml, or they get a duplicate scrape job against the same pods after upgrade.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, without deleting it they'd get a duplicate scrape of the same pods after upgrade. Called it out as a release note in the #264 interaction section. Pushed.

Comment thread design/metrics.md Outdated

- **Port name.** `http` (the serving port that also serves `/metrics`) versus
`metrics`. Leaning `http`, since it's the one serving port.
- **Configurability.** `interval` and `path` are fixed (30s, `/metrics`) for now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if 30s is too Long If it comes to KV Cache utilization e.g.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For routing this interval doesn't matter: the EPP scrapes engine /metrics on its own fast internal loop (~100ms) to make routing decisions, so routing isn't gated on it. This PodMonitor feeds Prometheus for dashboards and alerting, where 30s is a normal default. If finer KV series are wanted, interval is a knob on the metrics object (open question). Made that distinction explicit in What gets scraped.

Comment thread design/metrics.md Outdated
example). Nothing owns that wiring. The operator builds it by hand and keeps it in
sync with the deployment's shape. They delete it on teardown.

Instead, Modelplane composes the collection, per source:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is Monitoring for Cache / PVC here out of scope?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope here, yes, this doc is serving metrics (engine + EPP). ModelCache PVC and hydration observability is a separate concern that belongs with the ModelCache work. Added a scope line to the summary.

Comment thread design/metrics.md Outdated

### What gets scraped

The `PodMonitor` ingests everything the engine exposes on `/metrics`. What the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the expected series-per-pod ? is the Prometheus Stack configured with rolling / retention of the storage ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order of dozens of series per engine pod (request/latency histograms, KV-cache, throughput), plus the llm_d_epp_* set wherever an EPP runs. Retention and storage sizing are the serving stack's Prometheus config in compose-serving-stack, not this composition, worth a sane default there but out of scope for this doc. Noted it in What gets scraped.

Comment thread design/metrics.md Outdated
replicas: 1
template:
spec:
metrics:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider observability/monitoring as the object name if it's going to grow past scraping... later

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, and it lines up with #77 (ModelService.observability.traces). Renamed to spec.template.spec.observability.metrics.enabled so traces and logs can slot beside metrics later. (Metrics live on the deployment since we scrape its pods; #77's traces live on ModelService, different resource, same observability umbrella.) Pushed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by the reframe: after Nic's review the doc drops the per-deployment opt-out field entirely. Collection is now always-on at every layer (engine, EPP, serving-stack) with no opt-in or opt-out, feeding a central Modelplane Prometheus. So there's no longer a field to name under observability. Leaving this for context; the observability grouping question may come back if we add a deployment-level knob later.

Comment thread design/metrics.md Outdated
matchLabels:
modelplane.ai/serving: <replica-name>
podMetricsEndpoints:
- port: http

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is http the right name here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an open question in the doc (http vs metrics). Leaning http since it's the one serving port that also serves /metrics, but happy to go metrics if that reads clearer.

Align the opt-out field with modelplaneai#77 by nesting it at
observability.metrics.enabled, so traces and logs can join it later.
Clarify that the 30s scrape is an observability default and not a
routing input (the EPP scrapes engines on its own fast loop), that
cache/PVC observability is out of scope, and that an existing
hand-written PodMonitor must be deleted on upgrade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
@negz

negz commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

@dennis-upbound I think we need to step back and think about what we want to help folks monitor.

There's a few things you could monitor in Modelplane:

  1. Inference signal (data plane). vLLM /metrics, EPP llm_d_epp_*, Envoy. TTFT, tokens/sec, queue depth, KV-cache utilization, request/error rates per model. "Is my model serving well, and is it saturated?"
  2. Substrate health (the stack Modelplane installs on each workload cluster). Gateway up? cert-manager, LWS controller, NVIDIA DRA driver healthy? GPUs allocatable? "Is the machinery on this cluster working?"
  3. Control plane health (Modelplane itself). Crossplane reconcile rates/errors, function latency and panics, provider health, the fleet scheduler actually placing replicas, XR Ready/Synced. "Is the thing I operate working?"
  4. Fleet rollup. Across all clusters/deployments: total capacity, GPU utilization, how many deployments degraded, cost.

I think MD authors will care about 1. The rest feel more like platform team concerns.

This proposal helps with 1, but it feels incomplete. If I understand correctly, it configures the Prometheus instance we already deploy to each cluster to scrape ModelReplica (i.e. vLLM, EPP) metrics, but what then? How does the MD author see and consume those metrics? If the platform team needs to setup plumbing to actually expose the metrics somewhere the MD author can use them (e.g. a dashboard), why ask the MD author to opt-in (or out)?

My hunch is Modelplane should do something like:

  • Deploy a Prom per IC
  • Always setup PodMonitors for all MD bits (engines, EPPs, etc) - no opt-in or opt-out
  • Always setup PodMonitors for all serving stack components
  • Deploy a centralized Modelplane Prom instance
  • Have the per-IC instances feed into the centralized one, which also scrapes Crossplane metrics for Modelplane control plane health
  • Setup recording rules as appropriate

This'd give you one central Prom instance you can scrape to get your entire Modelplane deployment's metrics. Metrics you'd presumably feed onwards into your monitoring and alerting system of choice.

Adopt Nic's direction: step back to what's worth monitoring (data plane,
substrate, control plane, fleet roll-up), make PodMonitor collection
always-on at every layer instead of a per-deployment opt-out, and add a
central Modelplane Prometheus that the per-cluster instances feed and that
also scrapes the control plane. Drops the observability.metrics.enabled
toggle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
@dennis-upbound

Copy link
Copy Markdown
Collaborator Author

Completely agree, reframed the doc around this. It now opens with the four things to monitor (data plane / substrate / control plane / fleet roll-up), makes PodMonitor collection always-on at every layer (engine, EPP, serving-stack components) with no opt-in or opt-out, and adds a central Modelplane Prometheus that the per-IC instances feed (remote-write or federation) and that also scrapes Crossplane for control-plane health, with recording rules for the fleet roll-up.

The per-deployment opt-out field is dropped (now the first rejected alternative). Open questions left: remote-write vs federation, central retention sizing, and phasing (data-plane + substrate first, control-plane + roll-up after).

@negz

negz commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

reframed the doc around this

@dennis-upbound did you forget to push? 🤔 Doc looks unchanged to me.

Pull the design back from a central aggregated Prometheus to what this layer
should own: always-on PodMonitors inside each InferenceCluster and a Prometheus
URL on the cluster status, so the platform team can scrape or federate without
reaching into Modelplane internals. Leave cross-cluster aggregation and
control-plane monitoring to the platform team, as the main open question.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
@dennis-upbound

Copy link
Copy Markdown
Collaborator Author

Agree — pulled the scope to exactly this. The doc now composes collection always-on inside each IC (engines, EPPs, serving-stack), no MD-author opt-in/out, and exposes the cluster's Prometheus URL on the InferenceCluster status (status.metrics.prometheusURL) so the platform team can scrape or federate without reaching into internals. Cross-cluster aggregation and Crossplane monitoring stay with the platform team. I left the one-central-Prom-for-the-whole-deployment out of this proposal and flagged it as the main open question, so we can decide separately how far Modelplane takes it.

(Also: the doc genuinely hadn't updated when I said it had — I'd pushed to the wrong remote. Fixed now, the diff reflects the change.)

Comment thread design/metrics.md Outdated
Comment thread design/metrics.md Outdated
Comment thread design/metrics.md Outdated
Agree the per-replica composition earns nothing once collection is always-on,
so compose one cluster-wide PodMonitor per source in the serving stack instead.
State the proposal and what approving it covers up front, answer how the
per-cluster Prometheus is reached from outside (the platform team's existing
cross-cluster path; Modelplane only publishes the URL), and drop the opt-in
mention that was reacting to the earlier draft.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Reverse the earlier leave-aggregation-to-the-platform-team scope: aggregate
every cluster's metrics up to one Modelplane store at the control plane, with
recording rules rebranding every series under modelplane_* and a normalized
label set. Weigh two collection mechanisms, the incumbent Prometheus stack and
an OpenTelemetry collector, and lean toward the collector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
@dennis-upbound

Copy link
Copy Markdown
Collaborator Author

Revved the doc. It now aggregates every cluster's metrics up to one Modelplane-level view at the control plane, rebranded under modelplane_* with a normalized label set (engine, cluster, deployment, model), rather than stopping at per-cluster collection — that's now the first rejected alternative. It also weighs the per-cluster mechanism, the incumbent Prometheus stack versus an OpenTelemetry collector, and leans toward the collector (lighter, does the modelplane_* rename in-pipeline, and something we already run elsewhere). The mechanism choice is the main open question.

@negz

negz commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@dennis-upbound thanks! I like the direction. I think this needs some POC work before I can approve. Specifically I'd like to lock down:

  1. Do we use Prom or Otel collector?
  2. What metrics do we want to expose (i.e. what rewrite rules do we need)? What metrics are possible? Can we normalize metrics for different engines?

…rmalization

The doc left the collector as an open Prometheus-vs-OTel choice and asserted a
modelplane_* namespace without saying how metrics from an engine Modelplane
doesn't recognize become modelplane_* series. Commit to the OpenTelemetry
collector, with the reasons (the GenAI conventions are the naming target, the
rename runs in-pipeline, one pipeline carries metrics, traces, and logs). Add a
capture section: an engine exposes Prometheus /metrics, an engine-type label
picks a mapping from an extensible registry the way the GAIE picker already
selects one for routing, and an unmapped engine degrades to raw names rather
than a wrong guess. Add the normalized metric set and the per-engine mapping
table with TTFT, ITL, TPOT, and the prefill/decode split. Move the Prometheus
stack to a rejected alternative and re-tone throughout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
@dennis-upbound

Copy link
Copy Markdown
Collaborator Author

Locked both.

Collector: OpenTelemetry. The GenAI semantic conventions are the naming target, the rename runs in the collector's transform processor in-pipeline, and one pipeline carries metrics, traces (#77), and logs. A Prometheus stack would push the rename into per-cluster recording rules and stay metrics-only, so it's now a rejected alternative.

Metrics + normalization. Added the modelplane_* set and a per-engine mapping table (vLLM / SGLang / TRT-LLM), with TTFT, ITL, TPOT, and the prefill/decode split. On "can we normalize across engines": vLLM and SGLang map cleanly (near-identical names, both align to the OTel set); Triton/TRT-LLM exposes batch-manager stats, so those rows are derived — called out, not hidden.

On staying correct for an engine we don't recognize: an engine-type label picks the mapping, the same mechanism the GAIE picker already uses for routing, and an unmapped engine degrades to raw names rather than a wrong guess. See the new "Capture from an opaque engine" and "Normalize to modelplane_*" sections.

Still needs the POC to confirm the derived TRT-LLM rows and the collector's rename config, agreed.

… metrics

Add input and output sequence length to the normalized set, the two disaggregation
bottleneck signals (queued prefill tokens, in-flight decode KV tokens), and a note
that an autoscaler or SLA planner such as NVIDIA's Dynamo Planner reads these faster
than a dashboard, so the scrape interval is a knob. Add SLO attainment to the fleet
roll-up. Add a Cluster scheduler metrics section: the pod scheduler (kube-scheduler,
or a gang scheduler like NVIDIA KAI or Volcano) is captured the way an engine is, a
per-scheduler mapping normalized to modelplane_cluster_scheduler_*, with the name
reserving modelplane_fleet_scheduler_* for a future fleet scheduler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Fix five things a close read surfaced. A managed cluster's kube-scheduler runs in
the provider's control plane and may not be scrapable, so say so rather than claim
the collector reaches it. Drop the Dynamo-specific forward-pass-metrics label from
the general engine signal. Reword so the Dynamo Planner reads as the reference
pattern for a consumer of the normalized series, not a consumer of them. Add a
request-outcome row so the table matches the error rate the doc promises. Call the
control-plane scheduler the fleet scheduler now that a cluster scheduler exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Take a stance on each rather than leave it open. The central store is a single
Prometheus-compatible instance, moving to a horizontally scaled backend such as
Mimir when one instance can't hold the fleet. The mapping registry is a ConfigMap
the collector reads, with a CRD reserved for outside authors who need validation.
The metrics port is http, since it is the one serving port. Drop the Open
questions section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
The store is one Prometheus-compatible instance with a short retention window,
and scaling it horizontally is out of scope, so drop the Mimir path. Move the
mapping registry from a hand-edited ConfigMap to the serving-stack Composition,
which renders the collector's config and versions the mappings with the package;
a platform team extends the set through composition input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Say plainly what a mapping is, a selector plus the engine's source names, the
modelplane_* name each becomes, and the labels, and show a concrete vLLM
MetricMapping. Make the registry first-class Modelplane resources rather than a
ConfigMap or an EnvironmentConfig: compose-serving-stack reads every MetricMapping
as a required resource, the way compose-model-deployment reads InferenceCluster
and ModelCache, and renders them into the collector's config. A new engine is a
new MetricMapping, validated on apply and discoverable, with no fork and no
release. Schedulers use the same kind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tor-scoped rename

The picker and collector share the engine-type label, not a mapping registry, so
say the label serves both rather than claim one shared registry. Add a tokens_total
row so the table delivers the tokens-per-second the doc lists under what to monitor.
State that a MetricMapping's rename applies only to metrics from pods its selector
matches, which is what the selector is for.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
…osticism

Push is the default connectivity: each collector remote-writes or OTLP-exports
outbound, so the workload cluster needs only egress and it works across regions
and firewalls, with pull kept for when the center already reaches the cluster.
Modelplane needs no persistent store: the control-plane collector rolls up in
memory, a short-retention in-memory Prometheus handles PromQL and quantile
roll-ups with no volume so it runs in a Space, and durable storage is the
operator's own backend. State that the picker routes any engine and only its
KV- and queue-aware scoring needs the standard metrics, degrading without them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Every fleet roll-up is a spatial aggregation the OTel collector does in memory:
it sums gauges and counters across clusters and merges histograms, and SLO
attainment is a ratio of buckets when a boundary sits at the target. Computing a
percentile value or an ad-hoc query is read-time work for whatever consumes the
export, so Modelplane runs no store and the control plane stays stateless, which
is what runs in a Space. Removes the short-retention Prometheus the earlier draft
kept for quantiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
After moving to a stateless collector, three store references were left dangling.
The summary said series roll up to a single store, which contradicts runs no
store, so it now says one view. The PodMonitor-per-replica alternative said
cluster-wide store where it meant collector. The Prometheus-stack alternative
implied our design keeps a central store, now scoped to an operator that wants
one. Add cost to the aggregate roll-up list so it matches what to monitor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dennis Ramdass <dennis@upbound.io>
The doc read as entirely unbuilt while the MetricMapping kind has since been
implemented, so a reader could not tell which parts describe a proposal and which
describe code.

Note it in the status line and next to the section that introduces the kind, with
the two caveats that matter for reading the rest: nothing consumes a MetricMapping
yet, because the collector this hands them to does not exist, so applying one
reports Ready and changes no behaviour; and the reading side sits in its own
function rather than in compose-serving-stack as the doc describes, which is a
difference to close rather than keep.

Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Engine metric names are already namespaced, so a flat name-to-name map would
rename them unambiguously with no selector at all, and the field reads redundant.
It is not, and the reasons were nowhere in the doc, so the next reader is one step
from removing it.

Four of them. The degradation this section promises is label-based by
construction: reporting "no mapping for X" means reading a pod's claimed engine
and finding none, which name matching cannot tell from a successful rename of
nothing. The consistent label set is per pod rather than per series, so name
matching cannot label the series a mapping does not rename. A forked engine emits
the upstream names while needing its own mapping, and two mappings matching one
name cannot be told apart without the pod. And kube-scheduler's names carry no
vendor prefix at all, so the scheduler section needs the selector most.

Note the collector consequence while it is fresh: the rename is an OTTL transform
gated on a resource attribute, not the simpler metrics-transform processor, which
matches on metric name only.

Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Two things in the doc are no longer accurate now that the collector exists and
has been run against a real engine.

Passthrough is not byte-identical. The doc said an unmapped engine keeps its
"native names"; the collector's Prometheus exporter sanitizes `:` to `_`, so
`vllm:gpu_cache_usage_perc` is published as `vllm_gpu_cache_usage_perc`. That
changes the upgrade story from one rename to two, so it belongs in the
degradation rule rather than in a reader's surprise later.

The implementation-status note said nothing consumed a MetricMapping and that the
reading side sat in the wrong function. Both are fixed in modelplaneai#412, so the note now
says what is built and what was measured, and narrows the remaining gap to the
endpoint picker, which still exposes no metrics port.

Signed-off-by: Dennis Ramdass <dennis@upbound.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

3 participants