Replace FeedItem.priority with signals (#39)

* feat: replace FeedItem.priority with signals

Remove priority field from FeedItem and engine-level sorting.
Add FeedItemSignals with urgency and timeRelevance fields.
Update all source packages to emit signals instead of priority.

Ranking is now the post-processing layer's responsibility.
Urgency values are unchanged from the old priority values.

Co-authored-by: Ona <no-reply@ona.com>

* fix: use TimeRelevance enum in all tests

Co-authored-by: Ona <no-reply@ona.com>

---------

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2026-02-28 12:02:57 +00:00
committed by GitHub
parent 78b0ed94bd
commit 28d26b3c87
17 changed files with 278 additions and 145 deletions

View File

@@ -145,20 +145,22 @@ describe("WeatherSource", () => {
}
})
test("assigns priority based on weather conditions", async () => {
test("assigns signals based on weather conditions", async () => {
const source = new WeatherSource({ client: mockClient })
const context = createMockContext({ lat: 37.7749, lng: -122.4194 })
const items = await source.fetchItems(context)
for (const item of items) {
expect(item.priority).toBeGreaterThanOrEqual(0)
expect(item.priority).toBeLessThanOrEqual(1)
expect(item.signals).toBeDefined()
expect(item.signals!.urgency).toBeGreaterThanOrEqual(0)
expect(item.signals!.urgency).toBeLessThanOrEqual(1)
expect(item.signals!.timeRelevance).toBeDefined()
}
const currentItem = items.find((i) => i.type === WeatherFeedItemType.current)
expect(currentItem).toBeDefined()
expect(currentItem!.priority).toBeGreaterThanOrEqual(0.5)
expect(currentItem!.signals!.urgency).toBeGreaterThanOrEqual(0.5)
})
test("generates unique IDs for each item", async () => {

View File

@@ -1,6 +1,6 @@
import type { ActionDefinition, Context, FeedSource } from "@aris/core"
import type { ActionDefinition, Context, FeedItemSignals, FeedSource } from "@aris/core"
import { UnknownActionError, contextValue } from "@aris/core"
import { TimeRelevance, UnknownActionError, contextValue } from "@aris/core"
import { LocationKey } from "@aris/source-location"
import { WeatherFeedItemType, type WeatherFeedItem } from "./feed-items"
@@ -38,7 +38,7 @@ export interface WeatherSourceOptions {
const DEFAULT_HOURLY_LIMIT = 12
const DEFAULT_DAILY_LIMIT = 7
const BASE_PRIORITY = {
const BASE_URGENCY = {
current: 0.5,
hourly: 0.3,
daily: 0.2,
@@ -199,17 +199,17 @@ export class WeatherSource implements FeedSource<WeatherFeedItem> {
}
}
function adjustPriorityForCondition(basePriority: number, conditionCode: ConditionCode): number {
function adjustUrgencyForCondition(baseUrgency: number, conditionCode: ConditionCode): number {
if (SEVERE_CONDITIONS.has(conditionCode)) {
return Math.min(1, basePriority + 0.3)
return Math.min(1, baseUrgency + 0.3)
}
if (MODERATE_CONDITIONS.has(conditionCode)) {
return Math.min(1, basePriority + 0.15)
return Math.min(1, baseUrgency + 0.15)
}
return basePriority
return baseUrgency
}
function adjustPriorityForAlertSeverity(severity: Severity): number {
function adjustUrgencyForAlertSeverity(severity: Severity): number {
switch (severity) {
case "extreme":
return 1
@@ -218,7 +218,29 @@ function adjustPriorityForAlertSeverity(severity: Severity): number {
case "moderate":
return 0.75
case "minor":
return BASE_PRIORITY.alert
return BASE_URGENCY.alert
}
}
function timeRelevanceForCondition(conditionCode: ConditionCode): TimeRelevance {
if (SEVERE_CONDITIONS.has(conditionCode)) {
return TimeRelevance.Imminent
}
if (MODERATE_CONDITIONS.has(conditionCode)) {
return TimeRelevance.Upcoming
}
return TimeRelevance.Ambient
}
function timeRelevanceForAlertSeverity(severity: Severity): TimeRelevance {
switch (severity) {
case "extreme":
case "severe":
return TimeRelevance.Imminent
case "moderate":
return TimeRelevance.Upcoming
case "minor":
return TimeRelevance.Ambient
}
}
@@ -262,12 +284,14 @@ function createCurrentWeatherFeedItem(
timestamp: Date,
units: Units,
): WeatherFeedItem {
const priority = adjustPriorityForCondition(BASE_PRIORITY.current, current.conditionCode)
const signals: FeedItemSignals = {
urgency: adjustUrgencyForCondition(BASE_URGENCY.current, current.conditionCode),
timeRelevance: timeRelevanceForCondition(current.conditionCode),
}
return {
id: `weather-current-${timestamp.getTime()}`,
type: WeatherFeedItemType.current,
priority,
timestamp,
data: {
conditionCode: current.conditionCode,
@@ -284,6 +308,7 @@ function createCurrentWeatherFeedItem(
windGust: convertSpeed(current.windGust, units),
windSpeed: convertSpeed(current.windSpeed, units),
},
signals,
}
}
@@ -293,12 +318,14 @@ function createHourlyWeatherFeedItem(
timestamp: Date,
units: Units,
): WeatherFeedItem {
const priority = adjustPriorityForCondition(BASE_PRIORITY.hourly, hourly.conditionCode)
const signals: FeedItemSignals = {
urgency: adjustUrgencyForCondition(BASE_URGENCY.hourly, hourly.conditionCode),
timeRelevance: timeRelevanceForCondition(hourly.conditionCode),
}
return {
id: `weather-hourly-${timestamp.getTime()}-${index}`,
type: WeatherFeedItemType.hourly,
priority,
timestamp,
data: {
forecastTime: new Date(hourly.forecastStart),
@@ -315,6 +342,7 @@ function createHourlyWeatherFeedItem(
windGust: convertSpeed(hourly.windGust, units),
windSpeed: convertSpeed(hourly.windSpeed, units),
},
signals,
}
}
@@ -324,12 +352,14 @@ function createDailyWeatherFeedItem(
timestamp: Date,
units: Units,
): WeatherFeedItem {
const priority = adjustPriorityForCondition(BASE_PRIORITY.daily, daily.conditionCode)
const signals: FeedItemSignals = {
urgency: adjustUrgencyForCondition(BASE_URGENCY.daily, daily.conditionCode),
timeRelevance: timeRelevanceForCondition(daily.conditionCode),
}
return {
id: `weather-daily-${timestamp.getTime()}-${index}`,
type: WeatherFeedItemType.daily,
priority,
timestamp,
data: {
forecastDate: new Date(daily.forecastStart),
@@ -344,16 +374,19 @@ function createDailyWeatherFeedItem(
temperatureMax: convertTemperature(daily.temperatureMax, units),
temperatureMin: convertTemperature(daily.temperatureMin, units),
},
signals,
}
}
function createWeatherAlertFeedItem(alert: WeatherAlert, timestamp: Date): WeatherFeedItem {
const priority = adjustPriorityForAlertSeverity(alert.severity)
const signals: FeedItemSignals = {
urgency: adjustUrgencyForAlertSeverity(alert.severity),
timeRelevance: timeRelevanceForAlertSeverity(alert.severity),
}
return {
id: `weather-alert-${alert.id}`,
type: WeatherFeedItemType.alert,
priority,
timestamp,
data: {
alertId: alert.id,
@@ -367,5 +400,6 @@ function createWeatherAlertFeedItem(alert: WeatherAlert, timestamp: Date): Weath
source: alert.source,
urgency: alert.urgency,
},
signals,
}
}