@cartanova/qgrid-cli 2.5.2 → 2.5.4

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.
Files changed (68) hide show
  1. package/README.ko.md +7 -2
  2. package/README.md +7 -2
  3. package/bundle/dist/application/monit/monit.frame.js +3 -3
  4. package/bundle/dist/application/qgrid/oauth.js +1 -1
  5. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +41 -15
  6. package/bundle/dist/application/qgrid/qgrid.frame.js +74 -28
  7. package/bundle/dist/application/qgrid/qgrid.types.js +5 -3
  8. package/bundle/dist/application/qgrid/tool-emulation-schema.js +121 -0
  9. package/bundle/dist/application/qgrid/tool-emulation.js +116 -96
  10. package/bundle/dist/application/request-log/request-log.model.js +3 -3
  11. package/bundle/dist/application/request-log-step/request-log-step.model.js +2 -2
  12. package/bundle/dist/application/token/token.model.js +2 -2
  13. package/bundle/dist/server-error-handler.js +15 -0
  14. package/bundle/dist/sonamu.config.js +4 -7
  15. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +5 -4
  16. package/bundle/dist/utils/providers/anthropic/claude-session.js +7 -5
  17. package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +6 -4
  18. package/bundle/dist/utils/providers/common/schema-validation.js +178 -0
  19. package/bundle/dist/utils/providers/common/strictifier.js +329 -42
  20. package/bundle/dist/utils/providers/openai/codex-worker.js +16 -8
  21. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +5 -37
  22. package/bundle/dist/utils/providers/openai/openai-worker-pool-config.js +37 -0
  23. package/bundle/src/application/monit/monit.frame.ts +1 -1
  24. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +103 -0
  25. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +358 -1
  26. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +50 -28
  27. package/bundle/src/application/qgrid/qgrid.frame.test.ts +629 -8
  28. package/bundle/src/application/qgrid/qgrid.frame.ts +107 -27
  29. package/bundle/src/application/qgrid/qgrid.types.ts +4 -1
  30. package/bundle/src/application/qgrid/tool-emulation-schema.test.ts +188 -0
  31. package/bundle/src/application/qgrid/tool-emulation-schema.ts +176 -0
  32. package/bundle/src/application/qgrid/tool-emulation.test.ts +285 -67
  33. package/bundle/src/application/qgrid/tool-emulation.ts +199 -102
  34. package/bundle/src/server-error-handler.ts +22 -0
  35. package/bundle/src/sonamu.config.test.ts +37 -0
  36. package/bundle/src/sonamu.config.ts +2 -5
  37. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +56 -0
  38. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +3 -3
  39. package/bundle/src/utils/providers/anthropic/claude-session.test.ts +10 -0
  40. package/bundle/src/utils/providers/anthropic/claude-session.ts +6 -1
  41. package/bundle/src/utils/providers/anthropic/stream-json-adapter.test.ts +66 -0
  42. package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +12 -3
  43. package/bundle/src/utils/providers/common/provider-dispatcher.ts +2 -0
  44. package/bundle/src/utils/providers/common/schema-validation.test.ts +284 -0
  45. package/bundle/src/utils/providers/common/schema-validation.ts +308 -0
  46. package/bundle/src/utils/providers/common/strictifier.test.ts +746 -0
  47. package/bundle/src/utils/providers/common/strictifier.ts +513 -52
  48. package/bundle/src/utils/providers/openai/codex-worker.test.ts +31 -0
  49. package/bundle/src/utils/providers/openai/codex-worker.ts +12 -4
  50. package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +6 -4
  51. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +6 -79
  52. package/bundle/src/utils/providers/openai/openai-worker-pool-config.ts +80 -0
  53. package/bundle/web-dist/client/assets/{index-BQuKeBC5.js → index-DiY5V2e3.js} +3 -3
  54. package/bundle/web-dist/client/assets/{logs-BSsHMIKT.js → logs-UZ4Cvdmc.js} +1 -1
  55. package/bundle/web-dist/client/assets/{monit-D5ypGmGi.js → monit-C5cJo-HD.js} +1 -1
  56. package/bundle/web-dist/client/assets/{routes-BsCbz225.js → routes-DEKEmoQN.js} +1 -1
  57. package/bundle/web-dist/client/assets/{show-CXPhLZCl.js → show-DnX6b8KG.js} +1 -1
  58. package/bundle/web-dist/client/assets/{tokens-gEGH52O8.js → tokens-Ct0FGD4b.js} +1 -1
  59. package/bundle/web-dist/client/index.html +1 -1
  60. package/bundle/web-dist/server/entry-server.generated.js +1 -1
  61. package/package.json +1 -1
  62. package/skills/qgrid/references/ai-sdk-provider-contract.md +42 -2
  63. package/skills/qgrid/references/anthropic-claude-code-runtime.md +31 -1
  64. package/skills/qgrid/references/cli-env-and-server-boot.md +1 -1
  65. package/skills/qgrid/references/decision-rationale.md +11 -1
  66. package/skills/qgrid/references/repo-map.md +2 -1
  67. package/skills/qgrid/references/tool-calling-and-multiturn.md +33 -6
  68. package/skills/qgrid/references/verification-and-debugging.md +16 -1
@@ -5,15 +5,17 @@ import { type OpenAICredentials } from "../../../application/token/token.types";
5
5
  import { type GenerateRequest, type GenerateResult } from "../common/provider-dispatcher";
6
6
  import { type CodexAppServerWorker } from "./codex-worker";
7
7
  import {
8
- estimateOpenAIWorkerRssGiB,
9
8
  ImageGenerationError,
10
9
  makeOpenAIWorkerId,
11
- MAX_OPENAI_WORKERS_PER_TOKEN,
12
- type OpenAIWorkerPoolConfig,
13
10
  OpenAIDispatcher,
14
- resolveOpenAIWorkerPoolConfig,
15
11
  } from "./openai-dispatcher";
16
12
  import { type OpenAIQuotaUsageResult } from "./openai-quota";
13
+ import {
14
+ estimateOpenAIWorkerRssGiB,
15
+ MAX_OPENAI_WORKERS_PER_TOKEN,
16
+ type OpenAIWorkerPoolConfig,
17
+ resolveOpenAIWorkerPoolConfig,
18
+ } from "./openai-worker-pool-config";
17
19
 
18
20
  const { readOpenAIQuotaUsageMock, loggerInfoMock, loggerWarnMock } = vi.hoisted(() => ({
19
21
  readOpenAIQuotaUsageMock: vi.fn(),
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OpenAIDispatcher — codex app-server worker pool + 토큰 라우팅.
3
3
  *
4
- * - 토큰당 N worker (CodexAppServerWorker), 기본 3
4
+ * - 토큰당 CodexAppServerWorker 자동 확장 (기본 5~15, hard cap 20)
5
5
  * - idle worker round-robin 선택 + 큐 대기 (전부 busy 시)
6
6
  * - TokenSubscriber 이벤트로 worker pool 동기화
7
7
  * - backpressure: 큐 full 또는 timeout 시 SERVER_BUSY
@@ -29,6 +29,11 @@ import {
29
29
  type OpenAIRateLimitsWithMeta,
30
30
  } from "./openai-quota";
31
31
  import { handleChatgptAuthTokensRefresh } from "./openai-refresh";
32
+ import {
33
+ estimateOpenAIWorkerRssGiB,
34
+ type OpenAIWorkerPoolConfig,
35
+ resolveOpenAIWorkerPoolConfig,
36
+ } from "./openai-worker-pool-config";
32
37
 
33
38
  const logger = getLogger(["qgrid", "openai-dispatcher"]);
34
39
 
@@ -49,88 +54,10 @@ export class ImageGenerationError extends Error {
49
54
  }
50
55
 
51
56
  const DEFAULT_EFFORT = "low";
52
- export const MAX_OPENAI_WORKERS_PER_TOKEN = 20;
53
- const DEFAULT_OPENAI_MIN_WORKERS_PER_TOKEN = 5;
54
- const DEFAULT_OPENAI_MAX_WORKERS_PER_TOKEN = 15;
55
57
  const QUEUE_TIMEOUT_MS = 60_000;
56
58
  const MAX_QUEUE_SIZE = 50;
57
59
  const SPAWN_INTERVAL_MS = 500;
58
60
 
59
- export const OPENAI_WORKER_BASE_RSS_GIB = 0.71;
60
- export const OPENAI_WORKER_RSS_GIB = 0.157;
61
-
62
- export type OpenAIWorkerPoolConfig = {
63
- autoscale: boolean;
64
- minWorkersPerToken: number;
65
- maxWorkersPerToken: number;
66
- scaleIntervalMs: number;
67
- scaleDownIdleMs: number;
68
- maxEstimatedRssGiB: number;
69
- minHostAvailableGiB: number;
70
- };
71
-
72
- function boundedInteger(
73
- value: string | undefined,
74
- fallback: number,
75
- min: number,
76
- max: number,
77
- ): number {
78
- const parsed = Number(value);
79
- if (!Number.isFinite(parsed)) return fallback;
80
- return Math.max(min, Math.min(Math.floor(parsed), max));
81
- }
82
-
83
- function boundedNumber(value: string | undefined, fallback: number, min: number): number {
84
- const parsed = Number(value);
85
- if (!Number.isFinite(parsed)) return fallback;
86
- return Math.max(min, parsed);
87
- }
88
-
89
- export function resolveOpenAIWorkerPoolConfig(
90
- env: Record<string, string | undefined> = process.env,
91
- ): OpenAIWorkerPoolConfig {
92
- const autoscale = env.QGRID_OPENAI_AUTOSCALE !== "false" && env.QGRID_OPENAI_AUTOSCALE !== "0";
93
- const legacyWorkers = boundedInteger(
94
- env.QGRID_WORKERS_PER_TOKEN,
95
- DEFAULT_OPENAI_MIN_WORKERS_PER_TOKEN,
96
- 1,
97
- MAX_OPENAI_WORKERS_PER_TOKEN,
98
- );
99
- const minWorkersPerToken = boundedInteger(
100
- env.QGRID_OPENAI_MIN_WORKERS_PER_TOKEN,
101
- legacyWorkers,
102
- 1,
103
- MAX_OPENAI_WORKERS_PER_TOKEN,
104
- );
105
- const configuredMax = boundedInteger(
106
- env.QGRID_OPENAI_MAX_WORKERS_PER_TOKEN,
107
- autoscale ? DEFAULT_OPENAI_MAX_WORKERS_PER_TOKEN : minWorkersPerToken,
108
- 1,
109
- MAX_OPENAI_WORKERS_PER_TOKEN,
110
- );
111
-
112
- return {
113
- autoscale,
114
- minWorkersPerToken,
115
- maxWorkersPerToken: autoscale
116
- ? Math.max(minWorkersPerToken, configuredMax)
117
- : minWorkersPerToken,
118
- scaleIntervalMs: boundedInteger(env.QGRID_OPENAI_SCALE_INTERVAL_MS, 5_000, 250, 300_000),
119
- scaleDownIdleMs: boundedInteger(
120
- env.QGRID_OPENAI_SCALE_DOWN_IDLE_MS,
121
- 10 * 60_000,
122
- 1_000,
123
- 24 * 60 * 60_000,
124
- ),
125
- maxEstimatedRssGiB: boundedNumber(env.QGRID_OPENAI_MAX_ESTIMATED_RSS_GIB, 16, 1),
126
- minHostAvailableGiB: boundedNumber(env.QGRID_OPENAI_MIN_HOST_AVAILABLE_GIB, 20, 0),
127
- };
128
- }
129
-
130
- export function estimateOpenAIWorkerRssGiB(totalWorkers: number): number {
131
- return OPENAI_WORKER_BASE_RSS_GIB + OPENAI_WORKER_RSS_GIB * totalWorkers;
132
- }
133
-
134
61
  function readHostAvailableGiB(): number {
135
62
  try {
136
63
  const match = readFileSync("/proc/meminfo", "utf8").match(/^MemAvailable:\s+(\d+)\s+kB$/m);
@@ -0,0 +1,80 @@
1
+ export const MAX_OPENAI_WORKERS_PER_TOKEN = 20;
2
+ const DEFAULT_OPENAI_MIN_WORKERS_PER_TOKEN = 5;
3
+ const DEFAULT_OPENAI_MAX_WORKERS_PER_TOKEN = 15;
4
+
5
+ export const OPENAI_WORKER_BASE_RSS_GIB = 0.71;
6
+ export const OPENAI_WORKER_RSS_GIB = 0.157;
7
+
8
+ export type OpenAIWorkerPoolConfig = {
9
+ autoscale: boolean;
10
+ minWorkersPerToken: number;
11
+ maxWorkersPerToken: number;
12
+ scaleIntervalMs: number;
13
+ scaleDownIdleMs: number;
14
+ maxEstimatedRssGiB: number;
15
+ minHostAvailableGiB: number;
16
+ };
17
+
18
+ function boundedInteger(
19
+ value: string | undefined,
20
+ fallback: number,
21
+ min: number,
22
+ max: number,
23
+ ): number {
24
+ const parsed = Number(value);
25
+ if (!Number.isFinite(parsed)) return fallback;
26
+ return Math.max(min, Math.min(Math.floor(parsed), max));
27
+ }
28
+
29
+ function boundedNumber(value: string | undefined, fallback: number, min: number): number {
30
+ const parsed = Number(value);
31
+ if (!Number.isFinite(parsed)) return fallback;
32
+ return Math.max(min, parsed);
33
+ }
34
+
35
+ export function resolveOpenAIWorkerPoolConfig(
36
+ env: Record<string, string | undefined> = process.env,
37
+ ): OpenAIWorkerPoolConfig {
38
+ const autoscale = env.QGRID_OPENAI_AUTOSCALE !== "false" && env.QGRID_OPENAI_AUTOSCALE !== "0";
39
+ const legacyWorkers = boundedInteger(
40
+ env.QGRID_WORKERS_PER_TOKEN,
41
+ DEFAULT_OPENAI_MIN_WORKERS_PER_TOKEN,
42
+ 1,
43
+ MAX_OPENAI_WORKERS_PER_TOKEN,
44
+ );
45
+ const minWorkersPerToken = boundedInteger(
46
+ env.QGRID_OPENAI_MIN_WORKERS_PER_TOKEN,
47
+ legacyWorkers,
48
+ 1,
49
+ MAX_OPENAI_WORKERS_PER_TOKEN,
50
+ );
51
+ return {
52
+ autoscale,
53
+ minWorkersPerToken,
54
+ // autoscale 이 꺼지면 max 는 min 으로 고정되므로 env 파싱 자체가 autoscale 분기 안에만 있다.
55
+ maxWorkersPerToken: autoscale
56
+ ? Math.max(
57
+ minWorkersPerToken,
58
+ boundedInteger(
59
+ env.QGRID_OPENAI_MAX_WORKERS_PER_TOKEN,
60
+ DEFAULT_OPENAI_MAX_WORKERS_PER_TOKEN,
61
+ 1,
62
+ MAX_OPENAI_WORKERS_PER_TOKEN,
63
+ ),
64
+ )
65
+ : minWorkersPerToken,
66
+ scaleIntervalMs: boundedInteger(env.QGRID_OPENAI_SCALE_INTERVAL_MS, 5_000, 250, 300_000),
67
+ scaleDownIdleMs: boundedInteger(
68
+ env.QGRID_OPENAI_SCALE_DOWN_IDLE_MS,
69
+ 10 * 60_000,
70
+ 1_000,
71
+ 24 * 60 * 60_000,
72
+ ),
73
+ maxEstimatedRssGiB: boundedNumber(env.QGRID_OPENAI_MAX_ESTIMATED_RSS_GIB, 16, 1),
74
+ minHostAvailableGiB: boundedNumber(env.QGRID_OPENAI_MIN_HOST_AVAILABLE_GIB, 20, 0),
75
+ };
76
+ }
77
+
78
+ export function estimateOpenAIWorkerRssGiB(totalWorkers: number): number {
79
+ return OPENAI_WORKER_BASE_RSS_GIB + OPENAI_WORKER_RSS_GIB * totalWorkers;
80
+ }