coaiajs 0.4.2 → 0.4.3

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 CHANGED
@@ -50,7 +50,8 @@ Import from the root, or from a subpath to pull in only what you need. Every ent
50
50
  | `coaiajs/github` | `listIssues`, `getIssue`, `getIssueComments`, `resetClient` |
51
51
  | `coaiajs/environment` | `EnvironmentManager`, `createEnvironment`, `findEnvironment` |
52
52
  | `coaiajs/version` | `getPackageVersion` |
53
- | `coaiajs/langfuse` | Langfuse JS SDK v5/OpenTelemetry tracing plus v4 observations, prompts, datasets, scores, comments, media, projects, and formatters (55 exports) |
53
+ | `coaiajs/langfuse` | Langfuse JS SDK v5/OpenTelemetry tracing plus v4 observations, prompts, datasets, scores, comments, media, projects, and formatters (56 runtime exports) |
54
+ | `coaiajs/media-upload-proxy` | Deployable Custom GPT conversation-file to Langfuse media bridge |
54
55
  | `coaiajs/narrative` | `KnowledgeGraphManager`, chart operations, markdown export, tool definitions (29 exports) |
55
56
  | `coaiajs/pde` | `SessionManager`, `StcMapper`, `handlePdeTool`, `PDE_MCP_TOOLS` (10 exports) |
56
57
  | `coaiajs/planning` | `parsePlan`, `planToSTC`, `syncToChart`, `syncToPlan`, `handlePlanningTool` (10 exports) |
@@ -140,7 +141,9 @@ Migration references: [Langfuse v4](https://langfuse.com/docs/v4) · [versions a
140
141
 
141
142
  ### Custom GPT observation actions
142
143
 
143
- [`agents/custom_gpt/ceremony-observations.yml`](./agents/custom_gpt/ceremony-observations.yml) is a focused OpenAPI 3.1 action specification for creating a root observation and appending child observations to the same Langfuse trace through OTLP/HTTP JSON. It contains 20 core actions—below the 30-action limit—and omits media and destructive operations. Setup, authentication, ID/timestamp rules, and ready-to-paste GPT instructions are in [`ceremony-observations.instructions.md`](./agents/custom_gpt/ceremony-observations.instructions.md). The broader imported API surface remains available in [`ceremony.yml`](./agents/custom_gpt/ceremony.yml).
144
+ [`agents/custom_gpt/ceremony-observations.yml`](./agents/custom_gpt/ceremony-observations.yml) is a focused OpenAPI 3.1 action specification for creating a root observation and appending child observations to the same Langfuse trace through OTLP/HTTP JSON. It contains 23 core actions—below the 30-action limit—including media upload-record, retrieval, and finalization capabilities; destructive operations remain omitted. Media hashes use the required 44-character padded Base64 SHA-256 digest of the exact upload bytes, and the documented workflow distinguishes URL creation from the actual presigned PUT. Setup, authentication, ID/timestamp rules, and ready-to-paste GPT instructions are in [`ceremony-observations.instructions.md`](./agents/custom_gpt/ceremony-observations.instructions.md). The broader imported API surface remains available in [`ceremony.yml`](./agents/custom_gpt/ceremony.yml).
145
+
146
+ For autonomous conversation-file uploads, deploy the included `coaiajs-media-proxy` service and import [`ceremony-media-proxy.yml`](./agents/custom_gpt/ceremony-media-proxy.yml) as a second Action. It uses OpenAI's `openaiFileIdRefs` transfer, downloads the actual user/DALL-E/Code Interpreter file, performs the Langfuse media record + presigned PUT + finalization sequence, and returns a renderable `@@@langfuseMedia...@@@` token. Deployment and security instructions are in [`ceremony-media-proxy.instructions.md`](./agents/custom_gpt/ceremony-media-proxy.instructions.md). Together both specs expose 24 actions.
144
147
 
145
148
  ---
146
149
 
@@ -0,0 +1,86 @@
1
+ # Ceremony Media Upload Proxy
2
+
3
+ `ceremony-media-proxy.yml` is the executable bridge missing from direct Langfuse media actions. It uses the official Custom GPT `openaiFileIdRefs` mechanism, so the GPT sends a temporary reference to the user's real file instead of attempting to place a large Base64 payload in function arguments.
4
+
5
+ ## Why this is a second action specification
6
+
7
+ The main `ceremony-observations.yml` talks directly to `cloud.langfuse.com`. A presigned media URL points to a dynamic object-storage host, which cannot be represented as a static OpenAPI path. The bridge therefore runs on a public HTTPS service you control and is imported as a second GPT Action. Together the specifications expose 24 actions, below the 30-action limit.
8
+
9
+ ## Deploy the bridge
10
+
11
+ Build and run the included Node service:
12
+
13
+ ```bash
14
+ npm install
15
+ npm run build
16
+
17
+ export LANGFUSE_PUBLIC_KEY='pk-lf-...'
18
+ export LANGFUSE_SECRET_KEY='sk-lf-...'
19
+ export LANGFUSE_BASE_URL='https://cloud.langfuse.com'
20
+ export COAIA_MEDIA_PROXY_API_KEY='generate-a-long-random-secret'
21
+ export PORT=8787
22
+
23
+ node dist/src/media-upload-proxy.js
24
+ # or, from the installed package:
25
+ coaiajs-media-proxy
26
+ ```
27
+
28
+ Deploy it behind a public HTTPS URL. The Custom GPT service cannot reach localhost.
29
+
30
+ Optional controls:
31
+
32
+ - `COAIA_MEDIA_MAX_BYTES`: maximum downloaded file size; defaults to 10 MiB.
33
+ - `COAIA_OPENAI_FILE_HOSTS`: comma-separated additional trusted conversation-file hosts.
34
+ - `COAIA_MEDIA_UPLOAD_HOSTS`: comma-separated additional trusted presigned-upload hosts for self-hosted storage.
35
+ - `COAIA_MEDIA_ALLOW_HTTP=true`: allows HTTP for local tests only; never use it in production.
36
+
37
+ The proxy only downloads from approved OpenAI file hosts and only uploads to approved S3-compatible storage hosts. It rejects empty files, invalid media contexts, oversized payloads, and untrusted URLs.
38
+
39
+ ## Configure the Custom GPT
40
+
41
+ 1. Replace `https://replace-with-your-proxy.invalid` in `ceremony-media-proxy.yml` with the deployed HTTPS origin.
42
+ 2. Import it as a second Action alongside `ceremony-observations.yml`.
43
+ 3. Configure API Key authentication with:
44
+ - Header: `X-Coaia-Proxy-Key`
45
+ - Value: the `COAIA_MEDIA_PROXY_API_KEY` configured on the service
46
+ 4. Keep `x-openai-isConsequential: false` unchanged.
47
+ 5. Do not put the proxy key or Langfuse keys in GPT instructions or conversation text.
48
+
49
+ ## GPT behavior
50
+
51
+ Add this to the GPT instructions:
52
+
53
+ ```markdown
54
+ When the user supplies media for Langfuse, call media_uploadConversationFile
55
+ with exactly that conversation file in openaiFileIdRefs and the target trace or
56
+ dataset-item context. Never create placeholder bytes unless explicitly asked.
57
+
58
+ The bridge performs media-record creation, exact-byte hashing, the presigned PUT,
59
+ and media finalization. Do not claim success unless the bridge returns
60
+ success=true and a mediaToken.
61
+
62
+ Put the returned mediaToken unchanged into an observation input, output, or
63
+ metadata value, then call observations_export. Optionally call media_get with
64
+ the returned mediaId to verify uploadedAt and the temporary URL.
65
+ ```
66
+
67
+ ## Completed flow
68
+
69
+ ```text
70
+ user/DALL-E/Code Interpreter file
71
+ → openaiFileIdRefs temporary download URL
72
+ → media_uploadConversationFile
73
+ → download exact bytes
74
+ → compute padded Base64 SHA-256 and exact length/type
75
+ → POST /api/public/media
76
+ → PUT exact bytes to the presigned storage URL
77
+ → PATCH /api/public/media/{mediaId}
78
+ → return @@@langfuseMedia:type=...|id=...|source=bytes@@@
79
+ → observations_export containing the returned media token
80
+ → observations_list / media_get verification
81
+ ```
82
+
83
+ Primary references:
84
+
85
+ - OpenAI GPT Actions file transfer: https://developers.openai.com/api/docs/actions/sending-files
86
+ - Langfuse multi-modality and media tokens: https://langfuse.com/docs/observability/features/multi-modality
@@ -0,0 +1,158 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: Coaia Langfuse Media Upload Bridge
4
+ version: 0.4.2
5
+ description: >-
6
+ Companion Custom GPT action that transfers one conversation file into Langfuse object storage, finalizes the media
7
+ record, and returns a renderable Langfuse media token. Deploy coaiajs-media-proxy and replace the example server URL.
8
+ license:
9
+ name: MIT
10
+ identifier: MIT
11
+ servers:
12
+ - url: https://replace-with-your-proxy.invalid
13
+ description: Replace with the public HTTPS URL of the deployed coaiajs media proxy.
14
+ security:
15
+ - ProxyApiKey: []
16
+ paths:
17
+ /media/upload:
18
+ post:
19
+ operationId: media_uploadConversationFile
20
+ summary: Upload a conversation file to Langfuse
21
+ description: >-
22
+ Upload exactly one user, DALL-E, or Code Interpreter file. The bridge downloads its exact bytes, computes the
23
+ padded Base64 SHA-256 digest and length, creates the Langfuse record, performs the presigned PUT, finalizes it,
24
+ and returns a media token. Never substitute placeholder bytes.
25
+ requestBody:
26
+ required: true
27
+ content:
28
+ application/json:
29
+ schema:
30
+ $ref: '#/components/schemas/UploadConversationFileRequest'
31
+ responses:
32
+ '200':
33
+ description: Uploaded media and its renderable Langfuse token.
34
+ content:
35
+ application/json:
36
+ schema:
37
+ $ref: '#/components/schemas/UploadConversationFileResponse'
38
+ '400':
39
+ description: Invalid file reference, media context, or downstream upload.
40
+ content:
41
+ application/json:
42
+ schema:
43
+ $ref: '#/components/schemas/ErrorResponse'
44
+ '401':
45
+ description: Missing or invalid proxy API key.
46
+ content:
47
+ application/json:
48
+ schema:
49
+ $ref: '#/components/schemas/ErrorResponse'
50
+ '413':
51
+ description: File or request exceeds the configured size limit.
52
+ content:
53
+ application/json:
54
+ schema:
55
+ $ref: '#/components/schemas/ErrorResponse'
56
+ x-openai-isConsequential: false
57
+ components:
58
+ securitySchemes:
59
+ ProxyApiKey:
60
+ type: apiKey
61
+ in: header
62
+ name: X-Coaia-Proxy-Key
63
+ description: Configure this value in the GPT Action authentication UI; it must equal COAIA_MEDIA_PROXY_API_KEY.
64
+ schemas:
65
+ UploadConversationFileRequest:
66
+ type: object
67
+ additionalProperties: false
68
+ properties:
69
+ openaiFileIdRefs:
70
+ type: array
71
+ minItems: 1
72
+ maxItems: 1
73
+ items:
74
+ type: string
75
+ description: >-
76
+ Exactly one file from the conversation: the user's actual media, a DALL-E image, or a Code Interpreter
77
+ file. Custom GPT replaces this string entry with its temporary runtime file-reference object.
78
+ traceId:
79
+ type: string
80
+ description: Trace receiving the media. Use either trace context or dataset-item context.
81
+ observationId:
82
+ type: string
83
+ description: Optional observation receiving the media; requires traceId.
84
+ datasetId:
85
+ type: string
86
+ description: Dataset receiving the media; requires datasetItemId and no traceId.
87
+ datasetItemId:
88
+ type: string
89
+ description: Dataset item receiving the media; requires datasetId and no traceId.
90
+ field:
91
+ type: string
92
+ enum:
93
+ - input
94
+ - output
95
+ - expectedOutput
96
+ - metadata
97
+ default: input
98
+ description: Field where the returned media token will be referenced.
99
+ required:
100
+ - openaiFileIdRefs
101
+ UploadConversationFileResponse:
102
+ type: object
103
+ additionalProperties: false
104
+ properties:
105
+ success:
106
+ type: boolean
107
+ mediaId:
108
+ type: string
109
+ mediaToken:
110
+ type: string
111
+ description: Put this exact token in observation input, output, or metadata so Langfuse renders the media.
112
+ fileName:
113
+ type: string
114
+ openaiFileId:
115
+ type: string
116
+ contentType:
117
+ type: string
118
+ contentLength:
119
+ type: integer
120
+ sha256Hash:
121
+ type: string
122
+ minLength: 44
123
+ maxLength: 44
124
+ pattern: '^[A-Za-z0-9+/]{43}=$'
125
+ description: Standard padded Base64 SHA-256 digest of the uploaded bytes.
126
+ traceId:
127
+ type: string
128
+ observationId:
129
+ type: string
130
+ datasetId:
131
+ type: string
132
+ datasetItemId:
133
+ type: string
134
+ field:
135
+ type: string
136
+ uploadTimeMs:
137
+ type: integer
138
+ alreadyUploaded:
139
+ type: boolean
140
+ required:
141
+ - success
142
+ - mediaId
143
+ - mediaToken
144
+ - openaiFileId
145
+ - contentType
146
+ - contentLength
147
+ - sha256Hash
148
+ - field
149
+ - uploadTimeMs
150
+ - alreadyUploaded
151
+ ErrorResponse:
152
+ type: object
153
+ additionalProperties: false
154
+ properties:
155
+ error:
156
+ type: string
157
+ required:
158
+ - error
@@ -10,7 +10,6 @@ Langfuse v4 does not provide mutable REST trace and observation creation endpoin
10
10
  POST /api/public/otel/v1/traces
11
11
  Content-Type: application/json
12
12
  Authorization: Basic <base64(public-key:secret-key)>
13
- x-langfuse-ingestion-version: 4
14
13
  ```
15
14
 
16
15
  The `observations_export` action exposes that endpoint with a constrained OTLP schema and examples. It supports both:
@@ -18,7 +17,7 @@ The `observations_export` action exposes that endpoint with a constrained OTLP s
18
17
  1. Creating a trace by sending a root span without `parentSpanId`.
19
18
  2. Appending an observation by reusing the trace ID and setting `parentSpanId` to an existing observation/span ID.
20
19
 
21
- The file has **20 actions**, below the stated 30-action limit. Scores remain in this specification because the complete focused surface still fits. Media and destructive delete actions are omitted.
20
+ The file has **23 actions**, below the stated 30-action limit. Scores and the original media operations remain in this specification because the complete focused surface still fits. Destructive delete actions are omitted.
22
21
 
23
22
  ## Configure the Custom GPT
24
23
 
@@ -44,7 +43,7 @@ The file has **20 actions**, below the stated 30-action limit. Scores remain in
44
43
  end_ns = max(time.time_ns(), start_ns)
45
44
  ```
46
45
 
47
- The ingestion operation also declares the required `x-langfuse-ingestion-version` action header with the only allowed value `4`. If a GPT Actions client omits this non-auth header, Langfuse documents that direct OTLP ingestion still works but may take up to ten minutes to appear in v4 reads. A proxy is required if the client cannot send the header and real-time visibility is mandatory.
46
+ Custom GPT currently ignores ordinary OpenAPI header parameters, so this direct specification intentionally does not declare `x-langfuse-ingestion-version`. Langfuse documents that OTLP ingestion without that header still works, but data may take up to ten minutes to appear in v4 reads. If real-time visibility is mandatory, use a trusted proxy that adds `x-langfuse-ingestion-version: 4` before forwarding the request to Langfuse.
48
47
 
49
48
  ## Recommended GPT instructions
50
49
 
@@ -80,6 +79,19 @@ span. To record a correction or later result, append a new child event/span.
80
79
  After exporting, call observations_list with the traceId and fields
81
80
  core,basic,time,io,metadata,model,usage,trace_context to verify the hierarchy.
82
81
  Use each returned observation id as the span/parent ID for later children.
82
+
83
+ ### Langfuse media
84
+
85
+ When the user supplies media, use the actual file bytes—never placeholder text.
86
+ Compute contentLength from those bytes. Compute SHA-256 over those bytes and
87
+ send the standard padded Base64 digest, not hexadecimal, as sha256Hash.
88
+
89
+ Prefer media_uploadConversationFile from the companion proxy Action; pass the
90
+ user's actual conversation file in openaiFileIdRefs. It performs record
91
+ creation, the exact-byte PUT, and finalization and returns a mediaToken.
92
+
93
+ media_getUploadUrl alone only creates a record. Never claim success without a
94
+ completed PUT and media_patch, or success=true from the proxy bridge.
83
95
  ```
84
96
 
85
97
  ## Minimal append example
@@ -102,7 +114,7 @@ The parent trace and root observation must already exist. Replace every example
102
114
  {
103
115
  "scope": {
104
116
  "name": "custom-gpt-ceremony",
105
- "version": "0.4.0"
117
+ "version": "0.4.2"
106
118
  },
107
119
  "spans": [
108
120
  {
@@ -151,7 +163,28 @@ The parent trace and root observation must already exist. Replace every example
151
163
  | Datasets and dataset items | 6 |
152
164
  | Scores and score configurations | 5 |
153
165
  | Comments | 2 |
154
- | **Total** | **20** |
166
+ | Media | 3 |
167
+ | **Total** | **23** |
168
+
169
+ ## Media workflow
170
+
171
+ When the user supplies media, always use that media's actual bytes. Never create or hash placeholder text or surrogate bytes unless the user explicitly requests a placeholder.
172
+
173
+ 1. Read the exact bytes that will be uploaded.
174
+ 2. Derive `contentType` from those bytes/file and set `contentLength` to their exact byte length.
175
+ 3. Compute SHA-256 over those exact bytes, then encode the 32-byte digest with standard padded RFC 4648 Base64. Send the resulting 44-character value as `sha256Hash`; never send the 64-character hexadecimal digest.
176
+ 4. Call `media_getUploadUrl` with the context, file metadata, Base64 hash, and field.
177
+ 5. PUT the same exact bytes to the returned presigned `uploadUrl` using the declared content type.
178
+ 6. Only after that PUT completes, call `media_patch` with the completion time and HTTP result.
179
+ 7. Optionally call `media_get` to verify the media record and obtain a temporary download URL.
180
+
181
+ Example for the four UTF-8 bytes `test`:
182
+
183
+ ```text
184
+ sha256Hash: n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=
185
+ ```
186
+
187
+ Receiving `uploadUrl` creates a Langfuse media record but does **not** upload the bytes. Never report that media was uploaded after `media_getUploadUrl` alone. The direct OpenAPI document cannot describe a PUT to a dynamically returned storage host. For autonomous uploads, deploy the included `coaiajs-media-proxy`, import `ceremony-media-proxy.yml` as a second GPT Action, and use `media_uploadConversationFile`. The bridge consumes the user's actual `openaiFileIdRefs` file, performs the complete upload flow, and returns the media token.
155
188
 
156
189
  ## Why there are not separate create-trace and append-observation actions
157
190