kepler-chat/src/lib/backend/convex/schema.ts
2025-06-19 10:16:13 -07:00

83 lines
2.6 KiB
TypeScript

import { defineSchema, defineTable } from 'convex/server';
import { type Infer, v } from 'convex/values';
import { Provider } from '../../../lib/types';
export const providerValidator = v.union(...Object.values(Provider).map((p) => v.literal(p)));
export const messageRoleValidator = v.union(
v.literal('user'),
v.literal('assistant'),
v.literal('system')
);
export type MessageRole = Infer<typeof messageRoleValidator>;
export const ruleAttachValidator = v.union(v.literal('always'), v.literal('manual'));
export default defineSchema({
user_settings: defineTable({
user_id: v.string(),
privacy_mode: v.boolean(),
free_messages_used: v.optional(v.number()),
}).index('by_user', ['user_id']),
user_keys: defineTable({
user_id: v.string(),
provider: providerValidator,
key: v.string(),
})
.index('by_user', ['user_id'])
.index('by_provider_user', ['provider', 'user_id']),
user_enabled_models: defineTable({
user_id: v.string(),
provider: providerValidator,
/** Different providers may use different ids for the same model */
model_id: v.string(),
pinned: v.union(v.number(), v.null()),
})
.index('by_user', ['user_id'])
.index('by_model_provider', ['model_id', 'provider'])
.index('by_provider_user', ['provider', 'user_id'])
.index('by_model_provider_user', ['model_id', 'provider', 'user_id']),
user_rules: defineTable({
user_id: v.string(),
name: v.string(),
attach: ruleAttachValidator,
rule: v.string(),
})
.index('by_user', ['user_id'])
.index('by_user_attach', ['user_id', 'attach'])
.index('by_user_name', ['user_id', 'name']),
conversations: defineTable({
user_id: v.string(),
title: v.string(),
updated_at: v.optional(v.number()),
pinned: v.optional(v.boolean()),
generating: v.optional(v.boolean()),
cost_usd: v.optional(v.number()),
public: v.optional(v.boolean()),
branched_from: v.optional(v.id('conversations')),
}).index('by_user', ['user_id']),
messages: defineTable({
conversation_id: v.string(),
role: v.union(v.literal('user'), v.literal('assistant'), v.literal('system')),
content: v.string(),
content_html: v.optional(v.string()),
error: v.optional(v.string()),
// Optional, coming from SK API route
model_id: v.optional(v.string()),
provider: v.optional(providerValidator),
token_count: v.optional(v.number()),
// Optional image attachments
images: v.optional(
v.array(
v.object({
url: v.string(),
storage_id: v.string(),
fileName: v.optional(v.string()),
})
)
),
cost_usd: v.optional(v.number()),
generation_id: v.optional(v.string()),
web_search_enabled: v.optional(v.boolean()),
}).index('by_conversation', ['conversation_id']),
});