feat: add captain sessions model [CW-7485] (#14970)

This adds a `captain_sessions` table to log every Captain run, starting
with Assistant Responses and Copilot Responses. Each session records the
assistant, model, credits consumed, the FAQs/documents/scenario that
contributed to the response, and the full run context — giving customers
visibility into how a response was generated and giving us durable stats
on credit, FAQ, and document usage (which today only exist as ephemeral
trace metadata and an aggregate account counter).

## What changed

- New `Captain::Session` model with a `session_type` enum (`assistant`,
`copilot`). The subject (`Conversation` / `CopilotThread`) and result
(`Message` / `CopilotMessage`) classes are inferred from the session
type, so the table stores plain `subject_id` / `result_id` ids.
`result_id` is nullable so failed runs that still consumed credits can
be logged.
- Composite indexes on `[session_type, subject_id]`, `[session_type,
result_id]`, and `[account_id, session_type, created_at]` for lookup and
usage-stats queries.
- Factory and model specs.

This PR is schema + model only; the writer/instrumentation that records
sessions from the assistant and copilot flows will follow.

---------

Co-authored-by: Sony Mathew <sony@chatwoot.com>
This commit is contained in:
Shivam Mishra
2026-07-13 18:18:20 +05:30
committed by GitHub
parent 056b5eb89d
commit df7f137657
7 changed files with 311 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
class CreateAgentSessions < ActiveRecord::Migration[7.1]
def change
create_table :agent_sessions do |t|
t.integer :session_type, null: false
t.references :subject, polymorphic: true, null: false, index: false
t.references :result, polymorphic: true, index: false
t.references :account, null: false, index: true
t.references :assistant, null: false, index: true
t.references :user, index: true
t.string :llm_model
t.float :credits_consumed
t.jsonb :faq_ids, default: []
t.jsonb :document_ids, default: []
t.jsonb :scenario_ids, default: []
t.jsonb :run_context, default: {}
t.timestamps
end
add_index :agent_sessions, [:account_id, :session_type, :created_at]
add_index :agent_sessions, [:account_id, :subject_type, :subject_id]
add_index :agent_sessions, [:account_id, :result_type, :result_id]
end
end

View File

@@ -146,6 +146,31 @@ ActiveRecord::Schema[7.1].define(version: 2026_07_10_000000) do
t.index ["account_id"], name: "index_agent_capacity_policies_on_account_id"
end
create_table "agent_sessions", force: :cascade do |t|
t.integer "session_type", null: false
t.string "subject_type", null: false
t.bigint "subject_id", null: false
t.string "result_type"
t.bigint "result_id"
t.bigint "account_id", null: false
t.bigint "assistant_id", null: false
t.bigint "user_id"
t.string "llm_model"
t.float "credits_consumed"
t.jsonb "faq_ids", default: []
t.jsonb "document_ids", default: []
t.jsonb "scenario_ids", default: []
t.jsonb "run_context", default: {}
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "result_type", "result_id"], name: "idx_on_account_id_result_type_result_id_ca66c00cd7"
t.index ["account_id", "session_type", "created_at"], name: "idx_on_account_id_session_type_created_at_c20a14bd4e"
t.index ["account_id", "subject_type", "subject_id"], name: "idx_on_account_id_subject_type_subject_id_6d60963b3d"
t.index ["account_id"], name: "index_agent_sessions_on_account_id"
t.index ["assistant_id"], name: "index_agent_sessions_on_assistant_id"
t.index ["user_id"], name: "index_agent_sessions_on_user_id"
end
create_table "applied_slas", force: :cascade do |t|
t.bigint "account_id", null: false
t.bigint "sla_policy_id", null: false