@cartanova/qgrid-ai-sdk 0.1.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/README.md +250 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +750 -0
- package/e2e/e2e-logger.ts +112 -0
- package/e2e/e2e.ts +217 -0
- package/package.json +31 -0
- package/src/index.test.ts +338 -0
- package/src/index.ts +396 -0
- package/src/index.types.ts +131 -0
- package/src/logger.test.ts +563 -0
- package/src/logger.ts +364 -0
- package/src/utils.ts +305 -0
- package/tsconfig.json +15 -0
- package/tsdown.config.ts +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# @cartanova/qgrid-ai-sdk
|
|
2
|
+
|
|
3
|
+
AI SDK v6 custom `LanguageModelV3` provider for [qgrid](https://github.com/cartanova-ai/Qgrid).
|
|
4
|
+
|
|
5
|
+
**기존 AI SDK 코드 변경 없이, `model` 한 줄만 바꾸면 qgrid를 통한 구독 토큰으로 N개(토큰개수) 병렬 풀링 + request log 대시보드를 사용가능**
|
|
6
|
+
|
|
7
|
+
```diff
|
|
8
|
+
import { generateText } from "ai";
|
|
9
|
+
-import { openai } from "@ai-sdk/openai";
|
|
10
|
+
+import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
11
|
+
|
|
12
|
+
const { text } = await generateText({
|
|
13
|
+
- model: openai("gpt-5.4-mini"),
|
|
14
|
+
+ model: qgrid("openai/gpt-5.4-mini"),
|
|
15
|
+
prompt: "서울 날씨 알려줘",
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
이미 다른 provider(google, openai 등)를 직접 사용하고 있다면, **logger 옵션 한 줄**만 추가하면 에이전트의 매 step(generate, tool-call, reasoning)을 qgrid 대시보드에서 확인할 수 있습니다.
|
|
20
|
+
|
|
21
|
+
```diff
|
|
22
|
+
const { text } = await generateText({
|
|
23
|
+
model: google("gemini-3-flash"),
|
|
24
|
+
prompt: "복잡한 질문",
|
|
25
|
+
+ experimental_telemetry: createQgridLogger({ serverUrl: "http://localhost:44900" }),
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 설치
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm add @cartanova/qgrid-ai-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Peer dependencies: `ai@^6.0.0`, `@ai-sdk/provider@^3.0.0`
|
|
36
|
+
|
|
37
|
+
## 빠른 시작
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { generateText } from "ai";
|
|
41
|
+
import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
42
|
+
|
|
43
|
+
const { text } = await generateText({
|
|
44
|
+
model: qgrid("openai/gpt-5.4-mini"),
|
|
45
|
+
prompt: "서울 날씨 알려줘",
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
qgrid 서버(`http://localhost:44900`)가 실행 중이어야 합니다.
|
|
50
|
+
|
|
51
|
+
## 사용법
|
|
52
|
+
> 들어가기전에: 모든 클라언트 사용법은 [AI-SDK](https://ai-sdk.dev/docs/ai-sdk-core)와 동일합니다.
|
|
53
|
+
|
|
54
|
+
### 텍스트 생성
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { generateText } from "ai";
|
|
58
|
+
import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
59
|
+
|
|
60
|
+
const { text } = await generateText({
|
|
61
|
+
model: qgrid("openai/gpt-5.4-mini"),
|
|
62
|
+
system: "당신은 학술 논문 요약가입니다.",
|
|
63
|
+
prompt: paperText,
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 구조화 응답 (Structured Output)
|
|
68
|
+
> [AI-SDK structured output guide를 참조하세요](https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data)
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { generateText, Output } from "ai";
|
|
72
|
+
import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
73
|
+
import { z } from "zod";
|
|
74
|
+
|
|
75
|
+
const { output } = await generateText({
|
|
76
|
+
model: qgrid("openai/gpt-5.4"),
|
|
77
|
+
system: "논문 메타데이터를 추출해주세요.",
|
|
78
|
+
prompt: paperText,
|
|
79
|
+
output: Output.object({
|
|
80
|
+
schema: z.object({
|
|
81
|
+
title: z.string(),
|
|
82
|
+
authors: z.array(z.string()),
|
|
83
|
+
keyFindings: z.array(z.string()),
|
|
84
|
+
}),
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
console.log(output.title, output.authors);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
top-level이 `object`인 schema는 OpenAI structured output으로 서버에 전달됩니다 (임의로 타입 지정 시 파싱 에러)
|
|
92
|
+
top-level이 AI-SDK의 가이드를 따르지않으면 AI SDK 클라이언트 파싱으로 fallback되며 경고 로그가 출력됩니다.
|
|
93
|
+
|
|
94
|
+
### 스트리밍
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { streamText } from "ai";
|
|
98
|
+
import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
99
|
+
|
|
100
|
+
const { textStream } = streamText({
|
|
101
|
+
model: qgrid("openai/gpt-5.4-mini"),
|
|
102
|
+
prompt: "TypeScript의 장점을 설명해줘",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
for await (const chunk of textStream) {
|
|
106
|
+
process.stdout.write(chunk);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Tool Calling
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { generateText, tool } from "ai";
|
|
114
|
+
import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
115
|
+
import { z } from "zod";
|
|
116
|
+
|
|
117
|
+
const { text } = await generateText({
|
|
118
|
+
model: qgrid("openai/gpt-5.4-mini"),
|
|
119
|
+
prompt: "서울 날씨 알려줘",
|
|
120
|
+
tools: {
|
|
121
|
+
getWeather: tool({
|
|
122
|
+
description: "도시의 현재 날씨 조회",
|
|
123
|
+
parameters: z.object({ city: z.string() }),
|
|
124
|
+
execute: async ({ city }) => {
|
|
125
|
+
return { temperature: 22, condition: "맑음" };
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
tool-call은 qgrid 서버의 structured output emulation으로 동작합니다.
|
|
133
|
+
AI SDK가 tool 실행을 관리하고, qgrid는 각 턴의 LLM 호출만 담당합니다.
|
|
134
|
+
|
|
135
|
+
### Provider Options
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { generateText } from "ai";
|
|
139
|
+
import { qgrid, type QgridProviderOptions } from "@cartanova/qgrid-ai-sdk";
|
|
140
|
+
|
|
141
|
+
const providerOptions = {
|
|
142
|
+
openai: {
|
|
143
|
+
reasoningEffort: "high",
|
|
144
|
+
reasoningSummary: "concise",
|
|
145
|
+
textVerbosity: "medium",
|
|
146
|
+
},
|
|
147
|
+
} satisfies QgridProviderOptions;
|
|
148
|
+
|
|
149
|
+
const { text } = await generateText({
|
|
150
|
+
model: qgrid("openai/gpt-5.4"),
|
|
151
|
+
prompt: "복잡한 문제를 분석해줘",
|
|
152
|
+
providerOptions,
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| 옵션 | 값 | 설명 |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `reasoningEffort` | `"none"` \| `"minimal"` \| `"low"` \| `"medium"` \| `"high"` \| `"xhigh"` | reasoning 모델의 추론 깊이 |
|
|
159
|
+
| `reasoningSummary` | `"auto"` \| `"concise"` \| `"detailed"` \| `"none"` | 추론 요약 출력 방식 (Responses API 전용) |
|
|
160
|
+
| `textVerbosity` | `"low"` \| `"medium"` \| `"high"` | 응답 텍스트의 상세도 |
|
|
161
|
+
|
|
162
|
+
codex app-server가 지원하는 옵션만 포함되어 있습니다. `temperature`, `maxOutputTokens` 등은 codex가 무시합니다.
|
|
163
|
+
|
|
164
|
+
## Telemetry Logger
|
|
165
|
+
|
|
166
|
+
qgrid provider가 아닌 모델(google, openai 직접 호출)에서도 같은 request log 대시보드를 사용하려면 `createQgridLogger`를 `experimental_telemetry`에 넣으면 됩니다.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { generateText } from "ai";
|
|
170
|
+
import { google } from "@ai-sdk/google";
|
|
171
|
+
import { createQgridLogger } from "@cartanova/qgrid-ai-sdk";
|
|
172
|
+
|
|
173
|
+
const { text } = await generateText({
|
|
174
|
+
model: google("gemini-3-flash"),
|
|
175
|
+
prompt: "안녕하세요",
|
|
176
|
+
experimental_telemetry: createQgridLogger({
|
|
177
|
+
serverUrl: "http://localhost:44900",
|
|
178
|
+
}),
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
병렬 호출, run 분리, telemetry 활성화 등은 자동 처리됩니다.
|
|
183
|
+
|
|
184
|
+
### Logger 설정
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
createQgridLogger({
|
|
188
|
+
serverUrl: string; // qgrid 서버 주소 (필수)
|
|
189
|
+
projectName?: string; // request_logs.project_name
|
|
190
|
+
tokenName?: string; // request_logs.token_name (기본: "external")
|
|
191
|
+
staleRunTimeoutMs?: number; // watchdog timeout (기본: 30분, 0으로 비활성화)
|
|
192
|
+
onLogError?: (error: Error) => void; // 로깅 실패 콜백
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
모든 설정은 optional (serverUrl 제외). 기본값이 있으므로 `serverUrl`만 넣으면 동작합니다.
|
|
197
|
+
|
|
198
|
+
### qgrid provider와 함께 사용
|
|
199
|
+
|
|
200
|
+
`qgrid()` provider는 자체 lifecycle이 있으므로 logger가 자동으로 suppress됩니다. 같은 코드에서 qgrid provider와 다른 provider를 섞어 써도 이중 기록되지 않습니다.
|
|
201
|
+
|
|
202
|
+
## 지원 모델
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
type QgridSupportedModel =
|
|
206
|
+
// OpenAI (based on codex app-server)
|
|
207
|
+
| "openai/gpt-5.5"
|
|
208
|
+
| "openai/gpt-5.4"
|
|
209
|
+
| "openai/gpt-5.2"
|
|
210
|
+
| "openai/gpt-5.4-mini"
|
|
211
|
+
| "openai/gpt-5.3-codex"
|
|
212
|
+
| "openai/gpt-5.3-codex-spark"
|
|
213
|
+
// Anthropic
|
|
214
|
+
| "anthropic/claude-haiku-4-5"
|
|
215
|
+
| "anthropic/claude-sonnet-4"
|
|
216
|
+
| "anthropic/claude-sonnet-4-5"
|
|
217
|
+
| "anthropic/claude-sonnet-4-6"
|
|
218
|
+
| "anthropic/claude-sonnet-4-7"
|
|
219
|
+
| "anthropic/claude-opus-4"
|
|
220
|
+
| "anthropic/claude-opus-4-1"
|
|
221
|
+
| "anthropic/claude-opus-4-5"
|
|
222
|
+
| "anthropic/claude-opus-4-6"
|
|
223
|
+
| "anthropic/claude-opus-4-7"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## 설정
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
qgrid(modelId, {
|
|
230
|
+
serverUrl?: string; // qgrid 서버 주소 (기본: QGRID_URL 환경변수 또는 http://localhost:44900)
|
|
231
|
+
defaultEffort?: string; // reasoningEffort 기본값 (기본: "low")
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 환경변수
|
|
236
|
+
|
|
237
|
+
| 변수 | 설명 | 기본값 |
|
|
238
|
+
|---|---|---|
|
|
239
|
+
| `QGRID_URL` | qgrid 서버 주소 | `http://localhost:44900` |
|
|
240
|
+
|
|
241
|
+
## 주의사항
|
|
242
|
+
|
|
243
|
+
- `temperature`, `maxOutputTokens` 등 sampling 파라미터는 codex app-server가 지원하지 않아 무시됩니다.
|
|
244
|
+
- Structured output은 top-level `object` schema만 지원합니다. top-level `array`는 클라이언트 파싱 fallback.
|
|
245
|
+
|
|
246
|
+
## 요구사항
|
|
247
|
+
|
|
248
|
+
- Node.js >= 20
|
|
249
|
+
- AI SDK v6 (`ai@^6.0.0`)
|
|
250
|
+
- 실행 중인 qgrid 서버
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { LanguageModelV3 } from "@ai-sdk/provider";
|
|
2
|
+
import { TelemetrySettings } from "ai";
|
|
3
|
+
|
|
4
|
+
//#region src/index.types.d.ts
|
|
5
|
+
type QgridProviderConfig = {
|
|
6
|
+
serverUrl?: string;
|
|
7
|
+
defaultEffort?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
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
|
|
16
|
+
*/
|
|
17
|
+
type QgridOpenAIProviderOptions = {
|
|
18
|
+
/** reasoning 모델의 추론 깊이. 기본값은 qgrid config의 defaultEffort. */
|
|
19
|
+
reasoningEffort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
20
|
+
/** reasoning 모델의 추론 요약 출력 방식. Responses API 전용. */
|
|
21
|
+
reasoningSummary?: "auto" | "concise" | "detailed" | "none";
|
|
22
|
+
/** 응답 텍스트의 상세도. */
|
|
23
|
+
textVerbosity?: "low" | "medium" | "high";
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* {@link QgridOpenAIProviderOptions}
|
|
27
|
+
*/
|
|
28
|
+
type QgridProviderOptions = {
|
|
29
|
+
openai?: QgridOpenAIProviderOptions;
|
|
30
|
+
};
|
|
31
|
+
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";
|
|
32
|
+
type QgridLoggerConfig = {
|
|
33
|
+
serverUrl: string;
|
|
34
|
+
projectName?: string;
|
|
35
|
+
tokenName?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Fallback timeout for runs that receive onStart but never receive onFinish.
|
|
38
|
+
* AI SDK TelemetryIntegration does not expose an error hook, so provider
|
|
39
|
+
* failures before a final step can otherwise leave request logs running.
|
|
40
|
+
*
|
|
41
|
+
* Set to 0 to disable. Defaults to 30 minutes, or the AI SDK total timeout
|
|
42
|
+
* plus a short grace period when one is provided.
|
|
43
|
+
*/
|
|
44
|
+
staleRunTimeoutMs?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Receives qgrid logging failures. When reusing one logger integration across
|
|
47
|
+
* overlapping AI SDK calls, pass a unique `metadata.qgridRunId` per call so
|
|
48
|
+
* lifecycle events can be attributed to the correct run.
|
|
49
|
+
*/
|
|
50
|
+
onLogError?: (error: Error) => void;
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/logger.d.ts
|
|
54
|
+
declare function createQgridLogger(config: QgridLoggerConfig): TelemetrySettings;
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/index.d.ts
|
|
57
|
+
declare function qgrid(modelId: QgridSupportedModel, config?: QgridProviderConfig): LanguageModelV3;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { type QgridLoggerConfig, type QgridProviderOptions, createQgridLogger, qgrid as default, qgrid };
|