mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 23:41:08 +02:00
- Rename planta module to plants everywhere (routes, modules, API, branding, i18n, docker, docs, shared packages) - Fix package name collisions: @mana/credits-service, @mana/subscriptions-service (unblocks turbo) - Extract layout composables: use-ai-tier-items, use-sync-status-items, RouteTierGate (layout 1345→1015 lines) - Create shared DB pool for apps/api (lib/db.ts), migrate 5 modules - Add automations module queries.ts with useAllAutomations/useEnabledAutomations - Remove debug console.log statements from production code - Rename storage display name: Ablage → Speicher Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
/**
|
|
* Research module — DB schema (Drizzle / pgSchema 'research')
|
|
*
|
|
* Server-side store for deep-research runs orchestrated by apps/api.
|
|
* Lives in mana_platform under its own pgSchema.
|
|
*
|
|
* - research_results: one row per research run, holds plan + final synthesis
|
|
* - sources: one row per web source consumed by a run
|
|
*
|
|
* The local-first questions module references research_results.id from
|
|
* LocalAnswer.researchResultId; sources are fetched on-demand via the API
|
|
* and never mirrored into IndexedDB (they're public web content).
|
|
*/
|
|
|
|
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
import { pgSchema, uuid, text, timestamp, integer, jsonb } from 'drizzle-orm/pg-core';
|
|
import { getConnection } from '../../lib/db';
|
|
|
|
export const researchSchema = pgSchema('research');
|
|
|
|
/**
|
|
* One row per research run. Created in `planning` state immediately on
|
|
* /start, then updated as the orchestrator advances through phases.
|
|
*/
|
|
export const researchResults = researchSchema.table('research_results', {
|
|
id: uuid('id').defaultRandom().primaryKey(),
|
|
userId: text('user_id').notNull(),
|
|
questionId: text('question_id').notNull(), // mirrors local LocalQuestion.id (UUID)
|
|
depth: text('depth').notNull(), // 'quick' | 'standard' | 'deep'
|
|
status: text('status').notNull(), // 'planning' | 'searching' | 'extracting' | 'synthesizing' | 'done' | 'error'
|
|
subQueries: jsonb('sub_queries').$type<string[]>(),
|
|
summary: text('summary'),
|
|
keyPoints: jsonb('key_points').$type<string[]>(),
|
|
followUpQuestions: jsonb('follow_up_questions').$type<string[]>(),
|
|
errorMessage: text('error_message'),
|
|
startedAt: timestamp('started_at', { withTimezone: true }).defaultNow().notNull(),
|
|
finishedAt: timestamp('finished_at', { withTimezone: true }),
|
|
});
|
|
|
|
/**
|
|
* Sources consumed during a research run. Rank reflects ordering in the
|
|
* synthesis prompt so citation [n] in the summary maps to sources[n-1].
|
|
*/
|
|
export const sources = researchSchema.table('sources', {
|
|
id: uuid('id').defaultRandom().primaryKey(),
|
|
researchResultId: uuid('research_result_id')
|
|
.notNull()
|
|
.references(() => researchResults.id, { onDelete: 'cascade' }),
|
|
url: text('url').notNull(),
|
|
title: text('title'),
|
|
snippet: text('snippet'),
|
|
extractedContent: text('extracted_content'),
|
|
category: text('category'),
|
|
rank: integer('rank').notNull(),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
|
});
|
|
|
|
export const db = drizzle(getConnection(), { schema: { researchResults, sources } });
|
|
|
|
export type ResearchResult = typeof researchResults.$inferSelect;
|
|
export type Source = typeof sources.$inferSelect;
|
|
export type ResearchDepth = 'quick' | 'standard' | 'deep';
|
|
export type ResearchStatus =
|
|
| 'planning'
|
|
| 'searching'
|
|
| 'extracting'
|
|
| 'synthesizing'
|
|
| 'done'
|
|
| 'error';
|