MCPcopy
hub / github.com/CopilotKit/CopilotKit / buildFileContentParts

Function buildFileContentParts

packages/bot-telegram/src/download-files.ts:110–215  ·  view source on GitHub ↗
(
  files: TelegramFileRef[],
  token: string,
  getFilePath: (fileId: string) => Promise<string>,
  config: FileDeliveryConfig = {},
)

Source from the content-addressed store, hash-verified

108 * @param config Optional delivery configuration.
109 */
110export async function buildFileContentParts(
111 files: TelegramFileRef[],
112 token: string,
113 getFilePath: (fileId: string) => Promise<string>,
114 config: FileDeliveryConfig = {},
115): Promise<{ parts: AgentContentPart[]; notes: string[] }> {
116 const maxBytes = config.maxBytesPerFile ?? DEFAULTS.maxBytesPerFile;
117 const maxFiles = config.maxFiles ?? DEFAULTS.maxFiles;
118
119 const parts: AgentContentPart[] = [];
120 const notes: string[] = [];
121 const considered = files.slice(0, maxFiles);
122 if (files.length > maxFiles) {
123 notes.push(
124 `(only the first ${maxFiles} of ${files.length} files processed)`,
125 );
126 }
127
128 for (const f of considered) {
129 const label = f.fileName ?? f.fileId;
130 const mime = (f.mimeType ?? "application/octet-stream").toLowerCase();
131
132 if (typeof f.size === "number" && f.size > maxBytes) {
133 notes.push(
134 `skipped "${label}": ${f.size} bytes too large (cap is ${maxBytes} bytes)`,
135 );
136 continue;
137 }
138
139 let filePath: string;
140 try {
141 filePath = await getFilePath(f.fileId);
142 } catch (err) {
143 const msg = err instanceof Error ? err.message : String(err);
144 notes.push(
145 `skipped "${label}": could not resolve file path — ${redactToken(msg, token)}`,
146 );
147 continue;
148 }
149
150 const url = `https://api.telegram.org/file/bot${token}/${filePath}`;
151 let bytes: Buffer;
152 try {
153 const res = await fetch(url);
154 if (!res.ok) {
155 notes.push(`skipped "${label}": download failed (HTTP ${res.status})`);
156 continue;
157 }
158
159 // Pre-check Content-Length to avoid buffering oversized responses entirely
160 // into memory (memory-DoS guard for cases where f.size is absent, e.g. photos).
161 const contentLength = res.headers.get("content-length");
162 if (contentLength !== null) {
163 const declared = parseInt(contentLength, 10);
164 if (!isNaN(declared) && declared > maxBytes) {
165 notes.push(
166 `skipped "${label}": Content-Length ${declared} bytes too large (cap is ${maxBytes} bytes)`,
167 );

Callers 3

buildReplyContextPartsFunction · 0.70
handleMediaFunction · 0.70

Calls 7

redactTokenFunction · 0.85
fetchFunction · 0.85
isTextLikeFunction · 0.85
arrayBufferMethod · 0.80
mediaPartTypeFunction · 0.70
pushMethod · 0.65
getMethod · 0.65

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…