tiny-stdio-mcp-server 0.1.7 → 0.1.9
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/content/audio.d.ts +2 -1
- package/dist/content/audio.js +3 -3
- package/dist/content/file.d.ts +2 -1
- package/dist/content/file.js +3 -3
- package/dist/content/image.d.ts +2 -1
- package/dist/content/image.js +3 -3
- package/dist/content/index.d.ts +2 -0
- package/dist/content/index.js +1 -0
- package/dist/content/remote.d.ts +5 -0
- package/dist/content/remote.js +69 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +7 -4
- package/dist/server.js +62 -17
- package/package.json +1 -1
package/dist/content/audio.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FromUrlOptions } from "./remote.js";
|
|
1
2
|
export interface AudioContent {
|
|
2
3
|
type: "audio";
|
|
3
4
|
data: string;
|
|
@@ -7,7 +8,7 @@ export declare class Audio {
|
|
|
7
8
|
private readonly base64Data;
|
|
8
9
|
private readonly mimeType;
|
|
9
10
|
private constructor();
|
|
10
|
-
static fromUrl(url: string): Promise<Audio>;
|
|
11
|
+
static fromUrl(url: string, options?: FromUrlOptions): Promise<Audio>;
|
|
11
12
|
static fromBytes(data: Uint8Array, format?: string): Audio;
|
|
12
13
|
static fromBase64(base64: string, mimeType: string): Audio;
|
|
13
14
|
toContentBlock(): AudioContent;
|
package/dist/content/audio.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertBase64, fileTypeFromBuffer, parseContentType, safeRemoteLabel } from "./mime.js";
|
|
2
|
+
import { readRemoteBytes } from "./remote.js";
|
|
2
3
|
const SUPPORTED_AUDIO_MIMES = new Set([
|
|
3
4
|
"audio/mpeg",
|
|
4
5
|
"audio/wav",
|
|
@@ -19,13 +20,12 @@ export class Audio {
|
|
|
19
20
|
this.base64Data = base64Data;
|
|
20
21
|
this.mimeType = mimeType;
|
|
21
22
|
}
|
|
22
|
-
static async fromUrl(url) {
|
|
23
|
+
static async fromUrl(url, options) {
|
|
23
24
|
const response = await fetch(url);
|
|
24
25
|
if (!response.ok) {
|
|
25
26
|
throw new Error(`Failed to fetch audio from ${safeRemoteLabel(url)}: ${response.status} ${response.statusText}`);
|
|
26
27
|
}
|
|
27
|
-
const
|
|
28
|
-
const data = new Uint8Array(arrayBuffer);
|
|
28
|
+
const data = await readRemoteBytes(response, "audio", url, options);
|
|
29
29
|
const detected = fileTypeFromBuffer(data);
|
|
30
30
|
let mimeType;
|
|
31
31
|
if (detected && SUPPORTED_AUDIO_MIMES.has(detected.mime)) {
|
package/dist/content/file.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FromUrlOptions } from "./remote.js";
|
|
1
2
|
export interface TextResourceContents {
|
|
2
3
|
uri: string;
|
|
3
4
|
mimeType: string;
|
|
@@ -19,7 +20,7 @@ export declare class File {
|
|
|
19
20
|
private readonly name?;
|
|
20
21
|
private readonly charset;
|
|
21
22
|
private constructor();
|
|
22
|
-
static fromUrl(url: string): Promise<File>;
|
|
23
|
+
static fromUrl(url: string, options?: FromUrlOptions): Promise<File>;
|
|
23
24
|
static fromBytes(data: Uint8Array, mimeType: string): File;
|
|
24
25
|
static fromText(text: string, mimeType?: string): File;
|
|
25
26
|
static fromBase64(base64: string, mimeType: string): File;
|
package/dist/content/file.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertBase64, fileTypeFromBuffer, parseContentType, safeRemoteLabel } from "./mime.js";
|
|
2
|
+
import { readRemoteBytes } from "./remote.js";
|
|
2
3
|
function isTextMimeType(mimeType) {
|
|
3
4
|
const normalizedMimeType = mimeType.toLowerCase();
|
|
4
5
|
return (normalizedMimeType.startsWith("text/") ||
|
|
@@ -22,13 +23,12 @@ export class File {
|
|
|
22
23
|
this.name = name;
|
|
23
24
|
this.charset = charset;
|
|
24
25
|
}
|
|
25
|
-
static async fromUrl(url) {
|
|
26
|
+
static async fromUrl(url, options) {
|
|
26
27
|
const response = await fetch(url);
|
|
27
28
|
if (!response.ok) {
|
|
28
29
|
throw new Error(`Failed to fetch file from ${safeRemoteLabel(url)}: ${response.status} ${response.statusText}`);
|
|
29
30
|
}
|
|
30
|
-
const
|
|
31
|
-
const data = new Uint8Array(arrayBuffer);
|
|
31
|
+
const data = await readRemoteBytes(response, "file", url, options);
|
|
32
32
|
const detected = fileTypeFromBuffer(data);
|
|
33
33
|
let mimeType;
|
|
34
34
|
if (detected) {
|
package/dist/content/image.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FromUrlOptions } from "./remote.js";
|
|
1
2
|
export interface ImageContent {
|
|
2
3
|
type: "image";
|
|
3
4
|
data: string;
|
|
@@ -7,7 +8,7 @@ export declare class Image {
|
|
|
7
8
|
private readonly base64Data;
|
|
8
9
|
private readonly mimeType;
|
|
9
10
|
private constructor();
|
|
10
|
-
static fromUrl(url: string): Promise<Image>;
|
|
11
|
+
static fromUrl(url: string, options?: FromUrlOptions): Promise<Image>;
|
|
11
12
|
static fromBytes(data: Uint8Array, format?: string): Image;
|
|
12
13
|
static fromBase64(base64: string, mimeType: string): Image;
|
|
13
14
|
toContentBlock(): ImageContent;
|
package/dist/content/image.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertBase64, fileTypeFromBuffer, parseContentType, safeRemoteLabel } from "./mime.js";
|
|
2
|
+
import { readRemoteBytes } from "./remote.js";
|
|
2
3
|
const SUPPORTED_IMAGE_MIMES = new Set([
|
|
3
4
|
"image/png",
|
|
4
5
|
"image/jpeg",
|
|
@@ -12,13 +13,12 @@ export class Image {
|
|
|
12
13
|
this.base64Data = base64Data;
|
|
13
14
|
this.mimeType = mimeType;
|
|
14
15
|
}
|
|
15
|
-
static async fromUrl(url) {
|
|
16
|
+
static async fromUrl(url, options) {
|
|
16
17
|
const response = await fetch(url);
|
|
17
18
|
if (!response.ok) {
|
|
18
19
|
throw new Error(`Failed to fetch image from ${safeRemoteLabel(url)}: ${response.status} ${response.statusText}`);
|
|
19
20
|
}
|
|
20
|
-
const
|
|
21
|
-
const data = new Uint8Array(arrayBuffer);
|
|
21
|
+
const data = await readRemoteBytes(response, "image", url, options);
|
|
22
22
|
const detected = fileTypeFromBuffer(data);
|
|
23
23
|
let mimeType;
|
|
24
24
|
if (detected && SUPPORTED_IMAGE_MIMES.has(detected.mime)) {
|
package/dist/content/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { fileTypeFromBuffer, type FileTypeResult } from "./mime.js";
|
|
2
|
+
export { DEFAULT_FROM_URL_MAX_BYTES } from "./remote.js";
|
|
3
|
+
export type { FromUrlOptions } from "./remote.js";
|
|
2
4
|
export { Image, type ImageContent } from "./image.js";
|
|
3
5
|
export { Audio, type AudioContent } from "./audio.js";
|
|
4
6
|
export { File, type EmbeddedResource, type TextResourceContents, type BlobResourceContents, } from "./file.js";
|
package/dist/content/index.js
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { safeRemoteLabel } from "./mime.js";
|
|
2
|
+
export const DEFAULT_FROM_URL_MAX_BYTES = 5 * 1024 * 1024;
|
|
3
|
+
function resolveMaxBytes(options) {
|
|
4
|
+
const maxBytes = options?.maxBytes ?? DEFAULT_FROM_URL_MAX_BYTES;
|
|
5
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
|
|
6
|
+
throw new Error("maxBytes must be a positive integer");
|
|
7
|
+
}
|
|
8
|
+
return maxBytes;
|
|
9
|
+
}
|
|
10
|
+
function createSizeError(kind, url, maxBytes) {
|
|
11
|
+
return new Error(`Remote ${kind} from ${safeRemoteLabel(url)} exceeds maximum size of ${maxBytes} bytes`);
|
|
12
|
+
}
|
|
13
|
+
function readContentLength(response) {
|
|
14
|
+
const rawContentLength = response.headers.get("content-length");
|
|
15
|
+
if (typeof rawContentLength !== "string") {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const contentLength = Number(rawContentLength.trim());
|
|
19
|
+
if (!Number.isSafeInteger(contentLength) || contentLength < 0) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return contentLength;
|
|
23
|
+
}
|
|
24
|
+
function combineChunks(chunks, totalBytes) {
|
|
25
|
+
const combined = new Uint8Array(totalBytes);
|
|
26
|
+
let offset = 0;
|
|
27
|
+
for (const chunk of chunks) {
|
|
28
|
+
combined.set(chunk, offset);
|
|
29
|
+
offset += chunk.byteLength;
|
|
30
|
+
}
|
|
31
|
+
return combined;
|
|
32
|
+
}
|
|
33
|
+
async function readStreamBytes(body, kind, url, maxBytes) {
|
|
34
|
+
const reader = body.getReader();
|
|
35
|
+
const chunks = [];
|
|
36
|
+
let totalBytes = 0;
|
|
37
|
+
try {
|
|
38
|
+
while (true) {
|
|
39
|
+
const { done, value } = await reader.read();
|
|
40
|
+
if (done) {
|
|
41
|
+
return combineChunks(chunks, totalBytes);
|
|
42
|
+
}
|
|
43
|
+
totalBytes += value.byteLength;
|
|
44
|
+
if (totalBytes > maxBytes) {
|
|
45
|
+
await reader.cancel().catch(() => undefined);
|
|
46
|
+
throw createSizeError(kind, url, maxBytes);
|
|
47
|
+
}
|
|
48
|
+
chunks.push(value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
reader.releaseLock();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function readRemoteBytes(response, kind, url, options) {
|
|
56
|
+
const maxBytes = resolveMaxBytes(options);
|
|
57
|
+
const contentLength = readContentLength(response);
|
|
58
|
+
if (contentLength !== undefined && contentLength > maxBytes) {
|
|
59
|
+
throw createSizeError(kind, url, maxBytes);
|
|
60
|
+
}
|
|
61
|
+
if (response.body) {
|
|
62
|
+
return readStreamBytes(response.body, kind, url, maxBytes);
|
|
63
|
+
}
|
|
64
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
65
|
+
if (arrayBuffer.byteLength > maxBytes) {
|
|
66
|
+
throw createSizeError(kind, url, maxBytes);
|
|
67
|
+
}
|
|
68
|
+
return new Uint8Array(arrayBuffer);
|
|
69
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { createServer } from "./server.js";
|
|
|
2
2
|
export type { MessageHandler, MessageSession, Server } from "./server.js";
|
|
3
3
|
export { defineSchema } from "./schema.js";
|
|
4
4
|
export type { TypedSchema } from "./schema.js";
|
|
5
|
-
export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, } from "./content/index.js";
|
|
6
|
-
export type { ImageContent, AudioContent, EmbeddedResource, TextResourceContents, BlobResourceContents, ContentBlock, TextContent, FileTypeResult, } from "./content/index.js";
|
|
5
|
+
export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, DEFAULT_FROM_URL_MAX_BYTES, } from "./content/index.js";
|
|
6
|
+
export type { ImageContent, AudioContent, EmbeddedResource, TextResourceContents, BlobResourceContents, ContentBlock, TextContent, FileTypeResult, FromUrlOptions, } from "./content/index.js";
|
|
7
7
|
export type { ToolReturn } from "./content/index.js";
|
|
8
8
|
export type { ServerOptions, ToolHandler, ToolDefinition, Tool, ToolAnnotations, ToolExecution, Icon, ContentAnnotations, ResourceLink, CallToolResult, PromptContentItem, PromptArgument, Prompt, PromptMessage, GetPromptResult, PromptHandler, PromptDefinition, Resource, ResourceTemplate, ResourceContents, ReadResourceResult, ResourceHandler, ResourceDefinition, ResourceTemplateDefinition, HandleResult, ContentItem, JSONSchema, JSONSchemaProperty, Transport, SDKTransport, JSONRPCRequest, JSONRPCResponse, JSONRPCError, JSONRPCMessage, JSONRPCNotification, InitializeResult, } from "./types.js";
|
|
9
9
|
export { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -3,5 +3,5 @@ export { createServer } from "./server.js";
|
|
|
3
3
|
// Schema
|
|
4
4
|
export { defineSchema } from "./schema.js";
|
|
5
5
|
// Content helpers
|
|
6
|
-
export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, } from "./content/index.js";
|
|
6
|
+
export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, DEFAULT_FROM_URL_MAX_BYTES, } from "./content/index.js";
|
|
7
7
|
export { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
|
package/dist/schema.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ interface SchemaPropertyDef {
|
|
|
4
4
|
type: SchemaPropertyType;
|
|
5
5
|
description?: string;
|
|
6
6
|
optional?: boolean;
|
|
7
|
+
[keyword: string]: unknown;
|
|
7
8
|
}
|
|
8
9
|
type SchemaDefinition = Record<string, SchemaPropertyDef>;
|
|
9
10
|
type InferType<T extends SchemaPropertyType> = T extends "string" ? string : T extends "number" ? number : T extends "boolean" ? boolean : T extends "object" ? Record<string, unknown> : T extends "array" ? unknown[] : never;
|
package/dist/schema.js
CHANGED
|
@@ -2,14 +2,17 @@ export function defineSchema(definition) {
|
|
|
2
2
|
const properties = {};
|
|
3
3
|
const required = [];
|
|
4
4
|
for (const [key, prop] of Object.entries(definition)) {
|
|
5
|
+
const jsonSchemaProperty = {};
|
|
6
|
+
for (const [propertyKey, propertyValue] of Object.entries(prop)) {
|
|
7
|
+
if (propertyKey !== "optional") {
|
|
8
|
+
jsonSchemaProperty[propertyKey] = propertyValue;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
5
11
|
Object.defineProperty(properties, key, {
|
|
6
12
|
enumerable: true,
|
|
7
13
|
configurable: true,
|
|
8
14
|
writable: true,
|
|
9
|
-
value:
|
|
10
|
-
type: prop.type,
|
|
11
|
-
...(prop.description !== undefined && { description: prop.description }),
|
|
12
|
-
},
|
|
15
|
+
value: jsonSchemaProperty,
|
|
13
16
|
});
|
|
14
17
|
if (!prop.optional) {
|
|
15
18
|
required.push(key);
|
package/dist/server.js
CHANGED
|
@@ -320,6 +320,7 @@ export function createServer(options) {
|
|
|
320
320
|
};
|
|
321
321
|
const server = {
|
|
322
322
|
tool(name, description, inputSchema, handler, outputSchema) {
|
|
323
|
+
assertNonEmptyName(name, "Tool name required");
|
|
323
324
|
if (outputSchema !== undefined) {
|
|
324
325
|
assertSupportedOutputSchema(outputSchema);
|
|
325
326
|
}
|
|
@@ -333,6 +334,7 @@ export function createServer(options) {
|
|
|
333
334
|
return server;
|
|
334
335
|
},
|
|
335
336
|
registerTool(definition, handler) {
|
|
337
|
+
assertNonEmptyName(definition.name, "Tool name required");
|
|
336
338
|
if (definition.outputSchema !== undefined) {
|
|
337
339
|
assertSupportedOutputSchema(definition.outputSchema);
|
|
338
340
|
}
|
|
@@ -343,6 +345,7 @@ export function createServer(options) {
|
|
|
343
345
|
return server;
|
|
344
346
|
},
|
|
345
347
|
prompt(definition, handler) {
|
|
348
|
+
assertNonEmptyName(definition.name, "Prompt name required");
|
|
346
349
|
prompts.set(definition.name, { ...definition, handler });
|
|
347
350
|
return server;
|
|
348
351
|
},
|
|
@@ -354,7 +357,7 @@ export function createServer(options) {
|
|
|
354
357
|
return server;
|
|
355
358
|
},
|
|
356
359
|
resourceTemplate(definition, handler) {
|
|
357
|
-
|
|
360
|
+
assertReadableUriTemplate(definition.uriTemplate);
|
|
358
361
|
new UriTemplate(definition.uriTemplate);
|
|
359
362
|
resourceTemplates.set(definition.uriTemplate, { ...definition, handler });
|
|
360
363
|
return server;
|
|
@@ -536,6 +539,20 @@ function isValidUri(uri) {
|
|
|
536
539
|
return false;
|
|
537
540
|
}
|
|
538
541
|
}
|
|
542
|
+
function assertNonEmptyName(name, message) {
|
|
543
|
+
if (name.length === 0) {
|
|
544
|
+
throw new Error(message);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function assertReadableUriTemplate(uriTemplate) {
|
|
548
|
+
const parsed = uriTemplateParser.parse(uriTemplate);
|
|
549
|
+
const expanded = parsed.expand(new Proxy({}, {
|
|
550
|
+
get: (_target, property) => typeof property === "string" ? "value" : undefined,
|
|
551
|
+
}));
|
|
552
|
+
if (typeof expanded !== "string" || !isValidUri(expanded)) {
|
|
553
|
+
throw new Error(`Invalid resource URI template: ${uriTemplate}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
539
556
|
function toStringArguments(value) {
|
|
540
557
|
if (value === undefined) {
|
|
541
558
|
return {};
|
|
@@ -588,9 +605,13 @@ function normalizeToolResult(handlerResult, outputSchema) {
|
|
|
588
605
|
throw new Error("Invalid tool result");
|
|
589
606
|
}
|
|
590
607
|
if (outputSchema === undefined) {
|
|
591
|
-
|
|
608
|
+
const result = isCallToolResult(handlerResult)
|
|
592
609
|
? handlerResult
|
|
593
610
|
: { content: toContentBlocks(handlerResult) };
|
|
611
|
+
if (!isCallToolResult(result)) {
|
|
612
|
+
throw new Error("Invalid tool result");
|
|
613
|
+
}
|
|
614
|
+
return result;
|
|
594
615
|
}
|
|
595
616
|
const structuredContent = isCallToolResult(handlerResult)
|
|
596
617
|
? handlerResult.structuredContent
|
|
@@ -695,7 +716,8 @@ function isGetPromptResult(value) {
|
|
|
695
716
|
if (typeof value !== "object" || value === null || !hasOwnProperty(value, "messages")) {
|
|
696
717
|
return false;
|
|
697
718
|
}
|
|
698
|
-
return
|
|
719
|
+
return (!hasOwnProperty(value, "description") || value.description === undefined || typeof value.description === "string")
|
|
720
|
+
&& Array.isArray(value.messages)
|
|
699
721
|
&& value.messages.every((message) => typeof message === "object"
|
|
700
722
|
&& message !== null
|
|
701
723
|
&& hasOwnProperty(message, "role")
|
|
@@ -708,13 +730,7 @@ function isReadResourceResult(value) {
|
|
|
708
730
|
return false;
|
|
709
731
|
}
|
|
710
732
|
return Array.isArray(value.contents)
|
|
711
|
-
&& value.contents.every(
|
|
712
|
-
&& content !== null
|
|
713
|
-
&& hasOwnProperty(content, "uri")
|
|
714
|
-
&& typeof content.uri === "string"
|
|
715
|
-
&& isValidUri(content.uri)
|
|
716
|
-
&& ((hasOwnProperty(content, "text") && typeof content.text === "string")
|
|
717
|
-
|| (hasOwnProperty(content, "blob") && typeof content.blob === "string" && isBase64(content.blob))));
|
|
733
|
+
&& value.contents.every(isResourceContents);
|
|
718
734
|
}
|
|
719
735
|
function hasContentArray(value) {
|
|
720
736
|
return typeof value === "object" && value !== null && hasOwnProperty(value, "content")
|
|
@@ -725,6 +741,9 @@ function isContentItem(value) {
|
|
|
725
741
|
return false;
|
|
726
742
|
}
|
|
727
743
|
const block = value;
|
|
744
|
+
if (!hasValidContentAnnotations(block)) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
728
747
|
if (block.type === "text") {
|
|
729
748
|
return hasOwnProperty(block, "text") && typeof block.text === "string";
|
|
730
749
|
}
|
|
@@ -738,8 +757,13 @@ function isContentItem(value) {
|
|
|
738
757
|
if (block.type === "resource_link") {
|
|
739
758
|
return hasOwnProperty(block, "uri")
|
|
740
759
|
&& typeof block.uri === "string"
|
|
760
|
+
&& isValidUri(block.uri)
|
|
741
761
|
&& hasOwnProperty(block, "name")
|
|
742
|
-
&& typeof block.name === "string"
|
|
762
|
+
&& typeof block.name === "string"
|
|
763
|
+
&& (!hasOwnProperty(block, "title") || block.title === undefined || typeof block.title === "string")
|
|
764
|
+
&& (!hasOwnProperty(block, "description") || block.description === undefined || typeof block.description === "string")
|
|
765
|
+
&& (!hasOwnProperty(block, "mimeType") || block.mimeType === undefined || typeof block.mimeType === "string")
|
|
766
|
+
&& (!hasOwnProperty(block, "size") || block.size === undefined || typeof block.size === "number");
|
|
743
767
|
}
|
|
744
768
|
if (block.type !== "resource"
|
|
745
769
|
|| !hasOwnProperty(block, "resource")
|
|
@@ -747,12 +771,33 @@ function isContentItem(value) {
|
|
|
747
771
|
|| block.resource === null) {
|
|
748
772
|
return false;
|
|
749
773
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
774
|
+
return isResourceContents(block.resource);
|
|
775
|
+
}
|
|
776
|
+
function isResourceContents(value) {
|
|
777
|
+
if (typeof value !== "object"
|
|
778
|
+
|| value === null
|
|
779
|
+
|| !hasOwnProperty(value, "uri")
|
|
780
|
+
|| typeof value.uri !== "string"
|
|
781
|
+
|| !isValidUri(value.uri)) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
if (hasOwnProperty(value, "mimeType") && value.mimeType !== undefined && typeof value.mimeType !== "string") {
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
return (hasOwnProperty(value, "text") && typeof value.text === "string")
|
|
788
|
+
|| (hasOwnProperty(value, "blob") && typeof value.blob === "string" && isBase64(value.blob));
|
|
789
|
+
}
|
|
790
|
+
function hasValidContentAnnotations(value) {
|
|
791
|
+
if (!hasOwnProperty(value, "annotations") || value.annotations === undefined) {
|
|
792
|
+
return true;
|
|
793
|
+
}
|
|
794
|
+
if (!isJsonObject(value.annotations)) {
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
const { audience, priority, lastModified } = value.annotations;
|
|
798
|
+
return (audience === undefined || (Array.isArray(audience) && audience.every((item) => item === "user" || item === "assistant")))
|
|
799
|
+
&& (priority === undefined || typeof priority === "number")
|
|
800
|
+
&& (lastModified === undefined || typeof lastModified === "string");
|
|
756
801
|
}
|
|
757
802
|
function isBase64(value) {
|
|
758
803
|
if (value.length === 0) {
|