@goscribe/server 1.3.3 → 1.5.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/.env.example +12 -0
- package/.vscode/settings.json +3 -0
- package/REFACTOR_NOTES.md +60 -0
- package/TESTING_PROMPT.md +225 -0
- package/dist/controllers/admin.controller.d.ts +715 -0
- package/dist/controllers/admin.controller.js +9 -0
- package/dist/controllers/annotations.controller.d.ts +439 -0
- package/dist/controllers/annotations.controller.js +9 -0
- package/dist/controllers/app-router.controller.d.ts +3011 -0
- package/dist/controllers/app-router.controller.js +38 -0
- package/dist/controllers/app-router.controller.test.d.ts +1 -0
- package/dist/controllers/app-router.controller.test.js +36 -0
- package/dist/controllers/auth.controller.d.ts +323 -0
- package/dist/controllers/auth.controller.js +9 -0
- package/dist/controllers/base.controller.d.ts +4 -0
- package/dist/controllers/base.controller.js +5 -0
- package/dist/controllers/chat.controller.d.ts +341 -0
- package/dist/controllers/chat.controller.js +9 -0
- package/dist/controllers/copilot.controller.d.ts +397 -0
- package/dist/controllers/copilot.controller.js +9 -0
- package/dist/controllers/flashcards.controller.d.ts +651 -0
- package/dist/controllers/flashcards.controller.js +9 -0
- package/dist/controllers/members.controller.d.ts +339 -0
- package/dist/controllers/members.controller.js +9 -0
- package/dist/controllers/notifications.controller.d.ts +199 -0
- package/dist/controllers/notifications.controller.js +9 -0
- package/dist/controllers/payment.controller.d.ts +181 -0
- package/dist/controllers/payment.controller.js +9 -0
- package/dist/controllers/podcast.controller.d.ts +575 -0
- package/dist/controllers/podcast.controller.js +9 -0
- package/dist/controllers/router-module.controller.d.ts +5 -0
- package/dist/controllers/router-module.controller.js +6 -0
- package/dist/controllers/studyguide.controller.d.ts +73 -0
- package/dist/controllers/studyguide.controller.js +9 -0
- package/dist/controllers/worksheets.controller.d.ts +829 -0
- package/dist/controllers/worksheets.controller.js +9 -0
- package/dist/controllers/workspace.controller.d.ts +1207 -0
- package/dist/controllers/workspace.controller.js +9 -0
- package/dist/lib/activity_human_description.test.js +16 -15
- package/dist/lib/activity_log_service.test.js +28 -23
- package/dist/lib/ai/config.d.ts +20 -0
- package/dist/lib/ai/config.js +31 -0
- package/dist/lib/ai/embedding-client.d.ts +8 -0
- package/dist/lib/ai/embedding-client.js +30 -0
- package/dist/lib/ai/index.d.ts +47 -0
- package/dist/lib/ai/index.js +28 -0
- package/dist/lib/ai/inference-backend/client.d.ts +28 -0
- package/dist/lib/ai/inference-backend/client.js +301 -0
- package/dist/lib/ai/inference-backend/mocks.d.ts +12 -0
- package/dist/lib/ai/inference-backend/mocks.js +133 -0
- package/dist/lib/ai/inference-backend/types.d.ts +44 -0
- package/dist/lib/ai/inference-backend/types.js +1 -0
- package/dist/lib/ai/json-parse.d.ts +2 -0
- package/dist/lib/ai/json-parse.js +34 -0
- package/dist/lib/ai/llm-client.d.ts +6 -0
- package/dist/lib/ai/llm-client.js +19 -0
- package/dist/lib/ai/mock.d.ts +2 -0
- package/dist/lib/ai/mock.js +10 -0
- package/dist/lib/ai/types.d.ts +9 -0
- package/dist/lib/ai/types.js +1 -0
- package/dist/lib/chunking.d.ts +19 -0
- package/dist/lib/chunking.js +47 -0
- package/dist/lib/curated-kb-seed.d.ts +12 -0
- package/dist/lib/curated-kb-seed.js +155 -0
- package/dist/lib/email.js +67 -108
- package/dist/lib/embeddings.d.ts +2 -0
- package/dist/lib/embeddings.js +1 -0
- package/dist/lib/ensure-curated-kb-catalog.d.ts +6 -0
- package/dist/lib/ensure-curated-kb-catalog.js +53 -0
- package/dist/lib/env.d.ts +1 -5
- package/dist/lib/env.js +2 -7
- package/dist/lib/inference.d.ts +1 -8
- package/dist/lib/inference.js +1 -19
- package/dist/lib/kb-meta.d.ts +8 -0
- package/dist/lib/kb-meta.js +77 -0
- package/dist/lib/note-text.d.ts +1 -0
- package/dist/lib/note-text.js +47 -0
- package/dist/lib/notification-service.test.js +37 -36
- package/dist/lib/pdf.d.ts +11 -0
- package/dist/lib/pdf.js +11 -0
- package/dist/lib/usage_service.d.ts +2 -1
- package/dist/lib/usage_service.js +30 -12
- package/dist/lib/worksheet-generation.js +4 -4
- package/dist/lib/worksheet-generation.test.js +32 -17
- package/dist/lib/workspace-kb.d.ts +5 -0
- package/dist/lib/workspace-kb.js +7 -0
- package/dist/models/controller-context.model.d.ts +8 -0
- package/dist/models/controller-context.model.js +1 -0
- package/dist/repositories/artifact.repository.d.ts +60 -0
- package/dist/repositories/artifact.repository.js +40 -0
- package/dist/repositories/base.repository.d.ts +14 -0
- package/dist/repositories/base.repository.js +14 -0
- package/dist/repositories/invitation.repository.d.ts +94 -0
- package/dist/repositories/invitation.repository.js +44 -0
- package/dist/repositories/notification.repository.d.ts +72 -0
- package/dist/repositories/notification.repository.js +44 -0
- package/dist/repositories/router-module.repository.d.ts +10 -0
- package/dist/repositories/router-module.repository.js +14 -0
- package/dist/repositories/user.repository.d.ts +74 -0
- package/dist/repositories/user.repository.js +37 -0
- package/dist/repositories/workspace-member.repository.d.ts +31 -0
- package/dist/repositories/workspace-member.repository.js +31 -0
- package/dist/repositories/workspace.repository.d.ts +97 -0
- package/dist/repositories/workspace.repository.js +79 -0
- package/dist/routers/_app.d.ts +528 -33
- package/dist/routers/_app.js +4 -0
- package/dist/routers/admin.d.ts +0 -4
- package/dist/routers/admin.js +21 -549
- package/dist/routers/annotations.js +12 -170
- package/dist/routers/artifactVersions.d.ts +65 -0
- package/dist/routers/artifactVersions.js +14 -0
- package/dist/routers/auth.d.ts +0 -6
- package/dist/routers/auth.js +36 -421
- package/dist/routers/chat.js +15 -229
- package/dist/routers/copilot.d.ts +14 -13
- package/dist/routers/copilot.js +13 -532
- package/dist/routers/flashcards.d.ts +5 -5
- package/dist/routers/flashcards.js +23 -349
- package/dist/routers/knowledgeBase.d.ts +421 -0
- package/dist/routers/knowledgeBase.js +118 -0
- package/dist/routers/members.d.ts +0 -41
- package/dist/routers/members.js +22 -710
- package/dist/routers/notes.d.ts +94 -0
- package/dist/routers/notes.js +37 -0
- package/dist/routers/notifications.js +7 -109
- package/dist/routers/payment.d.ts +3 -2
- package/dist/routers/payment.js +11 -393
- package/dist/routers/podcast.d.ts +1 -1
- package/dist/routers/podcast.js +11 -784
- package/dist/routers/studyguide.js +3 -129
- package/dist/routers/worksheets.d.ts +29 -14
- package/dist/routers/worksheets.js +49 -628
- package/dist/routers/workspace.d.ts +0 -4
- package/dist/routers/workspace.js +28 -922
- package/dist/scripts/purge-deleted-users.js +2 -2
- package/dist/server.js +10 -3
- package/dist/services/activity/activity-human-description.service.d.ts +13 -0
- package/dist/services/activity/activity-human-description.service.js +221 -0
- package/dist/services/activity/activity-human-description.service.test.d.ts +1 -0
- package/dist/services/activity/activity-human-description.service.test.js +16 -0
- package/dist/services/activity/activity-log.service.d.ts +87 -0
- package/dist/services/activity/activity-log.service.js +276 -0
- package/dist/services/activity/activity-log.service.test.d.ts +1 -0
- package/dist/services/activity/activity-log.service.test.js +27 -0
- package/dist/services/activity-human-description.service.d.ts +13 -0
- package/dist/services/activity-human-description.service.js +221 -0
- package/dist/services/activity-human-description.service.test.d.ts +1 -0
- package/dist/services/activity-human-description.service.test.js +16 -0
- package/dist/services/activity-log.service.d.ts +87 -0
- package/dist/services/activity-log.service.js +276 -0
- package/dist/services/activity-log.service.test.d.ts +1 -0
- package/dist/services/activity-log.service.test.js +27 -0
- package/dist/services/admin/admin.service.d.ts +270 -0
- package/dist/services/admin/admin.service.js +476 -0
- package/dist/services/admin.service.d.ts +270 -0
- package/dist/services/admin.service.js +476 -0
- package/dist/services/ai/ai-session.service.d.ts +5 -0
- package/dist/services/ai/ai-session.service.js +4 -0
- package/dist/services/ai-session.service.d.ts +60 -0
- package/dist/services/ai-session.service.js +561 -0
- package/dist/services/annotation.service.d.ts +177 -0
- package/dist/services/annotation.service.js +154 -0
- package/dist/services/artifact-notification.service.d.ts +14 -0
- package/dist/services/artifact-notification.service.js +20 -0
- package/dist/services/artifact-version.service.d.ts +38 -0
- package/dist/services/artifact-version.service.js +129 -0
- package/dist/services/artifacts/annotation.service.d.ts +177 -0
- package/dist/services/artifacts/annotation.service.js +154 -0
- package/dist/services/artifacts/artifact-version.service.d.ts +38 -0
- package/dist/services/artifacts/artifact-version.service.js +129 -0
- package/dist/services/artifacts/chat.service.d.ts +127 -0
- package/dist/services/artifacts/chat.service.js +182 -0
- package/dist/services/artifacts/study-guide.service.d.ts +18 -0
- package/dist/services/artifacts/study-guide.service.js +65 -0
- package/dist/services/auth/auth.service.d.ts +94 -0
- package/dist/services/auth/auth.service.js +368 -0
- package/dist/services/auth.service.d.ts +94 -0
- package/dist/services/auth.service.js +368 -0
- package/dist/services/base.service.d.ts +14 -0
- package/dist/services/base.service.js +14 -0
- package/dist/services/billing/payment.service.d.ts +55 -0
- package/dist/services/billing/payment.service.js +368 -0
- package/dist/services/billing/subscription.service.d.ts +37 -0
- package/dist/services/billing/subscription.service.js +654 -0
- package/dist/services/billing/usage.service.d.ts +27 -0
- package/dist/services/billing/usage.service.js +77 -0
- package/dist/services/chat.service.d.ts +127 -0
- package/dist/services/chat.service.js +182 -0
- package/dist/services/content/copilot.service.d.ts +113 -0
- package/dist/services/content/copilot.service.js +453 -0
- package/dist/services/content/flashcard-progress.service.d.ts +159 -0
- package/dist/services/content/flashcard-progress.service.js +432 -0
- package/dist/services/content/flashcard.service.d.ts +140 -0
- package/dist/services/content/flashcard.service.js +326 -0
- package/dist/services/content/media-analysis.service.d.ts +23 -0
- package/dist/services/content/media-analysis.service.js +404 -0
- package/dist/services/content/podcast.service.d.ts +267 -0
- package/dist/services/content/podcast.service.js +653 -0
- package/dist/services/content/worksheet-content.service.d.ts +37 -0
- package/dist/services/content/worksheet-content.service.js +84 -0
- package/dist/services/content/worksheet-content.service.test.d.ts +1 -0
- package/dist/services/content/worksheet-content.service.test.js +69 -0
- package/dist/services/content/worksheet-generation.service.d.ts +91 -0
- package/dist/services/content/worksheet-generation.service.js +95 -0
- package/dist/services/content/worksheet-generation.service.test.d.ts +1 -0
- package/dist/services/content/worksheet-generation.service.test.js +20 -0
- package/dist/services/content/worksheet.service.d.ts +347 -0
- package/dist/services/content/worksheet.service.js +599 -0
- package/dist/services/copilot.service.d.ts +116 -0
- package/dist/services/copilot.service.js +447 -0
- package/dist/services/flashcard-progress.service.d.ts +2 -2
- package/dist/services/flashcard-progress.service.js +3 -2
- package/dist/services/flashcard.service.d.ts +140 -0
- package/dist/services/flashcard.service.js +325 -0
- package/dist/services/invitation.service.d.ts +66 -0
- package/dist/services/invitation.service.js +348 -0
- package/dist/services/knowledge/knowledge-base.service.d.ts +316 -0
- package/dist/services/knowledge/knowledge-base.service.js +544 -0
- package/dist/services/knowledge-base.service.d.ts +316 -0
- package/dist/services/knowledge-base.service.js +536 -0
- package/dist/services/media-analysis.service.d.ts +23 -0
- package/dist/services/media-analysis.service.js +384 -0
- package/dist/services/member.service.d.ts +36 -0
- package/dist/services/member.service.js +193 -0
- package/dist/services/members/invitation.service.d.ts +66 -0
- package/dist/services/members/invitation.service.js +348 -0
- package/dist/services/members/member.service.d.ts +36 -0
- package/dist/services/members/member.service.js +193 -0
- package/dist/services/note.service.d.ts +55 -0
- package/dist/services/note.service.js +111 -0
- package/dist/services/notification.service.d.ts +214 -0
- package/dist/services/notification.service.js +550 -0
- package/dist/services/notification.service.test.d.ts +1 -0
- package/dist/services/notification.service.test.js +87 -0
- package/dist/services/notifications/notification.service.d.ts +214 -0
- package/dist/services/notifications/notification.service.js +550 -0
- package/dist/services/notifications/notification.service.test.d.ts +1 -0
- package/dist/services/notifications/notification.service.test.js +87 -0
- package/dist/services/payment.service.d.ts +55 -0
- package/dist/services/payment.service.js +368 -0
- package/dist/services/podcast.service.d.ts +267 -0
- package/dist/services/podcast.service.js +654 -0
- package/dist/services/router-module.service.d.ts +7 -0
- package/dist/services/router-module.service.js +10 -0
- package/dist/services/study-guide.service.d.ts +18 -0
- package/dist/services/study-guide.service.js +65 -0
- package/dist/services/subscription.service.d.ts +37 -0
- package/dist/services/subscription.service.js +654 -0
- package/dist/services/usage-limit-policy.service.d.ts +12 -0
- package/dist/services/usage-limit-policy.service.js +22 -0
- package/dist/services/usage-limit-policy.service.test.d.ts +1 -0
- package/dist/services/usage-limit-policy.service.test.js +46 -0
- package/dist/services/usage.service.d.ts +27 -0
- package/dist/services/usage.service.js +77 -0
- package/dist/services/worksheet-content.service.d.ts +42 -0
- package/dist/services/worksheet-content.service.js +84 -0
- package/dist/services/worksheet-content.service.test.d.ts +1 -0
- package/dist/services/worksheet-content.service.test.js +69 -0
- package/dist/services/worksheet-generation.service.d.ts +91 -0
- package/dist/services/worksheet-generation.service.js +95 -0
- package/dist/services/worksheet-generation.service.test.d.ts +1 -0
- package/dist/services/worksheet-generation.service.test.js +20 -0
- package/dist/services/worksheet.service.d.ts +385 -0
- package/dist/services/worksheet.service.js +596 -0
- package/dist/services/workspace/workspace-analytics.service.d.ts +24 -0
- package/dist/services/workspace/workspace-analytics.service.js +95 -0
- package/dist/services/workspace/workspace-kb.service.d.ts +40 -0
- package/dist/services/workspace/workspace-kb.service.js +184 -0
- package/dist/services/workspace/workspace.service.d.ts +307 -0
- package/dist/services/workspace/workspace.service.js +394 -0
- package/dist/services/workspace-analytics.service.d.ts +24 -0
- package/dist/services/workspace-analytics.service.js +95 -0
- package/dist/services/workspace-kb.service.d.ts +40 -0
- package/dist/services/workspace-kb.service.js +184 -0
- package/dist/services/workspace-progress.service.d.ts +27 -0
- package/dist/services/workspace-progress.service.js +56 -0
- package/dist/services/workspace-progress.service.test.d.ts +1 -0
- package/dist/services/workspace-progress.service.test.js +49 -0
- package/dist/services/workspace.service.d.ts +307 -0
- package/dist/services/workspace.service.js +390 -0
- package/dist/trpc.js +2 -2
- package/package.json +5 -6
- package/prisma/migrations/20260509000001_add_knowledge_base/migration.sql +99 -0
- package/prisma/migrations/20260509000002_curate_knowledge_base/migration.sql +52 -0
- package/prisma/migrations/20260522000000_add_notes/migration.sql +27 -0
- package/prisma/migrations/20260524000000_remove_notes/migration.sql +3 -0
- package/prisma/schema.prisma +150 -48
- package/prisma/seed.mjs +67 -0
- package/scripts/debug/README.md +4 -0
- package/src/README.md +63 -0
- package/src/lib/ai/config.ts +34 -0
- package/src/lib/ai/embedding-client.ts +47 -0
- package/src/lib/ai/index.ts +62 -0
- package/src/lib/ai/inference-backend/client.ts +479 -0
- package/src/lib/ai/inference-backend/mocks.ts +171 -0
- package/src/lib/ai/inference-backend/types.ts +50 -0
- package/src/lib/ai/json-parse.ts +35 -0
- package/src/lib/ai/llm-client.ts +31 -0
- package/src/lib/ai/mock.ts +12 -0
- package/src/lib/ai/types.ts +11 -0
- package/src/lib/chunking.ts +81 -0
- package/src/lib/curated-kb-seed.ts +164 -0
- package/src/lib/email.ts +77 -115
- package/src/lib/embeddings.ts +9 -0
- package/src/lib/ensure-curated-kb-catalog.ts +60 -0
- package/src/lib/env.ts +2 -7
- package/src/lib/inference.ts +1 -21
- package/src/lib/kb-meta.ts +81 -0
- package/src/lib/pdf.ts +23 -0
- package/src/lib/workspace-kb.ts +7 -0
- package/src/repositories/artifact.repository.ts +55 -0
- package/src/repositories/base.repository.ts +19 -0
- package/src/repositories/invitation.repository.ts +53 -0
- package/src/repositories/notification.repository.ts +53 -0
- package/src/repositories/user.repository.ts +44 -0
- package/src/repositories/workspace-member.repository.ts +38 -0
- package/src/repositories/workspace.repository.ts +89 -0
- package/src/routers/_app.ts +4 -0
- package/src/routers/admin.ts +124 -692
- package/src/routers/annotations.ts +25 -203
- package/src/routers/artifactVersions.ts +32 -0
- package/src/routers/auth.ts +81 -519
- package/src/routers/chat.ts +42 -245
- package/src/routers/copilot.ts +41 -666
- package/src/routers/flashcards.ts +108 -404
- package/src/routers/knowledgeBase.ts +216 -0
- package/src/routers/members.ts +60 -782
- package/src/routers/notifications.ts +15 -117
- package/src/routers/payment.ts +37 -446
- package/src/routers/podcast.ts +36 -898
- package/src/routers/studyguide.ts +5 -144
- package/src/routers/worksheets.ts +171 -735
- package/src/routers/workspace.ts +138 -1109
- package/src/scripts/purge-deleted-users.ts +2 -2
- package/src/server.ts +10 -3
- package/src/{lib/activity_human_description.test.ts → services/activity/activity-human-description.service.test.ts} +1 -1
- package/src/{lib/activity_log_service.test.ts → services/activity/activity-log.service.test.ts} +1 -1
- package/src/{lib/activity_log_service.ts → services/activity/activity-log.service.ts} +2 -2
- package/src/services/admin/admin.service.ts +612 -0
- package/src/services/ai/ai-session.service.ts +5 -0
- package/src/services/artifacts/annotation.service.ts +189 -0
- package/src/services/artifacts/artifact-version.service.ts +151 -0
- package/src/services/artifacts/chat.service.ts +197 -0
- package/src/services/artifacts/study-guide.service.ts +72 -0
- package/src/services/auth/auth.service.ts +473 -0
- package/src/services/base.service.ts +19 -0
- package/src/services/billing/payment.service.ts +436 -0
- package/src/{lib/subscription_service.ts → services/billing/subscription.service.ts} +5 -5
- package/src/{lib/usage_service.ts → services/billing/usage.service.ts} +32 -12
- package/src/services/content/copilot.service.ts +596 -0
- package/src/services/{flashcard-progress.service.ts → content/flashcard-progress.service.ts} +6 -3
- package/src/services/content/flashcard.service.ts +394 -0
- package/src/services/content/media-analysis.service.ts +556 -0
- package/src/services/content/podcast.service.ts +777 -0
- package/src/services/content/worksheet-content.service.test.ts +83 -0
- package/src/services/content/worksheet-content.service.ts +117 -0
- package/src/{lib/worksheet-generation.test.ts → services/content/worksheet-generation.service.test.ts} +1 -1
- package/src/services/content/worksheet.service.ts +751 -0
- package/src/services/knowledge/knowledge-base.service.ts +705 -0
- package/src/services/members/invitation.service.ts +427 -0
- package/src/services/members/member.service.ts +241 -0
- package/src/{lib/notification-service.test.ts → services/notifications/notification.service.test.ts} +2 -2
- package/src/{lib/notification-service.ts → services/notifications/notification.service.ts} +102 -1
- package/src/services/workspace/workspace-analytics.service.ts +107 -0
- package/src/services/workspace/workspace-kb.service.ts +273 -0
- package/src/services/workspace/workspace.service.ts +481 -0
- package/src/trpc.ts +2 -2
- package/src/lib/ai-session.ts +0 -704
- package/src/lib/workspace-access.ts +0 -13
- /package/{check-difficulty.cjs → scripts/debug/check-difficulty.cjs} +0 -0
- /package/{check-questions.cjs → scripts/debug/check-questions.cjs} +0 -0
- /package/{db-summary.cjs → scripts/debug/db-summary.cjs} +0 -0
- /package/{mcq-test.cjs → scripts/debug/mcq-test.cjs} +0 -0
- /package/{test-generate.js → scripts/debug/test-generate.js} +0 -0
- /package/{test-ratio.cjs → scripts/debug/test-ratio.cjs} +0 -0
- /package/{zod-test.cjs → scripts/debug/zod-test.cjs} +0 -0
- /package/src/{lib/activity_human_description.ts → services/activity/activity-human-description.service.ts} +0 -0
- /package/src/{lib/worksheet-generation.ts → services/content/worksheet-generation.service.ts} +0 -0
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
|
2
|
+
import { logger } from '../lib/logger.js';
|
|
3
|
+
import { withRetry } from '../lib/retry.js';
|
|
4
|
+
// External AI service configuration
|
|
5
|
+
// const AI_SERVICE_URL = 'https://7gzvf7uib04yp9-61016.proxy.runpod.net/upload';
|
|
6
|
+
// const AI_RESPONSE_URL = 'https://7gzvf7uib04yp9-61016.proxy.runpod.net/last_response';
|
|
7
|
+
const AI_SERVICE_URL = process.env.INFERENCE_API_URL + '/upload';
|
|
8
|
+
const AI_RESPONSE_URL = process.env.INFERENCE_API_URL + '/last_response';
|
|
9
|
+
logger.info(`AI_SERVICE_URL: ${AI_SERVICE_URL}`);
|
|
10
|
+
logger.info(`AI_RESPONSE_URL: ${AI_RESPONSE_URL}`);
|
|
11
|
+
// Mock mode flag - when true, returns fake responses instead of calling AI service
|
|
12
|
+
const MOCK_MODE = process.env.DONT_TEST_INFERENCE === 'true';
|
|
13
|
+
const IMITATE_WAIT_TIME_MS = MOCK_MODE ? 1000 * 10 : 0;
|
|
14
|
+
export class AISessionService {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.sessions = new Map();
|
|
17
|
+
}
|
|
18
|
+
// Initialize a new AI session
|
|
19
|
+
async initSession(workspaceId, user) {
|
|
20
|
+
const sessionId = `${workspaceId}`;
|
|
21
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
22
|
+
// Mock mode - return fake session
|
|
23
|
+
if (MOCK_MODE) {
|
|
24
|
+
logger.info(`MOCK MODE: Initializing AI session for workspace ${workspaceId}`);
|
|
25
|
+
const session = {
|
|
26
|
+
id: sessionId,
|
|
27
|
+
workspaceId,
|
|
28
|
+
status: 'initialized',
|
|
29
|
+
files: [],
|
|
30
|
+
createdAt: new Date(),
|
|
31
|
+
updatedAt: new Date(),
|
|
32
|
+
};
|
|
33
|
+
this.sessions.set(sessionId, session);
|
|
34
|
+
return session;
|
|
35
|
+
}
|
|
36
|
+
const formData = new FormData();
|
|
37
|
+
formData.append('command', 'init_session');
|
|
38
|
+
formData.append('session', sessionId);
|
|
39
|
+
formData.append('user', user);
|
|
40
|
+
// Retry logic for AI service
|
|
41
|
+
const maxRetries = 3;
|
|
42
|
+
let lastError = null;
|
|
43
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
44
|
+
try {
|
|
45
|
+
logger.info(`AI Session init attempt ${attempt}/${maxRetries} for workspace ${workspaceId}`);
|
|
46
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
body: formData,
|
|
49
|
+
});
|
|
50
|
+
logger.info(`AI Service response status: ${response.status} ${response.statusText}`);
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
const errorText = await response.text();
|
|
53
|
+
logger.error(`AI Service error response: ${errorText}`);
|
|
54
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
55
|
+
}
|
|
56
|
+
const result = await response.json();
|
|
57
|
+
logger.debug(`AI Service result: ${JSON.stringify(result)}`);
|
|
58
|
+
// If we get a response with a message, consider it successful
|
|
59
|
+
if (!result.message) {
|
|
60
|
+
throw new Error(`AI service error: No response message`);
|
|
61
|
+
}
|
|
62
|
+
const session = {
|
|
63
|
+
id: sessionId,
|
|
64
|
+
workspaceId,
|
|
65
|
+
status: 'initialized',
|
|
66
|
+
files: [],
|
|
67
|
+
createdAt: new Date(),
|
|
68
|
+
updatedAt: new Date(),
|
|
69
|
+
};
|
|
70
|
+
this.sessions.set(sessionId, session);
|
|
71
|
+
logger.info(`AI Session initialized successfully on attempt ${attempt}`);
|
|
72
|
+
return session;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
lastError = error instanceof Error ? error : new Error('Unknown error');
|
|
76
|
+
logger.error(`AI Session init attempt ${attempt} failed: ${lastError.message}`);
|
|
77
|
+
if (attempt < maxRetries) {
|
|
78
|
+
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff: 2s, 4s, 8s
|
|
79
|
+
logger.info(`Retrying in ${delay}ms...`);
|
|
80
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
logger.error(`All ${maxRetries} attempts failed. Last error: ${lastError?.message}`);
|
|
85
|
+
throw new TRPCError({
|
|
86
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
87
|
+
message: `Failed to initialize AI session after ${maxRetries} attempts: ${lastError?.message || 'Unknown error'}`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Process file (PDF/image) and return comprehensive text descriptions
|
|
91
|
+
async processFile(sessionId, user, fileUrl, fileType, maxPages) {
|
|
92
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
93
|
+
// Mock mode - return fake processing result
|
|
94
|
+
if (MOCK_MODE) {
|
|
95
|
+
logger.info(`🎭 MOCK MODE: Processing ${fileType} file from URL for session ${sessionId}`);
|
|
96
|
+
const mockPageCount = fileType === 'pdf' ? 15 : 1;
|
|
97
|
+
return {
|
|
98
|
+
status: 'success',
|
|
99
|
+
textContent: `Mock extracted text content from ${fileType} file. This would contain the full text extracted from the document.`,
|
|
100
|
+
imageDescriptions: Array.from({ length: mockPageCount }, (_, i) => ({
|
|
101
|
+
page: i + 1,
|
|
102
|
+
description: `Page ${i + 1} contains educational content with diagrams and text.`,
|
|
103
|
+
hasVisualContent: true,
|
|
104
|
+
})),
|
|
105
|
+
comprehensiveDescription: `DOCUMENT SUMMARY (${mockPageCount} ${mockPageCount === 1 ? 'page' : 'pages'})\n\nTEXT CONTENT:\nMock extracted text content...\n\nVISUAL CONTENT DESCRIPTIONS:\nPage-by-page descriptions of visual elements.`,
|
|
106
|
+
pageCount: mockPageCount,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const formData = new FormData();
|
|
110
|
+
formData.append('command', 'process_file');
|
|
111
|
+
formData.append('fileUrl', fileUrl);
|
|
112
|
+
formData.append('fileType', fileType);
|
|
113
|
+
if (maxPages) {
|
|
114
|
+
formData.append('maxPages', maxPages.toString());
|
|
115
|
+
}
|
|
116
|
+
logger.debug('Processing file with formData');
|
|
117
|
+
// Retry logic for file processing
|
|
118
|
+
const maxRetries = 3;
|
|
119
|
+
let lastError = null;
|
|
120
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
121
|
+
try {
|
|
122
|
+
logger.info(`📄 Processing ${fileType} file attempt ${attempt}/${maxRetries} for session ${sessionId}`);
|
|
123
|
+
// Set timeout for large files (5 minutes)
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
const timeoutId = setTimeout(() => controller.abort(), 300000); // 5 min timeout
|
|
126
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
body: formData,
|
|
129
|
+
signal: controller.signal,
|
|
130
|
+
});
|
|
131
|
+
clearTimeout(timeoutId);
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
const errorText = await response.text();
|
|
134
|
+
logger.error(`❌ File processing error response:`, errorText);
|
|
135
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
136
|
+
}
|
|
137
|
+
const result = await response.json();
|
|
138
|
+
logger.info(`📋 File processing result: status=${result.status}, pageCount=${result.pageCount}`);
|
|
139
|
+
if (result.status === 'error') {
|
|
140
|
+
throw new Error(result.error || 'File processing failed');
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
lastError = error instanceof Error ? error : new Error('Unknown error');
|
|
146
|
+
logger.error(`❌ File processing attempt ${attempt} failed:`, lastError.message);
|
|
147
|
+
if (attempt < maxRetries) {
|
|
148
|
+
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff: 2s, 4s, 8s
|
|
149
|
+
logger.info(`⏳ Retrying file processing in ${delay}ms...`);
|
|
150
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
logger.error(`💥 All ${maxRetries} file processing attempts failed. Last error:`, lastError?.message);
|
|
155
|
+
return {
|
|
156
|
+
status: 'error',
|
|
157
|
+
textContent: null,
|
|
158
|
+
imageDescriptions: [],
|
|
159
|
+
comprehensiveDescription: null,
|
|
160
|
+
pageCount: 0,
|
|
161
|
+
error: `Failed to process file after ${maxRetries} attempts: ${lastError?.message || 'Unknown error'}`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// Generate study guide
|
|
165
|
+
async generateStudyGuide(sessionId, user) {
|
|
166
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
167
|
+
// Mock mode - return fake study guide
|
|
168
|
+
if (MOCK_MODE) {
|
|
169
|
+
logger.info(`MOCK MODE: Generating study guide for session ${sessionId}`);
|
|
170
|
+
return `# Mock Study Guide
|
|
171
|
+
|
|
172
|
+
## Overview
|
|
173
|
+
This is a mock study guide generated for testing purposes. In a real scenario, this would contain comprehensive study material based on the uploaded content.
|
|
174
|
+
|
|
175
|
+
## Key Concepts
|
|
176
|
+
1. **Concept A**: This is a mock concept that would be derived from the uploaded materials
|
|
177
|
+
2. **Concept B**: Another mock concept with detailed explanations
|
|
178
|
+
3. **Concept C**: A third concept with examples and applications
|
|
179
|
+
|
|
180
|
+
## Summary
|
|
181
|
+
This mock study guide demonstrates the structure and format that would be generated by the AI service when processing uploaded educational materials.
|
|
182
|
+
|
|
183
|
+
## Practice Questions
|
|
184
|
+
1. What is the main topic covered in this material?
|
|
185
|
+
2. How do the key concepts relate to each other?
|
|
186
|
+
3. What are the practical applications of these concepts?
|
|
187
|
+
|
|
188
|
+
*Note: This is a mock response generated when DONT_TEST_INFERENCE=true*`;
|
|
189
|
+
}
|
|
190
|
+
return withRetry(async () => {
|
|
191
|
+
const formData = new FormData();
|
|
192
|
+
formData.append('command', 'generate_study_guide');
|
|
193
|
+
formData.append('session', sessionId);
|
|
194
|
+
formData.append('user', user);
|
|
195
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
body: formData,
|
|
198
|
+
});
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
const errorBody = await response.text().catch(() => '');
|
|
201
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
202
|
+
}
|
|
203
|
+
const result = await response.json();
|
|
204
|
+
if (!result.markdown) {
|
|
205
|
+
throw new Error('AI service returned empty study guide');
|
|
206
|
+
}
|
|
207
|
+
return result.markdown;
|
|
208
|
+
}, { maxRetries: 3, timeoutMs: 300000, label: 'generateStudyGuide' });
|
|
209
|
+
}
|
|
210
|
+
async generateFlashcardQuestions(sessionId, user, numQuestions, difficulty, prompt) {
|
|
211
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
212
|
+
// Mock mode - return fake flashcard questions
|
|
213
|
+
if (MOCK_MODE) {
|
|
214
|
+
logger.info(`🎭 MOCK MODE: Generating ${numQuestions} ${difficulty} flashcard questions for session ${sessionId}`);
|
|
215
|
+
return JSON.stringify(Array.from({ length: numQuestions }, (_, i) => ({
|
|
216
|
+
id: `mock-flashcard-${i + 1}`,
|
|
217
|
+
question: `Mock question ${i + 1}: What is the main concept covered in this material?`,
|
|
218
|
+
answer: `Mock answer ${i + 1}: This is a sample answer that would be generated based on the uploaded content.`,
|
|
219
|
+
difficulty: difficulty,
|
|
220
|
+
category: `Mock Category ${(i % 3) + 1}`
|
|
221
|
+
})));
|
|
222
|
+
}
|
|
223
|
+
return withRetry(async () => {
|
|
224
|
+
const formData = new FormData();
|
|
225
|
+
formData.append('command', 'generate_flashcard_questions');
|
|
226
|
+
formData.append('session', sessionId);
|
|
227
|
+
formData.append('user', user);
|
|
228
|
+
formData.append('num_questions', numQuestions.toString());
|
|
229
|
+
formData.append('difficulty', difficulty);
|
|
230
|
+
if (prompt) {
|
|
231
|
+
formData.append('prompt', prompt);
|
|
232
|
+
}
|
|
233
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
234
|
+
method: 'POST',
|
|
235
|
+
body: formData,
|
|
236
|
+
});
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText}`);
|
|
239
|
+
}
|
|
240
|
+
const result = await response.json();
|
|
241
|
+
return JSON.parse(result.flashcards).flashcards;
|
|
242
|
+
}, { maxRetries: 3, timeoutMs: 300000, label: 'generateFlashcardQuestions' });
|
|
243
|
+
}
|
|
244
|
+
// Generate worksheet questions
|
|
245
|
+
async generateWorksheetQuestions(sessionId, user, numQuestions, difficulty, options) {
|
|
246
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
247
|
+
// Mock mode - return fake worksheet questions
|
|
248
|
+
if (MOCK_MODE) {
|
|
249
|
+
logger.info(`🎭 MOCK MODE: Generating ${numQuestions} ${difficulty} worksheet questions for session ${sessionId}`);
|
|
250
|
+
const mode = options?.mode ?? 'practice';
|
|
251
|
+
const isQuiz = mode === 'quiz';
|
|
252
|
+
return JSON.stringify({
|
|
253
|
+
title: isQuiz ? `Mock Quiz - ${difficulty}` : `Mock Worksheet - ${difficulty} Level`,
|
|
254
|
+
description: 'Mock generated content',
|
|
255
|
+
difficulty,
|
|
256
|
+
estimatedTime: `${numQuestions * 2} min`,
|
|
257
|
+
problems: Array.from({ length: numQuestions }, (_, i) => {
|
|
258
|
+
if (isQuiz) {
|
|
259
|
+
return {
|
|
260
|
+
question: `Mock MCQ ${i + 1}: What is 2+2?`,
|
|
261
|
+
answer: '1',
|
|
262
|
+
type: 'MULTIPLE_CHOICE',
|
|
263
|
+
options: ['3', '4', '5', '6'],
|
|
264
|
+
mark_scheme: { points: [{ point: 1, requirements: 'Select correct option' }], totalPoints: 1 },
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
question: `Mock question ${i + 1}: Explain a concept.`,
|
|
269
|
+
answer: 'Mock answer',
|
|
270
|
+
type: 'TEXT',
|
|
271
|
+
options: [],
|
|
272
|
+
mark_scheme: { points: [{ point: 1, requirements: 'Clear explanation' }], totalPoints: 1 },
|
|
273
|
+
};
|
|
274
|
+
}),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return withRetry(async () => {
|
|
278
|
+
const formData = new FormData();
|
|
279
|
+
formData.append('command', 'generate_worksheet_questions');
|
|
280
|
+
formData.append('session', sessionId);
|
|
281
|
+
formData.append('user', user);
|
|
282
|
+
formData.append('num_questions', numQuestions.toString());
|
|
283
|
+
formData.append('difficulty', difficulty);
|
|
284
|
+
formData.append('mode', options?.mode ?? 'practice');
|
|
285
|
+
if (options?.mcqRatio !== undefined) {
|
|
286
|
+
formData.append('mcq_ratio', String(options.mcqRatio));
|
|
287
|
+
}
|
|
288
|
+
if (options?.questionTypes?.length) {
|
|
289
|
+
formData.append('question_types', JSON.stringify(options.questionTypes));
|
|
290
|
+
}
|
|
291
|
+
if (options?.prompt) {
|
|
292
|
+
formData.append('worksheet_prompt', options.prompt);
|
|
293
|
+
}
|
|
294
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
295
|
+
method: 'POST',
|
|
296
|
+
body: formData,
|
|
297
|
+
});
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText}`);
|
|
300
|
+
}
|
|
301
|
+
const result = await response.json();
|
|
302
|
+
return result.worksheet;
|
|
303
|
+
}, { maxRetries: 3, timeoutMs: 300000, label: 'generateWorksheetQuestions' });
|
|
304
|
+
}
|
|
305
|
+
async checkWorksheetQuestions(sessionId, user, question, answer, mark_scheme) {
|
|
306
|
+
const formData = new FormData();
|
|
307
|
+
formData.append('command', 'mark_worksheet_questions');
|
|
308
|
+
formData.append('session', sessionId);
|
|
309
|
+
formData.append('user', user);
|
|
310
|
+
formData.append('question', question);
|
|
311
|
+
formData.append('answer', answer);
|
|
312
|
+
formData.append('mark_scheme', JSON.stringify(mark_scheme));
|
|
313
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
314
|
+
method: 'POST',
|
|
315
|
+
body: formData,
|
|
316
|
+
});
|
|
317
|
+
if (!response.ok) {
|
|
318
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText}`);
|
|
319
|
+
}
|
|
320
|
+
const result = await response.json();
|
|
321
|
+
logger.debug(`Worksheet marking result received`);
|
|
322
|
+
return JSON.parse(result.marking);
|
|
323
|
+
}
|
|
324
|
+
// Generate podcast structure
|
|
325
|
+
async generatePodcastStructure(sessionId, user, title, description, prompt, speakers) {
|
|
326
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
327
|
+
// Mock mode - return fake podcast structure
|
|
328
|
+
if (MOCK_MODE) {
|
|
329
|
+
logger.info(`🎭 MOCK MODE: Generating podcast structure for session ${sessionId}`);
|
|
330
|
+
return {
|
|
331
|
+
success: true,
|
|
332
|
+
structure: {
|
|
333
|
+
episodeTitle: `${title} - AI Generated Episode`,
|
|
334
|
+
totalEstimatedDuration: "15 minutes",
|
|
335
|
+
segments: [
|
|
336
|
+
{
|
|
337
|
+
title: "Welcome & Introduction",
|
|
338
|
+
content: "HOST: Welcome to today's episode!\nGUEST: Thanks for having me!\nHOST: Let's dive into the topic.\nGUEST: Great! Let's start with the basics...",
|
|
339
|
+
speaker: "dialogue",
|
|
340
|
+
voiceId: speakers[0]?.id || "mock-voice-1",
|
|
341
|
+
keyPoints: ["Introduction", "What to expect"],
|
|
342
|
+
estimatedDuration: "3 minutes",
|
|
343
|
+
order: 1
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
title: "Main Discussion",
|
|
347
|
+
content: "This is the main content section where we explore the key concepts in detail. We'll cover various aspects and provide practical examples.",
|
|
348
|
+
speaker: "host",
|
|
349
|
+
voiceId: speakers[0]?.id || "mock-voice-1",
|
|
350
|
+
keyPoints: ["Key concept 1", "Key concept 2"],
|
|
351
|
+
estimatedDuration: "8 minutes",
|
|
352
|
+
order: 2
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
title: "Conclusion & Takeaways",
|
|
356
|
+
content: "HOST: Let's wrap up what we've learned today.\nGUEST: Yes, the main takeaway is...\nHOST: Thanks for joining us!\nGUEST: Thank you!",
|
|
357
|
+
speaker: "dialogue",
|
|
358
|
+
voiceId: speakers[0]?.id || "mock-voice-1",
|
|
359
|
+
keyPoints: ["Summary", "Next steps"],
|
|
360
|
+
estimatedDuration: "4 minutes",
|
|
361
|
+
order: 3
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
const formData = new FormData();
|
|
368
|
+
formData.append('command', 'generate_podcast_structure');
|
|
369
|
+
formData.append('user', user);
|
|
370
|
+
formData.append('session', sessionId);
|
|
371
|
+
formData.append('title', title);
|
|
372
|
+
formData.append('description', description);
|
|
373
|
+
formData.append('prompt', prompt);
|
|
374
|
+
formData.append('speakers', JSON.stringify(speakers));
|
|
375
|
+
try {
|
|
376
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
377
|
+
method: 'POST',
|
|
378
|
+
body: formData,
|
|
379
|
+
});
|
|
380
|
+
if (!response.ok) {
|
|
381
|
+
const errorText = await response.text();
|
|
382
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
383
|
+
}
|
|
384
|
+
const result = await response.json();
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
throw new TRPCError({
|
|
389
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
390
|
+
message: `Failed to generate podcast structure: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Generate podcast audio from text
|
|
395
|
+
async generatePodcastAudioFromText(sessionId, user, podcastId, segmentIndex, text, speakers, voiceId) {
|
|
396
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
397
|
+
// Mock mode - return fake audio generation result
|
|
398
|
+
if (MOCK_MODE) {
|
|
399
|
+
logger.info(`🎭 MOCK MODE: Generating audio for segment ${segmentIndex} of podcast ${podcastId}`);
|
|
400
|
+
const isDialogue = text.includes('HOST:') || text.includes('GUEST:');
|
|
401
|
+
return {
|
|
402
|
+
success: true,
|
|
403
|
+
segmentIndex: segmentIndex,
|
|
404
|
+
objectKey: `${user}/${sessionId}/podcasts/${podcastId}/segment_${segmentIndex}.mp3`,
|
|
405
|
+
duration: 45 + Math.floor(Math.random() * 30), // Random duration between 45-75 seconds
|
|
406
|
+
type: isDialogue ? 'dialogue' : 'monologue',
|
|
407
|
+
...(isDialogue && { partCount: 4 })
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const formData = new FormData();
|
|
411
|
+
formData.append('command', 'generate_podcast_audio_from_text');
|
|
412
|
+
formData.append('user', user);
|
|
413
|
+
formData.append('session', sessionId);
|
|
414
|
+
formData.append('podcast_id', podcastId);
|
|
415
|
+
formData.append('segment_index', segmentIndex.toString());
|
|
416
|
+
formData.append('text', text);
|
|
417
|
+
formData.append('speakers', JSON.stringify(speakers));
|
|
418
|
+
if (voiceId) {
|
|
419
|
+
formData.append('voice_id', voiceId);
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
423
|
+
method: 'POST',
|
|
424
|
+
body: formData,
|
|
425
|
+
});
|
|
426
|
+
if (!response.ok) {
|
|
427
|
+
const errorText = await response.text();
|
|
428
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
429
|
+
}
|
|
430
|
+
const result = await response.json();
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
throw new TRPCError({
|
|
435
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
436
|
+
message: `Failed to generate podcast audio: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
async generatePodcastImage(sessionId, user, summary) {
|
|
441
|
+
const formData = new FormData();
|
|
442
|
+
formData.append('command', 'generate_podcast_image');
|
|
443
|
+
formData.append('session', sessionId);
|
|
444
|
+
formData.append('user', user);
|
|
445
|
+
formData.append('summary', summary);
|
|
446
|
+
try {
|
|
447
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
448
|
+
method: 'POST',
|
|
449
|
+
body: formData,
|
|
450
|
+
});
|
|
451
|
+
if (!response.ok) {
|
|
452
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText}`);
|
|
453
|
+
}
|
|
454
|
+
const result = await response.json();
|
|
455
|
+
return result.image_key;
|
|
456
|
+
}
|
|
457
|
+
catch (error) {
|
|
458
|
+
throw new TRPCError({
|
|
459
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
460
|
+
message: `Failed to generate podcast image: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async segmentStudyGuide(sessionId, user, studyGuide) {
|
|
465
|
+
// def generate_study_guide_segmentation(request):
|
|
466
|
+
// user = request.form.get("user")
|
|
467
|
+
// session = request.form.get("session")
|
|
468
|
+
// study_guide = request.form.get("study_guide")
|
|
469
|
+
// if not user or not session:
|
|
470
|
+
// return {"error": "Session not initialized."}, 400
|
|
471
|
+
// if not study_guide:
|
|
472
|
+
// print("Study guide not provided.")
|
|
473
|
+
// return {"error": "Study guide not provided."}, 400
|
|
474
|
+
// messages = generate_segmentation(study_guide)
|
|
475
|
+
// return {"segmentation": messages}, 200
|
|
476
|
+
const formData = new FormData();
|
|
477
|
+
formData.append('command', 'generate_study_guide_segmentation');
|
|
478
|
+
formData.append('session', sessionId);
|
|
479
|
+
formData.append('user', user);
|
|
480
|
+
formData.append('study_guide', studyGuide);
|
|
481
|
+
try {
|
|
482
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
483
|
+
method: 'POST',
|
|
484
|
+
body: formData,
|
|
485
|
+
});
|
|
486
|
+
if (!response.ok) {
|
|
487
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText}`);
|
|
488
|
+
}
|
|
489
|
+
const result = await response.json();
|
|
490
|
+
return result.segmentation;
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
throw new TRPCError({
|
|
494
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
495
|
+
message: `Failed to segment study guide: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
async validateSegmentSummary(sessionId, user, segmentContent, studentResponse, studyGuide) {
|
|
500
|
+
const formData = new FormData();
|
|
501
|
+
formData.append('command', 'validate_segment_summary');
|
|
502
|
+
formData.append('session', sessionId);
|
|
503
|
+
formData.append('user', user);
|
|
504
|
+
formData.append('segment_content', segmentContent);
|
|
505
|
+
formData.append('student_response', studentResponse);
|
|
506
|
+
formData.append('study_guide', studyGuide);
|
|
507
|
+
try {
|
|
508
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
509
|
+
method: 'POST',
|
|
510
|
+
body: formData,
|
|
511
|
+
});
|
|
512
|
+
if (!response.ok) {
|
|
513
|
+
throw new Error(`AI service error: ${response.status} ${response.statusText}`);
|
|
514
|
+
}
|
|
515
|
+
const result = await response.json();
|
|
516
|
+
return result.feedback;
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
throw new TRPCError({
|
|
520
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
521
|
+
message: `Failed to validate segment summary: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// Get session by ID
|
|
526
|
+
getSession(sessionId) {
|
|
527
|
+
return this.sessions.get(sessionId);
|
|
528
|
+
}
|
|
529
|
+
// Get sessions by user and workspace
|
|
530
|
+
getSessionsByUserAndWorkspace(userId, workspaceId) {
|
|
531
|
+
return Array.from(this.sessions.values()).filter(session => session.workspaceId === workspaceId);
|
|
532
|
+
}
|
|
533
|
+
// Delete session
|
|
534
|
+
deleteSession(sessionId) {
|
|
535
|
+
return this.sessions.delete(sessionId);
|
|
536
|
+
}
|
|
537
|
+
// Check if AI service is available
|
|
538
|
+
async checkHealth() {
|
|
539
|
+
await new Promise(resolve => setTimeout(resolve, IMITATE_WAIT_TIME_MS));
|
|
540
|
+
// Mock mode - always return healthy
|
|
541
|
+
if (MOCK_MODE) {
|
|
542
|
+
logger.info('MOCK MODE: AI service health check - returning healthy');
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
try {
|
|
546
|
+
logger.info('Checking AI service health...');
|
|
547
|
+
const response = await fetch(AI_SERVICE_URL, {
|
|
548
|
+
method: 'POST',
|
|
549
|
+
body: new FormData(), // Empty form data
|
|
550
|
+
});
|
|
551
|
+
logger.info(`AI Service health check status: ${response.status}`);
|
|
552
|
+
return response.ok;
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
logger.error('AI Service health check failed:', error);
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// Global instance
|
|
561
|
+
export const aiSessionService = new AISessionService();
|