@cartanova/qgrid-ai-sdk 2.3.3 → 2.3.5
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 +17 -23
- package/dist/index.js +41 -26
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -9,20 +9,26 @@ type QgridProviderConfig = {
|
|
|
9
9
|
projectName?: string;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
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
|
|
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
|
-
|
|
22
|
-
/**
|
|
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
|
-
|
|
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 ??
|
|
500
|
-
const effort = config?.defaultEffort ??
|
|
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:
|
|
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
|
|
513
|
-
const effectiveEffort =
|
|
514
|
-
const verbosity =
|
|
515
|
-
const reasoningSummary =
|
|
516
|
-
const
|
|
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"}".
|
|
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(
|
|
546
|
+
console.warn(PENDING_TOOL_RESULTS_WARNING);
|
|
536
547
|
clientRun = null;
|
|
537
548
|
}
|
|
538
549
|
}
|
|
539
|
-
const storedCoord =
|
|
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 (
|
|
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
|
|
617
|
-
const effectiveEffort =
|
|
618
|
-
const verbosity =
|
|
619
|
-
const reasoningSummary =
|
|
620
|
-
const
|
|
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(
|
|
651
|
+
console.warn(PENDING_TOOL_RESULTS_WARNING);
|
|
638
652
|
clientRun = null;
|
|
639
653
|
}
|
|
640
654
|
}
|
|
641
|
-
const storedCoord =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|