File size: 13,228 Bytes
a51a15b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
-- AGENTPRESS SCHEMA:
-- Create projects table
CREATE TABLE projects (
project_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
description TEXT,
account_id UUID NOT NULL REFERENCES basejump.accounts(id) ON DELETE CASCADE,
sandbox JSONB DEFAULT '{}'::jsonb,
is_public BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL
);
-- Create threads table
CREATE TABLE threads (
thread_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
account_id UUID REFERENCES basejump.accounts(id) ON DELETE CASCADE,
project_id UUID REFERENCES projects(project_id) ON DELETE CASCADE,
is_public BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL
);
-- Create messages table
CREATE TABLE messages (
message_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
thread_id UUID NOT NULL REFERENCES threads(thread_id) ON DELETE CASCADE,
type TEXT NOT NULL,
is_llm_message BOOLEAN NOT NULL DEFAULT TRUE,
content JSONB NOT NULL,
metadata JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL
);
-- Create agent_runs table
CREATE TABLE agent_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
thread_id UUID NOT NULL REFERENCES threads(thread_id),
status TEXT NOT NULL DEFAULT 'running',
started_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
completed_at TIMESTAMP WITH TIME ZONE,
responses JSONB NOT NULL DEFAULT '[]'::jsonb, -- TO BE REMOVED, NOT USED
error TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL
);
-- Create updated_at trigger function
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = TIMEZONE('utc'::text, NOW());
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create triggers for updated_at
CREATE TRIGGER update_threads_updated_at
BEFORE UPDATE ON threads
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_messages_updated_at
BEFORE UPDATE ON messages
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_agent_runs_updated_at
BEFORE UPDATE ON agent_runs
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_projects_updated_at
BEFORE UPDATE ON projects
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- Create indexes for better query performance
CREATE INDEX idx_threads_created_at ON threads(created_at);
CREATE INDEX idx_threads_account_id ON threads(account_id);
CREATE INDEX idx_threads_project_id ON threads(project_id);
CREATE INDEX idx_agent_runs_thread_id ON agent_runs(thread_id);
CREATE INDEX idx_agent_runs_status ON agent_runs(status);
CREATE INDEX idx_agent_runs_created_at ON agent_runs(created_at);
CREATE INDEX idx_projects_account_id ON projects(account_id);
CREATE INDEX idx_projects_created_at ON projects(created_at);
CREATE INDEX idx_messages_thread_id ON messages(thread_id);
CREATE INDEX idx_messages_created_at ON messages(created_at);
-- Enable Row Level Security
ALTER TABLE threads ENABLE ROW LEVEL SECURITY;
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE agent_runs ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
-- Project policies
CREATE POLICY project_select_policy ON projects
FOR SELECT
USING (
is_public = TRUE OR
basejump.has_role_on_account(account_id) = true
);
CREATE POLICY project_insert_policy ON projects
FOR INSERT
WITH CHECK (basejump.has_role_on_account(account_id) = true);
CREATE POLICY project_update_policy ON projects
FOR UPDATE
USING (basejump.has_role_on_account(account_id) = true);
CREATE POLICY project_delete_policy ON projects
FOR DELETE
USING (basejump.has_role_on_account(account_id) = true);
-- Thread policies based on project and account ownership
CREATE POLICY thread_select_policy ON threads
FOR SELECT
USING (
basejump.has_role_on_account(account_id) = true OR
EXISTS (
SELECT 1 FROM projects
WHERE projects.project_id = threads.project_id
AND (
projects.is_public = TRUE OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
CREATE POLICY thread_insert_policy ON threads
FOR INSERT
WITH CHECK (
basejump.has_role_on_account(account_id) = true OR
EXISTS (
SELECT 1 FROM projects
WHERE projects.project_id = threads.project_id
AND basejump.has_role_on_account(projects.account_id) = true
)
);
CREATE POLICY thread_update_policy ON threads
FOR UPDATE
USING (
basejump.has_role_on_account(account_id) = true OR
EXISTS (
SELECT 1 FROM projects
WHERE projects.project_id = threads.project_id
AND basejump.has_role_on_account(projects.account_id) = true
)
);
CREATE POLICY thread_delete_policy ON threads
FOR DELETE
USING (
basejump.has_role_on_account(account_id) = true OR
EXISTS (
SELECT 1 FROM projects
WHERE projects.project_id = threads.project_id
AND basejump.has_role_on_account(projects.account_id) = true
)
);
-- Create policies for agent_runs based on thread ownership
CREATE POLICY agent_run_select_policy ON agent_runs
FOR SELECT
USING (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = agent_runs.thread_id
AND (
projects.is_public = TRUE OR
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
CREATE POLICY agent_run_insert_policy ON agent_runs
FOR INSERT
WITH CHECK (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = agent_runs.thread_id
AND (
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
CREATE POLICY agent_run_update_policy ON agent_runs
FOR UPDATE
USING (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = agent_runs.thread_id
AND (
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
CREATE POLICY agent_run_delete_policy ON agent_runs
FOR DELETE
USING (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = agent_runs.thread_id
AND (
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
-- Create message policies based on thread ownership
CREATE POLICY message_select_policy ON messages
FOR SELECT
USING (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = messages.thread_id
AND (
projects.is_public = TRUE OR
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
CREATE POLICY message_insert_policy ON messages
FOR INSERT
WITH CHECK (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = messages.thread_id
AND (
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
CREATE POLICY message_update_policy ON messages
FOR UPDATE
USING (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = messages.thread_id
AND (
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
CREATE POLICY message_delete_policy ON messages
FOR DELETE
USING (
EXISTS (
SELECT 1 FROM threads
LEFT JOIN projects ON threads.project_id = projects.project_id
WHERE threads.thread_id = messages.thread_id
AND (
basejump.has_role_on_account(threads.account_id) = true OR
basejump.has_role_on_account(projects.account_id) = true
)
)
);
-- Grant permissions to roles
GRANT ALL PRIVILEGES ON TABLE projects TO authenticated, service_role;
GRANT SELECT ON TABLE projects TO anon;
GRANT SELECT ON TABLE threads TO authenticated, anon, service_role;
GRANT SELECT ON TABLE messages TO authenticated, anon, service_role;
GRANT ALL PRIVILEGES ON TABLE agent_runs TO authenticated, service_role;
-- Create a function that matches the Python get_messages behavior
CREATE OR REPLACE FUNCTION get_llm_formatted_messages(p_thread_id UUID)
RETURNS JSONB
SECURITY DEFINER -- Changed to SECURITY DEFINER to allow service role access
LANGUAGE plpgsql
AS $$
DECLARE
messages_array JSONB := '[]'::JSONB;
has_access BOOLEAN;
current_role TEXT;
latest_summary_id UUID;
latest_summary_time TIMESTAMP WITH TIME ZONE;
is_project_public BOOLEAN;
BEGIN
-- Get current role
SELECT current_user INTO current_role;
-- Check if associated project is public
SELECT p.is_public INTO is_project_public
FROM threads t
LEFT JOIN projects p ON t.project_id = p.project_id
WHERE t.thread_id = p_thread_id;
-- Skip access check for service_role or public projects
IF current_role = 'authenticated' AND NOT is_project_public THEN
-- Check if thread exists and user has access
SELECT EXISTS (
SELECT 1 FROM threads t
LEFT JOIN projects p ON t.project_id = p.project_id
WHERE t.thread_id = p_thread_id
AND (
basejump.has_role_on_account(t.account_id) = true OR
basejump.has_role_on_account(p.account_id) = true
)
) INTO has_access;
IF NOT has_access THEN
RAISE EXCEPTION 'Thread not found or access denied';
END IF;
END IF;
-- Find the latest summary message if it exists
SELECT message_id, created_at
INTO latest_summary_id, latest_summary_time
FROM messages
WHERE thread_id = p_thread_id
AND type = 'summary'
AND is_llm_message = TRUE
ORDER BY created_at DESC
LIMIT 1;
-- Log whether a summary was found (helpful for debugging)
IF latest_summary_id IS NOT NULL THEN
RAISE NOTICE 'Found latest summary message: id=%, time=%', latest_summary_id, latest_summary_time;
ELSE
RAISE NOTICE 'No summary message found for thread %', p_thread_id;
END IF;
-- Parse content if it's stored as a string and return proper JSON objects
WITH parsed_messages AS (
SELECT
message_id,
CASE
WHEN jsonb_typeof(content) = 'string' THEN content::text::jsonb
ELSE content
END AS parsed_content,
created_at,
type
FROM messages
WHERE thread_id = p_thread_id
AND is_llm_message = TRUE
AND (
-- Include the latest summary and all messages after it,
-- or all messages if no summary exists
latest_summary_id IS NULL
OR message_id = latest_summary_id
OR created_at > latest_summary_time
)
ORDER BY created_at
)
SELECT JSONB_AGG(parsed_content)
INTO messages_array
FROM parsed_messages;
-- Handle the case when no messages are found
IF messages_array IS NULL THEN
RETURN '[]'::JSONB;
END IF;
RETURN messages_array;
END;
$$;
-- Grant execute permissions
GRANT EXECUTE ON FUNCTION get_llm_formatted_messages TO authenticated, anon, service_role; |