diff --git a/cmd/agent/agent b/cmd/agent/agent new file mode 100755 index 00000000..7474f862 Binary files /dev/null and b/cmd/agent/agent differ diff --git a/db/migrations/0001_init.up.sql b/db/migrations/0001_init.up.sql index 7ccfb786..77f83e16 100644 --- a/db/migrations/0001_init.up.sql +++ b/db/migrations/0001_init.up.sql @@ -117,6 +117,7 @@ CREATE TABLE IF NOT EXISTS bots ( is_active BOOLEAN NOT NULL DEFAULT true, status TEXT NOT NULL DEFAULT 'ready', max_context_load_time INTEGER NOT NULL DEFAULT 1440, + max_context_tokens INTEGER NOT NULL DEFAULT 0, language TEXT NOT NULL DEFAULT 'auto', allow_guest BOOLEAN NOT NULL DEFAULT false, chat_model_id UUID REFERENCES models(id) ON DELETE SET NULL, @@ -242,6 +243,7 @@ CREATE TABLE IF NOT EXISTS bot_history_messages ( role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system', 'tool')), content JSONB NOT NULL, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + usage JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); diff --git a/db/migrations/0008_max_context_tokens.down.sql b/db/migrations/0008_max_context_tokens.down.sql new file mode 100644 index 00000000..dbb31877 --- /dev/null +++ b/db/migrations/0008_max_context_tokens.down.sql @@ -0,0 +1,4 @@ +-- 0008_max_context_tokens +-- Remove max_context_tokens column from bots table + +ALTER TABLE bots DROP COLUMN IF EXISTS max_context_tokens; diff --git a/db/migrations/0008_max_context_tokens.up.sql b/db/migrations/0008_max_context_tokens.up.sql new file mode 100644 index 00000000..753229eb --- /dev/null +++ b/db/migrations/0008_max_context_tokens.up.sql @@ -0,0 +1,4 @@ +-- 0008_max_context_tokens +-- Add max_context_tokens column to bots table for token-based context trimming + +ALTER TABLE bots ADD COLUMN IF NOT EXISTS max_context_tokens INTEGER NOT NULL DEFAULT 0; diff --git a/db/migrations/0009_message_usage.down.sql b/db/migrations/0009_message_usage.down.sql new file mode 100644 index 00000000..72281c0e --- /dev/null +++ b/db/migrations/0009_message_usage.down.sql @@ -0,0 +1,4 @@ +-- 0009_message_usage +-- Remove usage JSONB column from bot_history_messages + +ALTER TABLE bot_history_messages DROP COLUMN IF EXISTS usage; diff --git a/db/migrations/0009_message_usage.up.sql b/db/migrations/0009_message_usage.up.sql new file mode 100644 index 00000000..0132b6a5 --- /dev/null +++ b/db/migrations/0009_message_usage.up.sql @@ -0,0 +1,4 @@ +-- 0009_message_usage +-- Add usage JSONB column to bot_history_messages for storing LLM token usage + +ALTER TABLE bot_history_messages ADD COLUMN IF NOT EXISTS usage JSONB; diff --git a/db/queries/bots.sql b/db/queries/bots.sql index 0ee27164..fc1bbdb9 100644 --- a/db/queries/bots.sql +++ b/db/queries/bots.sql @@ -1,21 +1,21 @@ -- name: CreateBot :one INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status) VALUES ($1, $2, $3, $4, $5, $6, $7) -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; -- name: GetBotByID :one -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE id = $1; -- name: ListBotsByOwner :many -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE owner_user_id = $1 ORDER BY created_at DESC; -- name: ListBotsByMember :many -SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at +SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.max_context_tokens, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at FROM bots b JOIN bot_members m ON m.bot_id = b.id WHERE m.user_id = $1 @@ -29,14 +29,14 @@ SET display_name = $2, metadata = $5, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; -- name: UpdateBotOwner :one UPDATE bots SET owner_user_id = $2, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at; -- name: UpdateBotStatus :exec UPDATE bots diff --git a/db/queries/messages.sql b/db/queries/messages.sql index 51ed9906..84c70fec 100644 --- a/db/queries/messages.sql +++ b/db/queries/messages.sql @@ -9,7 +9,8 @@ INSERT INTO bot_history_messages ( source_reply_to_message_id, role, content, - metadata + metadata, + usage ) VALUES ( sqlc.arg(bot_id), @@ -21,7 +22,8 @@ VALUES ( sqlc.narg(source_reply_to_message_id)::text, sqlc.arg(role), sqlc.arg(content), - sqlc.arg(metadata) + sqlc.arg(metadata), + sqlc.arg(usage) ) RETURNING id, @@ -35,6 +37,7 @@ RETURNING role, content, metadata, + usage, created_at; -- name: ListMessages :many @@ -50,6 +53,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url @@ -72,6 +76,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url @@ -94,6 +99,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url @@ -117,6 +123,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url diff --git a/db/queries/settings.sql b/db/queries/settings.sql index 31b386e7..821c48eb 100644 --- a/db/queries/settings.sql +++ b/db/queries/settings.sql @@ -2,6 +2,7 @@ SELECT bots.id AS bot_id, bots.max_context_load_time, + bots.max_context_tokens, bots.language, bots.allow_guest, chat_models.model_id AS chat_model_id, @@ -19,6 +20,7 @@ WHERE bots.id = $1; WITH updated AS ( UPDATE bots SET max_context_load_time = sqlc.arg(max_context_load_time), + max_context_tokens = sqlc.arg(max_context_tokens), language = sqlc.arg(language), allow_guest = sqlc.arg(allow_guest), chat_model_id = COALESCE(sqlc.narg(chat_model_id)::uuid, bots.chat_model_id), @@ -27,11 +29,12 @@ WITH updated AS ( search_provider_id = COALESCE(sqlc.narg(search_provider_id)::uuid, bots.search_provider_id), updated_at = now() WHERE bots.id = sqlc.arg(id) - RETURNING bots.id, bots.max_context_load_time, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id + RETURNING bots.id, bots.max_context_load_time, bots.max_context_tokens, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id ) SELECT updated.id AS bot_id, updated.max_context_load_time, + updated.max_context_tokens, updated.language, updated.allow_guest, chat_models.model_id AS chat_model_id, @@ -47,6 +50,7 @@ LEFT JOIN search_providers ON search_providers.id = updated.search_provider_id; -- name: DeleteSettingsByBotID :exec UPDATE bots SET max_context_load_time = 1440, + max_context_tokens = 0, language = 'auto', allow_guest = false, chat_model_id = NULL, diff --git a/internal/conversation/flow/resolver.go b/internal/conversation/flow/resolver.go index a39606dc..82f7967c 100644 --- a/internal/conversation/flow/resolver.go +++ b/internal/conversation/flow/resolver.go @@ -150,6 +150,12 @@ type gatewayRequest struct { type gatewayResponse struct { Messages []conversation.ModelMessage `json:"messages"` Skills []string `json:"skills"` + Usage json.RawMessage `json:"usage,omitempty"` +} + +type gatewayUsage struct { + InputTokens *int `json:"inputTokens"` + OutputTokens *int `json:"outputTokens"` } // gatewaySchedule matches the agent gateway ScheduleModel for /chat/trigger-schedule. @@ -212,13 +218,15 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r return resolvedContext{}, err } maxCtx := coalescePositiveInt(req.MaxContextLoadTime, botSettings.MaxContextLoadTime, defaultMaxContextMinutes) + maxTokens := botSettings.MaxContextTokens var messages []conversation.ModelMessage if !skipHistory && r.conversationSvc != nil { - messages, err = r.loadMessages(ctx, req.ChatID, maxCtx) - if err != nil { - return resolvedContext{}, err + loaded, loadErr := r.loadMessages(ctx, req.ChatID, maxCtx) + if loadErr != nil { + return resolvedContext{}, loadErr } + messages = trimMessagesByTokens(loaded, maxTokens) } if memoryMsg := r.loadMemoryContextMessage(ctx, req); memoryMsg != nil { messages = append(messages, *memoryMsg) @@ -291,7 +299,7 @@ func (r *Resolver) Chat(ctx context.Context, req conversation.ChatRequest) (conv if err != nil { return conversation.ChatResponse{}, err } - if err := r.storeRound(ctx, req, resp.Messages); err != nil { + if err := r.storeRound(ctx, req, resp.Messages, resp.Usage); err != nil { return conversation.ChatResponse{}, err } return conversation.ChatResponse{ @@ -345,7 +353,7 @@ func (r *Resolver) TriggerSchedule(ctx context.Context, botID string, payload sc if err != nil { return err } - return r.storeRound(ctx, req, resp.Messages) + return r.storeRound(ctx, req, resp.Messages, resp.Usage) } // --- StreamChat --- @@ -552,7 +560,7 @@ func (r *Resolver) tryStoreStream(ctx context.Context, req conversation.ChatRequ if eventType == "done" { var resp gatewayResponse if err := json.Unmarshal([]byte(data), &resp); err == nil && len(resp.Messages) > 0 { - return true, r.storeRound(ctx, req, resp.Messages) + return true, r.storeRound(ctx, req, resp.Messages, resp.Usage) } } @@ -562,15 +570,16 @@ func (r *Resolver) tryStoreStream(ctx context.Context, req conversation.ChatRequ Data json.RawMessage `json:"data"` Messages []conversation.ModelMessage `json:"messages"` Skills []string `json:"skills"` + Usage json.RawMessage `json:"usage,omitempty"` } if err := json.Unmarshal([]byte(data), &envelope); err == nil { if (envelope.Type == "agent_end" || envelope.Type == "done") && len(envelope.Messages) > 0 { - return true, r.storeRound(ctx, req, envelope.Messages) + return true, r.storeRound(ctx, req, envelope.Messages, envelope.Usage) } if envelope.Type == "done" && len(envelope.Data) > 0 { var resp gatewayResponse if err := json.Unmarshal(envelope.Data, &resp); err == nil && len(resp.Messages) > 0 { - return true, r.storeRound(ctx, req, resp.Messages) + return true, r.storeRound(ctx, req, resp.Messages, resp.Usage) } } } @@ -578,7 +587,7 @@ func (r *Resolver) tryStoreStream(ctx context.Context, req conversation.ChatRequ // fallback: data: {messages: [...]} var resp gatewayResponse if err := json.Unmarshal([]byte(data), &resp); err == nil && len(resp.Messages) > 0 { - return true, r.storeRound(ctx, req, resp.Messages) + return true, r.storeRound(ctx, req, resp.Messages, resp.Usage) } return false, nil } @@ -648,7 +657,12 @@ func (r *Resolver) resolveContainerID(ctx context.Context, botID, explicit strin // --- message loading --- -func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMinutes int) ([]conversation.ModelMessage, error) { +type messageWithUsage struct { + Message conversation.ModelMessage + UsageInputTokens *int +} + +func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMinutes int) ([]messageWithUsage, error) { if r.messageService == nil { return nil, nil } @@ -657,7 +671,7 @@ func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMi if err != nil { return nil, err } - var result []conversation.ModelMessage + var result []messageWithUsage for _, m := range msgs { var mm conversation.ModelMessage if err := json.Unmarshal(m.Content, &mm); err != nil { @@ -667,11 +681,84 @@ func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMi } else { mm.Role = m.Role } - result = append(result, mm) + var inputTokens *int + if len(m.Usage) > 0 { + var u gatewayUsage + if json.Unmarshal(m.Usage, &u) == nil { + inputTokens = u.InputTokens + } + } + result = append(result, messageWithUsage{Message: mm, UsageInputTokens: inputTokens}) } return result, nil } +func estimateMessageTokens(msg conversation.ModelMessage) int { + text := msg.TextContent() + if len(text) == 0 { + data, _ := json.Marshal(msg.Content) + return len(data) / 4 + } + return len(text) / 4 +} + +func trimMessagesByTokens(messages []messageWithUsage, maxTokens int) []conversation.ModelMessage { + if maxTokens <= 0 || len(messages) == 0 { + result := make([]conversation.ModelMessage, len(messages)) + for i, m := range messages { + result[i] = m.Message + } + return result + } + + // Scan backwards. When a message with UsageInputTokens is found, that value + // represents the cumulative input tokens for all messages up to and including + // that message. Messages after it are estimated with chars/4. + totalTokens := 0 + anchorFound := false + cutoff := 0 + + tailEstimate := 0 + for i := len(messages) - 1; i >= 0; i-- { + if !anchorFound && messages[i].UsageInputTokens != nil { + anchorFound = true + totalTokens = *messages[i].UsageInputTokens + tailEstimate + if totalTokens > maxTokens { + cutoff = i + 1 + break + } + continue + } + est := estimateMessageTokens(messages[i].Message) + if anchorFound { + totalTokens += est + if totalTokens > maxTokens { + cutoff = i + 1 + break + } + } else { + tailEstimate += est + } + } + + if !anchorFound { + totalTokens = 0 + for i := len(messages) - 1; i >= 0; i-- { + totalTokens += estimateMessageTokens(messages[i].Message) + if totalTokens > maxTokens { + cutoff = i + 1 + break + } + } + } + + result := make([]conversation.ModelMessage, 0, len(messages)-cutoff) + for _, m := range messages[cutoff:] { + result = append(result, m.Message) + } + return result +} + type memoryContextItem struct { Namespace string Item memory.MemoryItem @@ -792,9 +879,7 @@ func (r *Resolver) persistUserMessage(ctx context.Context, req conversation.Chat return err } -func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage) error { - // Add user query as the first message if not already present in the round. - // This ensures the user's prompt is persisted alongside the assistant's response. +func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage, usage json.RawMessage) error { fullRound := make([]conversation.ModelMessage, 0, len(messages)+1) hasUserQuery := false for _, m := range messages { @@ -813,7 +898,6 @@ func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, } for _, m := range messages { if req.UserMessagePersisted && m.Role == "user" && strings.TrimSpace(m.TextContent()) == strings.TrimSpace(req.Query) { - // User message was already persisted before streaming; skip duplicate copy in round payload. continue } fullRound = append(fullRound, m) @@ -822,14 +906,12 @@ func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, return nil } - r.storeMessages(ctx, req, fullRound) - // Run memory extraction in the background so that the SSE stream can - // finish immediately after messages are persisted. + r.storeMessages(ctx, req, fullRound, usage) go r.storeMemory(context.WithoutCancel(ctx), req.BotID, fullRound) return nil } -func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage) { +func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage, usage json.RawMessage) { if r.messageService == nil { return } @@ -838,7 +920,7 @@ func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatReque } meta := buildRouteMetadata(req) senderChannelIdentityID, senderUserID := r.resolvePersistSenderIDs(ctx, req) - for _, msg := range messages { + for i, msg := range messages { content, err := json.Marshal(msg) if err != nil { r.logger.Warn("storeMessages: marshal failed", slog.Any("error", err)) @@ -857,9 +939,12 @@ func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatReque assets = chatAttachmentsToAssetRefs(req.Attachments) } } else if strings.TrimSpace(req.ExternalMessageID) != "" { - // Assistant/tool/system outputs are linked to the inbound source message for cross-channel reply threading. sourceReplyToMessageID = req.ExternalMessageID } + var msgUsage json.RawMessage + if i == len(messages)-1 && len(usage) > 0 { + msgUsage = usage + } if _, err := r.messageService.Persist(ctx, messagepkg.PersistInput{ BotID: req.BotID, RouteID: req.RouteID, @@ -871,6 +956,7 @@ func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatReque Role: msg.Role, Content: content, Metadata: meta, + Usage: msgUsage, Assets: assets, }); err != nil { r.logger.Warn("persist message failed", slog.Any("error", err)) diff --git a/internal/db/sqlc/bots.sql.go b/internal/db/sqlc/bots.sql.go index a5243268..2e958ac8 100644 --- a/internal/db/sqlc/bots.sql.go +++ b/internal/db/sqlc/bots.sql.go @@ -14,7 +14,7 @@ import ( const createBot = `-- name: CreateBot :one INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status) VALUES ($1, $2, $3, $4, $5, $6, $7) -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ` type CreateBotParams struct { @@ -47,6 +47,7 @@ func (q *Queries) CreateBot(ctx context.Context, arg CreateBotParams) (Bot, erro &i.IsActive, &i.Status, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -84,7 +85,7 @@ func (q *Queries) DeleteBotMember(ctx context.Context, arg DeleteBotMemberParams } const getBotByID = `-- name: GetBotByID :one -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE id = $1 ` @@ -101,6 +102,7 @@ func (q *Queries) GetBotByID(ctx context.Context, id pgtype.UUID) (Bot, error) { &i.IsActive, &i.Status, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -171,7 +173,7 @@ func (q *Queries) ListBotMembers(ctx context.Context, botID pgtype.UUID) ([]BotM } const listBotsByMember = `-- name: ListBotsByMember :many -SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at +SELECT b.id, b.owner_user_id, b.type, b.display_name, b.avatar_url, b.is_active, b.status, b.max_context_load_time, b.max_context_tokens, b.language, b.allow_guest, b.chat_model_id, b.memory_model_id, b.embedding_model_id, b.search_provider_id, b.metadata, b.created_at, b.updated_at FROM bots b JOIN bot_members m ON m.bot_id = b.id WHERE m.user_id = $1 @@ -196,6 +198,7 @@ func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]B &i.IsActive, &i.Status, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -217,7 +220,7 @@ func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]B } const listBotsByOwner = `-- name: ListBotsByOwner :many -SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +SELECT id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at FROM bots WHERE owner_user_id = $1 ORDER BY created_at DESC @@ -241,6 +244,7 @@ func (q *Queries) ListBotsByOwner(ctx context.Context, ownerUserID pgtype.UUID) &i.IsActive, &i.Status, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -266,7 +270,7 @@ UPDATE bots SET owner_user_id = $2, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ` type UpdateBotOwnerParams struct { @@ -286,6 +290,7 @@ func (q *Queries) UpdateBotOwner(ctx context.Context, arg UpdateBotOwnerParams) &i.IsActive, &i.Status, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -307,7 +312,7 @@ SET display_name = $2, metadata = $5, updated_at = now() WHERE id = $1 -RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at +RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ` type UpdateBotProfileParams struct { @@ -336,6 +341,7 @@ func (q *Queries) UpdateBotProfile(ctx context.Context, arg UpdateBotProfilePara &i.IsActive, &i.Status, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, diff --git a/internal/db/sqlc/conversations.sql.go b/internal/db/sqlc/conversations.sql.go index c5c15ca4..cbc6eaf1 100644 --- a/internal/db/sqlc/conversations.sql.go +++ b/internal/db/sqlc/conversations.sql.go @@ -590,7 +590,7 @@ WITH updated AS ( SET display_name = $1, updated_at = now() WHERE bots.id = $2 - RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at + RETURNING id, owner_user_id, type, display_name, avatar_url, is_active, status, max_context_load_time, max_context_tokens, language, allow_guest, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at ) SELECT updated.id AS id, diff --git a/internal/db/sqlc/messages.sql.go b/internal/db/sqlc/messages.sql.go index a9cde28e..b1299353 100644 --- a/internal/db/sqlc/messages.sql.go +++ b/internal/db/sqlc/messages.sql.go @@ -22,7 +22,8 @@ INSERT INTO bot_history_messages ( source_reply_to_message_id, role, content, - metadata + metadata, + usage ) VALUES ( $1, @@ -34,7 +35,8 @@ VALUES ( $7::text, $8, $9, - $10 + $10, + $11 ) RETURNING id, @@ -48,6 +50,7 @@ RETURNING role, content, metadata, + usage, created_at ` @@ -62,6 +65,7 @@ type CreateMessageParams struct { Role string `json:"role"` Content []byte `json:"content"` Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` } type CreateMessageRow struct { @@ -76,6 +80,7 @@ type CreateMessageRow struct { Role string `json:"role"` Content []byte `json:"content"` Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` CreatedAt pgtype.Timestamptz `json:"created_at"` } @@ -91,6 +96,7 @@ func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) (C arg.Role, arg.Content, arg.Metadata, + arg.Usage, ) var i CreateMessageRow err := row.Scan( @@ -105,6 +111,7 @@ func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) (C &i.Role, &i.Content, &i.Metadata, + &i.Usage, &i.CreatedAt, ) return i, err @@ -133,6 +140,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url @@ -155,6 +163,7 @@ type ListMessagesRow struct { Role string `json:"role"` Content []byte `json:"content"` Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` CreatedAt pgtype.Timestamptz `json:"created_at"` SenderDisplayName pgtype.Text `json:"sender_display_name"` SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"` @@ -181,6 +190,7 @@ func (q *Queries) ListMessages(ctx context.Context, botID pgtype.UUID) ([]ListMe &i.Role, &i.Content, &i.Metadata, + &i.Usage, &i.CreatedAt, &i.SenderDisplayName, &i.SenderAvatarUrl, @@ -208,6 +218,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url @@ -237,6 +248,7 @@ type ListMessagesBeforeRow struct { Role string `json:"role"` Content []byte `json:"content"` Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` CreatedAt pgtype.Timestamptz `json:"created_at"` SenderDisplayName pgtype.Text `json:"sender_display_name"` SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"` @@ -263,6 +275,7 @@ func (q *Queries) ListMessagesBefore(ctx context.Context, arg ListMessagesBefore &i.Role, &i.Content, &i.Metadata, + &i.Usage, &i.CreatedAt, &i.SenderDisplayName, &i.SenderAvatarUrl, @@ -290,6 +303,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url @@ -317,6 +331,7 @@ type ListMessagesLatestRow struct { Role string `json:"role"` Content []byte `json:"content"` Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` CreatedAt pgtype.Timestamptz `json:"created_at"` SenderDisplayName pgtype.Text `json:"sender_display_name"` SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"` @@ -343,6 +358,7 @@ func (q *Queries) ListMessagesLatest(ctx context.Context, arg ListMessagesLatest &i.Role, &i.Content, &i.Metadata, + &i.Usage, &i.CreatedAt, &i.SenderDisplayName, &i.SenderAvatarUrl, @@ -370,6 +386,7 @@ SELECT m.role, m.content, m.metadata, + m.usage, m.created_at, ci.display_name AS sender_display_name, ci.avatar_url AS sender_avatar_url @@ -397,6 +414,7 @@ type ListMessagesSinceRow struct { Role string `json:"role"` Content []byte `json:"content"` Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` CreatedAt pgtype.Timestamptz `json:"created_at"` SenderDisplayName pgtype.Text `json:"sender_display_name"` SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"` @@ -423,6 +441,7 @@ func (q *Queries) ListMessagesSince(ctx context.Context, arg ListMessagesSincePa &i.Role, &i.Content, &i.Metadata, + &i.Usage, &i.CreatedAt, &i.SenderDisplayName, &i.SenderAvatarUrl, diff --git a/internal/db/sqlc/models.go b/internal/db/sqlc/models.go index 141cc9b9..e3b214df 100644 --- a/internal/db/sqlc/models.go +++ b/internal/db/sqlc/models.go @@ -17,6 +17,7 @@ type Bot struct { IsActive bool `json:"is_active"` Status string `json:"status"` MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` ChatModelID pgtype.UUID `json:"chat_model_id"` @@ -69,6 +70,7 @@ type BotHistoryMessage struct { Role string `json:"role"` Content []byte `json:"content"` Metadata []byte `json:"metadata"` + Usage []byte `json:"usage"` CreatedAt pgtype.Timestamptz `json:"created_at"` } diff --git a/internal/db/sqlc/settings.sql.go b/internal/db/sqlc/settings.sql.go index 958b5443..7f3a612e 100644 --- a/internal/db/sqlc/settings.sql.go +++ b/internal/db/sqlc/settings.sql.go @@ -14,6 +14,7 @@ import ( const deleteSettingsByBotID = `-- name: DeleteSettingsByBotID :exec UPDATE bots SET max_context_load_time = 1440, + max_context_tokens = 0, language = 'auto', allow_guest = false, chat_model_id = NULL, @@ -33,6 +34,7 @@ const getSettingsByBotID = `-- name: GetSettingsByBotID :one SELECT bots.id AS bot_id, bots.max_context_load_time, + bots.max_context_tokens, bots.language, bots.allow_guest, chat_models.model_id AS chat_model_id, @@ -50,6 +52,7 @@ WHERE bots.id = $1 type GetSettingsByBotIDRow struct { BotID pgtype.UUID `json:"bot_id"` MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` ChatModelID pgtype.Text `json:"chat_model_id"` @@ -64,6 +67,7 @@ func (q *Queries) GetSettingsByBotID(ctx context.Context, id pgtype.UUID) (GetSe err := row.Scan( &i.BotID, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, @@ -78,19 +82,21 @@ const upsertBotSettings = `-- name: UpsertBotSettings :one WITH updated AS ( UPDATE bots SET max_context_load_time = $1, - language = $2, - allow_guest = $3, - chat_model_id = COALESCE($4::uuid, bots.chat_model_id), - memory_model_id = COALESCE($5::uuid, bots.memory_model_id), - embedding_model_id = COALESCE($6::uuid, bots.embedding_model_id), - search_provider_id = COALESCE($7::uuid, bots.search_provider_id), + max_context_tokens = $2, + language = $3, + allow_guest = $4, + chat_model_id = COALESCE($5::uuid, bots.chat_model_id), + memory_model_id = COALESCE($6::uuid, bots.memory_model_id), + embedding_model_id = COALESCE($7::uuid, bots.embedding_model_id), + search_provider_id = COALESCE($8::uuid, bots.search_provider_id), updated_at = now() - WHERE bots.id = $8 - RETURNING bots.id, bots.max_context_load_time, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id + WHERE bots.id = $9 + RETURNING bots.id, bots.max_context_load_time, bots.max_context_tokens, bots.language, bots.allow_guest, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id ) SELECT updated.id AS bot_id, updated.max_context_load_time, + updated.max_context_tokens, updated.language, updated.allow_guest, chat_models.model_id AS chat_model_id, @@ -106,6 +112,7 @@ LEFT JOIN search_providers ON search_providers.id = updated.search_provider_id type UpsertBotSettingsParams struct { MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` ChatModelID pgtype.UUID `json:"chat_model_id"` @@ -118,6 +125,7 @@ type UpsertBotSettingsParams struct { type UpsertBotSettingsRow struct { BotID pgtype.UUID `json:"bot_id"` MaxContextLoadTime int32 `json:"max_context_load_time"` + MaxContextTokens int32 `json:"max_context_tokens"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` ChatModelID pgtype.Text `json:"chat_model_id"` @@ -129,6 +137,7 @@ type UpsertBotSettingsRow struct { func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsParams) (UpsertBotSettingsRow, error) { row := q.db.QueryRow(ctx, upsertBotSettings, arg.MaxContextLoadTime, + arg.MaxContextTokens, arg.Language, arg.AllowGuest, arg.ChatModelID, @@ -141,6 +150,7 @@ func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsPa err := row.Scan( &i.BotID, &i.MaxContextLoadTime, + &i.MaxContextTokens, &i.Language, &i.AllowGuest, &i.ChatModelID, diff --git a/internal/message/service.go b/internal/message/service.go index 849cf9c8..e3f48b7f 100644 --- a/internal/message/service.go +++ b/internal/message/service.go @@ -79,6 +79,7 @@ func (s *DBService) Persist(ctx context.Context, input PersistInput) (Message, e Role: input.Role, Content: content, Metadata: metaBytes, + Usage: input.Usage, }) if err != nil { return Message{}, err @@ -213,6 +214,7 @@ func toMessageFromCreate(row sqlc.CreateMessageRow) Message { row.Role, row.Content, row.Metadata, + row.Usage, row.CreatedAt, ) } @@ -232,6 +234,7 @@ func toMessageFromListRow(row sqlc.ListMessagesRow) Message { row.Role, row.Content, row.Metadata, + row.Usage, row.CreatedAt, ) } @@ -251,6 +254,7 @@ func toMessageFromSinceRow(row sqlc.ListMessagesSinceRow) Message { row.Role, row.Content, row.Metadata, + row.Usage, row.CreatedAt, ) } @@ -270,6 +274,7 @@ func toMessageFromLatestRow(row sqlc.ListMessagesLatestRow) Message { row.Role, row.Content, row.Metadata, + row.Usage, row.CreatedAt, ) } @@ -288,6 +293,7 @@ func toMessageFields( role string, content []byte, metadata []byte, + usage []byte, createdAt pgtype.Timestamptz, ) Message { return Message{ @@ -304,6 +310,7 @@ func toMessageFields( Role: role, Content: json.RawMessage(content), Metadata: parseJSONMap(metadata), + Usage: json.RawMessage(usage), CreatedAt: createdAt.Time, } } @@ -347,6 +354,7 @@ func toMessageFromBeforeRow(row sqlc.ListMessagesBeforeRow) Message { row.Role, row.Content, row.Metadata, + row.Usage, row.CreatedAt, ) } diff --git a/internal/message/types.go b/internal/message/types.go index 5e0be8ed..bf91c07f 100644 --- a/internal/message/types.go +++ b/internal/message/types.go @@ -36,6 +36,7 @@ type Message struct { Role string `json:"role"` Content json.RawMessage `json:"content"` Metadata map[string]any `json:"metadata,omitempty"` + Usage json.RawMessage `json:"usage,omitempty"` Assets []MessageAsset `json:"assets,omitempty"` CreatedAt time.Time `json:"created_at"` } @@ -59,6 +60,7 @@ type PersistInput struct { Role string Content json.RawMessage Metadata map[string]any + Usage json.RawMessage Assets []AssetRef } diff --git a/internal/settings/service.go b/internal/settings/service.go index 838ac03c..a4464356 100644 --- a/internal/settings/service.go +++ b/internal/settings/service.go @@ -54,10 +54,13 @@ func (s *Service) UpsertBot(ctx context.Context, botID string, req UpsertRequest } isPersonalBot := strings.EqualFold(strings.TrimSpace(botRow.Type), "personal") - current := normalizeBotSetting(botRow.MaxContextLoadTime, botRow.Language, botRow.AllowGuest) + current := normalizeBotSetting(botRow.MaxContextLoadTime, botRow.MaxContextTokens, botRow.Language, botRow.AllowGuest) if req.MaxContextLoadTime != nil && *req.MaxContextLoadTime > 0 { current.MaxContextLoadTime = *req.MaxContextLoadTime } + if req.MaxContextTokens != nil && *req.MaxContextTokens >= 0 { + current.MaxContextTokens = *req.MaxContextTokens + } if strings.TrimSpace(req.Language) != "" { current.Language = strings.TrimSpace(req.Language) } @@ -106,6 +109,7 @@ func (s *Service) UpsertBot(ctx context.Context, botID string, req UpsertRequest updated, err := s.queries.UpsertBotSettings(ctx, sqlc.UpsertBotSettingsParams{ ID: pgID, MaxContextLoadTime: int32(current.MaxContextLoadTime), + MaxContextTokens: int32(current.MaxContextTokens), Language: current.Language, AllowGuest: current.AllowGuest, ChatModelID: chatModelUUID, @@ -130,15 +134,19 @@ func (s *Service) Delete(ctx context.Context, botID string) error { return s.queries.DeleteSettingsByBotID(ctx, pgID) } -func normalizeBotSetting(maxContextLoadTime int32, language string, allowGuest bool) Settings { +func normalizeBotSetting(maxContextLoadTime int32, maxContextTokens int32, language string, allowGuest bool) Settings { settings := Settings{ MaxContextLoadTime: int(maxContextLoadTime), + MaxContextTokens: int(maxContextTokens), Language: strings.TrimSpace(language), AllowGuest: allowGuest, } if settings.MaxContextLoadTime <= 0 { settings.MaxContextLoadTime = DefaultMaxContextLoadTime } + if settings.MaxContextTokens < 0 { + settings.MaxContextTokens = 0 + } if settings.Language == "" { settings.Language = DefaultLanguage } @@ -148,6 +156,7 @@ func normalizeBotSetting(maxContextLoadTime int32, language string, allowGuest b func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings { return normalizeBotSettingsFields( row.MaxContextLoadTime, + row.MaxContextTokens, row.Language, row.AllowGuest, row.ChatModelID, @@ -160,6 +169,7 @@ func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings { func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings { return normalizeBotSettingsFields( row.MaxContextLoadTime, + row.MaxContextTokens, row.Language, row.AllowGuest, row.ChatModelID, @@ -171,6 +181,7 @@ func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings { func normalizeBotSettingsFields( maxContextLoadTime int32, + maxContextTokens int32, language string, allowGuest bool, chatModelID pgtype.Text, @@ -178,7 +189,7 @@ func normalizeBotSettingsFields( embeddingModelID pgtype.Text, searchProviderID pgtype.UUID, ) Settings { - settings := normalizeBotSetting(maxContextLoadTime, language, allowGuest) + settings := normalizeBotSetting(maxContextLoadTime, maxContextTokens, language, allowGuest) settings.ChatModelID = strings.TrimSpace(chatModelID.String) settings.MemoryModelID = strings.TrimSpace(memoryModelID.String) settings.EmbeddingModelID = strings.TrimSpace(embeddingModelID.String) diff --git a/internal/settings/types.go b/internal/settings/types.go index 33c6dc66..93b3a790 100644 --- a/internal/settings/types.go +++ b/internal/settings/types.go @@ -11,6 +11,7 @@ type Settings struct { EmbeddingModelID string `json:"embedding_model_id"` SearchProviderID string `json:"search_provider_id"` MaxContextLoadTime int `json:"max_context_load_time"` + MaxContextTokens int `json:"max_context_tokens"` Language string `json:"language"` AllowGuest bool `json:"allow_guest"` } @@ -21,6 +22,7 @@ type UpsertRequest struct { EmbeddingModelID string `json:"embedding_model_id,omitempty"` SearchProviderID string `json:"search_provider_id,omitempty"` MaxContextLoadTime *int `json:"max_context_load_time,omitempty"` + MaxContextTokens *int `json:"max_context_tokens,omitempty"` Language string `json:"language,omitempty"` AllowGuest *bool `json:"allow_guest,omitempty"` } diff --git a/packages/sdk/src/types.gen.ts b/packages/sdk/src/types.gen.ts index 7aab5383..6af0445a 100644 --- a/packages/sdk/src/types.gen.ts +++ b/packages/sdk/src/types.gen.ts @@ -673,6 +673,7 @@ export type MessageMessage = { sender_display_name?: string; sender_user_id?: string; source_reply_to_message_id?: string; + usage?: Array; }; export type MessageMessageAsset = { @@ -866,6 +867,7 @@ export type SettingsSettings = { embedding_model_id?: string; language?: string; max_context_load_time?: number; + max_context_tokens?: number; memory_model_id?: string; search_provider_id?: string; }; @@ -876,6 +878,7 @@ export type SettingsUpsertRequest = { embedding_model_id?: string; language?: string; max_context_load_time?: number; + max_context_tokens?: number; memory_model_id?: string; search_provider_id?: string; }; diff --git a/packages/web/src/i18n/locales/en.json b/packages/web/src/i18n/locales/en.json index b9e2fe4d..86559792 100644 --- a/packages/web/src/i18n/locales/en.json +++ b/packages/web/src/i18n/locales/en.json @@ -354,6 +354,7 @@ "searchProvider": "Search Provider", "searchProviderPlaceholder": "Select search provider", "maxContextLoadTime": "Max Context Load Time", + "maxContextTokens": "Max Context Tokens", "language": "Language", "allowGuest": "Allow Guest Access", "allowGuestPersonalHint": "Personal bots do not support guest access. Use a public bot instead.", diff --git a/packages/web/src/i18n/locales/zh.json b/packages/web/src/i18n/locales/zh.json index 135279b5..1a30bbaf 100644 --- a/packages/web/src/i18n/locales/zh.json +++ b/packages/web/src/i18n/locales/zh.json @@ -350,6 +350,7 @@ "searchProvider": "搜索提供方", "searchProviderPlaceholder": "选择搜索提供方", "maxContextLoadTime": "最大上下文加载时间", + "maxContextTokens": "最大上下文Token数", "language": "语言", "allowGuest": "允许游客访问", "allowGuestPersonalHint": "个人 Bot 不支持游客访问,请使用公开 Bot。", diff --git a/packages/web/src/pages/bots/components/bot-settings.vue b/packages/web/src/pages/bots/components/bot-settings.vue index 76abaf60..25007313 100644 --- a/packages/web/src/pages/bots/components/bot-settings.vue +++ b/packages/web/src/pages/bots/components/bot-settings.vue @@ -58,6 +58,17 @@ /> + +
+ + +
+
@@ -226,6 +237,7 @@ const form = reactive({ embedding_model_id: '', search_provider_id: '', max_context_load_time: 0, + max_context_tokens: 0, language: '', allow_guest: false, }) @@ -238,6 +250,7 @@ watch(settings, (val) => { form.embedding_model_id = val.embedding_model_id ?? '' form.search_provider_id = val.search_provider_id ?? '' form.max_context_load_time = val.max_context_load_time ?? 0 + form.max_context_tokens = val.max_context_tokens ?? 0 form.language = val.language ?? '' form.allow_guest = val.allow_guest ?? false } @@ -252,6 +265,7 @@ const hasChanges = computed(() => { || form.embedding_model_id !== (s.embedding_model_id ?? '') || form.search_provider_id !== (s.search_provider_id ?? '') || form.max_context_load_time !== (s.max_context_load_time ?? 0) + || form.max_context_tokens !== (s.max_context_tokens ?? 0) || form.language !== (s.language ?? '') if (isPublicBot.value) { changed = changed || form.allow_guest !== (s.allow_guest ?? false) diff --git a/spec/docs.go b/spec/docs.go index 9a2f6b67..77df3d75 100644 --- a/spec/docs.go +++ b/spec/docs.go @@ -6451,6 +6451,12 @@ const docTemplate = `{ }, "source_reply_to_message_id": { "type": "string" + }, + "usage": { + "type": "array", + "items": { + "type": "integer" + } } } }, @@ -6949,6 +6955,9 @@ const docTemplate = `{ "max_context_load_time": { "type": "integer" }, + "max_context_tokens": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, @@ -6975,6 +6984,9 @@ const docTemplate = `{ "max_context_load_time": { "type": "integer" }, + "max_context_tokens": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, diff --git a/spec/swagger.json b/spec/swagger.json index e7e3b221..f9590be1 100644 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -6442,6 +6442,12 @@ }, "source_reply_to_message_id": { "type": "string" + }, + "usage": { + "type": "array", + "items": { + "type": "integer" + } } } }, @@ -6940,6 +6946,9 @@ "max_context_load_time": { "type": "integer" }, + "max_context_tokens": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, @@ -6966,6 +6975,9 @@ "max_context_load_time": { "type": "integer" }, + "max_context_tokens": { + "type": "integer" + }, "memory_model_id": { "type": "string" }, diff --git a/spec/swagger.yaml b/spec/swagger.yaml index c042cdd4..61854363 100644 --- a/spec/swagger.yaml +++ b/spec/swagger.yaml @@ -1101,6 +1101,10 @@ definitions: type: string source_reply_to_message_id: type: string + usage: + items: + type: integer + type: array type: object message.MessageAsset: properties: @@ -1435,6 +1439,8 @@ definitions: type: string max_context_load_time: type: integer + max_context_tokens: + type: integer memory_model_id: type: string search_provider_id: @@ -1452,6 +1458,8 @@ definitions: type: string max_context_load_time: type: integer + max_context_tokens: + type: integer memory_model_id: type: string search_provider_id: