@volter/twin-standard 1.0.82 → 1.0.84
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/src/clients/openai.js +3 -2
- package/dist/src/conformance.js +6 -13
- package/dist/src/walk/behavior-journey.js +9 -5
- package/dist/src/walk/serve-pack.d.ts +7 -0
- package/dist/src/walk/serve-pack.js +39 -0
- package/package.json +2 -2
- package/src/clients/openai.ts +3 -2
- package/src/conformance.ts +6 -14
- package/src/walk/behavior-journey.ts +7 -4
- package/src/walk/serve-pack.ts +31 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { chatCases } from "./openai-compatible.js";
|
|
2
2
|
const CHAT = 'https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create';
|
|
3
3
|
const REALTIME = 'https://developers.openai.com/api/reference/resources/realtime/server-events';
|
|
4
|
+
const API = 'https://api.openai.com/v1';
|
|
4
5
|
async function client(c) {
|
|
5
6
|
const { default: OpenAI } = await import('openai');
|
|
6
|
-
return new OpenAI({ apiKey: c.env.OPENAI_API_KEY ?? '', baseURL:
|
|
7
|
+
return new OpenAI({ apiKey: c.env.OPENAI_API_KEY ?? '', baseURL: API, fetch: c.fetch, maxRetries: 0, timeout: 10_000 });
|
|
7
8
|
}
|
|
8
9
|
const TOOLS = [{ type: 'function', function: { name: 'lookup', parameters: { type: 'object', properties: {} } } }];
|
|
9
10
|
// a turn that calls the tool wherever one is offered, and one that answers in words
|
|
@@ -37,7 +38,7 @@ async function realtimeTurn(c, toolChoice) {
|
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
export const cases = [
|
|
40
|
-
...chatCases({ key: 'OPENAI_API_KEY', base: (
|
|
41
|
+
...chatCases({ key: 'OPENAI_API_KEY', base: () => API, model: 'gpt-4o' }),
|
|
41
42
|
{
|
|
42
43
|
id: 'realtime-connects',
|
|
43
44
|
asks: 'a Realtime connection opens and its first event is session.created',
|
package/dist/src/conformance.js
CHANGED
|
@@ -27,6 +27,7 @@ import { basename, dirname, extname, join, matchesGlob, relative, resolve } from
|
|
|
27
27
|
import { citations, sourceHeld } from "./check-sources.js";
|
|
28
28
|
import { fileURLToPath } from 'node:url';
|
|
29
29
|
import { servedHttpFetch } from "./clients/harness.js";
|
|
30
|
+
import { servePack as serve } from "./walk/serve-pack.js";
|
|
30
31
|
const pascal = (s) => s.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join('');
|
|
31
32
|
const readJson = (p) => (existsSync(p) ? JSON.parse(readFileSync(p, 'utf8')) : undefined);
|
|
32
33
|
/** The pack's units: the pack and each lane in it (a directory with its own src/manifest.ts and no package.json). */
|
|
@@ -268,15 +269,6 @@ async function allowance(pack, runtime) {
|
|
|
268
269
|
}
|
|
269
270
|
return out;
|
|
270
271
|
}
|
|
271
|
-
/** The pack served on its own, as a World's process or an application's test serves it: its server.ts, from a scratch
|
|
272
|
-
* working directory (a server keeps its World under it), with the options given. */
|
|
273
|
-
async function serve(pack, options) {
|
|
274
|
-
const mod = (await import(__rewriteRelativeImportExtension(join(pack, 'src', 'server.ts'))));
|
|
275
|
-
const create = Object.entries(mod).find(([k, v]) => /^create\w+TwinServer$/.test(k) && typeof v === 'function')?.[1];
|
|
276
|
-
if (!create)
|
|
277
|
-
throw new Error('src/server.ts exports no create<Vendor>TwinServer');
|
|
278
|
-
return create({ port: 0, ...options });
|
|
279
|
-
}
|
|
280
272
|
const within = (ms, p) => Promise.race([p, new Promise((r) => setTimeout(() => r('timeout'), ms))]);
|
|
281
273
|
/** Served by its server.ts as a World runs it (its boot id set), the pack answers HTTP and the boot probe, and every socket
|
|
282
274
|
* its manifests declare takes an upgrade. Escaped twice: serveHttp never took the upgrade a pack's fetch carries, then
|
|
@@ -321,7 +313,7 @@ async function standalone(pack) {
|
|
|
321
313
|
}
|
|
322
314
|
}
|
|
323
315
|
finally {
|
|
324
|
-
server.stop();
|
|
316
|
+
await server.stop();
|
|
325
317
|
}
|
|
326
318
|
return out;
|
|
327
319
|
}
|
|
@@ -383,7 +375,8 @@ async function lifeServed(pack) {
|
|
|
383
375
|
const http = servedHttpFetch(port);
|
|
384
376
|
return {
|
|
385
377
|
// the vendor's URL as the application called it, sent to the served pack: its host carried in the Host header
|
|
386
|
-
fetch: async (request) => {
|
|
378
|
+
fetch: Object.assign(async (request) => {
|
|
379
|
+
await server.managedDatabaseUrl?.();
|
|
387
380
|
const abort = new AbortController();
|
|
388
381
|
try {
|
|
389
382
|
const answer = await within(10_000, http(request, { signal: abort.signal }));
|
|
@@ -392,7 +385,7 @@ async function lifeServed(pack) {
|
|
|
392
385
|
finally {
|
|
393
386
|
abort.abort();
|
|
394
387
|
}
|
|
395
|
-
},
|
|
388
|
+
}, server.managedDatabaseUrl ? { managedDatabaseUrl: server.managedDatabaseUrl } : {}),
|
|
396
389
|
// a socket opened as an application opens it: a WebSocket to the served port, the vendor's host in its Host header
|
|
397
390
|
socket: (url, headers) => new WebSocket(`ws://127.0.0.1:${port}${url}`, { headers }),
|
|
398
391
|
// a line connection opened as an application's client opens it: TCP to the protocol listener (the primary port)
|
|
@@ -477,7 +470,7 @@ async function clientCases(pack, scratch) {
|
|
|
477
470
|
return { failures: results.flatMap((r) => r.failures.map((f) => `${r.id} (${r.asks}): ${f}`)), note: `${results.length} case(s) through the vendor's client` };
|
|
478
471
|
}
|
|
479
472
|
finally {
|
|
480
|
-
server.stop();
|
|
473
|
+
await server.stop();
|
|
481
474
|
}
|
|
482
475
|
}
|
|
483
476
|
/** Every check over the pack at `dir`, in order. */
|
|
@@ -1174,11 +1174,15 @@ export async function walk(journey, root, collect, opts) {
|
|
|
1174
1174
|
await Promise.allSettled([...pending.values()].map((request) => request.answer));
|
|
1175
1175
|
setDeliveryObserver(previousObserver);
|
|
1176
1176
|
setInProcessTwins(previousTwins);
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1177
|
+
try {
|
|
1178
|
+
if (sqlSession)
|
|
1179
|
+
await sqlSession.then((session) => session.end(), () => { });
|
|
1180
|
+
}
|
|
1181
|
+
finally {
|
|
1182
|
+
await served?.stop();
|
|
1183
|
+
// a World's managed database ends with its walk (vendor-fetch.ts), so the next World declares it at the same port
|
|
1184
|
+
await twin.stopDatabase?.();
|
|
1185
|
+
}
|
|
1182
1186
|
if (clockFile && startedAt !== undefined)
|
|
1183
1187
|
store.write(clockFile, startedAt);
|
|
1184
1188
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
// The standard serves a pack with its declared backing, as the runtime binds it in a World.
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { worldNow } from '@volter/world-core/runtime';
|
|
12
|
+
import { startPgliteHost } from "./walk-database.js";
|
|
13
|
+
export async function servePack(pack, options) {
|
|
14
|
+
const mod = await import(__rewriteRelativeImportExtension(join(pack, 'src', 'server.ts')));
|
|
15
|
+
const create = Object.entries(mod).find(([name, value]) => /^create\w+TwinServer$/.test(name) && typeof value === 'function')?.[1];
|
|
16
|
+
if (!create)
|
|
17
|
+
throw new Error('src/server.ts exports no create<Vendor>TwinServer');
|
|
18
|
+
const descriptor = (await import(__rewriteRelativeImportExtension(join(pack, 'src', 'index.ts')))).pack;
|
|
19
|
+
let database;
|
|
20
|
+
if (descriptor?.managedDatabase?.kind === 'postgres' && options.database === undefined)
|
|
21
|
+
database = await startPgliteHost();
|
|
22
|
+
let server;
|
|
23
|
+
try {
|
|
24
|
+
server = await create({ port: 0, ...options, ...(database ? { database: database.url } : {}) });
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
await database?.stop();
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
if (!database)
|
|
31
|
+
return server;
|
|
32
|
+
const backing = database;
|
|
33
|
+
let stopping;
|
|
34
|
+
return {
|
|
35
|
+
...server,
|
|
36
|
+
managedDatabaseUrl: async () => { backing.clock(worldNow()); return backing.url; },
|
|
37
|
+
stop: () => stopping ??= (async () => { await server.stop(); await backing.stop(); })(),
|
|
38
|
+
};
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-standard",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.84",
|
|
4
4
|
"description": "The Protocol 3 standard for Volter twin packs: the criteria a pack is graded by, the grader, the gate a pack repository and the twin catalog run on every change and submission, and the spec derivation the grade checks against. Pin a version: every place a pack is judged judges by that standard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"yaml": "2.9.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@volter/world-core": "3.0.
|
|
78
|
+
"@volter/world-core": "3.0.92",
|
|
79
79
|
"@volter/world-runtime": "*",
|
|
80
80
|
"playwright": "^1.63.0"
|
|
81
81
|
},
|
package/src/clients/openai.ts
CHANGED
|
@@ -5,10 +5,11 @@ import { chatCases } from './openai-compatible.ts';
|
|
|
5
5
|
|
|
6
6
|
const CHAT = 'https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create';
|
|
7
7
|
const REALTIME = 'https://developers.openai.com/api/reference/resources/realtime/server-events';
|
|
8
|
+
const API = 'https://api.openai.com/v1';
|
|
8
9
|
|
|
9
10
|
async function client(c: ClientContext) {
|
|
10
11
|
const { default: OpenAI } = await import('openai');
|
|
11
|
-
return new OpenAI({ apiKey: c.env.OPENAI_API_KEY ?? '', baseURL:
|
|
12
|
+
return new OpenAI({ apiKey: c.env.OPENAI_API_KEY ?? '', baseURL: API, fetch: c.fetch, maxRetries: 0, timeout: 10_000 });
|
|
12
13
|
}
|
|
13
14
|
const TOOLS = [{ type: 'function' as const, function: { name: 'lookup', parameters: { type: 'object', properties: {} } } }];
|
|
14
15
|
// a turn that calls the tool wherever one is offered, and one that answers in words
|
|
@@ -40,7 +41,7 @@ async function realtimeTurn(c: ClientContext, toolChoice: 'auto' | 'none'): Prom
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export const cases: ClientCase[] = [
|
|
43
|
-
...chatCases({ key: 'OPENAI_API_KEY', base: (
|
|
44
|
+
...chatCases({ key: 'OPENAI_API_KEY', base: () => API, model: 'gpt-4o' }),
|
|
44
45
|
{
|
|
45
46
|
id: 'realtime-connects',
|
|
46
47
|
asks: 'a Realtime connection opens and its first event is session.created',
|
package/src/conformance.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type { TwinStream } from '@volter/world-core';
|
|
|
20
20
|
import { citations, sourceHeld, type SourceRecord } from './check-sources.ts';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
import { servedHttpFetch } from './clients/harness.ts';
|
|
23
|
+
import { servePack as serve, type ServedPack as Served } from './walk/serve-pack.ts';
|
|
23
24
|
|
|
24
25
|
export type ConformanceResult = { id: string; asks: string; failures: string[]; note?: string };
|
|
25
26
|
|
|
@@ -247,16 +248,6 @@ type Runtime = {
|
|
|
247
248
|
getPack: (vendor: string) => { stateSystem?: { perform?: unknown; refresh?: unknown } } | undefined;
|
|
248
249
|
DEFAULT_RATE_BUDGET: NonNullable<Manifest['rateBudget']>;
|
|
249
250
|
};
|
|
250
|
-
type Served = { port: number; inspectPort?: number; stop: () => void };
|
|
251
|
-
|
|
252
|
-
/** The pack served on its own, as a World's process or an application's test serves it: its server.ts, from a scratch
|
|
253
|
-
* working directory (a server keeps its World under it), with the options given. */
|
|
254
|
-
async function serve(pack: string, options: Record<string, unknown>): Promise<Served> {
|
|
255
|
-
const mod = (await import(join(pack, 'src', 'server.ts'))) as Record<string, unknown>;
|
|
256
|
-
const create = Object.entries(mod).find(([k, v]) => /^create\w+TwinServer$/.test(k) && typeof v === 'function')?.[1] as ((o: Record<string, unknown>) => Promise<Served>) | undefined;
|
|
257
|
-
if (!create) throw new Error('src/server.ts exports no create<Vendor>TwinServer');
|
|
258
|
-
return create({ port: 0, ...options });
|
|
259
|
-
}
|
|
260
251
|
const within = <T>(ms: number, p: Promise<T>): Promise<T | 'timeout'> => Promise.race([p, new Promise<'timeout'>((r) => setTimeout(() => r('timeout'), ms))]);
|
|
261
252
|
|
|
262
253
|
/** Served by its server.ts as a World runs it (its boot id set), the pack answers HTTP and the boot probe, and every socket
|
|
@@ -287,7 +278,7 @@ async function standalone(pack: string): Promise<string[]> {
|
|
|
287
278
|
});
|
|
288
279
|
if (upgraded) out.push(`${s.unit}: the socket at ${s.host ?? ''}${s.path}: ${upgraded}`);
|
|
289
280
|
}
|
|
290
|
-
} finally { server.stop(); }
|
|
281
|
+
} finally { await server.stop(); }
|
|
291
282
|
return out;
|
|
292
283
|
}
|
|
293
284
|
|
|
@@ -338,13 +329,14 @@ async function lifeServed(pack: string): Promise<string[]> {
|
|
|
338
329
|
const http = servedHttpFetch(port);
|
|
339
330
|
return {
|
|
340
331
|
// the vendor's URL as the application called it, sent to the served pack: its host carried in the Host header
|
|
341
|
-
fetch: async (request: Request): Promise<Response> => {
|
|
332
|
+
fetch: Object.assign(async (request: Request): Promise<Response> => {
|
|
333
|
+
await server.managedDatabaseUrl?.();
|
|
342
334
|
const abort = new AbortController();
|
|
343
335
|
try {
|
|
344
336
|
const answer = await within(10_000, http(request, { signal: abort.signal }));
|
|
345
337
|
return answer === 'timeout' ? new Response(`no answer in 10 s from the served pack (${request.method} ${new URL(request.url).pathname})`, { status: 599 }) : answer;
|
|
346
338
|
} finally { abort.abort(); }
|
|
347
|
-
},
|
|
339
|
+
}, server.managedDatabaseUrl ? { managedDatabaseUrl: server.managedDatabaseUrl } : {}),
|
|
348
340
|
// a socket opened as an application opens it: a WebSocket to the served port, the vendor's host in its Host header
|
|
349
341
|
socket: (url: string, headers: Record<string, string>) => new WebSocket(`ws://127.0.0.1:${port}${url}`, { headers } as unknown as string[]),
|
|
350
342
|
// a line connection opened as an application's client opens it: TCP to the protocol listener (the primary port)
|
|
@@ -413,7 +405,7 @@ async function clientCases(pack: string, scratch: string): Promise<{ failures: s
|
|
|
413
405
|
});
|
|
414
406
|
const results = out.split('\n').filter((l) => l.startsWith('{')).map((l) => JSON.parse(l) as { id: string; asks: string; failures: string[] });
|
|
415
407
|
return { failures: results.flatMap((r) => r.failures.map((f) => `${r.id} (${r.asks}): ${f}`)), note: `${results.length} case(s) through the vendor's client` };
|
|
416
|
-
} finally { server.stop(); }
|
|
408
|
+
} finally { await server.stop(); }
|
|
417
409
|
}
|
|
418
410
|
|
|
419
411
|
/** Every check over the pack at `dir`, in order. */
|
|
@@ -819,10 +819,13 @@ export async function walk(journey: Journey, root: string, collect: Failure[], o
|
|
|
819
819
|
await Promise.allSettled([...pending.values()].map((request) => request.answer));
|
|
820
820
|
setDeliveryObserver(previousObserver);
|
|
821
821
|
setInProcessTwins(previousTwins);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
822
|
+
try {
|
|
823
|
+
if (sqlSession) await sqlSession.then((session) => session.end(), () => {});
|
|
824
|
+
} finally {
|
|
825
|
+
await served?.stop();
|
|
826
|
+
// a World's managed database ends with its walk (vendor-fetch.ts), so the next World declares it at the same port
|
|
827
|
+
await (twin as { stopDatabase?: () => Promise<void> }).stopDatabase?.();
|
|
828
|
+
}
|
|
826
829
|
if (clockFile && startedAt !== undefined) store.write(clockFile, startedAt);
|
|
827
830
|
}
|
|
828
831
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The standard serves a pack with its declared backing, as the runtime binds it in a World.
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { worldNow } from '@volter/world-core/runtime';
|
|
4
|
+
import { startPgliteHost, type PgliteHost } from './walk-database.ts';
|
|
5
|
+
|
|
6
|
+
export type ServedPack = {
|
|
7
|
+
port: number;
|
|
8
|
+
inspectPort?: number;
|
|
9
|
+
stop: () => void | Promise<void>;
|
|
10
|
+
managedDatabaseUrl?: () => Promise<string>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export async function servePack(pack: string, options: Record<string, unknown>): Promise<ServedPack> {
|
|
14
|
+
const mod = await import(join(pack, 'src', 'server.ts')) as Record<string, unknown>;
|
|
15
|
+
const create = Object.entries(mod).find(([name, value]) => /^create\w+TwinServer$/.test(name) && typeof value === 'function')?.[1] as ((options: Record<string, unknown>) => Promise<ServedPack>) | undefined;
|
|
16
|
+
if (!create) throw new Error('src/server.ts exports no create<Vendor>TwinServer');
|
|
17
|
+
const descriptor = (await import(join(pack, 'src', 'index.ts'))).pack as { managedDatabase?: { kind: string } } | undefined;
|
|
18
|
+
let database: PgliteHost | undefined;
|
|
19
|
+
if (descriptor?.managedDatabase?.kind === 'postgres' && options.database === undefined) database = await startPgliteHost();
|
|
20
|
+
let server: ServedPack;
|
|
21
|
+
try { server = await create({ port: 0, ...options, ...(database ? { database: database.url } : {}) }); }
|
|
22
|
+
catch (error) { await database?.stop(); throw error; }
|
|
23
|
+
if (!database) return server;
|
|
24
|
+
const backing = database;
|
|
25
|
+
let stopping: Promise<void> | undefined;
|
|
26
|
+
return {
|
|
27
|
+
...server,
|
|
28
|
+
managedDatabaseUrl: async () => { backing.clock(worldNow()); return backing.url; },
|
|
29
|
+
stop: () => stopping ??= (async () => { await server.stop(); await backing.stop(); })(),
|
|
30
|
+
};
|
|
31
|
+
}
|