merlyn/server/prisma/schema.prisma
Marcello Fitton 41495cdabe
feat: Scheduled Jobs (#5322)
* initialize

* expand tool result text limit | add syntax highlighting and json formatting to tool result rendering

* fix onError jsdoc

* lint

* fix unread icon

* route protection

* improve form handling for NewJobModal

* safeJsonParse

* remove unneeded comments

* remove trycatch

* add truncateText helper

* add explicit fallback value tos safeJsonParse

* add shared cron constant and helpers

* reduce frontend indirection

* use isLight to compute syntax highlighting theme

* remove dead code

* remove forJob and make job limit to 50

* create recomputeNextRunAt helper method

* add comment about nextRunAt recomputation

* add job queue and concurrency control to scheduled jobs

* use p-queue

* change default max concurrent value to 1

* add comment explaining internal scheduling system

* add recomputeNextRunAt on boot

* add generated documents to run details

* Modify toolsOverride functionality where no tools selected means no tools are given to the agent

add a select all/deselect all toggle button for easily selecting all
tools in the cerate job form

* create usePolling hook

* add polling to scheduled jobs and scheduled job runs pages

* add cron generation feature in job form

* remove cron generation feature | add cron builder feature | add max active scheduled jobs limit

* set MAX_ACTIVE to null

* replace hour and minute input fields with input with type time

* simplify

* organize components

* move components to bottom of page component

* change Generated Documents to Generated Files

* add i18n to cronstrue

* add i18n

* add type="button" to button elements

* refactor fileSource retrieval logic

* one scheduled job run can have status "running"

* add protection of file retrieveal from scheduled job in multiuser mode

* fix comments

* make job status default to queued

* add queued status

* fix bug with result trace rendering

* store timeout ref and clearTimeout once race settles

* remove unneeded handlerPromise tracking

* move imports to top level

* refactor hardcoded paths to path resolve functions

* implement new job form design

* simplify

* fix button styles

* fix runJob bug

* implement styles for scheduled jobs page

* apply dark mode figma styles

* delete unused translation key

* implement light mode for new new job modal, run history, and run details

* lint

* fix light mode scroll bar in tool call card

* adjust table header contrast

* fix type in subtitle

* kill workers when job is in-flight before deleting job

* add border-none to buttons

* change locale time to iso string

* import BackgroundService module level | instatiate backgroundService singltone once and reuse across handlers

* add p-queue, @breejs/later and cron-validate as core deps

* parse cron expression to a builder state once

* add theme to day buttons in cron builder

* fix stale tools selection caption

* flip popover when popover clips screen height

* make ScheduleJob.trigger() await the run insertion | disable run now button if job is in flight

* regen table

* refactor generated file card

* refactor frontend

* remove logs

* major refactor for tool picking, fix bree/later bug

* combine action endpoints, move contine to method

* fix unoptimized query with include + take + order

* fix dangerous use, refactor job to utils

* add copy content to text response

* improve notification system subscription for browser

* remove unused translations

* prevent gen-file cleanup job from deleting active job file generated references

* rich text copy

* Scheduled Jobs: Translations (#5482)

* add locales for scheduled jobs

* i18n

---------

Co-authored-by: Timothy Carambat <rambat1010@gmail.com>

* add config flag with UI notice

* update README

* telemetry datapoints

* Always use UTC on backend, convert to local in frontend

* fix tz render

* Add job killing

* cleanup thinking text in job notifications and break out reasoning in response text.
Also hide zero metrics since that is useless

* Port generatedFile schema to the normalized workspace chat `outputs` file format so porting to thread is simple and implem between chats <> jobs is 1:1

* what the fuck

* compiled bug

* fixed thinking oddity in complied frontend

* supress multi-toast

* fix duration call

* Revert "fix duration call"

This reverts commit 0491bc71f4223e65ea4046561b15b268fefb8da2.

* revert and reapply fix

---------

Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
2026-04-29 12:05:46 -07:00

426 lines
16 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
// Uncomment the following lines and comment out the SQLite datasource block above to use PostgreSQL
// Make sure to set the correct DATABASE_URL in your .env file
// After swapping run `yarn prisma:setup` from the root directory to migrate the database
//
// datasource db {
// provider = "postgresql"
// url = env("DATABASE_URL")
// }
datasource db {
provider = "sqlite"
url = "file:../storage/anythingllm.db"
}
model api_keys {
id Int @id @default(autoincrement())
name String?
secret String? @unique
createdBy Int?
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
}
model workspace_documents {
id Int @id @default(autoincrement())
docId String @unique
filename String
docpath String
workspaceId Int
metadata String?
pinned Boolean? @default(false)
watched Boolean? @default(false)
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
workspace workspaces @relation(fields: [workspaceId], references: [id])
document_sync_queues document_sync_queues?
}
model invites {
id Int @id @default(autoincrement())
code String @unique
status String @default("pending")
claimedBy Int?
workspaceIds String?
createdAt DateTime @default(now())
createdBy Int
lastUpdatedAt DateTime @default(now())
}
model system_settings {
id Int @id @default(autoincrement())
label String @unique
value String?
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
}
model users {
id Int @id @default(autoincrement())
username String? @unique
password String
pfpFilename String?
role String @default("default")
suspended Int @default(0)
seen_recovery_codes Boolean? @default(false)
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
dailyMessageLimit Int?
bio String? @default("")
web_push_subscription_config String?
workspace_chats workspace_chats[]
workspace_users workspace_users[]
embed_configs embed_configs[]
embed_chats embed_chats[]
threads workspace_threads[]
recovery_codes recovery_codes[]
password_reset_tokens password_reset_tokens[]
workspace_agent_invocations workspace_agent_invocations[]
slash_command_presets slash_command_presets[]
browser_extension_api_keys browser_extension_api_keys[]
temporary_auth_tokens temporary_auth_tokens[]
system_prompt_variables system_prompt_variables[]
prompt_history prompt_history[]
desktop_mobile_devices desktop_mobile_devices[]
workspace_parsed_files workspace_parsed_files[]
}
model recovery_codes {
id Int @id @default(autoincrement())
user_id Int
code_hash String
createdAt DateTime @default(now())
user users @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@index([user_id])
}
model password_reset_tokens {
id Int @id @default(autoincrement())
user_id Int
token String @unique
expiresAt DateTime
createdAt DateTime @default(now())
user users @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@index([user_id])
}
model document_vectors {
id Int @id @default(autoincrement())
docId String
vectorId String
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
}
model workspaces {
id Int @id @default(autoincrement())
name String
slug String @unique
vectorTag String?
createdAt DateTime @default(now())
openAiTemp Float?
openAiHistory Int @default(20)
lastUpdatedAt DateTime @default(now())
// THIS IS THE SYSTEM PROMPT FOR THE WORKSPACE
openAiPrompt String?
similarityThreshold Float? @default(0.25)
chatProvider String?
chatModel String?
topN Int? @default(4)
chatMode String? @default("chat")
pfpFilename String?
agentProvider String?
agentModel String?
queryRefusalResponse String?
vectorSearchMode String? @default("default")
workspace_users workspace_users[]
documents workspace_documents[]
workspace_suggested_messages workspace_suggested_messages[]
embed_configs embed_configs[]
threads workspace_threads[]
workspace_agent_invocations workspace_agent_invocations[]
prompt_history prompt_history[]
workspace_parsed_files workspace_parsed_files[]
}
model workspace_threads {
id Int @id @default(autoincrement())
name String
slug String @unique
workspace_id Int
user_id Int?
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
workspace workspaces @relation(fields: [workspace_id], references: [id], onDelete: Cascade)
user users? @relation(fields: [user_id], references: [id], onDelete: Cascade)
workspace_parsed_files workspace_parsed_files[]
@@index([workspace_id])
@@index([user_id])
}
model workspace_suggested_messages {
id Int @id @default(autoincrement())
workspaceId Int
heading String
message String
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
workspace workspaces @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
@@index([workspaceId])
}
model workspace_chats {
id Int @id @default(autoincrement())
workspaceId Int
prompt String
response String
include Boolean @default(true)
user_id Int?
thread_id Int? // No relation to prevent whole table migration
api_session_id String? // String identifier for only the dev API to partition chats in any mode.
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
feedbackScore Boolean?
users users? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
}
model workspace_agent_invocations {
id Int @id @default(autoincrement())
uuid String @unique
prompt String // Contains agent invocation to parse + option additional text for seed.
closed Boolean @default(false)
user_id Int?
thread_id Int? // No relation to prevent whole table migration
workspace_id Int
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
user users? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
workspace workspaces @relation(fields: [workspace_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@index([uuid])
}
model workspace_users {
id Int @id @default(autoincrement())
user_id Int
workspace_id Int
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
workspaces workspaces @relation(fields: [workspace_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
}
model cache_data {
id Int @id @default(autoincrement())
name String
data String
belongsTo String?
byId Int?
expiresAt DateTime?
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
}
model embed_configs {
id Int @id @default(autoincrement())
uuid String @unique
enabled Boolean @default(false)
chat_mode String @default("query")
allowlist_domains String?
allow_model_override Boolean @default(false)
allow_temperature_override Boolean @default(false)
allow_prompt_override Boolean @default(false)
max_chats_per_day Int?
max_chats_per_session Int?
message_limit Int? @default(20)
workspace_id Int
createdBy Int?
usersId Int?
createdAt DateTime @default(now())
workspace workspaces @relation(fields: [workspace_id], references: [id], onDelete: Cascade)
embed_chats embed_chats[]
users users? @relation(fields: [usersId], references: [id])
}
model embed_chats {
id Int @id @default(autoincrement())
prompt String
response String
session_id String
include Boolean @default(true)
connection_information String?
embed_id Int
usersId Int?
createdAt DateTime @default(now())
embed_config embed_configs @relation(fields: [embed_id], references: [id], onDelete: Cascade)
users users? @relation(fields: [usersId], references: [id])
}
model event_logs {
id Int @id @default(autoincrement())
event String
metadata String?
userId Int?
occurredAt DateTime @default(now())
@@index([event])
}
model slash_command_presets {
id Int @id @default(autoincrement())
command String
prompt String
description String
uid Int @default(0) // 0 is null user
userId Int?
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
user users? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([uid, command])
}
model document_sync_queues {
id Int @id @default(autoincrement())
staleAfterMs Int @default(604800000) // 7 days
nextSyncAt DateTime
createdAt DateTime @default(now())
lastSyncedAt DateTime @default(now())
workspaceDocId Int @unique
workspaceDoc workspace_documents? @relation(fields: [workspaceDocId], references: [id], onDelete: Cascade)
runs document_sync_executions[]
}
model document_sync_executions {
id Int @id @default(autoincrement())
queueId Int
status String @default("unknown")
result String?
createdAt DateTime @default(now())
queue document_sync_queues @relation(fields: [queueId], references: [id], onDelete: Cascade)
}
model browser_extension_api_keys {
id Int @id @default(autoincrement())
key String @unique
user_id Int?
createdAt DateTime @default(now())
lastUpdatedAt DateTime @updatedAt
user users? @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@index([user_id])
}
model temporary_auth_tokens {
id Int @id @default(autoincrement())
token String @unique
userId Int
expiresAt DateTime
createdAt DateTime @default(now())
user users @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([token])
@@index([userId])
}
model system_prompt_variables {
id Int @id @default(autoincrement())
key String @unique
value String?
description String?
type String @default("system") // system, user, dynamic
userId Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user users? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model prompt_history {
id Int @id @default(autoincrement())
workspace workspaces @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
workspaceId Int
prompt String
modifiedBy Int?
modifiedAt DateTime @default(now())
user users? @relation(fields: [modifiedBy], references: [id])
@@index([workspaceId])
}
// Schema specific to mobile app <> Desktop app connection
model desktop_mobile_devices {
id Int @id @default(autoincrement())
deviceOs String
deviceName String
token String @unique
approved Boolean @default(false)
userId Int?
createdAt DateTime @default(now())
user users? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model workspace_parsed_files {
id Int @id @default(autoincrement())
filename String @unique
workspaceId Int
userId Int?
threadId Int?
metadata String?
tokenCountEstimate Int? @default(0)
createdAt DateTime @default(now())
workspace workspaces @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
user users? @relation(fields: [userId], references: [id], onDelete: Cascade)
thread workspace_threads? @relation(fields: [threadId], references: [id], onDelete: Cascade)
@@index([workspaceId])
@@index([userId])
}
model external_communication_connectors {
id Int @id @default(autoincrement())
type String @unique
config String @default("{}")
active Boolean @default(false)
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
}
model scheduled_jobs {
id Int @id @default(autoincrement())
name String
prompt String
tools String? // JSON array of tool identifiers (null = use all enabled agent skills)
schedule String // Cron expression
enabled Boolean @default(true)
lastRunAt DateTime?
nextRunAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now())
runs scheduled_job_runs[]
}
model scheduled_job_runs {
id Int @id @default(autoincrement())
jobId Int
status String @default("queued") // queued | running | completed | failed | timed_out — model always sets explicitly
result String? // JSON execution trace
error String?
startedAt DateTime @default(now())
completedAt DateTime?
readAt DateTime? // null = unread
job scheduled_jobs @relation(fields: [jobId], references: [id], onDelete: Cascade)
@@index([jobId])
}