@adobe/llm-apps-runtime 0.2.0 → 0.4.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 CHANGED
@@ -71,25 +71,39 @@ When `moduleContext` is provided, the webpack path is used. Otherwise the filesy
71
71
 
72
72
  **Returns** `{ main }` — an async function compatible with Adobe I/O Runtime's action signature.
73
73
 
74
- ### `createLocalServer(main, port)`
74
+ ### `createLocalServer(main, port, extraParams)`
75
75
 
76
76
  Starts a plain Node.js HTTP server that wraps a `main(params)` function. Useful for `npm run dev:local` in consumer apps — no Adobe credentials required.
77
77
 
78
78
  ```js
79
- const { createLocalServer } = require('@adobe/llm-apps-runtime')
79
+ const { createLocalServer, parseParamArgs } = require('@adobe/llm-apps-runtime/local')
80
80
  const { main } = require('./dist/index.js') // your webpack bundle
81
- createLocalServer(main, process.env.PORT || 9080)
81
+ const extraParams = parseParamArgs(process.argv) // --param KEY=VALUE flags
82
+ createLocalServer(main, process.env.PORT || 9080, extraParams)
82
83
  ```
83
84
 
84
85
  **Parameters**
85
86
 
86
- | Parameter | Type | Default | Description |
87
- |-----------|----------|---------|-------------|
88
- | `main` | Function | — | The `main(params)` function returned by `createMain`. |
89
- | `port` | number | `9080` | Port to listen on. |
87
+ | Parameter | Type | Default | Description |
88
+ |---------------|----------|---------|-------------|
89
+ | `main` | Function | — | The `main(params)` function returned by `createMain`. |
90
+ | `port` | number | `9080` | Port to listen on. |
91
+ | `extraParams` | object | `{}` | Key-value pairs merged into every request's `params`, simulating Adobe I/O Runtime action params. |
90
92
 
91
93
  **Returns** an [`http.Server`](https://nodejs.org/api/http.html#class-httpserver) instance.
92
94
 
95
+ #### Passing action params locally
96
+
97
+ In production, Adobe I/O Runtime merges action params (set via `aio app deploy --param`) into the `params` object that `main()` receives alongside the request. Locally there is no runtime, so you pass them on the command line using the same `--param KEY=VALUE` syntax:
98
+
99
+ ```bash
100
+ node server/local.js \
101
+ --param MY_API_URL=http://localhost:3000 \
102
+ --param MY_API_KEY=dev-secret
103
+ ```
104
+
105
+ `parseParamArgs(process.argv)` reads these flags and passes them to `createLocalServer` as `extraParams`. Every inbound request then receives them as top-level `params` fields, so any param-reading code in your action works identically to the deployed runtime.
106
+
93
107
  ### Action loading primitives
94
108
 
95
109
  Exported from the package root for advanced use cases (custom loaders, integration tests):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/llm-apps-runtime",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Server runtime for Adobe LLM Apps — a platform for building AI tools and interactive widgets on Adobe I/O Runtime",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -42,5 +42,9 @@
42
42
  "serverless",
43
43
  "i/o-runtime"
44
44
  ],
45
- "license": "SEE LICENSE IN LICENSE"
45
+ "license": "SEE LICENSE IN LICENSE",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/Adobe-AIFoundations/llm-apps-runtime.git"
49
+ }
46
50
  }
package/src/analytics.js CHANGED
@@ -13,7 +13,7 @@ governing permissions and limitations under the License.
13
13
  /**
14
14
  * Per-invocation analytics buffering + signed batch flush.
15
15
  *
16
- * OpenWhisk freezes containers after `main()` returns, so we cannot fire-and-forget
16
+ * Adobe I/O Runtime freezes containers after `main()` returns, so we cannot fire-and-forget
17
17
  * HTTP from one invocation and have it complete in the next. The wrapper buffers
18
18
  * events in a per-invocation array, then performs a single awaited POST at
19
19
  * end-of-request. One MCP request that triggers N tool calls = one HTTP round-trip
@@ -25,7 +25,7 @@ governing permissions and limitations under the License.
25
25
  * Cost model: this code runs inside the customer's Adobe I/O Runtime namespace
26
26
  * and consumes the customer's App Builder Pack entitlement (6M GB-s/year). The
27
27
  * 500 ms hard timeout caps the customer's billable activation extension at
28
- * ~600 ms (rounded up to the nearest 100 ms by OpenWhisk billing). The circuit
28
+ * ~600 ms (rounded up to the nearest 100 ms by Adobe I/O Runtime billing). The circuit
29
29
  * breaker exists primarily to protect the customer's GB-s budget when ingest
30
30
  * is slow or unhealthy — load-shedding for the customer's wallet, not just for
31
31
  * our service.
@@ -85,7 +85,7 @@ function safeSize (value) {
85
85
  }
86
86
 
87
87
  /**
88
- * Read analytics config from OpenWhisk action params. Returns null when
88
+ * Read analytics config from Adobe I/O Runtime action params. Returns null when
89
89
  * any required field is missing; the wrapper short-circuits to a no-op
90
90
  * so unconfigured deploys cost zero network calls.
91
91
  */
package/src/index.js CHANGED
@@ -207,8 +207,6 @@ function createMain (options = {}) {
207
207
 
208
208
  logger?.info('MCP request processed by SDK')
209
209
 
210
- await flushAnalyticsSafe(analyticsConfig)
211
-
212
210
  return {
213
211
  statusCode: response.status,
214
212
  headers: {
@@ -225,10 +223,6 @@ function createMain (options = {}) {
225
223
 
226
224
  try { server.close() } catch (e) { /* ignore cleanup errors */ }
227
225
 
228
- // Flush whatever we managed to buffer before the failure so
229
- // partial telemetry isn't lost on the error path.
230
- await flushAnalyticsSafe(analyticsConfig)
231
-
232
226
  return {
233
227
  statusCode: 500,
234
228
  headers: {
@@ -241,6 +235,11 @@ function createMain (options = {}) {
241
235
  id: null
242
236
  })
243
237
  }
238
+ } finally {
239
+ // Flush on every exit path — success, error, or unexpected throw.
240
+ // In JS, finally runs before the pending return resolves, so the
241
+ // POST completes before the Adobe I/O Runtime container freezes.
242
+ await flushAnalyticsSafe(analyticsConfig)
244
243
  }
245
244
  }
246
245
 
package/src/local.js CHANGED
@@ -18,21 +18,47 @@ governing permissions and limitations under the License.
18
18
  *
19
19
  * Usage in consumer app server/local.js:
20
20
  *
21
- * const { createLocalServer } = require('@adobe/llm-apps-runtime')
21
+ * const { createLocalServer, parseParamArgs } = require('@adobe/llm-apps-runtime/local')
22
22
  * const { main } = require('../dist/index.js') // webpack bundle
23
- * createLocalServer(main, process.env.PORT || 9080)
23
+ * const extraParams = parseParamArgs(process.argv) // --param KEY=VALUE flags
24
+ * createLocalServer(main, process.env.PORT || 9080, extraParams)
24
25
  */
25
26
 
26
27
  const http = require('http')
27
28
 
29
+ /**
30
+ * Parse --param KEY=VALUE pairs from an argv array, mirroring the grammar
31
+ * used by `aio app deploy --param`. Returns a plain object. Entries that
32
+ * do not contain '=' or have an empty key are silently skipped.
33
+ *
34
+ * @param {string[]} argv process.argv (or any argv-style array)
35
+ * @returns {object}
36
+ */
37
+ function parseParamArgs (argv) {
38
+ const out = {}
39
+ for (let i = 2; i < argv.length; i++) {
40
+ if (argv[i] === '--param' && argv[i + 1]) {
41
+ const eq = argv[i + 1].indexOf('=')
42
+ if (eq > 0) {
43
+ out[argv[i + 1].slice(0, eq)] = argv[i + 1].slice(eq + 1)
44
+ }
45
+ i++
46
+ }
47
+ }
48
+ return out
49
+ }
50
+
28
51
  /**
29
52
  * Start a local HTTP server that routes plain HTTP requests to an MCP main() function.
30
53
  *
31
- * @param {Function} main The main(params) function returned by createMain
32
- * @param {number} port Port to listen on (default 9080)
54
+ * @param {Function} main The main(params) function returned by createMain
55
+ * @param {number} port Port to listen on (default 9080)
56
+ * @param {object} extraParams Optional action params to inject into every request,
57
+ * simulating Adobe I/O Runtime action params (e.g. LLMA_ANALYTICS_*).
58
+ * Use parseParamArgs(process.argv) to populate from --param flags.
33
59
  * @returns {http.Server}
34
60
  */
35
- function createLocalServer (main, port) {
61
+ function createLocalServer (main, port, extraParams = {}) {
36
62
  // Use ?? (not ||) so an explicit 0 (ephemeral port, useful for tests) is honored.
37
63
  const listenPort = port ?? 9080
38
64
 
@@ -43,6 +69,7 @@ function createLocalServer (main, port) {
43
69
  const rawBody = Buffer.concat(chunks).toString('utf8')
44
70
 
45
71
  const params = {
72
+ ...extraParams, // simulate Adobe I/O Runtime action params
46
73
  __ow_method: req.method.toLowerCase(),
47
74
  __ow_path: req.url,
48
75
  __ow_body: rawBody || undefined,
@@ -88,4 +115,4 @@ function createLocalServer (main, port) {
88
115
  return server
89
116
  }
90
117
 
91
- module.exports = { createLocalServer }
118
+ module.exports = { createLocalServer, parseParamArgs }