@assistant-ui/react-a2a 0.2.49 → 0.2.50
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/conversions.d.ts.map +1 -1
- package/dist/conversions.js +5 -4
- package/dist/conversions.js.map +1 -1
- package/package.json +5 -5
- package/src/conversions.test.ts +28 -0
- package/src/conversions.ts +8 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../src/conversions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EACtB,aAAa,EACd,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../src/conversions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EACtB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAO5B,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,mBAAgB;AAwBjE,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAarD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,CAGtE;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,OAAO,EAAE,GACxB,SAAS,OAAO,EAAE,CAGpB;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,GACZ,sBAAsB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAwC3C;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EAAE,GACf,sBAAsB,CAAC,SAAS,CAAC,CAInC;AAcD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAEhE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAEnE;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CAkB3E;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,aAAa,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACtD,CAAC,EACF,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,EAAE,CA8EX;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,UAAU,GAClB,sBAAsB,CAAC,SAAS,CAAC,CAEnC;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxB,GACL,UAAU,CA6BZ"}
|
package/dist/conversions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { parseDataUrl, resolveFilePartSource, resolveImageMediaType } from "@assistant-ui/core/internal";
|
|
2
|
+
import { parseBase64, parseDataUrl, resolveFilePartSource, resolveImageMediaType } from "@assistant-ui/core/internal";
|
|
3
3
|
// src/conversions.ts
|
|
4
4
|
function isImageMediaType(mediaType) {
|
|
5
5
|
return !!mediaType && mediaType.startsWith("image/");
|
|
@@ -119,9 +119,10 @@ function contentPartsToA2AParts(content, fallbackMimeType) {
|
|
|
119
119
|
case "image": {
|
|
120
120
|
if (!part.image) return null;
|
|
121
121
|
const parsed = parseDataUrl(part.image);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
const data = parsed?.data ?? parseBase64(part.image);
|
|
123
|
+
if (data !== null) return {
|
|
124
|
+
raw: data,
|
|
125
|
+
mediaType: resolveImageMediaType(parsed ? part.image : data, fallbackMimeType),
|
|
125
126
|
...part.filename && { filename: part.filename }
|
|
126
127
|
};
|
|
127
128
|
return {
|
package/dist/conversions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversions.js","names":["isRecord"],"sources":["../src/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n MessageStatus,\n ThreadAssistantMessage,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport {\n parseDataUrl,\n resolveFilePartSource,\n resolveImageMediaType,\n} from \"@assistant-ui/core/internal\";\nimport type { A2AMessage, A2APart, A2ATaskState } from \"./types\";\n\nfunction isImageMediaType(mediaType?: string): boolean {\n return !!mediaType && mediaType.startsWith(\"image/\");\n}\n\nconst A2UI_OPERATION_KEYS = [\n \"createSurface\",\n \"updateComponents\",\n \"updateDataModel\",\n \"deleteSurface\",\n] as const;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isA2uiOperation = (value: unknown): boolean =>\n isRecord(value) &&\n typeof value.version === \"string\" &&\n A2UI_OPERATION_KEYS.some((key) => Object.hasOwn(value, key));\n\nconst hasA2uiMediaType = (value: unknown): boolean =>\n typeof value === \"string\" && value.toLowerCase().includes(\"a2ui\");\n\nexport function isA2uiDataPart(part: A2APart): boolean {\n if (part.data === undefined) return false;\n\n const metadata = isRecord(part.metadata) ? part.metadata : {};\n return (\n hasA2uiMediaType(part.mediaType) ||\n hasA2uiMediaType(metadata.mimeType) ||\n hasA2uiMediaType(metadata.mediaType) ||\n isA2uiOperation(part.data) ||\n (Array.isArray(part.data) &&\n part.data.length > 0 &&\n part.data.every(isA2uiOperation))\n );\n}\n\nexport function a2uiPartToOperations(part: A2APart): readonly unknown[] {\n if (!isA2uiDataPart(part)) return [];\n return Array.isArray(part.data) ? part.data : [part.data];\n}\n\nexport function a2uiPartsToOperations(\n parts: readonly A2APart[],\n): readonly unknown[] {\n if (!Array.isArray(parts)) return [];\n return parts.flatMap(a2uiPartToOperations);\n}\n\nexport function a2aPartToContent(\n part: A2APart,\n): ThreadAssistantMessage[\"content\"][number] {\n if (!isRecord(part)) return { type: \"text\", text: \"\" };\n if (part.text != null) {\n return { type: \"text\", text: part.text };\n }\n if (part.url != null) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: part.url,\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType ?? \"application/octet-stream\",\n sourceType: \"url\",\n ...(part.filename && { filename: part.filename }),\n };\n }\n if (part.raw != null) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: `data:${part.mediaType};base64,${part.raw}`,\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n type: \"file\",\n data: part.raw,\n mimeType: part.mediaType ?? \"application/octet-stream\",\n ...(part.filename && { filename: part.filename }),\n };\n }\n if (part.data !== undefined) {\n return { type: \"text\", text: JSON.stringify(part.data, null, 2) };\n }\n return { type: \"text\", text: \"\" };\n}\n\nexport function a2aPartsToContent(\n parts: A2APart[],\n): ThreadAssistantMessage[\"content\"] {\n return (Array.isArray(parts) ? parts : [])\n .filter((part) => isRecord(part) && !isA2uiDataPart(part))\n .map(a2aPartToContent);\n}\n\nconst TERMINAL_STATES = new Set<A2ATaskState>([\n \"completed\",\n \"failed\",\n \"canceled\",\n \"rejected\",\n]);\n\nconst INTERRUPTED_STATES = new Set<A2ATaskState>([\n \"input_required\",\n \"auth_required\",\n]);\n\nexport function isTerminalTaskState(state: A2ATaskState): boolean {\n return TERMINAL_STATES.has(state);\n}\n\nexport function isInterruptedTaskState(state: A2ATaskState): boolean {\n return INTERRUPTED_STATES.has(state);\n}\n\nexport function taskStateToMessageStatus(state: A2ATaskState): MessageStatus {\n switch (state) {\n case \"submitted\":\n case \"working\":\n return { type: \"running\" };\n case \"completed\":\n return { type: \"complete\", reason: \"stop\" };\n case \"failed\":\n case \"rejected\":\n return { type: \"incomplete\", reason: \"error\" };\n case \"canceled\":\n return { type: \"incomplete\", reason: \"cancelled\" };\n case \"input_required\":\n case \"auth_required\":\n return { type: \"requires-action\", reason: \"interrupt\" };\n default:\n return { type: \"running\" };\n }\n}\n\nexport function contentPartsToA2AParts(\n content: ReadonlyArray<{\n type: string;\n text?: string | undefined;\n image?: string | undefined;\n data?: unknown;\n mimeType?: string | undefined;\n filename?: string | undefined;\n sourceType?: \"url\" | \"id\" | undefined;\n audio?: { data: string; format: string } | undefined;\n }>,\n fallbackMimeType?: string,\n): A2APart[] {\n return content\n .map((part): A2APart | null => {\n switch (part.type) {\n case \"text\":\n return { text: part.text ?? \"\" };\n case \"image\": {\n if (!part.image) return null;\n const parsed = parseDataUrl(part.image);\n if (parsed) {\n return {\n raw: parsed.data,\n mediaType: resolveImageMediaType(part.image, fallbackMimeType),\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n url: part.image,\n ...(fallbackMimeType && { mediaType: fallbackMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"file\": {\n if (typeof part.data !== \"string\") return null;\n const declaredMimeType = part.mimeType || fallbackMimeType;\n const source = resolveFilePartSource({\n data: part.data,\n mimeType: declaredMimeType ?? \"application/octet-stream\",\n sourceType: part.sourceType,\n });\n if (source.kind === \"url\") {\n return {\n url: source.url,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n const parsed = parseDataUrl(part.data);\n if (parsed) {\n return {\n raw: source.data,\n mediaType: source.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n }\n if (/^data:/i.test(part.data)) {\n return {\n url: part.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n raw: source.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"audio\": {\n if (!part.audio) return null;\n return {\n raw: parseDataUrl(part.audio.data)?.data ?? part.audio.data,\n mediaType: `audio/${part.audio.format}`,\n };\n }\n case \"data\": {\n if (part.data === undefined) return null;\n return { data: part.data };\n }\n default:\n return null;\n }\n })\n .filter((p): p is A2APart => p !== null);\n}\n\nexport function a2aMessageToContent(\n message: A2AMessage,\n): ThreadAssistantMessage[\"content\"] {\n return a2aPartsToContent(message?.parts ?? []);\n}\n\nexport function threadMessageToA2AMessage(\n message: ThreadMessage,\n options: {\n contextId?: string | undefined;\n taskId?: string | undefined;\n } = {},\n): A2AMessage {\n const parts: A2APart[] = [];\n\n if (message.role === \"user\") {\n parts.push(...contentPartsToA2AParts(message.content));\n for (const attachment of message.attachments ?? []) {\n parts.push(\n ...contentPartsToA2AParts(\n attachment.content ?? [],\n attachment.contentType,\n ),\n );\n }\n }\n\n const a2aMsg: A2AMessage = {\n messageId: message.id,\n role: \"user\",\n parts,\n };\n\n if (options.contextId) {\n a2aMsg.contextId = options.contextId;\n }\n if (options.taskId) {\n a2aMsg.taskId = options.taskId;\n }\n\n return a2aMsg;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conversions.js","names":["isRecord"],"sources":["../src/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n MessageStatus,\n ThreadAssistantMessage,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport {\n parseBase64,\n parseDataUrl,\n resolveFilePartSource,\n resolveImageMediaType,\n} from \"@assistant-ui/core/internal\";\nimport type { A2AMessage, A2APart, A2ATaskState } from \"./types\";\n\nfunction isImageMediaType(mediaType?: string): boolean {\n return !!mediaType && mediaType.startsWith(\"image/\");\n}\n\nconst A2UI_OPERATION_KEYS = [\n \"createSurface\",\n \"updateComponents\",\n \"updateDataModel\",\n \"deleteSurface\",\n] as const;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isA2uiOperation = (value: unknown): boolean =>\n isRecord(value) &&\n typeof value.version === \"string\" &&\n A2UI_OPERATION_KEYS.some((key) => Object.hasOwn(value, key));\n\nconst hasA2uiMediaType = (value: unknown): boolean =>\n typeof value === \"string\" && value.toLowerCase().includes(\"a2ui\");\n\nexport function isA2uiDataPart(part: A2APart): boolean {\n if (part.data === undefined) return false;\n\n const metadata = isRecord(part.metadata) ? part.metadata : {};\n return (\n hasA2uiMediaType(part.mediaType) ||\n hasA2uiMediaType(metadata.mimeType) ||\n hasA2uiMediaType(metadata.mediaType) ||\n isA2uiOperation(part.data) ||\n (Array.isArray(part.data) &&\n part.data.length > 0 &&\n part.data.every(isA2uiOperation))\n );\n}\n\nexport function a2uiPartToOperations(part: A2APart): readonly unknown[] {\n if (!isA2uiDataPart(part)) return [];\n return Array.isArray(part.data) ? part.data : [part.data];\n}\n\nexport function a2uiPartsToOperations(\n parts: readonly A2APart[],\n): readonly unknown[] {\n if (!Array.isArray(parts)) return [];\n return parts.flatMap(a2uiPartToOperations);\n}\n\nexport function a2aPartToContent(\n part: A2APart,\n): ThreadAssistantMessage[\"content\"][number] {\n if (!isRecord(part)) return { type: \"text\", text: \"\" };\n if (part.text != null) {\n return { type: \"text\", text: part.text };\n }\n if (part.url != null) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: part.url,\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mediaType ?? \"application/octet-stream\",\n sourceType: \"url\",\n ...(part.filename && { filename: part.filename }),\n };\n }\n if (part.raw != null) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: `data:${part.mediaType};base64,${part.raw}`,\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n type: \"file\",\n data: part.raw,\n mimeType: part.mediaType ?? \"application/octet-stream\",\n ...(part.filename && { filename: part.filename }),\n };\n }\n if (part.data !== undefined) {\n return { type: \"text\", text: JSON.stringify(part.data, null, 2) };\n }\n return { type: \"text\", text: \"\" };\n}\n\nexport function a2aPartsToContent(\n parts: A2APart[],\n): ThreadAssistantMessage[\"content\"] {\n return (Array.isArray(parts) ? parts : [])\n .filter((part) => isRecord(part) && !isA2uiDataPart(part))\n .map(a2aPartToContent);\n}\n\nconst TERMINAL_STATES = new Set<A2ATaskState>([\n \"completed\",\n \"failed\",\n \"canceled\",\n \"rejected\",\n]);\n\nconst INTERRUPTED_STATES = new Set<A2ATaskState>([\n \"input_required\",\n \"auth_required\",\n]);\n\nexport function isTerminalTaskState(state: A2ATaskState): boolean {\n return TERMINAL_STATES.has(state);\n}\n\nexport function isInterruptedTaskState(state: A2ATaskState): boolean {\n return INTERRUPTED_STATES.has(state);\n}\n\nexport function taskStateToMessageStatus(state: A2ATaskState): MessageStatus {\n switch (state) {\n case \"submitted\":\n case \"working\":\n return { type: \"running\" };\n case \"completed\":\n return { type: \"complete\", reason: \"stop\" };\n case \"failed\":\n case \"rejected\":\n return { type: \"incomplete\", reason: \"error\" };\n case \"canceled\":\n return { type: \"incomplete\", reason: \"cancelled\" };\n case \"input_required\":\n case \"auth_required\":\n return { type: \"requires-action\", reason: \"interrupt\" };\n default:\n return { type: \"running\" };\n }\n}\n\nexport function contentPartsToA2AParts(\n content: ReadonlyArray<{\n type: string;\n text?: string | undefined;\n image?: string | undefined;\n data?: unknown;\n mimeType?: string | undefined;\n filename?: string | undefined;\n sourceType?: \"url\" | \"id\" | undefined;\n audio?: { data: string; format: string } | undefined;\n }>,\n fallbackMimeType?: string,\n): A2APart[] {\n return content\n .map((part): A2APart | null => {\n switch (part.type) {\n case \"text\":\n return { text: part.text ?? \"\" };\n case \"image\": {\n if (!part.image) return null;\n const parsed = parseDataUrl(part.image);\n const data = parsed?.data ?? parseBase64(part.image);\n if (data !== null) {\n return {\n raw: data,\n mediaType: resolveImageMediaType(\n parsed ? part.image : data,\n fallbackMimeType,\n ),\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n url: part.image,\n ...(fallbackMimeType && { mediaType: fallbackMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"file\": {\n if (typeof part.data !== \"string\") return null;\n const declaredMimeType = part.mimeType || fallbackMimeType;\n const source = resolveFilePartSource({\n data: part.data,\n mimeType: declaredMimeType ?? \"application/octet-stream\",\n sourceType: part.sourceType,\n });\n if (source.kind === \"url\") {\n return {\n url: source.url,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n const parsed = parseDataUrl(part.data);\n if (parsed) {\n return {\n raw: source.data,\n mediaType: source.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n }\n if (/^data:/i.test(part.data)) {\n return {\n url: part.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n raw: source.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"audio\": {\n if (!part.audio) return null;\n return {\n raw: parseDataUrl(part.audio.data)?.data ?? part.audio.data,\n mediaType: `audio/${part.audio.format}`,\n };\n }\n case \"data\": {\n if (part.data === undefined) return null;\n return { data: part.data };\n }\n default:\n return null;\n }\n })\n .filter((p): p is A2APart => p !== null);\n}\n\nexport function a2aMessageToContent(\n message: A2AMessage,\n): ThreadAssistantMessage[\"content\"] {\n return a2aPartsToContent(message?.parts ?? []);\n}\n\nexport function threadMessageToA2AMessage(\n message: ThreadMessage,\n options: {\n contextId?: string | undefined;\n taskId?: string | undefined;\n } = {},\n): A2AMessage {\n const parts: A2APart[] = [];\n\n if (message.role === \"user\") {\n parts.push(...contentPartsToA2AParts(message.content));\n for (const attachment of message.attachments ?? []) {\n parts.push(\n ...contentPartsToA2AParts(\n attachment.content ?? [],\n attachment.contentType,\n ),\n );\n }\n }\n\n const a2aMsg: A2AMessage = {\n messageId: message.id,\n role: \"user\",\n parts,\n };\n\n if (options.contextId) {\n a2aMsg.contextId = options.contextId;\n }\n if (options.taskId) {\n a2aMsg.taskId = options.taskId;\n }\n\n return a2aMsg;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-a2a",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.50",
|
|
4
4
|
"description": "A2A (Agent-to-Agent) v1.0 protocol adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a2a",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@assistant-ui/core": "^0.3.
|
|
33
|
-
"@assistant-ui/generative-ui": "^0.0.
|
|
34
|
-
"@assistant-ui/store": "^0.3.
|
|
35
|
-
"assistant-stream": "^0.3.
|
|
32
|
+
"@assistant-ui/core": "^0.3.33",
|
|
33
|
+
"@assistant-ui/generative-ui": "^0.0.9",
|
|
34
|
+
"@assistant-ui/store": "^0.3.23",
|
|
35
|
+
"assistant-stream": "^0.3.56"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@types/react": "*",
|
package/src/conversions.test.ts
CHANGED
|
@@ -475,6 +475,34 @@ describe("isInterruptedTaskState", () => {
|
|
|
475
475
|
});
|
|
476
476
|
|
|
477
477
|
describe("contentPartsToA2AParts", () => {
|
|
478
|
+
it.each([
|
|
479
|
+
[
|
|
480
|
+
"image/png",
|
|
481
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==",
|
|
482
|
+
],
|
|
483
|
+
["image/jpeg", "/9j/4AAQSkZJRg=="],
|
|
484
|
+
["image/jpeg", "/9j/4AAQ\r\nSkZJRg==", "/9j/4AAQSkZJRg=="],
|
|
485
|
+
["image/png", "PHN2Zy8+"],
|
|
486
|
+
])(
|
|
487
|
+
"sends bare base64 %s images as raw image bytes",
|
|
488
|
+
(mediaType, image, data = image) => {
|
|
489
|
+
expect(
|
|
490
|
+
contentPartsToA2AParts([
|
|
491
|
+
{ type: "image", image, filename: "pixel.png" },
|
|
492
|
+
]),
|
|
493
|
+
).toEqual([{ raw: data, mediaType, filename: "pixel.png" }]);
|
|
494
|
+
},
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
it.each(["/uploads/cat.png", "/9j/uploads/cat.png"])(
|
|
498
|
+
"preserves relative image reference %s",
|
|
499
|
+
(image) => {
|
|
500
|
+
expect(
|
|
501
|
+
contentPartsToA2AParts([{ type: "image", image, filename: "cat.png" }]),
|
|
502
|
+
).toEqual([{ url: image, filename: "cat.png" }]);
|
|
503
|
+
},
|
|
504
|
+
);
|
|
505
|
+
|
|
478
506
|
it.each(["application/pdf", ""])(
|
|
479
507
|
"unwraps a media-less data URL while retaining the adapter's %j MIME fallback",
|
|
480
508
|
(mimeType) => {
|
package/src/conversions.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ThreadMessage,
|
|
7
7
|
} from "@assistant-ui/core";
|
|
8
8
|
import {
|
|
9
|
+
parseBase64,
|
|
9
10
|
parseDataUrl,
|
|
10
11
|
resolveFilePartSource,
|
|
11
12
|
resolveImageMediaType,
|
|
@@ -174,10 +175,14 @@ export function contentPartsToA2AParts(
|
|
|
174
175
|
case "image": {
|
|
175
176
|
if (!part.image) return null;
|
|
176
177
|
const parsed = parseDataUrl(part.image);
|
|
177
|
-
|
|
178
|
+
const data = parsed?.data ?? parseBase64(part.image);
|
|
179
|
+
if (data !== null) {
|
|
178
180
|
return {
|
|
179
|
-
raw:
|
|
180
|
-
mediaType: resolveImageMediaType(
|
|
181
|
+
raw: data,
|
|
182
|
+
mediaType: resolveImageMediaType(
|
|
183
|
+
parsed ? part.image : data,
|
|
184
|
+
fallbackMimeType,
|
|
185
|
+
),
|
|
181
186
|
...(part.filename && { filename: part.filename }),
|
|
182
187
|
};
|
|
183
188
|
}
|