coaiajs 0.4.2 → 0.5.0
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/README.md +7 -4
- package/agents/custom_gpt/ceremony-media-proxy.instructions.md +86 -0
- package/agents/custom_gpt/ceremony-media-proxy.yml +158 -0
- package/agents/custom_gpt/ceremony-observations.instructions.md +38 -5
- package/agents/custom_gpt/ceremony-observations.yml +737 -287
- package/dist/mcp/config.d.ts.map +1 -1
- package/dist/mcp/config.js +13 -1
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/server.js +11 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/src/cli.js +17 -4
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +10 -2
- package/dist/src/config.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/langfuse/index.d.ts +2 -1
- package/dist/src/langfuse/index.d.ts.map +1 -1
- package/dist/src/langfuse/index.js +1 -1
- package/dist/src/langfuse/index.js.map +1 -1
- package/dist/src/langfuse/media.d.ts +30 -0
- package/dist/src/langfuse/media.d.ts.map +1 -1
- package/dist/src/langfuse/media.js +128 -77
- package/dist/src/langfuse/media.js.map +1 -1
- package/dist/src/media-upload-proxy.d.ts +32 -0
- package/dist/src/media-upload-proxy.d.ts.map +1 -0
- package/dist/src/media-upload-proxy.js +210 -0
- package/dist/src/media-upload-proxy.js.map +1 -0
- package/dist/src/narrative/argument-hygiene.d.ts +46 -0
- package/dist/src/narrative/argument-hygiene.d.ts.map +1 -0
- package/dist/src/narrative/argument-hygiene.js +155 -0
- package/dist/src/narrative/argument-hygiene.js.map +1 -0
- package/dist/src/narrative/contract.d.ts +200 -0
- package/dist/src/narrative/contract.d.ts.map +1 -0
- package/dist/src/narrative/contract.js +274 -0
- package/dist/src/narrative/contract.js.map +1 -0
- package/dist/src/narrative/github-bridge.d.ts +23 -0
- package/dist/src/narrative/github-bridge.d.ts.map +1 -0
- package/dist/src/narrative/github-bridge.js +320 -0
- package/dist/src/narrative/github-bridge.js.map +1 -0
- package/dist/src/narrative/graph-manager.d.ts +83 -2
- package/dist/src/narrative/graph-manager.d.ts.map +1 -1
- package/dist/src/narrative/graph-manager.js +444 -54
- package/dist/src/narrative/graph-manager.js.map +1 -1
- package/dist/src/narrative/index.d.ts +24 -3
- package/dist/src/narrative/index.d.ts.map +1 -1
- package/dist/src/narrative/index.js +36 -15
- package/dist/src/narrative/index.js.map +1 -1
- package/dist/src/narrative/jsonl-preservation.d.ts +26 -0
- package/dist/src/narrative/jsonl-preservation.d.ts.map +1 -0
- package/dist/src/narrative/jsonl-preservation.js +293 -0
- package/dist/src/narrative/jsonl-preservation.js.map +1 -0
- package/dist/src/narrative/jsonl-records.d.ts +31 -0
- package/dist/src/narrative/jsonl-records.d.ts.map +1 -0
- package/dist/src/narrative/jsonl-records.js +69 -0
- package/dist/src/narrative/jsonl-records.js.map +1 -0
- package/dist/src/narrative/tool-definitions.d.ts +2 -1
- package/dist/src/narrative/tool-definitions.d.ts.map +1 -1
- package/dist/src/narrative/tool-definitions.js +134 -1
- package/dist/src/narrative/tool-definitions.js.map +1 -1
- package/dist/src/narrative/tool-handlers.d.ts.map +1 -1
- package/dist/src/narrative/tool-handlers.js +203 -20
- package/dist/src/narrative/tool-handlers.js.map +1 -1
- package/dist/src/narrative/types.d.ts +1 -1
- package/dist/src/narrative/types.d.ts.map +1 -1
- package/dist/src/narrative/validation.d.ts +13 -2
- package/dist/src/narrative/validation.d.ts.map +1 -1
- package/dist/src/narrative/validation.js +19 -4
- package/dist/src/narrative/validation.js.map +1 -1
- package/dist/src/redis.d.ts +10 -5
- package/dist/src/redis.d.ts.map +1 -1
- package/dist/src/redis.js +94 -8
- package/dist/src/redis.js.map +1 -1
- package/dist/src/types.d.ts +189 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/llms-full.txt +73 -7
- package/llms.txt +5 -3
- package/package.json +11 -2
- package/rispecs/03-langfuse-module.spec.md +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/langfuse/media.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/langfuse/media.ts"],"names":[],"mappings":"AAwDA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,gBAAgB,GAAG,UAAU,CAAC;IAC3D,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,2EAA2E;AAC3E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA+ErG;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAWlB;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI/D;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAiCxD"}
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
// coaiajs/src/langfuse/media.ts — Media upload operations
|
|
2
2
|
// Port of cofuse.py media functions
|
|
3
|
-
import { readFileSync
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import { basename, extname } from 'node:path';
|
|
6
6
|
import { getClient } from './client.js';
|
|
7
7
|
const SUPPORTED_CONTENT_TYPES = new Set([
|
|
8
|
-
'image/
|
|
9
|
-
'image/tiff', 'image/bmp', 'image/avif',
|
|
10
|
-
'audio/mpeg', 'audio/
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
8
|
+
'image/png', 'image/jpeg', 'image/jpg', 'image/webp', 'image/gif', 'image/svg+xml',
|
|
9
|
+
'image/tiff', 'image/bmp', 'image/avif', 'image/heic',
|
|
10
|
+
'audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/ogg', 'audio/oga', 'audio/aac',
|
|
11
|
+
'audio/mp4', 'audio/flac', 'audio/opus', 'audio/webm',
|
|
12
|
+
'video/mp4', 'video/webm', 'video/ogg', 'video/mpeg', 'video/quicktime',
|
|
13
|
+
'video/x-msvideo', 'video/x-matroska',
|
|
14
|
+
'text/plain', 'text/html', 'text/css', 'text/csv', 'text/markdown',
|
|
15
|
+
'text/x-python', 'text/x-typescript',
|
|
16
|
+
'application/javascript', 'application/x-yaml', 'application/pdf', 'application/msword',
|
|
17
|
+
'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
18
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
19
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
20
|
+
'application/zip', 'application/json', 'application/xml', 'application/octet-stream',
|
|
21
|
+
'application/rtf', 'application/x-ndjson', 'application/vnd.apache.parquet',
|
|
22
|
+
'application/gzip', 'application/x-tar', 'application/x-7z-compressed',
|
|
15
23
|
]);
|
|
16
24
|
const EXTENSION_MAP = {
|
|
17
25
|
'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png',
|
|
18
26
|
'.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml',
|
|
19
27
|
'.tiff': 'image/tiff', '.tif': 'image/tiff', '.bmp': 'image/bmp',
|
|
20
|
-
'.avif': 'image/avif',
|
|
28
|
+
'.avif': 'image/avif', '.heic': 'image/heic',
|
|
21
29
|
'.mp3': 'audio/mpeg', '.m4a': 'audio/mp4', '.wav': 'audio/wav',
|
|
22
30
|
'.ogg': 'audio/ogg', '.flac': 'audio/flac', '.weba': 'audio/webm',
|
|
23
31
|
'.mp4': 'video/mp4', '.webm': 'video/webm', '.ogv': 'video/ogg',
|
|
@@ -25,7 +33,14 @@ const EXTENSION_MAP = {
|
|
|
25
33
|
'.pdf': 'application/pdf',
|
|
26
34
|
'.txt': 'text/plain', '.html': 'text/html', '.htm': 'text/html',
|
|
27
35
|
'.css': 'text/css', '.csv': 'text/csv',
|
|
28
|
-
'.json': 'application/json', '.xml': 'application/xml',
|
|
36
|
+
'.json': 'application/json', '.xml': 'application/xml', '.md': 'text/markdown',
|
|
37
|
+
'.py': 'text/x-python', '.ts': 'text/x-typescript', '.yaml': 'application/x-yaml', '.yml': 'application/x-yaml',
|
|
38
|
+
'.doc': 'application/msword', '.xls': 'application/vnd.ms-excel',
|
|
39
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
40
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
41
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
42
|
+
'.zip': 'application/zip', '.gz': 'application/gzip', '.tar': 'application/x-tar',
|
|
43
|
+
'.7z': 'application/x-7z-compressed', '.parquet': 'application/vnd.apache.parquet',
|
|
29
44
|
};
|
|
30
45
|
const TRUSTED_DOMAINS = [
|
|
31
46
|
'amazonaws.com',
|
|
@@ -34,79 +49,98 @@ const TRUSTED_DOMAINS = [
|
|
|
34
49
|
'blob.core.windows.net',
|
|
35
50
|
'r2.cloudflarestorage.com',
|
|
36
51
|
];
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
/** Upload exact bytes through Langfuse's presigned object-storage flow. */
|
|
53
|
+
export async function uploadMediaBytes(params) {
|
|
54
|
+
const data = Buffer.from(params.data);
|
|
39
55
|
const field = params.field ?? 'input';
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
const source = params.source ?? 'bytes';
|
|
57
|
+
if (!data.length)
|
|
58
|
+
throw new Error('Media data must not be empty');
|
|
59
|
+
if (!SUPPORTED_CONTENT_TYPES.has(params.contentType)) {
|
|
60
|
+
throw new Error(`Unsupported content type: ${params.contentType}`);
|
|
44
61
|
}
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
const hasTraceContext = Boolean(params.traceId) && !params.datasetId && !params.datasetItemId;
|
|
63
|
+
const hasDatasetContext = Boolean(params.datasetId && params.datasetItemId) && !params.traceId && !params.observationId;
|
|
64
|
+
if (!hasTraceContext && !hasDatasetContext) {
|
|
65
|
+
throw new Error('Provide traceId (optionally observationId) or both datasetId and datasetItemId');
|
|
66
|
+
}
|
|
67
|
+
if (params.observationId && !params.traceId) {
|
|
68
|
+
throw new Error('observationId requires traceId');
|
|
69
|
+
}
|
|
70
|
+
const sha256Hash = createHash('sha256').update(data).digest('base64');
|
|
71
|
+
const uploadRequest = {
|
|
72
|
+
contentType: params.contentType,
|
|
73
|
+
contentLength: data.byteLength,
|
|
74
|
+
sha256Hash,
|
|
56
75
|
field,
|
|
57
76
|
};
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
if (params.traceId)
|
|
78
|
+
uploadRequest.traceId = params.traceId;
|
|
79
|
+
if (params.observationId)
|
|
80
|
+
uploadRequest.observationId = params.observationId;
|
|
81
|
+
if (params.datasetId)
|
|
82
|
+
uploadRequest.datasetId = params.datasetId;
|
|
83
|
+
if (params.datasetItemId)
|
|
84
|
+
uploadRequest.datasetItemId = params.datasetItemId;
|
|
85
|
+
const client = getClient();
|
|
86
|
+
const uploadInfo = await client.request('POST', '/api/public/media', uploadRequest);
|
|
87
|
+
const mediaId = typeof uploadInfo.mediaId === 'string' ? uploadInfo.mediaId : '';
|
|
88
|
+
const uploadUrl = typeof uploadInfo.uploadUrl === 'string' ? uploadInfo.uploadUrl : undefined;
|
|
89
|
+
if (!mediaId)
|
|
90
|
+
throw new Error('Langfuse did not return a mediaId');
|
|
91
|
+
let uploadTimeMs = 0;
|
|
92
|
+
if (uploadUrl) {
|
|
93
|
+
validateUploadUrl(uploadUrl);
|
|
94
|
+
const startTime = Date.now();
|
|
95
|
+
const uploadResponse = await fetch(uploadUrl, {
|
|
96
|
+
method: 'PUT',
|
|
97
|
+
headers: {
|
|
98
|
+
'Content-Type': params.contentType,
|
|
99
|
+
'Content-Length': String(data.byteLength),
|
|
100
|
+
'x-amz-checksum-sha256': sha256Hash,
|
|
101
|
+
},
|
|
102
|
+
body: data,
|
|
71
103
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
'x-amz-checksum-sha256': sha256,
|
|
80
|
-
},
|
|
81
|
-
body: fileData,
|
|
82
|
-
});
|
|
83
|
-
const uploadTimeMs = Date.now() - startTime;
|
|
84
|
-
// Step 4: Patch status
|
|
85
|
-
await client.request('PATCH', `/api/public/media/${mediaId}`, {
|
|
86
|
-
uploadHttpStatus: uploadResponse.status,
|
|
87
|
-
uploadTimeMs,
|
|
88
|
-
uploadedAt: new Date().toISOString(),
|
|
89
|
-
});
|
|
90
|
-
if (!uploadResponse.ok) {
|
|
91
|
-
return JSON.stringify({
|
|
92
|
-
error: `Upload failed: ${uploadResponse.status}`,
|
|
93
|
-
mediaId,
|
|
104
|
+
uploadTimeMs = Date.now() - startTime;
|
|
105
|
+
const uploadHttpError = uploadResponse.ok ? undefined : (await uploadResponse.text()).slice(0, 1000);
|
|
106
|
+
await client.request('PATCH', `/api/public/media/${mediaId}`, {
|
|
107
|
+
uploadHttpStatus: uploadResponse.status,
|
|
108
|
+
uploadHttpError,
|
|
109
|
+
uploadTimeMs,
|
|
110
|
+
uploadedAt: new Date().toISOString(),
|
|
94
111
|
});
|
|
112
|
+
if (!uploadResponse.ok) {
|
|
113
|
+
throw new Error(`Media upload failed with HTTP ${uploadResponse.status}: ${uploadHttpError ?? ''}`.trim());
|
|
114
|
+
}
|
|
95
115
|
}
|
|
96
|
-
|
|
97
|
-
const token = `@@@langfuseMedia:type=${contentType}|id=${mediaId}|source=file@@@`;
|
|
98
|
-
return JSON.stringify({
|
|
116
|
+
return {
|
|
99
117
|
success: true,
|
|
100
118
|
mediaId,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
119
|
+
mediaToken: `@@@langfuseMedia:type=${params.contentType}|id=${mediaId}|source=${source}@@@`,
|
|
120
|
+
fileName: params.fileName,
|
|
121
|
+
contentType: params.contentType,
|
|
122
|
+
contentLength: data.byteLength,
|
|
123
|
+
sha256Hash,
|
|
124
|
+
traceId: params.traceId,
|
|
125
|
+
observationId: params.observationId,
|
|
126
|
+
datasetId: params.datasetId,
|
|
127
|
+
datasetItemId: params.datasetItemId,
|
|
105
128
|
field,
|
|
106
|
-
observationId: observationId ?? null,
|
|
107
129
|
uploadTimeMs,
|
|
108
|
-
|
|
109
|
-
}
|
|
130
|
+
alreadyUploaded: !uploadUrl,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export async function uploadAndAttachMedia(params) {
|
|
134
|
+
const result = await uploadMediaBytes({
|
|
135
|
+
data: readFileSync(params.filePath),
|
|
136
|
+
fileName: basename(params.filePath),
|
|
137
|
+
contentType: params.contentType ?? detectContentType(params.filePath),
|
|
138
|
+
traceId: params.traceId,
|
|
139
|
+
observationId: params.observationId,
|
|
140
|
+
field: normalizeMediaField(params.field),
|
|
141
|
+
source: 'file',
|
|
142
|
+
});
|
|
143
|
+
return JSON.stringify(result, null, 2);
|
|
110
144
|
}
|
|
111
145
|
export async function getMedia(mediaId) {
|
|
112
146
|
const client = getClient();
|
|
@@ -156,12 +190,29 @@ export function formatMediaDisplay(json) {
|
|
|
156
190
|
}
|
|
157
191
|
}
|
|
158
192
|
// ─── Helpers ────────────────────────────────────────────────────────
|
|
193
|
+
function normalizeMediaField(value) {
|
|
194
|
+
const field = value ?? 'input';
|
|
195
|
+
if (field === 'input' || field === 'output' || field === 'expectedOutput' || field === 'metadata')
|
|
196
|
+
return field;
|
|
197
|
+
throw new Error(`Unsupported media field: ${field}`);
|
|
198
|
+
}
|
|
199
|
+
function validateUploadUrl(uploadUrl) {
|
|
200
|
+
const url = new URL(uploadUrl);
|
|
201
|
+
const allowHttp = process.env['COAIA_MEDIA_ALLOW_HTTP'] === 'true';
|
|
202
|
+
if (url.protocol !== 'https:' && !(allowHttp && url.protocol === 'http:')) {
|
|
203
|
+
throw new Error(`Security: Upload URL protocol '${url.protocol}' is not allowed`);
|
|
204
|
+
}
|
|
205
|
+
if (!isTrustedDomain(url.hostname)) {
|
|
206
|
+
throw new Error(`Security: Upload URL domain '${url.hostname}' is not trusted`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
159
209
|
function isTrustedDomain(hostname) {
|
|
210
|
+
const configured = (process.env['COAIA_MEDIA_UPLOAD_HOSTS'] ?? '')
|
|
211
|
+
.split(',')
|
|
212
|
+
.map((domain) => domain.trim().toLowerCase())
|
|
213
|
+
.filter(Boolean);
|
|
214
|
+
const domains = [...TRUSTED_DOMAINS, ...configured];
|
|
160
215
|
const lower = hostname.toLowerCase();
|
|
161
|
-
|
|
162
|
-
if (lower === domain || lower.endsWith(`.${domain}`))
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
return false;
|
|
216
|
+
return domains.some((domain) => lower === domain || lower.endsWith(`.${domain}`));
|
|
166
217
|
}
|
|
167
218
|
//# sourceMappingURL=media.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/langfuse/media.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,oCAAoC;AAEpC,OAAO,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/langfuse/media.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type Server } from 'node:http';
|
|
3
|
+
import { type UploadMediaBytesResult } from './langfuse/media.js';
|
|
4
|
+
export interface OpenAIFileReference {
|
|
5
|
+
name: string;
|
|
6
|
+
id: string;
|
|
7
|
+
mime_type: string;
|
|
8
|
+
download_link: string;
|
|
9
|
+
}
|
|
10
|
+
export interface MediaUploadProxyRequest {
|
|
11
|
+
openaiFileIdRefs: unknown[];
|
|
12
|
+
traceId?: string;
|
|
13
|
+
observationId?: string;
|
|
14
|
+
datasetId?: string;
|
|
15
|
+
datasetItemId?: string;
|
|
16
|
+
field?: 'input' | 'output' | 'expectedOutput' | 'metadata';
|
|
17
|
+
}
|
|
18
|
+
export interface MediaUploadProxyOptions {
|
|
19
|
+
apiKey?: string;
|
|
20
|
+
maxFileBytes?: number;
|
|
21
|
+
maxBodyBytes?: number;
|
|
22
|
+
openAIFileHosts?: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface MediaUploadProxyResult extends UploadMediaBytesResult {
|
|
25
|
+
openaiFileId: string;
|
|
26
|
+
}
|
|
27
|
+
/** Download one Custom GPT conversation file and complete the Langfuse media upload flow. */
|
|
28
|
+
export declare function uploadOpenAIFileToLangfuse(input: MediaUploadProxyRequest, options?: MediaUploadProxyOptions): Promise<MediaUploadProxyResult>;
|
|
29
|
+
/** Create the small HTTP service used by the companion Custom GPT action spec. */
|
|
30
|
+
export declare function createMediaUploadProxyServer(options?: MediaUploadProxyOptions): Server;
|
|
31
|
+
export declare function startMediaUploadProxy(): Promise<Server>;
|
|
32
|
+
//# sourceMappingURL=media-upload-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-upload-proxy.d.ts","sourceRoot":"","sources":["../../src/media-upload-proxy.ts"],"names":[],"mappings":";AAIA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAEjG,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAMpF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,gBAAgB,EAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,gBAAgB,GAAG,UAAU,CAAC;CAC5D;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACpE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,6FAA6F;AAC7F,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,uBAAuB,EAC9B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAmBjC;AAED,kFAAkF;AAClF,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,uBAA4B,GAAG,MAAM,CA6B1F;AAmID,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU7D"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// coaiajs/src/media-upload-proxy.ts — Custom GPT file-to-Langfuse media bridge
|
|
3
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
4
|
+
import { createServer } from 'node:http';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { uploadMediaBytes } from './langfuse/media.js';
|
|
7
|
+
const DEFAULT_MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
8
|
+
const DEFAULT_BODY_BYTES = 1024 * 1024;
|
|
9
|
+
const DEFAULT_OPENAI_FILE_HOSTS = ['files.oaiusercontent.com', 'files.openai.com'];
|
|
10
|
+
/** Download one Custom GPT conversation file and complete the Langfuse media upload flow. */
|
|
11
|
+
export async function uploadOpenAIFileToLangfuse(input, options = {}) {
|
|
12
|
+
if (!Array.isArray(input.openaiFileIdRefs) || input.openaiFileIdRefs.length !== 1) {
|
|
13
|
+
throw new Error('openaiFileIdRefs must contain exactly one conversation file');
|
|
14
|
+
}
|
|
15
|
+
const file = parseOpenAIFileReference(input.openaiFileIdRefs[0]);
|
|
16
|
+
const allowedHosts = options.openAIFileHosts ?? configuredOpenAIFileHosts();
|
|
17
|
+
const bytes = await downloadFile(file.download_link, options.maxFileBytes ?? configuredMaxFileBytes(), allowedHosts);
|
|
18
|
+
const result = await uploadMediaBytes({
|
|
19
|
+
data: bytes,
|
|
20
|
+
fileName: file.name,
|
|
21
|
+
contentType: detectDownloadedContentType(bytes, file.mime_type),
|
|
22
|
+
traceId: input.traceId,
|
|
23
|
+
observationId: input.observationId,
|
|
24
|
+
datasetId: input.datasetId,
|
|
25
|
+
datasetItemId: input.datasetItemId,
|
|
26
|
+
field: input.field,
|
|
27
|
+
source: 'bytes',
|
|
28
|
+
});
|
|
29
|
+
return { ...result, openaiFileId: file.id };
|
|
30
|
+
}
|
|
31
|
+
/** Create the small HTTP service used by the companion Custom GPT action spec. */
|
|
32
|
+
export function createMediaUploadProxyServer(options = {}) {
|
|
33
|
+
const apiKey = options.apiKey ?? process.env['COAIA_MEDIA_PROXY_API_KEY'];
|
|
34
|
+
if (!apiKey)
|
|
35
|
+
throw new Error('COAIA_MEDIA_PROXY_API_KEY is required');
|
|
36
|
+
const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_BODY_BYTES;
|
|
37
|
+
return createServer(async (request, response) => {
|
|
38
|
+
try {
|
|
39
|
+
if (request.method === 'GET' && request.url === '/health') {
|
|
40
|
+
sendJson(response, 200, { ok: true });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (request.method !== 'POST' || request.url !== '/media/upload') {
|
|
44
|
+
sendJson(response, 404, { error: 'Not found' });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!secureEqual(request.headers['x-coaia-proxy-key'], apiKey)) {
|
|
48
|
+
sendJson(response, 401, { error: 'Unauthorized' });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const body = await readJsonBody(request, maxBodyBytes);
|
|
52
|
+
const result = await uploadOpenAIFileToLangfuse(body, options);
|
|
53
|
+
sendJson(response, 200, result);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
+
const status = /too large/i.test(message) ? 413 : 400;
|
|
58
|
+
sendJson(response, status, { error: message });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async function readJsonBody(request, maxBytes) {
|
|
63
|
+
const contentType = String(request.headers['content-type'] ?? '').toLowerCase();
|
|
64
|
+
if (!contentType.startsWith('application/json'))
|
|
65
|
+
throw new Error('Content-Type must be application/json');
|
|
66
|
+
const chunks = [];
|
|
67
|
+
let size = 0;
|
|
68
|
+
for await (const chunk of request) {
|
|
69
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
70
|
+
size += bytes.byteLength;
|
|
71
|
+
if (size > maxBytes)
|
|
72
|
+
throw new Error(`Request body is too large (maximum ${maxBytes} bytes)`);
|
|
73
|
+
chunks.push(bytes);
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new Error('Request body must contain valid JSON');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function parseOpenAIFileReference(value) {
|
|
83
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
84
|
+
throw new Error('Custom GPT did not provide a runtime file reference object');
|
|
85
|
+
}
|
|
86
|
+
const record = value;
|
|
87
|
+
const file = {
|
|
88
|
+
name: record.name,
|
|
89
|
+
id: record.id,
|
|
90
|
+
mime_type: record.mime_type,
|
|
91
|
+
download_link: record.download_link,
|
|
92
|
+
};
|
|
93
|
+
for (const [key, item] of Object.entries(file)) {
|
|
94
|
+
if (typeof item !== 'string' || !item)
|
|
95
|
+
throw new Error(`Conversation file reference is missing ${key}`);
|
|
96
|
+
}
|
|
97
|
+
return file;
|
|
98
|
+
}
|
|
99
|
+
async function downloadFile(urlValue, maxBytes, allowedHosts) {
|
|
100
|
+
let url = validateDownloadUrl(urlValue, allowedHosts);
|
|
101
|
+
for (let redirect = 0; redirect <= 3; redirect += 1) {
|
|
102
|
+
const response = await fetch(url, { redirect: 'manual' });
|
|
103
|
+
if (response.status >= 300 && response.status < 400) {
|
|
104
|
+
const location = response.headers.get('location');
|
|
105
|
+
if (!location)
|
|
106
|
+
throw new Error('Conversation file redirect did not include a location');
|
|
107
|
+
url = validateDownloadUrl(new URL(location, url).toString(), allowedHosts);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (!response.ok || !response.body) {
|
|
111
|
+
throw new Error(`Unable to download conversation file (HTTP ${response.status})`);
|
|
112
|
+
}
|
|
113
|
+
const declaredLength = Number(response.headers.get('content-length') ?? 0);
|
|
114
|
+
if (declaredLength > maxBytes)
|
|
115
|
+
throw new Error(`Conversation file is too large (maximum ${maxBytes} bytes)`);
|
|
116
|
+
const chunks = [];
|
|
117
|
+
let size = 0;
|
|
118
|
+
const reader = response.body.getReader();
|
|
119
|
+
while (true) {
|
|
120
|
+
const { done, value } = await reader.read();
|
|
121
|
+
if (done)
|
|
122
|
+
break;
|
|
123
|
+
size += value.byteLength;
|
|
124
|
+
if (size > maxBytes) {
|
|
125
|
+
await reader.cancel();
|
|
126
|
+
throw new Error(`Conversation file is too large (maximum ${maxBytes} bytes)`);
|
|
127
|
+
}
|
|
128
|
+
chunks.push(Buffer.from(value));
|
|
129
|
+
}
|
|
130
|
+
return Buffer.concat(chunks);
|
|
131
|
+
}
|
|
132
|
+
throw new Error('Conversation file download exceeded the redirect limit');
|
|
133
|
+
}
|
|
134
|
+
function validateDownloadUrl(value, allowedHosts) {
|
|
135
|
+
const url = new URL(value);
|
|
136
|
+
const allowHttp = process.env['COAIA_MEDIA_ALLOW_HTTP'] === 'true';
|
|
137
|
+
if (url.protocol !== 'https:' && !(allowHttp && url.protocol === 'http:')) {
|
|
138
|
+
throw new Error(`Conversation file URL protocol '${url.protocol}' is not allowed`);
|
|
139
|
+
}
|
|
140
|
+
if (!isAllowedHost(url.hostname, allowedHosts)) {
|
|
141
|
+
throw new Error(`Conversation file URL domain '${url.hostname}' is not allowed`);
|
|
142
|
+
}
|
|
143
|
+
if (url.username || url.password)
|
|
144
|
+
throw new Error('Conversation file URL credentials are not allowed');
|
|
145
|
+
return url;
|
|
146
|
+
}
|
|
147
|
+
function detectDownloadedContentType(bytes, declaredType) {
|
|
148
|
+
if (bytes.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
|
|
149
|
+
return 'image/png';
|
|
150
|
+
}
|
|
151
|
+
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
|
|
152
|
+
return 'image/jpeg';
|
|
153
|
+
const prefix = bytes.subarray(0, 6).toString('ascii');
|
|
154
|
+
if (prefix === 'GIF87a' || prefix === 'GIF89a')
|
|
155
|
+
return 'image/gif';
|
|
156
|
+
if (bytes.subarray(0, 4).toString('ascii') === 'RIFF' && bytes.subarray(8, 12).toString('ascii') === 'WEBP') {
|
|
157
|
+
return 'image/webp';
|
|
158
|
+
}
|
|
159
|
+
if (bytes.subarray(0, 5).toString('ascii') === '%PDF-')
|
|
160
|
+
return 'application/pdf';
|
|
161
|
+
return declaredType;
|
|
162
|
+
}
|
|
163
|
+
function configuredOpenAIFileHosts() {
|
|
164
|
+
const extra = (process.env['COAIA_OPENAI_FILE_HOSTS'] ?? '')
|
|
165
|
+
.split(',')
|
|
166
|
+
.map((host) => host.trim().toLowerCase())
|
|
167
|
+
.filter(Boolean);
|
|
168
|
+
return [...DEFAULT_OPENAI_FILE_HOSTS, ...extra];
|
|
169
|
+
}
|
|
170
|
+
function configuredMaxFileBytes() {
|
|
171
|
+
const configured = Number(process.env['COAIA_MEDIA_MAX_BYTES']);
|
|
172
|
+
return Number.isSafeInteger(configured) && configured > 0 ? configured : DEFAULT_MAX_FILE_BYTES;
|
|
173
|
+
}
|
|
174
|
+
function isAllowedHost(hostname, allowedHosts) {
|
|
175
|
+
const lower = hostname.toLowerCase();
|
|
176
|
+
return allowedHosts.some((host) => lower === host || lower.endsWith(`.${host}`));
|
|
177
|
+
}
|
|
178
|
+
function secureEqual(actual, expected) {
|
|
179
|
+
if (!actual || Array.isArray(actual))
|
|
180
|
+
return false;
|
|
181
|
+
const actualBytes = Buffer.from(actual);
|
|
182
|
+
const expectedBytes = Buffer.from(expected);
|
|
183
|
+
return actualBytes.byteLength === expectedBytes.byteLength && timingSafeEqual(actualBytes, expectedBytes);
|
|
184
|
+
}
|
|
185
|
+
function sendJson(response, status, body) {
|
|
186
|
+
response.writeHead(status, {
|
|
187
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
188
|
+
'Cache-Control': 'no-store',
|
|
189
|
+
});
|
|
190
|
+
response.end(JSON.stringify(body));
|
|
191
|
+
}
|
|
192
|
+
export async function startMediaUploadProxy() {
|
|
193
|
+
const port = Number(process.env['PORT'] ?? process.env['COAIA_MEDIA_PROXY_PORT'] ?? 8787);
|
|
194
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
195
|
+
throw new Error(`Invalid proxy port: ${port}`);
|
|
196
|
+
const server = createMediaUploadProxyServer();
|
|
197
|
+
await new Promise((resolve, reject) => {
|
|
198
|
+
server.once('error', reject);
|
|
199
|
+
server.listen(port, '0.0.0.0', () => resolve());
|
|
200
|
+
});
|
|
201
|
+
console.log(`coaiajs media upload proxy listening on port ${port}`);
|
|
202
|
+
return server;
|
|
203
|
+
}
|
|
204
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
205
|
+
startMediaUploadProxy().catch((error) => {
|
|
206
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=media-upload-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-upload-proxy.js","sourceRoot":"","sources":["../../src/media-upload-proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COAIA Narrative - Argument Hygiene
|
|
3
|
+
*
|
|
4
|
+
* A malformed tool call must not become chart content.
|
|
5
|
+
*
|
|
6
|
+
* On 2026-07-30 a live store was found carrying seven observations whose bodies
|
|
7
|
+
* ended in `</currentReality>` followed by a `<parameter name="dueDate">` block:
|
|
8
|
+
* the raw text of a call whose argument tags never parsed, persisted verbatim as
|
|
9
|
+
* if it were prose. The call did not fail — it wrote, and every consumer that
|
|
10
|
+
* renders current reality has shown that tag ever since.
|
|
11
|
+
*
|
|
12
|
+
* The check belongs here, at the write boundary. Anything that reaches the JSONL
|
|
13
|
+
* is already in every reader's render, so a read-side filter arrives too late.
|
|
14
|
+
* A body carrying plainly unparsed call syntax is refused with the offending
|
|
15
|
+
* fragment named, so the caller can retry with the text it meant to record.
|
|
16
|
+
*/
|
|
17
|
+
export interface UnparsedCallSyntaxHit {
|
|
18
|
+
/** The offending text, quoted back so the caller can see what it sent. */
|
|
19
|
+
fragment: string;
|
|
20
|
+
/** Plain-language account of what the fragment is. */
|
|
21
|
+
reason: string;
|
|
22
|
+
/** Where in the body the prose ended and the call text began. */
|
|
23
|
+
index: number;
|
|
24
|
+
}
|
|
25
|
+
export interface UnparsedCallSyntaxLocation extends UnparsedCallSyntaxHit {
|
|
26
|
+
/** Where in the arguments it was found, e.g. `newObservations[0]`. */
|
|
27
|
+
path: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Return the first unparsed-call-syntax fragment in `text`, or null if the text
|
|
31
|
+
* is ordinary prose. Angle brackets on their own are fine — `<div>`, `a < b`,
|
|
32
|
+
* `<rootDir>` all pass. Only call machinery and argument closing tags are refused.
|
|
33
|
+
*/
|
|
34
|
+
export declare function findUnparsedCallSyntax(text: string): UnparsedCallSyntaxHit | null;
|
|
35
|
+
/**
|
|
36
|
+
* Walk any argument value — string, array, or object — and return the first
|
|
37
|
+
* location carrying unparsed call syntax.
|
|
38
|
+
*/
|
|
39
|
+
export declare function findUnparsedCallSyntaxIn(value: unknown, path?: string): UnparsedCallSyntaxLocation | null;
|
|
40
|
+
/** The message a caller sees: what was refused, where, and what to do next. */
|
|
41
|
+
export declare function describeUnparsedCallSyntax(location: UnparsedCallSyntaxLocation): string;
|
|
42
|
+
/** Throw unless `value` is free of unparsed call syntax. Used at the store's edge. */
|
|
43
|
+
export declare function assertNoUnparsedCallSyntax(value: unknown, path: string): void;
|
|
44
|
+
/** The argument-name vocabulary, exported so the scrubber reports what the server refuses. */
|
|
45
|
+
export declare const KNOWN_ARGUMENT_NAMES: readonly string[];
|
|
46
|
+
//# sourceMappingURL=argument-hygiene.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"argument-hygiene.d.ts","sourceRoot":"","sources":["../../../src/narrative/argument-hygiene.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA2B,SAAQ,qBAAqB;IACvE,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;CACd;AA8DD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CA0BjF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAK,GAAG,0BAA0B,GAAG,IAAI,CAuBrG;AAED,+EAA+E;AAC/E,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,0BAA0B,GAAG,MAAM,CAYvF;AAED,sFAAsF;AACtF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAG7E;AAED,8FAA8F;AAC9F,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAAmB,CAAC"}
|