@dbx-tools/cli-model-proxy 0.3.37 → 0.3.40

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "repository": {
4
4
  "type": "git",
5
5
  "url": "git+https://github.com/reggie-db/dbx-tools.git",
6
- "directory": "workspaces/cli/model-proxy"
6
+ "directory": "packages/cli/model-proxy"
7
7
  },
8
8
  "bin": {
9
9
  "dbx-tools-model-proxy": "./bin/dbx-tools-model-proxy.mjs",
@@ -19,16 +19,16 @@
19
19
  "commander": "^15.0.0",
20
20
  "tsx": "^4.23.0",
21
21
  "undici": "^7.17.0",
22
- "@dbx-tools/model": "0.3.37",
23
- "@dbx-tools/shared-model": "0.3.37",
24
- "@dbx-tools/shared-core": "0.3.37"
22
+ "@dbx-tools/shared-core": "0.3.40",
23
+ "@dbx-tools/model": "0.3.40",
24
+ "@dbx-tools/shared-model": "0.3.40"
25
25
  },
26
26
  "main": "index.ts",
27
27
  "license": "UNLICENSED",
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  },
31
- "version": "0.3.37",
31
+ "version": "0.3.40",
32
32
  "types": "index.ts",
33
33
  "type": "module",
34
34
  "exports": {
@@ -39,6 +39,11 @@
39
39
  "./server": "./src/server.ts",
40
40
  "./package.json": "./package.json"
41
41
  },
42
+ "files": [
43
+ "index.ts",
44
+ "src",
45
+ "bin"
46
+ ],
42
47
  "dbxToolsConfig": {
43
48
  "tags": [
44
49
  "cli"
@@ -1,173 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer, type Server } from "node:http";
3
- import { describe, it } from "node:test";
4
-
5
- import type { DatabricksBackend } from "../src/backend";
6
- import { DEFAULT_RETRY, resolveRetryConfig } from "../src/defaults";
7
- import { backoffDelayMs, createProxyServer, parseRetryAfterMs } from "../src/server";
8
-
9
- /** Listen on an ephemeral port and resolve the bound port. */
10
- async function listen(server: Server): Promise<number> {
11
- await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", () => resolve()));
12
- const address = server.address();
13
- assert.ok(typeof address === "object" && address);
14
- return address.port;
15
- }
16
-
17
- describe("createProxyServer timeouts", () => {
18
- it("disables every inbound timeout that could cut a stream short", () => {
19
- const server = createProxyServer({} as DatabricksBackend);
20
- // Node's defaults (300s request, 60s headers) are sized for ordinary web
21
- // traffic and would kill a long model turn mid-stream.
22
- assert.equal(server.requestTimeout, 0);
23
- assert.equal(server.headersTimeout, 0);
24
- assert.equal(server.timeout, 0);
25
- server.close();
26
- });
27
-
28
- it("differs from a stock node server, so the override is doing the work", () => {
29
- const stock = createServer();
30
- assert.notEqual(stock.requestTimeout, 0);
31
- assert.notEqual(stock.headersTimeout, 0);
32
- stock.close();
33
- });
34
- });
35
-
36
- describe("upstream dispatcher", () => {
37
- /**
38
- * Serve one chunk, stall past the supplied body timeout, then finish. Proves
39
- * `bodyTimeout` measures the gap BETWEEN chunks - the failure mode that made
40
- * long-thinking turns look like dropped streams.
41
- */
42
- async function readThroughStall(bodyTimeout: number): Promise<string> {
43
- const upstream = createServer((_req, res) => {
44
- res.writeHead(200, { "content-type": "text/event-stream" });
45
- res.write("data: first\n\n");
46
- setTimeout(() => {
47
- res.write("data: second\n\n");
48
- res.end();
49
- }, 1_000);
50
- });
51
- const port = await listen(upstream);
52
- try {
53
- const { Agent } = await import("undici");
54
- const init: RequestInit = { method: "GET" };
55
- Reflect.set(init, "dispatcher", new Agent({ bodyTimeout, headersTimeout: 0 }));
56
- const response = await fetch(`http://127.0.0.1:${port}/`, init);
57
- let body = "";
58
- for await (const chunk of response.body as AsyncIterable<Uint8Array>) {
59
- body += Buffer.from(chunk).toString();
60
- }
61
- return body;
62
- } finally {
63
- upstream.close();
64
- }
65
- }
66
-
67
- it("aborts a stalled stream when bodyTimeout is set", async () => {
68
- await assert.rejects(() => readThroughStall(100));
69
- });
70
-
71
- it("survives the same stall with bodyTimeout disabled, as the proxy configures it", async () => {
72
- assert.equal(await readThroughStall(0), "data: first\n\ndata: second\n\n");
73
- });
74
- });
75
-
76
- describe("parseRetryAfterMs", () => {
77
- const now = Date.UTC(2026, 0, 1, 0, 0, 0);
78
-
79
- it("reads an integer count of seconds", () => {
80
- assert.equal(parseRetryAfterMs("2", now), 2000);
81
- assert.equal(parseRetryAfterMs(" 30 ", now), 30_000);
82
- });
83
-
84
- it("reads an HTTP date as ms-until, clamped at zero", () => {
85
- const future = new Date(now + 5000).toUTCString();
86
- assert.equal(parseRetryAfterMs(future, now), 5000);
87
- const past = new Date(now - 5000).toUTCString();
88
- assert.equal(parseRetryAfterMs(past, now), 0);
89
- });
90
-
91
- it("returns undefined for a missing or unparseable header", () => {
92
- assert.equal(parseRetryAfterMs(null, now), undefined);
93
- assert.equal(parseRetryAfterMs("soon", now), undefined);
94
- });
95
- });
96
-
97
- describe("backoffDelayMs", () => {
98
- const retry = { enabled: true, maxRetries: 5, baseDelayMs: 500, maxDelayMs: 30_000 };
99
-
100
- it("grows exponentially from the base with jitter=0", () => {
101
- assert.equal(backoffDelayMs(0, retry, undefined, 0), 500);
102
- assert.equal(backoffDelayMs(1, retry, undefined, 0), 1000);
103
- assert.equal(backoffDelayMs(2, retry, undefined, 0), 2000);
104
- });
105
-
106
- it("caps the exponential at maxDelayMs", () => {
107
- assert.equal(backoffDelayMs(20, retry, undefined, 0), retry.maxDelayMs);
108
- });
109
-
110
- it("adds up to +50% jitter", () => {
111
- assert.equal(backoffDelayMs(0, retry, undefined, 1), 750);
112
- });
113
-
114
- it("lets a Retry-After win outright, still capped", () => {
115
- assert.equal(backoffDelayMs(0, retry, 3000, 1), 3000);
116
- assert.equal(backoffDelayMs(0, retry, 99_000, 0), retry.maxDelayMs);
117
- });
118
- });
119
-
120
- describe("resolveRetryConfig", () => {
121
- /** Run `fn` with `env` applied to `process.env`, restoring after. */
122
- function withEnv(env: Record<string, string | undefined>, fn: () => void): void {
123
- const saved = new Map<string, string | undefined>();
124
- for (const key of Object.keys(env)) {
125
- saved.set(key, process.env[key]);
126
- if (env[key] === undefined) delete process.env[key];
127
- else process.env[key] = env[key];
128
- }
129
- try {
130
- fn();
131
- } finally {
132
- for (const [key, value] of saved) {
133
- if (value === undefined) delete process.env[key];
134
- else process.env[key] = value;
135
- }
136
- }
137
- }
138
-
139
- it("defaults to on with the built-in policy", () => {
140
- withEnv(
141
- {
142
- PROXY_RETRY_ON_429: undefined,
143
- PROXY_RETRY_MAX: undefined,
144
- PROXY_RETRY_BASE_MS: undefined,
145
- PROXY_RETRY_MAX_MS: undefined,
146
- },
147
- () => assert.deepEqual(resolveRetryConfig(), DEFAULT_RETRY),
148
- );
149
- });
150
-
151
- it("honors a loose PROXY_RETRY_ON_429 to switch it off", () => {
152
- withEnv({ PROXY_RETRY_ON_429: "no" }, () => assert.equal(resolveRetryConfig().enabled, false));
153
- withEnv({ PROXY_RETRY_ON_429: "off" }, () => assert.equal(resolveRetryConfig().enabled, false));
154
- });
155
-
156
- it("reads numeric tunables from env", () => {
157
- withEnv(
158
- { PROXY_RETRY_MAX: "9", PROXY_RETRY_BASE_MS: "250", PROXY_RETRY_MAX_MS: "60000" },
159
- () => {
160
- const config = resolveRetryConfig();
161
- assert.equal(config.maxRetries, 9);
162
- assert.equal(config.baseDelayMs, 250);
163
- assert.equal(config.maxDelayMs, 60_000);
164
- },
165
- );
166
- });
167
-
168
- it("lets an explicit override beat env (the CLI --no-retry-429 path)", () => {
169
- withEnv({ PROXY_RETRY_ON_429: "true" }, () =>
170
- assert.equal(resolveRetryConfig({ enabled: false }).enabled, false),
171
- );
172
- });
173
- });
@@ -1,14 +0,0 @@
1
- // ~~ Generated by projen. To modify, edit .projenrc.js and run "pnpm exec projen".
2
- {
3
- "extends": "../tsconfig.json",
4
- "compilerOptions": {
5
- "noEmit": true,
6
- "rootDir": ".."
7
- },
8
- "include": [
9
- "**/*.ts"
10
- ],
11
- "exclude": [
12
- "node_modules"
13
- ]
14
- }
package/tsconfig.json DELETED
@@ -1,43 +0,0 @@
1
- // ~~ Generated by projen. To modify, edit .projenrc.js and run "pnpm exec projen".
2
- {
3
- "compilerOptions": {
4
- "rootDir": ".",
5
- "outDir": "lib",
6
- "alwaysStrict": true,
7
- "declaration": true,
8
- "esModuleInterop": true,
9
- "experimentalDecorators": true,
10
- "inlineSourceMap": true,
11
- "inlineSources": true,
12
- "lib": [
13
- "ES2022"
14
- ],
15
- "module": "ESNext",
16
- "noEmitOnError": false,
17
- "noFallthroughCasesInSwitch": true,
18
- "noImplicitAny": true,
19
- "noImplicitReturns": true,
20
- "noImplicitThis": true,
21
- "noUnusedLocals": true,
22
- "noUnusedParameters": true,
23
- "resolveJsonModule": true,
24
- "strict": true,
25
- "strictNullChecks": true,
26
- "strictPropertyInitialization": true,
27
- "stripInternal": true,
28
- "target": "ES2022",
29
- "types": [
30
- "node"
31
- ],
32
- "moduleResolution": "bundler",
33
- "skipLibCheck": true
34
- },
35
- "include": [
36
- "src/**/*.ts",
37
- "index.ts",
38
- "bin/**/*.ts"
39
- ],
40
- "exclude": [
41
- "node_modules"
42
- ]
43
- }