@goscribe/server 1.0.11 → 1.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/ANALYSIS_PROGRESS_SPEC.md +463 -0
- package/PROGRESS_QUICK_REFERENCE.md +239 -0
- package/dist/lib/ai-session.d.ts +20 -9
- package/dist/lib/ai-session.js +316 -80
- package/dist/lib/auth.d.ts +35 -2
- package/dist/lib/auth.js +88 -15
- package/dist/lib/env.d.ts +32 -0
- package/dist/lib/env.js +46 -0
- package/dist/lib/errors.d.ts +33 -0
- package/dist/lib/errors.js +78 -0
- package/dist/lib/inference.d.ts +4 -1
- package/dist/lib/inference.js +9 -11
- package/dist/lib/logger.d.ts +62 -0
- package/dist/lib/logger.js +342 -0
- package/dist/lib/podcast-prompts.d.ts +43 -0
- package/dist/lib/podcast-prompts.js +135 -0
- package/dist/lib/pusher.d.ts +1 -0
- package/dist/lib/pusher.js +14 -2
- package/dist/lib/storage.d.ts +3 -3
- package/dist/lib/storage.js +51 -47
- package/dist/lib/validation.d.ts +51 -0
- package/dist/lib/validation.js +64 -0
- package/dist/routers/_app.d.ts +697 -111
- package/dist/routers/_app.js +5 -0
- package/dist/routers/auth.d.ts +11 -1
- package/dist/routers/chat.d.ts +11 -1
- package/dist/routers/flashcards.d.ts +205 -6
- package/dist/routers/flashcards.js +144 -66
- package/dist/routers/members.d.ts +165 -0
- package/dist/routers/members.js +531 -0
- package/dist/routers/podcast.d.ts +78 -63
- package/dist/routers/podcast.js +330 -393
- package/dist/routers/studyguide.d.ts +11 -1
- package/dist/routers/worksheets.d.ts +124 -13
- package/dist/routers/worksheets.js +123 -50
- package/dist/routers/workspace.d.ts +213 -26
- package/dist/routers/workspace.js +303 -181
- package/dist/server.js +12 -4
- package/dist/services/flashcard-progress.service.d.ts +183 -0
- package/dist/services/flashcard-progress.service.js +383 -0
- package/dist/services/flashcard.service.d.ts +183 -0
- package/dist/services/flashcard.service.js +224 -0
- package/dist/services/podcast-segment-reorder.d.ts +0 -0
- package/dist/services/podcast-segment-reorder.js +107 -0
- package/dist/services/podcast.service.d.ts +0 -0
- package/dist/services/podcast.service.js +326 -0
- package/dist/services/worksheet.service.d.ts +0 -0
- package/dist/services/worksheet.service.js +295 -0
- package/dist/trpc.d.ts +13 -2
- package/dist/trpc.js +55 -6
- package/dist/types/index.d.ts +126 -0
- package/dist/types/index.js +1 -0
- package/package.json +3 -2
- package/prisma/schema.prisma +142 -4
- package/src/lib/ai-session.ts +356 -85
- package/src/lib/auth.ts +113 -19
- package/src/lib/env.ts +59 -0
- package/src/lib/errors.ts +92 -0
- package/src/lib/inference.ts +11 -11
- package/src/lib/logger.ts +405 -0
- package/src/lib/pusher.ts +15 -3
- package/src/lib/storage.ts +56 -51
- package/src/lib/validation.ts +75 -0
- package/src/routers/_app.ts +5 -0
- package/src/routers/chat.ts +2 -23
- package/src/routers/flashcards.ts +108 -24
- package/src/routers/members.ts +586 -0
- package/src/routers/podcast.ts +385 -420
- package/src/routers/worksheets.ts +117 -35
- package/src/routers/workspace.ts +328 -195
- package/src/server.ts +13 -4
- package/src/services/flashcard-progress.service.ts +541 -0
- package/src/trpc.ts +59 -6
- package/src/types/index.ts +165 -0
- package/AUTH_FRONTEND_SPEC.md +0 -21
- package/CHAT_FRONTEND_SPEC.md +0 -474
- package/DATABASE_SETUP.md +0 -165
- package/MEETINGSUMMARY_FRONTEND_SPEC.md +0 -28
- package/PODCAST_FRONTEND_SPEC.md +0 -595
- package/STUDYGUIDE_FRONTEND_SPEC.md +0 -18
- package/WORKSHEETS_FRONTEND_SPEC.md +0 -26
- package/WORKSPACE_FRONTEND_SPEC.md +0 -47
- package/test-ai-integration.js +0 -134
|
@@ -7,7 +7,17 @@ export declare const studyguide: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
7
7
|
cookies: Record<string, string | undefined>;
|
|
8
8
|
};
|
|
9
9
|
meta: object;
|
|
10
|
-
errorShape:
|
|
10
|
+
errorShape: {
|
|
11
|
+
data: {
|
|
12
|
+
zodError: string | null;
|
|
13
|
+
code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
|
|
14
|
+
httpStatus: number;
|
|
15
|
+
path?: string;
|
|
16
|
+
stack?: string;
|
|
17
|
+
};
|
|
18
|
+
message: string;
|
|
19
|
+
code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
|
|
20
|
+
};
|
|
11
21
|
transformer: true;
|
|
12
22
|
}, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
|
|
13
23
|
get: import("@trpc/server").TRPCQueryProcedure<{
|
|
@@ -7,14 +7,60 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
7
7
|
cookies: Record<string, string | undefined>;
|
|
8
8
|
};
|
|
9
9
|
meta: object;
|
|
10
|
-
errorShape:
|
|
10
|
+
errorShape: {
|
|
11
|
+
data: {
|
|
12
|
+
zodError: string | null;
|
|
13
|
+
code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
|
|
14
|
+
httpStatus: number;
|
|
15
|
+
path?: string;
|
|
16
|
+
stack?: string;
|
|
17
|
+
};
|
|
18
|
+
message: string;
|
|
19
|
+
code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
|
|
20
|
+
};
|
|
11
21
|
transformer: true;
|
|
12
22
|
}, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
|
|
13
23
|
list: import("@trpc/server").TRPCQueryProcedure<{
|
|
14
24
|
input: {
|
|
15
25
|
workspaceId: string;
|
|
16
26
|
};
|
|
17
|
-
output:
|
|
27
|
+
output: ({
|
|
28
|
+
versions: {
|
|
29
|
+
id: string;
|
|
30
|
+
createdAt: Date;
|
|
31
|
+
createdById: string | null;
|
|
32
|
+
artifactId: string;
|
|
33
|
+
content: string;
|
|
34
|
+
data: import("@prisma/client/runtime/library").JsonValue | null;
|
|
35
|
+
version: number;
|
|
36
|
+
}[];
|
|
37
|
+
questions: {
|
|
38
|
+
meta: import("@prisma/client/runtime/library").JsonValue | null;
|
|
39
|
+
id: string;
|
|
40
|
+
createdAt: Date;
|
|
41
|
+
type: import("@prisma/client").$Enums.QuestionType;
|
|
42
|
+
difficulty: import("@prisma/client").$Enums.Difficulty;
|
|
43
|
+
artifactId: string;
|
|
44
|
+
prompt: string;
|
|
45
|
+
answer: string | null;
|
|
46
|
+
order: number;
|
|
47
|
+
}[];
|
|
48
|
+
} & {
|
|
49
|
+
id: string;
|
|
50
|
+
createdAt: Date;
|
|
51
|
+
updatedAt: Date;
|
|
52
|
+
title: string;
|
|
53
|
+
description: string | null;
|
|
54
|
+
workspaceId: string;
|
|
55
|
+
type: import("@prisma/client").$Enums.ArtifactType;
|
|
56
|
+
isArchived: boolean;
|
|
57
|
+
generating: boolean;
|
|
58
|
+
generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
|
|
59
|
+
difficulty: import("@prisma/client").$Enums.Difficulty | null;
|
|
60
|
+
estimatedTime: string | null;
|
|
61
|
+
imageObjectKey: string | null;
|
|
62
|
+
createdById: string | null;
|
|
63
|
+
})[];
|
|
18
64
|
meta: object;
|
|
19
65
|
}>;
|
|
20
66
|
create: import("@trpc/server").TRPCMutationProcedure<{
|
|
@@ -27,7 +73,7 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
27
73
|
problems?: {
|
|
28
74
|
question: string;
|
|
29
75
|
answer: string;
|
|
30
|
-
type?: "
|
|
76
|
+
type?: "MULTIPLE_CHOICE" | "TEXT" | "NUMERIC" | "TRUE_FALSE" | "MATCHING" | "FILL_IN_THE_BLANK" | undefined;
|
|
31
77
|
options?: string[] | undefined;
|
|
32
78
|
}[] | undefined;
|
|
33
79
|
};
|
|
@@ -39,9 +85,9 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
39
85
|
type: import("@prisma/client").$Enums.QuestionType;
|
|
40
86
|
difficulty: import("@prisma/client").$Enums.Difficulty;
|
|
41
87
|
artifactId: string;
|
|
42
|
-
order: number;
|
|
43
88
|
prompt: string;
|
|
44
89
|
answer: string | null;
|
|
90
|
+
order: number;
|
|
45
91
|
}[];
|
|
46
92
|
} & {
|
|
47
93
|
id: string;
|
|
@@ -52,8 +98,11 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
52
98
|
workspaceId: string;
|
|
53
99
|
type: import("@prisma/client").$Enums.ArtifactType;
|
|
54
100
|
isArchived: boolean;
|
|
101
|
+
generating: boolean;
|
|
102
|
+
generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
|
|
55
103
|
difficulty: import("@prisma/client").$Enums.Difficulty | null;
|
|
56
104
|
estimatedTime: string | null;
|
|
105
|
+
imageObjectKey: string | null;
|
|
57
106
|
createdById: string | null;
|
|
58
107
|
};
|
|
59
108
|
meta: object;
|
|
@@ -62,7 +111,34 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
62
111
|
input: {
|
|
63
112
|
worksheetId: string;
|
|
64
113
|
};
|
|
65
|
-
output:
|
|
114
|
+
output: {
|
|
115
|
+
questions: {
|
|
116
|
+
meta: import("@prisma/client/runtime/library").JsonValue | null;
|
|
117
|
+
id: string;
|
|
118
|
+
createdAt: Date;
|
|
119
|
+
type: import("@prisma/client").$Enums.QuestionType;
|
|
120
|
+
difficulty: import("@prisma/client").$Enums.Difficulty;
|
|
121
|
+
artifactId: string;
|
|
122
|
+
prompt: string;
|
|
123
|
+
answer: string | null;
|
|
124
|
+
order: number;
|
|
125
|
+
}[];
|
|
126
|
+
} & {
|
|
127
|
+
id: string;
|
|
128
|
+
createdAt: Date;
|
|
129
|
+
updatedAt: Date;
|
|
130
|
+
title: string;
|
|
131
|
+
description: string | null;
|
|
132
|
+
workspaceId: string;
|
|
133
|
+
type: import("@prisma/client").$Enums.ArtifactType;
|
|
134
|
+
isArchived: boolean;
|
|
135
|
+
generating: boolean;
|
|
136
|
+
generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
|
|
137
|
+
difficulty: import("@prisma/client").$Enums.Difficulty | null;
|
|
138
|
+
estimatedTime: string | null;
|
|
139
|
+
imageObjectKey: string | null;
|
|
140
|
+
createdById: string | null;
|
|
141
|
+
};
|
|
66
142
|
meta: object;
|
|
67
143
|
}>;
|
|
68
144
|
createWorksheetQuestion: import("@trpc/server").TRPCMutationProcedure<{
|
|
@@ -70,7 +146,7 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
70
146
|
worksheetId: string;
|
|
71
147
|
prompt: string;
|
|
72
148
|
answer?: string | undefined;
|
|
73
|
-
type?: "
|
|
149
|
+
type?: "MULTIPLE_CHOICE" | "TEXT" | "NUMERIC" | "TRUE_FALSE" | "MATCHING" | "FILL_IN_THE_BLANK" | undefined;
|
|
74
150
|
difficulty?: "EASY" | "MEDIUM" | "HARD" | undefined;
|
|
75
151
|
order?: number | undefined;
|
|
76
152
|
meta?: Record<string, unknown> | undefined;
|
|
@@ -82,9 +158,9 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
82
158
|
type: import("@prisma/client").$Enums.QuestionType;
|
|
83
159
|
difficulty: import("@prisma/client").$Enums.Difficulty;
|
|
84
160
|
artifactId: string;
|
|
85
|
-
order: number;
|
|
86
161
|
prompt: string;
|
|
87
162
|
answer: string | null;
|
|
163
|
+
order: number;
|
|
88
164
|
};
|
|
89
165
|
meta: object;
|
|
90
166
|
}>;
|
|
@@ -93,7 +169,7 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
93
169
|
worksheetQuestionId: string;
|
|
94
170
|
prompt?: string | undefined;
|
|
95
171
|
answer?: string | undefined;
|
|
96
|
-
type?: "
|
|
172
|
+
type?: "MULTIPLE_CHOICE" | "TEXT" | "NUMERIC" | "TRUE_FALSE" | "MATCHING" | "FILL_IN_THE_BLANK" | undefined;
|
|
97
173
|
difficulty?: "EASY" | "MEDIUM" | "HARD" | undefined;
|
|
98
174
|
order?: number | undefined;
|
|
99
175
|
meta?: Record<string, unknown> | undefined;
|
|
@@ -105,9 +181,9 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
105
181
|
type: import("@prisma/client").$Enums.QuestionType;
|
|
106
182
|
difficulty: import("@prisma/client").$Enums.Difficulty;
|
|
107
183
|
artifactId: string;
|
|
108
|
-
order: number;
|
|
109
184
|
prompt: string;
|
|
110
185
|
answer: string | null;
|
|
186
|
+
order: number;
|
|
111
187
|
};
|
|
112
188
|
meta: object;
|
|
113
189
|
}>;
|
|
@@ -123,6 +199,7 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
123
199
|
problemId: string;
|
|
124
200
|
completed: boolean;
|
|
125
201
|
answer?: string | undefined;
|
|
202
|
+
correct?: boolean | undefined;
|
|
126
203
|
};
|
|
127
204
|
output: {
|
|
128
205
|
meta: import("@prisma/client/runtime/library").JsonValue | null;
|
|
@@ -131,8 +208,9 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
131
208
|
updatedAt: Date;
|
|
132
209
|
userId: string;
|
|
133
210
|
worksheetQuestionId: string;
|
|
134
|
-
|
|
211
|
+
modified: boolean;
|
|
135
212
|
userAnswer: string | null;
|
|
213
|
+
correct: boolean | null;
|
|
136
214
|
completedAt: Date | null;
|
|
137
215
|
attempts: number;
|
|
138
216
|
timeSpentSec: number | null;
|
|
@@ -150,8 +228,9 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
150
228
|
updatedAt: Date;
|
|
151
229
|
userId: string;
|
|
152
230
|
worksheetQuestionId: string;
|
|
153
|
-
|
|
231
|
+
modified: boolean;
|
|
154
232
|
userAnswer: string | null;
|
|
233
|
+
correct: boolean | null;
|
|
155
234
|
completedAt: Date | null;
|
|
156
235
|
attempts: number;
|
|
157
236
|
timeSpentSec: number | null;
|
|
@@ -169,7 +248,7 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
169
248
|
question: string;
|
|
170
249
|
answer: string;
|
|
171
250
|
id?: string | undefined;
|
|
172
|
-
type?: "
|
|
251
|
+
type?: "MULTIPLE_CHOICE" | "TEXT" | "NUMERIC" | "TRUE_FALSE" | "MATCHING" | "FILL_IN_THE_BLANK" | undefined;
|
|
173
252
|
options?: string[] | undefined;
|
|
174
253
|
}[] | undefined;
|
|
175
254
|
};
|
|
@@ -181,9 +260,9 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
181
260
|
type: import("@prisma/client").$Enums.QuestionType;
|
|
182
261
|
difficulty: import("@prisma/client").$Enums.Difficulty;
|
|
183
262
|
artifactId: string;
|
|
184
|
-
order: number;
|
|
185
263
|
prompt: string;
|
|
186
264
|
answer: string | null;
|
|
265
|
+
order: number;
|
|
187
266
|
}[];
|
|
188
267
|
} & {
|
|
189
268
|
id: string;
|
|
@@ -194,8 +273,11 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
194
273
|
workspaceId: string;
|
|
195
274
|
type: import("@prisma/client").$Enums.ArtifactType;
|
|
196
275
|
isArchived: boolean;
|
|
276
|
+
generating: boolean;
|
|
277
|
+
generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
|
|
197
278
|
difficulty: import("@prisma/client").$Enums.Difficulty | null;
|
|
198
279
|
estimatedTime: string | null;
|
|
280
|
+
imageObjectKey: string | null;
|
|
199
281
|
createdById: string | null;
|
|
200
282
|
};
|
|
201
283
|
meta: object;
|
|
@@ -226,11 +308,40 @@ export declare const worksheets: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
226
308
|
workspaceId: string;
|
|
227
309
|
type: import("@prisma/client").$Enums.ArtifactType;
|
|
228
310
|
isArchived: boolean;
|
|
311
|
+
generating: boolean;
|
|
312
|
+
generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
|
|
229
313
|
difficulty: import("@prisma/client").$Enums.Difficulty | null;
|
|
230
314
|
estimatedTime: string | null;
|
|
315
|
+
imageObjectKey: string | null;
|
|
231
316
|
createdById: string | null;
|
|
232
317
|
};
|
|
233
318
|
};
|
|
234
319
|
meta: object;
|
|
235
320
|
}>;
|
|
321
|
+
checkAnswer: import("@trpc/server").TRPCMutationProcedure<{
|
|
322
|
+
input: {
|
|
323
|
+
worksheetId: string;
|
|
324
|
+
questionId: string;
|
|
325
|
+
answer: string;
|
|
326
|
+
};
|
|
327
|
+
output: {
|
|
328
|
+
isCorrect: boolean;
|
|
329
|
+
userMarkScheme: import("../types/index.js").UserMarkScheme | null;
|
|
330
|
+
progress: {
|
|
331
|
+
meta: import("@prisma/client/runtime/library").JsonValue | null;
|
|
332
|
+
id: string;
|
|
333
|
+
createdAt: Date;
|
|
334
|
+
updatedAt: Date;
|
|
335
|
+
userId: string;
|
|
336
|
+
worksheetQuestionId: string;
|
|
337
|
+
modified: boolean;
|
|
338
|
+
userAnswer: string | null;
|
|
339
|
+
correct: boolean | null;
|
|
340
|
+
completedAt: Date | null;
|
|
341
|
+
attempts: number;
|
|
342
|
+
timeSpentSec: number | null;
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
meta: object;
|
|
346
|
+
}>;
|
|
236
347
|
}>>;
|
|
@@ -3,6 +3,7 @@ import { TRPCError } from '@trpc/server';
|
|
|
3
3
|
import { router, authedProcedure } from '../trpc.js';
|
|
4
4
|
import { aiSessionService } from '../lib/ai-session.js';
|
|
5
5
|
import PusherService from '../lib/pusher.js';
|
|
6
|
+
import { logger } from '../lib/logger.js';
|
|
6
7
|
// Avoid importing Prisma enums directly; mirror values as string literals
|
|
7
8
|
const ArtifactType = {
|
|
8
9
|
WORKSHEET: 'WORKSHEET',
|
|
@@ -53,13 +54,15 @@ export const worksheets = router({
|
|
|
53
54
|
if (!p)
|
|
54
55
|
return q;
|
|
55
56
|
const existingMeta = q.meta ? (typeof q.meta === 'object' ? q.meta : JSON.parse(q.meta)) : {};
|
|
57
|
+
const progressMeta = p.meta ? (typeof p.meta === 'object' ? p.meta : JSON.parse(p.meta)) : {};
|
|
56
58
|
return {
|
|
57
59
|
...q,
|
|
58
60
|
meta: {
|
|
59
61
|
...existingMeta,
|
|
60
|
-
completed: p.
|
|
62
|
+
completed: p.modified,
|
|
61
63
|
userAnswer: p.userAnswer,
|
|
62
64
|
completedAt: p.completedAt,
|
|
65
|
+
userMarkScheme: progressMeta.userMarkScheme,
|
|
63
66
|
},
|
|
64
67
|
};
|
|
65
68
|
}),
|
|
@@ -141,13 +144,15 @@ export const worksheets = router({
|
|
|
141
144
|
if (!p)
|
|
142
145
|
return q;
|
|
143
146
|
const existingMeta = q.meta ? (typeof q.meta === 'object' ? q.meta : JSON.parse(q.meta)) : {};
|
|
147
|
+
const progressMeta = p.meta ? (typeof p.meta === 'object' ? p.meta : JSON.parse(p.meta)) : {};
|
|
144
148
|
return {
|
|
145
149
|
...q,
|
|
146
150
|
meta: {
|
|
147
151
|
...existingMeta,
|
|
148
|
-
completed: p.
|
|
152
|
+
completed: p.modified,
|
|
149
153
|
userAnswer: p.userAnswer,
|
|
150
154
|
completedAt: p.completedAt,
|
|
155
|
+
userMarkScheme: progressMeta.userMarkScheme,
|
|
151
156
|
},
|
|
152
157
|
};
|
|
153
158
|
}),
|
|
@@ -230,6 +235,7 @@ export const worksheets = router({
|
|
|
230
235
|
problemId: z.string(),
|
|
231
236
|
completed: z.boolean(),
|
|
232
237
|
answer: z.string().optional(),
|
|
238
|
+
correct: z.boolean().optional(),
|
|
233
239
|
}))
|
|
234
240
|
.mutation(async ({ ctx, input }) => {
|
|
235
241
|
// Verify question ownership through worksheet
|
|
@@ -255,13 +261,14 @@ export const worksheets = router({
|
|
|
255
261
|
create: {
|
|
256
262
|
worksheetQuestionId: input.problemId,
|
|
257
263
|
userId: ctx.session.user.id,
|
|
258
|
-
|
|
264
|
+
modified: input.completed,
|
|
259
265
|
userAnswer: input.answer,
|
|
266
|
+
correct: input.correct,
|
|
260
267
|
completedAt: input.completed ? new Date() : null,
|
|
261
268
|
attempts: 1,
|
|
262
269
|
},
|
|
263
270
|
update: {
|
|
264
|
-
|
|
271
|
+
modified: input.completed,
|
|
265
272
|
userAnswer: input.answer,
|
|
266
273
|
completedAt: input.completed ? new Date() : null,
|
|
267
274
|
attempts: { increment: 1 },
|
|
@@ -381,12 +388,6 @@ export const worksheets = router({
|
|
|
381
388
|
const workspace = await ctx.db.workspace.findFirst({ where: { id: input.workspaceId, ownerId: ctx.session.user.id } });
|
|
382
389
|
if (!workspace)
|
|
383
390
|
throw new TRPCError({ code: 'NOT_FOUND' });
|
|
384
|
-
await PusherService.emitTaskComplete(input.workspaceId, 'worksheet_load_start', { source: 'prompt' });
|
|
385
|
-
const session = await aiSessionService.initSession(input.workspaceId);
|
|
386
|
-
await aiSessionService.setInstruction(session.id, `Create a worksheet with ${input.numQuestions} questions at ${input.difficulty} difficulty from this prompt. Return JSON.\n\nPrompt:\n${input.prompt}`);
|
|
387
|
-
await aiSessionService.startLLMSession(session.id);
|
|
388
|
-
const content = await aiSessionService.generateWorksheetQuestions(session.id, input.numQuestions, input.difficulty);
|
|
389
|
-
await PusherService.emitTaskComplete(input.workspaceId, 'worksheet_info', { contentLength: content.length });
|
|
390
391
|
const artifact = await ctx.db.artifact.create({
|
|
391
392
|
data: {
|
|
392
393
|
workspaceId: input.workspaceId,
|
|
@@ -395,60 +396,132 @@ export const worksheets = router({
|
|
|
395
396
|
createdById: ctx.session.user.id,
|
|
396
397
|
difficulty: (input.difficulty.toUpperCase()),
|
|
397
398
|
estimatedTime: input.estimatedTime,
|
|
399
|
+
generating: true,
|
|
400
|
+
generatingMetadata: { quantity: input.numQuestions, difficulty: input.difficulty.toLowerCase() },
|
|
398
401
|
},
|
|
399
402
|
});
|
|
403
|
+
await PusherService.emitTaskComplete(input.workspaceId, 'worksheet_info', { contentLength: input.numQuestions });
|
|
400
404
|
try {
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
405
|
+
const content = await aiSessionService.generateWorksheetQuestions(input.workspaceId, ctx.session.user.id, input.numQuestions, input.difficulty);
|
|
406
|
+
try {
|
|
407
|
+
const worksheetData = JSON.parse(content);
|
|
408
|
+
let actualWorksheetData = worksheetData;
|
|
409
|
+
if (worksheetData.last_response) {
|
|
410
|
+
try {
|
|
411
|
+
actualWorksheetData = JSON.parse(worksheetData.last_response);
|
|
412
|
+
}
|
|
413
|
+
catch { }
|
|
406
414
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const type = problem.type || 'TEXT';
|
|
415
|
-
const options = problem.options || [];
|
|
416
|
-
await ctx.db.worksheetQuestion.create({
|
|
417
|
-
data: {
|
|
418
|
-
artifactId: artifact.id,
|
|
419
|
-
prompt,
|
|
420
|
-
answer,
|
|
421
|
-
difficulty: (input.difficulty.toUpperCase()),
|
|
422
|
-
order: i,
|
|
423
|
-
meta: {
|
|
424
|
-
type,
|
|
425
|
-
options: options.length > 0 ? options : undefined,
|
|
426
|
-
},
|
|
427
|
-
},
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
catch {
|
|
432
|
-
const lines = content.split('\n').filter(line => line.trim());
|
|
433
|
-
for (let i = 0; i < Math.min(lines.length, input.numQuestions); i++) {
|
|
434
|
-
const line = lines[i];
|
|
435
|
-
if (line.includes(' - ')) {
|
|
436
|
-
const [q, a] = line.split(' - ');
|
|
415
|
+
const problems = actualWorksheetData.problems || actualWorksheetData.questions || actualWorksheetData || [];
|
|
416
|
+
for (let i = 0; i < Math.min(problems.length, input.numQuestions); i++) {
|
|
417
|
+
const problem = problems[i];
|
|
418
|
+
const prompt = problem.question || problem.prompt || `Question ${i + 1}`;
|
|
419
|
+
const answer = problem.answer || problem.solution || `Answer ${i + 1}`;
|
|
420
|
+
const type = problem.type || 'TEXT';
|
|
421
|
+
const options = problem.options || [];
|
|
437
422
|
await ctx.db.worksheetQuestion.create({
|
|
438
423
|
data: {
|
|
439
424
|
artifactId: artifact.id,
|
|
440
|
-
prompt
|
|
441
|
-
answer
|
|
425
|
+
prompt,
|
|
426
|
+
answer,
|
|
442
427
|
difficulty: (input.difficulty.toUpperCase()),
|
|
443
428
|
order: i,
|
|
444
|
-
meta: {
|
|
429
|
+
meta: {
|
|
430
|
+
type,
|
|
431
|
+
options: options.length > 0 ? options : undefined,
|
|
432
|
+
mark_scheme: problem.mark_scheme || undefined,
|
|
433
|
+
},
|
|
445
434
|
},
|
|
446
435
|
});
|
|
447
436
|
}
|
|
448
437
|
}
|
|
438
|
+
catch {
|
|
439
|
+
logger.error('Failed to parse worksheet JSON,');
|
|
440
|
+
await ctx.db.artifact.delete({
|
|
441
|
+
where: { id: artifact.id },
|
|
442
|
+
});
|
|
443
|
+
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to parse worksheet JSON' });
|
|
444
|
+
}
|
|
445
|
+
await ctx.db.artifact.update({
|
|
446
|
+
where: { id: artifact.id },
|
|
447
|
+
data: { generating: false },
|
|
448
|
+
});
|
|
449
|
+
await PusherService.emitWorksheetComplete(input.workspaceId, artifact);
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
await ctx.db.artifact.delete({
|
|
453
|
+
where: { id: artifact.id },
|
|
454
|
+
});
|
|
455
|
+
await PusherService.emitError(input.workspaceId, `Failed to generate worksheet: ${error instanceof Error ? error.message : 'Unknown error'}`, 'worksheet_generation');
|
|
456
|
+
throw error;
|
|
449
457
|
}
|
|
450
|
-
await PusherService.emitWorksheetComplete(input.workspaceId, artifact);
|
|
451
|
-
aiSessionService.deleteSession(session.id);
|
|
452
458
|
return { artifact };
|
|
453
459
|
}),
|
|
460
|
+
checkAnswer: authedProcedure
|
|
461
|
+
.input(z.object({
|
|
462
|
+
worksheetId: z.string(),
|
|
463
|
+
questionId: z.string(),
|
|
464
|
+
answer: z.string().min(1),
|
|
465
|
+
}))
|
|
466
|
+
.mutation(async ({ ctx, input }) => {
|
|
467
|
+
const worksheet = await ctx.db.artifact.findFirst({ where: { id: input.worksheetId, type: ArtifactType.WORKSHEET, workspace: { ownerId: ctx.session.user.id } }, include: { workspace: true } });
|
|
468
|
+
if (!worksheet)
|
|
469
|
+
throw new TRPCError({ code: 'NOT_FOUND' });
|
|
470
|
+
const question = await ctx.db.worksheetQuestion.findFirst({ where: { id: input.questionId, artifactId: input.worksheetId } });
|
|
471
|
+
if (!question)
|
|
472
|
+
throw new TRPCError({ code: 'NOT_FOUND' });
|
|
473
|
+
// Parse question meta to get mark_scheme
|
|
474
|
+
const questionMeta = question.meta ? (typeof question.meta === 'object' ? question.meta : JSON.parse(question.meta)) : {};
|
|
475
|
+
const markScheme = questionMeta.mark_scheme;
|
|
476
|
+
let isCorrect = false;
|
|
477
|
+
let userMarkScheme = null;
|
|
478
|
+
// If mark scheme exists, use AI marking
|
|
479
|
+
if (markScheme && markScheme.points && markScheme.points.length > 0) {
|
|
480
|
+
try {
|
|
481
|
+
userMarkScheme = await aiSessionService.checkWorksheetQuestions(worksheet.workspace.id, ctx.session.user.id, question.prompt, input.answer, markScheme);
|
|
482
|
+
// Determine if correct by comparing achieved points vs total points
|
|
483
|
+
const achievedTotal = userMarkScheme.points.reduce((sum, p) => sum + (p.achievedPoints || 0), 0);
|
|
484
|
+
isCorrect = achievedTotal === markScheme.totalPoints;
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
logger.error('Failed to check answer with AI', error instanceof Error ? error.message : 'Unknown error');
|
|
488
|
+
// Fallback to simple string comparison
|
|
489
|
+
isCorrect = question.answer === input.answer;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
// Simple string comparison if no mark scheme
|
|
494
|
+
isCorrect = question.answer === input.answer;
|
|
495
|
+
}
|
|
496
|
+
// @todo: figure out this wierd fix
|
|
497
|
+
const progress = await ctx.db.worksheetQuestionProgress.upsert({
|
|
498
|
+
where: {
|
|
499
|
+
worksheetQuestionId_userId: {
|
|
500
|
+
worksheetQuestionId: input.questionId,
|
|
501
|
+
userId: ctx.session.user.id,
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
create: {
|
|
505
|
+
worksheetQuestionId: input.questionId,
|
|
506
|
+
userId: ctx.session.user.id,
|
|
507
|
+
modified: true,
|
|
508
|
+
userAnswer: input.answer,
|
|
509
|
+
correct: isCorrect,
|
|
510
|
+
completedAt: new Date(),
|
|
511
|
+
attempts: 1,
|
|
512
|
+
meta: userMarkScheme ? { userMarkScheme: JSON.parse(JSON.stringify(userMarkScheme)) } : { userMarkScheme: null },
|
|
513
|
+
},
|
|
514
|
+
update: {
|
|
515
|
+
modified: true,
|
|
516
|
+
userAnswer: input.answer,
|
|
517
|
+
correct: isCorrect,
|
|
518
|
+
completedAt: new Date(),
|
|
519
|
+
attempts: { increment: 1 },
|
|
520
|
+
meta: userMarkScheme
|
|
521
|
+
? { userMarkScheme: JSON.parse(JSON.stringify(userMarkScheme)) }
|
|
522
|
+
: { userMarkScheme: null },
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
return { isCorrect, userMarkScheme, progress };
|
|
526
|
+
}),
|
|
454
527
|
});
|