inferock-bench 0.1.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/dist/server.js ADDED
@@ -0,0 +1,48 @@
1
+ import { serve } from "@hono/node-server";
2
+ import { benchKeyFromConfig, DEFAULT_HOST, DEFAULT_PORT, providerKeyStatus, } from "./config.js";
3
+ import { createBenchApp } from "./proxy.js";
4
+ import { JsonlEventStore } from "./storage.js";
5
+ import { renderLiveCounter, summarizeBenchEvents } from "./summary.js";
6
+ export async function startServer(input) {
7
+ const log = input.log ?? console.log;
8
+ const env = input.env ?? process.env;
9
+ const host = input.host ?? env.INFEROCK_BENCH_HOST ?? DEFAULT_HOST;
10
+ const port = input.port ?? portFromEnv(env) ?? DEFAULT_PORT;
11
+ const store = new JsonlEventStore(input.paths.eventsFile);
12
+ const app = createBenchApp({
13
+ config: input.config,
14
+ paths: input.paths,
15
+ store,
16
+ env,
17
+ log,
18
+ });
19
+ const summary = summarizeBenchEvents(await store.readAll());
20
+ const dashboardUrl = `http://${host}:${port}/`;
21
+ log(`inferock-bench listening at http://${host}:${port}`);
22
+ log(`Dashboard: ${dashboardUrl}`);
23
+ log(`OpenAI SDK baseURL: http://${host}:${port}/v1`);
24
+ log(`Anthropic SDK baseURL: http://${host}:${port}`);
25
+ log(`Local bench key: ${input.config.benchKey ?? benchKeyFromConfig(input.config, env)}`);
26
+ if (env.INFEROCK_BENCH_KEY && input.config.benchKey) {
27
+ log("INFEROCK_BENCH_KEY override is also accepted for proxy requests.");
28
+ }
29
+ log(`Config: ${input.paths.configFile}`);
30
+ if (!providerKeyStatus("openai", input.config, env).configured &&
31
+ !providerKeyStatus("anthropic", input.config, env).configured) {
32
+ log(`Provider setup: open ${dashboardUrl} and save an OpenAI or Anthropic key locally.`);
33
+ }
34
+ log(renderLiveCounter(summary));
35
+ serve({
36
+ fetch: app.fetch,
37
+ hostname: host,
38
+ port,
39
+ });
40
+ }
41
+ function portFromEnv(env) {
42
+ const value = env.INFEROCK_BENCH_PORT;
43
+ if (!value)
44
+ return undefined;
45
+ const parsed = Number(value);
46
+ return Number.isInteger(parsed) && parsed > 0 && parsed <= 65_535 ? parsed : undefined;
47
+ }
48
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/sse.js ADDED
@@ -0,0 +1,48 @@
1
+ // Copied from apps/proxy/src/sse.ts for inferock-bench Track C.
2
+ // Reuse approved by .claude/plans/oss-wave-2026-07.md "Track C Reuse Boundary".
3
+ export class SseAccumulator {
4
+ #buffer = "";
5
+ push(chunk) {
6
+ this.#buffer += chunk;
7
+ const messages = [];
8
+ while (true) {
9
+ const normalized = this.#buffer.replace(/\r\n/g, "\n");
10
+ const boundary = normalized.indexOf("\n\n");
11
+ if (boundary === -1)
12
+ break;
13
+ const frame = normalized.slice(0, boundary);
14
+ this.#buffer = normalized.slice(boundary + 2);
15
+ const message = parseFrame(frame);
16
+ if (message)
17
+ messages.push(message);
18
+ }
19
+ return messages;
20
+ }
21
+ end() {
22
+ const frame = this.#buffer.trimEnd();
23
+ this.#buffer = "";
24
+ const message = parseFrame(frame);
25
+ return message ? [message] : [];
26
+ }
27
+ }
28
+ function parseFrame(frame) {
29
+ if (frame.length === 0)
30
+ return undefined;
31
+ const data = [];
32
+ let event;
33
+ for (const line of frame.split("\n")) {
34
+ if (line.startsWith(":"))
35
+ continue;
36
+ if (line.startsWith("event:")) {
37
+ event = line.slice("event:".length).trimStart();
38
+ continue;
39
+ }
40
+ if (line.startsWith("data:")) {
41
+ data.push(line.slice("data:".length).trimStart());
42
+ }
43
+ }
44
+ if (data.length === 0)
45
+ return undefined;
46
+ return { ...(event ? { event } : {}), data: data.join("\n") };
47
+ }
48
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,gFAAgF;AAOhF,MAAM,OAAO,cAAc;IACzB,OAAO,GAAG,EAAE,CAAC;IAEb,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;QACtB,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAElC,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACvD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,QAAQ,KAAK,CAAC,CAAC;gBAAE,MAAM;YAE3B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,OAAO;gBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,GAAG;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClC,CAAC;CACF;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEzC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,KAAyB,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACnC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC;YAChD,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAChE,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { CanonicalEventAny, } from "@inferock/measure/canonical-event";
4
+ import { isRecord, stringValue } from "./record.js";
5
+ export class JsonlEventStore {
6
+ filePath;
7
+ constructor(filePath) {
8
+ this.filePath = filePath;
9
+ }
10
+ async append(record) {
11
+ await mkdir(dirname(this.filePath), { recursive: true });
12
+ await writeFile(this.filePath, `${JSON.stringify(record)}\n`, {
13
+ encoding: "utf8",
14
+ flag: "a",
15
+ });
16
+ }
17
+ async readAll() {
18
+ let raw;
19
+ try {
20
+ raw = await readFile(this.filePath, "utf8");
21
+ }
22
+ catch (error) {
23
+ if (isNodeError(error) && error.code === "ENOENT")
24
+ return [];
25
+ throw error;
26
+ }
27
+ const records = [];
28
+ for (const line of raw.split("\n")) {
29
+ const trimmed = line.trim();
30
+ if (!trimmed)
31
+ continue;
32
+ const parsed = parseStoredBenchEvent(trimmed);
33
+ if (parsed)
34
+ records.push(parsed);
35
+ }
36
+ return records;
37
+ }
38
+ }
39
+ export function createStoredBenchEvent(event) {
40
+ return {
41
+ schemaVersion: "inferock-bench-event-v1",
42
+ capturedAt: new Date().toISOString(),
43
+ event,
44
+ };
45
+ }
46
+ function parseStoredBenchEvent(line) {
47
+ let parsed;
48
+ try {
49
+ parsed = JSON.parse(line);
50
+ }
51
+ catch {
52
+ return undefined;
53
+ }
54
+ if (!isRecord(parsed) || parsed.schemaVersion !== "inferock-bench-event-v1")
55
+ return undefined;
56
+ const capturedAt = stringValue(parsed.capturedAt);
57
+ if (!capturedAt)
58
+ return undefined;
59
+ const event = CanonicalEventAny.safeParse(parsed.event);
60
+ if (!event.success)
61
+ return undefined;
62
+ return {
63
+ schemaVersion: "inferock-bench-event-v1",
64
+ capturedAt,
65
+ event: event.data,
66
+ };
67
+ }
68
+ function isNodeError(error) {
69
+ return error instanceof Error && "code" in error;
70
+ }
71
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,iBAAiB,GAElB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAapD,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,QAAgB;QAAhB,aAAQ,GAAR,QAAQ,CAAQ;IAAG,CAAC;IAEjD,KAAK,CAAC,MAAM,CAAC,MAAwB;QACnC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE;YAC5D,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,GAAG;SACV,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,EAAE,CAAC;YAC7D,MAAM,KAAK,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAuB,EAAE,CAAC;QACvC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAI,MAAM;gBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED,MAAM,UAAU,sBAAsB,CAAC,KAA4B;IACjE,OAAO;QACL,aAAa,EAAE,yBAAyB;QACxC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,KAAK;KACN,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY;IACzC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,aAAa,KAAK,yBAAyB;QAAE,OAAO,SAAS,CAAC;IAC9F,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,KAAK,GAAG,iBAAiB,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACrC,OAAO;QACL,aAAa,EAAE,yBAAyB;QACxC,UAAU;QACV,KAAK,EAAE,KAAK,CAAC,IAAI;KAClB,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC;AACnD,CAAC"}
@@ -0,0 +1,112 @@
1
+ import { normalizeCanonicalEvent, } from "@inferock/measure/canonical-event";
2
+ import { estimateCostUsd, runStatelessDetectors } from "@inferock/measure/stateless";
3
+ export function summarizeBenchEvents(records, window = {}) {
4
+ const events = records
5
+ .filter((record) => withinWindow(record.event, window))
6
+ .map((record) => normalizeCanonicalEvent(record.event));
7
+ const eventsWithSignals = events.map((event) => ({
8
+ event,
9
+ signals: runStatelessDetectors(event),
10
+ }));
11
+ const signals = eventsWithSignals.flatMap((entry) => entry.signals);
12
+ const rows = reportRows(signals);
13
+ const providerRecognizedUsd = roundUsd(sum(rows.map((row) => row.providerRecognizedUsd)));
14
+ const unrecognizedUsd = roundUsd(sum(rows.map((row) => row.unrecognizedUsd)));
15
+ return {
16
+ period: {
17
+ since: window.since ? window.since.toISOString() : null,
18
+ until: (window.until ?? new Date()).toISOString(),
19
+ },
20
+ measuredCalls: events.length,
21
+ failureCount: signals.filter((signal) => signal.severity === "loss").length,
22
+ providerSpendUsd: roundUsd(sum(events.map((event) => estimateCostUsd(event)))),
23
+ providerRecognizedUsd,
24
+ unrecognizedUsd,
25
+ totalLostUsd: roundUsd(providerRecognizedUsd + unrecognizedUsd),
26
+ pricingUnknownCount: signals.filter((signal) => signal.code === "PRICING_UNKNOWN").length,
27
+ rows,
28
+ };
29
+ }
30
+ export function renderLiveCounter(summary) {
31
+ const failures = summary.failureCount === 1 ? "1 failure" : `${summary.failureCount} failures`;
32
+ return [
33
+ `$ lost so far: ${formatUsd(summary.totalLostUsd)}`,
34
+ `measured ${summary.measuredCalls} calls, ${failures}`,
35
+ `provider-recognized ${formatUsd(summary.providerRecognizedUsd)}, unrecognized ${formatUsd(summary.unrecognizedUsd)}`,
36
+ ].join(" | ");
37
+ }
38
+ export function renderReport(summary) {
39
+ const lines = [
40
+ renderLiveCounter(summary),
41
+ `provider spend observed: ${formatUsd(summary.providerSpendUsd)}`,
42
+ ];
43
+ if (summary.rows.length === 0) {
44
+ lines.push(`No loss rows. measured ${summary.measuredCalls} calls, 0 failures.`);
45
+ return lines.join("\n");
46
+ }
47
+ lines.push("class | evidence | count | provider-recognized | unrecognized");
48
+ for (const row of summary.rows) {
49
+ lines.push([
50
+ `${row.code}/${row.failureClass}`,
51
+ row.evidenceGrade,
52
+ String(row.count),
53
+ formatUsd(row.providerRecognizedUsd),
54
+ formatUsd(row.unrecognizedUsd),
55
+ ].join(" | "));
56
+ }
57
+ return lines.join("\n");
58
+ }
59
+ export function formatUsd(value) {
60
+ return new Intl.NumberFormat("en-US", {
61
+ style: "currency",
62
+ currency: "USD",
63
+ minimumFractionDigits: 2,
64
+ maximumFractionDigits: 2,
65
+ }).format(value);
66
+ }
67
+ function reportRows(signals) {
68
+ const rows = new Map();
69
+ for (const signal of signals) {
70
+ const key = `${signal.code}\u0000${signal.failureClass}\u0000${signal.evidenceGrade}`;
71
+ const existing = rows.get(key);
72
+ const providerRecognized = signal.providerRecoverableLossUsd ?? 0;
73
+ const unrecognized = unrecognizedUsdForSignal(signal, providerRecognized);
74
+ rows.set(key, {
75
+ code: signal.code,
76
+ failureClass: signal.failureClass,
77
+ evidenceGrade: signal.evidenceGrade,
78
+ count: (existing?.count ?? 0) + 1,
79
+ providerRecognizedUsd: roundUsd((existing?.providerRecognizedUsd ?? 0) + providerRecognized),
80
+ unrecognizedUsd: roundUsd((existing?.unrecognizedUsd ?? 0) + unrecognized),
81
+ });
82
+ }
83
+ return [...rows.values()].sort(compareRows);
84
+ }
85
+ function unrecognizedUsdForSignal(signal, providerRecognizedUsd) {
86
+ if (signal.valueKind !== "money")
87
+ return 0;
88
+ return Math.max(0, signal.costUsd - providerRecognizedUsd);
89
+ }
90
+ function withinWindow(event, window) {
91
+ const normalized = normalizeCanonicalEvent(event);
92
+ const startedAt = new Date(normalized.timing.startedAt).getTime();
93
+ if (window.since && startedAt < window.since.getTime())
94
+ return false;
95
+ if (window.until && startedAt > window.until.getTime())
96
+ return false;
97
+ return true;
98
+ }
99
+ function compareRows(left, right) {
100
+ const leftTotal = left.providerRecognizedUsd + left.unrecognizedUsd;
101
+ const rightTotal = right.providerRecognizedUsd + right.unrecognizedUsd;
102
+ if (leftTotal !== rightTotal)
103
+ return rightTotal - leftTotal;
104
+ return left.code.localeCompare(right.code);
105
+ }
106
+ function sum(values) {
107
+ return values.reduce((total, value) => total + value, 0);
108
+ }
109
+ function roundUsd(value) {
110
+ return Math.round(value * 1_000_000) / 1_000_000;
111
+ }
112
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { reliabilityEndpoint } from "./config.js";
2
+ export function buildReliabilityIndexPayload(summary) {
3
+ return {
4
+ schemaVersion: "inferock-bench-reliability-index-v1",
5
+ generatedAt: new Date().toISOString(),
6
+ period: summary.period,
7
+ measuredCalls: summary.measuredCalls,
8
+ failureCounts: summary.rows.map((row) => ({
9
+ failureClass: row.failureClass,
10
+ evidenceGrade: row.evidenceGrade,
11
+ count: row.count,
12
+ })),
13
+ };
14
+ }
15
+ export async function sendReliabilityIndexPayload(input) {
16
+ const payload = buildReliabilityIndexPayload(input.summary);
17
+ if (input.config.reliabilityIndex?.enabled !== true) {
18
+ return {
19
+ sent: false,
20
+ message: "reliability index is off",
21
+ payload,
22
+ };
23
+ }
24
+ const endpoint = reliabilityEndpoint(input.config, input.env);
25
+ if (!endpoint) {
26
+ return {
27
+ sent: false,
28
+ message: "index endpoint not yet live; payload assembled locally only",
29
+ payload,
30
+ };
31
+ }
32
+ return {
33
+ sent: false,
34
+ message: "index endpoint configured, but sender is disabled until the reliability index is live",
35
+ payload,
36
+ };
37
+ }
38
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAuBlD,MAAM,UAAU,4BAA4B,CAAC,OAAqB;IAChE,OAAO;QACL,aAAa,EAAE,qCAAqC;QACpD,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACxC,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,aAAa,EAAE,GAAG,CAAC,aAAa;YAChC,KAAK,EAAE,GAAG,CAAC,KAAK;SACjB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAIjD;IACC,MAAM,OAAO,GAAG,4BAA4B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,MAAM,CAAC,gBAAgB,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,0BAA0B;YACnC,OAAO;SACR,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,6DAA6D;YACtE,OAAO;SACR,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,uFAAuF;QAChG,OAAO;KACR,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "inferock-bench",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "license": "FSL-1.1-Apache-2.0",
6
+ "type": "module",
7
+ "bin": {
8
+ "inferock-bench": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "LICENSE",
13
+ "README.md",
14
+ "package.json"
15
+ ],
16
+ "main": "dist/index.js",
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsx watch src/index.ts",
20
+ "start": "node dist/index.js",
21
+ "test": "vitest run",
22
+ "test:integration": "vitest run --config vitest.integration.config.ts",
23
+ "lint": "eslint src/ --max-warnings 0",
24
+ "typecheck": "tsc --noEmit",
25
+ "clean": "rm -rf dist coverage"
26
+ },
27
+ "dependencies": {
28
+ "@hono/node-server": "^1.14.0",
29
+ "@inferock/measure": "^0.1.0",
30
+ "hono": "^4.7.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22",
34
+ "tsx": "^4.19.0",
35
+ "typescript": "^5.7.0",
36
+ "vitest": "^3.0.0"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }