import { Schema as S } from '@triplit/client'; import { authSchema } from './auth-schema'; import { Providers } from '../app/types/model'; export const schema = S.Collections({ ...authSchema, agents: { schema: S.Schema({ id: S.Id(), userId: S.String(), name: S.String(), systemPrompt: S.String({ nullable: true }), imageUrl: S.String({ nullable: true }), defaultModelId: S.String({ nullable: true }), createdAt: S.Date({ default: S.Default.now() }), }), relationships: { topics: S.RelationMany('topics', { where: [['agentId', '=', '$1.id']], }), model: S.RelationOne('models', { where: [['id', '=', '$defaultModelId']], }), }, permissions: { authenticated: { read: { filter: [['userId', '=', '$token.sub']], }, insert: { filter: [['userId', '=', '$token.sub']], }, update: { filter: [['userId', '=', '$token.sub']], }, delete: { filter: [['userId', '=', '$token.sub']], }, }, }, }, topics: { schema: S.Schema({ id: S.Id(), userId: S.String(), agentId: S.String(), name: S.String(), createdAt: S.Date({ default: S.Default.now() }), }), relationships: { agent: S.RelationOne('agents', { where: [['id', '=', '$agentId']], }), messages: S.RelationMany('messages', { where: [['topicId', '=', '$id']], }), generations: S.RelationMany('generations', { where: [['topicId', '=', '$id']], }), }, permissions: { authenticated: { read: { filter: [['userId', '=', '$token.sub']], }, insert: { filter: [['userId', '=', '$token.sub']], }, update: { filter: [['userId', '=', '$token.sub']], }, delete: { filter: [['userId', '=', '$token.sub']], }, }, }, }, message_parts: { schema: S.Schema({ id: S.Id(), messageId: S.String(), toolCallId: S.String({ nullable: true }), type: S.String({ enum: ['reasoning', 'text', 'tool-call', 'file'] }), content: S.String(), providerOptions: S.Json({ nullable: true }), finished: S.Boolean({ default: false }), createdAt: S.Date({ default: S.Default.now() }), lastUpdatedAt: S.Date({ default: S.Default.now() }), }), relationships: { message: S.RelationOne('messages', { where: [['id', '=', '$messageId']], }), toolCall: S.RelationOne('tool_calls', { where: [['id', '=', '$toolCallId']], }), }, permissions: { authenticated: { read: { filter: [['message.topic.userId', '=', '$token.sub']], }, insert: { // blank means it can only be inserted by the service token (ie the server) }, update: { filter: [['message.topic.userId', '=', '$token.sub']], }, delete: { filter: [['message.topic.userId', '=', '$token.sub']], }, }, }, }, tool_calls: { schema: S.Schema({ id: S.Id(), userId: S.String(), toolName: S.String(), status: S.String({ enum: ['pending', 'completed', 'failed', 'cancelled'], }), input: S.Record({ type: S.String({ enum: ['text', 'json'] }), value: S.String() }, { nullable: true }), output: S.Record({ type: S.String({ enum: ['text', 'json'] }), value: S.String() }, { nullable: true }), error: S.Record({ type: S.String({ enum: ['text', 'json'] }), value: S.String() }, { nullable: true }), createdAt: S.Date({ default: S.Default.now() }), }), relationships: { message_part: S.RelationOne('message_parts', { where: [['id', '=', '$messagePartId']], }), }, permissions: { authenticated: { read: { filter: [['userId', '=', '$token.sub']] }, insert: {}, update: {}, delete: { filter: [['userId', '=', '$token.sub']] }, }, }, }, messages: { schema: S.Schema({ id: S.Id(), topicId: S.String(), role: S.String({ enum: ['user', 'assistant'] }), content: S.String(), generationId: S.String({ nullable: true, default: null }), // a message has a "children" relationship that points to these messages. // For each child, if its topicId matches the parent topicId, it will be a // regeneration, but if the topicId is different, it will be a branching // message, basically making a sub-thread of the parent topic. parentMessageId: S.String({ nullable: true, default: null }), createdAt: S.Date({ default: S.Default.now() }), }), relationships: { topic: S.RelationOne('topics', { where: [['id', '=', '$topicId']], }), parts: S.RelationMany('message_parts', { where: [['messageId', '=', '$id']], }), generation: S.RelationOne('generations', { where: [['id', '=', '$generationId']], }), parentMessage: S.RelationOne('messages', { where: [['id', '=', '$parentMessageId']], }), children: S.RelationMany('messages', { where: [['parentMessageId', '=', '$id']], }), }, permissions: { authenticated: { read: { filter: [['topic.userId', '=', '$token.sub']], }, insert: { filter: [['topic.userId', '=', '$token.sub']], }, update: { filter: [['topic.userId', '=', '$token.sub']], }, delete: { filter: [['topic.userId', '=', '$token.sub']], }, }, }, }, generations: { schema: S.Schema({ id: S.Id(), status: S.String({ enum: ['pending', 'cancelled', 'completed', 'failed'], }), topicId: S.String(), messageId: S.String(), modelId: S.String(), tokens: S.Record({ input: S.Number({ nullable: true }), cache: S.Record({ read: S.Number({ nullable: true }), write: S.Number({ nullable: true }), }, { nullable: true }), output: S.Number({ nullable: true }), thinking: S.Number({ nullable: true }), }, { nullable: true }), error: S.String({ nullable: true, default: null }), }), relationships: { message: S.RelationOne('messages', { where: [['id', '=', '$messageId']], }), topic: S.RelationOne('topics', { where: [['id', '=', '$topicId']], }), }, permissions: { authenticated: { read: { filter: [ ['message.topic.userId', '=', '$token.sub'], ['topic.userId', '=', '$token.sub'], ], }, insert: {}, update: {}, delete: { filter: [ ['message.topic.userId', '=', '$token.sub'], ['topic.userId', '=', '$token.sub'], ], }, }, }, }, providers: { schema: S.Schema({ id: S.Id(), userId: S.String(), type: S.String({ enum: Providers }), name: S.String(), enabled: S.Boolean({ default: true }), config: S.Json(), createdAt: S.Date({ default: S.Default.now() }), updatedAt: S.Date({ default: S.Default.now() }), }), relationships: { models: S.RelationMany('models', { where: [['providerId', '=', '$id']], }), }, permissions: { authenticated: { read: { filter: [['userId', '=', '$token.sub']], }, insert: { filter: [['userId', '=', '$token.sub']], }, update: { filter: [['userId', '=', '$token.sub']], }, delete: { filter: [['userId', '=', '$token.sub']], }, }, } }, models: { schema: S.Schema({ id: S.Id(), userId: S.String(), externalId: S.String(),// e.g. "x-ai/grok-4.1-fast" providerId: S.String(), name: S.String(), attributes: S.Record({ inputModalities: S.Set(S.String({ enum: ['text', 'image'] })), outputModalities: S.Set(S.String({ enum: ['text', 'image'] })), capabilities: S.Set(S.String({ enum: ['reasoning', 'tools'] })), contextWindow: S.Number({ nullable: true }), supported_parameters: S.Set(S.String(), { nullable: true }), }), // From models.dev (context window, etc) isCustom: S.Boolean({ default: false }), enabled: S.Boolean({ default: true }), releasedAt: S.Date({ nullable: true }), createdAt: S.Date({ default: S.Default.now() }), updatedAt: S.Date({ default: S.Default.now() }), }), relationships: { provider: S.RelationOne('providers', { where: [['id', '=', '$providerId']], }), }, permissions: { authenticated: { read: { filter: [['userId', '=', '$token.sub'], ['provider.userId', '=', '$token.sub']], }, insert: { filter: [['userId', '=', '$token.sub'], ['provider.userId', '=', '$token.sub']], }, update: { filter: [['userId', '=', '$token.sub'], ['provider.userId', '=', '$token.sub']], }, delete: { filter: [['userId', '=', '$token.sub'], ['provider.userId', '=', '$token.sub']], }, }, }, } });