@cartanova/qgrid-ai-sdk 2.3.3 → 2.3.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.
package/dist/index.d.ts CHANGED
@@ -9,20 +9,26 @@ type QgridProviderConfig = {
9
9
  projectName?: string;
10
10
  };
11
11
  /**
12
- * codex app-server가 지원하는 OpenAI provider options.
13
- *
14
- * AI SDK의 openai provider options 중 codex가 처리할 수 있는 subset만 정의.
15
- * 여기에 없는 옵션(temperature, maxOutputTokens 등)은 codex가 무시합니다.
16
- *
17
- * @see https://ai-sdk.dev/providers/ai-sdk-providers/openai#provider-options
12
+ * qgrid provider options.
13
+ * providerOptions.qgrid 로 전달한다.
18
14
  */
19
- type QgridOpenAIProviderOptions = {
15
+ type QgridProviderOptions = {
16
+ /**
17
+ * 멀티턴 시 codex thread reuse를 위한 대화 식별자, 호출자가 자기 도메인 ID(예: 게임 세션 ID) 하나만 넘기면
18
+ * provider가 같은 sessionKey 의 thread 좌표를 내부에서 회송해 prompt cache 를 적중시킨다
19
+ * 좌표는 sessionKey 별로 격리된다.
20
+ */
21
+ sessionKey?: string;
20
22
  /** reasoning 모델의 추론 깊이. 기본값은 qgrid config의 defaultEffort. */
21
- reasoningEffort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
22
- /** reasoning 모델의 추론 요약 출력 방식. Responses API 전용. */
23
+ effort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
24
+ /** 응답 텍스트의 상세도. OpenAI/codex route에서만 적용된다. */
25
+ verbosity?: "low" | "medium" | "high";
26
+ /** reasoning 모델의 추론 요약 출력 방식. OpenAI/codex route에서만 적용된다. */
23
27
  reasoningSummary?: "auto" | "concise" | "detailed" | "none";
24
- /** 응답 텍스트의 상세도. */
25
- textVerbosity?: "low" | "medium" | "high";
28
+ /** OpenAI/codex service tier. OpenAI/codex route에서만 적용된다. */
29
+ serviceTier?: string;
30
+ /** @todo 향후 qgrid 서버 fallback routing에 사용할 후보 모델 목록. */
31
+ fallbackModels?: string[];
26
32
  };
27
33
  /**
28
34
  * codex thread 좌표
@@ -30,18 +36,6 @@ type QgridOpenAIProviderOptions = {
30
36
  * provider 내부 threadCoordStore 가 sessionKey 별로 관리
31
37
  */
32
38
 
33
- /**
34
- * {@link QgridOpenAIProviderOptions}
35
- */
36
- type QgridProviderOptions = {
37
- openai?: QgridOpenAIProviderOptions;
38
- /**
39
- * 멀티턴 대화 식별자. 호출자가 자기 도메인 ID(예: 게임 세션 ID) 하나만 넘기면, provider 가
40
- * 같은 sessionKey 의 thread 좌표를 내부에서 회송해 prompt cache 를 적중시킨다.
41
- * 좌표는 sessionKey 별로 격리된다.
42
- */
43
- sessionKey?: string;
44
- };
45
39
  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-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";
46
40
  type QgridLoggerConfig = {
47
41
  /** qgrid 서버 주소. default: process.env.QGRID_URL, 없으면 기본값은 "http://localhost:44900" */
package/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ //#region src/qgrid.constant.ts
2
+ const QGRID_PROVIDER_NAME = "qgrid";
3
+ const THREAD_COORD_TTL_MS = 10 * 6e4;
4
+ const TEXT_STREAM_ID_PREFIX = "text";
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";
7
+ //#endregion
1
8
  //#region src/utils.ts
2
9
  async function createRun(serverUrl, body) {
3
10
  return (await fetch(`${serverUrl}/api/qgrid/createRun`, {
@@ -240,7 +247,6 @@ function timedKeySet() {
240
247
  function createQgridLogger(config = {}) {
241
248
  const serverUrl = config.serverUrl ?? process.env.QGRID_URL ?? "http://localhost:44900";
242
249
  const projectName = config.projectName ?? process.env.QGRID_PROJECT_NAME;
243
- config.tokenName ?? process.env.QGRID_TOKEN_NAME;
244
250
  const onLogError = config.onLogError ?? ((e) => console.warn(`[qgrid-logger] ${e.message}`));
245
251
  const runs = /* @__PURE__ */ new Map();
246
252
  const keyTtl = typeof config.staleRunTimeoutMs === "number" && config.staleRunTimeoutMs > 0 ? config.staleRunTimeoutMs : DEFAULT_STALE_RUN_TIMEOUT_MS;
@@ -459,9 +465,6 @@ function createQgridLogger(config = {}) {
459
465
  }
460
466
  //#endregion
461
467
  //#region src/index.ts
462
- const DEFAULT_SERVER_URL = "http://localhost:44900";
463
- const DEFAULT_EFFORT = "low";
464
- const THREAD_COORD_TTL_MS = 10 * 6e4;
465
468
  const threadCoordStore = /* @__PURE__ */ new Map();
466
469
  function getThreadCoord(sessionKey) {
467
470
  const entry = threadCoordStore.get(sessionKey);
@@ -478,6 +481,12 @@ function setThreadCoord(sessionKey, coord) {
478
481
  expiresAt: Date.now() + THREAD_COORD_TTL_MS
479
482
  });
480
483
  }
484
+ function reusableSessionKey(modelId, sessionKey) {
485
+ return modelId.startsWith("anthropic/") ? void 0 : sessionKey;
486
+ }
487
+ function getQgridProviderOptions(options) {
488
+ return options.providerOptions?.qgrid ?? {};
489
+ }
481
490
  function toAiSdkUsage(usage) {
482
491
  const cacheRead = usage.cache_read_input_tokens;
483
492
  const cacheWrite = usage.cache_creation_input_tokens;
@@ -496,27 +505,29 @@ function toAiSdkUsage(usage) {
496
505
  };
497
506
  }
498
507
  function qgrid(modelId, config) {
499
- const serverUrl = config?.serverUrl ?? process.env.QGRID_URL ?? DEFAULT_SERVER_URL;
500
- const effort = config?.defaultEffort ?? DEFAULT_EFFORT;
508
+ const serverUrl = config?.serverUrl ?? process.env.QGRID_URL ?? "http://localhost:44900";
509
+ const effort = config?.defaultEffort ?? "low";
501
510
  const projectName = config?.projectName ?? process.env.QGRID_PROJECT_NAME;
502
511
  let clientRun = null;
503
512
  return {
504
513
  specificationVersion: "v3",
505
- provider: "qgrid",
514
+ provider: QGRID_PROVIDER_NAME,
506
515
  modelId,
507
516
  supportedUrls: {},
508
517
  async doGenerate(options) {
509
518
  const tools = options.tools?.filter((t) => t.type === "function");
510
519
  const hasTools = tools && tools.length > 0;
511
520
  const { prompt, system, history } = extractPromptAndHistory(options.prompt);
512
- const openaiOpts = options.providerOptions?.openai;
513
- const effectiveEffort = openaiOpts?.reasoningEffort ?? effort;
514
- const verbosity = openaiOpts?.textVerbosity ?? openaiOpts?.verbosity;
515
- const reasoningSummary = openaiOpts?.reasoningSummary;
516
- const sessionKey = (options.providerOptions?.qgrid)?.sessionKey;
521
+ const qgridOptions = getQgridProviderOptions(options);
522
+ const effectiveEffort = qgridOptions.effort ?? effort;
523
+ const verbosity = qgridOptions.verbosity;
524
+ const reasoningSummary = qgridOptions.reasoningSummary;
525
+ const serviceTier = qgridOptions.serviceTier;
526
+ const sessionKey = qgridOptions.sessionKey;
527
+ const reuseSessionKey = reusableSessionKey(modelId, sessionKey);
517
528
  const rawSchema = options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0;
518
529
  const schemaType = rawSchema ? rawSchema.type : void 0;
519
- if (rawSchema && !hasTools && schemaType !== "object") console.warn(`[qgrid] responseFormat.schema top-level type is "${schemaType ?? "unknown"}". OpenAI structured output requires "object". Falling back to client-side parsing.`);
530
+ if (rawSchema && !hasTools && schemaType !== "object") console.warn(`[qgrid] responseFormat.schema top-level type is "${schemaType ?? "unknown"}". ${TOP_LEVEL_SCHEMA_WARNING}`);
520
531
  const jsonSchema = !hasTools && rawSchema && schemaType === "object" ? JSON.stringify(rawSchema) : void 0;
521
532
  let runContext;
522
533
  let toolResultsPayload;
@@ -532,11 +543,11 @@ function qgrid(modelId, config) {
532
543
  }));
533
544
  logMode = "run";
534
545
  } else {
535
- console.warn("[qgrid] pending tool results not found in prompt, clearing client run state");
546
+ console.warn(PENDING_TOOL_RESULTS_WARNING);
536
547
  clientRun = null;
537
548
  }
538
549
  }
539
- const storedCoord = sessionKey ? getThreadCoord(sessionKey) : void 0;
550
+ const storedCoord = reuseSessionKey ? getThreadCoord(reuseSessionKey) : void 0;
540
551
  if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
541
552
  if (!logMode && hasTools) logMode = "run";
542
553
  const data = await fetch(`${serverUrl}/api/qgrid/query`, {
@@ -549,6 +560,7 @@ function qgrid(modelId, config) {
549
560
  effort: effectiveEffort,
550
561
  ...verbosity ? { verbosity } : {},
551
562
  ...reasoningSummary ? { reasoningSummary } : {},
563
+ ...serviceTier ? { serviceTier } : {},
552
564
  ...hasTools ? { tools: tools.map(toQgridTool) } : {},
553
565
  ...jsonSchema ? { jsonSchema } : {},
554
566
  ...history.length > 0 ? { history: JSON.stringify(history) } : {},
@@ -594,7 +606,7 @@ function qgrid(modelId, config) {
594
606
  pendingToolCallIds: new Set(content.filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
595
607
  };
596
608
  else clientRun = null;
597
- if (sessionKey && data.runContext?.threadCoord) setThreadCoord(sessionKey, data.runContext.threadCoord);
609
+ if (reuseSessionKey && data.runContext?.threadCoord) setThreadCoord(reuseSessionKey, data.runContext.threadCoord);
598
610
  return {
599
611
  content,
600
612
  finishReason,
@@ -613,11 +625,13 @@ function qgrid(modelId, config) {
613
625
  const tools = options.tools?.filter((t) => t.type === "function");
614
626
  const hasTools = tools && tools.length > 0;
615
627
  const { prompt, system, history } = extractPromptAndHistory(options.prompt);
616
- const openaiOpts = options.providerOptions?.openai;
617
- const effectiveEffort = openaiOpts?.reasoningEffort ?? effort;
618
- const verbosity = openaiOpts?.textVerbosity ?? openaiOpts?.verbosity;
619
- const reasoningSummary = openaiOpts?.reasoningSummary;
620
- const sessionKey = (options.providerOptions?.qgrid)?.sessionKey;
628
+ const qgridOptions = getQgridProviderOptions(options);
629
+ const effectiveEffort = qgridOptions.effort ?? effort;
630
+ const verbosity = qgridOptions.verbosity;
631
+ const reasoningSummary = qgridOptions.reasoningSummary;
632
+ const serviceTier = qgridOptions.serviceTier;
633
+ const sessionKey = qgridOptions.sessionKey;
634
+ const reuseSessionKey = reusableSessionKey(modelId, sessionKey);
621
635
  const rawSchema = options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0;
622
636
  const jsonSchema = !hasTools && rawSchema && rawSchema.type === "object" ? JSON.stringify(rawSchema) : void 0;
623
637
  let runContext;
@@ -634,11 +648,11 @@ function qgrid(modelId, config) {
634
648
  }));
635
649
  logMode = "run";
636
650
  } else {
637
- console.warn("[qgrid] pending tool results not found in prompt, clearing client run state");
651
+ console.warn(PENDING_TOOL_RESULTS_WARNING);
638
652
  clientRun = null;
639
653
  }
640
654
  }
641
- const storedCoord = sessionKey ? getThreadCoord(sessionKey) : void 0;
655
+ const storedCoord = reuseSessionKey ? getThreadCoord(reuseSessionKey) : void 0;
642
656
  if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
643
657
  if (!logMode && hasTools) logMode = "run";
644
658
  const prepRes = await fetch(`${serverUrl}/api/qgrid/prepareStream`, {
@@ -651,6 +665,7 @@ function qgrid(modelId, config) {
651
665
  effort: effectiveEffort,
652
666
  ...verbosity ? { verbosity } : {},
653
667
  ...reasoningSummary ? { reasoningSummary } : {},
668
+ ...serviceTier ? { serviceTier } : {},
654
669
  ...hasTools ? { tools: tools.map(toQgridTool) } : {},
655
670
  ...jsonSchema ? { jsonSchema } : {},
656
671
  ...history.length > 0 ? { history: JSON.stringify(history) } : {},
@@ -671,7 +686,7 @@ function qgrid(modelId, config) {
671
686
  const text = await streamRes.text().catch(() => "");
672
687
  throw new Error(`qgrid stream ${streamRes.status}: ${text}`);
673
688
  }
674
- const textId = `text_${Math.random().toString(36).slice(2, 10)}`;
689
+ const textId = `${TEXT_STREAM_ID_PREFIX}_${Math.random().toString(36).slice(2, 10)}`;
675
690
  let textStarted = false;
676
691
  let deltaTextEmitted = false;
677
692
  return { stream: new ReadableStream({ async start(controller) {
@@ -707,10 +722,10 @@ function qgrid(modelId, config) {
707
722
  pendingToolCallIds: new Set((done.content ?? []).filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
708
723
  };
709
724
  else clientRun = null;
710
- if (sessionKey && done.runContext?.threadCoord) setThreadCoord(sessionKey, done.runContext.threadCoord);
725
+ if (reuseSessionKey && done.runContext?.threadCoord) setThreadCoord(reuseSessionKey, done.runContext.threadCoord);
711
726
  if (done.content) {
712
727
  for (const item of done.content) if (item.type === "text" && !deltaTextEmitted) {
713
- const tid = `text_${Math.random().toString(36).slice(2, 10)}`;
728
+ const tid = `${TEXT_STREAM_ID_PREFIX}_${Math.random().toString(36).slice(2, 10)}`;
714
729
  controller.enqueue({
715
730
  type: "text-start",
716
731
  id: tid
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartanova/qgrid-ai-sdk",
3
- "version": "2.3.3",
3
+ "version": "2.3.4",
4
4
  "description": "AI SDK LanguageModelV3 provider for qgrid",
5
5
  "repository": {
6
6
  "type": "git",