@goscribe/server 1.0.11 → 1.1.1
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 +118 -36
- package/src/routers/workspace.ts +356 -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
package/src/routers/chat.ts
CHANGED
|
@@ -220,18 +220,7 @@ export const chat = router({
|
|
|
220
220
|
}
|
|
221
221
|
});
|
|
222
222
|
// Notify via Pusher
|
|
223
|
-
await PusherService.emitChannelEvent(chat.channelId, "edit_message",
|
|
224
|
-
chatId: updatedChat.id,
|
|
225
|
-
channelId: updatedChat.channelId,
|
|
226
|
-
userId: updatedChat.userId,
|
|
227
|
-
message: input.message,
|
|
228
|
-
updatedAt: updatedChat.updatedAt,
|
|
229
|
-
user: {
|
|
230
|
-
id: ctx.session.user.id,
|
|
231
|
-
name: updatedChat.user?.name,
|
|
232
|
-
image: updatedChat.user?.image,
|
|
233
|
-
},
|
|
234
|
-
});
|
|
223
|
+
await PusherService.emitChannelEvent(chat.channelId, "edit_message", updatedChat);
|
|
235
224
|
return updatedChat;
|
|
236
225
|
}),
|
|
237
226
|
deleteMessage: authedProcedure
|
|
@@ -259,17 +248,7 @@ export const chat = router({
|
|
|
259
248
|
where: { id: input.chatId },
|
|
260
249
|
});
|
|
261
250
|
// Notify via Pusher
|
|
262
|
-
await PusherService.emitChannelEvent(chat.channelId, "delete_message",
|
|
263
|
-
chatId: chat.id,
|
|
264
|
-
channelId: chat.channelId,
|
|
265
|
-
userId: chat.userId,
|
|
266
|
-
deletedAt: new Date().toISOString(),
|
|
267
|
-
user: {
|
|
268
|
-
id: ctx.session.user.id,
|
|
269
|
-
name: chat.user?.name,
|
|
270
|
-
image: chat.user?.image,
|
|
271
|
-
},
|
|
272
|
-
});
|
|
251
|
+
await PusherService.emitChannelEvent(chat.channelId, "delete_message", chat);
|
|
273
252
|
return { success: true };
|
|
274
253
|
}),
|
|
275
254
|
});
|
|
@@ -4,6 +4,7 @@ import { router, authedProcedure } from '../trpc.js';
|
|
|
4
4
|
import createInferenceService from '../lib/inference.js';
|
|
5
5
|
import { aiSessionService } from '../lib/ai-session.js';
|
|
6
6
|
import PusherService from '../lib/pusher.js';
|
|
7
|
+
import { createFlashcardProgressService } from '../services/flashcard-progress.service.js';
|
|
7
8
|
// Prisma enum values mapped manually to avoid type import issues in ESM
|
|
8
9
|
const ArtifactType = {
|
|
9
10
|
STUDY_GUIDE: 'STUDY_GUIDE',
|
|
@@ -38,13 +39,30 @@ export const flashcards = router({
|
|
|
38
39
|
const set = await ctx.db.artifact.findFirst({
|
|
39
40
|
where: { workspaceId: input.workspaceId, type: ArtifactType.FLASHCARD_SET, workspace: { ownerId: ctx.session.user.id } },
|
|
40
41
|
include: {
|
|
41
|
-
flashcards:
|
|
42
|
+
flashcards: {
|
|
43
|
+
include: {
|
|
44
|
+
progress: {
|
|
45
|
+
where: {
|
|
46
|
+
userId: ctx.session.user.id,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
42
52
|
},
|
|
43
53
|
orderBy: { updatedAt: 'desc' },
|
|
44
54
|
});
|
|
45
55
|
if (!set) throw new TRPCError({ code: 'NOT_FOUND' });
|
|
46
56
|
return set.flashcards;
|
|
47
57
|
}),
|
|
58
|
+
isGenerating: authedProcedure
|
|
59
|
+
.input(z.object({ workspaceId: z.string() }))
|
|
60
|
+
.query(async ({ ctx, input }) => {
|
|
61
|
+
const artifact = await ctx.db.artifact.findFirst({
|
|
62
|
+
where: { workspaceId: input.workspaceId, type: ArtifactType.FLASHCARD_SET, workspace: { ownerId: ctx.session.user.id } }, orderBy: { updatedAt: 'desc' },
|
|
63
|
+
});
|
|
64
|
+
return artifact?.generating;
|
|
65
|
+
}),
|
|
48
66
|
createCard: authedProcedure
|
|
49
67
|
.input(z.object({
|
|
50
68
|
workspaceId: z.string(),
|
|
@@ -137,14 +155,13 @@ export const flashcards = router({
|
|
|
137
155
|
});
|
|
138
156
|
if (!workspace) throw new TRPCError({ code: 'NOT_FOUND' });
|
|
139
157
|
|
|
140
|
-
// Pusher start
|
|
141
|
-
await PusherService.emitTaskComplete(input.workspaceId, 'flash_card_load_start', { source: 'prompt' });
|
|
142
158
|
const flashcardCurrent = await ctx.db.artifact.findFirst({
|
|
143
159
|
where: {
|
|
144
160
|
workspaceId: input.workspaceId,
|
|
145
161
|
type: ArtifactType.FLASHCARD_SET,
|
|
146
162
|
},
|
|
147
163
|
select: {
|
|
164
|
+
id: true,
|
|
148
165
|
flashcards: true,
|
|
149
166
|
},
|
|
150
167
|
orderBy: {
|
|
@@ -152,6 +169,13 @@ export const flashcards = router({
|
|
|
152
169
|
},
|
|
153
170
|
});
|
|
154
171
|
|
|
172
|
+
try {
|
|
173
|
+
await ctx.db.artifact.update({
|
|
174
|
+
where: { id: flashcardCurrent?.id },
|
|
175
|
+
data: { generating: true, generatingMetadata: { quantity: input.numCards, difficulty: input.difficulty.toLowerCase() } },
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
await PusherService.emitTaskComplete(input.workspaceId, 'flash_card_info', { status: 'generating', numCards: input.numCards, difficulty: input.difficulty });
|
|
155
179
|
|
|
156
180
|
const formattedPreviousCards = flashcardCurrent?.flashcards.map((card) => ({
|
|
157
181
|
front: card.front,
|
|
@@ -169,22 +193,6 @@ export const flashcards = router({
|
|
|
169
193
|
|
|
170
194
|
The user has also left you this prompt: ${input.prompt}
|
|
171
195
|
`
|
|
172
|
-
// Init AI session and seed with prompt as instruction
|
|
173
|
-
const session = await aiSessionService.initSession(input.workspaceId, ctx.session.user.id);
|
|
174
|
-
await aiSessionService.setInstruction(session.id, partialPrompt);
|
|
175
|
-
|
|
176
|
-
await aiSessionService.startLLMSession(session.id);
|
|
177
|
-
|
|
178
|
-
const currentCards = flashcardCurrent?.flashcards.length || 0;
|
|
179
|
-
const newCards = input.numCards - currentCards;
|
|
180
|
-
|
|
181
|
-
// Generate
|
|
182
|
-
await PusherService.emitTaskComplete(input.workspaceId, 'flash_card_info', { status: 'generating', numCards: input.numCards, difficulty: input.difficulty });
|
|
183
|
-
const content = await aiSessionService.generateFlashcardQuestions(session.id, input.numCards, input.difficulty);
|
|
184
|
-
|
|
185
|
-
// Previous cards
|
|
186
|
-
|
|
187
|
-
// Create artifact
|
|
188
196
|
const artifact = await ctx.db.artifact.create({
|
|
189
197
|
data: {
|
|
190
198
|
workspaceId: input.workspaceId,
|
|
@@ -199,11 +207,17 @@ export const flashcards = router({
|
|
|
199
207
|
},
|
|
200
208
|
},
|
|
201
209
|
});
|
|
210
|
+
|
|
211
|
+
const currentCards = flashcardCurrent?.flashcards.length || 0;
|
|
212
|
+
const newCards = input.numCards - currentCards;
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
// Generate
|
|
216
|
+
const content = await aiSessionService.generateFlashcardQuestions(input.workspaceId, ctx.session.user.id, input.numCards, input.difficulty);
|
|
202
217
|
|
|
203
|
-
// Parse and create cards
|
|
204
218
|
let createdCards = 0;
|
|
205
219
|
try {
|
|
206
|
-
const flashcardData =
|
|
220
|
+
const flashcardData: any = content;
|
|
207
221
|
for (let i = 0; i < Math.min(flashcardData.length, input.numCards); i++) {
|
|
208
222
|
const card = flashcardData[i];
|
|
209
223
|
const front = card.term || card.front || card.question || card.prompt || `Question ${i + 1}`;
|
|
@@ -243,10 +257,80 @@ export const flashcards = router({
|
|
|
243
257
|
// Pusher complete
|
|
244
258
|
await PusherService.emitFlashcardComplete(input.workspaceId, artifact);
|
|
245
259
|
|
|
246
|
-
// Cleanup AI session (best-effort)
|
|
247
|
-
aiSessionService.deleteSession(session.id);
|
|
248
|
-
|
|
249
260
|
return { artifact, createdCards };
|
|
261
|
+
|
|
262
|
+
} catch (error) {
|
|
263
|
+
await ctx.db.artifact.update({ where: { id: flashcardCurrent?.id }, data: { generating: false } });
|
|
264
|
+
await PusherService.emitError(input.workspaceId, `Failed to generate flashcards: ${error}`, 'flash_card_generation');
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}),
|
|
268
|
+
|
|
269
|
+
// Record study attempt
|
|
270
|
+
recordStudyAttempt: authedProcedure
|
|
271
|
+
.input(z.object({
|
|
272
|
+
flashcardId: z.string().cuid(),
|
|
273
|
+
isCorrect: z.boolean(),
|
|
274
|
+
confidence: z.enum(['easy', 'medium', 'hard']).optional(),
|
|
275
|
+
timeSpentMs: z.number().optional(),
|
|
276
|
+
}))
|
|
277
|
+
.mutation(async ({ ctx, input }) => {
|
|
278
|
+
const service = createFlashcardProgressService(ctx.db);
|
|
279
|
+
return service.recordStudyAttempt({
|
|
280
|
+
userId: ctx.userId,
|
|
281
|
+
...input,
|
|
282
|
+
});
|
|
283
|
+
}),
|
|
284
|
+
|
|
285
|
+
// Get progress for a flashcard set
|
|
286
|
+
getSetProgress: authedProcedure
|
|
287
|
+
.input(z.object({ artifactId: z.string().cuid() }))
|
|
288
|
+
.query(async ({ ctx, input }) => {
|
|
289
|
+
const service = createFlashcardProgressService(ctx.db);
|
|
290
|
+
return service.getSetProgress(ctx.userId, input.artifactId);
|
|
291
|
+
}),
|
|
292
|
+
|
|
293
|
+
// Get flashcards due for review
|
|
294
|
+
getDueFlashcards: authedProcedure
|
|
295
|
+
.input(z.object({ workspaceId: z.string() }))
|
|
296
|
+
.query(async ({ ctx, input }) => {
|
|
297
|
+
const service = createFlashcardProgressService(ctx.db);
|
|
298
|
+
|
|
299
|
+
return service.getDueFlashcards(ctx.userId, input.workspaceId);
|
|
300
|
+
}),
|
|
301
|
+
|
|
302
|
+
// Get statistics for a flashcard set
|
|
303
|
+
getSetStatistics: authedProcedure
|
|
304
|
+
.input(z.object({ artifactId: z.string().cuid() }))
|
|
305
|
+
.query(async ({ ctx, input }) => {
|
|
306
|
+
const service = createFlashcardProgressService(ctx.db);
|
|
307
|
+
return service.getSetStatistics(ctx.userId, input.artifactId);
|
|
308
|
+
}),
|
|
309
|
+
|
|
310
|
+
// Reset progress for a flashcard
|
|
311
|
+
resetProgress: authedProcedure
|
|
312
|
+
.input(z.object({ flashcardId: z.string().cuid() }))
|
|
313
|
+
.mutation(async ({ ctx, input }) => {
|
|
314
|
+
const service = createFlashcardProgressService(ctx.db);
|
|
315
|
+
return service.resetProgress(ctx.userId, input.flashcardId);
|
|
316
|
+
}),
|
|
317
|
+
|
|
318
|
+
// Bulk record study session
|
|
319
|
+
recordStudySession: authedProcedure
|
|
320
|
+
.input(z.object({
|
|
321
|
+
attempts: z.array(z.object({
|
|
322
|
+
flashcardId: z.string().cuid(),
|
|
323
|
+
isCorrect: z.boolean(),
|
|
324
|
+
confidence: z.enum(['easy', 'medium', 'hard']).optional(),
|
|
325
|
+
timeSpentMs: z.number().optional(),
|
|
326
|
+
})),
|
|
327
|
+
}))
|
|
328
|
+
.mutation(async ({ ctx, input }) => {
|
|
329
|
+
const service = createFlashcardProgressService(ctx.db);
|
|
330
|
+
return service.recordStudySession({
|
|
331
|
+
userId: ctx.userId,
|
|
332
|
+
...input,
|
|
333
|
+
});
|
|
250
334
|
}),
|
|
251
335
|
});
|
|
252
336
|
|