mirror of
https://github.com/kennethnym/aris.git
synced 2026-03-20 09:01:19 +00:00
feat(core): add Slot type and slots field to FeedItem (#53)
Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
87
packages/aris-core/src/feed.test.ts
Normal file
87
packages/aris-core/src/feed.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import type { FeedItem, Slot } from "./feed"
|
||||
|
||||
describe("FeedItem slots", () => {
|
||||
test("FeedItem without slots is valid", () => {
|
||||
const item: FeedItem<"test", { value: number }> = {
|
||||
id: "test-1",
|
||||
type: "test",
|
||||
timestamp: new Date(),
|
||||
data: { value: 42 },
|
||||
}
|
||||
|
||||
expect(item.slots).toBeUndefined()
|
||||
})
|
||||
|
||||
test("FeedItem with unfilled slots", () => {
|
||||
const item: FeedItem<"weather", { temp: number }> = {
|
||||
id: "weather-1",
|
||||
type: "weather",
|
||||
timestamp: new Date(),
|
||||
data: { temp: 18 },
|
||||
slots: {
|
||||
insight: {
|
||||
description: "A short contextual insight about the current weather",
|
||||
content: null,
|
||||
},
|
||||
"cross-source": {
|
||||
description: "Connection between weather and calendar events",
|
||||
content: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(item.slots).toBeDefined()
|
||||
expect(Object.keys(item.slots!)).toEqual(["insight", "cross-source"])
|
||||
expect(item.slots!.insight!.content).toBeNull()
|
||||
expect(item.slots!["cross-source"]!.content).toBeNull()
|
||||
})
|
||||
|
||||
test("FeedItem with filled slots", () => {
|
||||
const item: FeedItem<"weather", { temp: number }> = {
|
||||
id: "weather-1",
|
||||
type: "weather",
|
||||
timestamp: new Date(),
|
||||
data: { temp: 18 },
|
||||
slots: {
|
||||
insight: {
|
||||
description: "A short contextual insight about the current weather",
|
||||
content: "Rain after 3pm — grab a jacket before your walk",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(item.slots!.insight!.content).toBe("Rain after 3pm — grab a jacket before your walk")
|
||||
})
|
||||
|
||||
test("Slot interface enforces required fields", () => {
|
||||
const slot: Slot = {
|
||||
description: "Test slot description",
|
||||
content: null,
|
||||
}
|
||||
|
||||
expect(slot.description).toBe("Test slot description")
|
||||
expect(slot.content).toBeNull()
|
||||
|
||||
const filledSlot: Slot = {
|
||||
description: "Test slot description",
|
||||
content: "Filled content",
|
||||
}
|
||||
|
||||
expect(filledSlot.content).toBe("Filled content")
|
||||
})
|
||||
|
||||
test("FeedItem with empty slots record", () => {
|
||||
const item: FeedItem<"test", { value: number }> = {
|
||||
id: "test-1",
|
||||
type: "test",
|
||||
timestamp: new Date(),
|
||||
data: { value: 1 },
|
||||
slots: {},
|
||||
}
|
||||
|
||||
expect(item.slots).toEqual({})
|
||||
expect(Object.keys(item.slots!)).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
@@ -23,6 +23,20 @@ export interface FeedItemSignals {
|
||||
timeRelevance?: TimeRelevance
|
||||
}
|
||||
|
||||
/**
|
||||
* A named slot for LLM-fillable content on a feed item.
|
||||
*
|
||||
* Sources declare slots with a description that tells the LLM what content
|
||||
* to generate. The enhancement harness fills `content` asynchronously;
|
||||
* until then it remains `null`.
|
||||
*/
|
||||
export interface Slot {
|
||||
/** Tells the LLM what this slot wants — written by the source */
|
||||
description: string
|
||||
/** LLM-filled text content, null until enhanced */
|
||||
content: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* A single item in the feed.
|
||||
*
|
||||
@@ -36,6 +50,12 @@ export interface FeedItemSignals {
|
||||
* timestamp: new Date(),
|
||||
* data: { temp: 18, condition: "cloudy" },
|
||||
* signals: { urgency: 0.5, timeRelevance: "ambient" },
|
||||
* slots: {
|
||||
* insight: {
|
||||
* description: "A short contextual insight about the current weather",
|
||||
* content: null,
|
||||
* },
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@@ -53,4 +73,6 @@ export interface FeedItem<
|
||||
data: TData
|
||||
/** Source-provided hints for post-processors. Optional — omit if no signals apply. */
|
||||
signals?: FeedItemSignals
|
||||
/** Named slots for LLM-fillable content. Keys are slot names. */
|
||||
slots?: Record<string, Slot>
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export type { ActionDefinition } from "./action"
|
||||
export { UnknownActionError } from "./action"
|
||||
|
||||
// Feed
|
||||
export type { FeedItem, FeedItemSignals } from "./feed"
|
||||
export type { FeedItem, FeedItemSignals, Slot } from "./feed"
|
||||
export { TimeRelevance } from "./feed"
|
||||
|
||||
// Feed Source
|
||||
|
||||
Reference in New Issue
Block a user