@cartanova/qgrid-ai-sdk 2.3.2 → 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
@@ -5,22 +5,30 @@ import { TelemetrySettings } from "ai";
5
5
  type QgridProviderConfig = {
6
6
  serverUrl?: string;
7
7
  defaultEffort?: string;
8
+ /** request_logs.project_name. default: process.env.QGRID_PROJECT_NAME */
9
+ projectName?: string;
8
10
  };
9
11
  /**
10
- * codex app-server가 지원하는 OpenAI provider options.
11
- *
12
- * AI SDK의 openai provider options 중 codex가 처리할 수 있는 subset만 정의.
13
- * 여기에 없는 옵션(temperature, maxOutputTokens 등)은 codex가 무시합니다.
14
- *
15
- * @see https://ai-sdk.dev/providers/ai-sdk-providers/openai#provider-options
12
+ * qgrid provider options.
13
+ * providerOptions.qgrid 로 전달한다.
16
14
  */
17
- type QgridOpenAIProviderOptions = {
15
+ type QgridProviderOptions = {
16
+ /**
17
+ * 멀티턴 시 codex thread reuse를 위한 대화 식별자, 호출자가 자기 도메인 ID(예: 게임 세션 ID) 하나만 넘기면
18
+ * provider가 같은 sessionKey 의 thread 좌표를 내부에서 회송해 prompt cache 를 적중시킨다
19
+ * 좌표는 sessionKey 별로 격리된다.
20
+ */
21
+ sessionKey?: string;
18
22
  /** reasoning 모델의 추론 깊이. 기본값은 qgrid config의 defaultEffort. */
19
- reasoningEffort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
20
- /** 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에서만 적용된다. */
21
27
  reasoningSummary?: "auto" | "concise" | "detailed" | "none";
22
- /** 응답 텍스트의 상세도. */
23
- textVerbosity?: "low" | "medium" | "high";
28
+ /** OpenAI/codex service tier. OpenAI/codex route에서만 적용된다. */
29
+ serviceTier?: string;
30
+ /** @todo 향후 qgrid 서버 fallback routing에 사용할 후보 모델 목록. */
31
+ fallbackModels?: string[];
24
32
  };
25
33
  /**
26
34
  * codex thread 좌표
@@ -28,18 +36,6 @@ type QgridOpenAIProviderOptions = {
28
36
  * provider 내부 threadCoordStore 가 sessionKey 별로 관리
29
37
  */
30
38
 
31
- /**
32
- * {@link QgridOpenAIProviderOptions}
33
- */
34
- type QgridProviderOptions = {
35
- openai?: QgridOpenAIProviderOptions;
36
- /**
37
- * 멀티턴 대화 식별자. 호출자가 자기 도메인 ID(예: 게임 세션 ID) 하나만 넘기면, provider 가
38
- * 같은 sessionKey 의 thread 좌표를 내부에서 회송해 prompt cache 를 적중시킨다.
39
- * 좌표는 sessionKey 별로 격리된다.
40
- */
41
- sessionKey?: string;
42
- };
43
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";
44
40
  type QgridLoggerConfig = {
45
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,26 +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";
510
+ const projectName = config?.projectName ?? process.env.QGRID_PROJECT_NAME;
501
511
  let clientRun = null;
502
512
  return {
503
513
  specificationVersion: "v3",
504
- provider: "qgrid",
514
+ provider: QGRID_PROVIDER_NAME,
505
515
  modelId,
506
516
  supportedUrls: {},
507
517
  async doGenerate(options) {
508
518
  const tools = options.tools?.filter((t) => t.type === "function");
509
519
  const hasTools = tools && tools.length > 0;
510
520
  const { prompt, system, history } = extractPromptAndHistory(options.prompt);
511
- const openaiOpts = options.providerOptions?.openai;
512
- const effectiveEffort = openaiOpts?.reasoningEffort ?? effort;
513
- const verbosity = openaiOpts?.textVerbosity ?? openaiOpts?.verbosity;
514
- const reasoningSummary = openaiOpts?.reasoningSummary;
515
- 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);
516
528
  const rawSchema = options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0;
517
529
  const schemaType = rawSchema ? rawSchema.type : void 0;
518
- 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}`);
519
531
  const jsonSchema = !hasTools && rawSchema && schemaType === "object" ? JSON.stringify(rawSchema) : void 0;
520
532
  let runContext;
521
533
  let toolResultsPayload;
@@ -531,11 +543,11 @@ function qgrid(modelId, config) {
531
543
  }));
532
544
  logMode = "run";
533
545
  } else {
534
- console.warn("[qgrid] pending tool results not found in prompt, clearing client run state");
546
+ console.warn(PENDING_TOOL_RESULTS_WARNING);
535
547
  clientRun = null;
536
548
  }
537
549
  }
538
- const storedCoord = sessionKey ? getThreadCoord(sessionKey) : void 0;
550
+ const storedCoord = reuseSessionKey ? getThreadCoord(reuseSessionKey) : void 0;
539
551
  if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
540
552
  if (!logMode && hasTools) logMode = "run";
541
553
  const data = await fetch(`${serverUrl}/api/qgrid/query`, {
@@ -548,9 +560,11 @@ function qgrid(modelId, config) {
548
560
  effort: effectiveEffort,
549
561
  ...verbosity ? { verbosity } : {},
550
562
  ...reasoningSummary ? { reasoningSummary } : {},
563
+ ...serviceTier ? { serviceTier } : {},
551
564
  ...hasTools ? { tools: tools.map(toQgridTool) } : {},
552
565
  ...jsonSchema ? { jsonSchema } : {},
553
566
  ...history.length > 0 ? { history: JSON.stringify(history) } : {},
567
+ ...projectName ? { projectName } : {},
554
568
  ...logMode ? { logMode } : {},
555
569
  ...runContext ? { runContext } : {},
556
570
  ...toolResultsPayload ? { toolResults: toolResultsPayload } : {}
@@ -592,7 +606,7 @@ function qgrid(modelId, config) {
592
606
  pendingToolCallIds: new Set(content.filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
593
607
  };
594
608
  else clientRun = null;
595
- if (sessionKey && data.runContext?.threadCoord) setThreadCoord(sessionKey, data.runContext.threadCoord);
609
+ if (reuseSessionKey && data.runContext?.threadCoord) setThreadCoord(reuseSessionKey, data.runContext.threadCoord);
596
610
  return {
597
611
  content,
598
612
  finishReason,
@@ -611,11 +625,13 @@ function qgrid(modelId, config) {
611
625
  const tools = options.tools?.filter((t) => t.type === "function");
612
626
  const hasTools = tools && tools.length > 0;
613
627
  const { prompt, system, history } = extractPromptAndHistory(options.prompt);
614
- const openaiOpts = options.providerOptions?.openai;
615
- const effectiveEffort = openaiOpts?.reasoningEffort ?? effort;
616
- const verbosity = openaiOpts?.textVerbosity ?? openaiOpts?.verbosity;
617
- const reasoningSummary = openaiOpts?.reasoningSummary;
618
- 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);
619
635
  const rawSchema = options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0;
620
636
  const jsonSchema = !hasTools && rawSchema && rawSchema.type === "object" ? JSON.stringify(rawSchema) : void 0;
621
637
  let runContext;
@@ -632,11 +648,11 @@ function qgrid(modelId, config) {
632
648
  }));
633
649
  logMode = "run";
634
650
  } else {
635
- console.warn("[qgrid] pending tool results not found in prompt, clearing client run state");
651
+ console.warn(PENDING_TOOL_RESULTS_WARNING);
636
652
  clientRun = null;
637
653
  }
638
654
  }
639
- const storedCoord = sessionKey ? getThreadCoord(sessionKey) : void 0;
655
+ const storedCoord = reuseSessionKey ? getThreadCoord(reuseSessionKey) : void 0;
640
656
  if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
641
657
  if (!logMode && hasTools) logMode = "run";
642
658
  const prepRes = await fetch(`${serverUrl}/api/qgrid/prepareStream`, {
@@ -649,9 +665,11 @@ function qgrid(modelId, config) {
649
665
  effort: effectiveEffort,
650
666
  ...verbosity ? { verbosity } : {},
651
667
  ...reasoningSummary ? { reasoningSummary } : {},
668
+ ...serviceTier ? { serviceTier } : {},
652
669
  ...hasTools ? { tools: tools.map(toQgridTool) } : {},
653
670
  ...jsonSchema ? { jsonSchema } : {},
654
671
  ...history.length > 0 ? { history: JSON.stringify(history) } : {},
672
+ ...projectName ? { projectName } : {},
655
673
  ...logMode ? { logMode } : {},
656
674
  ...runContext ? { runContext } : {},
657
675
  ...toolResultsPayload ? { toolResults: toolResultsPayload } : {}
@@ -668,7 +686,7 @@ function qgrid(modelId, config) {
668
686
  const text = await streamRes.text().catch(() => "");
669
687
  throw new Error(`qgrid stream ${streamRes.status}: ${text}`);
670
688
  }
671
- const textId = `text_${Math.random().toString(36).slice(2, 10)}`;
689
+ const textId = `${TEXT_STREAM_ID_PREFIX}_${Math.random().toString(36).slice(2, 10)}`;
672
690
  let textStarted = false;
673
691
  let deltaTextEmitted = false;
674
692
  return { stream: new ReadableStream({ async start(controller) {
@@ -704,10 +722,10 @@ function qgrid(modelId, config) {
704
722
  pendingToolCallIds: new Set((done.content ?? []).filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
705
723
  };
706
724
  else clientRun = null;
707
- if (sessionKey && done.runContext?.threadCoord) setThreadCoord(sessionKey, done.runContext.threadCoord);
725
+ if (reuseSessionKey && done.runContext?.threadCoord) setThreadCoord(reuseSessionKey, done.runContext.threadCoord);
708
726
  if (done.content) {
709
727
  for (const item of done.content) if (item.type === "text" && !deltaTextEmitted) {
710
- const tid = `text_${Math.random().toString(36).slice(2, 10)}`;
728
+ const tid = `${TEXT_STREAM_ID_PREFIX}_${Math.random().toString(36).slice(2, 10)}`;
711
729
  controller.enqueue({
712
730
  type: "text-start",
713
731
  id: tid
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartanova/qgrid-ai-sdk",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
4
4
  "description": "AI SDK LanguageModelV3 provider for qgrid",
5
5
  "repository": {
6
6
  "type": "git",