mirror of
https://github.com/kennethnym/aris.git
synced 2026-03-20 17:11:17 +00:00
Compare commits
1 Commits
2717ec1b30
...
feat/feed-
| Author | SHA1 | Date | |
|---|---|---|---|
|
3abba22887
|
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
|
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.
|
* A single item in the feed.
|
||||||
*
|
*
|
||||||
@@ -36,6 +50,12 @@ export interface FeedItemSignals {
|
|||||||
* timestamp: new Date(),
|
* timestamp: new Date(),
|
||||||
* data: { temp: 18, condition: "cloudy" },
|
* data: { temp: 18, condition: "cloudy" },
|
||||||
* signals: { urgency: 0.5, timeRelevance: "ambient" },
|
* 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
|
data: TData
|
||||||
/** Source-provided hints for post-processors. Optional — omit if no signals apply. */
|
/** Source-provided hints for post-processors. Optional — omit if no signals apply. */
|
||||||
signals?: FeedItemSignals
|
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"
|
export { UnknownActionError } from "./action"
|
||||||
|
|
||||||
// Feed
|
// Feed
|
||||||
export type { FeedItem, FeedItemSignals } from "./feed"
|
export type { FeedItem, FeedItemSignals, Slot } from "./feed"
|
||||||
export { TimeRelevance } from "./feed"
|
export { TimeRelevance } from "./feed"
|
||||||
|
|
||||||
// Feed Source
|
// Feed Source
|
||||||
|
|||||||
Reference in New Issue
Block a user