-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
feat(catalog): implement conversation and message api (#77)
Because catalog needs to supoort conversation and message data This commit implemented the related APIs
- v0.22.0-alpha
- v0.22
- v0.21.1-alpha
- v0.21.0-alpha
- v0.21
- v0.20.1-alpha
- v0.20.0-alpha
- v0.20
- v0.19.0-alpha
- v0.19
- v0.18.0-alpha
- v0.18
- v0.17.0-alpha
- v0.17
- v0.16.1-alpha
- v0.16.0-alpha
- v0.16
- v0.15.0-alpha
- v0.15
- v0.14.2-alpha
- v0.14.1-alpha
- v0.14.0-alpha
- v0.14
- v0.13.2-alpha
- v0.13.1-alpha
- v0.13.0-alpha
- v0.13
- v0.12.0-alpha
- v0.12
- v0.11.0-alpha
- v0.11
- v0
Showing
21 changed files
with
13,372 additions
and
7,957 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
pkg/db/migration/000015_create_conversation_and_message_table.down.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
BEGIN; | ||
|
||
-- Drop the message table | ||
DROP TABLE IF EXISTS message; | ||
|
||
-- Drop the conversation table | ||
DROP TABLE IF EXISTS conversation; | ||
|
||
COMMIT; |
53 changes: 53 additions & 0 deletions
53
pkg/db/migration/000015_create_conversation_and_message_table.up.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
BEGIN; | ||
-- Create the conversation table | ||
CREATE TABLE conversation ( | ||
uid UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||
namespace_uid UUID NOT NULL, | ||
catalog_uid UUID NOT NULL, | ||
id VARCHAR(255) NOT NULL, | ||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
delete_time TIMESTAMP | ||
); | ||
-- Create unique index on namespace_uid, catalog_uid, and id | ||
CREATE UNIQUE INDEX idx_unique_namespace_catalog_id ON conversation (namespace_uid, catalog_uid, id) | ||
WHERE delete_time IS NULL; | ||
-- Create the message table | ||
CREATE TABLE message ( | ||
uid UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||
namespace_uid UUID NOT NULL, | ||
catalog_uid UUID NOT NULL, | ||
conversation_uid UUID NOT NULL, | ||
content TEXT, | ||
role VARCHAR(50) NOT NULL, | ||
type VARCHAR(50) NOT NULL, | ||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
delete_time TIMESTAMP | ||
); | ||
-- Add foreign key constraint with CASCADE DELETE | ||
ALTER TABLE message | ||
ADD CONSTRAINT fk_message_conversation FOREIGN KEY (conversation_uid) REFERENCES conversation(uid) ON DELETE CASCADE; | ||
-- Add index for efficient message retrieval | ||
CREATE INDEX idx_message_catalog_conversation ON message (namespace_uid, catalog_uid, conversation_uid); | ||
-- Add comments | ||
COMMENT ON TABLE conversation IS 'Table to store conversations'; | ||
COMMENT ON COLUMN conversation.uid IS 'Unique identifier(uuid) for the conversation'; | ||
COMMENT ON COLUMN conversation.namespace_uid IS 'Namespace identifier(uuid) for the conversation'; | ||
COMMENT ON COLUMN conversation.catalog_uid IS 'Catalog identifier(uuid) for the conversation'; | ||
COMMENT ON COLUMN conversation.id IS 'User-defined identifier for the conversation'; | ||
COMMENT ON COLUMN conversation.create_time IS 'Timestamp when the conversation was created'; | ||
COMMENT ON COLUMN conversation.update_time IS 'Timestamp when the conversation was last updated'; | ||
COMMENT ON COLUMN conversation.delete_time IS 'Timestamp when the conversation was deleted (soft delete)'; | ||
COMMENT ON TABLE message IS 'Table to store messages within conversations'; | ||
COMMENT ON COLUMN message.uid IS 'Unique identifier(uuid) for the message'; | ||
COMMENT ON COLUMN message.namespace_uid IS 'Namespace identifier(uuid) for the message'; | ||
COMMENT ON COLUMN message.catalog_uid IS 'Catalog identifier(uuid) for the message'; | ||
COMMENT ON COLUMN message.conversation_uid IS 'Reference to the conversation this message belongs to'; | ||
COMMENT ON COLUMN message.content IS 'Content of the message'; | ||
COMMENT ON COLUMN message.role IS 'Role of the message sender (e.g., user, assistant)'; | ||
COMMENT ON COLUMN message.type IS 'Type of the message'; | ||
COMMENT ON COLUMN message.create_time IS 'Timestamp when the message was created'; | ||
COMMENT ON COLUMN message.update_time IS 'Timestamp when the message was last updated'; | ||
COMMENT ON COLUMN message.delete_time IS 'Timestamp when the message was deleted (soft delete)'; | ||
COMMIT; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package handler | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"go.uber.org/zap" | ||
"google.golang.org/protobuf/types/known/timestamppb" | ||
|
||
"github.com/instill-ai/artifact-backend/pkg/customerror" | ||
"github.com/instill-ai/artifact-backend/pkg/logger" | ||
"github.com/instill-ai/artifact-backend/pkg/repository" | ||
artifactpb "github.com/instill-ai/protogen-go/artifact/artifact/v1alpha" | ||
) | ||
|
||
// CreateConversation creates a new conversation | ||
func (ph *PublicHandler) CreateConversation(ctx context.Context, req *artifactpb.CreateConversationRequest) (*artifactpb.CreateConversationResponse, error) { | ||
log, _ := logger.GetZapLogger(ctx) | ||
|
||
// Get user ID from context | ||
authUID, err := getUserUIDFromContext(ctx) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get user id from header: %v", err) | ||
} | ||
|
||
nameOk := isValidName(req.GetConversationId()) | ||
if !nameOk { | ||
msg := "the conversation id should be lowercase without any space or special character besides the hyphen, " + | ||
"it can not start with number or hyphen, and should be less than 32 characters. name: %v. err: %w" | ||
return nil, fmt.Errorf(msg, req.GetConversationId(), customerror.ErrInvalidArgument) | ||
} | ||
|
||
// ACL - check user's permission to create conversation in the namespace | ||
ns, catalog, err := ph.service.CheckCatalogUserPermission(ctx, req.GetNamespaceId(), req.GetCatalogId(), authUID) | ||
if err != nil { | ||
log.Error( | ||
"failed to check user permission", | ||
zap.Error(err), | ||
zap.String("namespace_id", req.GetNamespaceId()), | ||
zap.String("auth_uid", authUID), | ||
) | ||
return nil, fmt.Errorf("failed to check user permission: %w", err) | ||
} | ||
|
||
// Create conversation | ||
conversation, err := ph.service.Repository.CreateConversation(ctx, repository.Conversation{ | ||
NamespaceUID: ns.NsUID, | ||
CatalogUID: catalog.UID, | ||
ID: req.GetConversationId(), | ||
}) | ||
if err != nil { | ||
log.Error("failed to create conversation", zap.Error(err)) | ||
return nil, fmt.Errorf("failed to create conversation: %w", err) | ||
} | ||
|
||
return &artifactpb.CreateConversationResponse{ | ||
Conversation: convertToProtoConversation(conversation, req.GetNamespaceId(), req.GetCatalogId()), | ||
}, nil | ||
} | ||
|
||
// ListConversations lists conversations for a given catalog | ||
func (ph *PublicHandler) ListConversations(ctx context.Context, req *artifactpb.ListConversationsRequest) (*artifactpb.ListConversationsResponse, error) { | ||
log, _ := logger.GetZapLogger(ctx) | ||
|
||
// Get user ID from context | ||
authUID, err := getUserUIDFromContext(ctx) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get user id from header: %v", err) | ||
} | ||
|
||
// ACL - check user's permission to list conversations in the catalog | ||
ns, catalog, err := ph.service.CheckCatalogUserPermission(ctx, req.GetNamespaceId(), req.GetCatalogId(), authUID) | ||
if err != nil { | ||
log.Error( | ||
"failed to check user permission", | ||
zap.Error(err), | ||
zap.String("namespace_id", req.GetNamespaceId()), | ||
zap.String("auth_uid", authUID), | ||
) | ||
return nil, fmt.Errorf("failed to check user permission: %w", err) | ||
} | ||
|
||
// Get conversations | ||
conversations, totalCount, nextPageToken, err := ph.service.Repository.ListConversations(ctx, ns.NsUID, catalog.UID, req.GetPageSize(), req.GetPageToken()) | ||
if err != nil { | ||
log.Error("failed to list conversations", zap.Error(err)) | ||
return nil, fmt.Errorf("failed to list conversations: %w", err) | ||
} | ||
|
||
// Convert to proto conversations | ||
protoConversations := make([]*artifactpb.Conversation, len(conversations)) | ||
for i, conv := range conversations { | ||
protoConversations[i] = convertToProtoConversation(conv, req.GetNamespaceId(), req.GetCatalogId()) | ||
} | ||
|
||
return &artifactpb.ListConversationsResponse{ | ||
Conversations: protoConversations, | ||
NextPageToken: nextPageToken, | ||
TotalSize: int32(totalCount), | ||
}, nil | ||
} | ||
|
||
// UpdateConversation updates an existing conversation | ||
func (ph *PublicHandler) UpdateConversation(ctx context.Context, req *artifactpb.UpdateConversationRequest) (*artifactpb.UpdateConversationResponse, error) { | ||
log, _ := logger.GetZapLogger(ctx) | ||
|
||
// Get user ID from context | ||
authUID, err := getUserUIDFromContext(ctx) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get user id from header: %v", err) | ||
} | ||
|
||
// ACL - check user's permission to update conversation in the catalog | ||
ns, catalog, err := ph.service.CheckCatalogUserPermission(ctx, req.GetNamespaceId(), req.GetCatalogId(), authUID) | ||
if err != nil { | ||
log.Error( | ||
"failed to check user permission", | ||
zap.Error(err), | ||
zap.String("namespace_id", req.GetNamespaceId()), | ||
zap.String("auth_uid", authUID), | ||
) | ||
return nil, fmt.Errorf("failed to check user permission: %w", err) | ||
} | ||
|
||
// Get the existing conversation | ||
existingConv, err := ph.service.Repository.GetConversationByID(ctx, ns.NsUID, catalog.UID, req.GetConversationId()) | ||
if err != nil { | ||
log.Error("failed to get existing conversation", zap.Error(err)) | ||
return nil, fmt.Errorf("failed to get existing conversation: %w", err) | ||
} | ||
|
||
// Update conversation | ||
updatedConv, err := ph.service.Repository.UpdateConversationByUpdateMap(ctx, existingConv.UID, map[string]interface{}{ | ||
repository.ConversationColumn.ID: req.GetNewConversationId(), | ||
}) | ||
if err != nil { | ||
log.Error("failed to update conversation", zap.Error(err)) | ||
return nil, fmt.Errorf("failed to update conversation: %w", err) | ||
} | ||
|
||
return &artifactpb.UpdateConversationResponse{ | ||
Conversation: convertToProtoConversation(updatedConv, req.GetNamespaceId(), req.GetCatalogId()), | ||
}, nil | ||
} | ||
|
||
// DeleteConversation deletes an existing conversation | ||
func (ph *PublicHandler) DeleteConversation(ctx context.Context, req *artifactpb.DeleteConversationRequest) (*artifactpb.DeleteConversationResponse, error) { | ||
log, _ := logger.GetZapLogger(ctx) | ||
|
||
// Get user ID from context | ||
authUID, err := getUserUIDFromContext(ctx) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get user id from header: %v", err) | ||
} | ||
|
||
// ACL - check user's permission to delete conversation in the namespace | ||
ns, catalog, err := ph.service.CheckCatalogUserPermission(ctx, req.GetNamespaceId(), req.GetCatalogId(), authUID) | ||
if err != nil { | ||
log.Error( | ||
"failed to check user permission", | ||
zap.Error(err), | ||
zap.String("namespace_id", req.GetNamespaceId()), | ||
zap.String("auth_uid", authUID), | ||
) | ||
return nil, fmt.Errorf("failed to check user permission: %w", err) | ||
} | ||
// Delete conversation | ||
err = ph.service.Repository.SoftDeleteConversation(ctx, ns.NsUID, catalog.UID, req.GetConversationId()) | ||
if err != nil { | ||
log.Error("failed to delete conversation", zap.Error(err)) | ||
return nil, fmt.Errorf("failed to delete conversation: %w", err) | ||
} | ||
|
||
return &artifactpb.DeleteConversationResponse{}, nil | ||
} | ||
|
||
// Helper function to convert repository.Conversation to artifactpb.Conversation | ||
func convertToProtoConversation(conv *repository.Conversation, nsID, catalogID string) *artifactpb.Conversation { | ||
|
||
return &artifactpb.Conversation{ | ||
Uid: conv.UID.String(), | ||
NamespaceId: nsID, | ||
CatalogId: catalogID, | ||
Id: conv.ID, | ||
CreateTime: timestamppb.New(conv.CreateTime), | ||
UpdateTime: timestamppb.New(conv.UpdateTime), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.