@taco_tsinghua/graphnode-sdk 0.1.17 → 0.1.21
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 +91 -333
- package/dist/client.d.ts +24 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +34 -8
- package/dist/client.js.map +1 -1
- package/dist/endpoints/ai.d.ts +25 -30
- package/dist/endpoints/ai.d.ts.map +1 -1
- package/dist/endpoints/ai.js +220 -31
- package/dist/endpoints/ai.js.map +1 -1
- package/dist/endpoints/graph.d.ts +4 -1
- package/dist/endpoints/graph.d.ts.map +1 -1
- package/dist/endpoints/graph.js +10 -0
- package/dist/endpoints/graph.js.map +1 -1
- package/dist/endpoints/graphAi.d.ts +21 -0
- package/dist/endpoints/graphAi.d.ts.map +1 -1
- package/dist/endpoints/graphAi.js +24 -0
- package/dist/endpoints/graphAi.js.map +1 -1
- package/dist/endpoints/notification.d.ts +13 -0
- package/dist/endpoints/notification.d.ts.map +1 -1
- package/dist/endpoints/notification.js +17 -0
- package/dist/endpoints/notification.js.map +1 -1
- package/dist/endpoints/sync.d.ts +3 -1
- package/dist/endpoints/sync.d.ts.map +1 -1
- package/dist/endpoints/sync.js.map +1 -1
- package/dist/http-builder.d.ts +38 -0
- package/dist/http-builder.d.ts.map +1 -1
- package/dist/http-builder.js +43 -6
- package/dist/http-builder.js.map +1 -1
- package/dist/types/graph.d.ts +66 -0
- package/dist/types/graph.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/client.ts +140 -0
- package/src/config.ts +9 -0
- package/src/endpoints/agent.ts +171 -0
- package/src/endpoints/ai.ts +296 -0
- package/src/endpoints/auth.apple.ts +39 -0
- package/src/endpoints/auth.google.ts +39 -0
- package/src/endpoints/conversations.ts +362 -0
- package/src/endpoints/graph.ts +398 -0
- package/src/endpoints/graphAi.ts +111 -0
- package/src/endpoints/health.ts +40 -0
- package/src/endpoints/me.ts +97 -0
- package/src/endpoints/note.ts +351 -0
- package/src/endpoints/notification.ts +69 -0
- package/src/endpoints/sync.ts +71 -0
- package/src/http-builder.ts +290 -0
- package/src/index.ts +60 -0
- package/src/types/aiInput.ts +111 -0
- package/src/types/conversation.ts +51 -0
- package/src/types/graph.ts +201 -0
- package/src/types/graphAi.ts +21 -0
- package/src/types/me.ts +49 -0
- package/src/types/message.ts +40 -0
- package/src/types/note.ts +89 -0
- package/src/types/problem.ts +22 -0
- package/src/types/sync.ts +35 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Request Builder (fluent)
|
|
3
|
+
* - baseUrl을 내부에 보관하고, 외부에서는 경로(path)만 넘겨 호출할 수 있게 한다.
|
|
4
|
+
* - 기본적으로 credentials: 'include' 로 세션 쿠키를 전송한다.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
8
|
+
|
|
9
|
+
export interface BuilderOptions {
|
|
10
|
+
baseUrl: string; // e.g., https://api.example.com
|
|
11
|
+
fetch?: FetchLike; // Node<18 환경 등에서 주입 가능
|
|
12
|
+
defaultHeaders?: Record<string, string>;
|
|
13
|
+
credentials?: RequestCredentials; // default 'include'HttpError
|
|
14
|
+
accessToken?: string | (() => string | null);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// export class HttpError<TBody = unknown> extends Error {
|
|
18
|
+
// status: number;
|
|
19
|
+
// body?: TBody;
|
|
20
|
+
// constructor(message: string, status: number, body?: TBody) {
|
|
21
|
+
// super(message);
|
|
22
|
+
// this.name = 'HttpError';
|
|
23
|
+
// this.status = status;
|
|
24
|
+
// this.body = body;
|
|
25
|
+
// }
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
export type HttpResponseSuccess<T> = {
|
|
29
|
+
isSuccess: true;
|
|
30
|
+
data: T;
|
|
31
|
+
statusCode: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type HttpResponseError = {
|
|
35
|
+
isSuccess: false;
|
|
36
|
+
error: {
|
|
37
|
+
statusCode: number;
|
|
38
|
+
message: string;
|
|
39
|
+
body?: unknown;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type HttpResponse<T> = HttpResponseSuccess<T> | HttpResponseError;
|
|
44
|
+
|
|
45
|
+
export class RequestBuilder {
|
|
46
|
+
/**
|
|
47
|
+
* API 기본 URL (마지막 슬래시 제거됨)
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
private readonly baseUrl: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 실제 HTTP 요청을 수행할 fetch 함수 구현체
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
private readonly fetchImpl: FetchLike;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 모든 요청에 포함될 기본 HTTP 헤더 (예: Accept: application/json)
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
private readonly headers: Record<string, string>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 자격 증명(쿠키) 전송 모드 ('include' | 'omit' | 'same-origin')
|
|
66
|
+
* - 'include'로 설정 시 브라우저가 자동으로 쿠키를 전송합니다.
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
private readonly credentials: RequestCredentials;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Access Token을 동적으로 반환하는 함수 또는 정적 문자열.
|
|
73
|
+
* - 함수로 설정된 경우 요청 시점의 최신 토큰을 조회하여 Authorization 헤더에 사용합니다.
|
|
74
|
+
* - 값이 없으면(undefined) Authorization 헤더를 추가하지 않습니다 (쿠키 인증 의존).
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
private readonly accessToken?: string | (() => string | null);
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 현재 빌더가 가지고 있는 URL 경로 조각들
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
private readonly segments: string[];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 현재 빌더가 가지고 있는 쿼리 파라미터들
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
private readonly queryParams: URLSearchParams;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* RequestBuilder 생성자
|
|
93
|
+
* @param opts 빌더 공통 옵션 (baseUrl, fetch, headers 등)
|
|
94
|
+
* @param segments 초기 URL 경로 조각 리스트
|
|
95
|
+
* @param query 초기 쿼리 파라미터
|
|
96
|
+
*/
|
|
97
|
+
constructor(opts: BuilderOptions, segments: string[] = [], query?: URLSearchParams) {
|
|
98
|
+
this.baseUrl = opts.baseUrl.replace(/\/$/, '');
|
|
99
|
+
this.fetchImpl = opts.fetch ?? (globalThis.fetch as FetchLike);
|
|
100
|
+
this.headers = { Accept: 'application/json', ...(opts.defaultHeaders ?? {}) };
|
|
101
|
+
this.credentials = opts.credentials ?? 'include';
|
|
102
|
+
this.accessToken = opts.accessToken;
|
|
103
|
+
this.segments = segments;
|
|
104
|
+
this.queryParams = query ?? new URLSearchParams();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 경로 조각을 추가한다. '/v1/me' 같은 절대 경로도 허용한다.
|
|
109
|
+
* @internal SDK 내부에서만 사용된다. FE에서는 직접 호출하지 말 것.
|
|
110
|
+
*/
|
|
111
|
+
path(p: string): RequestBuilder {
|
|
112
|
+
if (!p) return this;
|
|
113
|
+
if (p.startsWith('http://') || p.startsWith('https://')) {
|
|
114
|
+
// 절대 URL을 넣으면 baseUrl을 무시하고 해당 URL 전체를 하나의 세그먼트로 취급
|
|
115
|
+
return new RequestBuilder(
|
|
116
|
+
{
|
|
117
|
+
baseUrl: p,
|
|
118
|
+
fetch: this.fetchImpl,
|
|
119
|
+
defaultHeaders: this.headers,
|
|
120
|
+
credentials: this.credentials,
|
|
121
|
+
accessToken: this.accessToken,
|
|
122
|
+
},
|
|
123
|
+
[],
|
|
124
|
+
new URLSearchParams(this.queryParams)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const segs = p.split('/').filter(Boolean);
|
|
128
|
+
return new RequestBuilder(
|
|
129
|
+
{
|
|
130
|
+
baseUrl: this.baseUrl,
|
|
131
|
+
fetch: this.fetchImpl,
|
|
132
|
+
defaultHeaders: this.headers,
|
|
133
|
+
credentials: this.credentials,
|
|
134
|
+
},
|
|
135
|
+
[...this.segments, ...segs],
|
|
136
|
+
new URLSearchParams(this.queryParams)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 쿼리 파라미터를 추가한다.
|
|
142
|
+
* @internal SDK 내부에서만 사용된다. FE에서는 직접 호출하지 말 것.
|
|
143
|
+
*/
|
|
144
|
+
query(params?: Record<string, unknown>): RequestBuilder {
|
|
145
|
+
if (!params) return this;
|
|
146
|
+
const q = new URLSearchParams(this.queryParams);
|
|
147
|
+
for (const [k, v] of Object.entries(params)) {
|
|
148
|
+
if (v === undefined || v === null) continue;
|
|
149
|
+
q.set(k, String(v));
|
|
150
|
+
}
|
|
151
|
+
return new RequestBuilder(
|
|
152
|
+
{
|
|
153
|
+
baseUrl: this.baseUrl,
|
|
154
|
+
fetch: this.fetchImpl,
|
|
155
|
+
defaultHeaders: this.headers,
|
|
156
|
+
credentials: this.credentials,
|
|
157
|
+
},
|
|
158
|
+
[...this.segments],
|
|
159
|
+
q
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async get<T>(): Promise<HttpResponse<T>> {
|
|
164
|
+
return this.send<T>('GET');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async post<T>(body?: unknown): Promise<HttpResponse<T>> {
|
|
168
|
+
return this.send<T>('POST', body);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async patch<T>(body?: unknown): Promise<HttpResponse<T>> {
|
|
172
|
+
return this.send<T>('PATCH', body);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async delete<T>(body?: unknown): Promise<HttpResponse<T>> {
|
|
176
|
+
return this.send<T>('DELETE', body);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public url(): string {
|
|
180
|
+
const path = this.segments.length ? '/' + this.segments.map(encodeURIComponent).join('/') : '';
|
|
181
|
+
const qs = this.queryParams.toString();
|
|
182
|
+
return this.baseUrl + path + (qs ? `?${qs}` : '');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async sendRaw(
|
|
186
|
+
method: string,
|
|
187
|
+
body?: unknown,
|
|
188
|
+
extraHeaders?: Record<string, string>
|
|
189
|
+
): Promise<Response> {
|
|
190
|
+
const headers = { ...this.headers, ...extraHeaders } as Record<string, string>;
|
|
191
|
+
|
|
192
|
+
// Access Token 주입
|
|
193
|
+
if (this.accessToken) {
|
|
194
|
+
const token = typeof this.accessToken === 'function' ? this.accessToken() : this.accessToken;
|
|
195
|
+
if (token) {
|
|
196
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const init: RequestInit = { method, headers, credentials: this.credentials };
|
|
201
|
+
if (body !== undefined) {
|
|
202
|
+
if (typeof FormData !== 'undefined' && body instanceof FormData) {
|
|
203
|
+
init.body = body;
|
|
204
|
+
} else {
|
|
205
|
+
headers['Content-Type'] = 'application/json';
|
|
206
|
+
init.body = JSON.stringify(body);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const makeRequest = async () => this.fetchImpl(this.url(), init);
|
|
211
|
+
let res = await makeRequest();
|
|
212
|
+
|
|
213
|
+
// 401 Unauthorized 발생 시 Refresh Token으로 갱신 시도
|
|
214
|
+
if (res.status === 401) {
|
|
215
|
+
try {
|
|
216
|
+
// Refresh API 호출 (http-builder 내부 로직 재사용 방지 위해 fetchImpl 직접 사용)
|
|
217
|
+
const refreshUrl = `${this.baseUrl}/auth/refresh`;
|
|
218
|
+
const refreshRes = await this.fetchImpl(refreshUrl, {
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: { 'Content-Type': 'application/json' },
|
|
221
|
+
credentials: 'include', // 쿠키 전송 필수
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (refreshRes.ok) {
|
|
225
|
+
// 갱신 성공 시, 원래 요청 재시도
|
|
226
|
+
// (브라우저가 새 Access Token 쿠키를 자동으로 저장했으므로, 재요청 시 새 토큰이 나감)
|
|
227
|
+
res = await makeRequest();
|
|
228
|
+
}
|
|
229
|
+
} catch (e) {
|
|
230
|
+
// Refresh 실패 시, 원래의 401 응답을 그대로 반환 (또는 로깅)
|
|
231
|
+
console.error('Auto-refresh failed:', e);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return res;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private async send<T>(method: string, body?: unknown): Promise<HttpResponse<T>> {
|
|
239
|
+
try {
|
|
240
|
+
const res = await this.sendRaw(method, body);
|
|
241
|
+
const ct = res.headers.get('content-type') || '';
|
|
242
|
+
const isJson = ct.includes('application/json') || ct.includes('application/problem+json');
|
|
243
|
+
|
|
244
|
+
const isNoContent =
|
|
245
|
+
res.status === 204 || res.status === 205 || res.headers.get('content-length') === '0';
|
|
246
|
+
|
|
247
|
+
let payload: unknown = undefined;
|
|
248
|
+
|
|
249
|
+
if (!isNoContent) {
|
|
250
|
+
if (isJson) {
|
|
251
|
+
payload = await res.json();
|
|
252
|
+
} else {
|
|
253
|
+
payload = await res.text();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!res.ok) {
|
|
258
|
+
return {
|
|
259
|
+
isSuccess: false,
|
|
260
|
+
error: {
|
|
261
|
+
statusCode: res.status,
|
|
262
|
+
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
263
|
+
body: payload,
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
isSuccess: true,
|
|
269
|
+
statusCode: res.status,
|
|
270
|
+
data: payload as T,
|
|
271
|
+
};
|
|
272
|
+
} catch (e) {
|
|
273
|
+
const err = e as Error;
|
|
274
|
+
return {
|
|
275
|
+
isSuccess: false,
|
|
276
|
+
error: {
|
|
277
|
+
statusCode: 0,
|
|
278
|
+
message: err.message,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 외부에서 사용: createRequestBuilder({ baseUrl }).path('/v1/me').get()
|
|
287
|
+
*/
|
|
288
|
+
export function createRequestBuilder(opts: BuilderOptions): RequestBuilder {
|
|
289
|
+
return new RequestBuilder(opts);
|
|
290
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphNode SDK Entry Point
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Barrel exports: 공개 API만 노출
|
|
7
|
+
export { createGraphNodeClient, GraphNodeClient } from './client.js';
|
|
8
|
+
// 내부 전송 레이어는 노출 최소화: HttpError만 공개 (RequestBuilder는 @internal)
|
|
9
|
+
export type { HttpResponse } from './http-builder.js';
|
|
10
|
+
|
|
11
|
+
// Endpoint classes (선택적으로 직접 사용 가능)
|
|
12
|
+
export { HealthApi } from './endpoints/health.js';
|
|
13
|
+
export { MeApi } from './endpoints/me.js';
|
|
14
|
+
export { ConversationsApi } from './endpoints/conversations.js';
|
|
15
|
+
export { GoogleAuthApi } from './endpoints/auth.google.js';
|
|
16
|
+
export { GraphApi } from './endpoints/graph.js';
|
|
17
|
+
export { GraphAiApi } from './endpoints/graphAi.js';
|
|
18
|
+
export { NoteApi } from './endpoints/note.js';
|
|
19
|
+
export { AppleAuthApi } from './endpoints/auth.apple.js';
|
|
20
|
+
export { SyncApi } from './endpoints/sync.js';
|
|
21
|
+
export { AiApi } from './endpoints/ai.js';
|
|
22
|
+
export { NotificationApi } from './endpoints/notification.js';
|
|
23
|
+
|
|
24
|
+
// Types
|
|
25
|
+
export type { ProblemDetails } from './types/problem.js';
|
|
26
|
+
export type { MeResponseDto, UserProfileDto } from './types/me.js';
|
|
27
|
+
export type {
|
|
28
|
+
ConversationDto,
|
|
29
|
+
ConversationCreateDto,
|
|
30
|
+
ConversationUpdateDto,
|
|
31
|
+
ConversationBulkCreateDto,
|
|
32
|
+
} from './types/conversation.js';
|
|
33
|
+
export type { MessageDto, MessageCreateDto, MessageUpdateDto } from './types/message.js';
|
|
34
|
+
export type {
|
|
35
|
+
NoteDto,
|
|
36
|
+
NoteCreateDto,
|
|
37
|
+
NoteUpdateDto,
|
|
38
|
+
FolderDto,
|
|
39
|
+
FolderCreateDto,
|
|
40
|
+
FolderUpdateDto,
|
|
41
|
+
} from './types/note.js';
|
|
42
|
+
export type { SyncPushRequest, SyncPullResponse } from './types/sync.js';
|
|
43
|
+
export type {
|
|
44
|
+
AgentChatMode,
|
|
45
|
+
AgentChatModeHint,
|
|
46
|
+
AgentChatStreamEvent,
|
|
47
|
+
AgentChatStreamParams,
|
|
48
|
+
AgentChatStreamOptions,
|
|
49
|
+
AgentChatStreamHandler,
|
|
50
|
+
} from './endpoints/agent.js';
|
|
51
|
+
export { openAgentChatStream } from './endpoints/agent.js';
|
|
52
|
+
export type { AIChatRequestDto, AIChatResponseDto } from './endpoints/ai.js';
|
|
53
|
+
export type { GraphGenerationResponseDto } from './types/graphAi.js';
|
|
54
|
+
export type {
|
|
55
|
+
AiInputData,
|
|
56
|
+
AiInputMappingNode,
|
|
57
|
+
AiInputMessage,
|
|
58
|
+
AiInputMessageAuthor,
|
|
59
|
+
AiInputMessageContent,
|
|
60
|
+
} from './types/aiInput.js';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI 모듈 입력 데이터 DTO 정의
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 메시지 내용
|
|
8
|
+
*
|
|
9
|
+
* 메시지의 실제 콘텐츠를 담고 있습니다. 현재는 텍스트 타입만 지원합니다.
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export interface AiInputMessageContent {
|
|
13
|
+
/**
|
|
14
|
+
* 콘텐츠 타입 (고정값: 'text')
|
|
15
|
+
*/
|
|
16
|
+
content_type: 'text';
|
|
17
|
+
/**
|
|
18
|
+
* 메시지 본문 내용의 배열. 보통 하나의 문자열 요소를 가집니다.
|
|
19
|
+
*/
|
|
20
|
+
parts: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 메시지 작성자 정보
|
|
25
|
+
*
|
|
26
|
+
* 메시지를 누가 작성했는지 나타냅니다.
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
export interface AiInputMessageAuthor {
|
|
30
|
+
/**
|
|
31
|
+
* 작성자의 역할
|
|
32
|
+
* - `user`: 사용자
|
|
33
|
+
* - `assistant`: AI 모델
|
|
34
|
+
* - `system`: 시스템 프롬프트
|
|
35
|
+
*/
|
|
36
|
+
role: 'user' | 'assistant' | 'system';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* AI 입력 메시지 구조
|
|
41
|
+
*
|
|
42
|
+
* 개별 메시지의 상세 정보를 담는 객체입니다.
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
export interface AiInputMessage {
|
|
46
|
+
/**
|
|
47
|
+
* 메시지 고유 ID
|
|
48
|
+
*/
|
|
49
|
+
id: string;
|
|
50
|
+
/**
|
|
51
|
+
* 작성자 정보
|
|
52
|
+
*/
|
|
53
|
+
author: AiInputMessageAuthor;
|
|
54
|
+
/**
|
|
55
|
+
* 메시지 내용
|
|
56
|
+
*/
|
|
57
|
+
content: AiInputMessageContent;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 대화 트리 매핑 노드
|
|
62
|
+
*
|
|
63
|
+
* 대화의 흐름(트리 구조)을 표현하기 위한 노드 객체입니다.
|
|
64
|
+
* 각 노드는 메시지 정보와 부모/자식 관계를 가집니다.
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
export interface AiInputMappingNode {
|
|
68
|
+
/**
|
|
69
|
+
* 노드 ID (메시지 ID와 동일)
|
|
70
|
+
*/
|
|
71
|
+
id: string;
|
|
72
|
+
/**
|
|
73
|
+
* 해당 노드의 메시지 데이터. 루트 노드 등 일부 경우 null일 수 있음.
|
|
74
|
+
*/
|
|
75
|
+
message: AiInputMessage | null;
|
|
76
|
+
/**
|
|
77
|
+
* 부모 노드의 ID. 대화의 시작점인 경우 null.
|
|
78
|
+
*/
|
|
79
|
+
parent: string | null;
|
|
80
|
+
/**
|
|
81
|
+
* 자식 노드 ID들의 배열. 대화의 분기를 표현합니다.
|
|
82
|
+
*/
|
|
83
|
+
children: string[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* AI 입력 데이터 (대화 스레드)
|
|
88
|
+
*
|
|
89
|
+
* 하나의 대화 스레드 전체를 나타내는 최상위 객체입니다.
|
|
90
|
+
* ChatGPT 데이터 내보내기 포맷과 호환됩니다.
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
export interface AiInputData {
|
|
94
|
+
/**
|
|
95
|
+
* 대화 제목
|
|
96
|
+
*/
|
|
97
|
+
title: string;
|
|
98
|
+
/**
|
|
99
|
+
* 생성 시간 (Unix Timestamp, 초 단위)
|
|
100
|
+
*/
|
|
101
|
+
create_time: number;
|
|
102
|
+
/**
|
|
103
|
+
* 마지막 업데이트 시간 (Unix Timestamp, 초 단위)
|
|
104
|
+
*/
|
|
105
|
+
update_time: number;
|
|
106
|
+
/**
|
|
107
|
+
* 메시지 노드들의 맵 (Key: 노드 ID, Value: 노드 객체)
|
|
108
|
+
* 대화의 전체 구조를 담고 있습니다.
|
|
109
|
+
*/
|
|
110
|
+
mapping: Record<string, AiInputMappingNode>;
|
|
111
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { MessageDto } from './message.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 대화(Conversation) DTO
|
|
5
|
+
* @public
|
|
6
|
+
* @property id 대화 ID (UUID)
|
|
7
|
+
* @property title 대화 제목
|
|
8
|
+
* @property createdAt 생성 일시 (ISO 8601)
|
|
9
|
+
* @property updatedAt 수정 일시 (ISO 8601)
|
|
10
|
+
* @property deletedAt 삭제 일시 (ISO 8601, null이면 활성)
|
|
11
|
+
* @property messages 대화에 포함된 메시지 목록
|
|
12
|
+
*/
|
|
13
|
+
export interface ConversationDto {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
createdAt?: string; // ISO 8601
|
|
17
|
+
updatedAt?: string; // ISO 8601
|
|
18
|
+
deletedAt?: string | null; // ISO 8601
|
|
19
|
+
messages: MessageDto[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 대화 생성 요청 DTO
|
|
24
|
+
* @public
|
|
25
|
+
* @property id 대화 ID (선택, 클라이언트 생성 시)
|
|
26
|
+
* @property title 대화 제목
|
|
27
|
+
* @property messages 초기 메시지 목록 (선택)
|
|
28
|
+
*/
|
|
29
|
+
export interface ConversationCreateDto {
|
|
30
|
+
id: string;
|
|
31
|
+
title: string;
|
|
32
|
+
messages?: MessageDto[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 대화 수정 요청 DTO
|
|
37
|
+
* @public
|
|
38
|
+
* @property title 변경할 대화 제목 (선택)
|
|
39
|
+
*/
|
|
40
|
+
export interface ConversationUpdateDto {
|
|
41
|
+
title?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 대화 일괄 생성 요청 DTO
|
|
46
|
+
* @public
|
|
47
|
+
* @property conversations 생성할 대화 목록
|
|
48
|
+
*/
|
|
49
|
+
export interface ConversationBulkCreateDto {
|
|
50
|
+
conversations: ConversationCreateDto[];
|
|
51
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph API에 사용되는 타입 정의
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 그래프 노드 DTO
|
|
7
|
+
* @public
|
|
8
|
+
* @property id 노드 ID (정수)
|
|
9
|
+
* @property userId 사용자 ID
|
|
10
|
+
* @property origId 원본 데이터 ID (예: conversationId)
|
|
11
|
+
* @property clusterId 클러스터 ID
|
|
12
|
+
* @property clusterName 클러스터 이름
|
|
13
|
+
* @property timestamp 타임스탬프 (ISO 8601, null 가능)
|
|
14
|
+
* @property numMessages 메시지 수
|
|
15
|
+
* @property createdAt 생성 일시 (ISO 8601)
|
|
16
|
+
* @property updatedAt 수정 일시 (ISO 8601)
|
|
17
|
+
*/
|
|
18
|
+
export interface GraphNodeDto {
|
|
19
|
+
id: number;
|
|
20
|
+
userId: string;
|
|
21
|
+
origId: string;
|
|
22
|
+
clusterId: string;
|
|
23
|
+
clusterName: string;
|
|
24
|
+
timestamp: string | null;
|
|
25
|
+
numMessages: number;
|
|
26
|
+
createdAt?: string;
|
|
27
|
+
updatedAt?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 그래프 엣지 타입 ('hard' | 'insight')
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export type GraphEdgeType = 'hard' | 'insight';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 그래프 엣지 DTO
|
|
38
|
+
* @public
|
|
39
|
+
* @property userId 사용자 ID
|
|
40
|
+
* @property id 엣지 ID (선택)
|
|
41
|
+
* @property source 출발 노드 ID
|
|
42
|
+
* @property target 도착 노드 ID
|
|
43
|
+
* @property weight 가중치
|
|
44
|
+
* @property type 엣지 타입
|
|
45
|
+
* @property intraCluster 클러스터 내부 연결 여부
|
|
46
|
+
* @property createdAt 생성 일시 (ISO 8601)
|
|
47
|
+
* @property updatedAt 수정 일시 (ISO 8601)
|
|
48
|
+
*/
|
|
49
|
+
export interface GraphEdgeDto {
|
|
50
|
+
userId: string;
|
|
51
|
+
id?: string;
|
|
52
|
+
source: number;
|
|
53
|
+
target: number;
|
|
54
|
+
weight: number;
|
|
55
|
+
type: GraphEdgeType;
|
|
56
|
+
intraCluster: boolean;
|
|
57
|
+
createdAt?: string;
|
|
58
|
+
updatedAt?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 그래프 클러스터 DTO
|
|
63
|
+
* @public
|
|
64
|
+
* @property id 클러스터 ID
|
|
65
|
+
* @property userId 사용자 ID
|
|
66
|
+
* @property name 클러스터 이름
|
|
67
|
+
* @property description 클러스터 설명
|
|
68
|
+
* @property size 클러스터 크기 (노드 수)
|
|
69
|
+
* @property themes 주요 테마 목록
|
|
70
|
+
* @property createdAt 생성 일시 (ISO 8601)
|
|
71
|
+
* @property updatedAt 수정 일시 (ISO 8601)
|
|
72
|
+
*/
|
|
73
|
+
export interface GraphClusterDto {
|
|
74
|
+
id: string;
|
|
75
|
+
userId: string;
|
|
76
|
+
name: string;
|
|
77
|
+
description: string;
|
|
78
|
+
size: number;
|
|
79
|
+
themes: string[];
|
|
80
|
+
createdAt?: string;
|
|
81
|
+
updatedAt?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 그래프 통계 DTO
|
|
86
|
+
* @public
|
|
87
|
+
* @property userId 사용자 ID
|
|
88
|
+
* @property nodes 노드 수
|
|
89
|
+
* @property edges 엣지 수
|
|
90
|
+
* @property clusters 클러스터 수
|
|
91
|
+
* @property generatedAt 생성 일시 (ISO 8601)
|
|
92
|
+
* @property metadata 추가 메타데이터
|
|
93
|
+
*/
|
|
94
|
+
export interface GraphStatsDto {
|
|
95
|
+
userId: string;
|
|
96
|
+
nodes: number;
|
|
97
|
+
edges: number;
|
|
98
|
+
clusters: number;
|
|
99
|
+
generatedAt?: string;
|
|
100
|
+
metadata?: Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 그래프 서브클러스터 DTO
|
|
105
|
+
* @public
|
|
106
|
+
*/
|
|
107
|
+
export interface GraphSubclusterDto {
|
|
108
|
+
id: string;
|
|
109
|
+
userId: string;
|
|
110
|
+
clusterId: string;
|
|
111
|
+
nodeIds: number[];
|
|
112
|
+
representativeNodeId: number;
|
|
113
|
+
size: number;
|
|
114
|
+
density: number;
|
|
115
|
+
topKeywords: string[];
|
|
116
|
+
createdAt?: string;
|
|
117
|
+
updatedAt?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 그래프 스냅샷 DTO (전체 그래프 데이터)
|
|
122
|
+
* @public
|
|
123
|
+
* @property nodes 노드 목록
|
|
124
|
+
* @property edges 엣지 목록
|
|
125
|
+
* @property clusters 클러스터 목록
|
|
126
|
+
* @property subclusters 서브클러스터 목록
|
|
127
|
+
* @property stats 그래프 통계 (userId 제외)
|
|
128
|
+
*/
|
|
129
|
+
export interface GraphSnapshotDto {
|
|
130
|
+
nodes: GraphNodeDto[];
|
|
131
|
+
edges: GraphEdgeDto[];
|
|
132
|
+
clusters: GraphClusterDto[];
|
|
133
|
+
subclusters?: GraphSubclusterDto[];
|
|
134
|
+
stats: Omit<GraphStatsDto, 'userId'>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 엣지 생성 응답 DTO
|
|
139
|
+
* @public
|
|
140
|
+
* @property id 생성된 엣지 ID
|
|
141
|
+
*/
|
|
142
|
+
export interface CreateEdgeResponse {
|
|
143
|
+
id: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 노드 업데이트 페이로드
|
|
148
|
+
* @public
|
|
149
|
+
* @property clusterId 클러스터 ID (선택)
|
|
150
|
+
* @property clusterName 클러스터 이름 (선택)
|
|
151
|
+
*/
|
|
152
|
+
export type UpdateNodePayload = Partial<Pick<GraphNodeDto, 'clusterId' | 'clusterName'>>;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Graph Summary DTO
|
|
156
|
+
*/
|
|
157
|
+
export interface GraphSummaryDto {
|
|
158
|
+
overview: {
|
|
159
|
+
total_conversations: number;
|
|
160
|
+
time_span: string;
|
|
161
|
+
primary_interests: string[];
|
|
162
|
+
conversation_style: string;
|
|
163
|
+
most_active_period: string;
|
|
164
|
+
summary_text: string;
|
|
165
|
+
};
|
|
166
|
+
clusters: Array<{
|
|
167
|
+
cluster_id: string;
|
|
168
|
+
name: string;
|
|
169
|
+
size: number;
|
|
170
|
+
density: number;
|
|
171
|
+
centrality: number;
|
|
172
|
+
recency: string;
|
|
173
|
+
top_keywords: string[];
|
|
174
|
+
key_themes: string[];
|
|
175
|
+
common_question_types: string[];
|
|
176
|
+
insight_text: string;
|
|
177
|
+
notable_conversations: string[];
|
|
178
|
+
}>;
|
|
179
|
+
patterns: Array<{
|
|
180
|
+
pattern_type: string;
|
|
181
|
+
description: string;
|
|
182
|
+
evidence: string[];
|
|
183
|
+
significance: string;
|
|
184
|
+
}>;
|
|
185
|
+
connections: Array<{
|
|
186
|
+
source_cluster: string;
|
|
187
|
+
target_cluster: string;
|
|
188
|
+
connection_strength: number;
|
|
189
|
+
bridge_keywords: string[];
|
|
190
|
+
description: string;
|
|
191
|
+
}>;
|
|
192
|
+
recommendations: Array<{
|
|
193
|
+
type: string;
|
|
194
|
+
title: string;
|
|
195
|
+
description: string;
|
|
196
|
+
related_nodes: string[];
|
|
197
|
+
priority: string;
|
|
198
|
+
}>;
|
|
199
|
+
generated_at: string;
|
|
200
|
+
detail_level: string;
|
|
201
|
+
}
|