diff --git a/packages/contracts/src/assistantCitations.ts b/packages/contracts/src/assistantCitations.ts index 71934d1684ab0..96572a4bfa656 100644 --- a/packages/contracts/src/assistantCitations.ts +++ b/packages/contracts/src/assistantCitations.ts @@ -4,13 +4,15 @@ import { EnvironmentId, MessageId, NonNegativeInt, ThreadId } from "./baseSchema export const ASSISTANT_CITATION_MAX_TEXT_LENGTH = 8_000; export const ASSISTANT_CITATION_MAX_COMMENT_LENGTH = 8_000; export const ASSISTANT_CITATION_CONTEXT_LENGTH = 32; +// Provider message IDs nest and re-encode their thread and turn IDs, so they outgrow a few hundred characters. +export const ASSISTANT_CITATION_MAX_ID_LENGTH = 4_096; /** Positions are UTF-16 offsets, not Markdown offsets. */ export const AssistantCitation = Schema.Struct({ version: Schema.Literal(1), - environmentId: EnvironmentId.check(Schema.isMaxLength(512)), - threadId: ThreadId.check(Schema.isMaxLength(512)), - messageId: MessageId.check(Schema.isMaxLength(512)), + environmentId: EnvironmentId.check(Schema.isMaxLength(ASSISTANT_CITATION_MAX_ID_LENGTH)), + threadId: ThreadId.check(Schema.isMaxLength(ASSISTANT_CITATION_MAX_ID_LENGTH)), + messageId: MessageId.check(Schema.isMaxLength(ASSISTANT_CITATION_MAX_ID_LENGTH)), text: Schema.String.check( Schema.isNonEmpty(), Schema.isMaxLength(ASSISTANT_CITATION_MAX_TEXT_LENGTH), diff --git a/packages/shared/src/assistantCitations.test.ts b/packages/shared/src/assistantCitations.test.ts index 7300de72e9d1c..7dbaaf9d8bdeb 100644 --- a/packages/shared/src/assistantCitations.test.ts +++ b/packages/shared/src/assistantCitations.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vite-plus/test"; import { ASSISTANT_CITATION_CONTEXT_LENGTH, ASSISTANT_CITATION_MAX_COMMENT_LENGTH, + ASSISTANT_CITATION_MAX_ID_LENGTH, ASSISTANT_CITATION_MAX_TEXT_LENGTH, EnvironmentId, MessageId, @@ -126,9 +127,9 @@ describe("assistant citation references", () => { it("accepts complete 8k CJK quotes and comments with maximum-sized source selectors", () => { const largeCitation: AssistantCitation = { ...citation, - environmentId: EnvironmentId.make("環".repeat(512)), - threadId: ThreadId.make("線".repeat(512)), - messageId: MessageId.make("文".repeat(512)), + environmentId: EnvironmentId.make("環".repeat(ASSISTANT_CITATION_MAX_ID_LENGTH)), + threadId: ThreadId.make("線".repeat(ASSISTANT_CITATION_MAX_ID_LENGTH)), + messageId: MessageId.make("文".repeat(ASSISTANT_CITATION_MAX_ID_LENGTH)), text: "引".repeat(ASSISTANT_CITATION_MAX_TEXT_LENGTH), comment: "注".repeat(ASSISTANT_CITATION_MAX_COMMENT_LENGTH), start: Number.MAX_SAFE_INTEGER - ASSISTANT_CITATION_MAX_TEXT_LENGTH, diff --git a/packages/shared/src/assistantCitations.ts b/packages/shared/src/assistantCitations.ts index 64bb2b3904ba1..bb072e462a45e 100644 --- a/packages/shared/src/assistantCitations.ts +++ b/packages/shared/src/assistantCitations.ts @@ -1,5 +1,7 @@ import { + ASSISTANT_CITATION_CONTEXT_LENGTH, ASSISTANT_CITATION_MAX_COMMENT_LENGTH, + ASSISTANT_CITATION_MAX_ID_LENGTH, ASSISTANT_CITATION_MAX_TEXT_LENGTH, AssistantCitation, } from "@t3tools/contracts"; @@ -8,9 +10,14 @@ import * as Schema from "effect/Schema"; const CITATION_PROTOCOL = "t3-citation:"; const CITATION_HREF_PREFIX = `${CITATION_PROTOCOL}//v1/`; -// Percent encoding needs up to nine characters per UTF-16 code unit; 16k covers selectors. +// Percent encoding needs up to nine characters per UTF-16 code unit; 1k covers keys and offsets. const MAX_CITATION_HREF_LENGTH = - 9 * (ASSISTANT_CITATION_MAX_TEXT_LENGTH + ASSISTANT_CITATION_MAX_COMMENT_LENGTH) + 16_000; + 9 * + (ASSISTANT_CITATION_MAX_TEXT_LENGTH + + ASSISTANT_CITATION_MAX_COMMENT_LENGTH + + 3 * ASSISTANT_CITATION_MAX_ID_LENGTH + + 2 * ASSISTANT_CITATION_CONTEXT_LENGTH) + + 1_000; const CITATION_LINK = new RegExp( String.raw`\[Assistant quote\]\((${CITATION_HREF_PREFIX}[^\s)]{1,${MAX_CITATION_HREF_LENGTH - CITATION_HREF_PREFIX.length}})\)`, "g",