@cartanova/qgrid-ai-sdk 2.1.0 → 2.2.0

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
@@ -22,11 +22,23 @@ type QgridOpenAIProviderOptions = {
22
22
  /** 응답 텍스트의 상세도. */
23
23
  textVerbosity?: "low" | "medium" | "high";
24
24
  };
25
+ /**
26
+ * codex thread 좌표
27
+ * 멀티턴 대화에서 같은 대화를 같은 codex thread 로 라우팅 → conversation_id 고정 → 캐시 적중
28
+ * provider 내부 threadCoordStore 가 sessionKey 별로 관리
29
+ */
30
+
25
31
  /**
26
32
  * {@link QgridOpenAIProviderOptions}
27
33
  */
28
34
  type QgridProviderOptions = {
29
35
  openai?: QgridOpenAIProviderOptions;
36
+ /**
37
+ * 멀티턴 대화 식별자. 호출자가 자기 도메인 ID(예: 게임 세션 ID) 하나만 넘기면, provider 가
38
+ * 같은 sessionKey 의 thread 좌표를 내부에서 회송해 prompt cache 를 적중시킨다.
39
+ * 좌표는 sessionKey 별로 격리된다.
40
+ */
41
+ sessionKey?: string;
30
42
  };
31
43
  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";
32
44
  type QgridLoggerConfig = {
package/dist/index.js CHANGED
@@ -461,6 +461,23 @@ function createQgridLogger(config = {}) {
461
461
  //#region src/index.ts
462
462
  const DEFAULT_SERVER_URL = "http://localhost:44900";
463
463
  const DEFAULT_EFFORT = "low";
464
+ const THREAD_COORD_TTL_MS = 10 * 6e4;
465
+ const threadCoordStore = /* @__PURE__ */ new Map();
466
+ function getThreadCoord(sessionKey) {
467
+ const entry = threadCoordStore.get(sessionKey);
468
+ if (!entry) return void 0;
469
+ if (entry.expiresAt <= Date.now()) {
470
+ threadCoordStore.delete(sessionKey);
471
+ return;
472
+ }
473
+ return entry.coord;
474
+ }
475
+ function setThreadCoord(sessionKey, coord) {
476
+ threadCoordStore.set(sessionKey, {
477
+ coord,
478
+ expiresAt: Date.now() + THREAD_COORD_TTL_MS
479
+ });
480
+ }
464
481
  function qgrid(modelId, config) {
465
482
  const serverUrl = config?.serverUrl ?? process.env.QGRID_URL ?? DEFAULT_SERVER_URL;
466
483
  const effort = config?.defaultEffort ?? DEFAULT_EFFORT;
@@ -478,6 +495,7 @@ function qgrid(modelId, config) {
478
495
  const effectiveEffort = openaiOpts?.reasoningEffort ?? effort;
479
496
  const verbosity = openaiOpts?.textVerbosity ?? openaiOpts?.verbosity;
480
497
  const reasoningSummary = openaiOpts?.reasoningSummary;
498
+ const sessionKey = (options.providerOptions?.qgrid)?.sessionKey;
481
499
  const rawSchema = options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0;
482
500
  const schemaType = rawSchema ? rawSchema.type : void 0;
483
501
  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.`);
@@ -500,6 +518,8 @@ function qgrid(modelId, config) {
500
518
  clientRun = null;
501
519
  }
502
520
  }
521
+ const storedCoord = sessionKey ? getThreadCoord(sessionKey) : void 0;
522
+ if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
503
523
  if (!logMode && hasTools) logMode = "run";
504
524
  const data = await fetch(`${serverUrl}/api/qgrid/query`, {
505
525
  method: "POST",
@@ -555,6 +575,7 @@ function qgrid(modelId, config) {
555
575
  pendingToolCallIds: new Set(content.filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
556
576
  };
557
577
  else clientRun = null;
578
+ if (sessionKey && data.runContext?.threadCoord) setThreadCoord(sessionKey, data.runContext.threadCoord);
558
579
  return {
559
580
  content,
560
581
  finishReason,
@@ -589,6 +610,7 @@ function qgrid(modelId, config) {
589
610
  const effectiveEffort = openaiOpts?.reasoningEffort ?? effort;
590
611
  const verbosity = openaiOpts?.textVerbosity ?? openaiOpts?.verbosity;
591
612
  const reasoningSummary = openaiOpts?.reasoningSummary;
613
+ const sessionKey = (options.providerOptions?.qgrid)?.sessionKey;
592
614
  const rawSchema = options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0;
593
615
  const jsonSchema = !hasTools && rawSchema && rawSchema.type === "object" ? JSON.stringify(rawSchema) : void 0;
594
616
  let runContext;
@@ -609,6 +631,8 @@ function qgrid(modelId, config) {
609
631
  clientRun = null;
610
632
  }
611
633
  }
634
+ const storedCoord = sessionKey ? getThreadCoord(sessionKey) : void 0;
635
+ if (!runContext && storedCoord) runContext = { threadCoord: storedCoord };
612
636
  if (!logMode && hasTools) logMode = "run";
613
637
  const prepRes = await fetch(`${serverUrl}/api/qgrid/prepareStream`, {
614
638
  method: "POST",
@@ -675,6 +699,7 @@ function qgrid(modelId, config) {
675
699
  pendingToolCallIds: new Set((done.content ?? []).filter((c) => c.type === "tool-call").map((c) => c.toolCallId))
676
700
  };
677
701
  else clientRun = null;
702
+ if (sessionKey && done.runContext?.threadCoord) setThreadCoord(sessionKey, done.runContext.threadCoord);
678
703
  if (done.content) {
679
704
  for (const item of done.content) if (item.type === "text" && !deltaTextEmitted) {
680
705
  const tid = `text_${Math.random().toString(36).slice(2, 10)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartanova/qgrid-ai-sdk",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "AI SDK LanguageModelV3 provider for qgrid",
5
5
  "repository": {
6
6
  "type": "git",