fix(llm): preserve response text annotations

This commit is contained in:
Aiden Cline
2026-07-23 14:05:13 -05:00
parent 6b13d117d3
commit 9c7bf1c219
2 changed files with 80 additions and 6 deletions
+30 -3
View File
@@ -210,6 +210,7 @@ const OpenAIResponsesStreamItem = Schema.Struct({
server_label: Schema.optional(Schema.String),
output: Schema.optional(Schema.Unknown),
error: Schema.optional(Schema.Unknown),
content: Schema.optional(Schema.Array(Schema.Unknown)),
encrypted_content: optionalNull(Schema.String),
phase: optionalNull(OpenAIResponsesMessagePhase),
})
@@ -340,6 +341,11 @@ const messageStatus = (part: TextPart) => {
return status === "in_progress" || status === "completed" || status === "incomplete" ? status : undefined
}
const messageAnnotations = (part: TextPart) => {
const annotations = part.providerMetadata?.openai?.annotations
return Array.isArray(annotations) ? annotations : []
}
const hostedToolItemID = (part: ToolResultPart) => {
const openai = part.providerMetadata?.openai
return ProviderShared.isRecord(openai) && typeof openai.itemId === "string" && openai.itemId.length > 0
@@ -426,7 +432,11 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ
id: itemID,
status: status ?? "completed",
role: "assistant",
content: content.map((part) => ({ type: "output_text", text: part.text, annotations: [] })),
content: content.map((part) => ({
type: "output_text",
text: part.text,
annotations: messageAnnotations(part),
})),
...(phase !== undefined ? { phase } : {}),
}
: {
@@ -695,6 +705,17 @@ const messageMetadata = (item: OpenAIResponsesStreamItem, id: string, previous?:
})
}
const messageContentMetadata = (
providerMetadata: ProviderMetadata,
item: OpenAIResponsesStreamItem,
index: number,
): ProviderMetadata => {
const content = item.content?.[index]
if (!ProviderShared.isRecord(content) || content.type !== "output_text" || !Array.isArray(content.annotations))
return providerMetadata
return openaiMetadata({ ...providerMetadata.openai, annotations: content.annotations })
}
const ensureMessageContent = (state: ParserState, event: OpenAIResponsesEvent) => {
const itemID = event.item_id ?? "text-0"
const index = event.content_index ?? 0
@@ -1005,8 +1026,14 @@ const onOutputItemDone = Effect.fn("OpenAIResponses.onOutputItemDone")(function*
const messageItem = state.messageItems[itemID]
const { [itemID]: _finished, ...messageItems } = state.messageItems
const providerMetadata = messageMetadata(item, itemID, messageItem?.providerMetadata)
const lifecycle = Object.values(messageItem?.content ?? {}).reduce(
(lifecycle, content) => Lifecycle.textEnd(lifecycle, events, content.id, providerMetadata),
const lifecycle = Object.entries(messageItem?.content ?? {}).reduce(
(lifecycle, entry) =>
Lifecycle.textEnd(
lifecycle,
events,
entry[1].id,
messageContentMetadata(providerMetadata, item, Number(entry[0])),
),
state.lifecycle,
)
return [
@@ -893,7 +893,26 @@ describe("OpenAI Responses route", () => {
},
{
type: "response.output_item.done",
item: { type: "message", id: "openai-text-0", phase: "final_answer" },
item: {
type: "message",
id: "openai-text-0",
phase: "final_answer",
content: [
{
type: "output_text",
text: "Final.",
annotations: [
{
type: "url_citation",
url: "https://example.com",
title: "Example",
start_index: 0,
end_index: 6,
},
],
},
],
},
},
{
type: "response.output_item.added",
@@ -948,7 +967,21 @@ describe("OpenAI Responses route", () => {
{
type: "text",
text: "Final.",
providerMetadata: { openai: { itemId: "openai-text-0", phase: "final_answer" } },
providerMetadata: {
openai: {
itemId: "openai-text-0",
phase: "final_answer",
annotations: [
{
type: "url_citation",
url: "https://example.com",
title: "Example",
start_index: 0,
end_index: 6,
},
],
},
},
},
{
type: "text",
@@ -991,7 +1024,21 @@ describe("OpenAI Responses route", () => {
status: "completed",
role: "assistant",
phase: "final_answer",
content: [{ type: "output_text", text: "Final.", annotations: [] }],
content: [
{
type: "output_text",
text: "Final.",
annotations: [
{
type: "url_citation",
url: "https://example.com",
title: "Example",
start_index: 0,
end_index: 6,
},
],
},
],
},
{
type: "message",