@@ -21,6 +21,7 @@ import (
2121 "errors"
2222 "fmt"
2323 "strings"
24+ "sync"
2425
2526 "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend"
2627 backendmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
@@ -191,6 +192,9 @@ type Pod interface {
191192 GetPod () * backend.Pod
192193 GetMetrics () * backendmetrics.MetricsState
193194 String () string
195+ Put (key string , value Cloneable )
196+ Get (key string ) (Cloneable , bool )
197+ Keys () []string
194198}
195199
196200type ScoredPod struct {
@@ -217,6 +221,19 @@ func (pm *PodMetrics) GetMetrics() *backendmetrics.MetricsState {
217221type PodMetrics struct {
218222 * backend.Pod
219223 * backendmetrics.MetricsState
224+ AttributeMap
225+ }
226+
227+ func (pm * PodMetrics ) Put (key string , value Cloneable ) {
228+ pm .AttributeMap .Put (key , value )
229+ }
230+
231+ func (pm * PodMetrics ) Get (key string ) (Cloneable , bool ) {
232+ return pm .AttributeMap .Get (key )
233+ }
234+
235+ func (pm * PodMetrics ) Keys () []string {
236+ return pm .AttributeMap .Keys ()
220237}
221238
222239// ProfileRunResult captures the profile run result.
@@ -229,3 +246,72 @@ type SchedulingResult struct {
229246 ProfileResults map [string ]* ProfileRunResult
230247 PrimaryProfileName string
231248}
249+
250+ // Cloneable types support cloning of the value.
251+ type Cloneable interface {
252+ Clone () Cloneable
253+ }
254+
255+ // AttributeMap is used to store flexible metadata or traits
256+ // across different aspects of an inference server.
257+ // Stored values must be Cloneable.
258+ type AttributeMap interface {
259+ Put (string , Cloneable )
260+ Get (string ) (Cloneable , bool )
261+ Keys () []string
262+ }
263+
264+ // Attributes provides a goroutine-safe implementation of AttributeMap.
265+ type Attributes struct {
266+ data sync.Map // key: attribute name (string), value: attribute value (opaque, Cloneable)
267+ }
268+
269+ // NewAttributes returns a new instance of Attributes.
270+ func NewAttributes () * Attributes {
271+ return & Attributes {}
272+ }
273+
274+ // Put adds or updates an attribute in the map.
275+ func (a * Attributes ) Put (key string , value Cloneable ) {
276+ if value != nil {
277+ a .data .Store (key , value ) // TODO: Clone into map to ensure isolation
278+ }
279+ }
280+
281+ // Get retrieves an attribute by key, returning a cloned copy.
282+ func (a * Attributes ) Get (key string ) (Cloneable , bool ) {
283+ val , ok := a .data .Load (key )
284+ if ! ok {
285+ return nil , false
286+ }
287+ if cloneable , ok := val .(Cloneable ); ok {
288+ return cloneable .Clone (), true
289+ }
290+ return nil , false
291+ }
292+
293+ // Keys returns all keys in the attribute map.
294+ func (a * Attributes ) Keys () []string {
295+ var keys []string
296+ a .data .Range (func (key , _ any ) bool {
297+ if sk , ok := key .(string ); ok {
298+ keys = append (keys , sk )
299+ }
300+ return true
301+ })
302+ return keys
303+ }
304+
305+ // Clone creates a deep copy of the entire attribute map.
306+ func (a * Attributes ) Clone () * Attributes {
307+ clone := NewAttributes ()
308+ a .data .Range (func (key , value any ) bool {
309+ if sk , ok := key .(string ); ok {
310+ if v , ok := value .(Cloneable ); ok {
311+ clone .Put (sk , v )
312+ }
313+ }
314+ return true
315+ })
316+ return clone
317+ }
0 commit comments