@assistant-ui/core 0.3.10 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +17 -10
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +15 -2
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/package.json +2 -2
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +49 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +11 -2
- package/src/react/runtimes/external-message-converter.test.ts +71 -0
- package/src/react/runtimes/external-message-converter.test.tsx +9 -0
- package/src/react/runtimes/external-message-converter.ts +35 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCloudThreadListAdapter.d.ts","names":[],"sources":["../../../../src/react/runtimes/cloud/useCloudThreadListAdapter.tsx"],"mappings":";;;KAkBK;EACH;;KAGG;EACH,QAAQ;EAER,gBAAgB,QAAQ;EACxB,WAAW,qBAAqB;;
|
|
1
|
+
{"version":3,"file":"useCloudThreadListAdapter.d.ts","names":[],"sources":["../../../../src/react/runtimes/cloud/useCloudThreadListAdapter.tsx"],"mappings":";;;KAkBK;EACH;;KAGG;EACH,QAAQ;EAER,gBAAgB,QAAQ;EACxB,WAAW,qBAAqB;;cAerB,4BACX,SAAS,kCACR"}
|
|
@@ -13,6 +13,7 @@ const autoCloud = baseUrl ? new AssistantCloud({
|
|
|
13
13
|
baseUrl,
|
|
14
14
|
anonymous: true
|
|
15
15
|
}) : void 0;
|
|
16
|
+
const CLOUD_THREAD_PAGE_SIZE = 20;
|
|
16
17
|
const useCloudThreadListAdapter = (adapter) => {
|
|
17
18
|
const adapterRef = useRef(adapter);
|
|
18
19
|
useEffect(() => {
|
|
@@ -47,16 +48,22 @@ const useCloudThreadListAdapter = (adapter) => {
|
|
|
47
48
|
return inMemory;
|
|
48
49
|
}
|
|
49
50
|
return {
|
|
50
|
-
list: async () => {
|
|
51
|
-
const { threads } = await cloud.threads.list(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
list: async ({ after } = {}) => {
|
|
52
|
+
const { threads } = await cloud.threads.list({
|
|
53
|
+
limit: CLOUD_THREAD_PAGE_SIZE,
|
|
54
|
+
...after ? { after } : {}
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
threads: threads.map((t) => ({
|
|
58
|
+
status: t.is_archived ? "archived" : "regular",
|
|
59
|
+
remoteId: t.id,
|
|
60
|
+
title: t.title,
|
|
61
|
+
lastMessageAt: t.last_message_at ? new Date(t.last_message_at) : void 0,
|
|
62
|
+
externalId: t.external_id ?? void 0,
|
|
63
|
+
custom: toCustom(t.metadata)
|
|
64
|
+
})),
|
|
65
|
+
nextCursor: threads.length === CLOUD_THREAD_PAGE_SIZE ? threads.at(-1)?.id : void 0
|
|
66
|
+
};
|
|
60
67
|
},
|
|
61
68
|
initialize: async () => {
|
|
62
69
|
const t_0 = await (adapterRef.current.create?.() ?? Promise.resolve());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCloudThreadListAdapter.js","names":["process","env","Record","FC","PropsWithChildren","useCallback","useEffect","useMemo","useRef","AssistantCloud","RemoteThreadListAdapter","InMemoryThreadListAdapter","useAssistantCloudThreadHistoryAdapter","RuntimeAdapterProvider","CloudFileAttachmentAdapter","isRecord","ThreadData","externalId","CloudThreadListAdapterOptions","cloud","create","Promise","delete","threadId","toCustom","value","undefined","baseUrl","NEXT_PUBLIC_ASSISTANT_BASE_URL","autoCloud","anonymous","useCloudThreadListAdapter","adapter","adapterRef","current","unstable_Provider","Provider","children","history","cloudInstance","attachments","adapters","ref","inMemory","initialize","result","remoteId","list","threads","map","t","status","is_archived","id","title","lastMessageAt","last_message_at","Date","external_id","custom","metadata","createTask","resolve","thread_id","rename","newTitle","update","updateCustom","archive","unarchive","generateTitle","messages","filteredMessages","msg","content","filter","part","type","runs","stream","assistant_id","fetch","thread","get"],"sources":["../../../../src/react/runtimes/cloud/useCloudThreadListAdapter.tsx"],"sourcesContent":["declare const process: { env: Record<string, string | undefined> };\n\nimport {\n type FC,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { AssistantCloud } from \"assistant-cloud\";\nimport type { RemoteThreadListAdapter } from \"../../../runtimes/remote-thread-list/types\";\nimport { InMemoryThreadListAdapter } from \"../../../runtimes/remote-thread-list/adapter/in-memory\";\nimport { useAssistantCloudThreadHistoryAdapter } from \"./AssistantCloudThreadHistoryAdapter\";\nimport { RuntimeAdapterProvider } from \"../RuntimeAdapterProvider\";\nimport { CloudFileAttachmentAdapter } from \"./CloudFileAttachmentAdapter\";\nimport { isRecord } from \"../../../utils/json/is-json\";\n\ntype ThreadData = {\n externalId: string | undefined;\n};\n\ntype CloudThreadListAdapterOptions = {\n cloud?: AssistantCloud | undefined;\n\n create?: (() => Promise<ThreadData>) | undefined;\n delete?: ((threadId: string) => Promise<void>) | undefined;\n};\n\nconst toCustom = (value: unknown): Record<string, unknown> | undefined =>\n isRecord(value) ? value : undefined;\n\nconst baseUrl =\n typeof process !== \"undefined\" &&\n process?.env?.NEXT_PUBLIC_ASSISTANT_BASE_URL;\nconst autoCloud = baseUrl\n ? new AssistantCloud({ baseUrl, anonymous: true })\n : undefined;\n\nexport const useCloudThreadListAdapter = (\n adapter: CloudThreadListAdapterOptions,\n): RemoteThreadListAdapter => {\n const adapterRef = useRef(adapter);\n useEffect(() => {\n adapterRef.current = adapter;\n }, [adapter]);\n\n const unstable_Provider = useCallback<FC<PropsWithChildren>>(\n function Provider({ children }) {\n const history = useAssistantCloudThreadHistoryAdapter({\n get current() {\n return adapterRef.current.cloud ?? autoCloud!;\n },\n });\n const cloudInstance = adapterRef.current.cloud ?? autoCloud!;\n const attachments = useMemo(\n () => new CloudFileAttachmentAdapter(cloudInstance),\n [cloudInstance],\n );\n\n const adapters = useMemo(\n () => ({\n history,\n attachments,\n }),\n [history, attachments],\n );\n\n return (\n <RuntimeAdapterProvider adapters={adapters}>\n {children}\n </RuntimeAdapterProvider>\n );\n },\n [],\n );\n\n const cloud = adapter.cloud ?? autoCloud;\n return useMemo<RemoteThreadListAdapter>(() => {\n if (!cloud) {\n const ref = adapterRef;\n const inMemory = new InMemoryThreadListAdapter();\n inMemory.initialize = async (threadId: string) => {\n const result = await ref.current.create?.();\n return { remoteId: threadId, externalId: result?.externalId };\n };\n return inMemory;\n }\n\n return {\n list: async () => {\n const { threads } = await cloud.threads.list();\n return {\n threads: threads.map((t) => ({\n status: t.is_archived ? \"archived\" : \"regular\",\n remoteId: t.id,\n title: t.title,\n lastMessageAt: t.last_message_at\n ? new Date(t.last_message_at)\n : undefined,\n externalId: t.external_id ?? undefined,\n custom: toCustom(t.metadata),\n })),\n };\n },\n\n initialize: async () => {\n const createTask = adapterRef.current.create?.() ?? Promise.resolve();\n const t = await createTask;\n const external_id = t ? t.externalId : undefined;\n const { thread_id: remoteId } = await cloud.threads.create({\n last_message_at: new Date(),\n external_id,\n });\n\n return { externalId: external_id, remoteId: remoteId };\n },\n\n rename: async (threadId, newTitle) => {\n return cloud.threads.update(threadId, { title: newTitle });\n },\n updateCustom: async (threadId, custom) => {\n return cloud.threads.update(threadId, { metadata: custom ?? null });\n },\n archive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: true });\n },\n unarchive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: false });\n },\n delete: async (threadId) => {\n await adapterRef.current.delete?.(threadId);\n return cloud.threads.delete(threadId);\n },\n\n generateTitle: async (threadId, messages) => {\n // Filter messages to only include content types the title generator understands\n // (reasoning, source, etc. are not needed for title generation)\n // TODO serialize these to a more efficient format\n const filteredMessages = messages.map((msg) => ({\n ...msg,\n content: msg.content.filter(\n (part) => part.type === \"text\" || part.type === \"tool-call\",\n ),\n }));\n\n return cloud.runs.stream({\n thread_id: threadId,\n assistant_id: \"system/thread_title\",\n messages: filteredMessages,\n });\n },\n\n fetch: async (threadId: string) => {\n const thread = await cloud.threads.get(threadId);\n return {\n status: thread.is_archived ? \"archived\" : \"regular\",\n remoteId: thread.id,\n title: thread.title,\n lastMessageAt: thread.last_message_at\n ? new Date(thread.last_message_at)\n : undefined,\n externalId: thread.external_id ?? undefined,\n custom: toCustom(thread.metadata),\n };\n },\n\n unstable_Provider,\n };\n }, [cloud, unstable_Provider]);\n};\n"],"mappings":";;;;;;;;;AA6BA,MAAMwB,YAAYC,UAChBV,SAASU,KAAK,IAAIA,QAAQC,KAAAA;AAE5B,MAAMC,UACJ,OAAO3B,YAAY,eACnBA,SAASC,KAAK2B;AAChB,MAAMC,YAAYF,UACd,IAAIlB,eAAe;CAAEkB;CAASG,WAAW;AAAK,CAAC,IAC/CJ,KAAAA;AAEJ,MAAaK,6BACXC,YAC4B;CAC5B,MAAMC,aAAazB,OAAOwB,OAAO;CACjC1B,gBAAgB;EACd2B,WAAWC,UAAUF;CACvB,GAAG,CAACA,OAAO,CAAC;CAEZ,MAAMG,oBAAoB9B,YACxB,SAAS+B,SAAS,EAAEC,YAAY;EAC9B,MAAMC,UAAU1B,sCAAsC,EACpD,IAAIsB,UAAU;GACZ,OAAOD,WAAWC,QAAQf,SAASU;EACrC,EACF,CAAC;EACD,MAAMU,gBAAgBN,WAAWC,QAAQf,SAASU;EAClD,MAAMW,cAAcjC,cACZ,IAAIO,2BAA2ByB,aAAa,GAClD,CAACA,aAAa,CAChB;EAEA,MAAME,WAAWlC,eACR;GACL+B;GACAE;EACF,IACA,CAACF,SAASE,WAAW,CACvB;EAEA,OACE,oBAAC,wBAAD;GAAkCC;GAC/BJ;EACqB,CAAA;CAE5B,GACA,CAAA,CACF;CAEA,MAAMlB,QAAQa,QAAQb,SAASU;CAC/B,OAAOtB,cAAuC;EAC5C,IAAI,CAACY,OAAO;GACV,MAAMuB,MAAMT;GACZ,MAAMU,WAAW,IAAIhC,0BAA0B;GAC/CgC,SAASC,aAAa,OAAOrB,aAAqB;IAEhD,OAAO;KAAEuB,UAAUvB;KAAUN,aAAY4B,MADpBH,IAAIR,QAAQd,SAAS,EAAA,EACOH;IAAW;GAC9D;GACA,OAAO0B;EACT;EAEA,OAAO;GACLI,MAAM,YAAY;IAChB,MAAM,EAAEC,YAAY,MAAM7B,MAAM6B,QAAQD,KAAK;IAC7C,OAAO,EACLC,SAASA,QAAQC,KAAKC,OAAO;KAC3BC,QAAQD,EAAEE,cAAc,aAAa;KACrCN,UAAUI,EAAEG;KACZC,OAAOJ,EAAEI;KACTC,eAAeL,EAAEM,kBACb,IAAIC,KAAKP,EAAEM,eAAe,IAC1B9B,KAAAA;KACJT,YAAYiC,EAAEQ,eAAehC,KAAAA;KAC7BiC,QAAQnC,SAAS0B,EAAEU,QAAQ;IAC7B,EAAE,EACJ;GACF;GAEAhB,YAAY,YAAY;IAEtB,MAAMM,MAAI,OADSjB,WAAWC,QAAQd,SAAS,KAAKC,QAAQyC,QAAQ;IAEpE,MAAMJ,cAAcR,MAAIA,IAAEjC,aAAaS,KAAAA;IACvC,MAAM,EAAEqC,WAAWjB,aAAa,MAAM3B,MAAM6B,QAAQ5B,OAAO;KACzDoC,iCAAiB,IAAIC,KAAK;KAC1BC;IACF,CAAC;IAED,OAAO;KAAEzC,YAAYyC;KAAuBZ;IAAS;GACvD;GAEAkB,QAAQ,OAAOzC,YAAU0C,aAAa;IACpC,OAAO9C,MAAM6B,QAAQkB,OAAO3C,YAAU,EAAE+B,OAAOW,SAAS,CAAC;GAC3D;GACAE,cAAc,OAAO5C,YAAUoC,WAAW;IACxC,OAAOxC,MAAM6B,QAAQkB,OAAO3C,YAAU,EAAEqC,UAAUD,UAAU,KAAK,CAAC;GACpE;GACAS,SAAS,OAAO7C,eAAa;IAC3B,OAAOJ,MAAM6B,QAAQkB,OAAO3C,YAAU,EAAE6B,aAAa,KAAK,CAAC;GAC7D;GACAiB,WAAW,OAAO9C,eAAa;IAC7B,OAAOJ,MAAM6B,QAAQkB,OAAO3C,YAAU,EAAE6B,aAAa,MAAM,CAAC;GAC9D;GACA9B,QAAQ,OAAOC,eAAa;IAC1B,MAAMU,WAAWC,QAAQZ,SAASC,UAAQ;IAC1C,OAAOJ,MAAM6B,QAAQ1B,OAAOC,UAAQ;GACtC;GAEA+C,eAAe,OAAO/C,YAAUgD,aAAa;IAI3C,MAAMC,mBAAmBD,SAAStB,KAAKwB,SAAS;KAC9C,GAAGA;KACHC,SAASD,IAAIC,QAAQC,QAClBC,SAASA,KAAKC,SAAS,UAAUD,KAAKC,SAAS,WAClD;IACF,EAAE;IAEF,OAAO1D,MAAM2D,KAAKC,OAAO;KACvBhB,WAAWxC;KACXyD,cAAc;KACdT,UAAUC;IACZ,CAAC;GACH;GAEAS,OAAO,OAAO1D,eAAqB;IACjC,MAAM2D,SAAS,MAAM/D,MAAM6B,QAAQmC,IAAI5D,UAAQ;IAC/C,OAAO;KACL4B,QAAQ+B,OAAO9B,cAAc,aAAa;KAC1CN,UAAUoC,OAAO7B;KACjBC,OAAO4B,OAAO5B;KACdC,eAAe2B,OAAO1B,kBAClB,IAAIC,KAAKyB,OAAO1B,eAAe,IAC/B9B,KAAAA;KACJT,YAAYiE,OAAOxB,eAAehC,KAAAA;KAClCiC,QAAQnC,SAAS0D,OAAOtB,QAAQ;IAClC;GACF;GAEAzB;EACF;CACF,GAAG,CAAChB,OAAOgB,iBAAiB,CAAC;AAC/B"}
|
|
1
|
+
{"version":3,"file":"useCloudThreadListAdapter.js","names":["process","env","Record","FC","PropsWithChildren","useCallback","useEffect","useMemo","useRef","AssistantCloud","RemoteThreadListAdapter","InMemoryThreadListAdapter","useAssistantCloudThreadHistoryAdapter","RuntimeAdapterProvider","CloudFileAttachmentAdapter","isRecord","ThreadData","externalId","CloudThreadListAdapterOptions","cloud","create","Promise","delete","threadId","toCustom","value","undefined","baseUrl","NEXT_PUBLIC_ASSISTANT_BASE_URL","autoCloud","anonymous","CLOUD_THREAD_PAGE_SIZE","useCloudThreadListAdapter","adapter","adapterRef","current","unstable_Provider","Provider","children","history","cloudInstance","attachments","adapters","ref","inMemory","initialize","result","remoteId","list","after","threads","limit","map","t","status","is_archived","id","title","lastMessageAt","last_message_at","Date","external_id","custom","metadata","nextCursor","length","at","createTask","resolve","thread_id","rename","newTitle","update","updateCustom","archive","unarchive","generateTitle","messages","filteredMessages","msg","content","filter","part","type","runs","stream","assistant_id","fetch","thread","get"],"sources":["../../../../src/react/runtimes/cloud/useCloudThreadListAdapter.tsx"],"sourcesContent":["declare const process: { env: Record<string, string | undefined> };\n\nimport {\n type FC,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { AssistantCloud } from \"assistant-cloud\";\nimport type { RemoteThreadListAdapter } from \"../../../runtimes/remote-thread-list/types\";\nimport { InMemoryThreadListAdapter } from \"../../../runtimes/remote-thread-list/adapter/in-memory\";\nimport { useAssistantCloudThreadHistoryAdapter } from \"./AssistantCloudThreadHistoryAdapter\";\nimport { RuntimeAdapterProvider } from \"../RuntimeAdapterProvider\";\nimport { CloudFileAttachmentAdapter } from \"./CloudFileAttachmentAdapter\";\nimport { isRecord } from \"../../../utils/json/is-json\";\n\ntype ThreadData = {\n externalId: string | undefined;\n};\n\ntype CloudThreadListAdapterOptions = {\n cloud?: AssistantCloud | undefined;\n\n create?: (() => Promise<ThreadData>) | undefined;\n delete?: ((threadId: string) => Promise<void>) | undefined;\n};\n\nconst toCustom = (value: unknown): Record<string, unknown> | undefined =>\n isRecord(value) ? value : undefined;\n\nconst baseUrl =\n typeof process !== \"undefined\" &&\n process?.env?.NEXT_PUBLIC_ASSISTANT_BASE_URL;\nconst autoCloud = baseUrl\n ? new AssistantCloud({ baseUrl, anonymous: true })\n : undefined;\n\nconst CLOUD_THREAD_PAGE_SIZE = 20;\n\nexport const useCloudThreadListAdapter = (\n adapter: CloudThreadListAdapterOptions,\n): RemoteThreadListAdapter => {\n const adapterRef = useRef(adapter);\n useEffect(() => {\n adapterRef.current = adapter;\n }, [adapter]);\n\n const unstable_Provider = useCallback<FC<PropsWithChildren>>(\n function Provider({ children }) {\n const history = useAssistantCloudThreadHistoryAdapter({\n get current() {\n return adapterRef.current.cloud ?? autoCloud!;\n },\n });\n const cloudInstance = adapterRef.current.cloud ?? autoCloud!;\n const attachments = useMemo(\n () => new CloudFileAttachmentAdapter(cloudInstance),\n [cloudInstance],\n );\n\n const adapters = useMemo(\n () => ({\n history,\n attachments,\n }),\n [history, attachments],\n );\n\n return (\n <RuntimeAdapterProvider adapters={adapters}>\n {children}\n </RuntimeAdapterProvider>\n );\n },\n [],\n );\n\n const cloud = adapter.cloud ?? autoCloud;\n return useMemo<RemoteThreadListAdapter>(() => {\n if (!cloud) {\n const ref = adapterRef;\n const inMemory = new InMemoryThreadListAdapter();\n inMemory.initialize = async (threadId: string) => {\n const result = await ref.current.create?.();\n return { remoteId: threadId, externalId: result?.externalId };\n };\n return inMemory;\n }\n\n return {\n list: async ({ after } = {}) => {\n const { threads } = await cloud.threads.list({\n limit: CLOUD_THREAD_PAGE_SIZE,\n ...(after ? { after } : {}),\n });\n return {\n threads: threads.map((t) => ({\n status: t.is_archived ? \"archived\" : \"regular\",\n remoteId: t.id,\n title: t.title,\n lastMessageAt: t.last_message_at\n ? new Date(t.last_message_at)\n : undefined,\n externalId: t.external_id ?? undefined,\n custom: toCustom(t.metadata),\n })),\n nextCursor:\n threads.length === CLOUD_THREAD_PAGE_SIZE\n ? threads.at(-1)?.id\n : undefined,\n };\n },\n\n initialize: async () => {\n const createTask = adapterRef.current.create?.() ?? Promise.resolve();\n const t = await createTask;\n const external_id = t ? t.externalId : undefined;\n const { thread_id: remoteId } = await cloud.threads.create({\n last_message_at: new Date(),\n external_id,\n });\n\n return { externalId: external_id, remoteId: remoteId };\n },\n\n rename: async (threadId, newTitle) => {\n return cloud.threads.update(threadId, { title: newTitle });\n },\n updateCustom: async (threadId, custom) => {\n return cloud.threads.update(threadId, { metadata: custom ?? null });\n },\n archive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: true });\n },\n unarchive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: false });\n },\n delete: async (threadId) => {\n await adapterRef.current.delete?.(threadId);\n return cloud.threads.delete(threadId);\n },\n\n generateTitle: async (threadId, messages) => {\n // Filter messages to only include content types the title generator understands\n // (reasoning, source, etc. are not needed for title generation)\n // TODO serialize these to a more efficient format\n const filteredMessages = messages.map((msg) => ({\n ...msg,\n content: msg.content.filter(\n (part) => part.type === \"text\" || part.type === \"tool-call\",\n ),\n }));\n\n return cloud.runs.stream({\n thread_id: threadId,\n assistant_id: \"system/thread_title\",\n messages: filteredMessages,\n });\n },\n\n fetch: async (threadId: string) => {\n const thread = await cloud.threads.get(threadId);\n return {\n status: thread.is_archived ? \"archived\" : \"regular\",\n remoteId: thread.id,\n title: thread.title,\n lastMessageAt: thread.last_message_at\n ? new Date(thread.last_message_at)\n : undefined,\n externalId: thread.external_id ?? undefined,\n custom: toCustom(thread.metadata),\n };\n },\n\n unstable_Provider,\n };\n }, [cloud, unstable_Provider]);\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-message-converter.d.ts","names":[],"sources":["../../../src/react/runtimes/external-message-converter.ts"],"mappings":";;;;;KAwBY;kBAoBK;OACH,WACP;aACU;eACE,eAAe;;;IAI1B;IACA;IACA;IACA;IACA;IACA;IACA,oBAAoB;;OAGd;aACD,eAAe,eAAe;aAC9B,QAAQ;aACR,gBAAgB,eAAe;;OAG9B,SAAS,MACnB,SAAS,GACT,UAAU,aACP,UAAU;;
|
|
1
|
+
{"version":3,"file":"external-message-converter.d.ts","names":[],"sources":["../../../src/react/runtimes/external-message-converter.ts"],"mappings":";;;;;KAwBY;kBAoBK;OACH,WACP;aACU;eACE,eAAe;;;IAI1B;IACA;IACA;IACA;IACA;IACA;IACA,oBAAoB;;OAGd;aACD,eAAe,eAAe;aAC9B,QAAQ;aACR,gBAAgB,eAAe;;OAG9B,SAAS,MACnB,SAAS,GACT,UAAU,aACP,UAAU;;cAsTJ,0BAA2B,UAAU,SAChD,UAAU,KACV,UAAU,4BAA4B,SAAS,IAC/C,oBACA,UAAU,4BAA4B,aAAQ;cA+CnC,8BAA+B,UAAU,WAAS,UAAA,UAAA,WAAA,cAAA;EAO7D,UAAU,4BAA4B,SAAS;EAC/C,UAAU;EACV;EACA,eAAe;EACf,WAAW,4BAA4B;MACxC"}
|
|
@@ -12,6 +12,19 @@ const isInterruptedToolCall = (c) => {
|
|
|
12
12
|
if (c.type !== "tool-call" || c.result !== void 0) return false;
|
|
13
13
|
return c.interrupt != null || c.approval != null && c.approval.approved === void 0 && c.approval.resolution === void 0;
|
|
14
14
|
};
|
|
15
|
+
const stringifyForError = (value) => {
|
|
16
|
+
let text;
|
|
17
|
+
try {
|
|
18
|
+
text = value instanceof Error ? String(value) : JSON.stringify(value);
|
|
19
|
+
} catch {}
|
|
20
|
+
text ??= String(value);
|
|
21
|
+
return text.length > 200 ? `${text.slice(0, 200)}…` : text;
|
|
22
|
+
};
|
|
23
|
+
const toCallbackOutputs = (output, input) => {
|
|
24
|
+
const outputs = Array.isArray(output) ? output : [output];
|
|
25
|
+
for (const o of outputs) if (!(typeof o === "object" && o !== null && (o.role === "tool" || (o.role === "assistant" || o.role === "user" || o.role === "system") && (typeof o.content === "string" || Array.isArray(o.content))))) throw new Error(`External message converter: the converter callback returned an invalid message (${stringifyForError(o)}) for input ${stringifyForError(input)}. Return an empty array to skip a message.`);
|
|
26
|
+
return outputs;
|
|
27
|
+
};
|
|
15
28
|
const mergeInnerMessages = (existing, incoming) => ({ [symbolInnerMessage]: [...existing[symbolInnerMessage] ?? [], ...incoming[symbolInnerMessage] ?? []] });
|
|
16
29
|
const joinExternalMessages = (messages) => {
|
|
17
30
|
const assistantMessage = {
|
|
@@ -157,7 +170,7 @@ const convertExternalMessages = (messages, callback, isRunning, metadata) => {
|
|
|
157
170
|
const output = callback(message, metadata);
|
|
158
171
|
const result = {
|
|
159
172
|
input: message,
|
|
160
|
-
outputs:
|
|
173
|
+
outputs: toCallbackOutputs(output, message)
|
|
161
174
|
};
|
|
162
175
|
callbackResults.push(result);
|
|
163
176
|
}
|
|
@@ -210,7 +223,7 @@ const useExternalMessageConverter = (t0) => {
|
|
|
210
223
|
const output = state.callback(message, state.metadata);
|
|
211
224
|
result = {
|
|
212
225
|
input: message,
|
|
213
|
-
outputs:
|
|
226
|
+
outputs: toCallbackOutputs(output, message),
|
|
214
227
|
metadata: state.metadata,
|
|
215
228
|
callback: state.callback
|
|
216
229
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-message-converter.js","names":["c","_c","useMemo","useState","ThreadMessageConverter","getExternalStoreMessages","symbolInnerMessage","bindExternalStoreMessage","fromThreadMessageLike","ThreadMessageLike","getAutoStatus","isAutoStatus","ToolExecutionStatus","ReadonlyJSONValue","generateErrorMessageId","ThreadAssistantMessage","ThreadMessage","ToolCallMessagePart","MessageTiming","JoinStrategy","ThreadMessageLikeContentItem","Exclude","isPendingToolCall","type","result","undefined","isInterruptedToolCall","interrupt","approval","approved","resolution","useExternalMessageConverter","Message","convertConfig","joinStrategy","role","toolCallId","toolName","artifact","isError","messages","Metadata","toolStatuses","Record","error","messageTiming","Callback","message","T","metadata","CallbackResult","input","outputs","CallbackCacheEntry","callback","ChunkResult","inputs","Mutable","P","mergeInnerMessages","existing","incoming","joinExternalMessages","assistantMessage","Omit","content","output","toolCallIdx","findIndex","toolCall","Error","const","text","map","length","id","createdAt","status","attachments","unstable_state","unstable_annotations","unstable_data","steps","custom","timing","submittedFeedback","isOptimistic","part","existingIdx","parentId","push","unsupportedRole","chunkExternalMessages","callbackResults","results","isAssistant","pendingNone","flush","callbackResult","at","createErrorAssistantMessage","msg","reason","Date","convertExternalMessages","WeakKey","isRunning","Array","isArray","chunks","idx","isLast","joined","hasInterruptedToolCalls","some","hasPendingToolCalls","autoStatus","newMessage","toString","lastMessage","t0","$","caches","_temp","t1","t2","state","threadMessages","callbackCache","get","set","t3","chunkCache","m","key","cached","shallowArrayEqual","converterCache","convertMessages","cache","message_0","a","b","i","WeakMap"],"sources":["../../../src/react/runtimes/external-message-converter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { ThreadMessageConverter } from \"../../runtimes/external-store/thread-message-converter\";\nimport {\n getExternalStoreMessages,\n symbolInnerMessage,\n bindExternalStoreMessage,\n} from \"../../runtime/utils/external-store-message\";\nimport {\n fromThreadMessageLike,\n type ThreadMessageLike,\n} from \"../../runtime/utils/thread-message-like\";\nimport { getAutoStatus, isAutoStatus } from \"../../runtime/utils/auto-status\";\nimport type { ToolExecutionStatus } from \"../../runtimes/tool-invocations/ToolInvocationTracker\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport { generateErrorMessageId } from \"../../utils/id\";\nimport type {\n ThreadAssistantMessage,\n ThreadMessage,\n ToolCallMessagePart,\n} from \"../../types/message\";\nimport type { MessageTiming } from \"../../types/message\";\n\nexport type JoinStrategy = \"concat-content\" | \"none\";\n\ntype ThreadMessageLikeContentItem = Exclude<\n ThreadMessageLike[\"content\"],\n string\n>[number];\n\nconst isPendingToolCall = (c: ThreadMessageLikeContentItem): boolean =>\n c.type === \"tool-call\" && c.result === undefined;\n\nconst isInterruptedToolCall = (c: ThreadMessageLikeContentItem): boolean => {\n if (c.type !== \"tool-call\" || c.result !== undefined) return false;\n return (\n c.interrupt != null ||\n (c.approval != null &&\n c.approval.approved === undefined &&\n c.approval.resolution === undefined)\n );\n};\n\nexport namespace useExternalMessageConverter {\n export type Message =\n | (ThreadMessageLike & {\n readonly convertConfig?: {\n readonly joinStrategy?: JoinStrategy;\n };\n })\n | {\n role: \"tool\";\n toolCallId: string;\n toolName?: string | undefined;\n result: any;\n artifact?: any;\n isError?: boolean;\n messages?: readonly ThreadMessage[];\n };\n\n export type Metadata = {\n readonly toolStatuses?: Record<string, ToolExecutionStatus>;\n readonly error?: ReadonlyJSONValue;\n readonly messageTiming?: Record<string, MessageTiming>;\n };\n\n export type Callback<T> = (\n message: T,\n metadata: Metadata,\n ) => Message | Message[];\n}\n\ntype CallbackResult<T> = {\n input: T;\n outputs: useExternalMessageConverter.Message[];\n};\n\ntype CallbackCacheEntry<T> = CallbackResult<T> & {\n metadata: useExternalMessageConverter.Metadata;\n callback: useExternalMessageConverter.Callback<T>;\n};\n\ntype ChunkResult<T> = {\n inputs: T[];\n outputs: useExternalMessageConverter.Message[];\n};\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nconst mergeInnerMessages = (existing: object, incoming: object) => ({\n [symbolInnerMessage]: [\n ...((existing as any)[symbolInnerMessage] ?? []),\n ...((incoming as any)[symbolInnerMessage] ?? []),\n ],\n});\n\nconst joinExternalMessages = (\n messages: readonly useExternalMessageConverter.Message[],\n): ThreadMessageLike => {\n const assistantMessage: Mutable<Omit<ThreadMessageLike, \"metadata\">> & {\n content: Exclude<ThreadMessageLike[\"content\"][0], string>[];\n metadata?: Mutable<ThreadMessageLike[\"metadata\"]>;\n } = {\n role: \"assistant\",\n content: [],\n };\n for (const output of messages) {\n if (output.role === \"tool\") {\n const toolCallIdx = assistantMessage.content.findIndex(\n (c) => c.type === \"tool-call\" && c.toolCallId === output.toolCallId,\n );\n // Ignore orphaned tool results so one bad tool message does not\n // prevent rendering the rest of the conversation.\n if (toolCallIdx !== -1) {\n const toolCall = assistantMessage.content[\n toolCallIdx\n ]! as ToolCallMessagePart;\n if (output.toolName != null) {\n if (toolCall.toolName !== output.toolName)\n throw new Error(\n `Tool call name ${output.toolCallId} ${output.toolName} does not match existing tool call ${toolCall.toolName}`,\n );\n }\n assistantMessage.content[toolCallIdx] = {\n ...toolCall,\n ...{\n [symbolInnerMessage]: [\n ...((toolCall as any)[symbolInnerMessage] ?? []),\n output,\n ],\n },\n result: output.result,\n artifact: output.artifact,\n isError: output.isError,\n messages: output.messages,\n };\n }\n } else {\n const role = output.role;\n const content = (\n typeof output.content === \"string\"\n ? [{ type: \"text\" as const, text: output.content }]\n : output.content\n ).map((c) => ({\n ...c,\n ...{ [symbolInnerMessage]: [output] },\n }));\n switch (role) {\n case \"system\":\n case \"user\":\n return {\n ...output,\n content,\n };\n case \"assistant\":\n if (assistantMessage.content.length === 0) {\n assistantMessage.id = output.id;\n assistantMessage.createdAt ??= output.createdAt;\n assistantMessage.status ??= output.status;\n\n if (output.attachments) {\n assistantMessage.attachments = [\n ...(assistantMessage.attachments ?? []),\n ...output.attachments,\n ];\n }\n\n if (output.metadata) {\n assistantMessage.metadata ??= {};\n if (output.metadata.unstable_state !== undefined) {\n assistantMessage.metadata.unstable_state =\n output.metadata.unstable_state;\n }\n if (output.metadata.unstable_annotations) {\n assistantMessage.metadata.unstable_annotations = [\n ...(assistantMessage.metadata.unstable_annotations ?? []),\n ...output.metadata.unstable_annotations,\n ];\n }\n if (output.metadata.unstable_data) {\n assistantMessage.metadata.unstable_data = [\n ...(assistantMessage.metadata.unstable_data ?? []),\n ...output.metadata.unstable_data,\n ];\n }\n if (output.metadata.steps) {\n assistantMessage.metadata.steps = [\n ...(assistantMessage.metadata.steps ?? []),\n ...output.metadata.steps,\n ];\n }\n if (output.metadata.custom) {\n assistantMessage.metadata.custom = {\n ...(assistantMessage.metadata.custom ?? {}),\n ...output.metadata.custom,\n };\n }\n\n if (output.metadata.timing) {\n assistantMessage.metadata.timing = output.metadata.timing;\n }\n\n if (output.metadata.submittedFeedback) {\n assistantMessage.metadata.submittedFeedback =\n output.metadata.submittedFeedback;\n }\n\n if (output.metadata.isOptimistic) {\n assistantMessage.metadata.isOptimistic = true;\n }\n }\n // TODO keep this in sync\n }\n\n // Add content parts, merging reasoning parts with same parentId\n for (const part of content) {\n if (part.type === \"tool-call\") {\n const existingIdx = assistantMessage.content.findIndex(\n (c) =>\n c.type === \"tool-call\" && c.toolCallId === part.toolCallId,\n );\n if (existingIdx !== -1) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n ...part,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n\n if (\n part.type === \"reasoning\" &&\n \"parentId\" in part &&\n part.parentId\n ) {\n const existingIdx = assistantMessage.content.findIndex(\n (c) =>\n c.type === \"reasoning\" &&\n \"parentId\" in c &&\n c.parentId === part.parentId,\n );\n if (existingIdx !== -1) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n text: `${existing.text}\\n\\n${part.text}`,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n assistantMessage.content.push(part);\n }\n break;\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n }\n }\n return assistantMessage;\n};\n\nconst chunkExternalMessages = <T>(\n callbackResults: CallbackResult<T>[],\n joinStrategy?: JoinStrategy,\n) => {\n const results: ChunkResult<T>[] = [];\n let isAssistant = false;\n let pendingNone = false; // true if the previous assistant message had joinStrategy \"none\"\n let inputs: T[] = [];\n let outputs: useExternalMessageConverter.Message[] = [];\n\n const flush = () => {\n if (outputs.length) {\n results.push({\n inputs,\n outputs,\n });\n }\n inputs = [];\n outputs = [];\n isAssistant = false;\n pendingNone = false;\n };\n\n for (const callbackResult of callbackResults) {\n for (const output of callbackResult.outputs) {\n if (\n (pendingNone && output.role !== \"tool\") ||\n !isAssistant ||\n output.role === \"user\" ||\n output.role === \"system\"\n ) {\n flush();\n }\n isAssistant = output.role === \"assistant\" || output.role === \"tool\";\n\n if (inputs.at(-1) !== callbackResult.input) {\n inputs.push(callbackResult.input);\n }\n outputs.push(output);\n\n if (\n output.role === \"assistant\" &&\n (output.convertConfig?.joinStrategy === \"none\" ||\n joinStrategy === \"none\")\n ) {\n pendingNone = true;\n }\n }\n }\n flush();\n return results;\n};\n\nfunction createErrorAssistantMessage(\n error: ReadonlyJSONValue,\n): ThreadAssistantMessage {\n const msg: ThreadAssistantMessage = {\n id: generateErrorMessageId(),\n role: \"assistant\",\n content: [],\n status: { type: \"incomplete\", reason: \"error\", error },\n createdAt: new Date(),\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n custom: {},\n steps: [],\n },\n };\n bindExternalStoreMessage(msg, []);\n return msg;\n}\n\nexport const convertExternalMessages = <T extends WeakKey>(\n messages: T[],\n callback: useExternalMessageConverter.Callback<T>,\n isRunning: boolean,\n metadata: useExternalMessageConverter.Metadata,\n) => {\n const callbackResults: CallbackResult<T>[] = [];\n for (const message of messages) {\n const output = callback(message, metadata);\n const outputs = Array.isArray(output) ? output : [output];\n const result = { input: message, outputs };\n callbackResults.push(result);\n }\n\n const chunks = chunkExternalMessages(callbackResults);\n\n const result = chunks.map((message, idx) => {\n const isLast = idx === chunks.length - 1;\n const joined = joinExternalMessages(message.outputs);\n const hasInterruptedToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isInterruptedToolCall);\n const hasPendingToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isPendingToolCall);\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n hasInterruptedToolCalls,\n hasPendingToolCalls,\n isLast ? metadata.error : undefined,\n );\n const newMessage = fromThreadMessageLike(\n joined,\n idx.toString(),\n autoStatus,\n );\n bindExternalStoreMessage(newMessage, message.inputs);\n return newMessage;\n });\n\n if (metadata.error) {\n const lastMessage = result.at(-1);\n if (!lastMessage || lastMessage.role !== \"assistant\") {\n result.push(createErrorAssistantMessage(metadata.error));\n }\n }\n\n return result;\n};\n\nexport const useExternalMessageConverter = <T extends WeakKey>({\n callback,\n messages,\n isRunning,\n joinStrategy,\n metadata,\n}: {\n callback: useExternalMessageConverter.Callback<T>;\n messages: T[];\n isRunning: boolean;\n joinStrategy?: JoinStrategy | undefined;\n metadata?: useExternalMessageConverter.Metadata | undefined;\n}) => {\n // The caches live for the component lifetime; React Compiler hoists\n // allocations without reactive dependencies out of useMemo, so re-creating\n // them on dependency change would not survive compilation. Staleness is\n // instead tracked per entry via the metadata/callback that produced it,\n // keeping correctness independent of memoization (which React treats as\n // droppable). A \"use no memo\" opt-out would restore the useMemo semantics\n // but leave cache flushing coupled to memo firing.\n const [caches] = useState(() => ({\n callbackCache: new WeakMap<T, CallbackCacheEntry<T>>(),\n chunkCache: new WeakMap<\n useExternalMessageConverter.Message,\n ChunkResult<T>\n >(),\n converterCache: new ThreadMessageConverter(),\n }));\n\n const state = useMemo(\n () => ({\n metadata: metadata ?? {},\n callback,\n ...caches,\n }),\n [callback, metadata, caches],\n );\n\n return useMemo(() => {\n const callbackResults: CallbackResult<T>[] = [];\n for (const message of messages) {\n let result = state.callbackCache.get(message);\n if (\n !result ||\n result.metadata !== state.metadata ||\n result.callback !== state.callback\n ) {\n const output = state.callback(message, state.metadata);\n const outputs = Array.isArray(output) ? output : [output];\n result = {\n input: message,\n outputs,\n metadata: state.metadata,\n callback: state.callback,\n };\n state.callbackCache.set(message, result);\n }\n callbackResults.push(result);\n }\n\n const chunks = chunkExternalMessages(callbackResults, joinStrategy).map(\n (m) => {\n const key = m.outputs[0];\n if (!key) return m;\n\n const cached = state.chunkCache.get(key);\n if (cached && shallowArrayEqual(cached.outputs, m.outputs))\n return cached;\n state.chunkCache.set(key, m);\n return m;\n },\n );\n\n const threadMessages = state.converterCache.convertMessages(\n chunks,\n (cache, message, idx) => {\n const isLast = idx === chunks.length - 1;\n\n const joined = joinExternalMessages(message.outputs);\n const hasInterruptedToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isInterruptedToolCall);\n const hasPendingToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isPendingToolCall);\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n hasInterruptedToolCalls,\n hasPendingToolCalls,\n isLast ? state.metadata.error : undefined,\n );\n\n if (\n cache &&\n (cache.role !== \"assistant\" ||\n !isAutoStatus(cache.status) ||\n cache.status === autoStatus)\n ) {\n const inputs = getExternalStoreMessages<T>(cache);\n if (shallowArrayEqual(inputs, message.inputs)) {\n return cache;\n }\n }\n\n const newMessage = fromThreadMessageLike(\n joined,\n idx.toString(),\n autoStatus,\n );\n bindExternalStoreMessage(newMessage, message.inputs);\n return newMessage;\n },\n );\n\n bindExternalStoreMessage(threadMessages, messages);\n\n if (state.metadata.error) {\n const lastMessage = threadMessages.at(-1);\n if (!lastMessage || lastMessage.role !== \"assistant\") {\n threadMessages.push(createErrorAssistantMessage(state.metadata.error));\n }\n }\n\n return threadMessages;\n }, [state, messages, isRunning, joinStrategy]);\n};\n\nconst shallowArrayEqual = (a: unknown[], b: unknown[]) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"external-message-converter.js","names":["c","_c","useMemo","useState","ThreadMessageConverter","getExternalStoreMessages","symbolInnerMessage","bindExternalStoreMessage","fromThreadMessageLike","ThreadMessageLike","getAutoStatus","isAutoStatus","ToolExecutionStatus","ReadonlyJSONValue","generateErrorMessageId","ThreadAssistantMessage","ThreadMessage","ToolCallMessagePart","MessageTiming","JoinStrategy","ThreadMessageLikeContentItem","Exclude","isPendingToolCall","type","result","undefined","isInterruptedToolCall","interrupt","approval","approved","resolution","useExternalMessageConverter","Message","convertConfig","joinStrategy","role","toolCallId","toolName","artifact","isError","messages","Metadata","toolStatuses","Record","error","messageTiming","Callback","message","T","metadata","CallbackResult","input","outputs","stringifyForError","value","text","Error","String","JSON","stringify","length","slice","toCallbackOutputs","output","Array","isArray","o","valid","content","CallbackCacheEntry","callback","ChunkResult","inputs","Mutable","P","mergeInnerMessages","existing","incoming","joinExternalMessages","assistantMessage","Omit","toolCallIdx","findIndex","toolCall","const","map","id","createdAt","status","attachments","unstable_state","unstable_annotations","unstable_data","steps","custom","timing","submittedFeedback","isOptimistic","part","existingIdx","parentId","push","unsupportedRole","chunkExternalMessages","callbackResults","results","isAssistant","pendingNone","flush","callbackResult","at","createErrorAssistantMessage","msg","reason","Date","convertExternalMessages","WeakKey","isRunning","chunks","idx","isLast","joined","hasInterruptedToolCalls","some","hasPendingToolCalls","autoStatus","newMessage","toString","lastMessage","t0","$","caches","_temp","t1","t2","state","threadMessages","callbackCache","get","set","t3","chunkCache","m","key","cached","shallowArrayEqual","converterCache","convertMessages","cache","message_0","a","b","i","WeakMap"],"sources":["../../../src/react/runtimes/external-message-converter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { ThreadMessageConverter } from \"../../runtimes/external-store/thread-message-converter\";\nimport {\n getExternalStoreMessages,\n symbolInnerMessage,\n bindExternalStoreMessage,\n} from \"../../runtime/utils/external-store-message\";\nimport {\n fromThreadMessageLike,\n type ThreadMessageLike,\n} from \"../../runtime/utils/thread-message-like\";\nimport { getAutoStatus, isAutoStatus } from \"../../runtime/utils/auto-status\";\nimport type { ToolExecutionStatus } from \"../../runtimes/tool-invocations/ToolInvocationTracker\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport { generateErrorMessageId } from \"../../utils/id\";\nimport type {\n ThreadAssistantMessage,\n ThreadMessage,\n ToolCallMessagePart,\n} from \"../../types/message\";\nimport type { MessageTiming } from \"../../types/message\";\n\nexport type JoinStrategy = \"concat-content\" | \"none\";\n\ntype ThreadMessageLikeContentItem = Exclude<\n ThreadMessageLike[\"content\"],\n string\n>[number];\n\nconst isPendingToolCall = (c: ThreadMessageLikeContentItem): boolean =>\n c.type === \"tool-call\" && c.result === undefined;\n\nconst isInterruptedToolCall = (c: ThreadMessageLikeContentItem): boolean => {\n if (c.type !== \"tool-call\" || c.result !== undefined) return false;\n return (\n c.interrupt != null ||\n (c.approval != null &&\n c.approval.approved === undefined &&\n c.approval.resolution === undefined)\n );\n};\n\nexport namespace useExternalMessageConverter {\n export type Message =\n | (ThreadMessageLike & {\n readonly convertConfig?: {\n readonly joinStrategy?: JoinStrategy;\n };\n })\n | {\n role: \"tool\";\n toolCallId: string;\n toolName?: string | undefined;\n result: any;\n artifact?: any;\n isError?: boolean;\n messages?: readonly ThreadMessage[];\n };\n\n export type Metadata = {\n readonly toolStatuses?: Record<string, ToolExecutionStatus>;\n readonly error?: ReadonlyJSONValue;\n readonly messageTiming?: Record<string, MessageTiming>;\n };\n\n export type Callback<T> = (\n message: T,\n metadata: Metadata,\n ) => Message | Message[];\n}\n\ntype CallbackResult<T> = {\n input: T;\n outputs: useExternalMessageConverter.Message[];\n};\n\nconst stringifyForError = (value: unknown) => {\n let text;\n try {\n text = value instanceof Error ? String(value) : JSON.stringify(value);\n } catch {\n /* circular */\n }\n text ??= String(value);\n return text.length > 200 ? `${text.slice(0, 200)}…` : text;\n};\n\nconst toCallbackOutputs = (\n output:\n | useExternalMessageConverter.Message\n | useExternalMessageConverter.Message[],\n input: unknown,\n): useExternalMessageConverter.Message[] => {\n const outputs = Array.isArray(output) ? output : [output];\n for (const o of outputs) {\n const valid =\n typeof o === \"object\" &&\n o !== null &&\n (o.role === \"tool\" ||\n ((o.role === \"assistant\" || o.role === \"user\" || o.role === \"system\") &&\n (typeof o.content === \"string\" || Array.isArray(o.content))));\n if (!valid)\n throw new Error(\n `External message converter: the converter callback returned an invalid message (${stringifyForError(o)}) for input ${stringifyForError(input)}. Return an empty array to skip a message.`,\n );\n }\n return outputs;\n};\n\ntype CallbackCacheEntry<T> = CallbackResult<T> & {\n metadata: useExternalMessageConverter.Metadata;\n callback: useExternalMessageConverter.Callback<T>;\n};\n\ntype ChunkResult<T> = {\n inputs: T[];\n outputs: useExternalMessageConverter.Message[];\n};\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nconst mergeInnerMessages = (existing: object, incoming: object) => ({\n [symbolInnerMessage]: [\n ...((existing as any)[symbolInnerMessage] ?? []),\n ...((incoming as any)[symbolInnerMessage] ?? []),\n ],\n});\n\nconst joinExternalMessages = (\n messages: readonly useExternalMessageConverter.Message[],\n): ThreadMessageLike => {\n const assistantMessage: Mutable<Omit<ThreadMessageLike, \"metadata\">> & {\n content: Exclude<ThreadMessageLike[\"content\"][0], string>[];\n metadata?: Mutable<ThreadMessageLike[\"metadata\"]>;\n } = {\n role: \"assistant\",\n content: [],\n };\n for (const output of messages) {\n if (output.role === \"tool\") {\n const toolCallIdx = assistantMessage.content.findIndex(\n (c) => c.type === \"tool-call\" && c.toolCallId === output.toolCallId,\n );\n // Ignore orphaned tool results so one bad tool message does not\n // prevent rendering the rest of the conversation.\n if (toolCallIdx !== -1) {\n const toolCall = assistantMessage.content[\n toolCallIdx\n ]! as ToolCallMessagePart;\n if (output.toolName != null) {\n if (toolCall.toolName !== output.toolName)\n throw new Error(\n `Tool call name ${output.toolCallId} ${output.toolName} does not match existing tool call ${toolCall.toolName}`,\n );\n }\n assistantMessage.content[toolCallIdx] = {\n ...toolCall,\n ...{\n [symbolInnerMessage]: [\n ...((toolCall as any)[symbolInnerMessage] ?? []),\n output,\n ],\n },\n result: output.result,\n artifact: output.artifact,\n isError: output.isError,\n messages: output.messages,\n };\n }\n } else {\n const role = output.role;\n const content = (\n typeof output.content === \"string\"\n ? [{ type: \"text\" as const, text: output.content }]\n : output.content\n ).map((c) => ({\n ...c,\n ...{ [symbolInnerMessage]: [output] },\n }));\n switch (role) {\n case \"system\":\n case \"user\":\n return {\n ...output,\n content,\n };\n case \"assistant\":\n if (assistantMessage.content.length === 0) {\n assistantMessage.id = output.id;\n assistantMessage.createdAt ??= output.createdAt;\n assistantMessage.status ??= output.status;\n\n if (output.attachments) {\n assistantMessage.attachments = [\n ...(assistantMessage.attachments ?? []),\n ...output.attachments,\n ];\n }\n\n if (output.metadata) {\n assistantMessage.metadata ??= {};\n if (output.metadata.unstable_state !== undefined) {\n assistantMessage.metadata.unstable_state =\n output.metadata.unstable_state;\n }\n if (output.metadata.unstable_annotations) {\n assistantMessage.metadata.unstable_annotations = [\n ...(assistantMessage.metadata.unstable_annotations ?? []),\n ...output.metadata.unstable_annotations,\n ];\n }\n if (output.metadata.unstable_data) {\n assistantMessage.metadata.unstable_data = [\n ...(assistantMessage.metadata.unstable_data ?? []),\n ...output.metadata.unstable_data,\n ];\n }\n if (output.metadata.steps) {\n assistantMessage.metadata.steps = [\n ...(assistantMessage.metadata.steps ?? []),\n ...output.metadata.steps,\n ];\n }\n if (output.metadata.custom) {\n assistantMessage.metadata.custom = {\n ...(assistantMessage.metadata.custom ?? {}),\n ...output.metadata.custom,\n };\n }\n\n if (output.metadata.timing) {\n assistantMessage.metadata.timing = output.metadata.timing;\n }\n\n if (output.metadata.submittedFeedback) {\n assistantMessage.metadata.submittedFeedback =\n output.metadata.submittedFeedback;\n }\n\n if (output.metadata.isOptimistic) {\n assistantMessage.metadata.isOptimistic = true;\n }\n }\n // TODO keep this in sync\n }\n\n // Add content parts, merging reasoning parts with same parentId\n for (const part of content) {\n if (part.type === \"tool-call\") {\n const existingIdx = assistantMessage.content.findIndex(\n (c) =>\n c.type === \"tool-call\" && c.toolCallId === part.toolCallId,\n );\n if (existingIdx !== -1) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n ...part,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n\n if (\n part.type === \"reasoning\" &&\n \"parentId\" in part &&\n part.parentId\n ) {\n const existingIdx = assistantMessage.content.findIndex(\n (c) =>\n c.type === \"reasoning\" &&\n \"parentId\" in c &&\n c.parentId === part.parentId,\n );\n if (existingIdx !== -1) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n text: `${existing.text}\\n\\n${part.text}`,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n assistantMessage.content.push(part);\n }\n break;\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n }\n }\n return assistantMessage;\n};\n\nconst chunkExternalMessages = <T>(\n callbackResults: CallbackResult<T>[],\n joinStrategy?: JoinStrategy,\n) => {\n const results: ChunkResult<T>[] = [];\n let isAssistant = false;\n let pendingNone = false; // true if the previous assistant message had joinStrategy \"none\"\n let inputs: T[] = [];\n let outputs: useExternalMessageConverter.Message[] = [];\n\n const flush = () => {\n if (outputs.length) {\n results.push({\n inputs,\n outputs,\n });\n }\n inputs = [];\n outputs = [];\n isAssistant = false;\n pendingNone = false;\n };\n\n for (const callbackResult of callbackResults) {\n for (const output of callbackResult.outputs) {\n if (\n (pendingNone && output.role !== \"tool\") ||\n !isAssistant ||\n output.role === \"user\" ||\n output.role === \"system\"\n ) {\n flush();\n }\n isAssistant = output.role === \"assistant\" || output.role === \"tool\";\n\n if (inputs.at(-1) !== callbackResult.input) {\n inputs.push(callbackResult.input);\n }\n outputs.push(output);\n\n if (\n output.role === \"assistant\" &&\n (output.convertConfig?.joinStrategy === \"none\" ||\n joinStrategy === \"none\")\n ) {\n pendingNone = true;\n }\n }\n }\n flush();\n return results;\n};\n\nfunction createErrorAssistantMessage(\n error: ReadonlyJSONValue,\n): ThreadAssistantMessage {\n const msg: ThreadAssistantMessage = {\n id: generateErrorMessageId(),\n role: \"assistant\",\n content: [],\n status: { type: \"incomplete\", reason: \"error\", error },\n createdAt: new Date(),\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n custom: {},\n steps: [],\n },\n };\n bindExternalStoreMessage(msg, []);\n return msg;\n}\n\nexport const convertExternalMessages = <T extends WeakKey>(\n messages: T[],\n callback: useExternalMessageConverter.Callback<T>,\n isRunning: boolean,\n metadata: useExternalMessageConverter.Metadata,\n) => {\n const callbackResults: CallbackResult<T>[] = [];\n for (const message of messages) {\n const output = callback(message, metadata);\n const outputs = toCallbackOutputs(output, message);\n const result = { input: message, outputs };\n callbackResults.push(result);\n }\n\n const chunks = chunkExternalMessages(callbackResults);\n\n const result = chunks.map((message, idx) => {\n const isLast = idx === chunks.length - 1;\n const joined = joinExternalMessages(message.outputs);\n const hasInterruptedToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isInterruptedToolCall);\n const hasPendingToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isPendingToolCall);\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n hasInterruptedToolCalls,\n hasPendingToolCalls,\n isLast ? metadata.error : undefined,\n );\n const newMessage = fromThreadMessageLike(\n joined,\n idx.toString(),\n autoStatus,\n );\n bindExternalStoreMessage(newMessage, message.inputs);\n return newMessage;\n });\n\n if (metadata.error) {\n const lastMessage = result.at(-1);\n if (!lastMessage || lastMessage.role !== \"assistant\") {\n result.push(createErrorAssistantMessage(metadata.error));\n }\n }\n\n return result;\n};\n\nexport const useExternalMessageConverter = <T extends WeakKey>({\n callback,\n messages,\n isRunning,\n joinStrategy,\n metadata,\n}: {\n callback: useExternalMessageConverter.Callback<T>;\n messages: T[];\n isRunning: boolean;\n joinStrategy?: JoinStrategy | undefined;\n metadata?: useExternalMessageConverter.Metadata | undefined;\n}) => {\n // The caches live for the component lifetime; React Compiler hoists\n // allocations without reactive dependencies out of useMemo, so re-creating\n // them on dependency change would not survive compilation. Staleness is\n // instead tracked per entry via the metadata/callback that produced it,\n // keeping correctness independent of memoization (which React treats as\n // droppable). A \"use no memo\" opt-out would restore the useMemo semantics\n // but leave cache flushing coupled to memo firing.\n const [caches] = useState(() => ({\n callbackCache: new WeakMap<T, CallbackCacheEntry<T>>(),\n chunkCache: new WeakMap<\n useExternalMessageConverter.Message,\n ChunkResult<T>\n >(),\n converterCache: new ThreadMessageConverter(),\n }));\n\n const state = useMemo(\n () => ({\n metadata: metadata ?? {},\n callback,\n ...caches,\n }),\n [callback, metadata, caches],\n );\n\n return useMemo(() => {\n const callbackResults: CallbackResult<T>[] = [];\n for (const message of messages) {\n let result = state.callbackCache.get(message);\n if (\n !result ||\n result.metadata !== state.metadata ||\n result.callback !== state.callback\n ) {\n const output = state.callback(message, state.metadata);\n const outputs = toCallbackOutputs(output, message);\n result = {\n input: message,\n outputs,\n metadata: state.metadata,\n callback: state.callback,\n };\n state.callbackCache.set(message, result);\n }\n callbackResults.push(result);\n }\n\n const chunks = chunkExternalMessages(callbackResults, joinStrategy).map(\n (m) => {\n const key = m.outputs[0];\n if (!key) return m;\n\n const cached = state.chunkCache.get(key);\n if (cached && shallowArrayEqual(cached.outputs, m.outputs))\n return cached;\n state.chunkCache.set(key, m);\n return m;\n },\n );\n\n const threadMessages = state.converterCache.convertMessages(\n chunks,\n (cache, message, idx) => {\n const isLast = idx === chunks.length - 1;\n\n const joined = joinExternalMessages(message.outputs);\n const hasInterruptedToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isInterruptedToolCall);\n const hasPendingToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isPendingToolCall);\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n hasInterruptedToolCalls,\n hasPendingToolCalls,\n isLast ? state.metadata.error : undefined,\n );\n\n if (\n cache &&\n (cache.role !== \"assistant\" ||\n !isAutoStatus(cache.status) ||\n cache.status === autoStatus)\n ) {\n const inputs = getExternalStoreMessages<T>(cache);\n if (shallowArrayEqual(inputs, message.inputs)) {\n return cache;\n }\n }\n\n const newMessage = fromThreadMessageLike(\n joined,\n idx.toString(),\n autoStatus,\n );\n bindExternalStoreMessage(newMessage, message.inputs);\n return newMessage;\n },\n );\n\n bindExternalStoreMessage(threadMessages, messages);\n\n if (state.metadata.error) {\n const lastMessage = threadMessages.at(-1);\n if (!lastMessage || lastMessage.role !== \"assistant\") {\n threadMessages.push(createErrorAssistantMessage(state.metadata.error));\n }\n }\n\n return threadMessages;\n }, [state, messages, isRunning, joinStrategy]);\n};\n\nconst shallowArrayEqual = (a: unknown[], b: unknown[]) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "Framework-agnostic core runtime for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
],
|
|
60
60
|
"sideEffects": false,
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"assistant-stream": "^0.3.
|
|
62
|
+
"assistant-stream": "^0.3.37",
|
|
63
63
|
"nanoid": "^6.0.1"
|
|
64
64
|
},
|
|
65
65
|
"optionalDevDependencies": {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { renderHook } from "@testing-library/react";
|
|
4
|
+
import type { AssistantCloud } from "assistant-cloud";
|
|
5
|
+
import { describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { useCloudThreadListAdapter } from "./useCloudThreadListAdapter";
|
|
7
|
+
|
|
8
|
+
const makeThread = (id: string) => ({
|
|
9
|
+
id,
|
|
10
|
+
title: id,
|
|
11
|
+
is_archived: false,
|
|
12
|
+
external_id: null,
|
|
13
|
+
metadata: null,
|
|
14
|
+
last_message_at: new Date(0),
|
|
15
|
+
created_at: new Date(0),
|
|
16
|
+
updated_at: new Date(0),
|
|
17
|
+
project_id: "project-1",
|
|
18
|
+
workspace_id: "workspace-1",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("useCloudThreadListAdapter", () => {
|
|
22
|
+
it("exposes full Cloud pages through the remote thread cursor", async () => {
|
|
23
|
+
const firstPage = Array.from({ length: 20 }, (_, index) =>
|
|
24
|
+
makeThread(`thread-${index + 1}`),
|
|
25
|
+
);
|
|
26
|
+
const secondPage = [makeThread("thread-21")];
|
|
27
|
+
const list = vi
|
|
28
|
+
.fn()
|
|
29
|
+
.mockResolvedValueOnce({ threads: firstPage })
|
|
30
|
+
.mockResolvedValueOnce({ threads: secondPage });
|
|
31
|
+
const cloud = { threads: { list } } as unknown as AssistantCloud;
|
|
32
|
+
const { result } = renderHook(() => useCloudThreadListAdapter({ cloud }));
|
|
33
|
+
|
|
34
|
+
const first = await result.current.list();
|
|
35
|
+
expect(list).toHaveBeenNthCalledWith(1, { limit: 20 });
|
|
36
|
+
expect(first.threads).toHaveLength(20);
|
|
37
|
+
expect(first.nextCursor).toBe("thread-20");
|
|
38
|
+
|
|
39
|
+
const second = await result.current.list({ after: first.nextCursor });
|
|
40
|
+
expect(list).toHaveBeenNthCalledWith(2, {
|
|
41
|
+
limit: 20,
|
|
42
|
+
after: "thread-20",
|
|
43
|
+
});
|
|
44
|
+
expect(second.threads.map((thread) => thread.remoteId)).toEqual([
|
|
45
|
+
"thread-21",
|
|
46
|
+
]);
|
|
47
|
+
expect(second.nextCursor).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -37,6 +37,8 @@ const autoCloud = baseUrl
|
|
|
37
37
|
? new AssistantCloud({ baseUrl, anonymous: true })
|
|
38
38
|
: undefined;
|
|
39
39
|
|
|
40
|
+
const CLOUD_THREAD_PAGE_SIZE = 20;
|
|
41
|
+
|
|
40
42
|
export const useCloudThreadListAdapter = (
|
|
41
43
|
adapter: CloudThreadListAdapterOptions,
|
|
42
44
|
): RemoteThreadListAdapter => {
|
|
@@ -88,8 +90,11 @@ export const useCloudThreadListAdapter = (
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
return {
|
|
91
|
-
list: async () => {
|
|
92
|
-
const { threads } = await cloud.threads.list(
|
|
93
|
+
list: async ({ after } = {}) => {
|
|
94
|
+
const { threads } = await cloud.threads.list({
|
|
95
|
+
limit: CLOUD_THREAD_PAGE_SIZE,
|
|
96
|
+
...(after ? { after } : {}),
|
|
97
|
+
});
|
|
93
98
|
return {
|
|
94
99
|
threads: threads.map((t) => ({
|
|
95
100
|
status: t.is_archived ? "archived" : "regular",
|
|
@@ -101,6 +106,10 @@ export const useCloudThreadListAdapter = (
|
|
|
101
106
|
externalId: t.external_id ?? undefined,
|
|
102
107
|
custom: toCustom(t.metadata),
|
|
103
108
|
})),
|
|
109
|
+
nextCursor:
|
|
110
|
+
threads.length === CLOUD_THREAD_PAGE_SIZE
|
|
111
|
+
? threads.at(-1)?.id
|
|
112
|
+
: undefined,
|
|
104
113
|
};
|
|
105
114
|
},
|
|
106
115
|
|
|
@@ -343,4 +343,75 @@ describe("convertExternalMessages", () => {
|
|
|
343
343
|
expect(result[0]!.role).toBe("user");
|
|
344
344
|
});
|
|
345
345
|
});
|
|
346
|
+
|
|
347
|
+
describe("invalid converter output", () => {
|
|
348
|
+
it("throws a descriptive error when the callback returns undefined", () => {
|
|
349
|
+
const messages = [{ id: "m1", type: "remove" }];
|
|
350
|
+
const callback = (() =>
|
|
351
|
+
undefined) as unknown as useExternalMessageConverter.Callback<
|
|
352
|
+
(typeof messages)[number]
|
|
353
|
+
>;
|
|
354
|
+
|
|
355
|
+
expect(() =>
|
|
356
|
+
convertExternalMessages(messages, callback, false, {}),
|
|
357
|
+
).toThrowError(
|
|
358
|
+
/returned an invalid message \(undefined\) for input \{"id":"m1","type":"remove"\}/,
|
|
359
|
+
);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("throws a descriptive error when the callback returns an array containing undefined", () => {
|
|
363
|
+
const messages = [{ id: "m1", role: "user" as const, content: "hi" }];
|
|
364
|
+
const callback = ((msg: (typeof messages)[number]) => [
|
|
365
|
+
msg,
|
|
366
|
+
undefined,
|
|
367
|
+
]) as unknown as useExternalMessageConverter.Callback<
|
|
368
|
+
(typeof messages)[number]
|
|
369
|
+
>;
|
|
370
|
+
|
|
371
|
+
expect(() =>
|
|
372
|
+
convertExternalMessages(messages, callback, false, {}),
|
|
373
|
+
).toThrowError(/returned an invalid message \(undefined\)/);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("throws a descriptive error when the callback returns an unsupported role", () => {
|
|
377
|
+
const messages = [{ id: "m1", role: "user" as const, content: "hi" }];
|
|
378
|
+
const callback = (() => ({
|
|
379
|
+
role: "other",
|
|
380
|
+
content: "hi",
|
|
381
|
+
})) as unknown as useExternalMessageConverter.Callback<
|
|
382
|
+
(typeof messages)[number]
|
|
383
|
+
>;
|
|
384
|
+
|
|
385
|
+
expect(() =>
|
|
386
|
+
convertExternalMessages(messages, callback, false, {}),
|
|
387
|
+
).toThrowError(/returned an invalid message \(\{"role":"other"/);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("throws a descriptive error when the callback returns a message without content", () => {
|
|
391
|
+
const messages = [{ id: "m1", role: "user" as const, content: "hi" }];
|
|
392
|
+
const callback = (() => ({
|
|
393
|
+
role: "user",
|
|
394
|
+
})) as unknown as useExternalMessageConverter.Callback<
|
|
395
|
+
(typeof messages)[number]
|
|
396
|
+
>;
|
|
397
|
+
|
|
398
|
+
expect(() =>
|
|
399
|
+
convertExternalMessages(messages, callback, false, {}),
|
|
400
|
+
).toThrowError(/returned an invalid message \(\{"role":"user"\}\)/);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("tolerates a tool message without toolCallId as an orphaned tool result", () => {
|
|
404
|
+
const messages = [{ id: "m1", role: "user" as const, content: "hi" }];
|
|
405
|
+
const callback = ((msg: (typeof messages)[number]) => [
|
|
406
|
+
msg,
|
|
407
|
+
{ role: "tool", result: "ok" },
|
|
408
|
+
]) as unknown as useExternalMessageConverter.Callback<
|
|
409
|
+
(typeof messages)[number]
|
|
410
|
+
>;
|
|
411
|
+
|
|
412
|
+
const result = convertExternalMessages(messages, callback, false, {});
|
|
413
|
+
expect(result[0]!.role).toBe("user");
|
|
414
|
+
expect(result[1]!.content).toHaveLength(0);
|
|
415
|
+
});
|
|
416
|
+
});
|
|
346
417
|
});
|
|
@@ -100,6 +100,15 @@ describe("useExternalMessageConverter", () => {
|
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
it("throws the descriptive converter error when the callback returns an invalid message", () => {
|
|
104
|
+
const bad = (() =>
|
|
105
|
+
undefined) as unknown as useExternalMessageConverter.Callback<TestMessage>;
|
|
106
|
+
|
|
107
|
+
expect(() => renderConverter({ callback: bad })).toThrowError(
|
|
108
|
+
/External message converter: the converter callback returned an invalid message \(undefined\) for input \{"id":"u1"/,
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
103
112
|
it.each([
|
|
104
113
|
["false", false],
|
|
105
114
|
["zero", 0],
|
|
@@ -76,6 +76,39 @@ type CallbackResult<T> = {
|
|
|
76
76
|
outputs: useExternalMessageConverter.Message[];
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
+
const stringifyForError = (value: unknown) => {
|
|
80
|
+
let text;
|
|
81
|
+
try {
|
|
82
|
+
text = value instanceof Error ? String(value) : JSON.stringify(value);
|
|
83
|
+
} catch {
|
|
84
|
+
/* circular */
|
|
85
|
+
}
|
|
86
|
+
text ??= String(value);
|
|
87
|
+
return text.length > 200 ? `${text.slice(0, 200)}…` : text;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const toCallbackOutputs = (
|
|
91
|
+
output:
|
|
92
|
+
| useExternalMessageConverter.Message
|
|
93
|
+
| useExternalMessageConverter.Message[],
|
|
94
|
+
input: unknown,
|
|
95
|
+
): useExternalMessageConverter.Message[] => {
|
|
96
|
+
const outputs = Array.isArray(output) ? output : [output];
|
|
97
|
+
for (const o of outputs) {
|
|
98
|
+
const valid =
|
|
99
|
+
typeof o === "object" &&
|
|
100
|
+
o !== null &&
|
|
101
|
+
(o.role === "tool" ||
|
|
102
|
+
((o.role === "assistant" || o.role === "user" || o.role === "system") &&
|
|
103
|
+
(typeof o.content === "string" || Array.isArray(o.content))));
|
|
104
|
+
if (!valid)
|
|
105
|
+
throw new Error(
|
|
106
|
+
`External message converter: the converter callback returned an invalid message (${stringifyForError(o)}) for input ${stringifyForError(input)}. Return an empty array to skip a message.`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return outputs;
|
|
110
|
+
};
|
|
111
|
+
|
|
79
112
|
type CallbackCacheEntry<T> = CallbackResult<T> & {
|
|
80
113
|
metadata: useExternalMessageConverter.Metadata;
|
|
81
114
|
callback: useExternalMessageConverter.Callback<T>;
|
|
@@ -354,7 +387,7 @@ export const convertExternalMessages = <T extends WeakKey>(
|
|
|
354
387
|
const callbackResults: CallbackResult<T>[] = [];
|
|
355
388
|
for (const message of messages) {
|
|
356
389
|
const output = callback(message, metadata);
|
|
357
|
-
const outputs =
|
|
390
|
+
const outputs = toCallbackOutputs(output, message);
|
|
358
391
|
const result = { input: message, outputs };
|
|
359
392
|
callbackResults.push(result);
|
|
360
393
|
}
|
|
@@ -444,7 +477,7 @@ export const useExternalMessageConverter = <T extends WeakKey>({
|
|
|
444
477
|
result.callback !== state.callback
|
|
445
478
|
) {
|
|
446
479
|
const output = state.callback(message, state.metadata);
|
|
447
|
-
const outputs =
|
|
480
|
+
const outputs = toCallbackOutputs(output, message);
|
|
448
481
|
result = {
|
|
449
482
|
input: message,
|
|
450
483
|
outputs,
|