2025-03-30 14:48:57 +08:00
|
|
|
-- Create projects table
|
|
|
|
CREATE TABLE projects (
|
|
|
|
project_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
description TEXT,
|
2025-03-29 06:33:55 +08:00
|
|
|
user_id UUID NOT NULL,
|
|
|
|
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
|
|
|
|
);
|
|
|
|
|
2025-03-30 14:48:57 +08:00
|
|
|
-- Create threads table
|
|
|
|
CREATE TABLE threads (
|
2025-04-06 17:10:18 +08:00
|
|
|
thread_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
2025-03-30 14:48:57 +08:00
|
|
|
user_id UUID,
|
|
|
|
project_id UUID REFERENCES projects(project_id) ON DELETE CASCADE,
|
2025-04-06 17:10:18 +08:00
|
|
|
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,
|
2025-03-29 06:33:55 +08:00
|
|
|
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,
|
|
|
|
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();
|
|
|
|
|
2025-04-06 17:10:18 +08:00
|
|
|
CREATE TRIGGER update_messages_updated_at
|
|
|
|
BEFORE UPDATE ON messages
|
|
|
|
FOR EACH ROW
|
|
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
|
2025-03-30 14:48:57 +08:00
|
|
|
CREATE TRIGGER update_agent_runs_updated_at
|
|
|
|
BEFORE UPDATE ON agent_runs
|
2025-03-29 06:33:55 +08:00
|
|
|
FOR EACH ROW
|
|
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
|
2025-03-30 14:48:57 +08:00
|
|
|
CREATE TRIGGER update_projects_updated_at
|
|
|
|
BEFORE UPDATE ON projects
|
2025-03-29 06:33:55 +08:00
|
|
|
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_user_id ON threads(user_id);
|
2025-03-30 14:48:57 +08:00
|
|
|
CREATE INDEX idx_threads_project_id ON threads(project_id);
|
2025-03-29 06:33:55 +08:00
|
|
|
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);
|
2025-03-30 14:48:57 +08:00
|
|
|
CREATE INDEX idx_projects_user_id ON projects(user_id);
|
|
|
|
CREATE INDEX idx_projects_created_at ON projects(created_at);
|
2025-04-06 17:10:18 +08:00
|
|
|
CREATE INDEX idx_messages_thread_id ON messages(thread_id);
|
|
|
|
CREATE INDEX idx_messages_created_at ON messages(created_at);
|
2025-03-29 06:33:55 +08:00
|
|
|
|
|
|
|
-- Enable Row Level Security
|
|
|
|
ALTER TABLE threads ENABLE ROW LEVEL SECURITY;
|
2025-04-06 17:10:18 +08:00
|
|
|
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
|
2025-03-29 06:33:55 +08:00
|
|
|
ALTER TABLE agent_runs ENABLE ROW LEVEL SECURITY;
|
2025-03-30 14:48:57 +08:00
|
|
|
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
|
2025-03-29 06:33:55 +08:00
|
|
|
|
2025-03-30 14:48:57 +08:00
|
|
|
-- Project policies
|
|
|
|
CREATE POLICY project_select_policy ON projects
|
2025-03-29 06:33:55 +08:00
|
|
|
FOR SELECT
|
|
|
|
USING (auth.uid() = user_id);
|
|
|
|
|
2025-03-30 14:48:57 +08:00
|
|
|
CREATE POLICY project_insert_policy ON projects
|
|
|
|
FOR INSERT
|
|
|
|
WITH CHECK (auth.uid() IS NOT NULL);
|
|
|
|
|
|
|
|
CREATE POLICY project_update_policy ON projects
|
|
|
|
FOR UPDATE
|
|
|
|
USING (auth.uid() = user_id);
|
|
|
|
|
|
|
|
CREATE POLICY project_delete_policy ON projects
|
|
|
|
FOR DELETE
|
|
|
|
USING (auth.uid() = user_id);
|
|
|
|
|
|
|
|
-- Thread policies based on project ownership
|
|
|
|
CREATE POLICY thread_select_policy ON threads
|
|
|
|
FOR SELECT
|
|
|
|
USING (
|
|
|
|
auth.uid() = user_id OR
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1 FROM projects
|
|
|
|
WHERE projects.project_id = threads.project_id
|
|
|
|
AND projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2025-03-29 06:33:55 +08:00
|
|
|
CREATE POLICY thread_insert_policy ON threads
|
|
|
|
FOR INSERT
|
2025-03-30 14:48:57 +08:00
|
|
|
WITH CHECK (
|
|
|
|
auth.uid() = user_id OR
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1 FROM projects
|
|
|
|
WHERE projects.project_id = threads.project_id
|
|
|
|
AND projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
);
|
2025-03-29 06:33:55 +08:00
|
|
|
|
|
|
|
CREATE POLICY thread_update_policy ON threads
|
|
|
|
FOR UPDATE
|
2025-03-30 14:48:57 +08:00
|
|
|
USING (
|
|
|
|
auth.uid() = user_id OR
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1 FROM projects
|
|
|
|
WHERE projects.project_id = threads.project_id
|
|
|
|
AND projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
);
|
2025-03-29 06:33:55 +08:00
|
|
|
|
|
|
|
CREATE POLICY thread_delete_policy ON threads
|
|
|
|
FOR DELETE
|
2025-03-30 14:48:57 +08:00
|
|
|
USING (
|
|
|
|
auth.uid() = user_id OR
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1 FROM projects
|
|
|
|
WHERE projects.project_id = threads.project_id
|
|
|
|
AND projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
);
|
2025-03-29 06:33:55 +08:00
|
|
|
|
|
|
|
-- 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
|
2025-03-30 14:48:57 +08:00
|
|
|
JOIN projects ON threads.project_id = projects.project_id
|
2025-03-29 06:33:55 +08:00
|
|
|
WHERE threads.thread_id = agent_runs.thread_id
|
2025-03-30 14:48:57 +08:00
|
|
|
AND (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
2025-03-29 06:33:55 +08:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
CREATE POLICY agent_run_insert_policy ON agent_runs
|
|
|
|
FOR INSERT
|
|
|
|
WITH CHECK (
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1 FROM threads
|
2025-03-30 14:48:57 +08:00
|
|
|
JOIN projects ON threads.project_id = projects.project_id
|
2025-03-29 06:33:55 +08:00
|
|
|
WHERE threads.thread_id = agent_runs.thread_id
|
2025-03-30 14:48:57 +08:00
|
|
|
AND (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
2025-03-29 06:33:55 +08:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
CREATE POLICY agent_run_update_policy ON agent_runs
|
|
|
|
FOR UPDATE
|
|
|
|
USING (
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1 FROM threads
|
2025-03-30 14:48:57 +08:00
|
|
|
JOIN projects ON threads.project_id = projects.project_id
|
2025-03-29 06:33:55 +08:00
|
|
|
WHERE threads.thread_id = agent_runs.thread_id
|
2025-03-30 14:48:57 +08:00
|
|
|
AND (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
2025-03-29 06:33:55 +08:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
CREATE POLICY agent_run_delete_policy ON agent_runs
|
|
|
|
FOR DELETE
|
|
|
|
USING (
|
|
|
|
EXISTS (
|
|
|
|
SELECT 1 FROM threads
|
2025-03-30 14:48:57 +08:00
|
|
|
JOIN projects ON threads.project_id = projects.project_id
|
2025-03-29 06:33:55 +08:00
|
|
|
WHERE threads.thread_id = agent_runs.thread_id
|
2025-03-30 14:48:57 +08:00
|
|
|
AND (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
2025-03-29 06:33:55 +08:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2025-04-06 17:10:18 +08:00
|
|
|
-- 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 (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
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 (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
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 (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
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 (
|
|
|
|
threads.user_id = auth.uid() OR
|
|
|
|
projects.user_id = auth.uid()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2025-03-30 14:48:57 +08:00
|
|
|
-- Grant permissions to roles
|
|
|
|
GRANT ALL PRIVILEGES ON TABLE projects TO authenticated, service_role;
|
|
|
|
GRANT ALL PRIVILEGES ON TABLE threads TO authenticated, service_role;
|
2025-04-06 17:10:18 +08:00
|
|
|
GRANT ALL PRIVILEGES ON TABLE messages TO authenticated, service_role;
|
|
|
|
GRANT ALL PRIVILEGES ON TABLE agent_runs TO authenticated, service_role;
|
|
|
|
|
2025-04-11 07:53:01 +08:00
|
|
|
-- Create a function that matches the Python get_llm_messages behavior
|
2025-04-06 17:10:18 +08:00
|
|
|
CREATE OR REPLACE FUNCTION get_llm_formatted_messages(p_thread_id UUID)
|
|
|
|
RETURNS JSONB
|
|
|
|
SECURITY INVOKER
|
|
|
|
LANGUAGE plpgsql
|
|
|
|
AS $$
|
|
|
|
DECLARE
|
|
|
|
messages_array JSONB := '[]'::JSONB;
|
|
|
|
BEGIN
|
|
|
|
-- Check if thread exists
|
|
|
|
IF NOT EXISTS (
|
|
|
|
SELECT 1 FROM threads t
|
|
|
|
WHERE t.thread_id = p_thread_id
|
|
|
|
) THEN
|
|
|
|
RAISE EXCEPTION 'Thread not found';
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
-- Parse content if it's stored as a string and return proper JSON objects
|
|
|
|
WITH parsed_messages AS (
|
|
|
|
SELECT
|
|
|
|
CASE
|
|
|
|
WHEN jsonb_typeof(content) = 'string' THEN content::text::jsonb
|
|
|
|
ELSE content
|
|
|
|
END AS parsed_content,
|
|
|
|
created_at
|
|
|
|
FROM messages
|
|
|
|
WHERE thread_id = p_thread_id
|
|
|
|
AND is_llm_message = TRUE
|
|
|
|
)
|
|
|
|
SELECT JSONB_AGG(parsed_content ORDER BY created_at)
|
|
|
|
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 permission on the function
|
|
|
|
GRANT EXECUTE ON FUNCTION get_llm_formatted_messages TO authenticated, service_role;
|