diff --git a/agent/src/agent.ts b/agent/src/agent.ts index e8e4a66a..20672422 100644 --- a/agent/src/agent.ts +++ b/agent/src/agent.ts @@ -178,6 +178,7 @@ export const createAgent = ( channelIdentityId: identity.channelIdentityId || identity.contactId || '', displayName: identity.displayName || identity.contactName || 'User', channel: currentChannel, + conversationType: identity.conversationType || 'direct', date: new Date(), attachments: files, }) diff --git a/agent/src/models.ts b/agent/src/models.ts index c2a39c36..999762a6 100644 --- a/agent/src/models.ts +++ b/agent/src/models.ts @@ -33,6 +33,7 @@ export const IdentityContextModel = z.object({ contactAlias: z.string().optional(), userId: z.string().optional(), currentPlatform: z.string().optional(), + conversationType: z.string().optional(), replyTarget: z.string().optional(), sessionToken: z.string().optional(), }) diff --git a/agent/src/prompts/user.ts b/agent/src/prompts/user.ts index 742b8a0b..3601c3c0 100644 --- a/agent/src/prompts/user.ts +++ b/agent/src/prompts/user.ts @@ -4,18 +4,20 @@ export interface UserParams { channelIdentityId: string displayName: string channel: string + conversationType: string date: Date attachments: ContainerFileAttachment[] } export const user = ( query: string, - { channelIdentityId, displayName, channel, date, attachments }: UserParams + { channelIdentityId, displayName, channel, conversationType, date, attachments }: UserParams ) => { const headers = { 'channel-identity-id': channelIdentityId, 'display-name': displayName, 'channel': channel, + 'conversation-type': conversationType, 'time': date.toISOString(), 'attachments': attachments.map(attachment => attachment.path), } diff --git a/agent/src/types/agent.ts b/agent/src/types/agent.ts index c93fffa1..50e60569 100644 --- a/agent/src/types/agent.ts +++ b/agent/src/types/agent.ts @@ -16,6 +16,7 @@ export interface IdentityContext { userId?: string currentPlatform?: string + conversationType?: string replyTarget?: string sessionToken?: string } diff --git a/db/migrations/0001_init.up.sql b/db/migrations/0001_init.up.sql index b2f82da4..ee738478 100644 --- a/db/migrations/0001_init.up.sql +++ b/db/migrations/0001_init.up.sql @@ -207,6 +207,7 @@ CREATE TABLE IF NOT EXISTS bot_channel_routes ( channel_config_id UUID REFERENCES bot_channel_configs(id) ON DELETE SET NULL, external_conversation_id TEXT NOT NULL, external_thread_id TEXT, + conversation_type TEXT, default_reply_target TEXT, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), diff --git a/db/migrations/0002_channel_identity_avatar.down.sql b/db/migrations/0002_channel_identity_avatar.down.sql new file mode 100644 index 00000000..da834906 --- /dev/null +++ b/db/migrations/0002_channel_identity_avatar.down.sql @@ -0,0 +1,2 @@ +-- 0002_channel_identity_avatar (down) +ALTER TABLE channel_identities DROP COLUMN IF EXISTS avatar_url; diff --git a/db/migrations/0002_channel_identity_avatar.up.sql b/db/migrations/0002_channel_identity_avatar.up.sql new file mode 100644 index 00000000..7684b8a2 --- /dev/null +++ b/db/migrations/0002_channel_identity_avatar.up.sql @@ -0,0 +1,3 @@ +-- 0002_channel_identity_avatar +-- Add avatar_url column to channel_identities for sender profile display. +ALTER TABLE channel_identities ADD COLUMN IF NOT EXISTS avatar_url TEXT; diff --git a/db/migrations/0003_route_conversation_type.down.sql b/db/migrations/0003_route_conversation_type.down.sql new file mode 100644 index 00000000..b8a7b3be --- /dev/null +++ b/db/migrations/0003_route_conversation_type.down.sql @@ -0,0 +1,2 @@ +-- 0003_route_conversation_type (down) +ALTER TABLE bot_channel_routes DROP COLUMN IF EXISTS conversation_type; diff --git a/db/migrations/0003_route_conversation_type.up.sql b/db/migrations/0003_route_conversation_type.up.sql new file mode 100644 index 00000000..297b046f --- /dev/null +++ b/db/migrations/0003_route_conversation_type.up.sql @@ -0,0 +1,3 @@ +-- 0003_route_conversation_type +-- Add conversation_type column to bot_channel_routes for conversation context. +ALTER TABLE bot_channel_routes ADD COLUMN IF NOT EXISTS conversation_type TEXT; diff --git a/db/queries/channel_routes.sql b/db/queries/channel_routes.sql index be84a19f..5a98bd5c 100644 --- a/db/queries/channel_routes.sql +++ b/db/queries/channel_routes.sql @@ -1,6 +1,6 @@ -- name: CreateChatRoute :one INSERT INTO bot_channel_routes ( - bot_id, channel_type, channel_config_id, external_conversation_id, external_thread_id, default_reply_target, metadata + bot_id, channel_type, channel_config_id, external_conversation_id, external_thread_id, conversation_type, default_reply_target, metadata ) VALUES ( sqlc.arg(bot_id), @@ -8,6 +8,7 @@ VALUES ( sqlc.narg(channel_config_id)::uuid, sqlc.arg(conversation_id), sqlc.narg(thread_id)::text, + sqlc.narg(conversation_type)::text, sqlc.narg(reply_target)::text, sqlc.arg(metadata) ) @@ -19,6 +20,7 @@ RETURNING channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, @@ -33,6 +35,7 @@ SELECT channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, @@ -53,6 +56,7 @@ SELECT channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, @@ -69,6 +73,7 @@ SELECT channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, diff --git a/internal/channel/inbound/channel.go b/internal/channel/inbound/channel.go index c57b436d..cb4b9847 100644 --- a/internal/channel/inbound/channel.go +++ b/internal/channel/inbound/channel.go @@ -279,6 +279,7 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel RouteID: resolved.RouteID, ChatToken: chatToken, ExternalMessageID: sourceMessageID, + ConversationType: msg.Conversation.Type, Query: text, CurrentChannel: msg.Channel.String(), Channels: []string{msg.Channel.String()}, diff --git a/internal/channel/route/service.go b/internal/channel/route/service.go index 149f7146..12cfc381 100644 --- a/internal/channel/route/service.go +++ b/internal/channel/route/service.go @@ -63,14 +63,15 @@ func (s *DBService) Create(ctx context.Context, input CreateInput) (Route, error } row, err := s.queries.CreateChatRoute(ctx, sqlc.CreateChatRouteParams{ - ChatID: pgConversationID, - BotID: pgBotID, - Platform: input.Platform, - ChannelConfigID: pgConfigID, - ConversationID: input.ConversationID, - ThreadID: toPgText(input.ThreadID), - ReplyTarget: toPgText(input.ReplyTarget), - Metadata: metadata, + ChatID: pgConversationID, + BotID: pgBotID, + Platform: input.Platform, + ChannelConfigID: pgConfigID, + ConversationID: input.ConversationID, + ThreadID: toPgText(input.ThreadID), + ConversationType: toPgText(input.ConversationType), + ReplyTarget: toPgText(input.ReplyTarget), + Metadata: metadata, }) if err != nil { return Route{}, fmt.Errorf("create route: %w", err) @@ -208,13 +209,14 @@ func (s *DBService) ResolveConversation(ctx context.Context, input ResolveInput) } newRoute, err := s.Create(ctx, CreateInput{ - ChatID: createdConversation.ID, - BotID: input.BotID, - Platform: input.Platform, - ChannelConfigID: input.ChannelConfigID, - ConversationID: input.ConversationID, - ThreadID: input.ThreadID, - ReplyTarget: input.ReplyTarget, + ChatID: createdConversation.ID, + BotID: input.BotID, + Platform: input.Platform, + ChannelConfigID: input.ChannelConfigID, + ConversationID: input.ConversationID, + ThreadID: input.ThreadID, + ConversationType: input.ConversationType, + ReplyTarget: input.ReplyTarget, }) if err != nil { return ResolveConversationResult{}, fmt.Errorf("create route: %w", err) @@ -260,81 +262,50 @@ func (s *DBService) resolveConversationCreatorChannelIdentityID(ctx context.Cont func toRouteFromCreate(row sqlc.CreateChatRouteRow) Route { return toRouteFields( - row.ID, - row.ChatID, - row.BotID, - row.Platform, - row.ChannelConfigID, - row.ConversationID, - row.ThreadID, - row.ReplyTarget, - row.Metadata, - row.CreatedAt, - row.UpdatedAt, + row.ID, row.ChatID, row.BotID, row.Platform, row.ChannelConfigID, + row.ConversationID, row.ThreadID, row.ConversationType, row.ReplyTarget, + row.Metadata, row.CreatedAt, row.UpdatedAt, ) } func toRouteFromFind(row sqlc.FindChatRouteRow) Route { return toRouteFields( - row.ID, - row.ChatID, - row.BotID, - row.Platform, - row.ChannelConfigID, - row.ConversationID, - row.ThreadID, - row.ReplyTarget, - row.Metadata, - row.CreatedAt, - row.UpdatedAt, + row.ID, row.ChatID, row.BotID, row.Platform, row.ChannelConfigID, + row.ConversationID, row.ThreadID, row.ConversationType, row.ReplyTarget, + row.Metadata, row.CreatedAt, row.UpdatedAt, ) } func toRouteFromGet(row sqlc.GetChatRouteByIDRow) Route { return toRouteFields( - row.ID, - row.ChatID, - row.BotID, - row.Platform, - row.ChannelConfigID, - row.ConversationID, - row.ThreadID, - row.ReplyTarget, - row.Metadata, - row.CreatedAt, - row.UpdatedAt, + row.ID, row.ChatID, row.BotID, row.Platform, row.ChannelConfigID, + row.ConversationID, row.ThreadID, row.ConversationType, row.ReplyTarget, + row.Metadata, row.CreatedAt, row.UpdatedAt, ) } func toRouteFromList(row sqlc.ListChatRoutesRow) Route { return toRouteFields( - row.ID, - row.ChatID, - row.BotID, - row.Platform, - row.ChannelConfigID, - row.ConversationID, - row.ThreadID, - row.ReplyTarget, - row.Metadata, - row.CreatedAt, - row.UpdatedAt, + row.ID, row.ChatID, row.BotID, row.Platform, row.ChannelConfigID, + row.ConversationID, row.ThreadID, row.ConversationType, row.ReplyTarget, + row.Metadata, row.CreatedAt, row.UpdatedAt, ) } -func toRouteFields(id, conversationID, botID pgtype.UUID, platform string, channelConfigID pgtype.UUID, externalConversationID string, threadID, replyTarget pgtype.Text, metadata []byte, createdAt, updatedAt pgtype.Timestamptz) Route { +func toRouteFields(id, conversationID, botID pgtype.UUID, platform string, channelConfigID pgtype.UUID, externalConversationID string, threadID, conversationType, replyTarget pgtype.Text, metadata []byte, createdAt, updatedAt pgtype.Timestamptz) Route { return Route{ - ID: id.String(), - ChatID: conversationID.String(), - BotID: botID.String(), - Platform: platform, - ChannelConfigID: channelConfigID.String(), - ConversationID: externalConversationID, - ThreadID: dbpkg.TextToString(threadID), - ReplyTarget: dbpkg.TextToString(replyTarget), - Metadata: parseJSONMap(metadata), - CreatedAt: createdAt.Time, - UpdatedAt: updatedAt.Time, + ID: id.String(), + ChatID: conversationID.String(), + BotID: botID.String(), + Platform: platform, + ChannelConfigID: channelConfigID.String(), + ConversationID: externalConversationID, + ThreadID: dbpkg.TextToString(threadID), + ConversationType: dbpkg.TextToString(conversationType), + ReplyTarget: dbpkg.TextToString(replyTarget), + Metadata: parseJSONMap(metadata), + CreatedAt: createdAt.Time, + UpdatedAt: updatedAt.Time, } } diff --git a/internal/channel/route/types.go b/internal/channel/route/types.go index a7c1e44c..73443b7d 100644 --- a/internal/channel/route/types.go +++ b/internal/channel/route/types.go @@ -7,17 +7,18 @@ import ( // Route maps external channel conversations to an internal conversation. type Route struct { - ID string `json:"id"` - ChatID string `json:"chat_id"` - BotID string `json:"bot_id"` - Platform string `json:"platform"` - ChannelConfigID string `json:"channel_config_id,omitempty"` - ConversationID string `json:"conversation_id"` - ThreadID string `json:"thread_id,omitempty"` - ReplyTarget string `json:"reply_target,omitempty"` - Metadata map[string]any `json:"metadata,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID string `json:"id"` + ChatID string `json:"chat_id"` + BotID string `json:"bot_id"` + Platform string `json:"platform"` + ChannelConfigID string `json:"channel_config_id,omitempty"` + ConversationID string `json:"conversation_id"` + ThreadID string `json:"thread_id,omitempty"` + ConversationType string `json:"conversation_type,omitempty"` + ReplyTarget string `json:"reply_target,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } // ResolveConversationResult is returned by ResolveConversation. @@ -29,14 +30,15 @@ type ResolveConversationResult struct { // CreateInput is the input for creating a route. type CreateInput struct { - ChatID string - BotID string - Platform string - ChannelConfigID string - ConversationID string - ThreadID string - ReplyTarget string - Metadata map[string]any + ChatID string + BotID string + Platform string + ChannelConfigID string + ConversationID string + ThreadID string + ConversationType string + ReplyTarget string + Metadata map[string]any } // ResolveInput is the input for route-to-conversation resolution. diff --git a/internal/conversation/flow/resolver.go b/internal/conversation/flow/resolver.go index 83057e17..db402b58 100644 --- a/internal/conversation/flow/resolver.go +++ b/internal/conversation/flow/resolver.go @@ -122,6 +122,7 @@ type gatewayIdentity struct { ChannelIdentityID string `json:"channelIdentityId"` DisplayName string `json:"displayName"` CurrentPlatform string `json:"currentPlatform,omitempty"` + ConversationType string `json:"conversationType,omitempty"` SessionToken string `json:"sessionToken,omitempty"` } @@ -270,6 +271,7 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r ChannelIdentityID: strings.TrimSpace(req.SourceChannelIdentityID), DisplayName: r.resolveDisplayName(ctx, req), CurrentPlatform: req.CurrentChannel, + ConversationType: strings.TrimSpace(req.ConversationType), SessionToken: req.ChatToken, }, Attachments: []any{}, diff --git a/internal/conversation/types.go b/internal/conversation/types.go index aa6db230..c6431a28 100644 --- a/internal/conversation/types.go +++ b/internal/conversation/types.go @@ -203,6 +203,7 @@ type ChatRequest struct { RouteID string `json:"-"` ChatToken string `json:"-"` ExternalMessageID string `json:"-"` + ConversationType string `json:"-"` UserMessagePersisted bool `json:"-"` Query string `json:"query"` diff --git a/internal/db/sqlc/channel_routes.sql.go b/internal/db/sqlc/channel_routes.sql.go index 0be56106..b444ea0b 100644 --- a/internal/db/sqlc/channel_routes.sql.go +++ b/internal/db/sqlc/channel_routes.sql.go @@ -13,7 +13,7 @@ import ( const createChatRoute = `-- name: CreateChatRoute :one INSERT INTO bot_channel_routes ( - bot_id, channel_type, channel_config_id, external_conversation_id, external_thread_id, default_reply_target, metadata + bot_id, channel_type, channel_config_id, external_conversation_id, external_thread_id, conversation_type, default_reply_target, metadata ) VALUES ( $1, @@ -22,16 +22,18 @@ VALUES ( $4, $5::text, $6::text, - $7 + $7::text, + $8 ) RETURNING id, - $8::uuid AS chat_id, + $9::uuid AS chat_id, bot_id, channel_type AS platform, channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, @@ -39,28 +41,30 @@ RETURNING ` type CreateChatRouteParams struct { - BotID pgtype.UUID `json:"bot_id"` - Platform string `json:"platform"` - ChannelConfigID pgtype.UUID `json:"channel_config_id"` - ConversationID string `json:"conversation_id"` - ThreadID pgtype.Text `json:"thread_id"` - ReplyTarget pgtype.Text `json:"reply_target"` - Metadata []byte `json:"metadata"` - ChatID pgtype.UUID `json:"chat_id"` + BotID pgtype.UUID `json:"bot_id"` + Platform string `json:"platform"` + ChannelConfigID pgtype.UUID `json:"channel_config_id"` + ConversationID string `json:"conversation_id"` + ThreadID pgtype.Text `json:"thread_id"` + ConversationType pgtype.Text `json:"conversation_type"` + ReplyTarget pgtype.Text `json:"reply_target"` + Metadata []byte `json:"metadata"` + ChatID pgtype.UUID `json:"chat_id"` } type CreateChatRouteRow struct { - ID pgtype.UUID `json:"id"` - ChatID pgtype.UUID `json:"chat_id"` - BotID pgtype.UUID `json:"bot_id"` - Platform string `json:"platform"` - ChannelConfigID pgtype.UUID `json:"channel_config_id"` - ConversationID string `json:"conversation_id"` - ThreadID pgtype.Text `json:"thread_id"` - ReplyTarget pgtype.Text `json:"reply_target"` - Metadata []byte `json:"metadata"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` + ID pgtype.UUID `json:"id"` + ChatID pgtype.UUID `json:"chat_id"` + BotID pgtype.UUID `json:"bot_id"` + Platform string `json:"platform"` + ChannelConfigID pgtype.UUID `json:"channel_config_id"` + ConversationID string `json:"conversation_id"` + ThreadID pgtype.Text `json:"thread_id"` + ConversationType pgtype.Text `json:"conversation_type"` + ReplyTarget pgtype.Text `json:"reply_target"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } func (q *Queries) CreateChatRoute(ctx context.Context, arg CreateChatRouteParams) (CreateChatRouteRow, error) { @@ -70,6 +74,7 @@ func (q *Queries) CreateChatRoute(ctx context.Context, arg CreateChatRouteParams arg.ChannelConfigID, arg.ConversationID, arg.ThreadID, + arg.ConversationType, arg.ReplyTarget, arg.Metadata, arg.ChatID, @@ -83,6 +88,7 @@ func (q *Queries) CreateChatRoute(ctx context.Context, arg CreateChatRouteParams &i.ChannelConfigID, &i.ConversationID, &i.ThreadID, + &i.ConversationType, &i.ReplyTarget, &i.Metadata, &i.CreatedAt, @@ -110,6 +116,7 @@ SELECT channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, @@ -130,17 +137,18 @@ type FindChatRouteParams struct { } type FindChatRouteRow struct { - ID pgtype.UUID `json:"id"` - ChatID pgtype.UUID `json:"chat_id"` - BotID pgtype.UUID `json:"bot_id"` - Platform string `json:"platform"` - ChannelConfigID pgtype.UUID `json:"channel_config_id"` - ConversationID string `json:"conversation_id"` - ThreadID pgtype.Text `json:"thread_id"` - ReplyTarget pgtype.Text `json:"reply_target"` - Metadata []byte `json:"metadata"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` + ID pgtype.UUID `json:"id"` + ChatID pgtype.UUID `json:"chat_id"` + BotID pgtype.UUID `json:"bot_id"` + Platform string `json:"platform"` + ChannelConfigID pgtype.UUID `json:"channel_config_id"` + ConversationID string `json:"conversation_id"` + ThreadID pgtype.Text `json:"thread_id"` + ConversationType pgtype.Text `json:"conversation_type"` + ReplyTarget pgtype.Text `json:"reply_target"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } func (q *Queries) FindChatRoute(ctx context.Context, arg FindChatRouteParams) (FindChatRouteRow, error) { @@ -159,6 +167,7 @@ func (q *Queries) FindChatRoute(ctx context.Context, arg FindChatRouteParams) (F &i.ChannelConfigID, &i.ConversationID, &i.ThreadID, + &i.ConversationType, &i.ReplyTarget, &i.Metadata, &i.CreatedAt, @@ -176,6 +185,7 @@ SELECT channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, @@ -185,17 +195,18 @@ WHERE id = $1 ` type GetChatRouteByIDRow struct { - ID pgtype.UUID `json:"id"` - ChatID pgtype.UUID `json:"chat_id"` - BotID pgtype.UUID `json:"bot_id"` - Platform string `json:"platform"` - ChannelConfigID pgtype.UUID `json:"channel_config_id"` - ConversationID string `json:"conversation_id"` - ThreadID pgtype.Text `json:"thread_id"` - ReplyTarget pgtype.Text `json:"reply_target"` - Metadata []byte `json:"metadata"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` + ID pgtype.UUID `json:"id"` + ChatID pgtype.UUID `json:"chat_id"` + BotID pgtype.UUID `json:"bot_id"` + Platform string `json:"platform"` + ChannelConfigID pgtype.UUID `json:"channel_config_id"` + ConversationID string `json:"conversation_id"` + ThreadID pgtype.Text `json:"thread_id"` + ConversationType pgtype.Text `json:"conversation_type"` + ReplyTarget pgtype.Text `json:"reply_target"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } func (q *Queries) GetChatRouteByID(ctx context.Context, id pgtype.UUID) (GetChatRouteByIDRow, error) { @@ -209,6 +220,7 @@ func (q *Queries) GetChatRouteByID(ctx context.Context, id pgtype.UUID) (GetChat &i.ChannelConfigID, &i.ConversationID, &i.ThreadID, + &i.ConversationType, &i.ReplyTarget, &i.Metadata, &i.CreatedAt, @@ -226,6 +238,7 @@ SELECT channel_config_id, external_conversation_id AS conversation_id, external_thread_id AS thread_id, + conversation_type, default_reply_target AS reply_target, metadata, created_at, @@ -236,17 +249,18 @@ ORDER BY created_at ASC ` type ListChatRoutesRow struct { - ID pgtype.UUID `json:"id"` - ChatID pgtype.UUID `json:"chat_id"` - BotID pgtype.UUID `json:"bot_id"` - Platform string `json:"platform"` - ChannelConfigID pgtype.UUID `json:"channel_config_id"` - ConversationID string `json:"conversation_id"` - ThreadID pgtype.Text `json:"thread_id"` - ReplyTarget pgtype.Text `json:"reply_target"` - Metadata []byte `json:"metadata"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` + ID pgtype.UUID `json:"id"` + ChatID pgtype.UUID `json:"chat_id"` + BotID pgtype.UUID `json:"bot_id"` + Platform string `json:"platform"` + ChannelConfigID pgtype.UUID `json:"channel_config_id"` + ConversationID string `json:"conversation_id"` + ThreadID pgtype.Text `json:"thread_id"` + ConversationType pgtype.Text `json:"conversation_type"` + ReplyTarget pgtype.Text `json:"reply_target"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } func (q *Queries) ListChatRoutes(ctx context.Context, chatID pgtype.UUID) ([]ListChatRoutesRow, error) { @@ -266,6 +280,7 @@ func (q *Queries) ListChatRoutes(ctx context.Context, chatID pgtype.UUID) ([]Lis &i.ChannelConfigID, &i.ConversationID, &i.ThreadID, + &i.ConversationType, &i.ReplyTarget, &i.Metadata, &i.CreatedAt, diff --git a/internal/db/sqlc/models.go b/internal/db/sqlc/models.go index 2a59c020..6eea8817 100644 --- a/internal/db/sqlc/models.go +++ b/internal/db/sqlc/models.go @@ -49,6 +49,7 @@ type BotChannelRoute struct { ChannelConfigID pgtype.UUID `json:"channel_config_id"` ExternalConversationID string `json:"external_conversation_id"` ExternalThreadID pgtype.Text `json:"external_thread_id"` + ConversationType pgtype.Text `json:"conversation_type"` DefaultReplyTarget pgtype.Text `json:"default_reply_target"` Metadata []byte `json:"metadata"` CreatedAt pgtype.Timestamptz `json:"created_at"` diff --git a/internal/handlers/message.go b/internal/handlers/message.go index e959dbc9..d25b2e0a 100644 --- a/internal/handlers/message.go +++ b/internal/handlers/message.go @@ -98,6 +98,9 @@ func (h *MessageHandler) SendMessage(c echo.Context) error { if strings.TrimSpace(req.CurrentChannel) == "" { req.CurrentChannel = "web" } + if strings.TrimSpace(req.ConversationType) == "" { + req.ConversationType = "direct" + } if len(req.Channels) == 0 { req.Channels = []string{req.CurrentChannel} } @@ -145,6 +148,9 @@ func (h *MessageHandler) StreamMessage(c echo.Context) error { if strings.TrimSpace(req.CurrentChannel) == "" { req.CurrentChannel = "web" } + if strings.TrimSpace(req.ConversationType) == "" { + req.ConversationType = "direct" + } if len(req.Channels) == 0 { req.Channels = []string{req.CurrentChannel} } @@ -198,12 +204,12 @@ func (h *MessageHandler) StreamMessage(c echo.Context) error { h.logger.Error("conversation stream failed", slog.Any("error", err)) if processingState == "started" { processingState = "failed" - if writeErr := writeSSEJSON(writer, flusher, map[string]string{ - "type": "processing_failed", - "error": err.Error(), - }); writeErr != nil { - h.logger.Warn("write SSE processing_failed event failed", slog.Any("error", writeErr)) - } + if writeErr := writeSSEJSON(writer, flusher, map[string]string{ + "type": "processing_failed", + "error": err.Error(), + }); writeErr != nil { + h.logger.Warn("write SSE processing_failed event failed", slog.Any("error", writeErr)) + } } errData := map[string]string{ "type": "error",