@cartanova/qgrid-ai-sdk 2.4.1 → 2.4.3

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/README.ko.md CHANGED
@@ -160,7 +160,7 @@ const { text } = await generateText({
160
160
  | 옵션 | 값 | 적용 범위 | 설명 |
161
161
  |---|---|---|---|
162
162
  | `sessionKey` | `string` | OpenAI 전용 | 멀티턴 대화 식별자. 같은 key는 같은 codex thread로 라우팅되어 prompt cache 적중 ([아래](#멀티턴-prompt-cache-sessionkey) 참조) |
163
- | `effort` | `"none"` \| `"minimal"` \| `"low"` \| `"medium"` \| `"high"` \| `"xhigh"` | 공통 | reasoning 모델의 추론 깊이. 기본값은 config의 `defaultEffort` (`"low"`) |
163
+ | `effort` | `"none"` \| `"minimal"` \| `"low"` \| `"medium"` \| `"high"` \| `"xhigh"` \| `"max"` | 공통 (지원 값은 모델별 상이, 예: `"max"`는 GPT-5.6+) | reasoning 모델의 추론 깊이. 기본값은 config의 `defaultEffort` (`"low"`) |
164
164
  | `verbosity` | `"low"` \| `"medium"` \| `"high"` | OpenAI 전용 | 응답 텍스트의 상세도 |
165
165
  | `reasoningSummary` | `"auto"` \| `"concise"` \| `"detailed"` \| `"none"` | OpenAI 전용 | 추론 요약 출력 방식 |
166
166
  | `serviceTier` | `string` | OpenAI 전용 | OpenAI/codex service tier |
@@ -269,6 +269,9 @@ createQgridLogger({
269
269
  ```typescript
270
270
  type QgridSupportedModel =
271
271
  // OpenAI (based on codex app-server)
272
+ | "openai/gpt-5.6-sol"
273
+ | "openai/gpt-5.6-terra"
274
+ | "openai/gpt-5.6-luna"
272
275
  | "openai/gpt-5.5"
273
276
  | "openai/gpt-5.4"
274
277
  | "openai/gpt-5.2"
@@ -290,6 +293,16 @@ type QgridSupportedModel =
290
293
  | "anthropic/claude-opus-4-8"
291
294
  ```
292
295
 
296
+ ### GPT-5.6 사양
297
+
298
+ | 모델 | Context (qgrid/codex 런타임) | 최대 출력 | 1M tokens당 input / cached input / output |
299
+ |---|---:|---:|---:|
300
+ | `openai/gpt-5.6-sol` | 372K | 128K | $5 / $0.50 / $30 |
301
+ | `openai/gpt-5.6-terra` | 372K | 128K | $2.50 / $0.25 / $15 |
302
+ | `openai/gpt-5.6-luna` | 372K | 128K | $1 / $0.10 / $6 |
303
+
304
+ 세 모델 모두 `max` reasoning effort를 지원합니다. OpenAI native API 사양은 1.05M context / 128K 최대 출력이지만, qgrid는 codex app-server 구독 경로로 실행되어 세 모델 모두 context window가 372K(95% effective, 실사용 입력 약 353K)로 보고되며 설정으로 올릴 수 없습니다. 입력이 272K tokens를 넘으면 요청 전체에 input 2x, output 1.5x 장문 컨텍스트 할증이 적용되며, cache write는 uncached input 단가의 1.25x입니다.
305
+
293
306
  ## 설정
294
307
 
295
308
  ```typescript
package/README.md CHANGED
@@ -160,7 +160,7 @@ const { text } = await generateText({
160
160
  | Option | Values | Applies to | Description |
161
161
  |---|---|---|---|
162
162
  | `sessionKey` | `string` | OpenAI only | Multi-turn conversation identifier. The same key routes to the same codex thread for prompt-cache hits (see [below](#multi-turn-prompt-cache-sessionkey)) |
163
- | `effort` | `"none"` \| `"minimal"` \| `"low"` \| `"medium"` \| `"high"` \| `"xhigh"` | both providers | Reasoning depth. Defaults to the config's `defaultEffort` (`"low"`) |
163
+ | `effort` | `"none"` \| `"minimal"` \| `"low"` \| `"medium"` \| `"high"` \| `"xhigh"` \| `"max"` | both providers (supported values are model-dependent, e.g. `"max"` is GPT-5.6+) | Reasoning depth. Defaults to the config's `defaultEffort` (`"low"`) |
164
164
  | `verbosity` | `"low"` \| `"medium"` \| `"high"` | OpenAI only | Response text verbosity |
165
165
  | `reasoningSummary` | `"auto"` \| `"concise"` \| `"detailed"` \| `"none"` | OpenAI only | Reasoning summary output mode |
166
166
  | `serviceTier` | `string` | OpenAI only | OpenAI/codex service tier |
@@ -269,6 +269,9 @@ The `qgrid()` provider has its own lifecycle, so the logger automatically suppre
269
269
  ```typescript
270
270
  type QgridSupportedModel =
271
271
  // OpenAI (based on codex app-server)
272
+ | "openai/gpt-5.6-sol"
273
+ | "openai/gpt-5.6-terra"
274
+ | "openai/gpt-5.6-luna"
272
275
  | "openai/gpt-5.5"
273
276
  | "openai/gpt-5.4"
274
277
  | "openai/gpt-5.2"
@@ -290,6 +293,16 @@ type QgridSupportedModel =
290
293
  | "anthropic/claude-opus-4-8"
291
294
  ```
292
295
 
296
+ ### GPT-5.6 specifications
297
+
298
+ | Model | Context (qgrid/codex runtime) | Max output | Input / cached input / output per 1M tokens |
299
+ |---|---:|---:|---:|
300
+ | `openai/gpt-5.6-sol` | 372K | 128K | $5 / $0.50 / $30 |
301
+ | `openai/gpt-5.6-terra` | 372K | 128K | $2.50 / $0.25 / $15 |
302
+ | `openai/gpt-5.6-luna` | 372K | 128K | $1 / $0.10 / $6 |
303
+
304
+ All GPT-5.6 models support reasoning through `max`. The OpenAI native API spec is a 1.05M context window with 128K max output, but qgrid runs on the codex app-server subscription path, where all three models report a 372K context window (95% effective — about 353K of usable input) that cannot be raised by configuration. Prompts over 272K input tokens apply a 2x input and 1.5x output surcharge to the full request; cache writes cost 1.25x the uncached input rate.
305
+
293
306
  ## Configuration
294
307
 
295
308
  ```typescript
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ type QgridProviderOptions = {
20
20
  */
21
21
  sessionKey?: string;
22
22
  /** reasoning 모델의 추론 깊이. 기본값은 qgrid config의 defaultEffort. */
23
- effort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
23
+ effort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
24
24
  /** 응답 텍스트의 상세도. OpenAI/codex route에서만 적용된다. */
25
25
  verbosity?: "low" | "medium" | "high";
26
26
  /** reasoning 모델의 추론 요약 출력 방식. OpenAI/codex route에서만 적용된다. */
@@ -50,7 +50,7 @@ type QgridProviderOptions = {
50
50
  * provider 내부 threadCoordStore 가 sessionKey 별로 관리
51
51
  */
52
52
 
53
- type QgridSupportedModel = "openai/gpt-5.5" | "openai/gpt-5.4" | "openai/gpt-5.2" | "openai/gpt-5.4-mini" | "openai/gpt-5.3-codex" | "openai/gpt-5.3-codex-spark" | "anthropic/claude-haiku-4-5" | "anthropic/claude-sonnet-4" | "anthropic/claude-sonnet-4-5" | "anthropic/claude-sonnet-4-6" | "anthropic/claude-sonnet-4-7" | "anthropic/claude-sonnet-5" | "anthropic/claude-opus-4" | "anthropic/claude-opus-4-1" | "anthropic/claude-opus-4-5" | "anthropic/claude-opus-4-6" | "anthropic/claude-opus-4-7" | "anthropic/claude-opus-4-8";
53
+ type QgridSupportedModel = "openai/gpt-5.6-sol" | "openai/gpt-5.6-terra" | "openai/gpt-5.6-luna" | "openai/gpt-5.5" | "openai/gpt-5.4" | "openai/gpt-5.2" | "openai/gpt-5.4-mini" | "openai/gpt-5.3-codex" | "openai/gpt-5.3-codex-spark" | "anthropic/claude-haiku-4-5" | "anthropic/claude-sonnet-4" | "anthropic/claude-sonnet-4-5" | "anthropic/claude-sonnet-4-6" | "anthropic/claude-sonnet-4-7" | "anthropic/claude-sonnet-5" | "anthropic/claude-opus-4" | "anthropic/claude-opus-4-1" | "anthropic/claude-opus-4-5" | "anthropic/claude-opus-4-6" | "anthropic/claude-opus-4-7" | "anthropic/claude-opus-4-8";
54
54
  type QgridLoggerConfig = {
55
55
  /** qgrid 서버 주소. default: process.env.QGRID_URL, 없으면 기본값은 "http://localhost:44900" */
56
56
  serverUrl?: string;
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ const QGRID_PROVIDER_NAME = "qgrid";
3
3
  const THREAD_COORD_TTL_MS = 10 * 6e4;
4
4
  const TEXT_STREAM_ID_PREFIX = "text";
5
5
  const TOP_LEVEL_SCHEMA_WARNING = "OpenAI structured output requires \"object\". Falling back to client-side parsing.";
6
- const PENDING_TOOL_RESULTS_WARNING = "[qgrid] pending tool results not found in prompt, clearing client run state";
6
+ const PENDING_TOOL_RESULTS_WARNING = "[qgrid] tool results did not match any pending qgrid run; sending request without runContext";
7
7
  //#endregion
8
8
  //#region src/utils.ts
9
9
  async function createRun(serverUrl, body) {
@@ -602,16 +602,16 @@ function createQgridLogger(config = {}) {
602
602
  //#endregion
603
603
  //#region src/index.ts
604
604
  const threadCoordStore = /* @__PURE__ */ new Map();
605
- const MAX_IMAGE_INPUT_DATA_URL_CHARS = 9e5;
606
- const MAX_IMAGE_INPUT_DATA_URL_CHARS_LABEL = `${MAX_IMAGE_INPUT_DATA_URL_CHARS / 1e3}k`;
605
+ const MAX_IMAGE_INPUT_DATA_URL_CHARS = 9e6;
606
+ const MAX_IMAGE_INPUT_DATA_URL_CHARS_LABEL = "9M";
607
607
  function assertImageInputFitsJsonTransport(imageUrls) {
608
608
  let totalDataUrlChars = 0;
609
609
  for (const url of imageUrls) {
610
610
  if (!url.toLowerCase().startsWith("data:")) continue;
611
611
  totalDataUrlChars += url.length;
612
- if (url.length > MAX_IMAGE_INPUT_DATA_URL_CHARS) throw new Error(`qgrid: reference image input is too large for JSON transport (${url.length} chars). Compress or resize it before passing it to imageGeneration, for example as WebP/JPEG under ${MAX_IMAGE_INPUT_DATA_URL_CHARS_LABEL} total base64 data-url chars.`);
612
+ if (url.length > MAX_IMAGE_INPUT_DATA_URL_CHARS) throw new Error(`qgrid: image input is too large for JSON transport (${url.length} chars). Compress or resize it before passing it to imageGeneration, for example as WebP/JPEG under ${MAX_IMAGE_INPUT_DATA_URL_CHARS_LABEL} total base64 data-url chars.`);
613
613
  }
614
- if (totalDataUrlChars > MAX_IMAGE_INPUT_DATA_URL_CHARS) throw new Error(`qgrid: reference image inputs are too large for JSON transport (${totalDataUrlChars} total chars). Compress or resize them before passing them to imageGeneration, for example as WebP/JPEG under ${MAX_IMAGE_INPUT_DATA_URL_CHARS_LABEL} total base64 data-url chars.`);
614
+ if (totalDataUrlChars > MAX_IMAGE_INPUT_DATA_URL_CHARS) throw new Error(`qgrid: image inputs are too large for JSON transport (${totalDataUrlChars} total chars). Compress or resize them before passing them to imageGeneration, for example as WebP/JPEG under ${MAX_IMAGE_INPUT_DATA_URL_CHARS_LABEL} total base64 data-url chars.`);
615
615
  }
616
616
  function warnDroppedImages(droppedImageCount) {
617
617
  if (droppedImageCount <= 0) return;
@@ -659,7 +659,39 @@ function qgrid(modelId, config) {
659
659
  const serverUrl = config?.serverUrl ?? process.env.QGRID_URL ?? "http://localhost:44900";
660
660
  const effort = config?.defaultEffort ?? "low";
661
661
  const projectName = config?.projectName ?? process.env.QGRID_PROJECT_NAME;
662
- let clientRun = null;
662
+ const clientRuns = /* @__PURE__ */ new Map();
663
+ let anonymousClientRunId = 0;
664
+ function clientRunKey(runContext) {
665
+ if (runContext.requestLogId !== void 0) return `request:${runContext.requestLogId}`;
666
+ if (runContext.threadCoord) {
667
+ const { workerId, epoch, threadId } = runContext.threadCoord;
668
+ return `thread:${workerId}:${epoch}:${threadId}`;
669
+ }
670
+ anonymousClientRunId++;
671
+ return `anonymous:${anonymousClientRunId}`;
672
+ }
673
+ function rememberClientRun(runContext, toolCallIds) {
674
+ if (toolCallIds.length === 0) return;
675
+ clientRuns.set(clientRunKey(runContext), {
676
+ runContext,
677
+ pendingToolCallIds: new Set(toolCallIds)
678
+ });
679
+ }
680
+ function matchClientRun(messages) {
681
+ const toolResults = extractToolResultsFromHistory(messages);
682
+ if (toolResults.length === 0) return null;
683
+ const resultIds = new Set(toolResults.map((r) => r.callId));
684
+ for (const [key, run] of clientRuns) if (run.pendingToolCallIds.size > 0 && [...run.pendingToolCallIds].every((id) => resultIds.has(id))) return {
685
+ key,
686
+ runContext: run.runContext,
687
+ toolResultsPayload: toolResults.filter((r) => run.pendingToolCallIds.has(r.callId)).map((r) => ({
688
+ toolCallId: r.callId,
689
+ output: r.result
690
+ }))
691
+ };
692
+ if (clientRuns.size > 0) console.warn(PENDING_TOOL_RESULTS_WARNING);
693
+ return null;
694
+ }
663
695
  return {
664
696
  specificationVersion: "v3",
665
697
  provider: QGRID_PROVIDER_NAME,
@@ -687,20 +719,11 @@ function qgrid(modelId, config) {
687
719
  let runContext;
688
720
  let toolResultsPayload;
689
721
  let logMode;
690
- if (clientRun) {
691
- const toolResults = extractToolResultsFromHistory(options.prompt);
692
- const resultIds = new Set(toolResults.map((r) => r.callId));
693
- if (clientRun.pendingToolCallIds.size > 0 && [...clientRun.pendingToolCallIds].every((id) => resultIds.has(id))) {
694
- runContext = clientRun.runContext;
695
- toolResultsPayload = toolResults.filter((r) => clientRun.pendingToolCallIds.has(r.callId)).map((r) => ({
696
- toolCallId: r.callId,
697
- output: r.result
698
- }));
699
- logMode = "run";
700
- } else {
701
- console.warn(PENDING_TOOL_RESULTS_WARNING);
702
- clientRun = null;
703
- }
722
+ const matchedClientRun = matchClientRun(options.prompt);
723
+ if (matchedClientRun) {
724
+ runContext = matchedClientRun.runContext;
725
+ toolResultsPayload = matchedClientRun.toolResultsPayload;
726
+ logMode = "run";
704
727
  }
705
728
  const storedCoord = reuseSessionKey ? getThreadCoord(reuseSessionKey) : void 0;
706
729
  if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
@@ -765,11 +788,8 @@ function qgrid(modelId, config) {
765
788
  text: data.text
766
789
  });
767
790
  if (imageGeneration && !content.some((c) => c.type === "file")) throw new Error("qgrid: imageGeneration was requested but the response contained no image — the server may be an older version that does not support image generation.");
768
- if (data.runContext && finishReason.unified === "tool-calls") clientRun = {
769
- runContext: data.runContext,
770
- pendingToolCallIds: new Set(content.filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
771
- };
772
- else clientRun = null;
791
+ if (matchedClientRun) clientRuns.delete(matchedClientRun.key);
792
+ if (data.runContext && finishReason.unified === "tool-calls") rememberClientRun(data.runContext, content.filter((c) => c.type === "tool-call").map((c) => c.toolCallId));
773
793
  if (reuseSessionKey && data.runContext?.threadCoord) setThreadCoord(reuseSessionKey, data.runContext.threadCoord);
774
794
  return {
775
795
  content,
@@ -803,20 +823,11 @@ function qgrid(modelId, config) {
803
823
  let runContext;
804
824
  let toolResultsPayload;
805
825
  let logMode;
806
- if (clientRun) {
807
- const toolResults = extractToolResultsFromHistory(options.prompt);
808
- const resultIds = new Set(toolResults.map((r) => r.callId));
809
- if (clientRun.pendingToolCallIds.size > 0 && [...clientRun.pendingToolCallIds].every((id) => resultIds.has(id))) {
810
- runContext = clientRun.runContext;
811
- toolResultsPayload = toolResults.filter((r) => clientRun.pendingToolCallIds.has(r.callId)).map((r) => ({
812
- toolCallId: r.callId,
813
- output: r.result
814
- }));
815
- logMode = "run";
816
- } else {
817
- console.warn(PENDING_TOOL_RESULTS_WARNING);
818
- clientRun = null;
819
- }
826
+ const matchedClientRun = matchClientRun(options.prompt);
827
+ if (matchedClientRun) {
828
+ runContext = matchedClientRun.runContext;
829
+ toolResultsPayload = matchedClientRun.toolResultsPayload;
830
+ logMode = "run";
820
831
  }
821
832
  const storedCoord = reuseSessionKey ? getThreadCoord(reuseSessionKey) : void 0;
822
833
  if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
@@ -883,11 +894,8 @@ function qgrid(modelId, config) {
883
894
  textStarted = false;
884
895
  }
885
896
  const done = event.data;
886
- if (done.runContext && done.finishReason === "tool-calls") clientRun = {
887
- runContext: done.runContext,
888
- pendingToolCallIds: new Set((done.content ?? []).filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
889
- };
890
- else clientRun = null;
897
+ if (matchedClientRun) clientRuns.delete(matchedClientRun.key);
898
+ if (done.runContext && done.finishReason === "tool-calls") rememberClientRun(done.runContext, (done.content ?? []).filter((c) => c.type === "tool-call").map((c) => c.toolCallId));
891
899
  if (reuseSessionKey && done.runContext?.threadCoord) setThreadCoord(reuseSessionKey, done.runContext.threadCoord);
892
900
  if (done.content) {
893
901
  for (const item of done.content) if (item.type === "text" && !deltaTextEmitted) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartanova/qgrid-ai-sdk",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "description": "AI SDK LanguageModelV3 provider for qgrid",
5
5
  "repository": {
6
6
  "type": "git",