diff --git a/backend/pkg/database/msgchains.sql.go b/backend/pkg/database/msgchains.sql.go index 678188b2f..a036d1dde 100644 --- a/backend/pkg/database/msgchains.sql.go +++ b/backend/pkg/database/msgchains.sql.go @@ -203,6 +203,55 @@ func (q *Queries) GetFlowMsgChains(ctx context.Context, flowID int64) ([]Msgchai return items, nil } +const getFlowTaskSubtaskTypeLastMsgChain = `-- name: GetFlowTaskSubtaskTypeLastMsgChain :one +SELECT + mc.id, mc.type, mc.model, mc.model_provider, mc.usage_in, mc.usage_out, mc.chain, mc.flow_id, mc.task_id, mc.subtask_id, mc.created_at, mc.updated_at, mc.usage_cache_in, mc.usage_cache_out, mc.usage_cost_in, mc.usage_cost_out, mc.duration_seconds +FROM msgchains mc +WHERE mc.flow_id = $1 + AND mc.task_id IS NOT DISTINCT FROM $2 + AND mc.subtask_id IS NOT DISTINCT FROM $3 + AND mc.type = $4 +ORDER BY mc.created_at DESC +LIMIT 1 +` + +type GetFlowTaskSubtaskTypeLastMsgChainParams struct { + FlowID int64 `json:"flow_id"` + TaskID sql.NullInt64 `json:"task_id"` + SubtaskID sql.NullInt64 `json:"subtask_id"` + Type MsgchainType `json:"type"` +} + +func (q *Queries) GetFlowTaskSubtaskTypeLastMsgChain(ctx context.Context, arg GetFlowTaskSubtaskTypeLastMsgChainParams) (Msgchain, error) { + row := q.db.QueryRowContext(ctx, getFlowTaskSubtaskTypeLastMsgChain, + arg.FlowID, + arg.TaskID, + arg.SubtaskID, + arg.Type, + ) + var i Msgchain + err := row.Scan( + &i.ID, + &i.Type, + &i.Model, + &i.ModelProvider, + &i.UsageIn, + &i.UsageOut, + &i.Chain, + &i.FlowID, + &i.TaskID, + &i.SubtaskID, + &i.CreatedAt, + &i.UpdatedAt, + &i.UsageCacheIn, + &i.UsageCacheOut, + &i.UsageCostIn, + &i.UsageCostOut, + &i.DurationSeconds, + ) + return i, err +} + const getFlowTaskTypeLastMsgChain = `-- name: GetFlowTaskTypeLastMsgChain :one SELECT mc.id, mc.type, mc.model, mc.model_provider, mc.usage_in, mc.usage_out, mc.chain, mc.flow_id, mc.task_id, mc.subtask_id, mc.created_at, mc.updated_at, mc.usage_cache_in, mc.usage_cache_out, mc.usage_cost_in, mc.usage_cost_out, mc.duration_seconds diff --git a/backend/pkg/database/querier.go b/backend/pkg/database/querier.go index 7cb656e1e..d1e7bada2 100644 --- a/backend/pkg/database/querier.go +++ b/backend/pkg/database/querier.go @@ -101,6 +101,7 @@ type Querier interface { GetFlowSubtask(ctx context.Context, arg GetFlowSubtaskParams) (Subtask, error) GetFlowSubtasks(ctx context.Context, flowID int64) ([]Subtask, error) GetFlowTask(ctx context.Context, arg GetFlowTaskParams) (Task, error) + GetFlowTaskSubtaskTypeLastMsgChain(ctx context.Context, arg GetFlowTaskSubtaskTypeLastMsgChainParams) (Msgchain, error) GetFlowTaskSubtasks(ctx context.Context, arg GetFlowTaskSubtasksParams) ([]Subtask, error) GetFlowTaskTypeLastMsgChain(ctx context.Context, arg GetFlowTaskTypeLastMsgChainParams) (Msgchain, error) GetFlowTasks(ctx context.Context, flowID int64) ([]Task, error) diff --git a/backend/pkg/providers/helpers.go b/backend/pkg/providers/helpers.go index 7b3bb02df..f22e4c5aa 100644 --- a/backend/pkg/providers/helpers.go +++ b/backend/pkg/providers/helpers.go @@ -444,6 +444,19 @@ func (fp *flowProvider) getTaskMsgLogsSummary( return summary, nil } +func scopedMsgChainLookupParams( + flowID int64, + taskID, subtaskID *int64, + msgChainType database.MsgchainType, +) database.GetFlowTaskSubtaskTypeLastMsgChainParams { + return database.GetFlowTaskSubtaskTypeLastMsgChainParams{ + FlowID: flowID, + TaskID: database.Int64ToNullInt64(taskID), + SubtaskID: database.Int64ToNullInt64(subtaskID), + Type: msgChainType, + } +} + func (fp *flowProvider) restoreChain( ctx context.Context, taskID, subtaskID *int64, @@ -454,11 +467,9 @@ func (fp *flowProvider) restoreChain( ctx, observation := obs.Observer.NewObservation(ctx) // Get raw chain from DB for observation input - msgChain, err := fp.db.GetFlowTaskTypeLastMsgChain(ctx, database.GetFlowTaskTypeLastMsgChainParams{ - FlowID: fp.flowID, - TaskID: database.Int64ToNullInt64(taskID), - Type: msgChainType, - }) + msgChain, err := fp.db.GetFlowTaskSubtaskTypeLastMsgChain(ctx, scopedMsgChainLookupParams( + fp.flowID, taskID, subtaskID, msgChainType, + )) var rawChain []llms.MessageContent if err == nil && !isEmptyChain(msgChain.Chain) { diff --git a/backend/pkg/providers/helpers_test.go b/backend/pkg/providers/helpers_test.go index 2b4834871..f710be978 100644 --- a/backend/pkg/providers/helpers_test.go +++ b/backend/pkg/providers/helpers_test.go @@ -12,6 +12,7 @@ import ( "pentagi/pkg/cast" "pentagi/pkg/config" + "pentagi/pkg/database" "github.com/stretchr/testify/assert" "github.com/vxcontrol/langchaingo/llms" @@ -1096,6 +1097,33 @@ func TestExecutionMonitorDetector_ShouldInvokeAdviser(t *testing.T) { } } +func TestScopedMsgChainLookupParams(t *testing.T) { + taskID := int64(12) + subtaskID := int64(34) + + tests := []struct { + name string + taskID *int64 + subtaskID *int64 + wantTask *int64 + wantSub *int64 + }{ + {name: "assistant scope keeps null identifiers"}, + {name: "task scope keeps a null subtask", taskID: &taskID, wantTask: &taskID}, + {name: "subtask scope includes both identifiers", taskID: &taskID, subtaskID: &subtaskID, wantTask: &taskID, wantSub: &subtaskID}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + params := scopedMsgChainLookupParams(7, tt.taskID, tt.subtaskID, database.MsgchainTypeCoder) + assert.Equal(t, int64(7), params.FlowID) + assert.Equal(t, database.Int64ToNullInt64(tt.wantTask), params.TaskID) + assert.Equal(t, database.Int64ToNullInt64(tt.wantSub), params.SubtaskID) + assert.Equal(t, database.MsgchainTypeCoder, params.Type) + }) + } +} + func TestExecutionMonitorDetector_Reset(t *testing.T) { emd := &executionMonitor{ enabled: true, diff --git a/backend/sqlc/models/msgchains.sql b/backend/sqlc/models/msgchains.sql index 7639875e0..8d54a1301 100644 --- a/backend/sqlc/models/msgchains.sql +++ b/backend/sqlc/models/msgchains.sql @@ -77,6 +77,17 @@ WHERE mc.flow_id = $1 AND (mc.task_id = $2 OR $2 IS NULL) AND mc.type = $3 ORDER BY mc.created_at DESC LIMIT 1; +-- name: GetFlowTaskSubtaskTypeLastMsgChain :one +SELECT + mc.* +FROM msgchains mc +WHERE mc.flow_id = $1 + AND mc.task_id IS NOT DISTINCT FROM $2 + AND mc.subtask_id IS NOT DISTINCT FROM $3 + AND mc.type = $4 +ORDER BY mc.created_at DESC +LIMIT 1; + -- name: GetMsgChain :one SELECT mc.*