407 lines
15 KiB
TypeScript
407 lines
15 KiB
TypeScript
import { Schema as S } from '@triplit/client';
|
|
import { authSchema } from './auth-schema';
|
|
import { Providers, SupportedModalities } from '../app/types/model';
|
|
|
|
const systemAssistantRecord = S.Record({
|
|
enabled: S.Boolean({ default: true }),
|
|
prompt: S.String({ nullable: true }),
|
|
modelId: S.String({ nullable: true }),
|
|
})
|
|
|
|
export const schema = S.Collections({
|
|
...authSchema,
|
|
settings: {
|
|
schema: S.Schema({
|
|
id: S.Id(),
|
|
userId: S.String(),
|
|
systemAssistants: S.Record({
|
|
rename: systemAssistantRecord
|
|
}),
|
|
}),
|
|
permissions: {
|
|
authenticated: {
|
|
read: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
insert: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
update: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
postUpdate: {
|
|
filter: [['userId', '=', '$prev.userId']],
|
|
},
|
|
delete: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
},
|
|
}
|
|
},
|
|
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']],
|
|
},
|
|
postUpdate: {
|
|
filter: [['userId', '=', '$prev.userId']],
|
|
},
|
|
delete: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
topics: {
|
|
schema: S.Schema({
|
|
id: S.Id(),
|
|
userId: S.String(),
|
|
agentId: S.String(),
|
|
renaming: S.Boolean({ default: false, nullable: true }),
|
|
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']],
|
|
},
|
|
postUpdate: {
|
|
filter: [['userId', '=', '$prev.userId']],
|
|
},
|
|
delete: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
message_parts: {
|
|
schema: S.Schema({
|
|
id: S.Id(),
|
|
userId: S.String(),
|
|
topicId: S.String(),
|
|
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: [['userId', '=', '$token.sub']],
|
|
},
|
|
insert: {
|
|
// blank means it can only be inserted by the service token (ie the server)
|
|
},
|
|
update: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
postUpdate: {
|
|
filter: [
|
|
['userId', '=', '$prev.userId'],
|
|
['topicId', '=', '$prev.topicId'],
|
|
['messageId', '=', '$prev.messageId'],
|
|
['toolCallId', '=', '$prev.toolCallId'],
|
|
],
|
|
},
|
|
delete: {
|
|
filter: [['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({ nullable: true }),
|
|
focusedIndex: S.Number({ nullable: true }),
|
|
userId: S.String(),
|
|
deleted: S.Boolean({ default: false, nullable: true }),
|
|
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: [['userId', '=', '$token.sub']],
|
|
},
|
|
insert: {
|
|
filter: [
|
|
['userId', '=', '$token.sub'],
|
|
['topic.userId', '=', '$token.sub'],
|
|
],
|
|
},
|
|
update: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
postUpdate: {
|
|
filter: [
|
|
['userId', '=', '$prev.userId'],
|
|
['topicId', '=', '$prev.topicId'],
|
|
['generationId', '=', '$prev.generationId'],
|
|
['parentMessageId', '=', '$prev.parentMessageId'],
|
|
],
|
|
},
|
|
delete: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
generations: {
|
|
schema: S.Schema({
|
|
id: S.Id(),
|
|
userId: S.String(),
|
|
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 }),
|
|
ttft: S.Number({ nullable: true }),
|
|
tps: 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: [['userId', '=', '$token.sub']],
|
|
},
|
|
insert: {},
|
|
update: {
|
|
filter: [['userId', '=', '$token.sub']],
|
|
},
|
|
postUpdate: {
|
|
filter: [
|
|
['userId', '=', '$prev.userId'],
|
|
['topicId', '=', '$prev.topicId'],
|
|
['messageId', '=', '$prev.messageId'],
|
|
],
|
|
},
|
|
delete: {
|
|
filter: [['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']],
|
|
},
|
|
postUpdate: {
|
|
filter: [['userId', '=', '$prev.userId']],
|
|
},
|
|
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(),
|
|
cost: S.Record({
|
|
prompt: S.String({ nullable: true }),
|
|
completion: S.String({ nullable: true }),
|
|
request: S.String({ nullable: true }),
|
|
image: S.String({ nullable: true }),
|
|
imageTokens: S.String({ nullable: true }),
|
|
imageOutput: S.String({ nullable: true }),
|
|
audio: S.String({ nullable: true }),
|
|
audioOutput: S.String({ nullable: true }),
|
|
inputAudioCache: S.String({ nullable: true }),
|
|
webSearch: S.String({ nullable: true }),
|
|
internalReasoning: S.String({ nullable: true }),
|
|
inputCacheRead: S.String({ nullable: true }),
|
|
inputCacheWrite: S.String({ nullable: true }),
|
|
discount: S.String({ nullable: true }),
|
|
}),
|
|
attributes: S.Record({
|
|
inputModalities: S.Set(S.String({ enum: SupportedModalities })),
|
|
outputModalities: S.Set(S.String({ enum: SupportedModalities })),
|
|
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 }),
|
|
}),
|
|
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']],
|
|
},
|
|
postUpdate: {
|
|
filter: [
|
|
['userId', '=', '$prev.userId'],
|
|
['providerId', '=', '$prev.providerId'],
|
|
]
|
|
},
|
|
delete: {
|
|
filter: [['userId', '=', '$token.sub'], ['provider.userId', '=', '$token.sub']],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
});
|