Skip to content

Commit 8f151eb

Browse files
committed
feat: add renderEntityContext for semi-structured LLM prompt text
1 parent 0da87ef commit 8f151eb

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

packages/core/src/entity-context.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
22

33
import {
44
buildEntityContext,
5+
renderEntityContext,
56
type ConversationEntity,
67
type Task,
78
} from "./index";
@@ -200,4 +201,37 @@ describe("entity context", () => {
200201
prompt: "What time should I schedule it?",
201202
});
202203
});
204+
205+
it("renders deterministic prompt text with explicit empty sections", () => {
206+
const rendered = renderEntityContext({
207+
knownEntities: [
208+
{
209+
id: "task-1",
210+
label: "Gym session",
211+
expectedType: "task",
212+
state: "scheduled",
213+
},
214+
],
215+
focusedEntityId: "task-1",
216+
activeProposal: null,
217+
openClarification: null,
218+
});
219+
220+
expect(rendered).toBe(
221+
'Known entities:\n- "Gym session" (task, scheduled) [id: task-1]\n\nCurrently focused: "Gym session" [id: task-1]\n\nNo active proposal.\n\nNo open clarification.',
222+
);
223+
});
224+
225+
it("renders an explicit no-known-entities line when the context is empty", () => {
226+
expect(
227+
renderEntityContext({
228+
knownEntities: [],
229+
focusedEntityId: null,
230+
activeProposal: null,
231+
openClarification: null,
232+
}),
233+
).toBe(
234+
"Known entities:\nNo known entities.\n\nNo focused entity.\n\nNo active proposal.\n\nNo open clarification.",
235+
);
236+
});
203237
});

packages/core/src/entity-context.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,59 @@ export function buildEntityContext(
170170
openClarification,
171171
};
172172
}
173+
174+
export function renderEntityContext(context: EntityContext): string {
175+
const lines = ["Known entities:"];
176+
177+
if (context.knownEntities.length === 0) {
178+
lines.push("No known entities.");
179+
} else {
180+
for (const entity of context.knownEntities) {
181+
lines.push(
182+
`- "${entity.label}" (${entity.expectedType}, ${entity.state}) [id: ${entity.id}]`,
183+
);
184+
}
185+
}
186+
187+
lines.push("");
188+
189+
if (context.focusedEntityId) {
190+
const focusedEntity = context.knownEntities.find(
191+
(entity) => entity.id === context.focusedEntityId,
192+
);
193+
lines.push(
194+
focusedEntity
195+
? `Currently focused: "${focusedEntity.label}" [id: ${focusedEntity.id}]`
196+
: "No focused entity.",
197+
);
198+
} else {
199+
lines.push("No focused entity.");
200+
}
201+
202+
lines.push("");
203+
204+
if (context.activeProposal) {
205+
const missingFields =
206+
context.activeProposal.missingFields &&
207+
context.activeProposal.missingFields.length > 0
208+
? ` - still needs: ${context.activeProposal.missingFields.join(", ")}`
209+
: "";
210+
lines.push(
211+
`Active proposal: "${context.activeProposal.summary}"${missingFields} [id: ${context.activeProposal.id}]`,
212+
);
213+
} else {
214+
lines.push("No active proposal.");
215+
}
216+
217+
lines.push("");
218+
219+
if (context.openClarification) {
220+
lines.push(
221+
`Open clarification: "${context.openClarification.prompt}" [id: ${context.openClarification.id}]`,
222+
);
223+
} else {
224+
lines.push("No open clarification.");
225+
}
226+
227+
return lines.join("\n");
228+
}

0 commit comments

Comments
 (0)