@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 +19 -23
- package/dist/index.js +44 -26
- package/package.json +1 -1
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
|
-
*
|
|
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
|
|
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
|
-
|
|
20
|
-
/**
|
|
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
|
-
|
|
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 ??
|
|
500
|
-
const effort = config?.defaultEffort ??
|
|
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:
|
|
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
|
|
512
|
-
const effectiveEffort =
|
|
513
|
-
const verbosity =
|
|
514
|
-
const reasoningSummary =
|
|
515
|
-
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);
|
|
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"}".
|
|
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(
|
|
546
|
+
console.warn(PENDING_TOOL_RESULTS_WARNING);
|
|
535
547
|
clientRun = null;
|
|
536
548
|
}
|
|
537
549
|
}
|
|
538
|
-
const storedCoord =
|
|
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 (
|
|
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
|
|
615
|
-
const effectiveEffort =
|
|
616
|
-
const verbosity =
|
|
617
|
-
const reasoningSummary =
|
|
618
|
-
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);
|
|
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(
|
|
651
|
+
console.warn(PENDING_TOOL_RESULTS_WARNING);
|
|
636
652
|
clientRun = null;
|
|
637
653
|
}
|
|
638
654
|
}
|
|
639
|
-
const storedCoord =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|