@isomorph.ai/app-sdk 2.0.0 → 2.0.1
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/runtime.d.ts +11 -0
- package/dist/runtime.js +17 -3
- package/package.json +1 -1
package/dist/runtime.d.ts
CHANGED
|
@@ -100,6 +100,8 @@ export interface RuntimeOptions {
|
|
|
100
100
|
gatewayIdentity?: 'sts-session';
|
|
101
101
|
/** The SQS queue an enqueue sends its run to (ISOMORPH_JOBS_QUEUE_URL); the queue loop is off when set. */
|
|
102
102
|
queueUrl?: string;
|
|
103
|
+
/** How long a refused AI binding is remembered before the next handler asks the gateway again. */
|
|
104
|
+
aiRetryMs?: number;
|
|
103
105
|
}
|
|
104
106
|
export declare class Runtime {
|
|
105
107
|
private readonly options;
|
|
@@ -109,6 +111,7 @@ export declare class Runtime {
|
|
|
109
111
|
private readonly timers;
|
|
110
112
|
private stopped;
|
|
111
113
|
private aiBinding;
|
|
114
|
+
private aiRefusedUntil;
|
|
112
115
|
private queueWorkers;
|
|
113
116
|
private queueWake;
|
|
114
117
|
private claimTail;
|
|
@@ -151,6 +154,14 @@ export declare class Runtime {
|
|
|
151
154
|
private awaitDataPath;
|
|
152
155
|
stop(): Promise<void>;
|
|
153
156
|
private every;
|
|
157
|
+
/**
|
|
158
|
+
* Puts this version's AI binding in the environment before a handler runs;
|
|
159
|
+
* app code reads OPENAI_BASE_URL and OPENAI_API_KEY itself, so the fetch
|
|
160
|
+
* cannot wait for the first AI call. A binding the gateway refuses (a
|
|
161
|
+
* capability the company has no route for, a version it has not finished)
|
|
162
|
+
* does not stop the handler: the refusal is logged and remembered for
|
|
163
|
+
* aiRetryMs, and only the handler's AI calls fail (ADR 0096).
|
|
164
|
+
*/
|
|
154
165
|
private prepareAI;
|
|
155
166
|
private refreshToken;
|
|
156
167
|
private scheduleTick;
|
package/dist/runtime.js
CHANGED
|
@@ -87,6 +87,7 @@ const MAX_BODY_BYTES = 1 << 20;
|
|
|
87
87
|
const LEASE_MS = 5 * 60_000;
|
|
88
88
|
const QUEUE_WORKERS = 2;
|
|
89
89
|
const QUEUE_POLL_MS = 2_000;
|
|
90
|
+
const AI_RETRY_MS = 30_000;
|
|
90
91
|
const TOKEN_REFRESH_MS = 60_000;
|
|
91
92
|
// A gateway session lasts an hour. An action runs at most 300 s, so a request
|
|
92
93
|
// takes a new one in the session's last 10 minutes; an Event Function
|
|
@@ -318,6 +319,7 @@ export class Runtime {
|
|
|
318
319
|
timers = [];
|
|
319
320
|
stopped = false;
|
|
320
321
|
aiBinding;
|
|
322
|
+
aiRefusedUntil = 0;
|
|
321
323
|
queueWorkers = 0;
|
|
322
324
|
queueWake = 0;
|
|
323
325
|
claimTail = Promise.resolve();
|
|
@@ -510,9 +512,17 @@ export class Runtime {
|
|
|
510
512
|
};
|
|
511
513
|
this.timers.push(setTimeout(loop, ms));
|
|
512
514
|
}
|
|
515
|
+
/**
|
|
516
|
+
* Puts this version's AI binding in the environment before a handler runs;
|
|
517
|
+
* app code reads OPENAI_BASE_URL and OPENAI_API_KEY itself, so the fetch
|
|
518
|
+
* cannot wait for the first AI call. A binding the gateway refuses (a
|
|
519
|
+
* capability the company has no route for, a version it has not finished)
|
|
520
|
+
* does not stop the handler: the refusal is logged and remembered for
|
|
521
|
+
* aiRetryMs, and only the handler's AI calls fail (ADR 0096).
|
|
522
|
+
*/
|
|
513
523
|
async prepareAI() {
|
|
514
524
|
const versionId = process.env.ISOMORPH_AI_VERSION_ID;
|
|
515
|
-
if (!versionId)
|
|
525
|
+
if (!versionId || Date.now() < this.aiRefusedUntil)
|
|
516
526
|
return;
|
|
517
527
|
this.aiBinding ??= (async () => {
|
|
518
528
|
const token = process.env.ISOMORPH_WORKLOAD_TOKEN;
|
|
@@ -520,7 +530,7 @@ export class Runtime {
|
|
|
520
530
|
throw new Error('AI workload identity is unavailable.');
|
|
521
531
|
const response = await fetch(`${this.options.gatewayUrl}/_harbour/internal/ai/connection`, { method: 'POST', headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' }, body: JSON.stringify({ versionId }), signal: AbortSignal.timeout(15_000) });
|
|
522
532
|
if (!response.ok)
|
|
523
|
-
throw new Error(
|
|
533
|
+
throw new Error(`the gateway answered ${response.status} ${(await response.text()).slice(0, 200)}`);
|
|
524
534
|
const result = await response.json();
|
|
525
535
|
const binding = result.data;
|
|
526
536
|
if (!binding?.key || !binding.gatewayBaseUrl || new URL(binding.gatewayBaseUrl).protocol !== 'https:')
|
|
@@ -533,7 +543,11 @@ export class Runtime {
|
|
|
533
543
|
process.env.ISOMORPH_AI_IMAGE_MODEL_ALIAS = binding.modelAliases.image;
|
|
534
544
|
if (binding.modelAliases?.document)
|
|
535
545
|
process.env.ISOMORPH_AI_DOCUMENT_MODEL_ALIAS = binding.modelAliases.document;
|
|
536
|
-
})().catch(error => {
|
|
546
|
+
})().catch((error) => {
|
|
547
|
+
this.aiBinding = undefined;
|
|
548
|
+
this.aiRefusedUntil = Date.now() + (this.options.aiRetryMs ?? AI_RETRY_MS);
|
|
549
|
+
this.log(`AI is not available to this app version (${versionId}): ${error instanceof Error ? error.message : String(error)}; its AI calls fail until the binding is`);
|
|
550
|
+
});
|
|
537
551
|
await this.aiBinding;
|
|
538
552
|
}
|
|
539
553
|
async refreshToken(minValidityMs = SESSION_MIN_VALIDITY_MS) {
|