fix(llm): preserve response text annotations
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user